From 176be44ea021717cf6ad0b64b58bf4b5a3ffb8af Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Thu, 29 Oct 2026 06:32:57 -0700 Subject: [PATCH 1/9] typescript, javascript: Hono apps get a route table, middleware chains, context keys and request links The engine now reads what a Hono app declares and what it does with a request, in both the TypeScript and the JavaScript graphs (each graph on its own, nothing links the two). What an agent can now ask: - Routes with their full paths. Every get/post/on/all/route registration becomes a route with its verb and its composed path, through basePath, route() mounts several levels deep, sub-apps built by factories or registration functions, declarative route lists, and an app variable re-pointed at a basePath result (exact when unconditional, partial when it is not). A path that is not a literal or a const is reported as undetermined with a reason, never guessed. - Middleware chains in order. Each route lists the middleware that runs before its handler, app-wide use("*") and path-scoped use(..) included, in registration order, with a tag when a registration sits inside a branch or a loop. - Context keys. A c.set(key) in a middleware is linked to the c.get(key) and c.var.key reads in the handlers it runs before. - Typed-client and test-request links. An hc client call, a fetch to a path the program spells, and app.request(..) in a test are linked to the route they reach. A request whose method or base cannot be decided says so instead of joining at random. - Validator schemas. A zod schema handed to a validator is linked to the handler that reads it through c.req.valid(..), including schemas built by project helpers. - Declared unknowns. Anything the rules cannot decide (a dynamic path piece, an unresolvable middleware list, a run-time base path) is a row that names the reason. impact, context and path answer on all of these: "how does a request to POST /x run" names the route, its handler and the middleware in front of it in order; path from a middleware to a handler goes through the route; impact on a middleware lists every route it runs before. The rules live in the config-resolution and framework-behavior phases of each engine, and the framework names only in the knob files. In the TypeScript engine, every router rule that starts from a program-wide relation runs only when the program imports a module a router knob names, so a program without such an import pays nothing for them; the served-app fetch lookup is a keyed join. Generic changes outside them, framework-free: - resolution: what `await import('./m')` loads, and the method a loop over a const array of typed elements calls (read by the path evaluation only, it draws no call edge); - the string evaluation that names a request's path, shared by every HTTP framework, now follows class fields, a parameter of a function called once and a zero-argument call, stops trusting a variable that is reassigned, and no longer reads a fetch of a module-relative file URL as a request; - a backtick string with no substitution counts as a literal path for entry points. - the bundle indexes the new router relations the queries read (EXT_QUERIED), as it does the other queried ext tables. The existing Express and Nest route suites give the same results as before. Tests: graph/test/typescript/hono and graph/test/javascript/hono, every case checked against an independent oracle built on the TypeScript compiler. Whole request call chains (handler into service into repository) are not in this change. --- graph/bundle/catalog.ts | 10 +- .../config-resolution/http-router-apps.dl | 188 + .../config-resolution/http-router-routes.dl | 330 ++ .../engine/config-resolution/knobs.dl | 107 + .../framework-behavior/http-router-chain.dl | 54 + .../framework-behavior/http-rpc-client.dl | 295 ++ graph/javascript/souffle/decls_all.dl | 12 + graph/javascript/souffle/export_manifest.tsv | 12 + graph/test/javascript/hono/.gitignore | 2 + .../js/bun-websocket-destructure/src/index.js | 10 + .../src/package.json | 1 + .../hono/cases/js/cjs-basic/src/index.js | 13 + .../hono/cases/js/cjs-basic/src/package.json | 1 + .../hono/cases/js/cjs-basic/src/users.js | 6 + .../hono/cases/js/cjs-context/src/index.js | 16 + .../cases/js/cjs-context/src/package.json | 1 + .../cases/js/cjs-exports-members/src/app.js | 14 + .../cases/js/cjs-exports-members/src/auth.js | 7 + .../js/cjs-exports-members/src/package.json | 1 + .../hono/cases/js/cjs-negative/src/index.js | 19 + .../cases/js/cjs-negative/src/package.json | 1 + .../cases/js/cjs-routes-mount/src/index.cjs | 16 + .../cases/js/cjs-routes-mount/src/users.cjs | 7 + .../js/cjs-websocket-module/src/package.json | 1 + .../src/routes/container.js | 14 + .../js/cjs-websocket-module/src/server.js | 10 + .../src/utils/websocket.js | 5 + .../js/create-middleware-factory/src/app.js | 11 + .../src/package.json | 1 + .../create-middleware-factory/src/session.js | 8 + .../hono/cases/js/esm-basic/src/index.js | 16 + .../hono/cases/js/esm-basic/src/package.json | 1 + .../hono/cases/js/esm-basic/src/users.mjs | 7 + .../hono/cases/js/esm-context/src/index.js | 16 + .../cases/js/esm-context/src/package.json | 1 + .../js/esm-fetch-unknown-base/src/api.js | 10 + .../js/esm-fetch-unknown-base/src/client.js | 15 + .../esm-fetch-unknown-base/src/package.json | 1 + .../hono/cases/js/esm-hc-client/src/client.js | 12 + .../cases/js/esm-hc-client/src/package.json | 1 + .../hono/cases/js/esm-hc-client/src/server.js | 7 + .../js/esm-twin-of-cjs-basic/src/index.mjs | 11 + .../js/esm-twin-of-cjs-basic/src/users.mjs | 6 + .../hono/cases/js/negative-js/src/index.js | 20 + .../cases/js/negative-js/src/package.json | 1 + .../cases/js/unresolved-lib-mw/src/index.js | 15 + .../js/unresolved-lib-mw/src/package.json | 1 + .../js/bun-websocket-destructure.entries | 2 + .../expected/js/bun-websocket-destructure.tsv | 4 + .../hono/expected/js/cjs-basic.entries | 4 + .../javascript/hono/expected/js/cjs-basic.tsv | 10 + .../hono/expected/js/cjs-context.entries | 6 + .../hono/expected/js/cjs-context.tsv | 15 + .../expected/js/cjs-exports-members.entries | 6 + .../hono/expected/js/cjs-exports-members.tsv | 12 + .../hono/expected/js/cjs-negative.entries | 3 + .../hono/expected/js/cjs-negative.tsv | 10 + .../hono/expected/js/cjs-routes-mount.entries | 7 + .../hono/expected/js/cjs-routes-mount.tsv | 18 + .../expected/js/cjs-websocket-module.entries | 3 + .../hono/expected/js/cjs-websocket-module.tsv | 7 + .../js/create-middleware-factory.entries | 4 + .../expected/js/create-middleware-factory.tsv | 7 + .../hono/expected/js/esm-basic.entries | 7 + .../javascript/hono/expected/js/esm-basic.tsv | 18 + .../hono/expected/js/esm-context.entries | 6 + .../hono/expected/js/esm-context.tsv | 15 + .../js/esm-fetch-unknown-base.entries | 4 + .../expected/js/esm-fetch-unknown-base.tsv | 9 + .../hono/expected/js/esm-hc-client.entries | 4 + .../hono/expected/js/esm-hc-client.tsv | 9 + .../expected/js/esm-twin-of-cjs-basic.entries | 4 + .../expected/js/esm-twin-of-cjs-basic.tsv | 10 + .../hono/expected/js/negative-js.entries | 3 + .../hono/expected/js/negative-js.tsv | 10 + .../expected/js/unresolved-lib-mw.entries | 4 + .../hono/expected/js/unresolved-lib-mw.tsv | 7 + graph/test/javascript/hono/run-tests.sh | 8 + graph/test/typescript/hono/.gitignore | 8 + .../abstract-strategy-mount/src/index.ts | 31 + .../aliased-reexport-mount/src/index.ts | 10 + .../src/search/index.ts | 5 + .../src/search/routes/api.ts | 7 + .../compose/async-wrapper-mount/src/main.ts | 13 + .../compose/async-wrapper-mount/src/wrap.ts | 11 + .../cases/compose/basepath-group/src/index.ts | 13 + .../compose/basepath-sub-mounted/src/index.ts | 11 + .../compose/builder-called-twice/src/main.ts | 15 + .../builder-called-twice/src/routes.ts | 9 + .../compose/builder-called-twice/src/users.ts | 12 + .../declarative-routes-const/src/index.ts | 24 + .../declarative-routes-derived/src/app.ts | 13 + .../src/mount.test.ts | 29 + .../declarative-routes-derived/src/mount.ts | 27 + .../src/plugins/inbox.ts | 10 + .../compose/discarded-basepath/src/index.ts | 13 + .../cases/compose/dynamic-prefix/src/index.ts | 13 + .../compose/factory-basepath/src/index.ts | 12 + .../hono/cases/compose/factory-fn/src/app.ts | 11 + .../cases/compose/factory-fn/src/index.ts | 10 + .../field-app-class/src/routes/health.ts | 3 + .../field-app-class/src/routes/users.ts | 9 + .../compose/field-app-class/src/server.ts | 31 + .../filtered-instance-loop/src/index.ts | 31 + .../cases/compose/foreign-mount/src/index.ts | 13 + .../getter-prefix-typed-holder/src/index.ts | 48 + .../cases/compose/glob-module-list/src/env.ts | 7 + .../compose/glob-module-list/src/index.ts | 25 + .../glob-module-list/src/lazy/reports.page.ts | 6 + .../src/routes/orders/helpers.ts | 7 + .../src/routes/orders/orders.index.ts | 6 + .../src/routes/users/users.index.ts | 7 + .../late-sub-registration/src/index.ts | 14 + .../let-rebase-conditional/src/index.ts | 29 + .../compose/let-rebase-literal/src/index.ts | 15 + .../hono/cases/compose/lib-dirs/src/index.ts | 8 + .../cases/compose/lib-dirs/src/lib/app.ts | 4 + .../compose/lib-dirs/src/libs/users/routes.ts | 4 + .../literal-prefix-method/src/index.ts | 41 + .../loop-mount-method-fallback/src/index.ts | 43 + .../method-prefix-const-list/src/index.ts | 17 + .../method-prefix-const-list/src/members.ts | 13 + .../method-prefix-const-list/src/routes.ts | 9 + .../compose/mount-arg-never-root/src/index.ts | 13 + .../cases/compose/mount-chain/src/index.ts | 17 + .../compose/nested-three-deep/src/api.ts | 6 + .../compose/nested-three-deep/src/comments.ts | 6 + .../compose/nested-three-deep/src/index.ts | 10 + .../compose/nested-three-deep/src/posts.ts | 7 + .../opaque-module-list/src/routes/billing.ts | 6 + .../opaque-module-list/src/routes/status.ts | 6 + .../compose/opaque-module-list/src/server.ts | 20 + .../compose/opaque-module-list/src/shared.ts | 7 + .../compose/opaque-module-list/src/worker.ts | 7 + .../compose/oracle-eval-cycle/src/cli.ts | 39 + .../compose/oracle-eval-cycle/src/index.ts | 21 + .../compose/oracle-eval-cycle/src/saver.ts | 24 + .../plugin-helper-list/src/app.test.ts | 20 + .../compose/plugin-helper-list/src/app.ts | 15 + .../compose/plugin-helper-list/src/mount.ts | 22 + .../src/plugins/magic-link.ts | 8 + .../compose/plugin-helper-list/src/sort.ts | 17 + .../plugin-instance-never-mounted/src/app.ts | 18 + .../src/plugins/legacy-export.ts | 12 + .../src/plugins/magic-link.ts | 21 + .../plugin-register-method/src/index.ts | 22 + .../src/plugins/media.ts | 17 + .../src/plugins/variables.ts | 18 + .../compose/plugin-register-method/src/sdk.ts | 12 + .../src/plugins.ts | 15 + .../src/plugins/billing.ts | 7 + .../src/plugins/search.ts | 8 + .../plugin-shared-test-and-prod/src/server.ts | 10 + .../src/test/search.test.ts | 22 + .../rebased-holder-getter/src/index.ts | 42 + .../compose/rebased-holder-mount/src/index.ts | 26 + .../rebased-holder-one-partial/src/index.ts | 46 + .../compose/rebased-two-mounts/src/index.ts | 32 + .../compose/reexport-barrel/src/index.ts | 9 + .../reexport-barrel/src/routes/books.ts | 6 + .../reexport-barrel/src/routes/index.ts | 1 + .../cases/compose/register-fn/src/index.ts | 15 + .../cases/compose/register-fn/src/monitors.ts | 7 + .../cases/compose/register-fn/src/users.ts | 6 + .../register-loop-plugins/src/plugins.ts | 9 + .../register-loop-plugins/src/register.ts | 11 + .../cases/compose/registered-by/src/index.ts | 14 + .../compose/registered-by/src/monitors.ts | 9 + .../compose/route-and-partial/src/index.ts | 19 + .../compose/route-cross-module/src/index.ts | 11 + .../compose/route-cross-module/src/users.ts | 9 + .../cases/compose/route-root/src/index.ts | 16 + .../cases/compose/same-sub-twice/src/index.ts | 12 + .../compose/self-basepath-field/src/index.ts | 22 + .../shared-factory-filtered-loop/src/index.ts | 47 + .../compose/sync-wrapper-mount/src/main.ts | 13 + .../compose/sync-wrapper-mount/src/wrap.ts | 11 + .../src/__tests__/define-plugin.test.ts | 29 + .../src/__tests__/mount.test.ts | 33 + .../compose/test-plugin-own-app/src/app.ts | 15 + .../test-plugin-own-app/src/plugins/audit.ts | 8 + .../src/plugins/tenants.ts | 15 + .../compose/test-plugin-own-app/src/sdk.ts | 70 + .../typed-property-receiver/src/index.ts | 20 + .../compose/uncalled-start-server/src/api.ts | 4 + .../uncalled-start-server/src/server.ts | 11 + .../src/apps/api/src/index.ts | 14 + .../src/apps/api/tsconfig.json | 1 + .../src/packages/core/adapters/index.ts | 5 + .../src/packages/core/index.ts | 1 + .../src/packages/core/package.json | 1 + .../context/chain-of-setters/src/index.ts | 20 + .../cases/context/cross-module/src/auth.ts | 8 + .../cases/context/cross-module/src/index.ts | 12 + .../cases/context/cross-module/src/routes.ts | 11 + .../hono/cases/context/set-get/src/index.ts | 22 + .../cases/deno/denoland-url/src/deno.json | 1 + .../hono/cases/deno/denoland-url/src/main.ts | 10 + .../deno/import-map-deno-json/src/deno.json | 7 + .../deno/import-map-deno-json/src/main.ts | 12 + .../import-map-deno-json/src/routes/users.ts | 5 + .../deno/import-map-file/src/import_map.json | 6 + .../cases/deno/import-map-file/src/main.ts | 13 + .../deno/import-map-jsonc/src/deno.jsonc | 6 + .../cases/deno/import-map-jsonc/src/main.ts | 7 + .../cases/deno/jsr-specifier/src/deno.json | 1 + .../hono/cases/deno/jsr-specifier/src/main.ts | 11 + .../deno/jsr-static-method/src/api/deno.json | 1 + .../deno/jsr-static-method/src/api/main.ts | 8 + .../deno/jsr-static-method/src/api/routes.ts | 7 + .../cases/deno/npm-specifier/src/deno.json | 1 + .../hono/cases/deno/npm-specifier/src/main.ts | 10 + .../aliased-app-fetch-wrapper/src/index.ts | 13 + .../aliased-app-fetch-wrapper/src/lib/app.ts | 5 + .../src/tsconfig.json | 1 + .../entry/app-request-test/src/app.test.ts | 30 + .../cases/entry/app-request-test/src/app.ts | 6 + .../hono/cases/entry/deno-bun/src/index.ts | 14 + .../dynamic-import-test-app/src/admin.ts | 3 + .../src/skills.test.ts | 29 + .../dynamic-import-test-app/src/skills.ts | 5 + .../cases/entry/fetch-object/src/index.ts | 18 + .../fixture-prop-request/src/app.test.ts | 15 + .../entry/fixture-prop-request/src/app.ts | 7 + .../cases/entry/jsx-render/src/components.tsx | 10 + .../hono/cases/entry/jsx-render/src/index.tsx | 15 + .../hono/cases/entry/node-serve/src/index.ts | 9 + .../cases/entry/notfound-onerror/src/index.ts | 12 + .../request-object-and-fetch/src/app.test.ts | 18 + .../entry/request-object-and-fetch/src/app.ts | 5 + .../entry/test-built-app/src/app.test.ts | 16 + .../middleware/barrel-library-mw/src/index.ts | 14 + .../barrel-library-mw/src/middleware/audit.ts | 6 + .../barrel-library-mw/src/middleware/index.ts | 4 + .../cases/middleware/builtin/src/index.ts | 16 + .../cases/middleware/combine/src/index.ts | 19 + .../conditional-lib-mw/src/index.ts | 16 + .../middleware/const-library-mw/src/index.ts | 35 + .../detached-factory-method/src/factory.ts | 11 + .../detached-factory-method/src/index.ts | 23 + .../cases/middleware/factory-fn/src/index.ts | 10 + .../cases/middleware/factory-fn/src/mw.ts | 13 + .../middleware/factory-handlers/src/index.ts | 22 + .../factory-two-returns/src/index.ts | 25 + .../src/routes.test.ts | 40 + .../helper-app-mounted-thrice/src/routes.ts | 10 + .../if-else-unequal-mw/src/index.ts | 32 + .../middleware/lib-mw-options/src/index.ts | 18 + .../cases/middleware/loop-use/src/index.ts | 26 + .../src/guards/index.ts | 2 + .../src/guards/scope.ts | 9 + .../src/guards/session.ts | 7 + .../mw-from-namespace-field/src/index.ts | 31 + .../partial-sub-app-chain/src/index.ts | 55 + .../runtime-list-except-ternary/src/index.ts | 42 + .../cases/middleware/scoped-use/src/index.ts | 22 + .../spread-after-mount/src/index.ts | 19 + .../spread-call-result/src/index.ts | 29 + .../sub-app-middleware/src/index.ts | 18 + .../cases/middleware/use-order/src/index.ts | 20 + .../cases/middleware/validator/src/index.ts | 18 + .../cases/middleware/verb-next/src/index.ts | 11 + .../negative/context-lookalike/src/index.ts | 18 + .../cases/negative/foreign-fetch/src/index.ts | 16 + .../cases/negative/http-client/src/client.ts | 13 + .../cases/negative/http-client/src/index.ts | 7 + .../negative/local-hono-class/src/index.ts | 19 + .../hono/cases/negative/map-get/src/index.ts | 22 + .../cases/negative/other-router/src/index.ts | 16 + .../negative/other-router/src/minirouter.ts | 8 + .../negative/request-lookalike/src/index.ts | 18 + .../negative/zod-openapi-meta/src/index.ts | 20 + .../cases/openapi/create-route/src/index.ts | 20 + .../cases/openapi/create-route/src/routes.ts | 16 + .../namespace-handlers/src/auth.handlers.ts | 3 + .../namespace-handlers/src/auth.routes.ts | 3 + .../openapi/namespace-handlers/src/index.ts | 11 + .../openapi/response-helper/src/index.ts | 42 + .../openapi/route-middleware/src/index.ts | 21 + .../spread-error-responses/src/errors.ts | 14 + .../spread-error-responses/src/index.ts | 21 + .../status-key-helpers/src/handlers.ts | 4 + .../status-key-helpers/src/helpers/index.ts | 2 + .../src/helpers/json-body-required.ts | 10 + .../src/helpers/json-body.ts | 12 + .../openapi/status-key-helpers/src/index.ts | 12 + .../openapi/status-key-helpers/src/routes.ts | 26 + .../openapi/status-key-helpers/src/status.ts | 3 + .../cases/routing/all-and-any/src/index.ts | 10 + .../routing/conditional-loop/src/index.ts | 20 + .../cases/routing/const-paths/src/index.ts | 14 + .../cases/routing/const-paths/src/paths.ts | 2 + .../cases/routing/dynamic-path/src/index.ts | 11 + .../cases/routing/every-verb/src/index.ts | 20 + .../routing/library-handler/src/index.ts | 14 + .../cases/routing/mts-source/src/server.mts | 7 + .../cases/routing/multi-handler/src/index.ts | 23 + .../routing/named-handlers/src/handlers.ts | 11 + .../cases/routing/named-handlers/src/index.ts | 12 + .../hono/cases/routing/on-arrays/src/index.ts | 11 + .../routing/partial-route-scope/src/index.ts | 16 + .../cases/routing/path-shapes/src/index.ts | 16 + .../cases/routing/pathless-chain/src/index.ts | 16 + .../cases/routing/scope-table/src/index.ts | 23 + .../cases/routing/strict-false/src/index.ts | 10 + .../websocket-project-module/src/chat.ts | 15 + .../websocket-project-module/src/index.ts | 12 + .../src/tsconfig.json | 9 + .../src/utils/websocket.ts | 3 + .../cases/routing/websocket-sse/src/index.ts | 23 + .../base-unknown-implausible/src/client.ts | 19 + .../src/server.test.ts | 9 + .../base-unknown-implausible/src/server.ts | 13 + .../cases/rpc/fetch-unknown-base/src/api.ts | 11 + .../cases/rpc/fetch-unknown-base/src/cli.ts | 17 + .../rpc/fetch-verb-parameter/src/discovery.ts | 8 + .../fetch-verb-parameter/src/server.test.ts | 18 + .../rpc/fetch-verb-parameter/src/server.ts | 14 + .../fetch-verb-parameter/src/skills-client.ts | 14 + .../hono/cases/rpc/hc-basic/src/client.ts | 22 + .../hono/cases/rpc/hc-basic/src/server.ts | 12 + .../rpc/local-fetch-full-path/src/api.ts | 6 + .../rpc/local-fetch-full-path/src/main.ts | 11 + .../rpc/local-fetch-full-path/src/probe.ts | 17 + .../cases/rpc/mixed-segment-hole/src/index.ts | 16 + .../rpc/monorepo-client/src/server/app.ts | 7 + .../rpc/monorepo-client/src/web/orders.ts | 16 + .../cases/rpc/query-string/src/app.test.ts | 10 + .../hono/cases/rpc/query-string/src/app.ts | 5 + .../cases/rpc/reassigned-let/src/api.test.ts | 21 + .../hono/cases/rpc/reassigned-let/src/api.ts | 8 + .../request-on-mounted-subapp/src/api.test.ts | 10 + .../rpc/request-on-mounted-subapp/src/api.ts | 8 + .../request-on-mounted-subapp/src/index.ts | 10 + .../cases/rpc/single-label-host/src/index.ts | 19 + .../cases/rpc/template-holes/src/app.test.ts | 18 + .../hono/cases/rpc/template-holes/src/app.ts | 6 + .../src/apps/api/package.json | 1 + .../src/apps/api/src/index.ts | 11 + .../src/packages/routes/package.json | 11 + .../src/packages/routes/src/adapters/hono.ts | 8 + .../src/packages/routes/src/index.ts | 1 + .../src/packages/client/lib/client.ts | 6 + .../src/packages/client/lib/v2/index.ts | 4 + .../src/packages/client/package.json | 1 + .../src/packages/server/package.json | 1 + .../src/packages/server/src/app.ts | 7 + .../src/packages/admin/package.json | 1 + .../src/packages/admin/routes.ts | 3 + .../src/packages/server/package.json | 1 + .../src/packages/server/src/index.ts | 8 + .../src/packages/web/client.ts | 13 + .../hono/cases/scale/deep-mount/budget | 2 + .../hono/cases/scale/deep-mount/src/index.ts | 34 + .../typescript/hono/cases/scale/mw-200/budget | 2 + .../hono/cases/scale/mw-200/src/index.ts | 417 ++ .../hono/cases/scale/routes-2k/budget | 2 + .../hono/cases/scale/routes-2k/src/index.ts | 46 + .../hono/cases/scale/routes-2k/src/mod0.ts | 103 + .../hono/cases/scale/routes-2k/src/mod1.ts | 103 + .../hono/cases/scale/routes-2k/src/mod10.ts | 103 + .../hono/cases/scale/routes-2k/src/mod11.ts | 103 + .../hono/cases/scale/routes-2k/src/mod12.ts | 103 + .../hono/cases/scale/routes-2k/src/mod13.ts | 103 + .../hono/cases/scale/routes-2k/src/mod14.ts | 103 + .../hono/cases/scale/routes-2k/src/mod15.ts | 103 + .../hono/cases/scale/routes-2k/src/mod16.ts | 103 + .../hono/cases/scale/routes-2k/src/mod17.ts | 103 + .../hono/cases/scale/routes-2k/src/mod18.ts | 103 + .../hono/cases/scale/routes-2k/src/mod19.ts | 103 + .../hono/cases/scale/routes-2k/src/mod2.ts | 103 + .../hono/cases/scale/routes-2k/src/mod3.ts | 103 + .../hono/cases/scale/routes-2k/src/mod4.ts | 103 + .../hono/cases/scale/routes-2k/src/mod5.ts | 103 + .../hono/cases/scale/routes-2k/src/mod6.ts | 103 + .../hono/cases/scale/routes-2k/src/mod7.ts | 103 + .../hono/cases/scale/routes-2k/src/mod8.ts | 103 + .../hono/cases/scale/routes-2k/src/mod9.ts | 103 + .../hono/cases/zod/composition/src/index.ts | 27 + .../hono/cases/zod/decl-forms/src/index.ts | 21 + .../cases/zod/decl-forms/src/schemas/index.ts | 1 + .../cases/zod/decl-forms/src/schemas/query.ts | 3 + .../cases/zod/decl-forms/src/schemas/user.ts | 6 + .../hono/cases/zod/no-route/src/forms.ts | 14 + .../hono/cases/zod/specifiers/src/index.ts | 26 + .../hono/cases/zod/valid-targets/src/index.ts | 35 + .../compose/abstract-strategy-mount.entries | 4 + .../compose/abstract-strategy-mount.tsv | 8 + .../compose/aliased-reexport-mount.entries | 4 + .../compose/aliased-reexport-mount.tsv | 7 + .../compose/async-wrapper-mount.entries | 4 + .../expected/compose/async-wrapper-mount.tsv | 6 + .../expected/compose/basepath-group.entries | 5 + .../hono/expected/compose/basepath-group.tsv | 9 + .../compose/basepath-sub-mounted.entries | 3 + .../expected/compose/basepath-sub-mounted.tsv | 6 + .../compose/builder-called-twice.entries | 4 + .../expected/compose/builder-called-twice.tsv | 6 + .../compose/declarative-routes-const.entries | 4 + .../compose/declarative-routes-const.tsv | 8 + .../declarative-routes-derived.entries | 6 + .../compose/declarative-routes-derived.tsv | 14 + .../compose/discarded-basepath.entries | 3 + .../expected/compose/discarded-basepath.tsv | 4 + .../expected/compose/dynamic-prefix.entries | 4 + .../hono/expected/compose/dynamic-prefix.tsv | 7 + .../expected/compose/factory-basepath.entries | 3 + .../expected/compose/factory-basepath.tsv | 5 + .../hono/expected/compose/factory-fn.entries | 4 + .../hono/expected/compose/factory-fn.tsv | 8 + .../expected/compose/field-app-class.entries | 5 + .../hono/expected/compose/field-app-class.tsv | 10 + .../compose/filtered-instance-loop.entries | 5 + .../compose/filtered-instance-loop.tsv | 12 + .../expected/compose/foreign-mount.entries | 4 + .../hono/expected/compose/foreign-mount.tsv | 8 + .../getter-prefix-typed-holder.entries | 4 + .../compose/getter-prefix-typed-holder.tsv | 6 + .../expected/compose/glob-module-list.entries | 7 + .../expected/compose/glob-module-list.tsv | 16 + .../compose/late-sub-registration.entries | 4 + .../compose/late-sub-registration.tsv | 6 + .../compose/let-rebase-conditional.entries | 6 + .../compose/let-rebase-conditional.tsv | 10 + .../compose/let-rebase-literal.entries | 5 + .../expected/compose/let-rebase-literal.tsv | 9 + .../hono/expected/compose/lib-dirs.entries | 4 + .../hono/expected/compose/lib-dirs.tsv | 8 + .../compose/literal-prefix-method.entries | 5 + .../compose/literal-prefix-method.tsv | 9 + .../loop-mount-method-fallback.entries | 4 + .../compose/loop-mount-method-fallback.tsv | 7 + .../compose/method-prefix-const-list.entries | 5 + .../compose/method-prefix-const-list.tsv | 9 + .../compose/mount-arg-never-root.entries | 3 + .../expected/compose/mount-arg-never-root.tsv | 4 + .../hono/expected/compose/mount-chain.entries | 4 + .../hono/expected/compose/mount-chain.tsv | 6 + .../compose/nested-three-deep.entries | 5 + .../expected/compose/nested-three-deep.tsv | 12 + .../compose/opaque-module-list.entries | 6 + .../expected/compose/opaque-module-list.tsv | 12 + .../compose/oracle-eval-cycle.entries | 4 + .../expected/compose/oracle-eval-cycle.tsv | 6 + .../compose/plugin-helper-list.entries | 6 + .../expected/compose/plugin-helper-list.tsv | 16 + .../plugin-instance-never-mounted.entries | 6 + .../compose/plugin-instance-never-mounted.tsv | 13 + .../compose/plugin-register-method.entries | 7 + .../compose/plugin-register-method.tsv | 19 + .../plugin-shared-test-and-prod.entries | 7 + .../compose/plugin-shared-test-and-prod.tsv | 32 + .../compose/rebased-holder-getter.entries | 4 + .../compose/rebased-holder-getter.tsv | 6 + .../compose/rebased-holder-mount.entries | 3 + .../expected/compose/rebased-holder-mount.tsv | 4 + .../rebased-holder-one-partial.entries | 4 + .../compose/rebased-holder-one-partial.tsv | 6 + .../compose/rebased-two-mounts.entries | 4 + .../expected/compose/rebased-two-mounts.tsv | 6 + .../expected/compose/reexport-barrel.entries | 3 + .../hono/expected/compose/reexport-barrel.tsv | 6 + .../hono/expected/compose/register-fn.entries | 7 + .../hono/expected/compose/register-fn.tsv | 14 + .../compose/register-loop-plugins.entries | 4 + .../compose/register-loop-plugins.tsv | 6 + .../expected/compose/registered-by.entries | 6 + .../hono/expected/compose/registered-by.tsv | 11 + .../compose/route-and-partial.entries | 3 + .../expected/compose/route-and-partial.tsv | 4 + .../compose/route-cross-module.entries | 6 + .../expected/compose/route-cross-module.tsv | 12 + .../hono/expected/compose/route-root.entries | 5 + .../hono/expected/compose/route-root.tsv | 11 + .../expected/compose/same-sub-twice.entries | 3 + .../hono/expected/compose/same-sub-twice.tsv | 9 + .../compose/self-basepath-field.entries | 3 + .../expected/compose/self-basepath-field.tsv | 4 + .../shared-factory-filtered-loop.entries | 6 + .../compose/shared-factory-filtered-loop.tsv | 15 + .../compose/sync-wrapper-mount.entries | 5 + .../expected/compose/sync-wrapper-mount.tsv | 9 + .../compose/test-plugin-own-app.entries | 9 + .../expected/compose/test-plugin-own-app.tsv | 38 + .../compose/typed-property-receiver.entries | 3 + .../compose/typed-property-receiver.tsv | 4 + .../compose/uncalled-start-server.entries | 3 + .../compose/uncalled-start-server.tsv | 6 + ...workspace-unlisted-subpath-bundler.entries | 4 + .../workspace-unlisted-subpath-bundler.tsv | 7 + .../expected/context/chain-of-setters.entries | 5 + .../expected/context/chain-of-setters.tsv | 11 + .../expected/context/cross-module.entries | 5 + .../hono/expected/context/cross-module.tsv | 13 + .../hono/expected/context/set-get.entries | 7 + .../hono/expected/context/set-get.tsv | 19 + .../hono/expected/deno/denoland-url.entries | 3 + .../hono/expected/deno/denoland-url.tsv | 7 + .../deno/import-map-deno-json.entries | 4 + .../expected/deno/import-map-deno-json.tsv | 11 + .../expected/deno/import-map-file.entries | 4 + .../hono/expected/deno/import-map-file.tsv | 7 + .../expected/deno/import-map-jsonc.entries | 3 + .../hono/expected/deno/import-map-jsonc.tsv | 5 + .../hono/expected/deno/jsr-specifier.entries | 3 + .../hono/expected/deno/jsr-specifier.tsv | 7 + .../expected/deno/jsr-static-method.entries | 3 + .../hono/expected/deno/jsr-static-method.tsv | 4 + .../hono/expected/deno/npm-specifier.entries | 3 + .../hono/expected/deno/npm-specifier.tsv | 7 + .../entry/aliased-app-fetch-wrapper.entries | 3 + .../entry/aliased-app-fetch-wrapper.tsv | 5 + .../expected/entry/app-request-test.entries | 5 + .../hono/expected/entry/app-request-test.tsv | 14 + .../hono/expected/entry/deno-bun.entries | 3 + .../hono/expected/entry/deno-bun.tsv | 5 + .../entry/dynamic-import-test-app.entries | 6 + .../entry/dynamic-import-test-app.tsv | 15 + .../hono/expected/entry/fetch-object.entries | 5 + .../hono/expected/entry/fetch-object.tsv | 5 + .../entry/fixture-prop-request.entries | 3 + .../expected/entry/fixture-prop-request.tsv | 5 + .../hono/expected/entry/jsx-render.entries | 4 + .../hono/expected/entry/jsx-render.tsv | 12 + .../hono/expected/entry/node-serve.entries | 3 + .../hono/expected/entry/node-serve.tsv | 5 + .../expected/entry/notfound-onerror.entries | 5 + .../hono/expected/entry/notfound-onerror.tsv | 5 + .../entry/request-object-and-fetch.entries | 4 + .../entry/request-object-and-fetch.tsv | 10 + .../expected/entry/test-built-app.entries | 3 + .../hono/expected/entry/test-built-app.tsv | 6 + .../middleware/barrel-library-mw.entries | 5 + .../expected/middleware/barrel-library-mw.tsv | 12 + .../hono/expected/middleware/builtin.entries | 4 + .../hono/expected/middleware/builtin.tsv | 13 + .../hono/expected/middleware/combine.entries | 8 + .../hono/expected/middleware/combine.tsv | 14 + .../middleware/conditional-lib-mw.entries | 3 + .../middleware/conditional-lib-mw.tsv | 8 + .../middleware/const-library-mw.entries | 5 + .../expected/middleware/const-library-mw.tsv | 14 + .../detached-factory-method.entries | 5 + .../middleware/detached-factory-method.tsv | 7 + .../expected/middleware/factory-fn.entries | 5 + .../hono/expected/middleware/factory-fn.tsv | 8 + .../middleware/factory-handlers.entries | 5 + .../expected/middleware/factory-handlers.tsv | 8 + .../middleware/factory-two-returns.entries | 5 + .../middleware/factory-two-returns.tsv | 8 + .../helper-app-mounted-thrice.entries | 4 + .../middleware/helper-app-mounted-thrice.tsv | 23 + .../middleware/if-else-unequal-mw.entries | 10 + .../middleware/if-else-unequal-mw.tsv | 22 + .../middleware/lib-mw-options.entries | 3 + .../expected/middleware/lib-mw-options.tsv | 6 + .../hono/expected/middleware/loop-use.entries | 5 + .../hono/expected/middleware/loop-use.tsv | 7 + .../mw-from-namespace-field.entries | 7 + .../middleware/mw-from-namespace-field.tsv | 21 + .../middleware/partial-sub-app-chain.entries | 6 + .../middleware/partial-sub-app-chain.tsv | 8 + .../runtime-list-except-ternary.entries | 5 + .../runtime-list-except-ternary.tsv | 10 + .../expected/middleware/scoped-use.entries | 8 + .../hono/expected/middleware/scoped-use.tsv | 15 + .../middleware/spread-after-mount.entries | 6 + .../middleware/spread-after-mount.tsv | 11 + .../middleware/spread-call-result.entries | 5 + .../middleware/spread-call-result.tsv | 8 + .../middleware/sub-app-middleware.entries | 6 + .../middleware/sub-app-middleware.tsv | 12 + .../expected/middleware/use-order.entries | 7 + .../hono/expected/middleware/use-order.tsv | 15 + .../expected/middleware/validator.entries | 4 + .../hono/expected/middleware/validator.tsv | 12 + .../expected/middleware/verb-next.entries | 4 + .../hono/expected/middleware/verb-next.tsv | 9 + .../negative/context-lookalike.entries | 3 + .../expected/negative/context-lookalike.tsv | 7 + .../expected/negative/foreign-fetch.entries | 3 + .../hono/expected/negative/foreign-fetch.tsv | 5 + .../expected/negative/http-client.entries | 3 + .../hono/expected/negative/http-client.tsv | 8 + .../negative/local-hono-class.entries | 2 + .../expected/negative/local-hono-class.tsv | 5 + .../hono/expected/negative/map-get.entries | 3 + .../hono/expected/negative/map-get.tsv | 9 + .../expected/negative/other-router.entries | 3 + .../hono/expected/negative/other-router.tsv | 8 + .../negative/request-lookalike.entries | 3 + .../expected/negative/request-lookalike.tsv | 7 + .../negative/zod-openapi-meta.entries | 3 + .../expected/negative/zod-openapi-meta.tsv | 8 + .../expected/openapi/create-route.entries | 4 + .../hono/expected/openapi/create-route.tsv | 12 + .../openapi/namespace-handlers.entries | 3 + .../expected/openapi/namespace-handlers.tsv | 5 + .../expected/openapi/response-helper.entries | 5 + .../hono/expected/openapi/response-helper.tsv | 17 + .../expected/openapi/route-middleware.entries | 5 + .../expected/openapi/route-middleware.tsv | 9 + .../openapi/spread-error-responses.entries | 3 + .../openapi/spread-error-responses.tsv | 9 + .../openapi/status-key-helpers.entries | 4 + .../expected/openapi/status-key-helpers.tsv | 11 + .../hono/expected/routing/all-and-any.entries | 4 + .../hono/expected/routing/all-and-any.tsv | 7 + .../expected/routing/conditional-loop.entries | 6 + .../expected/routing/conditional-loop.tsv | 16 + .../hono/expected/routing/const-paths.entries | 6 + .../hono/expected/routing/const-paths.tsv | 11 + .../expected/routing/dynamic-path.entries | 4 + .../hono/expected/routing/dynamic-path.tsv | 7 + .../hono/expected/routing/every-verb.entries | 12 + .../hono/expected/routing/every-verb.tsv | 23 + .../expected/routing/library-handler.entries | 2 + .../hono/expected/routing/library-handler.tsv | 7 + .../hono/expected/routing/mts-source.entries | 4 + .../hono/expected/routing/mts-source.tsv | 7 + .../expected/routing/multi-handler.entries | 7 + .../hono/expected/routing/multi-handler.tsv | 13 + .../expected/routing/named-handlers.entries | 5 + .../hono/expected/routing/named-handlers.tsv | 9 + .../hono/expected/routing/on-arrays.entries | 5 + .../hono/expected/routing/on-arrays.tsv | 15 + .../routing/partial-route-scope.entries | 4 + .../expected/routing/partial-route-scope.tsv | 5 + .../hono/expected/routing/path-shapes.entries | 10 + .../hono/expected/routing/path-shapes.tsv | 19 + .../expected/routing/pathless-chain.entries | 8 + .../hono/expected/routing/pathless-chain.tsv | 15 + .../hono/expected/routing/scope-table.entries | 12 + .../hono/expected/routing/scope-table.tsv | 24 + .../expected/routing/strict-false.entries | 4 + .../hono/expected/routing/strict-false.tsv | 7 + .../routing/websocket-project-module.entries | 3 + .../routing/websocket-project-module.tsv | 8 + .../expected/routing/websocket-sse.entries | 3 + .../hono/expected/routing/websocket-sse.tsv | 6 + .../rpc/base-unknown-implausible.entries | 6 + .../expected/rpc/base-unknown-implausible.tsv | 12 + .../expected/rpc/fetch-unknown-base.entries | 5 + .../hono/expected/rpc/fetch-unknown-base.tsv | 11 + .../expected/rpc/fetch-verb-parameter.entries | 7 + .../expected/rpc/fetch-verb-parameter.tsv | 25 + .../hono/expected/rpc/hc-basic.entries | 6 + .../typescript/hono/expected/rpc/hc-basic.tsv | 16 + .../rpc/local-fetch-full-path.entries | 5 + .../expected/rpc/local-fetch-full-path.tsv | 11 + .../expected/rpc/mixed-segment-hole.entries | 4 + .../hono/expected/rpc/mixed-segment-hole.tsv | 8 + .../hono/expected/rpc/monorepo-client.entries | 4 + .../hono/expected/rpc/monorepo-client.tsv | 9 + .../hono/expected/rpc/query-string.entries | 4 + .../hono/expected/rpc/query-string.tsv | 8 + .../hono/expected/rpc/reassigned-let.entries | 5 + .../hono/expected/rpc/reassigned-let.tsv | 11 + .../rpc/request-on-mounted-subapp.entries | 4 + .../rpc/request-on-mounted-subapp.tsv | 11 + .../expected/rpc/single-label-host.entries | 4 + .../hono/expected/rpc/single-label-host.tsv | 8 + .../hono/expected/rpc/template-holes.entries | 5 + .../hono/expected/rpc/template-holes.tsv | 14 + .../rpc/workspace-dist-exports.entries | 5 + .../expected/rpc/workspace-dist-exports.tsv | 9 + .../expected/rpc/workspace-exports.entries | 3 + .../hono/expected/rpc/workspace-exports.tsv | 5 + .../expected/rpc/workspace-package.entries | 4 + .../hono/expected/rpc/workspace-package.tsv | 10 + .../hono/expected/scale/deep-mount.entries | 12 + .../hono/expected/scale/deep-mount.tsv | 32 + .../hono/expected/scale/mw-200.entries | 212 + .../typescript/hono/expected/scale/mw-200.tsv | 2024 +++++++++ .../hono/expected/scale/routes-2k.entries | 2002 ++++++++ .../hono/expected/scale/routes-2k.tsv | 4023 +++++++++++++++++ .../hono/expected/zod/composition.entries | 9 + .../hono/expected/zod/composition.tsv | 47 + .../hono/expected/zod/decl-forms.entries | 8 + .../hono/expected/zod/decl-forms.tsv | 34 + .../hono/expected/zod/no-route.entries | 2 + .../typescript/hono/expected/zod/no-route.tsv | 4 + .../hono/expected/zod/specifiers.entries | 8 + .../hono/expected/zod/specifiers.tsv | 32 + .../hono/expected/zod/valid-targets.entries | 4 + .../hono/expected/zod/valid-targets.tsv | 30 + graph/test/typescript/hono/relations.tsv | 22 + graph/test/typescript/hono/run-tests.sh | 225 + graph/test/typescript/hono/tools/fn-locs.mjs | 19 + .../test/typescript/hono/tools/gen-scale.mjs | 38 + .../typescript/hono/tools/hono_compare.py | 398 ++ .../typescript/hono/tools/promote-draft.sh | 16 + graph/test/typescript/hono/truth/.gitignore | 2 + .../hono/truth/deno-shims/deno.d.ts | 8 + .../hono/truth/deno-shims/middleware.d.ts | 7 + .../typescript/hono/truth/hono-oracle.mjs | 1847 ++++++++ graph/test/typescript/hono/truth/package.json | 14 + .../typescript/hono/truth/runtime-check.mjs | 154 + .../typescript/hono/truth/runtime-hooks.mjs | 20 + .../engine/config-resolution/entry-points.dl | 7 +- .../config-resolution/http-router-apps.dl | 1109 +++++ .../config-resolution/http-router-routes.dl | 827 ++++ .../engine/config-resolution/knobs.dl | 167 + .../engine/framework-behavior/destinations.dl | 157 +- .../framework-behavior/http-router-chain.dl | 74 + .../framework-behavior/http-router-schema.dl | 149 + .../framework-behavior/http-rpc-client.dl | 265 ++ .../engine/resolution/dynamic-import.dl | 61 + .../engine/resolution/value-flow.dl | 9 + graph/typescript/souffle/decls_all.dl | 16 + graph/typescript/souffle/export_manifest.tsv | 15 + .../skills/axiomcode/scripts/ax_blocks.py | 7 +- .../axiomcode/scripts/axiomcode-context | 394 +- .../skills/axiomcode/scripts/axiomcode-impact | 250 + .../skills/axiomcode/scripts/axiomcode-path | 12 + .../skills/axiomcode/scripts/dl/impact.dl | 24 + .../skills/axiomcode/scripts/graph_sql.py | 3 + 717 files changed, 25021 insertions(+), 21 deletions(-) create mode 100644 graph/javascript/engine/config-resolution/http-router-apps.dl create mode 100644 graph/javascript/engine/config-resolution/http-router-routes.dl create mode 100644 graph/javascript/engine/config-resolution/knobs.dl create mode 100644 graph/javascript/engine/framework-behavior/http-router-chain.dl create mode 100644 graph/javascript/engine/framework-behavior/http-rpc-client.dl create mode 100644 graph/test/javascript/hono/.gitignore create mode 100644 graph/test/javascript/hono/cases/js/bun-websocket-destructure/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/bun-websocket-destructure/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/cjs-basic/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-basic/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/cjs-basic/src/users.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-context/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-context/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/cjs-exports-members/src/app.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-exports-members/src/auth.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-exports-members/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/cjs-negative/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-negative/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/cjs-routes-mount/src/index.cjs create mode 100644 graph/test/javascript/hono/cases/js/cjs-routes-mount/src/users.cjs create mode 100644 graph/test/javascript/hono/cases/js/cjs-websocket-module/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/cjs-websocket-module/src/routes/container.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-websocket-module/src/server.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-websocket-module/src/utils/websocket.js create mode 100644 graph/test/javascript/hono/cases/js/create-middleware-factory/src/app.js create mode 100644 graph/test/javascript/hono/cases/js/create-middleware-factory/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/create-middleware-factory/src/session.js create mode 100644 graph/test/javascript/hono/cases/js/esm-basic/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/esm-basic/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/esm-basic/src/users.mjs create mode 100644 graph/test/javascript/hono/cases/js/esm-context/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/esm-context/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/api.js create mode 100644 graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/client.js create mode 100644 graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/esm-hc-client/src/client.js create mode 100644 graph/test/javascript/hono/cases/js/esm-hc-client/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/esm-hc-client/src/server.js create mode 100644 graph/test/javascript/hono/cases/js/esm-twin-of-cjs-basic/src/index.mjs create mode 100644 graph/test/javascript/hono/cases/js/esm-twin-of-cjs-basic/src/users.mjs create mode 100644 graph/test/javascript/hono/cases/js/negative-js/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/negative-js/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/unresolved-lib-mw/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/unresolved-lib-mw/src/package.json create mode 100644 graph/test/javascript/hono/expected/js/bun-websocket-destructure.entries create mode 100644 graph/test/javascript/hono/expected/js/bun-websocket-destructure.tsv create mode 100644 graph/test/javascript/hono/expected/js/cjs-basic.entries create mode 100644 graph/test/javascript/hono/expected/js/cjs-basic.tsv create mode 100644 graph/test/javascript/hono/expected/js/cjs-context.entries create mode 100644 graph/test/javascript/hono/expected/js/cjs-context.tsv create mode 100644 graph/test/javascript/hono/expected/js/cjs-exports-members.entries create mode 100644 graph/test/javascript/hono/expected/js/cjs-exports-members.tsv create mode 100644 graph/test/javascript/hono/expected/js/cjs-negative.entries create mode 100644 graph/test/javascript/hono/expected/js/cjs-negative.tsv create mode 100644 graph/test/javascript/hono/expected/js/cjs-routes-mount.entries create mode 100644 graph/test/javascript/hono/expected/js/cjs-routes-mount.tsv create mode 100644 graph/test/javascript/hono/expected/js/cjs-websocket-module.entries create mode 100644 graph/test/javascript/hono/expected/js/cjs-websocket-module.tsv create mode 100644 graph/test/javascript/hono/expected/js/create-middleware-factory.entries create mode 100644 graph/test/javascript/hono/expected/js/create-middleware-factory.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-basic.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-basic.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-context.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-context.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-fetch-unknown-base.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-fetch-unknown-base.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-hc-client.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-hc-client.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-twin-of-cjs-basic.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-twin-of-cjs-basic.tsv create mode 100644 graph/test/javascript/hono/expected/js/negative-js.entries create mode 100644 graph/test/javascript/hono/expected/js/negative-js.tsv create mode 100644 graph/test/javascript/hono/expected/js/unresolved-lib-mw.entries create mode 100644 graph/test/javascript/hono/expected/js/unresolved-lib-mw.tsv create mode 100755 graph/test/javascript/hono/run-tests.sh create mode 100644 graph/test/typescript/hono/.gitignore create mode 100644 graph/test/typescript/hono/cases/compose/abstract-strategy-mount/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/search/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/search/routes/api.ts create mode 100644 graph/test/typescript/hono/cases/compose/async-wrapper-mount/src/main.ts create mode 100644 graph/test/typescript/hono/cases/compose/async-wrapper-mount/src/wrap.ts create mode 100644 graph/test/typescript/hono/cases/compose/basepath-group/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/basepath-sub-mounted/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/builder-called-twice/src/main.ts create mode 100644 graph/test/typescript/hono/cases/compose/builder-called-twice/src/routes.ts create mode 100644 graph/test/typescript/hono/cases/compose/builder-called-twice/src/users.ts create mode 100644 graph/test/typescript/hono/cases/compose/declarative-routes-const/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/app.ts create mode 100644 graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/mount.test.ts create mode 100644 graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/mount.ts create mode 100644 graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/plugins/inbox.ts create mode 100644 graph/test/typescript/hono/cases/compose/discarded-basepath/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/dynamic-prefix/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/factory-basepath/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/factory-fn/src/app.ts create mode 100644 graph/test/typescript/hono/cases/compose/factory-fn/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/field-app-class/src/routes/health.ts create mode 100644 graph/test/typescript/hono/cases/compose/field-app-class/src/routes/users.ts create mode 100644 graph/test/typescript/hono/cases/compose/field-app-class/src/server.ts create mode 100644 graph/test/typescript/hono/cases/compose/filtered-instance-loop/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/foreign-mount/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/getter-prefix-typed-holder/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-module-list/src/env.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-module-list/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-module-list/src/lazy/reports.page.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/orders/helpers.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/orders/orders.index.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/users/users.index.ts create mode 100644 graph/test/typescript/hono/cases/compose/late-sub-registration/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/let-rebase-conditional/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/let-rebase-literal/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/lib-dirs/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/lib-dirs/src/lib/app.ts create mode 100644 graph/test/typescript/hono/cases/compose/lib-dirs/src/libs/users/routes.ts create mode 100644 graph/test/typescript/hono/cases/compose/literal-prefix-method/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/loop-mount-method-fallback/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/members.ts create mode 100644 graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/routes.ts create mode 100644 graph/test/typescript/hono/cases/compose/mount-arg-never-root/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/mount-chain/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/nested-three-deep/src/api.ts create mode 100644 graph/test/typescript/hono/cases/compose/nested-three-deep/src/comments.ts create mode 100644 graph/test/typescript/hono/cases/compose/nested-three-deep/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/nested-three-deep/src/posts.ts create mode 100644 graph/test/typescript/hono/cases/compose/opaque-module-list/src/routes/billing.ts create mode 100644 graph/test/typescript/hono/cases/compose/opaque-module-list/src/routes/status.ts create mode 100644 graph/test/typescript/hono/cases/compose/opaque-module-list/src/server.ts create mode 100644 graph/test/typescript/hono/cases/compose/opaque-module-list/src/shared.ts create mode 100644 graph/test/typescript/hono/cases/compose/opaque-module-list/src/worker.ts create mode 100644 graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/cli.ts create mode 100644 graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/saver.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-helper-list/src/app.test.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-helper-list/src/app.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-helper-list/src/mount.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-helper-list/src/plugins/magic-link.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-helper-list/src/sort.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/app.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/plugins/legacy-export.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/plugins/magic-link.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-register-method/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-register-method/src/plugins/media.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-register-method/src/plugins/variables.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-register-method/src/sdk.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins/billing.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins/search.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/server.ts create mode 100644 graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/test/search.test.ts create mode 100644 graph/test/typescript/hono/cases/compose/rebased-holder-getter/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/rebased-holder-mount/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/rebased-holder-one-partial/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/rebased-two-mounts/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/reexport-barrel/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/reexport-barrel/src/routes/books.ts create mode 100644 graph/test/typescript/hono/cases/compose/reexport-barrel/src/routes/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/register-fn/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/register-fn/src/monitors.ts create mode 100644 graph/test/typescript/hono/cases/compose/register-fn/src/users.ts create mode 100644 graph/test/typescript/hono/cases/compose/register-loop-plugins/src/plugins.ts create mode 100644 graph/test/typescript/hono/cases/compose/register-loop-plugins/src/register.ts create mode 100644 graph/test/typescript/hono/cases/compose/registered-by/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/registered-by/src/monitors.ts create mode 100644 graph/test/typescript/hono/cases/compose/route-and-partial/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/route-cross-module/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/route-cross-module/src/users.ts create mode 100644 graph/test/typescript/hono/cases/compose/route-root/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/same-sub-twice/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/self-basepath-field/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/shared-factory-filtered-loop/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/sync-wrapper-mount/src/main.ts create mode 100644 graph/test/typescript/hono/cases/compose/sync-wrapper-mount/src/wrap.ts create mode 100644 graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/__tests__/define-plugin.test.ts create mode 100644 graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/__tests__/mount.test.ts create mode 100644 graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/app.ts create mode 100644 graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/plugins/audit.ts create mode 100644 graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/plugins/tenants.ts create mode 100644 graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/sdk.ts create mode 100644 graph/test/typescript/hono/cases/compose/typed-property-receiver/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/uncalled-start-server/src/api.ts create mode 100644 graph/test/typescript/hono/cases/compose/uncalled-start-server/src/server.ts create mode 100644 graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/apps/api/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/apps/api/tsconfig.json create mode 100644 graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/adapters/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/package.json create mode 100644 graph/test/typescript/hono/cases/context/chain-of-setters/src/index.ts create mode 100644 graph/test/typescript/hono/cases/context/cross-module/src/auth.ts create mode 100644 graph/test/typescript/hono/cases/context/cross-module/src/index.ts create mode 100644 graph/test/typescript/hono/cases/context/cross-module/src/routes.ts create mode 100644 graph/test/typescript/hono/cases/context/set-get/src/index.ts create mode 100644 graph/test/typescript/hono/cases/deno/denoland-url/src/deno.json create mode 100644 graph/test/typescript/hono/cases/deno/denoland-url/src/main.ts create mode 100644 graph/test/typescript/hono/cases/deno/import-map-deno-json/src/deno.json create mode 100644 graph/test/typescript/hono/cases/deno/import-map-deno-json/src/main.ts create mode 100644 graph/test/typescript/hono/cases/deno/import-map-deno-json/src/routes/users.ts create mode 100644 graph/test/typescript/hono/cases/deno/import-map-file/src/import_map.json create mode 100644 graph/test/typescript/hono/cases/deno/import-map-file/src/main.ts create mode 100644 graph/test/typescript/hono/cases/deno/import-map-jsonc/src/deno.jsonc create mode 100644 graph/test/typescript/hono/cases/deno/import-map-jsonc/src/main.ts create mode 100644 graph/test/typescript/hono/cases/deno/jsr-specifier/src/deno.json create mode 100644 graph/test/typescript/hono/cases/deno/jsr-specifier/src/main.ts create mode 100644 graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/deno.json create mode 100644 graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/main.ts create mode 100644 graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/routes.ts create mode 100644 graph/test/typescript/hono/cases/deno/npm-specifier/src/deno.json create mode 100644 graph/test/typescript/hono/cases/deno/npm-specifier/src/main.ts create mode 100644 graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/index.ts create mode 100644 graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/lib/app.ts create mode 100644 graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/tsconfig.json create mode 100644 graph/test/typescript/hono/cases/entry/app-request-test/src/app.test.ts create mode 100644 graph/test/typescript/hono/cases/entry/app-request-test/src/app.ts create mode 100644 graph/test/typescript/hono/cases/entry/deno-bun/src/index.ts create mode 100644 graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/admin.ts create mode 100644 graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/skills.test.ts create mode 100644 graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/skills.ts create mode 100644 graph/test/typescript/hono/cases/entry/fetch-object/src/index.ts create mode 100644 graph/test/typescript/hono/cases/entry/fixture-prop-request/src/app.test.ts create mode 100644 graph/test/typescript/hono/cases/entry/fixture-prop-request/src/app.ts create mode 100644 graph/test/typescript/hono/cases/entry/jsx-render/src/components.tsx create mode 100644 graph/test/typescript/hono/cases/entry/jsx-render/src/index.tsx create mode 100644 graph/test/typescript/hono/cases/entry/node-serve/src/index.ts create mode 100644 graph/test/typescript/hono/cases/entry/notfound-onerror/src/index.ts create mode 100644 graph/test/typescript/hono/cases/entry/request-object-and-fetch/src/app.test.ts create mode 100644 graph/test/typescript/hono/cases/entry/request-object-and-fetch/src/app.ts create mode 100644 graph/test/typescript/hono/cases/entry/test-built-app/src/app.test.ts create mode 100644 graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/middleware/audit.ts create mode 100644 graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/middleware/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/builtin/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/combine/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/conditional-lib-mw/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/const-library-mw/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/detached-factory-method/src/factory.ts create mode 100644 graph/test/typescript/hono/cases/middleware/detached-factory-method/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/factory-fn/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/factory-fn/src/mw.ts create mode 100644 graph/test/typescript/hono/cases/middleware/factory-handlers/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/factory-two-returns/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/helper-app-mounted-thrice/src/routes.test.ts create mode 100644 graph/test/typescript/hono/cases/middleware/helper-app-mounted-thrice/src/routes.ts create mode 100644 graph/test/typescript/hono/cases/middleware/if-else-unequal-mw/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/lib-mw-options/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/loop-use/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/scope.ts create mode 100644 graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/session.ts create mode 100644 graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/partial-sub-app-chain/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/runtime-list-except-ternary/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/scoped-use/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/spread-after-mount/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/spread-call-result/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/sub-app-middleware/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/use-order/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/validator/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/verb-next/src/index.ts create mode 100644 graph/test/typescript/hono/cases/negative/context-lookalike/src/index.ts create mode 100644 graph/test/typescript/hono/cases/negative/foreign-fetch/src/index.ts create mode 100644 graph/test/typescript/hono/cases/negative/http-client/src/client.ts create mode 100644 graph/test/typescript/hono/cases/negative/http-client/src/index.ts create mode 100644 graph/test/typescript/hono/cases/negative/local-hono-class/src/index.ts create mode 100644 graph/test/typescript/hono/cases/negative/map-get/src/index.ts create mode 100644 graph/test/typescript/hono/cases/negative/other-router/src/index.ts create mode 100644 graph/test/typescript/hono/cases/negative/other-router/src/minirouter.ts create mode 100644 graph/test/typescript/hono/cases/negative/request-lookalike/src/index.ts create mode 100644 graph/test/typescript/hono/cases/negative/zod-openapi-meta/src/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/create-route/src/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/create-route/src/routes.ts create mode 100644 graph/test/typescript/hono/cases/openapi/namespace-handlers/src/auth.handlers.ts create mode 100644 graph/test/typescript/hono/cases/openapi/namespace-handlers/src/auth.routes.ts create mode 100644 graph/test/typescript/hono/cases/openapi/namespace-handlers/src/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/response-helper/src/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/route-middleware/src/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/spread-error-responses/src/errors.ts create mode 100644 graph/test/typescript/hono/cases/openapi/spread-error-responses/src/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/status-key-helpers/src/handlers.ts create mode 100644 graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/json-body-required.ts create mode 100644 graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/json-body.ts create mode 100644 graph/test/typescript/hono/cases/openapi/status-key-helpers/src/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/status-key-helpers/src/routes.ts create mode 100644 graph/test/typescript/hono/cases/openapi/status-key-helpers/src/status.ts create mode 100644 graph/test/typescript/hono/cases/routing/all-and-any/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/conditional-loop/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/const-paths/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/const-paths/src/paths.ts create mode 100644 graph/test/typescript/hono/cases/routing/dynamic-path/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/every-verb/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/library-handler/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/mts-source/src/server.mts create mode 100644 graph/test/typescript/hono/cases/routing/multi-handler/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/named-handlers/src/handlers.ts create mode 100644 graph/test/typescript/hono/cases/routing/named-handlers/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/on-arrays/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/partial-route-scope/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/path-shapes/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/pathless-chain/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/scope-table/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/strict-false/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/websocket-project-module/src/chat.ts create mode 100644 graph/test/typescript/hono/cases/routing/websocket-project-module/src/index.ts create mode 100644 graph/test/typescript/hono/cases/routing/websocket-project-module/src/tsconfig.json create mode 100644 graph/test/typescript/hono/cases/routing/websocket-project-module/src/utils/websocket.ts create mode 100644 graph/test/typescript/hono/cases/routing/websocket-sse/src/index.ts create mode 100644 graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/client.ts create mode 100644 graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/server.test.ts create mode 100644 graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/server.ts create mode 100644 graph/test/typescript/hono/cases/rpc/fetch-unknown-base/src/api.ts create mode 100644 graph/test/typescript/hono/cases/rpc/fetch-unknown-base/src/cli.ts create mode 100644 graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/discovery.ts create mode 100644 graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/server.test.ts create mode 100644 graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/server.ts create mode 100644 graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/skills-client.ts create mode 100644 graph/test/typescript/hono/cases/rpc/hc-basic/src/client.ts create mode 100644 graph/test/typescript/hono/cases/rpc/hc-basic/src/server.ts create mode 100644 graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/api.ts create mode 100644 graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/main.ts create mode 100644 graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/probe.ts create mode 100644 graph/test/typescript/hono/cases/rpc/mixed-segment-hole/src/index.ts create mode 100644 graph/test/typescript/hono/cases/rpc/monorepo-client/src/server/app.ts create mode 100644 graph/test/typescript/hono/cases/rpc/monorepo-client/src/web/orders.ts create mode 100644 graph/test/typescript/hono/cases/rpc/query-string/src/app.test.ts create mode 100644 graph/test/typescript/hono/cases/rpc/query-string/src/app.ts create mode 100644 graph/test/typescript/hono/cases/rpc/reassigned-let/src/api.test.ts create mode 100644 graph/test/typescript/hono/cases/rpc/reassigned-let/src/api.ts create mode 100644 graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/api.test.ts create mode 100644 graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/api.ts create mode 100644 graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/index.ts create mode 100644 graph/test/typescript/hono/cases/rpc/single-label-host/src/index.ts create mode 100644 graph/test/typescript/hono/cases/rpc/template-holes/src/app.test.ts create mode 100644 graph/test/typescript/hono/cases/rpc/template-holes/src/app.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/apps/api/package.json create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/apps/api/src/index.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/package.json create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/src/adapters/hono.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/src/index.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/lib/client.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/lib/v2/index.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/package.json create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/server/package.json create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/server/src/app.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/admin/package.json create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/admin/routes.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/server/package.json create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/server/src/index.ts create mode 100644 graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/web/client.ts create mode 100644 graph/test/typescript/hono/cases/scale/deep-mount/budget create mode 100644 graph/test/typescript/hono/cases/scale/deep-mount/src/index.ts create mode 100644 graph/test/typescript/hono/cases/scale/mw-200/budget create mode 100644 graph/test/typescript/hono/cases/scale/mw-200/src/index.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/budget create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/index.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod0.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod1.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod10.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod11.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod12.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod13.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod14.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod15.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod16.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod17.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod18.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod19.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod2.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod3.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod4.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod5.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod6.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod7.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod8.ts create mode 100644 graph/test/typescript/hono/cases/scale/routes-2k/src/mod9.ts create mode 100644 graph/test/typescript/hono/cases/zod/composition/src/index.ts create mode 100644 graph/test/typescript/hono/cases/zod/decl-forms/src/index.ts create mode 100644 graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/index.ts create mode 100644 graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/query.ts create mode 100644 graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/user.ts create mode 100644 graph/test/typescript/hono/cases/zod/no-route/src/forms.ts create mode 100644 graph/test/typescript/hono/cases/zod/specifiers/src/index.ts create mode 100644 graph/test/typescript/hono/cases/zod/valid-targets/src/index.ts create mode 100644 graph/test/typescript/hono/expected/compose/abstract-strategy-mount.entries create mode 100644 graph/test/typescript/hono/expected/compose/abstract-strategy-mount.tsv create mode 100644 graph/test/typescript/hono/expected/compose/aliased-reexport-mount.entries create mode 100644 graph/test/typescript/hono/expected/compose/aliased-reexport-mount.tsv create mode 100644 graph/test/typescript/hono/expected/compose/async-wrapper-mount.entries create mode 100644 graph/test/typescript/hono/expected/compose/async-wrapper-mount.tsv create mode 100644 graph/test/typescript/hono/expected/compose/basepath-group.entries create mode 100644 graph/test/typescript/hono/expected/compose/basepath-group.tsv create mode 100644 graph/test/typescript/hono/expected/compose/basepath-sub-mounted.entries create mode 100644 graph/test/typescript/hono/expected/compose/basepath-sub-mounted.tsv create mode 100644 graph/test/typescript/hono/expected/compose/builder-called-twice.entries create mode 100644 graph/test/typescript/hono/expected/compose/builder-called-twice.tsv create mode 100644 graph/test/typescript/hono/expected/compose/declarative-routes-const.entries create mode 100644 graph/test/typescript/hono/expected/compose/declarative-routes-const.tsv create mode 100644 graph/test/typescript/hono/expected/compose/declarative-routes-derived.entries create mode 100644 graph/test/typescript/hono/expected/compose/declarative-routes-derived.tsv create mode 100644 graph/test/typescript/hono/expected/compose/discarded-basepath.entries create mode 100644 graph/test/typescript/hono/expected/compose/discarded-basepath.tsv create mode 100644 graph/test/typescript/hono/expected/compose/dynamic-prefix.entries create mode 100644 graph/test/typescript/hono/expected/compose/dynamic-prefix.tsv create mode 100644 graph/test/typescript/hono/expected/compose/factory-basepath.entries create mode 100644 graph/test/typescript/hono/expected/compose/factory-basepath.tsv create mode 100644 graph/test/typescript/hono/expected/compose/factory-fn.entries create mode 100644 graph/test/typescript/hono/expected/compose/factory-fn.tsv create mode 100644 graph/test/typescript/hono/expected/compose/field-app-class.entries create mode 100644 graph/test/typescript/hono/expected/compose/field-app-class.tsv create mode 100644 graph/test/typescript/hono/expected/compose/filtered-instance-loop.entries create mode 100644 graph/test/typescript/hono/expected/compose/filtered-instance-loop.tsv create mode 100644 graph/test/typescript/hono/expected/compose/foreign-mount.entries create mode 100644 graph/test/typescript/hono/expected/compose/foreign-mount.tsv create mode 100644 graph/test/typescript/hono/expected/compose/getter-prefix-typed-holder.entries create mode 100644 graph/test/typescript/hono/expected/compose/getter-prefix-typed-holder.tsv create mode 100644 graph/test/typescript/hono/expected/compose/glob-module-list.entries create mode 100644 graph/test/typescript/hono/expected/compose/glob-module-list.tsv create mode 100644 graph/test/typescript/hono/expected/compose/late-sub-registration.entries create mode 100644 graph/test/typescript/hono/expected/compose/late-sub-registration.tsv create mode 100644 graph/test/typescript/hono/expected/compose/let-rebase-conditional.entries create mode 100644 graph/test/typescript/hono/expected/compose/let-rebase-conditional.tsv create mode 100644 graph/test/typescript/hono/expected/compose/let-rebase-literal.entries create mode 100644 graph/test/typescript/hono/expected/compose/let-rebase-literal.tsv create mode 100644 graph/test/typescript/hono/expected/compose/lib-dirs.entries create mode 100644 graph/test/typescript/hono/expected/compose/lib-dirs.tsv create mode 100644 graph/test/typescript/hono/expected/compose/literal-prefix-method.entries create mode 100644 graph/test/typescript/hono/expected/compose/literal-prefix-method.tsv create mode 100644 graph/test/typescript/hono/expected/compose/loop-mount-method-fallback.entries create mode 100644 graph/test/typescript/hono/expected/compose/loop-mount-method-fallback.tsv create mode 100644 graph/test/typescript/hono/expected/compose/method-prefix-const-list.entries create mode 100644 graph/test/typescript/hono/expected/compose/method-prefix-const-list.tsv create mode 100644 graph/test/typescript/hono/expected/compose/mount-arg-never-root.entries create mode 100644 graph/test/typescript/hono/expected/compose/mount-arg-never-root.tsv create mode 100644 graph/test/typescript/hono/expected/compose/mount-chain.entries create mode 100644 graph/test/typescript/hono/expected/compose/mount-chain.tsv create mode 100644 graph/test/typescript/hono/expected/compose/nested-three-deep.entries create mode 100644 graph/test/typescript/hono/expected/compose/nested-three-deep.tsv create mode 100644 graph/test/typescript/hono/expected/compose/opaque-module-list.entries create mode 100644 graph/test/typescript/hono/expected/compose/opaque-module-list.tsv create mode 100644 graph/test/typescript/hono/expected/compose/oracle-eval-cycle.entries create mode 100644 graph/test/typescript/hono/expected/compose/oracle-eval-cycle.tsv create mode 100644 graph/test/typescript/hono/expected/compose/plugin-helper-list.entries create mode 100644 graph/test/typescript/hono/expected/compose/plugin-helper-list.tsv create mode 100644 graph/test/typescript/hono/expected/compose/plugin-instance-never-mounted.entries create mode 100644 graph/test/typescript/hono/expected/compose/plugin-instance-never-mounted.tsv create mode 100644 graph/test/typescript/hono/expected/compose/plugin-register-method.entries create mode 100644 graph/test/typescript/hono/expected/compose/plugin-register-method.tsv create mode 100644 graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.entries create mode 100644 graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.tsv create mode 100644 graph/test/typescript/hono/expected/compose/rebased-holder-getter.entries create mode 100644 graph/test/typescript/hono/expected/compose/rebased-holder-getter.tsv create mode 100644 graph/test/typescript/hono/expected/compose/rebased-holder-mount.entries create mode 100644 graph/test/typescript/hono/expected/compose/rebased-holder-mount.tsv create mode 100644 graph/test/typescript/hono/expected/compose/rebased-holder-one-partial.entries create mode 100644 graph/test/typescript/hono/expected/compose/rebased-holder-one-partial.tsv create mode 100644 graph/test/typescript/hono/expected/compose/rebased-two-mounts.entries create mode 100644 graph/test/typescript/hono/expected/compose/rebased-two-mounts.tsv create mode 100644 graph/test/typescript/hono/expected/compose/reexport-barrel.entries create mode 100644 graph/test/typescript/hono/expected/compose/reexport-barrel.tsv create mode 100644 graph/test/typescript/hono/expected/compose/register-fn.entries create mode 100644 graph/test/typescript/hono/expected/compose/register-fn.tsv create mode 100644 graph/test/typescript/hono/expected/compose/register-loop-plugins.entries create mode 100644 graph/test/typescript/hono/expected/compose/register-loop-plugins.tsv create mode 100644 graph/test/typescript/hono/expected/compose/registered-by.entries create mode 100644 graph/test/typescript/hono/expected/compose/registered-by.tsv create mode 100644 graph/test/typescript/hono/expected/compose/route-and-partial.entries create mode 100644 graph/test/typescript/hono/expected/compose/route-and-partial.tsv create mode 100644 graph/test/typescript/hono/expected/compose/route-cross-module.entries create mode 100644 graph/test/typescript/hono/expected/compose/route-cross-module.tsv create mode 100644 graph/test/typescript/hono/expected/compose/route-root.entries create mode 100644 graph/test/typescript/hono/expected/compose/route-root.tsv create mode 100644 graph/test/typescript/hono/expected/compose/same-sub-twice.entries create mode 100644 graph/test/typescript/hono/expected/compose/same-sub-twice.tsv create mode 100644 graph/test/typescript/hono/expected/compose/self-basepath-field.entries create mode 100644 graph/test/typescript/hono/expected/compose/self-basepath-field.tsv create mode 100644 graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.entries create mode 100644 graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.tsv create mode 100644 graph/test/typescript/hono/expected/compose/sync-wrapper-mount.entries create mode 100644 graph/test/typescript/hono/expected/compose/sync-wrapper-mount.tsv create mode 100644 graph/test/typescript/hono/expected/compose/test-plugin-own-app.entries create mode 100644 graph/test/typescript/hono/expected/compose/test-plugin-own-app.tsv create mode 100644 graph/test/typescript/hono/expected/compose/typed-property-receiver.entries create mode 100644 graph/test/typescript/hono/expected/compose/typed-property-receiver.tsv create mode 100644 graph/test/typescript/hono/expected/compose/uncalled-start-server.entries create mode 100644 graph/test/typescript/hono/expected/compose/uncalled-start-server.tsv create mode 100644 graph/test/typescript/hono/expected/compose/workspace-unlisted-subpath-bundler.entries create mode 100644 graph/test/typescript/hono/expected/compose/workspace-unlisted-subpath-bundler.tsv create mode 100644 graph/test/typescript/hono/expected/context/chain-of-setters.entries create mode 100644 graph/test/typescript/hono/expected/context/chain-of-setters.tsv create mode 100644 graph/test/typescript/hono/expected/context/cross-module.entries create mode 100644 graph/test/typescript/hono/expected/context/cross-module.tsv create mode 100644 graph/test/typescript/hono/expected/context/set-get.entries create mode 100644 graph/test/typescript/hono/expected/context/set-get.tsv create mode 100644 graph/test/typescript/hono/expected/deno/denoland-url.entries create mode 100644 graph/test/typescript/hono/expected/deno/denoland-url.tsv create mode 100644 graph/test/typescript/hono/expected/deno/import-map-deno-json.entries create mode 100644 graph/test/typescript/hono/expected/deno/import-map-deno-json.tsv create mode 100644 graph/test/typescript/hono/expected/deno/import-map-file.entries create mode 100644 graph/test/typescript/hono/expected/deno/import-map-file.tsv create mode 100644 graph/test/typescript/hono/expected/deno/import-map-jsonc.entries create mode 100644 graph/test/typescript/hono/expected/deno/import-map-jsonc.tsv create mode 100644 graph/test/typescript/hono/expected/deno/jsr-specifier.entries create mode 100644 graph/test/typescript/hono/expected/deno/jsr-specifier.tsv create mode 100644 graph/test/typescript/hono/expected/deno/jsr-static-method.entries create mode 100644 graph/test/typescript/hono/expected/deno/jsr-static-method.tsv create mode 100644 graph/test/typescript/hono/expected/deno/npm-specifier.entries create mode 100644 graph/test/typescript/hono/expected/deno/npm-specifier.tsv create mode 100644 graph/test/typescript/hono/expected/entry/aliased-app-fetch-wrapper.entries create mode 100644 graph/test/typescript/hono/expected/entry/aliased-app-fetch-wrapper.tsv create mode 100644 graph/test/typescript/hono/expected/entry/app-request-test.entries create mode 100644 graph/test/typescript/hono/expected/entry/app-request-test.tsv create mode 100644 graph/test/typescript/hono/expected/entry/deno-bun.entries create mode 100644 graph/test/typescript/hono/expected/entry/deno-bun.tsv create mode 100644 graph/test/typescript/hono/expected/entry/dynamic-import-test-app.entries create mode 100644 graph/test/typescript/hono/expected/entry/dynamic-import-test-app.tsv create mode 100644 graph/test/typescript/hono/expected/entry/fetch-object.entries create mode 100644 graph/test/typescript/hono/expected/entry/fetch-object.tsv create mode 100644 graph/test/typescript/hono/expected/entry/fixture-prop-request.entries create mode 100644 graph/test/typescript/hono/expected/entry/fixture-prop-request.tsv create mode 100644 graph/test/typescript/hono/expected/entry/jsx-render.entries create mode 100644 graph/test/typescript/hono/expected/entry/jsx-render.tsv create mode 100644 graph/test/typescript/hono/expected/entry/node-serve.entries create mode 100644 graph/test/typescript/hono/expected/entry/node-serve.tsv create mode 100644 graph/test/typescript/hono/expected/entry/notfound-onerror.entries create mode 100644 graph/test/typescript/hono/expected/entry/notfound-onerror.tsv create mode 100644 graph/test/typescript/hono/expected/entry/request-object-and-fetch.entries create mode 100644 graph/test/typescript/hono/expected/entry/request-object-and-fetch.tsv create mode 100644 graph/test/typescript/hono/expected/entry/test-built-app.entries create mode 100644 graph/test/typescript/hono/expected/entry/test-built-app.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/barrel-library-mw.entries create mode 100644 graph/test/typescript/hono/expected/middleware/barrel-library-mw.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/builtin.entries create mode 100644 graph/test/typescript/hono/expected/middleware/builtin.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/combine.entries create mode 100644 graph/test/typescript/hono/expected/middleware/combine.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/conditional-lib-mw.entries create mode 100644 graph/test/typescript/hono/expected/middleware/conditional-lib-mw.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/const-library-mw.entries create mode 100644 graph/test/typescript/hono/expected/middleware/const-library-mw.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/detached-factory-method.entries create mode 100644 graph/test/typescript/hono/expected/middleware/detached-factory-method.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/factory-fn.entries create mode 100644 graph/test/typescript/hono/expected/middleware/factory-fn.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/factory-handlers.entries create mode 100644 graph/test/typescript/hono/expected/middleware/factory-handlers.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/factory-two-returns.entries create mode 100644 graph/test/typescript/hono/expected/middleware/factory-two-returns.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.entries create mode 100644 graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/if-else-unequal-mw.entries create mode 100644 graph/test/typescript/hono/expected/middleware/if-else-unequal-mw.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/lib-mw-options.entries create mode 100644 graph/test/typescript/hono/expected/middleware/lib-mw-options.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/loop-use.entries create mode 100644 graph/test/typescript/hono/expected/middleware/loop-use.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/mw-from-namespace-field.entries create mode 100644 graph/test/typescript/hono/expected/middleware/mw-from-namespace-field.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/partial-sub-app-chain.entries create mode 100644 graph/test/typescript/hono/expected/middleware/partial-sub-app-chain.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/runtime-list-except-ternary.entries create mode 100644 graph/test/typescript/hono/expected/middleware/runtime-list-except-ternary.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/scoped-use.entries create mode 100644 graph/test/typescript/hono/expected/middleware/scoped-use.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/spread-after-mount.entries create mode 100644 graph/test/typescript/hono/expected/middleware/spread-after-mount.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/spread-call-result.entries create mode 100644 graph/test/typescript/hono/expected/middleware/spread-call-result.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/sub-app-middleware.entries create mode 100644 graph/test/typescript/hono/expected/middleware/sub-app-middleware.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/use-order.entries create mode 100644 graph/test/typescript/hono/expected/middleware/use-order.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/validator.entries create mode 100644 graph/test/typescript/hono/expected/middleware/validator.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/verb-next.entries create mode 100644 graph/test/typescript/hono/expected/middleware/verb-next.tsv create mode 100644 graph/test/typescript/hono/expected/negative/context-lookalike.entries create mode 100644 graph/test/typescript/hono/expected/negative/context-lookalike.tsv create mode 100644 graph/test/typescript/hono/expected/negative/foreign-fetch.entries create mode 100644 graph/test/typescript/hono/expected/negative/foreign-fetch.tsv create mode 100644 graph/test/typescript/hono/expected/negative/http-client.entries create mode 100644 graph/test/typescript/hono/expected/negative/http-client.tsv create mode 100644 graph/test/typescript/hono/expected/negative/local-hono-class.entries create mode 100644 graph/test/typescript/hono/expected/negative/local-hono-class.tsv create mode 100644 graph/test/typescript/hono/expected/negative/map-get.entries create mode 100644 graph/test/typescript/hono/expected/negative/map-get.tsv create mode 100644 graph/test/typescript/hono/expected/negative/other-router.entries create mode 100644 graph/test/typescript/hono/expected/negative/other-router.tsv create mode 100644 graph/test/typescript/hono/expected/negative/request-lookalike.entries create mode 100644 graph/test/typescript/hono/expected/negative/request-lookalike.tsv create mode 100644 graph/test/typescript/hono/expected/negative/zod-openapi-meta.entries create mode 100644 graph/test/typescript/hono/expected/negative/zod-openapi-meta.tsv create mode 100644 graph/test/typescript/hono/expected/openapi/create-route.entries create mode 100644 graph/test/typescript/hono/expected/openapi/create-route.tsv create mode 100644 graph/test/typescript/hono/expected/openapi/namespace-handlers.entries create mode 100644 graph/test/typescript/hono/expected/openapi/namespace-handlers.tsv create mode 100644 graph/test/typescript/hono/expected/openapi/response-helper.entries create mode 100644 graph/test/typescript/hono/expected/openapi/response-helper.tsv create mode 100644 graph/test/typescript/hono/expected/openapi/route-middleware.entries create mode 100644 graph/test/typescript/hono/expected/openapi/route-middleware.tsv create mode 100644 graph/test/typescript/hono/expected/openapi/spread-error-responses.entries create mode 100644 graph/test/typescript/hono/expected/openapi/spread-error-responses.tsv create mode 100644 graph/test/typescript/hono/expected/openapi/status-key-helpers.entries create mode 100644 graph/test/typescript/hono/expected/openapi/status-key-helpers.tsv create mode 100644 graph/test/typescript/hono/expected/routing/all-and-any.entries create mode 100644 graph/test/typescript/hono/expected/routing/all-and-any.tsv create mode 100644 graph/test/typescript/hono/expected/routing/conditional-loop.entries create mode 100644 graph/test/typescript/hono/expected/routing/conditional-loop.tsv create mode 100644 graph/test/typescript/hono/expected/routing/const-paths.entries create mode 100644 graph/test/typescript/hono/expected/routing/const-paths.tsv create mode 100644 graph/test/typescript/hono/expected/routing/dynamic-path.entries create mode 100644 graph/test/typescript/hono/expected/routing/dynamic-path.tsv create mode 100644 graph/test/typescript/hono/expected/routing/every-verb.entries create mode 100644 graph/test/typescript/hono/expected/routing/every-verb.tsv create mode 100644 graph/test/typescript/hono/expected/routing/library-handler.entries create mode 100644 graph/test/typescript/hono/expected/routing/library-handler.tsv create mode 100644 graph/test/typescript/hono/expected/routing/mts-source.entries create mode 100644 graph/test/typescript/hono/expected/routing/mts-source.tsv create mode 100644 graph/test/typescript/hono/expected/routing/multi-handler.entries create mode 100644 graph/test/typescript/hono/expected/routing/multi-handler.tsv create mode 100644 graph/test/typescript/hono/expected/routing/named-handlers.entries create mode 100644 graph/test/typescript/hono/expected/routing/named-handlers.tsv create mode 100644 graph/test/typescript/hono/expected/routing/on-arrays.entries create mode 100644 graph/test/typescript/hono/expected/routing/on-arrays.tsv create mode 100644 graph/test/typescript/hono/expected/routing/partial-route-scope.entries create mode 100644 graph/test/typescript/hono/expected/routing/partial-route-scope.tsv create mode 100644 graph/test/typescript/hono/expected/routing/path-shapes.entries create mode 100644 graph/test/typescript/hono/expected/routing/path-shapes.tsv create mode 100644 graph/test/typescript/hono/expected/routing/pathless-chain.entries create mode 100644 graph/test/typescript/hono/expected/routing/pathless-chain.tsv create mode 100644 graph/test/typescript/hono/expected/routing/scope-table.entries create mode 100644 graph/test/typescript/hono/expected/routing/scope-table.tsv create mode 100644 graph/test/typescript/hono/expected/routing/strict-false.entries create mode 100644 graph/test/typescript/hono/expected/routing/strict-false.tsv create mode 100644 graph/test/typescript/hono/expected/routing/websocket-project-module.entries create mode 100644 graph/test/typescript/hono/expected/routing/websocket-project-module.tsv create mode 100644 graph/test/typescript/hono/expected/routing/websocket-sse.entries create mode 100644 graph/test/typescript/hono/expected/routing/websocket-sse.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/base-unknown-implausible.entries create mode 100644 graph/test/typescript/hono/expected/rpc/base-unknown-implausible.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/fetch-unknown-base.entries create mode 100644 graph/test/typescript/hono/expected/rpc/fetch-unknown-base.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/fetch-verb-parameter.entries create mode 100644 graph/test/typescript/hono/expected/rpc/fetch-verb-parameter.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/hc-basic.entries create mode 100644 graph/test/typescript/hono/expected/rpc/hc-basic.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/local-fetch-full-path.entries create mode 100644 graph/test/typescript/hono/expected/rpc/local-fetch-full-path.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/mixed-segment-hole.entries create mode 100644 graph/test/typescript/hono/expected/rpc/mixed-segment-hole.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/monorepo-client.entries create mode 100644 graph/test/typescript/hono/expected/rpc/monorepo-client.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/query-string.entries create mode 100644 graph/test/typescript/hono/expected/rpc/query-string.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/reassigned-let.entries create mode 100644 graph/test/typescript/hono/expected/rpc/reassigned-let.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/request-on-mounted-subapp.entries create mode 100644 graph/test/typescript/hono/expected/rpc/request-on-mounted-subapp.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/single-label-host.entries create mode 100644 graph/test/typescript/hono/expected/rpc/single-label-host.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/template-holes.entries create mode 100644 graph/test/typescript/hono/expected/rpc/template-holes.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/workspace-dist-exports.entries create mode 100644 graph/test/typescript/hono/expected/rpc/workspace-dist-exports.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/workspace-exports.entries create mode 100644 graph/test/typescript/hono/expected/rpc/workspace-exports.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/workspace-package.entries create mode 100644 graph/test/typescript/hono/expected/rpc/workspace-package.tsv create mode 100644 graph/test/typescript/hono/expected/scale/deep-mount.entries create mode 100644 graph/test/typescript/hono/expected/scale/deep-mount.tsv create mode 100644 graph/test/typescript/hono/expected/scale/mw-200.entries create mode 100644 graph/test/typescript/hono/expected/scale/mw-200.tsv create mode 100644 graph/test/typescript/hono/expected/scale/routes-2k.entries create mode 100644 graph/test/typescript/hono/expected/scale/routes-2k.tsv create mode 100644 graph/test/typescript/hono/expected/zod/composition.entries create mode 100644 graph/test/typescript/hono/expected/zod/composition.tsv create mode 100644 graph/test/typescript/hono/expected/zod/decl-forms.entries create mode 100644 graph/test/typescript/hono/expected/zod/decl-forms.tsv create mode 100644 graph/test/typescript/hono/expected/zod/no-route.entries create mode 100644 graph/test/typescript/hono/expected/zod/no-route.tsv create mode 100644 graph/test/typescript/hono/expected/zod/specifiers.entries create mode 100644 graph/test/typescript/hono/expected/zod/specifiers.tsv create mode 100644 graph/test/typescript/hono/expected/zod/valid-targets.entries create mode 100644 graph/test/typescript/hono/expected/zod/valid-targets.tsv create mode 100644 graph/test/typescript/hono/relations.tsv create mode 100755 graph/test/typescript/hono/run-tests.sh create mode 100644 graph/test/typescript/hono/tools/fn-locs.mjs create mode 100644 graph/test/typescript/hono/tools/gen-scale.mjs create mode 100644 graph/test/typescript/hono/tools/hono_compare.py create mode 100755 graph/test/typescript/hono/tools/promote-draft.sh create mode 100644 graph/test/typescript/hono/truth/.gitignore create mode 100644 graph/test/typescript/hono/truth/deno-shims/deno.d.ts create mode 100644 graph/test/typescript/hono/truth/deno-shims/middleware.d.ts create mode 100644 graph/test/typescript/hono/truth/hono-oracle.mjs create mode 100644 graph/test/typescript/hono/truth/package.json create mode 100644 graph/test/typescript/hono/truth/runtime-check.mjs create mode 100644 graph/test/typescript/hono/truth/runtime-hooks.mjs create mode 100644 graph/typescript/engine/config-resolution/http-router-apps.dl create mode 100644 graph/typescript/engine/config-resolution/http-router-routes.dl create mode 100644 graph/typescript/engine/framework-behavior/http-router-chain.dl create mode 100644 graph/typescript/engine/framework-behavior/http-router-schema.dl create mode 100644 graph/typescript/engine/framework-behavior/http-rpc-client.dl create mode 100644 graph/typescript/engine/resolution/dynamic-import.dl diff --git a/graph/bundle/catalog.ts b/graph/bundle/catalog.ts index 9e216e7d6..036262536 100644 --- a/graph/bundle/catalog.ts +++ b/graph/bundle/catalog.ts @@ -24,11 +24,13 @@ export interface ExtRelation { */ export const EXT_QUERIED: ReadonlySet = new Set([ 'bean_def', 'call_runs_edge', 'call_site_unresolved', 'config_affects_method', 'config_binding', 'config_key_ref', - 'config_unresolved', 'ctor_implicit_type', 'decorated_name_target', 'di_edge', 'fn_value_call', 'framework_edge', + 'config_unresolved', 'ctor_implicit_type', 'ctx_key_flow', 'decorated_name_target', 'di_edge', 'fn_value_call', + 'framework_edge', 'framework_undetermined', 'http_mount', 'http_route', 'http_route_partial', 'http_served_by', 'inject_point', 'lib_callback_edge', 'lib_callback_site', 'library_receiver', 'method_returns_method', - 'method_returns_param', 'persistence_field', 'remote_edge', 'remote_unsent', 'remote_unserved', 'return_value', - 'state_call_alloc', 'state_call_open', 'state_gate', 'state_gate_alloc', 'state_world_of_gated', - 'type_base_unresolved', 'unresolved_value_callee', + 'method_returns_param', 'middleware_applies', 'middleware_may_apply', 'middleware_registered_at', 'middleware_site', + 'persistence_field', 'registration_conditional', 'remote_edge', 'remote_unsent', 'remote_unserved', 'return_value', + 'route_registered_by', 'route_schema', 'router_node_at', 'state_call_alloc', 'state_call_open', 'state_gate', + 'state_gate_alloc', 'state_world_of_gated', 'type_base_unresolved', 'unresolved_value_callee', 'valid_read', ]); /** relationfile per line; comments and blanks skipped. */ diff --git a/graph/javascript/engine/config-resolution/http-router-apps.dl b/graph/javascript/engine/config-resolution/http-router-apps.dl new file mode 100644 index 000000000..ebaff0904 --- /dev/null +++ b/graph/javascript/engine/config-resolution/http-router-apps.dl @@ -0,0 +1,188 @@ +// ============================================================================ +// CONFIG-RESOLUTION · HTTP ROUTER APPLICATIONS (JavaScript: which values are apps) +// +// The JavaScript counterpart of typescript/engine/config-resolution/http-router-apps.dl. +// JavaScript has no declared types, but it has resolution/value-flow.dl: a value +// follows its binding through consts, imports and exports (ESM and CommonJS), +// parameters at resolved calls and returned values. So an application is one more +// VALUE KIND there, `router_app`, keyed by the `new X()` that built it, and the flow +// engine carries it wherever it goes. Rules here only say where the value starts and +// which calls hand it back (`app.get(...)` returns the app, `basePath` a value sharing +// its route list). +// +// LIMIT, stated: a function that builds and returns an app yields the SAME value at +// every call here (the flow engine has one allocation per `new`), so two sub-apps made +// by one factory share a list. The TypeScript engine splits them per call. +// +// Framework names live in config-resolution/knobs.dl only (router_*). +// ============================================================================ + +// ── a specifier as the package it names ───────────────────────────────────── +// `npm:x@4/sub` and `jsr:@s/x@^4/sub` are `x/sub` and `@s/x/sub`; a URL under a knob +// prefix is the package (`/mod.ts`) or its subpath; anything else is itself. +.decl jr_spec_raw(c0:symbol) +jr_spec_raw(raw) :- import_decl(_, raw, _, _, _, _, _, _, _). +.decl jr_unscheme(c0:symbol, c1:symbol) +jr_unscheme(raw, x) :- jr_spec_raw(raw), router_spec_scheme(s), strlen(raw) > strlen(s), + substr(raw, 0, strlen(s)) = s, x = substr(raw, strlen(s), strlen(raw) - strlen(s)). +.decl jr_ver_at(c0:symbol, c1:number) +jr_ver_at(x, i) :- jr_unscheme(_, x), i = range(1, strlen(x)), substr(x, i, 1) = "@". +.decl jr_slash_after(c0:symbol, c1:number, c2:number) +jr_slash_after(x, i, j) :- jr_ver_at(x, i), j = range(i, strlen(x)), substr(x, j, 1) = "/". +.decl jr_ver_end(c0:symbol, c1:number, c2:number) +jr_ver_end(x, i, j) :- jr_ver_at(x, i), j = min y : { jr_slash_after(x, i, y) }. +jr_ver_end(x, i, strlen(x)) :- jr_ver_at(x, i), !jr_slash_after(x, i, _). +.decl jr_spec(c0:symbol, c1:symbol) +jr_spec(raw, raw) :- jr_spec_raw(raw). +jr_spec(raw, x) :- jr_unscheme(raw, x), !jr_ver_at(x, _). +jr_spec(raw, y) :- jr_unscheme(raw, x), jr_ver_at(x, i), jr_ver_end(x, i, j), + y = cat(substr(x, 0, i), substr(x, j, strlen(x) - j)). +.decl jr_url_rest(c0:symbol, c1:symbol, c2:symbol) +jr_url_rest(raw, pkg, rest) :- jr_spec_raw(raw), router_spec_url(pre, pkg), strlen(raw) > strlen(pre), + substr(raw, 0, strlen(pre)) = pre, rest = substr(raw, strlen(pre), strlen(raw) - strlen(pre)). +.decl jr_url_path(c0:symbol, c1:symbol, c2:symbol) +jr_url_path(raw, pkg, rest) :- jr_url_rest(raw, pkg, rest), substr(rest, 0, 1) = "/". +jr_url_path(raw, pkg, r2) :- jr_url_rest(raw, pkg, rest), substr(rest, 0, 1) = "@", + j = min y : { y = range(0, strlen(rest)), substr(rest, y, 1) = "/" }, r2 = substr(rest, j, strlen(rest) - j). +jr_spec(raw, pkg) :- jr_url_path(raw, pkg, "/mod.ts"). + +.decl jr_module(c0:symbol) +jr_module(spec) :- router_app_class(spec, _). +jr_module(spec) :- router_mw_wrapper(spec, _). +jr_module(spec) :- router_mw_combinator(spec, _). +jr_module(spec) :- router_mw_except(spec, _). +jr_module(spec) :- router_rpc_client(spec, _). +jr_module(spec) :- router_test_client(spec, _). + +// jr_ref(Expr, Specifier, Name): Expr denotes export Name of a knob module ("" = the +// module itself), through an import, a require, a destructure or a member read +.decl jr_ref(c0:symbol, c1:symbol, c2:symbol) +jr_ref(e, spec, "") :- expr_kind(_, "MODULE_EDGE_CALL", _, e), expr_module_edge(_, imp, e), + import_decl(_, raw, _, _, _, _, _, _, imp), jr_spec(raw, spec), jr_module(spec). +jr_ref(e, spec, "") :- expr_kind(_, "IDENTIFIER", _, e), expr_binding(_, v, e), var_import(_, imp, v), + import_decl(_, raw, _, bf, _, _, _, _, imp), import_binds_whole_module(bf), jr_spec(raw, spec), jr_module(spec). +jr_ref(e, spec, n) :- expr_kind(_, "IDENTIFIER", _, e), expr_binding(_, v, e), var_import(_, imp, v), + import_decl(_, raw, _, bf, n, _, _, _, imp), import_binding_is_named(bf), n != "", jr_spec(raw, spec), jr_module(spec). +jr_ref(e, spec, n) :- expr_kind(_, "PROPERTY_ACCESS", _, e), expr_name(_, n, e), + expr_child(_, e, "ACCESS_TARGET", _, r), jr_ref(r, spec, ""). +jr_ref(e, spec, n) :- expr_kind(_, "IDENTIFIER", _, e), expr_binding(_, v, e), !var_import(_, _, v), + var_init(_, _, i, v), jr_ref(i, spec, n). + +.decl jr_new(c0:symbol) +jr_new(ne) :- expr_kind(_, "NEW", _, ne), expr_child(_, ne, "CALLEE", _, c), jr_ref(c, spec, n), + router_app_class(spec, n). + +// ── the value kind ────────────────────────────────────────────────────────── +expr_value(ne, "router_app", ne) :- jr_new(ne). +expr_value(ce, "router_app", a) :- call_site(_, "METHOD_CALL", cn, _, r, _, ce, _, _), + (router_returns_app(cn) ; router_base_path(cn)), expr_value(r, "router_app", a). + +// ── a registration: a call on an app value ────────────────────────────────── +.decl jr_registrar(c0:symbol) +jr_registrar(n) :- router_verb(n, _). +jr_registrar(n) :- router_use(n). +jr_registrar(n) :- router_on(n). +jr_registrar(n) :- router_mount_app(n). +jr_registrar(n) :- router_mount_foreign(n). +jr_registrar(n) :- router_fallback(n). +.decl jr_reg(c0:symbol, c1:symbol, c2:symbol) +jr_reg(ce, a, cn) :- call_site(_, "METHOD_CALL", cn, _, r, _, ce, _, _), jr_registrar(cn), + expr_value(r, "router_app", a). +.decl jr_recv(c0:symbol, c1:symbol) +jr_recv(ce, r) :- jr_reg(ce, _, _), call_site(_, "METHOD_CALL", _, _, r, _, ce, _, _). + +// ── strings: what the flow engine knows a value to be, else a hole ───────── +.decl jr_need_str(c0:symbol) +jr_need_str(p) :- jr_reg(ce, _, _), call_arg(ce, 0, p). +jr_need_str(p) :- jr_reg(ce, _, cn), router_on(cn), call_arg(ce, 1, p). +jr_need_str(el) :- jr_reg(ce, _, cn), router_on(cn), call_arg(ce, pos, x), pos <= 1, + expr_kind(_, "ARRAY_LITERAL", _, x), expr_child(_, x, _, _, el). +jr_need_str(p) :- call_site(_, "METHOD_CALL", cn, _, r, _, ce, _, _), router_base_path(cn), + expr_value(r, "router_app", _), call_arg(ce, 0, p). +.decl jr_has_str(c0:symbol) +jr_has_str(p) :- jr_need_str(p), expr_value(p, "str", _). +.decl jr_str(c0:symbol, c1:symbol) +jr_str(p, s) :- jr_need_str(p), expr_value(p, "str", s). +jr_str(p, "{}") :- jr_need_str(p), !jr_has_str(p). + +// ── the base each app value carries (basePath), where it can be followed ─── +// Same module, through consts and chains. A value reached any other way (an import, a +// parameter) is taken at the base of the list it belongs to, `/`; that misses a +// basePath applied in another module, and the routes come out without it. +.decl jr_base(c0:symbol, c1:symbol) +jr_base(ne, "/") :- jr_new(ne). +jr_base(ce, b) :- call_site(_, "METHOD_CALL", cn, _, r, _, ce, _, _), router_returns_app(cn), + expr_value(ce, "router_app", _), jr_base(r, b). +jr_base(ce, nb) :- call_site(_, "METHOD_CALL", cn, _, r, _, ce, _, _), router_base_path(cn), + jr_base(r, b), call_arg(ce, 0, p), jr_str(p, pv), jr_bmerge(b, pv, nb), strlen(nb) < 300. +jr_bmreq(b, pv) :- call_site(_, "METHOD_CALL", cn, _, r, _, ce, _, _), router_base_path(cn), + jr_base(r, b), call_arg(ce, 0, p), jr_str(p, pv). +jr_base(e, b) :- expr_kind(_, "IDENTIFIER", _, e), expr_value(e, "router_app", _), expr_binding(_, v, e), + !var_import(_, _, v), var_init(_, _, i, v), jr_base(i, b). +.decl jr_has_base(c0:symbol) +jr_has_base(e) :- jr_base(e, _). +.decl jr_val(c0:symbol, c1:symbol, c2:symbol) +jr_val(e, a, b) :- expr_value(e, "router_app", a), jr_base(e, b). +jr_val(e, a, "/") :- expr_value(e, "router_app", a), !jr_has_base(e). + +// ── mergePath, for basePath (a stratum of its own: the base decides which values +// have one, and the later merges depend on that) +.decl jr_bmreq(c0:symbol, c1:symbol) +.decl jr_bmerge(c0:symbol, c1:symbol, c2:symbol) +.decl jr_blead(c0:symbol, c1:symbol) +jr_blead(x, x) :- (jr_bmreq(x, _) ; jr_bmreq(_, x)), strlen(x) >= 1, substr(x, 0, 1) = "/". +jr_blead(x, y) :- (jr_bmreq(x, _) ; jr_bmreq(_, x)), strlen(x) >= 1, substr(x, 0, 1) != "/", y = cat("/", x). +jr_blead("", "/") :- (jr_bmreq("", _) ; jr_bmreq(_, "")). +jr_bmerge(a, b, a1) :- jr_bmreq(a, b), jr_blead(a, a1), jr_blead(b, "/"), + substr(a1, strlen(a1) - 1, 1) = "/". +jr_bmerge(a, b, r) :- jr_bmreq(a, b), jr_blead(a, a1), jr_blead(b, b1), b1 != "/", + substr(a1, strlen(a1) - 1, 1) = "/", r = cat(substr(a1, 0, strlen(a1) - 1), b1). +jr_bmerge(a, b, a1) :- jr_bmreq(a, b), jr_blead(a, a1), jr_blead(b, "/"), + substr(a1, strlen(a1) - 1, 1) != "/". +jr_bmerge(a, b, r) :- jr_bmreq(a, b), jr_blead(a, a1), jr_blead(b, b1), b1 != "/", + substr(a1, strlen(a1) - 1, 1) != "/", r = cat(a1, b1). + +// ── mergePath ─────────────────────────────────────────────────────────────── +.decl jr_mreq(c0:symbol, c1:symbol) +.decl jr_merge(c0:symbol, c1:symbol, c2:symbol) +.decl jr_lead(c0:symbol, c1:symbol) +jr_lead(x, x) :- (jr_mreq(x, _) ; jr_mreq(_, x)), strlen(x) >= 1, substr(x, 0, 1) = "/". +jr_lead(x, y) :- (jr_mreq(x, _) ; jr_mreq(_, x)), strlen(x) >= 1, substr(x, 0, 1) != "/", y = cat("/", x). +jr_lead("", "/") :- (jr_mreq("", _) ; jr_mreq(_, "")). +jr_merge(a, b, a1) :- jr_mreq(a, b), jr_lead(a, a1), jr_lead(b, "/"), + substr(a1, strlen(a1) - 1, 1) = "/". +jr_merge(a, b, r) :- jr_mreq(a, b), jr_lead(a, a1), jr_lead(b, b1), b1 != "/", + substr(a1, strlen(a1) - 1, 1) = "/", r = cat(substr(a1, 0, strlen(a1) - 1), b1). +jr_merge(a, b, a1) :- jr_mreq(a, b), jr_lead(a, a1), jr_lead(b, "/"), + substr(a1, strlen(a1) - 1, 1) != "/". +jr_merge(a, b, r) :- jr_mreq(a, b), jr_lead(a, a1), jr_lead(b, b1), b1 != "/", + substr(a1, strlen(a1) - 1, 1) != "/", r = cat(a1, b1). + +// ── mounts and roots ──────────────────────────────────────────────────────── +.decl jr_inc(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +jr_inc(a, s, ce, pre) :- jr_reg(ce, a, cn), router_mount_app(cn), jr_recv(ce, r), jr_val(r, a, b), + call_arg(ce, 1, sx), expr_value(sx, "router_app", s), s != a, + call_arg(ce, 0, px), jr_str(px, pv), jr_merge(b, pv, pre). +jr_mreq(b, pv) :- jr_reg(ce, a, cn), router_mount_app(cn), jr_recv(ce, r), jr_val(r, a, b), + call_arg(ce, 0, px), jr_str(px, pv). +.decl jr_included(c0:symbol) +jr_included(s) :- jr_reg(ce, a, cn), router_mount_app(cn), call_arg(ce, 1, sx), + expr_value(sx, "router_app", s), s != a. +.decl jr_root(c0:symbol) +jr_root(a) :- jr_new(a), !jr_included(a). + +// ── position keys ─────────────────────────────────────────────────────────── +// The first argument's position: a chain `app.get(…).post(…)` starts every call at +// the same receiver, and the arguments are what moves left to right. +.decl jr_need_key(c0:symbol) +jr_need_key(ce) :- jr_reg(ce, _, _). +.decl jr_key_expr(c0:symbol, c1:symbol) +jr_key_expr(ce, a) :- jr_need_key(ce), call_arg(ce, 0, a). +.decl jr_has_arg(c0:symbol) +jr_has_arg(ce) :- jr_key_expr(ce, _). +.decl jr_key_x(c0:symbol, c1:symbol) +jr_key_x(ce, a) :- jr_key_expr(ce, a). +jr_key_x(ce, ce) :- jr_need_key(ce), !jr_has_arg(ce). +.decl jr_key(c0:symbol, c1:symbol) +jr_key(ce, k) :- jr_key_x(ce, x), expr_span(_, l, c, _, _, x), strlen(l) <= 7, strlen(c) <= 5, + k = cat(cat(substr("0000000", 0, 7 - strlen(l)), l), cat(":", cat(substr("00000", 0, 5 - strlen(c)), c))). diff --git a/graph/javascript/engine/config-resolution/http-router-routes.dl b/graph/javascript/engine/config-resolution/http-router-routes.dl new file mode 100644 index 000000000..176eac992 --- /dev/null +++ b/graph/javascript/engine/config-resolution/http-router-routes.dl @@ -0,0 +1,330 @@ +// ============================================================================ +// CONFIG-RESOLUTION · HTTP ROUTER ROUTES (JavaScript: the route table, composed) +// +// The JavaScript counterpart of typescript/engine/config-resolution/http-router-routes.dl, +// same relations, same contract: +// +// http_route(Handler, Verb, FullPath, App, Order) +// http_route_partial(Handler, Verb, KnownPath, App, Reason) +// http_mount(ParentApp, ChildApp, Prefix, Order) +// middleware_applies(Mw, Handler, Verb, FullPath, Position, App) +// middleware_site(Mw, Kind, Callee, Specifier) +// framework_undetermined(Node, What, Reason) +// +// and every function handed to a registration is an `http` entry point. A registering +// call puts one entry per handler argument on its app's list (the last is the route's +// handler, earlier ones its own middleware); `use` puts middleware entries for every +// verb. A list no `route(prefix, list)` mounts is a root; flattening splices each +// mounted list at the mount, under its prefix. A path the flow engine cannot read as a +// string is a `{}` hole: http_route_partial with a reason, never http_route. +// ============================================================================ + +// ── where the handlers start ──────────────────────────────────────────────── +.decl jr_arg0_fnlike(c0:symbol) +jr_arg0_fnlike(ce) :- jr_reg(ce, _, _), call_arg(ce, 0, a), expr_kind(_, k, _, a), + (k = "FUNCTION_EXPRESSION" ; k = "CALL" ; k = "SPREAD"). +jr_arg0_fnlike(ce) :- jr_reg(ce, _, _), call_arg(ce, 0, a), expr_value(a, "func", _). +.decl jr_pathed(c0:symbol) +jr_pathed(ce) :- jr_reg(ce, _, cn), (router_verb(cn, _) ; router_use(cn)), call_arg(ce, 0, _), !jr_arg0_fnlike(ce). +.decl jr_pathless(c0:symbol) +jr_pathless(ce) :- jr_reg(ce, _, cn), (router_verb(cn, _) ; router_use(cn)), jr_arg0_fnlike(ce). +.decl jr_hstart(c0:symbol, c1:number) +jr_hstart(ce, 1) :- jr_pathed(ce). +jr_hstart(ce, 0) :- jr_pathless(ce). +jr_hstart(ce, 2) :- jr_reg(ce, _, cn), router_on(cn). +jr_hstart(ce, 0) :- jr_reg(ce, _, cn), router_fallback(cn). + +// ── handler arguments: (call, ordinal = position * 100 + element, expression) ── +.decl jr_harg(c0:symbol, c1:number, c2:symbol) +jr_harg(ce, n, a) :- jr_hstart(ce, s), call_arg(ce, p, a), p >= s, n = p * 100, !expr_kind(_, "SPREAD", _, a). +jr_harg(ce, 100, a) :- jr_reg(ce, _, cn), router_mount_foreign(cn), call_arg(ce, 1, a). +.decl jr_spread(c0:symbol, c1:number, c2:symbol) +jr_spread(ce, p, x) :- jr_hstart(ce, s), call_arg(ce, p, a), p >= s, expr_kind(_, "SPREAD", _, a), + expr_child(_, a, _, _, x). +.decl jr_spread_arr(c0:symbol, c1:number, c2:symbol) +jr_spread_arr(ce, p, x) :- jr_spread(ce, p, x), expr_kind(_, "ARRAY_LITERAL", _, x). +jr_spread_arr(ce, p, arr) :- jr_spread(ce, p, x), expr_binding(_, v, x), var_init(_, _, arr, v), + expr_kind(_, "ARRAY_LITERAL", _, arr). +jr_harg(ce, n, el) :- jr_spread_arr(ce, p, arr), expr_child(_, arr, _, epos, el), + n = p * 100 + to_number(epos), to_number(epos) < 100. +.decl jr_spread_call(c0:symbol, c1:number, c2:symbol) +jr_spread_call(ce, p, x) :- jr_spread(ce, p, x), call_site(_, _, sn, _, _, _, x, _, _), router_handler_spread(sn). +jr_harg(ce, n, el) :- jr_spread_call(ce, p, x), call_arg(x, ep, el), n = p * 100 + ep, ep < 100. +.decl jr_spread_known(c0:symbol, c1:number) +jr_spread_known(ce, p) :- jr_spread_arr(ce, p, _). +jr_spread_known(ce, p) :- jr_spread_call(ce, p, _). +framework_undetermined(m, "middleware", "dynamic_middleware_list") :- jr_spread(ce, p, _), + !jr_spread_known(ce, p), call_site(_, _, _, _, _, m, ce, _, _). + +.decl jr_last(c0:symbol, c1:number) +jr_last(ce, n) :- jr_harg(ce, _, _), n = max x : { jr_harg(ce, x, _) }. + +// ── the function each handler argument is ─────────────────────────────────── +// What the flow engine says the expression may be (written in place, named, imported, +// returned by a project function), or the argument of a knob wrapper. +.decl jr_wrapper_call(c0:symbol) +jr_wrapper_call(a) :- call_site(_, "FUNCTION_CALL", _, _, _, _, a, _, _), expr_child(_, a, "CALLEE", _, c), + jr_ref(c, spec, n), router_mw_wrapper(spec, n). +.decl jr_fn(c0:symbol, c1:symbol) +jr_fn(a, m) :- jr_harg(_, _, a), expr_value(a, "func", m). +// a project factory returning a knob wrapper's result (`function session(o) { return createMiddleware(fn) }`, +// called as `session(o)` directly or through a const): the middleware is `fn`, at every call +.decl jr_factory_wrap(c0:symbol, c1:symbol) +jr_factory_wrap(w, m) :- expr_root(_, "RETURN", e), expr_owner(_, w, _, e), jr_wrapper_call(e), call_arg(e, 0, x), + expr_value(x, "func", m). +jr_fn(a, m) :- jr_harg(_, _, a), expr_kind(_, "CALL", _, a), expr_resolves_to_method(a, w), jr_factory_wrap(w, m). +jr_fn(a, m) :- jr_harg(_, _, a), expr_binding(_, v, a), var_init(_, _, i, v), expr_kind(_, "CALL", _, i), + expr_resolves_to_method(i, w), jr_factory_wrap(w, m). +jr_fn(a, m) :- jr_harg(_, _, a), jr_wrapper_call(a), call_arg(a, 0, x), expr_value(x, "func", m). +jr_fn(a, m) :- jr_harg(_, _, a), expr_binding(_, v, a), var_init(_, _, i, v), jr_wrapper_call(i), + call_arg(i, 0, x), expr_value(x, "func", m). +.decl jr_node_fn(c0:symbol, c1:number, c2:symbol) +jr_node_fn(ce, n, m) :- jr_harg(ce, n, a), jr_fn(a, m), method_decl("client", _, _, _, _, _, _, m). +// a route's handler node: the project function, or a library call's result keyed at the call +.decl jr_route_node(c0:symbol, c1:number, c2:symbol) +jr_route_node(ce, n, m) :- jr_node_fn(ce, n, m). +jr_route_node(ce, n, a) :- jr_harg(ce, n, a), !jr_has_fn(ce, n), expr_kind(_, "CALL", _, a). +.decl jr_has_fn(c0:symbol, c1:number) +jr_has_fn(ce, n) :- jr_node_fn(ce, n, _). +.decl jr_node(c0:symbol, c1:number, c2:symbol) +jr_node(ce, n, m) :- jr_node_fn(ce, n, m). +jr_node(ce, n, a) :- jr_harg(ce, n, a), !jr_has_fn(ce, n). + +// a call of a knob function: (call, specifier, name) +.decl jr_knob_call(c0:symbol, c1:symbol, c2:symbol) +jr_knob_call(a, spec, n) :- call_site(_, "FUNCTION_CALL", _, _, _, _, a, _, _), expr_child(_, a, "CALLEE", _, c), + jr_ref(c, spec, n). +.decl jr_comb_arg(c0:symbol) +jr_comb_arg(x) :- jr_harg(_, _, a), jr_knob_call(a, spec, n), router_mw_combinator(spec, n), call_arg(a, _, x). +jr_comb_arg(x) :- jr_harg(_, _, a), jr_knob_call(a, spec, n), router_mw_except(spec, n), call_arg(a, 1, x). +entry_point(m, "http") :- jr_comb_arg(x), expr_value(x, "func", m), method_decl("client", _, _, _, _, _, _, m). +.decl jr_excepted(c0:symbol, c1:number, c2:symbol) +jr_excepted(ce, n, v) :- jr_harg(ce, n, a), jr_knob_call(a, spec, nm), router_mw_except(spec, nm), + call_arg(a, 0, x), expr_value(x, "str", v). +jr_excepted(ce, n, v) :- jr_harg(ce, n, a), jr_knob_call(a, spec, nm), router_mw_except(spec, nm), + call_arg(a, 0, arr), expr_kind(_, "ARRAY_LITERAL", _, arr), expr_child(_, arr, _, _, x), expr_value(x, "str", v). + +// http: every function a router calls, receiver-checked +entry_point(m, "http") :- jr_node_fn(_, _, m). + +// ── local paths ───────────────────────────────────────────────────────────── +.decl jr_local(c0:symbol, c1:symbol) +jr_local(ce, lp) :- jr_pathed(ce), call_arg(ce, 0, p), jr_str(p, lp). +jr_local(ce, "*") :- jr_pathless(ce), jr_reg(ce, _, cn), router_use(cn). +.decl jr_cont_src(c0:symbol) +jr_cont_src(r) :- jr_reg(r, _, rn), (router_verb(rn, _) ; router_use(rn)). +jr_cont_src(r) :- jr_new(r). +jr_cont_src(r) :- call_site(_, "METHOD_CALL", cn, _, _, _, r, _, _), router_base_path(cn). +jr_local(ce, lp) :- jr_pathless(ce), jr_reg(ce, _, cn), router_verb(cn, _), jr_recv(ce, r), + jr_local(r, lp), jr_reg(r, _, rn), (router_verb(rn, _) ; router_use(rn)). +jr_local(ce, "/") :- jr_pathless(ce), jr_reg(ce, _, cn), router_verb(cn, _), jr_recv(ce, r), + (jr_new(r) ; call_site(_, "METHOD_CALL", bn, _, _, _, r, _, _), router_base_path(bn)). +// on a variable: the path of the previous registration on that app in this module +.decl jr_pathless_var(c0:symbol) +jr_pathless_var(ce) :- jr_pathless(ce), jr_reg(ce, _, cn), router_verb(cn, _), jr_recv(ce, r), !jr_cont_src(r). +.decl jr_keyn(c0:symbol, c1:number) +jr_keyn(ce, n) :- jr_key_x(ce, x), expr_span(_, l, c, _, _, x), + to_number(l) < 200000, to_number(c) < 10000, n = to_number(l) * 10000 + to_number(c). +.decl jr_prev_cand(c0:symbol, c1:symbol, c2:number) +jr_prev_cand(ce, r2, n2) :- jr_pathless_var(ce), jr_reg(ce, a, _), jr_keyn(ce, n), call_site(_, _, _, _, _, _, ce, m, _), + jr_reg(r2, a, rn), (router_verb(rn, _) ; router_use(rn)), r2 != ce, call_site(_, _, _, _, _, _, r2, m, _), + jr_keyn(r2, n2), n2 < n. +.decl jr_prev(c0:symbol, c1:symbol) +jr_prev(ce, r2) :- jr_prev_cand(ce, _, _), b = max x : { jr_prev_cand(ce, _, x) }, jr_prev_cand(ce, r2, b). +.decl jr_has_prev(c0:symbol) +jr_has_prev(ce) :- jr_prev(ce, _). +jr_local(ce, lp) :- jr_prev(ce, r2), jr_local(r2, lp). +jr_local(ce, "/") :- jr_pathless_var(ce), !jr_has_prev(ce). +jr_local(ce, lp) :- jr_reg(ce, _, cn), router_mount_foreign(cn), call_arg(ce, 0, p), jr_str(p, pv), + jr_merge(pv, "*", lp). +jr_mreq(pv, "*") :- jr_reg(ce, _, cn), router_mount_foreign(cn), call_arg(ce, 0, p), jr_str(p, pv). + +.decl jr_on_piece(c0:symbol, c1:number, c2:symbol) +jr_on_piece(ce, pos, v) :- jr_reg(ce, _, cn), router_on(cn), call_arg(ce, pos, x), pos <= 1, + !expr_kind(_, "ARRAY_LITERAL", _, x), jr_str(x, v). +jr_on_piece(ce, pos, v) :- jr_reg(ce, _, cn), router_on(cn), call_arg(ce, pos, x), pos <= 1, + expr_kind(_, "ARRAY_LITERAL", _, x), expr_child(_, x, _, _, el), jr_str(el, v). +.decl jr_on_verb(c0:symbol, c1:symbol) +jr_on_verb(ce, vv) :- jr_on_piece(ce, 0, w), router_verb_name(w, vv). +jr_on_verb(ce, w) :- jr_on_piece(ce, 0, w), !router_verb_name(w, _). + +// ── entries: (list, key, kind, verb, path, call, ordinal) ─────────────────── +.decl jr_entry_base(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol) +jr_entry_base(ce, a, "route", vv, lp, b) :- jr_reg(ce, a, cn), router_verb(cn, vv), jr_local(ce, lp), + jr_recv(ce, r), jr_val(r, a, b). +jr_entry_base(ce, a, "mw", "ANY", lp, b) :- jr_reg(ce, a, cn), router_use(cn), jr_local(ce, lp), + jr_recv(ce, r), jr_val(r, a, b). +jr_entry_base(ce, a, "route", "ANY", lp, b) :- jr_reg(ce, a, cn), router_mount_foreign(cn), jr_local(ce, lp), + jr_recv(ce, r), jr_val(r, a, b). +jr_entry_base(ce, a, "route", vv, lp, b) :- jr_reg(ce, a, _), jr_on_verb(ce, vv), jr_on_piece(ce, 1, lp), + jr_recv(ce, r), jr_val(r, a, b). +jr_mreq(b, lp) :- jr_entry_base(_, _, _, _, lp, b). +.decl jr_entry(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol, c6:number) +jr_entry(a, k2, kind, vv, p, ce, n) :- jr_entry_base(ce, a, kind, vv, lp, b), jr_merge(b, lp, p), + jr_harg(ce, n, _), jr_key(ce, k), s = to_string(n), + k2 = cat(k, cat(",", cat(substr("00000", 0, 5 - strlen(s)), s))). +.decl jr_entry_own(c0:symbol, c1:symbol) +jr_entry_own(ce, p) :- jr_entry(_, _, _, _, p, ce, _). + +// ── flattening a root ─────────────────────────────────────────────────────── +.decl jr_flat(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol) +jr_flat(r, r, "/", "", "", "") :- jr_root(r). +.decl jr_cutoff(c0:symbol, c1:symbol, c2:symbol) +jr_cutoff(site, k, m) :- jr_inc(_, _, site, _), jr_key(site, k), call_site(_, _, _, _, _, _, site, m, _). +jr_flat(r, s, np, nk, cut, cm) :- jr_flat(r, a, p, kp, _, _), jr_inc(a, s, site, pre), + jr_merge(p, pre, np), jr_key(site, k), nk = cat(kp, cat(k, ".")), strlen(nk) < 160, + jr_cutoff(site, cut, cm). +jr_mreq(p, pre) :- jr_flat(_, a, p, _, _, _), jr_inc(a, _, _, pre). +.decl jr_fcand(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol, c6:number, c7:symbol) +jr_fcand(r, fk, kind, vv, p, ce, n, pre) :- jr_flat(r, a, pre, kp, "", _), jr_entry(a, k, kind, vv, p, ce, n), + fk = cat(kp, k). +jr_fcand(r, fk, kind, vv, p, ce, n, pre) :- jr_flat(r, a, pre, kp, cut, _), cut != "", + jr_entry(a, k, kind, vv, p, ce, n), k < cut, fk = cat(kp, k). +jr_fcand(r, fk, kind, vv, p, ce, n, pre) :- jr_flat(r, a, pre, kp, cut, cm), cut != "", + jr_entry(a, k, kind, vv, p, ce, n), call_site(_, _, _, _, _, _, ce, em, _), em != cm, fk = cat(kp, k). +jr_mreq(pre, p) :- jr_fcand(_, _, _, _, p, _, _, pre). +.decl jr_fentry(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol, c6:number) +jr_fentry(r, fk, kind, vv, fp, ce, n) :- jr_fcand(r, fk, kind, vv, p, ce, n, pre), jr_merge(pre, p, fp). + +// http_route(Handler, Verb, FullPath, App, Order) — a route a served app answers, its +// full path composed through every basePath and mount; App is the `new` that built +// the root app, Order its registration key there (compares as program order). +http_route(m, vv, fp, r, fk) :- jr_fentry(r, fk, "route", vv, fp, ce, n), jr_last(ce, n), + jr_route_node(ce, n, m), !contains("{}", fp). + +// route_registered_by(RegMethod, Handler, Verb, FullPath, App) — the function whose body makes the +// registering call (a registration function, an app factory, a module body) +route_registered_by(rm, m, vv, fp, r) :- jr_fentry(r, _, "route", vv, fp, ce, n), jr_last(ce, n), + jr_route_node(ce, n, m), !contains("{}", fp), call_site(_, _, _, _, _, rm, ce, _, _). + +.decl jr_partial(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +jr_partial(m, vv, fp, r, "dynamic_route_path") :- jr_fentry(r, _, "route", vv, fp, ce, n), jr_last(ce, n), + jr_route_node(ce, n, m), contains("{}", fp), jr_entry_own(ce, op), contains("{}", op). +jr_partial(m, vv, fp, r, "dynamic_mount_prefix") :- jr_fentry(r, _, "route", vv, fp, ce, n), jr_last(ce, n), + jr_route_node(ce, n, m), contains("{}", fp), jr_entry_own(ce, op), !contains("{}", op). +// http_route_partial(Handler, Verb, KnownPath, App, Reason) — a route whose path is +// not fully static: what is known, `{}` for each unknown piece, and why +http_route_partial(m, vv, fp, r, why) :- jr_partial(m, vv, fp, r, why). +remote_undetermined(m, "http", why) :- jr_partial(m, _, _, _, why). + +// http_mount(ParentApp, ChildApp, Prefix, Order) — `route(prefix, child)` +http_mount(a, s, pre, k) :- jr_inc(a, s, site, pre), jr_key(site, k). +framework_undetermined(m, "mount", "unresolved_mount") :- jr_reg(ce, _, cn), router_mount_app(cn), + call_arg(ce, 1, sx), !expr_value(sx, "router_app", _), call_site(_, _, _, _, _, m, ce, _, _). +framework_undetermined(m, "mount", "foreign_mount") :- jr_reg(ce, _, cn), router_mount_foreign(cn), + !jr_has_fn(ce, 100), call_site(_, _, _, _, _, m, ce, _, _). + +// ── middleware application ────────────────────────────────────────────────── +.decl jr_hent(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +jr_hent(r, hk, ce, vv, fp) :- jr_fentry(r, hk, "route", vv, fp, ce, n), jr_last(ce, n), !contains("{}", fp). +// (root, key, call, ordinal, scope, verb): `use` entries, and a verb registration whose +// function takes `next` and calls it +.decl jr_ment(c0:symbol, c1:symbol, c2:symbol, c3:number, c4:symbol, c5:symbol) +jr_ment(r, mk, ce, n, q, vv) :- jr_fentry(r, mk, "mw", vv, q, ce, n), !contains("{}", q). +jr_ment(r, mk, ce, n, q, vv) :- jr_fentry(r, mk, "route", vv, q, ce, n), !contains("{}", q), + jr_node_fn(ce, n, m), jr_calls_next(m). +.decl jr_calls_next(c0:symbol) +jr_calls_next(m) :- jr_node_fn(_, _, m), param_decl("client", _, "1", m, p), expr_param(_, p, f), + expr_child(_, ce, "CALLEE", _, f), call_site(_, _, _, _, _, _, ce, _, _). +.decl jr_verb_ok(c0:symbol, c1:symbol) +jr_verb_ok("ANY", v) :- jr_hent(_, _, _, v, _). +jr_verb_ok(v, v) :- jr_hent(_, _, _, v, _). +.decl jr_spair(c0:symbol, c1:symbol) +jr_spair(q, p) :- jr_ment(r, mk, _, _, q, _), jr_hent(r, hk, _, _, p), mk < hk. + +.decl jr_spath(c0:symbol) +jr_spath(q) :- jr_spair(q, _). +jr_spath(p) :- jr_spair(_, p). +.decl jr_slash(c0:symbol, c1:number) +jr_slash(p, i) :- jr_spath(p), i = range(0, strlen(p)), substr(p, i, 1) = "/". +.decl jr_next_slash(c0:symbol, c1:number, c2:number) +jr_next_slash(p, i, j) :- jr_slash(p, i), j = min x : { jr_slash(p, x), x > i }. +.decl jr_has_next(c0:symbol, c1:number) +jr_has_next(p, i) :- jr_next_slash(p, i, _). +.decl jr_seg_at(c0:symbol, c1:number, c2:symbol) +jr_seg_at(p, i, t) :- jr_next_slash(p, i, j), t = substr(p, i + 1, j - i - 1). +jr_seg_at(p, i, t) :- jr_slash(p, i), !jr_has_next(p, i), t = substr(p, i + 1, strlen(p) - i - 1). +.decl jr_seg_ne(c0:symbol, c1:number) +jr_seg_ne(p, i) :- jr_seg_at(p, i, t), t != "". +.decl jr_seg(c0:symbol, c1:number, c2:symbol) +jr_seg(p, k, t) :- jr_seg_at(p, i, t), t != "", k = count : { jr_seg_ne(p, x), x < i }. +.decl jr_nseg(c0:symbol, c1:number) +jr_nseg(p, n) :- jr_spath(p), n = count : { jr_seg(p, _, _) }. +.decl jr_lit(c0:symbol) +jr_lit(t) :- jr_seg(_, _, t), substr(t, 0, 1) != ":", t != "*". +.decl jr_qtail(c0:symbol) +jr_qtail(q) :- jr_nseg(q, n), n >= 1, jr_seg(q, n - 1, "*"). +.decl jr_qfix(c0:symbol, c1:number) +jr_qfix(q, n - 1) :- jr_qtail(q), jr_nseg(q, n). +jr_qfix(q, n) :- jr_nseg(q, n), !jr_qtail(q). +.decl jr_len_ok(c0:symbol, c1:symbol) +jr_len_ok(q, p) :- jr_spair(q, p), jr_qtail(q), jr_qfix(q, f), jr_nseg(p, np), np >= f. +jr_len_ok(q, p) :- jr_spair(q, p), !jr_qtail(q), jr_nseg(q, n), jr_nseg(p, n). +.decl jr_conflict(c0:symbol, c1:symbol) +jr_conflict(q, p) :- jr_spair(q, p), jr_qfix(q, f), jr_seg(q, i, x), i < f, jr_seg(p, i, y), + jr_lit(x), jr_lit(y), x != y. +.decl jr_undec(c0:symbol, c1:symbol) +jr_undec(q, p) :- jr_spair(q, p), jr_qfix(q, f), jr_seg(q, i, x), i < f, jr_seg(p, i, y), + jr_lit(x), !jr_lit(y). +.decl jr_scope_ok(c0:symbol, c1:symbol) +jr_scope_ok(q, p) :- jr_len_ok(q, p), !jr_conflict(q, p), !jr_undec(q, p). + +.decl jr_applies(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +jr_applies(r, hk, mk, mce, mn) :- jr_ment(r, mk, mce, mn, q, mv), jr_hent(r, hk, _, hv, p), mk < hk, + jr_verb_ok(mv, hv), jr_scope_ok(q, p), !jr_excepted(mce, mn, p). +jr_applies(r, hk, mk, ce, mn) :- jr_hent(r, hk, ce, vv, fp), jr_fentry(r, mk, "route", vv, fp, ce, mn), + jr_last(ce, n), mn < n. +framework_undetermined(mw, "middleware_scope", "param_vs_literal_scope") :- jr_ment(r, mk, mce, mn, q, mv), + jr_hent(r, hk, _, hv, p), mk < hk, jr_verb_ok(mv, hv), jr_len_ok(q, p), !jr_conflict(q, p), jr_undec(q, p), jr_node(mce, mn, mw). +.decl jr_applies_k(c0:symbol, c1:symbol, c2:symbol) +jr_applies_k(r, hk, mk) :- jr_applies(r, hk, mk, _, _). +.decl jr_rank(c0:symbol, c1:symbol, c2:symbol, c3:number) +jr_rank(r, hk, mk, pos) :- jr_applies_k(r, hk, mk), pos = count : { jr_applies_k(r, hk, k2), k2 < mk }. + +// middleware_applies(Mw, Handler, Verb, FullPath, Position, App) — in served app App, Mw runs before Handler on +// a request for Verb FullPath, at Position (0 first). Mw is the project function, or +// the registering argument expression for library middleware. +// a spread no rule could read stands in the chain: no position after it is claimed +.decl jr_dyn_key(c0:symbol, c1:symbol) +jr_dyn_key(a, k) :- jr_spread(ce, p, _), !jr_spread_known(ce, p), jr_reg(ce, a, _), jr_key(ce, k0), + s = to_string(p * 100), k = cat(k0, cat(",", cat(substr("00000", 0, 5 - strlen(s)), s))). +.decl jr_fdyn(c0:symbol, c1:symbol) +jr_fdyn(r, fk) :- jr_flat(r, a, _, kp, _, _), jr_dyn_key(a, k), fk = cat(kp, k). +.decl jr_after_dyn(c0:symbol, c1:symbol, c2:symbol) +jr_after_dyn(r, hk, mk) :- jr_applies_k(r, hk, mk), jr_fdyn(r, sk), sk < hk, sk < mk. +middleware_applies(mw, h, vv, fp, to_string(pos), r) :- jr_hent(r, hk, ce, vv, fp), jr_last(ce, n), + jr_route_node(ce, n, h), jr_applies(r, hk, mk, mce, mn), jr_node(mce, mn, mw), jr_rank(r, hk, mk, pos), + !jr_after_dyn(r, hk, mk). + +// middleware_registered_at(Mw, Handler, Verb, FullPath, App, RegCall) — the registering call that puts Mw in the +// chain of Handler on App: an app-level `use(..)` (or a verb registration that calls next), or the route's own +// call for its per-route middleware +middleware_registered_at(mw, h, vv, fp, r, mce) :- jr_hent(r, hk, ce, vv, fp), jr_last(ce, n), + jr_route_node(ce, n, h), jr_applies(r, hk, mk, mce, mn), jr_node(mce, mn, mw), !jr_after_dyn(r, hk, mk). + +// a registration inside a branch or a loop is still emitted, and said to be one: the block of the function +// that makes the call, whose span holds the call +.decl jr_in_block(c0:symbol, c1:symbol) +jr_in_block(ce, k) :- jr_reg(ce, _, _), expr_owner(_, h, mod, ce), expr_span(_, l, c, _, _, ce), + block_owner(k, h, b), block_span(_, mod, l1, c1, l2, c2, b), + (to_number(l) > to_number(l1) ; to_number(l) = to_number(l1), to_number(c) >= to_number(c1)), + (to_number(l) < to_number(l2) ; to_number(l) = to_number(l2), to_number(c) <= to_number(c2)). +.decl jr_cond_kind(c0:symbol) +jr_cond_kind("IF"). jr_cond_kind("ELSE"). jr_cond_kind("SWITCH"). jr_cond_kind("SWITCH_CASE"). jr_cond_kind("CATCH"). +.decl jr_loop_kind(c0:symbol) +jr_loop_kind("FOR"). jr_loop_kind("FOR_IN"). jr_loop_kind("FOR_OF"). jr_loop_kind("WHILE"). jr_loop_kind("DO"). +framework_undetermined(m, "registration", "conditional_registration") :- jr_in_block(ce, k), jr_cond_kind(k), + jr_last(ce, n), jr_node_fn(ce, n, m). +framework_undetermined(m, "registration", "loop_registration") :- jr_in_block(ce, k), jr_loop_kind(k), + jr_last(ce, n), jr_node_fn(ce, n, m). + +// middleware_site(Mw, Kind, Callee, Specifier) — what a middleware node is +.decl jr_mw_arg(c0:symbol, c1:number, c2:symbol) +jr_mw_arg(ce, n, a) :- jr_entry(_, _, "mw", _, _, ce, n), jr_harg(ce, n, a). +jr_mw_arg(ce, n, a) :- jr_harg(ce, n, a), jr_last(ce, l), n < l, jr_entry(_, _, "route", _, _, ce, n). +middleware_site(m, "project_fn", "", "") :- jr_mw_arg(ce, n, a), jr_node_fn(ce, n, m), !expr_kind(_, "CALL", _, a). +middleware_site(m, "factory_result", cn, "") :- jr_mw_arg(ce, n, a), jr_node_fn(ce, n, m), + call_site(_, _, cn, _, _, _, a, _, _). +middleware_site(a, "library_call", n, spec) :- jr_mw_arg(ce, k, a), !jr_has_fn(ce, k), + call_site(_, _, _, _, _, _, a, _, _), expr_child(_, a, "CALLEE", _, c), expr_binding(_, v, c), + var_import(_, imp, v), import_decl(_, spec, _, _, n, _, _, _, imp). diff --git a/graph/javascript/engine/config-resolution/knobs.dl b/graph/javascript/engine/config-resolution/knobs.dl new file mode 100644 index 000000000..ace8606f2 --- /dev/null +++ b/graph/javascript/engine/config-resolution/knobs.dl @@ -0,0 +1,107 @@ +// ============================================================================ +// CONFIG-RESOLUTION · KNOBS (JavaScript: framework name lists) +// +// The JavaScript counterpart of typescript/engine/config-resolution/knobs.dl. Every +// framework-specific NAME the config-resolution and framework-behavior rules of this +// engine key on lives here and nowhere else; no rule mentions a framework by name. +// ============================================================================ + +// ── HTTP ROUTER APPLICATIONS (config-resolution/http-router-*.dl) ─────────── +// An application object a program builds and registers routes, middleware and +// sub-applications on. Recognised by the class it is constructed from and the module +// that class is imported (or required) from, never by a method name. The package need +// not be installed: the import specifier is the evidence. + +// router_app_class(Specifier, ExportedName): `new ()` of this import is an app +.decl router_app_class(c0:symbol, c1:symbol) +router_app_class("hono", "Hono"). +router_app_class("hono/tiny", "Hono"). +router_app_class("hono/quick", "Hono"). +router_app_class("@hono/zod-openapi", "OpenAPIHono"). +// ── how a module may be named: Deno specifiers and URLs ───────────────────── +// router_spec_scheme(Prefix): `npm:hono@4`, `jsr:@hono/hono@^4/logger` name the package +// after the prefix, a version after `@` dropped; router_spec_url(Prefix, Package): a URL +// import of the package's published module (`@v3/mod.ts` is the package itself) +.decl router_spec_scheme(c0:symbol) +router_spec_scheme("npm:"). +router_spec_scheme("jsr:"). +.decl router_spec_url(c0:symbol, c1:symbol) +router_spec_url("https://deno.land/x/hono", "hono"). +router_spec_url("https://esm.sh/hono", "hono"). +// the JSR name of the package +router_app_class("@hono/hono", "Hono"). +// router_verb(Method, Verb): a route registration and the verb it serves +.decl router_verb(c0:symbol, c1:symbol) +router_verb("get", "GET"). router_verb("post", "POST"). +router_verb("put", "PUT"). router_verb("delete", "DELETE"). +router_verb("patch", "PATCH"). router_verb("options", "OPTIONS"). +router_verb("all", "ANY"). +// a verb as `on(...)` may spell it +.decl router_verb_name(c0:symbol, c1:symbol) +router_verb_name("get", "GET"). router_verb_name("GET", "GET"). +router_verb_name("post", "POST"). router_verb_name("POST", "POST"). +router_verb_name("put", "PUT"). router_verb_name("PUT", "PUT"). +router_verb_name("delete", "DELETE"). router_verb_name("DELETE", "DELETE"). +router_verb_name("patch", "PATCH"). router_verb_name("PATCH", "PATCH"). +router_verb_name("options", "OPTIONS"). router_verb_name("OPTIONS", "OPTIONS"). +router_verb_name("head", "HEAD"). router_verb_name("HEAD", "HEAD"). +.decl router_use(c0:symbol) +router_use("use"). +.decl router_on(c0:symbol) +router_on("on"). +.decl router_mount_app(c0:symbol) +router_mount_app("route"). +.decl router_base_path(c0:symbol) +router_base_path("basePath"). +.decl router_mount_foreign(c0:symbol) +router_mount_foreign("mount"). +.decl router_fallback(c0:symbol) +router_fallback("notFound"). +router_fallback("onError"). +.decl router_returns_app(c0:symbol) +router_returns_app(n) :- router_verb(n, _). +router_returns_app(n) :- router_use(n). +router_returns_app(n) :- router_on(n). +router_returns_app(n) :- router_mount_app(n). +router_returns_app(n) :- router_mount_foreign(n). +router_returns_app(n) :- router_fallback(n). +// router_mw_wrapper(Specifier, Name): `name(fn)` is middleware whose body is `fn` +.decl router_mw_wrapper(c0:symbol, c1:symbol) +router_mw_wrapper("hono/factory", "createMiddleware"). +// router_mw_combinator(Specifier, Name): `name(mw, ...)` is one middleware running its +// function arguments; router_mw_except: `name(paths, mw)` runs mw but on those paths +.decl router_mw_combinator(c0:symbol, c1:symbol) +router_mw_combinator("hono/combine", "every"). +router_mw_combinator("hono/combine", "some"). +.decl router_mw_except(c0:symbol, c1:symbol) +router_mw_except("hono/combine", "except"). +// router_handler_spread(Name): `...x.name(a, b)` spreads its arguments as handlers +.decl router_handler_spread(c0:symbol) +router_handler_spread("createHandlers"). +// ── a client that sends to a route (framework-behavior/http-rpc-client.dl) ── +// router_rpc_client: `name(base)` is a client whose property path is a route path and +// whose router_rpc_verb methods send (JavaScript has no type argument naming the app, so +// it is matched against every route); router_test_client: `name(app)` is the same over +// one app; router_app_request: `app.(path, init)` sends to the app itself +.decl router_rpc_client(c0:symbol, c1:symbol) +router_rpc_client("hono/client", "hc"). +.decl router_test_client(c0:symbol, c1:symbol) +router_test_client("hono/testing", "testClient"). +.decl router_rpc_index(c0:symbol) +router_rpc_index("index"). +.decl router_rpc_verb(c0:symbol, c1:symbol) +router_rpc_verb("$get", "GET"). router_rpc_verb("$post", "POST"). +router_rpc_verb("$put", "PUT"). router_rpc_verb("$delete", "DELETE"). +router_rpc_verb("$patch", "PATCH"). router_rpc_verb("$options", "OPTIONS"). +.decl router_app_request(c0:symbol) +router_app_request("request"). +.decl router_init_method_key(c0:symbol) +router_init_method_key("method"). +// router_fetch_fn: the platform function a program sends a request with (`fetch(url, init)`); router_url_class: the +// URL class (`new URL(path, base)`); router_local_host: a host name that is this machine +.decl router_fetch_fn(c0:symbol) +router_fetch_fn("fetch"). +.decl router_url_class(c0:symbol) +router_url_class("URL"). +.decl router_local_host(c0:symbol) +router_local_host("localhost"). router_local_host("127.0.0.1"). router_local_host("0.0.0.0"). router_local_host("[::1]"). diff --git a/graph/javascript/engine/framework-behavior/http-router-chain.dl b/graph/javascript/engine/framework-behavior/http-router-chain.dl new file mode 100644 index 000000000..f7977dc0c --- /dev/null +++ b/graph/javascript/engine/framework-behavior/http-router-chain.dl @@ -0,0 +1,54 @@ +// ============================================================================ +// FRAMEWORK-BEHAVIOR · HTTP ROUTER CHAIN (JavaScript: what the router does at request time) +// +// The JavaScript counterpart of typescript/engine/framework-behavior/http-router-chain.dl: +// +// ctx_key_flow(Setter, Reader, Key) +// a function on a route's chain writes a context key (`c.set('user', u)`) and a +// later function on the same chain reads it (`c.get('user')`, `c.var.user`). +// +// The context is the first parameter of a function the router calls; a key is what +// the value flow knows a string to be. A dynamic key, and a read no earlier function +// on any of its chains serves, are declared in framework_undetermined. +// ============================================================================ + +.decl jb_ctx_ref(c0:symbol, c1:symbol) +jb_ctx_ref(e, m) :- jr_node_fn(_, _, m), param_decl("client", _, "0", m, p), expr_param(_, p, e). + +.decl jb_set_site(c0:symbol, c1:symbol, c2:symbol) +jb_set_site(m, ce, k) :- call_site(_, "METHOD_CALL", "set", _, r, _, ce, _, _), jb_ctx_ref(r, m), call_arg(ce, 0, k). +.decl jb_get_site(c0:symbol, c1:symbol, c2:symbol) +jb_get_site(m, ce, k) :- call_site(_, "METHOD_CALL", "get", _, r, _, ce, _, _), jb_ctx_ref(r, m), call_arg(ce, 0, k). +.decl jb_set_at(c0:symbol, c1:symbol, c2:symbol) +jb_set_at(m, key, ce) :- jb_set_site(m, ce, k), expr_value(k, "str", key). +.decl jb_get_at(c0:symbol, c1:symbol, c2:symbol) +jb_get_at(m, key, ce) :- jb_get_site(m, ce, k), expr_value(k, "str", key). +jb_get_at(m, key, e) :- expr_kind(_, "PROPERTY_ACCESS", _, e), expr_name(_, key, e), expr_child(_, e, "ACCESS_TARGET", _, v), + expr_kind(_, "PROPERTY_ACCESS", _, v), expr_name(_, "var", v), expr_child(_, v, "ACCESS_TARGET", _, c), jb_ctx_ref(c, m). +.decl jb_keyed(c0:symbol) +jb_keyed(k) :- jb_set_site(_, _, k), expr_value(k, "str", _). +jb_keyed(k) :- jb_get_site(_, _, k), expr_value(k, "str", _). +framework_undetermined(m, "ctx_key", "dynamic_ctx_key") :- jb_set_site(m, _, k), !jb_keyed(k). +framework_undetermined(m, "ctx_key", "dynamic_ctx_key") :- jb_get_site(m, _, k), !jb_keyed(k). +.decl jb_set(c0:symbol, c1:symbol) +jb_set(m, key) :- jb_set_at(m, key, _). +.decl jb_get(c0:symbol, c1:symbol) +jb_get(m, key) :- jb_get_at(m, key, _). +.decl jb_pos(c0:symbol, c1:number, c2:number) +jb_pos(e, to_number(l), to_number(c)) :- (jb_set_at(_, _, e) ; jb_get_at(_, _, e)), expr_span(_, l, c, _, _, e). +.decl jb_set_before_get(c0:symbol, c1:symbol) +jb_set_before_get(m, key) :- jb_set_at(m, key, s), jb_get_at(m, key, g), jb_pos(s, sl, sc), jb_pos(g, gl, gc), + (sl < gl ; sl = gl, sc < gc). + +.decl jb_chain(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +jb_chain(h, vv, p, mw, to_number(pos)) :- middleware_applies(mw, h, vv, p, pos, _). +jb_chain(h, vv, p, h, 100000) :- http_route(h, vv, p, _, _). + +// ctx_key_flow(Setter, Reader, Key) — on some route's chain, Setter runs before Reader +// (or is Reader, setting before it reads) and writes the context key Reader reads. +ctx_key_flow(s, r, key) :- jb_set(s, key), jb_get(r, key), jb_chain(h, vv, p, s, i), jb_chain(h, vv, p, r, j), i < j. +ctx_key_flow(s, s, key) :- jb_set_before_get(s, key), jb_chain(_, _, _, s, _). +.decl jb_read_served(c0:symbol, c1:symbol) +jb_read_served(r, key) :- ctx_key_flow(_, r, key). +framework_undetermined(r, "ctx_key", "no_setter_on_chain") :- jb_get(r, key), jb_chain(_, _, _, r, _), + !jb_read_served(r, key). diff --git a/graph/javascript/engine/framework-behavior/http-rpc-client.dl b/graph/javascript/engine/framework-behavior/http-rpc-client.dl new file mode 100644 index 000000000..5a056988c --- /dev/null +++ b/graph/javascript/engine/framework-behavior/http-rpc-client.dl @@ -0,0 +1,295 @@ +// ============================================================================ +// FRAMEWORK-BEHAVIOR · HTTP RPC CLIENT (JavaScript: a request that names a route) +// +// The JavaScript counterpart of typescript/engine/framework-behavior/http-rpc-client.dl. +// Three ways a program sends a request to a route this engine has in http_route: +// * `app.request('/items/42', { method })` the app value itself, in a test; +// * `testClient(app).items[':id'].$get()` a client over an app value; +// * `hc(base).items[':id'].$get()` a client with no app named: JavaScript +// has no type argument, so it is matched against every route in the graph. +// Each property is a path segment (`index` adds none, `[':id']` the written segment) and +// `$` sends. The send joins the route whose full path it fits: `exact` when every +// segment is equal, `route_shape` when a route parameter stood in; of several, the fewest +// stand-ins win. A client with no app named is never better than `route_shape`. +// +// remote_edge(Sender, Handler, "http", " ", Confidence) +// remote_unserved(Sender, "http", " ") +// ============================================================================ + +// (client root call, app list; "*" = every app) +.decl jc_root(c0:symbol, c1:symbol) +jc_root(ce, "*") :- jr_knob_call(ce, spec, n), router_rpc_client(spec, n). +jc_root(ce, a) :- jr_knob_call(ce, spec, n), router_test_client(spec, n), call_arg(ce, 0, x), + expr_value(x, "router_app", a). + +// the path a client expression has accumulated +.decl jc_cpath(c0:symbol, c1:symbol, c2:symbol) +jc_cpath(ce, a, "") :- jc_root(ce, a). +jc_cpath(e, a, p) :- expr_kind(_, "IDENTIFIER", _, e), expr_binding(_, v, e), var_init(_, _, i, v), jc_cpath(i, a, p). +jc_cpath(e, a, p) :- expr_kind(_, "PROPERTY_ACCESS", _, e), expr_name(_, n, e), router_rpc_index(n), + expr_child(_, e, "ACCESS_TARGET", _, r), jc_cpath(r, a, p). +jc_cpath(e, a, cat(p, cat("/", n))) :- expr_kind(_, "PROPERTY_ACCESS", _, e), expr_name(_, n, e), + !router_rpc_index(n), !router_rpc_verb(n, _), expr_child(_, e, "ACCESS_TARGET", _, r), jc_cpath(r, a, p), + strlen(p) < 300. +jc_cpath(e, a, cat(p, cat("/", k))) :- expr_kind(_, "ELEMENT_ACCESS", _, e), expr_child(_, e, "ACCESS_TARGET", _, r), + jc_cpath(r, a, p), expr_child(_, e, "COMPUTED_KEY", _, x), expr_value(x, "str", k), strlen(p) < 300. +// a function that returns a client: each call is that client +jc_cpath(ce, a, p) :- expr_kind(_, "CALL", _, ce), expr_resolves_to_method(ce, w), jc_return_expr(w, e), jc_cpath(e, a, p). +.decl jc_return_expr(c0:symbol, c1:symbol) +jc_return_expr(w, e) :- expr_root(_, "RETURN", e), expr_owner(_, w, _, e). + +// (site, sender, app, verb, path) +.decl jc_send(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +jc_send(ce, from, a, vv, p) :- call_site(_, "METHOD_CALL", cn, _, r, from, ce, _, _), router_rpc_verb(cn, vv), + jc_cpath(r, a, p0), p0 != "", p = p0. +jc_send(ce, from, a, vv, "/") :- call_site(_, "METHOD_CALL", cn, _, r, from, ce, _, _), router_rpc_verb(cn, vv), + jc_cpath(r, a, ""). +// `app.request(path, init?)`: GET unless the init names a method +.decl jc_req(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +jc_req(ce, from, a, p) :- jc_req_raw(ce, from, a, raw), jc_req_path(raw, p). +// the URL as written: a path, or an absolute URL whose path is taken (`http://localhost/x`); the query +// string and fragment are not part of the path (SPEC 3.5, ruling R9) +.decl jc_req_raw(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +jc_req_raw(ce, from, a, raw) :- call_site(_, "METHOD_CALL", cn, _, r, from, ce, _, _), router_app_request(cn), + expr_value(r, "router_app", a), call_arg(ce, 0, x), expr_value(x, "str", raw), strlen(raw) >= 1. +.decl jc_sch(c0:symbol, c1:number) +jc_sch(raw, i) :- jc_req_raw(_, _, _, raw), strlen(raw) >= 3, i = range(0, strlen(raw) - 2), substr(raw, i, 3) = "://". +.decl jc_after(c0:symbol, c1:symbol) +jc_after(raw, r) :- jc_sch(raw, _), i = min x : { jc_sch(raw, x) }, r = substr(raw, i + 3, strlen(raw) - i - 3). +.decl jc_aslash(c0:symbol, c1:number) +jc_aslash(r, i) :- jc_after(_, r), i = range(0, strlen(r)), substr(r, i, 1) = "/". +.decl jc_full(c0:symbol, c1:symbol) +jc_full(raw, raw) :- jc_req_raw(_, _, _, raw), substr(raw, 0, 1) = "/". +jc_full(raw, p) :- jc_after(raw, r), m = min x : { jc_aslash(r, x) }, p = substr(r, m, strlen(r) - m). +jc_full(raw, "/") :- jc_after(raw, r), !jc_aslash(r, _). +.decl jc_cut(c0:symbol, c1:number) +jc_cut(f, i) :- jc_full(_, f), i = range(0, strlen(f)), (substr(f, i, 1) = "?" ; substr(f, i, 1) = "#"). +.decl jc_req_path(c0:symbol, c1:symbol) +jc_req_path(raw, f) :- jc_full(raw, f), !jc_cut(f, _). +jc_req_path(raw, q) :- jc_full(raw, f), m = min i : { jc_cut(f, i) }, m > 0, q = substr(f, 0, m). +jc_req_path(raw, "/") :- jc_full(raw, f), jc_cut(f, 0). +.decl jc_req_method(c0:symbol, c1:symbol) +jc_req_method(ce, vv) :- jc_req(ce, _, _, _), call_arg(ce, 1, o), expr_value(o, k, i), prop_value(k, i, key, "str", w), + router_init_method_key(key), router_verb_name(w, vv). +.decl jc_req_has_method(c0:symbol) +jc_req_has_method(ce) :- jc_req_method(ce, _). +jc_send(ce, from, a, vv, p) :- jc_req(ce, from, a, p), jc_req_method(ce, vv), !jc_req_mu(ce). +jc_send(ce, from, a, "GET", p) :- jc_req(ce, from, a, p), !jc_req_has_method(ce), !jc_req_mu(ce). +// a request whose method is not decidable (SPEC ruling R17) stands for every verb (`*`), as a fetch does below: an +// init that is not an object the program shows, a method that is not a literal verb, or a method set only in a branch +jc_send(ce, from, a, "*", p) :- jc_req(ce, from, a, p), jc_req_mu(ce). +.decl jc_req_mu(c0:symbol) +jc_req_mu(ce) :- jc_req(ce, _, _, _), call_arg(ce, 1, o), !jc_req_init_obj(o). +jc_req_mu(ce) :- jc_req(ce, _, _, _), call_arg(ce, 1, o), expr_value(o, "obj", l), jf_lit_method(l), !jc_req_has_method(ce). +jc_req_mu(ce) :- jc_req(ce, _, _, _), call_arg(ce, 1, o), expr_value(o, "obj", l), jf_cond_method_write(l), !jf_lit_method(l). +.decl jc_req_init_obj(c0:symbol) +jc_req_init_obj(o) :- jc_req(ce, _, _, _), call_arg(ce, 1, o), expr_value(o, "obj", _). + +// the routes a send may reach: (app, verb, full path, handler) +.decl jc_app_route(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +jc_app_route(a, vv, fp, h) :- jc_send(_, _, a, _, _), a != "*", http_route(h, vv, fp, a, _). +jc_app_route(a, vv, fp, h) :- jc_send(_, _, a, _, _), a != "*", jr_flat(r, a, "/", _, _, _), http_route(h, vv, fp, r, _). +jc_app_route("*", vv, fp, h) :- jc_send(_, _, "*", _, _), http_route(h, vv, fp, _, _). + +.decl jc_path(c0:symbol) +jc_path(p) :- jc_send(_, _, _, _, p). +jc_path(p) :- jc_app_route(_, _, p, _). +.decl jc_slash(c0:symbol, c1:number) +jc_slash(p, i) :- jc_path(p), i = range(0, strlen(p)), substr(p, i, 1) = "/". +.decl jc_next(c0:symbol, c1:number, c2:number) +jc_next(p, i, j) :- jc_slash(p, i), j = min x : { jc_slash(p, x), x > i }. +.decl jc_has_next(c0:symbol, c1:number) +jc_has_next(p, i) :- jc_next(p, i, _). +.decl jc_seg_at(c0:symbol, c1:number, c2:symbol) +jc_seg_at(p, i, t) :- jc_next(p, i, j), t = substr(p, i + 1, j - i - 1). +jc_seg_at(p, i, t) :- jc_slash(p, i), !jc_has_next(p, i), t = substr(p, i + 1, strlen(p) - i - 1). +.decl jc_seg_ne(c0:symbol, c1:number) +jc_seg_ne(p, i) :- jc_seg_at(p, i, t), t != "". +.decl jc_seg(c0:symbol, c1:number, c2:symbol) +jc_seg(p, k, t) :- jc_seg_at(p, i, t), t != "", k = count : { jc_seg_ne(p, x), x < i }. +.decl jc_nseg(c0:symbol, c1:number) +jc_nseg(p, n) :- jc_path(p), n = count : { jc_seg(p, _, _) }. +.decl jc_param(c0:symbol) +jc_param(t) :- jc_seg(_, _, t), substr(t, 0, 1) = ":". +jc_param("*"). + +.decl jc_pair(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +jc_pair(ce, h, p, rp) :- jc_send(ce, _, a, sv, p), jc_app_route(a, rv, rp, h), (rv = sv ; rv = "ANY" ; sv = "*"), + jc_nseg(p, n), jc_nseg(rp, n). +.decl jc_conflict(c0:symbol, c1:symbol) +jc_conflict(p, rp) :- jc_pair(_, _, p, rp), jc_seg(p, k, x), jc_seg(rp, k, y), x != y, !jc_param(y). +.decl jc_ndiff(c0:symbol, c1:symbol, c2:number) +jc_ndiff(p, rp, n) :- jc_pair(_, _, p, rp), !jc_conflict(p, rp), n = count : { jc_seg(p, k, x), jc_seg(rp, k, y), x != y }. +.decl jc_best(c0:symbol, c1:number) +jc_best(ce, b) :- jc_pair(ce, _, p, _), jc_ndiff(p, _, _), b = min n : { jc_pair(ce, _, p2, rp2), jc_ndiff(p2, rp2, n) }. + +// remote_edge(Sender, Handler, "http", " ", Confidence) — a request this +// function sends that the handler serves, joined on the route path +remote_edge(from, h, "http", cat(sv, cat(" ", p)), "exact") :- jc_send(ce, from, a, sv, p), a != "*", sv != "*", jc_pair(ce, h, p, rp), + jc_ndiff(p, rp, 0), jc_best(ce, 0), from != h. +remote_edge(from, h, "http", cat(sv, cat(" ", p)), "route_shape") :- jc_send(ce, from, _, sv, p), sv != "*", jc_pair(ce, h, p, rp), + jc_ndiff(p, rp, n), jc_best(ce, n), from != h, (n > 0 ; jc_unbound(ce)). +// a send whose verb is not decidable: every route of any verb its path fits best, "may reach" (SPEC ruling R17) +remote_edge(from, h, "http", cat("* ", p), "method_unknown") :- jc_send(ce, from, _, "*", p), jc_pair(ce, h, p, rp), + jc_ndiff(p, rp, n), jc_best(ce, n), from != h. +.decl jc_unbound(c0:symbol) +jc_unbound(ce) :- jc_send(ce, _, "*", _, _). +.decl jc_served(c0:symbol) +jc_served(ce) :- jc_best(ce, _). +// remote_unserved(Sender, "http", " ") — a send no route fits +remote_unserved(from, "http", cat(sv, cat(" ", p))) :- jc_send(ce, from, _, sv, p), !jc_served(ce). + +// ── `fetch(url, init)`: a request to this machine, or to a base the program does not spell (SPEC R14, R14a) ── +// The URL as written, `{}` for every piece the program does not show: a literal, a template (each substitution its +// literal value when the flow engine knows exactly one, else a hole), `new URL(path, base)`. A request to localhost +// joins with the confidence of its match; one whose base is a hole (`${host}/x`, `new URL('/x', endpoint)`), whose +// host is computed, or whose literal host is a single label (`http://billing:9000`, a service of the same +// deployment) joins with confidence base_unknown; a relative path or a host with a dot names no app here. +.decl jf_call(c0:symbol, c1:symbol) +jf_call(ce, from) :- call_site(_, "FUNCTION_CALL", n, _, _, from, ce, _, _), router_fetch_fn(n), !expr_resolves_to_method(ce, _). +// the expressions whose text is needed: the URL argument, and the path argument of `new URL(path, base)` +.decl jf_need(c0:symbol) +jf_need(x) :- jf_call(ce, _), call_arg(ce, 0, x). +jf_need(pa) :- jf_need(x), expr_kind(_, "NEW", _, x), expr_child(_, x, "ARGUMENT", "1", pa). +.decl jf_txt(c0:symbol, c1:symbol) +jf_txt(x, s) :- jf_need(x), expr_value(x, "str", s). +.decl jf_tmpl(c0:symbol, c1:symbol, c2:symbol) +jf_tmpl(x, x, raw) :- jf_need(x), expr_kind(_, "TEMPLATE", _, x), expr_child(_, x, _, _, _), + expr_text(_, raw, x), strlen(raw) >= 2. +.decl jf_sub_n(c0:symbol, c1:symbol, c2:number) +jf_sub_n(x, idx, n) :- jf_tmpl(_, x, _), expr_child(_, x, "TEMPLATE_SUBSTITUTION", idx, c), n = count : { expr_value(c, "str", _) }. +.decl jf_sub(c0:symbol, c1:symbol, c2:symbol) +jf_sub(x, idx, s) :- jf_sub_n(x, idx, 1), expr_child(_, x, "TEMPLATE_SUBSTITUTION", idx, c), expr_value(c, "str", s). +jf_sub(x, idx, "{}") :- jf_sub_n(x, idx, n), n != 1. +// (template, index, text so far, brace depth inside a substitution, substitutions passed) +.decl jf_tstep(c0:symbol, c1:number, c2:symbol, c3:number, c4:number) +jf_tstep(x, 1, "", 0, 0) :- jf_tmpl(_, x, _). +jf_tstep(x, i + 2, cat(acc, s), 1, h) :- jf_tstep(x, i, acc, 0, h), jf_tmpl(_, x, raw), i + 2 < strlen(raw), + substr(raw, i, 2) = "${", jf_sub(x, to_string(h), s), strlen(acc) < 300. +jf_tstep(x, i + 1, cat(acc, c), 0, h) :- jf_tstep(x, i, acc, 0, h), jf_tmpl(_, x, raw), i + 1 < strlen(raw), + c = substr(raw, i, 1), (c != "$" ; substr(raw, i + 1, 1) != "{"), strlen(acc) < 300. +jf_tstep(x, i + 1, acc, d + 1, h) :- jf_tstep(x, i, acc, d, h), d > 0, jf_tmpl(_, x, raw), i + 1 < strlen(raw), substr(raw, i, 1) = "{", d < 20. +jf_tstep(x, i + 1, acc, d - 1, h) :- jf_tstep(x, i, acc, d, h), d > 1, jf_tmpl(_, x, raw), i + 1 < strlen(raw), substr(raw, i, 1) = "}". +jf_tstep(x, i + 1, acc, 0, h + 1) :- jf_tstep(x, i, acc, 1, h), jf_tmpl(_, x, raw), i + 1 < strlen(raw), substr(raw, i, 1) = "}". +jf_tstep(x, i + 1, acc, d, h) :- jf_tstep(x, i, acc, d, h), d > 0, jf_tmpl(_, x, raw), i + 1 < strlen(raw), + c = substr(raw, i, 1), c != "{", c != "}". +jf_txt(x, v) :- jf_tmpl(x, _, raw), jf_tstep(x, i, v, 0, _), i = strlen(raw) - 1. +.decl jf_url(c0:symbol, c1:symbol) +jf_url(ce, s) :- jf_call(ce, _), call_arg(ce, 0, x), jf_txt(x, s). +// `new URL(path, base)`: a literal absolute path under the base's origin, a hole when the base is not a literal +.decl jf_newurl(c0:symbol, c1:symbol, c2:symbol) +jf_newurl(ce, p, b) :- jf_call(ce, _), call_arg(ce, 0, x), expr_kind(_, "NEW", _, x), expr_child(_, x, "CALLEE", _, cc), + expr_text(_, cn, cc), router_url_class(cn), expr_child(_, x, "ARGUMENT", "1", pa), jf_txt(pa, p), + strlen(p) >= 1, substr(p, 0, 1) = "/", expr_child(_, x, "ARGUMENT", "2", b). +jf_url(ce, cat("{}", p)) :- jf_newurl(ce, p, b), !expr_value(b, "str", _). +// …its literal base's origin: `scheme://host` (a stratum of its own, before the URLs it completes) +.decl jf_origin_need(c0:symbol) +jf_origin_need(bs) :- jf_newurl(_, _, b), expr_value(b, "str", bs). +.decl jf_osch(c0:symbol, c1:number) +jf_osch(u, i) :- jf_origin_need(u), strlen(u) >= 3, i = range(0, strlen(u) - 2), substr(u, i, 3) = "://". +.decl jf_oend(c0:symbol, c1:number) +jf_oend(u, j) :- jf_osch(u, _), i = min y : { jf_osch(u, y) }, j = range(i + 3, strlen(u)), (substr(u, j, 1) = "/" ; substr(u, j, 1) = "?" ; substr(u, j, 1) = "#"). +.decl jf_origin(c0:symbol, c1:symbol) +jf_origin(u, substr(u, 0, j)) :- jf_oend(u, _), j = min y : { jf_oend(u, y) }. +jf_origin(u, u) :- jf_osch(u, _), !jf_oend(u, _). +.decl jf_url_all(c0:symbol, c1:symbol) +jf_url_all(ce, u) :- jf_url(ce, u). +jf_url_all(ce, cat(o, p)) :- jf_newurl(ce, p, b), expr_value(b, "str", bs), jf_origin(bs, o). +.decl jf_sch(c0:symbol, c1:number) +jf_sch(u, i) :- jf_url_all(_, u), strlen(u) >= 3, i = range(0, strlen(u) - 2), substr(u, i, 3) = "://". +.decl jf_after(c0:symbol, c1:symbol, c2:number) +jf_after(u, r, i) :- jf_sch(u, _), i = min y : { jf_sch(u, y) }, r = substr(u, i + 3, strlen(u) - i - 3). +.decl jf_rslash(c0:symbol, c1:number) +jf_rslash(r, j) :- jf_after(_, r, _), j = range(0, strlen(r)), (substr(r, j, 1) = "/" ; substr(r, j, 1) = "?" ; substr(r, j, 1) = "#"). +.decl jf_hostpath(c0:symbol, c1:symbol, c2:symbol) +jf_hostpath(u, h, p) :- jf_after(u, r, _), jf_rslash(r, _), j = min y : { jf_rslash(r, y) }, h = substr(r, 0, j), p = substr(r, j, strlen(r) - j). +jf_hostpath(u, r, "/") :- jf_after(u, r, _), !jf_rslash(r, _). +.decl jf_hostname(c0:symbol, c1:symbol) +jf_hostname(h, substr(h, 0, i)) :- jf_hostpath(_, h, _), substr(h, 0, 1) != "[", contains(":", h), + i = min y : { y = range(0, strlen(h)), substr(h, y, 1) = ":" }. +jf_hostname(h, h) :- jf_hostpath(_, h, _), (!contains(":", h) ; substr(h, 0, 1) = "["). +// (call, raw path, confidence) +.decl jf_raw(c0:symbol, c1:symbol, c2:symbol) +jf_raw(ce, p, "exact") :- jf_url_all(ce, u), jf_hostpath(u, h, p), jf_hostname(h, hn), router_local_host(hn). +jf_raw(ce, p, "base_unknown") :- jf_url_all(ce, u), jf_hostpath(u, h, p), jf_hostname(h, hn), contains("{}", hn). +jf_raw(ce, p, "base_unknown") :- jf_url_all(ce, u), jf_hostpath(u, h, p), jf_hostname(h, hn), hn != "", !contains("{}", hn), + !contains(".", hn), !router_local_host(hn), substr(hn, 0, 1) != "[". +// a leading hole (`${host}/x`, an optional `:port` after it) +jf_raw(ce, p, "base_unknown") :- jf_url_all(ce, u), !jf_sch(u, _), strlen(u) > 3, substr(u, 0, 2) = "{}", r = substr(u, 2, strlen(u) - 2), + substr(r, 0, 1) = "/", p = r. +jf_raw(ce, p, "base_unknown") :- jf_url_all(ce, u), jf_portrest(u, r), j = min y : { y = range(0, strlen(r)), substr(r, y, 1) = "/" }, + p = substr(r, j, strlen(r) - j). +.decl jf_portrest(c0:symbol, c1:symbol) +jf_portrest(u, r) :- jf_url_all(_, u), !jf_sch(u, _), strlen(u) > 3, substr(u, 0, 3) = "{}:", r = substr(u, 3, strlen(u) - 3), + contains("/", r). +// the query string and fragment are not the path (SPEC R9) +.decl jf_cut(c0:symbol, c1:number) +jf_cut(p, i) :- jf_raw(_, p, _), i = range(0, strlen(p)), (substr(p, i, 1) = "?" ; substr(p, i, 1) = "#"). +.decl jf_path(c0:symbol, c1:symbol) +jf_path(p, p) :- jf_raw(_, p, _), !jf_cut(p, _). +jf_path(p, q) :- jf_raw(_, p, _), m = min i : { jf_cut(p, i) }, m > 0, q = substr(p, 0, m). +jf_path(p, "/") :- jf_raw(_, p, _), jf_cut(p, 0). +.decl jf_method(c0:symbol, c1:symbol) +jf_method(ce, vv) :- jf_raw(ce, _, _), call_arg(ce, 1, o), expr_value(o, k, i), prop_value(k, i, key, "str", w), + router_init_method_key(key), router_verb_name(w, vv). +.decl jf_has_method(c0:symbol) +jf_has_method(ce) :- jf_method(ce, _). +// (call, sender, verb, path, confidence) +.decl jf_req(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +jf_req(ce, from, vv, p, cf) :- jf_raw(ce, raw, cf), jf_path(raw, p), jf_call(ce, from), jf_method(ce, vv), !jf_mu(ce). +jf_req(ce, from, "GET", p, cf) :- jf_raw(ce, raw, cf), jf_path(raw, p), jf_call(ce, from), !jf_has_method(ce), !jf_mu(ce). +// a request whose method is not decidable (SPEC ruling R17) stands for every verb (`*`): an init object whose OWN +// text names no method, given one only inside a branch (`if (push) init.method = 'POST'`, GET or POST at run +// time), an init that is not an object the program shows, or a method that is not a literal +jf_req(ce, from, "*", p, cf) :- jf_raw(ce, raw, cf), jf_path(raw, p), jf_call(ce, from), jf_mu(ce). +.decl jf_mu(c0:symbol) +jf_mu(ce) :- jf_raw(ce, _, _), call_arg(ce, 1, o), expr_value(o, "obj", l), jf_cond_method_write(l), !jf_lit_method(l). +jf_mu(ce) :- jf_raw(ce, _, _), call_arg(ce, 1, o), !jf_init_obj(o). +jf_mu(ce) :- jf_raw(ce, _, _), call_arg(ce, 1, o), expr_value(o, "obj", l), jf_lit_method(l), !jf_has_method(ce). +.decl jf_init_obj(c0:symbol) +jf_init_obj(o) :- jf_raw(ce, _, _), call_arg(ce, 1, o), expr_value(o, "obj", _). +.decl jf_lit_method(c0:symbol) +jf_lit_method(l) :- expr_child(_, l, "PROPERTY_KEY", _, key), expr_name(_, n, key), router_init_method_key(n). +.decl jf_cond_method_write(c0:symbol) +jf_cond_method_write(l) :- expr_kind(_, "ASSIGNMENT", _, a), expr_child(_, a, "ASSIGNMENT_TARGET", _, tgt), + expr_kind(_, "PROPERTY_ACCESS", _, tgt), expr_name(_, n, tgt), router_init_method_key(n), + expr_child(_, tgt, "ACCESS_TARGET", _, r), expr_value(r, "obj", l), expr_kind(_, "OBJECT_LITERAL", _, l), + expr_owner(_, h, mod, a), expr_span(_, ln, c, _, _, a), block_owner(k, h, b), jr_cond_kind(k), block_span(_, mod, l1, c1, l2, c2, b), + (to_number(ln) > to_number(l1) ; to_number(ln) = to_number(l1), to_number(c) >= to_number(c1)), + (to_number(ln) < to_number(l2) ; to_number(ln) = to_number(l2), to_number(c) <= to_number(c2)). +jc_path(p) :- jf_req(_, _, _, p, _). +jc_path(fp) :- jf_req(_, _, _, _, _), http_route(_, _, fp, _, _). +// a route fits when every segment is equal, or the route's is a parameter, or the request's is a hole +.decl jf_pair(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +jf_pair(ce, h, p, rp) :- jf_req(ce, _, sv, p, _), http_route(h, rv, rp, _, _), (rv = sv ; rv = "ANY" ; sv = "*"), jc_nseg(p, n), jc_nseg(rp, n). +.decl jf_conflict(c0:symbol, c1:symbol) +jf_conflict(p, rp) :- jf_pair(_, _, p, rp), jc_seg(p, k, x), jc_seg(rp, k, y), x != y, !jc_param(y), x != "{}". +.decl jf_ndiff(c0:symbol, c1:symbol, c2:number) +jf_ndiff(p, rp, n) :- jf_pair(_, _, p, rp), !jf_conflict(p, rp), n = count : { jc_seg(p, k, x), jc_seg(rp, k, y), x != y }. +.decl jf_best(c0:symbol, c1:number) +jf_best(ce, b) :- jf_pair(ce, _, p, _), jf_ndiff(p, _, _), b = min n : { jf_pair(ce, _, p2, rp2), jf_ndiff(p2, rp2, n) }. +// a path with a hole may be any request of its shape: every route it fits; a written path: the route that fits best +remote_edge(from, h, "http", cat(sv, cat(" ", p)), "base_unknown") :- jf_req(ce, from, sv, p, "base_unknown"), jf_pair(ce, h, p, rp), + jf_ndiff(p, rp, n), (contains("{}", p) ; jf_best(ce, n)), from != h, jf_has_lit(p), !jf_catchall(rp), !jf_test_only(ce, h, rp). +// …nor, from code outside the tests, a route only an app built in a test file serves (that app is never deployed) +.decl jf_test_path(c0:symbol) +jf_test_path(fp) :- module_file("client", fp, _), + (contains(".test.", fp) ; contains(".spec.", fp) ; contains("__tests__/", fp) ; contains("/test/", fp) ; contains("/tests/", fp) ; + contains("/e2e/", fp) ; substr(fp, 0, 5) = "test/" ; substr(fp, 0, 6) = "tests/" ; substr(fp, 0, 4) = "e2e/"). +.decl jf_route_app_in_test(c0:symbol, c1:symbol, c2:number) +jf_route_app_in_test(h, rp, t) :- jf_pair(_, h, _, rp), http_route(h, _, rp, a, _), expr_owner(_, _, mod, a), module_file("client", fp, mod), + (jf_test_path(fp), t = 1 ; !jf_test_path(fp), t = 0). +.decl jf_test_only(c0:symbol, c1:symbol, c2:symbol) +jf_test_only(ce, h, rp) :- jf_pair(ce, h, _, rp), jf_route_app_in_test(h, rp, 1), !jf_route_app_in_test(h, rp, 0), + expr_owner(_, _, mod, ce), module_file("client", fp, mod), !jf_test_path(fp). +// a request whose base is unknown names a route only through a written segment, and never a catch-all (`*`) +.decl jf_has_lit(c0:symbol) +jf_has_lit(p) :- jf_req(_, _, _, p, _), jc_seg(p, _, x), x != "{}". +.decl jf_catchall(c0:symbol) +jf_catchall(rp) :- jf_pair(_, _, _, rp), jc_seg(rp, _, "*"). +remote_edge(from, h, "http", cat(sv, cat(" ", p)), "exact") :- jf_req(ce, from, sv, p, "exact"), sv != "*", jf_pair(ce, h, p, rp), + jf_ndiff(p, rp, 0), jf_best(ce, 0), !contains("{}", p), from != h. +remote_edge(from, h, "http", cat(sv, cat(" ", p)), "route_shape") :- jf_req(ce, from, sv, p, "exact"), sv != "*", jf_pair(ce, h, p, rp), + jf_ndiff(p, rp, n), n > 0, (contains("{}", p) ; jf_best(ce, n)), from != h. +remote_edge(from, h, "http", cat("* ", p), "method_unknown") :- jf_req(ce, from, "*", p, "exact"), jf_pair(ce, h, p, rp), + jf_ndiff(p, rp, n), (contains("{}", p) ; jf_best(ce, n)), from != h. diff --git a/graph/javascript/souffle/decls_all.dl b/graph/javascript/souffle/decls_all.dl index bc489cb6e..50b48f0c2 100644 --- a/graph/javascript/souffle/decls_all.dl +++ b/graph/javascript/souffle/decls_all.dl @@ -622,3 +622,15 @@ .decl state_call_alloc(c0:symbol, c1:symbol, c2:symbol) .decl state_call_open(c0:symbol, c1:symbol) .decl state_world_of_gated(c0:symbol, c1:symbol) +.decl http_route(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +.decl http_route_partial(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +.decl http_mount(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +.decl middleware_applies(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol) +.decl middleware_site(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +.decl middleware_registered_at(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol) +.decl framework_undetermined(c0:symbol, c1:symbol, c2:symbol) +.decl remote_undetermined(c0:symbol, c1:symbol, c2:symbol) +.decl ctx_key_flow(c0:symbol, c1:symbol, c2:symbol) +.decl remote_edge(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +.decl remote_unserved(c0:symbol, c1:symbol, c2:symbol) +.decl route_registered_by(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) diff --git a/graph/javascript/souffle/export_manifest.tsv b/graph/javascript/souffle/export_manifest.tsv index d62ccfa3a..46b0df9ae 100644 --- a/graph/javascript/souffle/export_manifest.tsv +++ b/graph/javascript/souffle/export_manifest.tsv @@ -49,3 +49,15 @@ state_call_alloc state-call-alloc.csv state_call_open state-call-open.csv state_world_of_gated state-world.csv field_access field-access.csv +http_route http-route.csv +http_route_partial http-route-partial.csv +http_mount http-mount.csv +middleware_applies middleware-applies.csv +middleware_site middleware-site.csv +middleware_registered_at middleware-registered-at.csv +framework_undetermined framework-undetermined.csv +remote_undetermined remote-undetermined.csv +ctx_key_flow ctx-key-flow.csv +remote_edge remote-edge.csv +remote_unserved remote-unserved.csv +route_registered_by route-registered-by.csv diff --git a/graph/test/javascript/hono/.gitignore b/graph/test/javascript/hono/.gitignore new file mode 100644 index 000000000..ba004eff9 --- /dev/null +++ b/graph/test/javascript/hono/.gitignore @@ -0,0 +1,2 @@ +.work/ +*.draft diff --git a/graph/test/javascript/hono/cases/js/bun-websocket-destructure/src/index.js b/graph/test/javascript/hono/cases/js/bun-websocket-destructure/src/index.js new file mode 100644 index 000000000..57ea90d35 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/bun-websocket-destructure/src/index.js @@ -0,0 +1,10 @@ +// census: none (oracle gap O10, JavaScript): a library function taken by destructuring a library call's result +import { Hono } from 'hono' +import { createBunWebSocket } from 'hono/bun' + +const { upgradeWebSocket } = createBunWebSocket() + +const app = new Hono() +app.get('/ws', upgradeWebSocket(() => ({ onMessage() {} }))) + +export default app diff --git a/graph/test/javascript/hono/cases/js/bun-websocket-destructure/src/package.json b/graph/test/javascript/hono/cases/js/bun-websocket-destructure/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/js/bun-websocket-destructure/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/cases/js/cjs-basic/src/index.js b/graph/test/javascript/hono/cases/js/cjs-basic/src/index.js new file mode 100644 index 000000000..623aa9bac --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-basic/src/index.js @@ -0,0 +1,13 @@ +// census: new_hono, route_mount_literal, builtin_mw_import (CommonJS JavaScript; SPEC 10 C2: no CJS project +// in the corpus, torture-only) +// require('hono') destructured, a library middleware by require, a sub-app from module.exports. +const { Hono } = require('hono') +const { cors } = require('hono/cors') +const users = require('./users') + +const app = new Hono() +app.use('/api/*', cors()) +app.route('/api/users', users) +app.get('/health', (c) => c.text('ok')) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-basic/src/package.json b/graph/test/javascript/hono/cases/js/cjs-basic/src/package.json new file mode 100644 index 000000000..729ac4d93 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-basic/src/package.json @@ -0,0 +1 @@ +{"type":"commonjs"} diff --git a/graph/test/javascript/hono/cases/js/cjs-basic/src/users.js b/graph/test/javascript/hono/cases/js/cjs-basic/src/users.js new file mode 100644 index 000000000..4bd75392c --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-basic/src/users.js @@ -0,0 +1,6 @@ +const { Hono } = require('hono') + +const users = new Hono() +users.get('/:id', (c) => c.json({ id: c.req.param('id') })) + +module.exports = users diff --git a/graph/test/javascript/hono/cases/js/cjs-context/src/index.js b/graph/test/javascript/hono/cases/js/cjs-context/src/index.js new file mode 100644 index 000000000..7c7bde6e1 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-context/src/index.js @@ -0,0 +1,16 @@ +// census: none for CommonJS (corpus gap); CJS twin of js/esm-context +const { Hono } = require('hono') +const { createMiddleware } = require('hono/factory') + +const withUser = createMiddleware(async (c, next) => { + c.set('user', { id: 'u1' }) + await next() +}) + +const app = new Hono() +app.use('/me/*', withUser) +app.get('/me/profile', (c) => c.json(c.get('user'))) +app.get('/me/id', (c) => c.text(c.var.user.id)) +app.get('/public', (c) => c.text(String(c.get('user')))) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-context/src/package.json b/graph/test/javascript/hono/cases/js/cjs-context/src/package.json new file mode 100644 index 000000000..729ac4d93 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-context/src/package.json @@ -0,0 +1 @@ +{"type":"commonjs"} diff --git a/graph/test/javascript/hono/cases/js/cjs-exports-members/src/app.js b/graph/test/javascript/hono/cases/js/cjs-exports-members/src/app.js new file mode 100644 index 000000000..659538b91 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-exports-members/src/app.js @@ -0,0 +1,14 @@ +// census: none for CommonJS (corpus gap in census; shape of a CJS-on-Bun project): oracle gap O7 +// Middleware exported as `module.exports.x = fn` and imported by destructured require; a library middleware +// from a package the program cannot resolve stays in the chain. +const { Hono } = require('hono') +const { authenticate, isAdmin } = require('./auth') +const { limiter } = require('acme-rate-limit') + +const app = new Hono() +app.use('/api/*', limiter({ max: 10 })) +app.use('/api/*', authenticate) +app.get('/api/me', (c) => c.json(c.get('user'))) +app.delete('/api/users/:id', isAdmin, (c) => c.body(null, 204)) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-exports-members/src/auth.js b/graph/test/javascript/hono/cases/js/cjs-exports-members/src/auth.js new file mode 100644 index 000000000..29ea6df9e --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-exports-members/src/auth.js @@ -0,0 +1,7 @@ +module.exports.authenticate = async (c, next) => { + c.set('user', { id: 'u' }) + await next() +} +module.exports.isAdmin = async (c, next) => { + await next() +} diff --git a/graph/test/javascript/hono/cases/js/cjs-exports-members/src/package.json b/graph/test/javascript/hono/cases/js/cjs-exports-members/src/package.json new file mode 100644 index 000000000..729ac4d93 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-exports-members/src/package.json @@ -0,0 +1 @@ +{"type":"commonjs"} diff --git a/graph/test/javascript/hono/cases/js/cjs-negative/src/index.js b/graph/test/javascript/hono/cases/js/cjs-negative/src/index.js new file mode 100644 index 000000000..2b42b09a4 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-negative/src/index.js @@ -0,0 +1,19 @@ +// census: none for CommonJS (corpus gap); CJS twin of js/negative-js +const { Hono } = require('hono') + +const cache = new Map() +cache.set('/x', () => 'cached') +cache.get('/x') + +const router = { + get(path, fn) { return fn }, + use(fn) { return fn }, + route(path, other) { return other }, +} +router.get('/not-hono', () => 'nope') +router.use(() => {}) + +const app = new Hono() +app.get('/real', (c) => c.text('real')) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-negative/src/package.json b/graph/test/javascript/hono/cases/js/cjs-negative/src/package.json new file mode 100644 index 000000000..729ac4d93 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-negative/src/package.json @@ -0,0 +1 @@ +{"type":"commonjs"} diff --git a/graph/test/javascript/hono/cases/js/cjs-routes-mount/src/index.cjs b/graph/test/javascript/hono/cases/js/cjs-routes-mount/src/index.cjs new file mode 100644 index 000000000..4e2e435e7 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-routes-mount/src/index.cjs @@ -0,0 +1,16 @@ +// census: none for CommonJS (corpus gap: no CJS Hono app in the corpus); CJS twin of js/esm-basic +// The same routes, middleware and mount written with require / module.exports in .cjs files. +const { Hono } = require('hono') +const users = require('./users.cjs') + +const audit = async (c, next) => { + await next() +} + +const app = new Hono() +app.use(audit) +app.get('/', (c) => c.text('home')) +app.route('/users', users) +app.get('/items/:id', audit, (c) => c.text(c.req.param('id'))) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-routes-mount/src/users.cjs b/graph/test/javascript/hono/cases/js/cjs-routes-mount/src/users.cjs new file mode 100644 index 000000000..dd0c60c98 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-routes-mount/src/users.cjs @@ -0,0 +1,7 @@ +const { Hono } = require('hono') + +const users = new Hono() +users.get('/', (c) => c.json([])) +users.post('/', (c) => c.json({}, 201)) + +module.exports = users diff --git a/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/package.json b/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/package.json new file mode 100644 index 000000000..729ac4d93 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/package.json @@ -0,0 +1 @@ +{"type":"commonjs"} diff --git a/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/routes/container.js b/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/routes/container.js new file mode 100644 index 000000000..02c7d531d --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/routes/container.js @@ -0,0 +1,14 @@ +const { Hono } = require('hono') +const { upgradeWebSocket } = require('../utils/websocket') + +const app = new Hono() + +app.get('/:id', (c) => c.json({ id: c.req.param('id') })) + +app.get('/:id/logs/stream', upgradeWebSocket(async (c) => ({ + onOpen(_evt, ws) { + ws.send(c.req.param('id')) + }, +}))) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/server.js b/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/server.js new file mode 100644 index 000000000..3e0169db6 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/server.js @@ -0,0 +1,10 @@ +// census: cjs_require_hono_destructure (oracle gap O10 residual, CommonJS) +// upgradeWebSocket comes from a PROJECT CommonJS module that destructures createBunWebSocket() and re-exports it in +// module.exports: the route's handler is the library call, keyed at the call. +const { Hono } = require('hono') +const containerRoutes = require('./routes/container') + +const app = new Hono() +app.route('/api/containers', containerRoutes) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/utils/websocket.js b/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/utils/websocket.js new file mode 100644 index 000000000..e90f99d1e --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-websocket-module/src/utils/websocket.js @@ -0,0 +1,5 @@ +const { createBunWebSocket } = require('hono/bun') + +const { upgradeWebSocket, websocket } = createBunWebSocket() + +module.exports = { upgradeWebSocket, websocket } diff --git a/graph/test/javascript/hono/cases/js/create-middleware-factory/src/app.js b/graph/test/javascript/hono/cases/js/create-middleware-factory/src/app.js new file mode 100644 index 000000000..5e1507c57 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/create-middleware-factory/src/app.js @@ -0,0 +1,11 @@ +// census: create_middleware, mw_factory_returning_fn, ctx_set, ctx_get (violation hono-1-07, JavaScript) +// In JavaScript as in TypeScript the middleware is the function passed to createMiddleware inside the project +// factory: it is the http entry, it is on the chain, and its c.set reaches the handler's c.get. +import { Hono } from 'hono' +import { sessionMiddleware } from './session.js' + +const app = new Hono() +app.use(sessionMiddleware('k')) +app.get('/me', (c) => c.json(c.get('session'))) + +export default app diff --git a/graph/test/javascript/hono/cases/js/create-middleware-factory/src/package.json b/graph/test/javascript/hono/cases/js/create-middleware-factory/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/js/create-middleware-factory/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/cases/js/create-middleware-factory/src/session.js b/graph/test/javascript/hono/cases/js/create-middleware-factory/src/session.js new file mode 100644 index 000000000..dd7383332 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/create-middleware-factory/src/session.js @@ -0,0 +1,8 @@ +import { createMiddleware } from 'hono/factory' + +export function sessionMiddleware(secret) { + return createMiddleware(async (c, next) => { + c.set('session', { secret }) + await next() + }) +} diff --git a/graph/test/javascript/hono/cases/js/esm-basic/src/index.js b/graph/test/javascript/hono/cases/js/esm-basic/src/index.js new file mode 100644 index 000000000..2f30f844d --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-basic/src/index.js @@ -0,0 +1,16 @@ +// census: import_hono_named, route_mount_literal, use_global, export_default_app (ESM JavaScript) +// The TypeScript routing and composition shapes, written in ESM .js and .mjs with no types at all. +import { Hono } from 'hono' +import users from './users.mjs' + +const audit = async (c, next) => { + await next() +} + +const app = new Hono() +app.use(audit) +app.get('/', (c) => c.text('home')) +app.route('/users', users) +app.get('/items/:id', audit, (c) => c.text(c.req.param('id'))) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-basic/src/package.json b/graph/test/javascript/hono/cases/js/esm-basic/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-basic/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/cases/js/esm-basic/src/users.mjs b/graph/test/javascript/hono/cases/js/esm-basic/src/users.mjs new file mode 100644 index 000000000..8e7b49f8f --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-basic/src/users.mjs @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +const users = new Hono() +users.get('/', (c) => c.json([])) +users.post('/', (c) => c.json({}, 201)) + +export default users diff --git a/graph/test/javascript/hono/cases/js/esm-context/src/index.js b/graph/test/javascript/hono/cases/js/esm-context/src/index.js new file mode 100644 index 000000000..e638933e2 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-context/src/index.js @@ -0,0 +1,16 @@ +// census: ctx_set, ctx_get, ctx_var, create_middleware (ESM JavaScript) +import { Hono } from 'hono' +import { createMiddleware } from 'hono/factory' + +const withUser = createMiddleware(async (c, next) => { + c.set('user', { id: 'u1' }) + await next() +}) + +const app = new Hono() +app.use('/me/*', withUser) +app.get('/me/profile', (c) => c.json(c.get('user'))) +app.get('/me/id', (c) => c.text(c.var.user.id)) +app.get('/public', (c) => c.text(String(c.get('user')))) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-context/src/package.json b/graph/test/javascript/hono/cases/js/esm-context/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-context/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/api.js b/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/api.js new file mode 100644 index 000000000..334435f28 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/api.js @@ -0,0 +1,10 @@ +// census: request_fetch (violation hono-5-09, SPEC R14 / R14a in JavaScript) +// Requests whose base the program does not spell join on the composed full path as base_unknown; a single-label +// host joins as base_unknown; a dotted external host never joins. +import { Hono } from 'hono' + +const app = new Hono().basePath('/api') +app.post('/sessions', (c) => c.json({ ok: true }, 201)) +app.get('/sessions/:id', (c) => c.json({ id: c.req.param('id') })) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/client.js b/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/client.js new file mode 100644 index 000000000..3d2849a78 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/client.js @@ -0,0 +1,15 @@ +export async function open(host) { + return fetch(`${host}/api/sessions`, { method: 'POST' }) +} + +export async function read(base, id) { + return fetch(new URL(`/api/sessions/${id}`, base)) +} + +export async function viaService() { + return fetch('http://sessions:8080/api/sessions/1') +} + +export async function upstream() { + return fetch('https://example.org/api/sessions') +} diff --git a/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/package.json b/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-fetch-unknown-base/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/cases/js/esm-hc-client/src/client.js b/graph/test/javascript/hono/cases/js/esm-hc-client/src/client.js new file mode 100644 index 000000000..7b2ca1463 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-hc-client/src/client.js @@ -0,0 +1,12 @@ +// census: hc_client, hc_dollar_call, hc_element_param (JavaScript: no type argument, SPEC 3.5 route_shape) +// A JS hc client has no AppType: it is matched by path against every served app in the JS graph. +import { hc } from 'hono/client' + +const client = hc('http://localhost:8787') + +export async function listTasks() { + return client.tasks.$get() +} +export async function finishTask(id) { + return client.tasks[':id'].done.$post({ param: { id } }) +} diff --git a/graph/test/javascript/hono/cases/js/esm-hc-client/src/package.json b/graph/test/javascript/hono/cases/js/esm-hc-client/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-hc-client/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/cases/js/esm-hc-client/src/server.js b/graph/test/javascript/hono/cases/js/esm-hc-client/src/server.js new file mode 100644 index 000000000..ab8ccc782 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-hc-client/src/server.js @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +const app = new Hono() +app.get('/tasks', (c) => c.json([])) +app.post('/tasks/:id/done', (c) => c.json({ id: c.req.param('id') })) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-twin-of-cjs-basic/src/index.mjs b/graph/test/javascript/hono/cases/js/esm-twin-of-cjs-basic/src/index.mjs new file mode 100644 index 000000000..1343bd660 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-twin-of-cjs-basic/src/index.mjs @@ -0,0 +1,11 @@ +// census: new_hono, route_mount_literal, builtin_mw_import; ESM .mjs twin of js/cjs-basic +import { Hono } from 'hono' +import { cors } from 'hono/cors' +import users from './users.mjs' + +const app = new Hono() +app.use('/api/*', cors()) +app.route('/api/users', users) +app.get('/health', (c) => c.text('ok')) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-twin-of-cjs-basic/src/users.mjs b/graph/test/javascript/hono/cases/js/esm-twin-of-cjs-basic/src/users.mjs new file mode 100644 index 000000000..85476052b --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-twin-of-cjs-basic/src/users.mjs @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const users = new Hono() +users.get('/:id', (c) => c.json({ id: c.req.param('id') })) + +export default users diff --git a/graph/test/javascript/hono/cases/js/negative-js/src/index.js b/graph/test/javascript/hono/cases/js/negative-js/src/index.js new file mode 100644 index 000000000..d698dc135 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/negative-js/src/index.js @@ -0,0 +1,20 @@ +// census: neg_map_get_in_hono_file, neg_route_on_nonhono (JavaScript) +// A Map, a cache object, and a router-shaped object beside one real app: only the app's route. +import { Hono } from 'hono' + +const cache = new Map() +cache.set('/x', () => 'cached') +cache.get('/x') + +const router = { + get(path, fn) { return fn }, + use(fn) { return fn }, + route(path, other) { return other }, +} +router.get('/not-hono', () => 'nope') +router.use(() => {}) + +const app = new Hono() +app.get('/real', (c) => c.text('real')) + +export default app diff --git a/graph/test/javascript/hono/cases/js/negative-js/src/package.json b/graph/test/javascript/hono/cases/js/negative-js/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/js/negative-js/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/cases/js/unresolved-lib-mw/src/index.js b/graph/test/javascript/hono/cases/js/unresolved-lib-mw/src/index.js new file mode 100644 index 000000000..c08ee1d29 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/unresolved-lib-mw/src/index.js @@ -0,0 +1,15 @@ +// census: builtin_mw_import (oracle gap O6: a library middleware whose package cannot be resolved) +// It is still a middleware node at its registering call, ahead of the project middleware, in its place. +import { Hono } from 'hono' +import { authSdk } from 'acme-auth-sdk' + +const tag = async (c, next) => { + await next() +} + +const app = new Hono() +app.use(authSdk.middleware({ project: 'p' })) +app.use(tag) +app.get('/secure', (c) => c.text('secure')) + +export default app diff --git a/graph/test/javascript/hono/cases/js/unresolved-lib-mw/src/package.json b/graph/test/javascript/hono/cases/js/unresolved-lib-mw/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/js/unresolved-lib-mw/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/expected/js/bun-websocket-destructure.entries b/graph/test/javascript/hono/expected/js/bun-websocket-destructure.entries new file mode 100644 index 000000000..9ff240ceb --- /dev/null +++ b/graph/test/javascript/hono/expected/js/bun-websocket-destructure.entries @@ -0,0 +1,2 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O10: upgradeWebSocket destructured from createBunWebSocket() is a library-call handler +# census: none (oracle gap O10, JavaScript): a library function taken by destructuring a library call's result diff --git a/graph/test/javascript/hono/expected/js/bun-websocket-destructure.tsv b/graph/test/javascript/hono/expected/js/bun-websocket-destructure.tsv new file mode 100644 index 000000000..f792ad75b --- /dev/null +++ b/graph/test/javascript/hono/expected/js/bun-websocket-destructure.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O10: upgradeWebSocket destructured from createBunWebSocket() is a library-call handler +# census: none (oracle gap O10, JavaScript): a library function taken by destructuring a library call's result +route GET /ws lib:hono/bun.createBunWebSocket().upgradeWebSocket@index.js:8:16 index.js:7:13 0 +regby index.js:1:1 lib:hono/bun.createBunWebSocket().upgradeWebSocket@index.js:8:16 GET /ws index.js:7:13 diff --git a/graph/test/javascript/hono/expected/js/cjs-basic.entries b/graph/test/javascript/hono/expected/js/cjs-basic.entries new file mode 100644 index 000000000..0fcaf5146 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-basic.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: require-destructured Hono and cors; sub-app from module.exports mounted at /api/users; cors reaches it, not /health +# census: new_hono, route_mount_literal, builtin_mw_import (CommonJS JavaScript; SPEC 10 C2: no CJS project +entry index.js:11:20 http +entry users.js:4:19 http diff --git a/graph/test/javascript/hono/expected/js/cjs-basic.tsv b/graph/test/javascript/hono/expected/js/cjs-basic.tsv new file mode 100644 index 000000000..3e297197d --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-basic.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: require-destructured Hono and cors; sub-app from module.exports mounted at /api/users; cors reaches it, not /health +# census: new_hono, route_mount_literal, builtin_mw_import (CommonJS JavaScript; SPEC 10 C2: no CJS project +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /api/users/:id users.js:4:19 index.js:8:13 1 +route GET /health index.js:11:20 index.js:8:13 2 +mw lib:hono/cors.cors@index.js:9:19 users.js:4:19 GET /api/users/:id 0 index.js:8:13 +submount /api/users index.js:10:5 +regby index.js:1:1 index.js:11:20 GET /health index.js:8:13 +regby users.js:1:1 users.js:4:19 GET /api/users/:id index.js:8:13 diff --git a/graph/test/javascript/hono/expected/js/cjs-context.entries b/graph/test/javascript/hono/expected/js/cjs-context.entries new file mode 100644 index 000000000..a462075c8 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-context.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: CJS twin of esm-context: user flows to both /me routes; /public has no setter +# census: none for CommonJS (corpus gap); CJS twin of js/esm-context +entry index.js:12:24 http +entry index.js:13:19 http +entry index.js:14:20 http +entry index.js:5:35 http diff --git a/graph/test/javascript/hono/expected/js/cjs-context.tsv b/graph/test/javascript/hono/expected/js/cjs-context.tsv new file mode 100644 index 000000000..b7993f148 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-context.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: CJS twin of esm-context: user flows to both /me routes; /public has no setter +# census: none for CommonJS (corpus gap); CJS twin of js/esm-context +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /me/profile index.js:12:24 index.js:10:13 1 +route GET /me/id index.js:13:19 index.js:10:13 2 +route GET /public index.js:14:20 index.js:10:13 3 +mw index.js:5:35 index.js:12:24 GET /me/profile 0 index.js:10:13 +mw index.js:5:35 index.js:13:19 GET /me/id 0 index.js:10:13 +ctx index.js:5:35 index.js:12:24 user +ctx index.js:5:35 index.js:13:19 user +regby index.js:1:1 index.js:12:24 GET /me/profile index.js:10:13 +regby index.js:1:1 index.js:13:19 GET /me/id index.js:10:13 +regby index.js:1:1 index.js:14:20 GET /public index.js:10:13 +unknown index.js:14:20 no_setter_on_chain index.js:14:20 diff --git a/graph/test/javascript/hono/expected/js/cjs-exports-members.entries b/graph/test/javascript/hono/expected/js/cjs-exports-members.entries new file mode 100644 index 000000000..1acc3a28e --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-exports-members.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O7: module.exports members through destructured require: authenticate (sets user) and isAdmin on the chain after the unresolvable limiter; user flows to /api/me +# census: none for CommonJS (corpus gap in census; shape of a CJS-on-Bun project): oracle gap O7 +entry app.js:11:20 http +entry app.js:12:39 http +entry auth.js:1:31 http +entry auth.js:5:26 http diff --git a/graph/test/javascript/hono/expected/js/cjs-exports-members.tsv b/graph/test/javascript/hono/expected/js/cjs-exports-members.tsv new file mode 100644 index 000000000..c63c2b3a8 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-exports-members.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O7: module.exports members through destructured require: authenticate (sets user) and isAdmin on the chain after the unresolvable limiter; user flows to /api/me +# census: none for CommonJS (corpus gap in census; shape of a CJS-on-Bun project): oracle gap O7 +route GET /api/me app.js:11:20 app.js:8:13 2 +route DELETE /api/users/:id app.js:12:39 app.js:8:13 4 +mw lib:acme-rate-limit.limiter@app.js:9:19 app.js:11:20 GET /api/me 0 app.js:8:13 +mw auth.js:1:31 app.js:11:20 GET /api/me 1 app.js:8:13 +mw lib:acme-rate-limit.limiter@app.js:9:19 app.js:12:39 DELETE /api/users/:id 0 app.js:8:13 +mw auth.js:1:31 app.js:12:39 DELETE /api/users/:id 1 app.js:8:13 +mw auth.js:5:26 app.js:12:39 DELETE /api/users/:id 2 app.js:8:13 +ctx auth.js:1:31 app.js:11:20 user +regby app.js:1:1 app.js:11:20 GET /api/me app.js:8:13 +regby app.js:1:1 app.js:12:39 DELETE /api/users/:id app.js:8:13 diff --git a/graph/test/javascript/hono/expected/js/cjs-negative.entries b/graph/test/javascript/hono/expected/js/cjs-negative.entries new file mode 100644 index 000000000..dc0deacbe --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-negative.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: CJS twin of negative-js: only /real; Map and router callbacks absent +# census: none for CommonJS (corpus gap); CJS twin of js/negative-js +entry index.js:17:18 http diff --git a/graph/test/javascript/hono/expected/js/cjs-negative.tsv b/graph/test/javascript/hono/expected/js/cjs-negative.tsv new file mode 100644 index 000000000..8b0af86ac --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-negative.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: CJS twin of negative-js: only /real; Map and router callbacks absent +# census: none for CommonJS (corpus gap); CJS twin of js/negative-js +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /real index.js:17:18 index.js:16:13 0 +regby index.js:1:1 index.js:17:18 GET /real index.js:16:13 +absent index.js:5:17 +absent index.js:13:25 +absent index.js:14:12 +absent index.js:9:3 +absent index.js:10:3 diff --git a/graph/test/javascript/hono/expected/js/cjs-routes-mount.entries b/graph/test/javascript/hono/expected/js/cjs-routes-mount.entries new file mode 100644 index 000000000..e14e2dc57 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-routes-mount.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: CJS twin of esm-basic: identical rows in .cjs files +# census: none for CommonJS (corpus gap: no CJS Hono app in the corpus); CJS twin of js/esm-basic +entry index.cjs:12:14 http +entry index.cjs:14:30 http +entry index.cjs:6:15 http +entry users.cjs:4:16 http +entry users.cjs:5:17 http diff --git a/graph/test/javascript/hono/expected/js/cjs-routes-mount.tsv b/graph/test/javascript/hono/expected/js/cjs-routes-mount.tsv new file mode 100644 index 000000000..ae34e8f19 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-routes-mount.tsv @@ -0,0 +1,18 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: CJS twin of esm-basic: identical rows in .cjs files +# census: none for CommonJS (corpus gap: no CJS Hono app in the corpus); CJS twin of js/esm-basic +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET / index.cjs:12:14 index.cjs:10:13 1 +route GET /users users.cjs:4:16 index.cjs:10:13 2 +route POST /users users.cjs:5:17 index.cjs:10:13 3 +route GET /items/:id index.cjs:14:30 index.cjs:10:13 5 +mw index.cjs:6:15 index.cjs:12:14 GET / 0 index.cjs:10:13 +mw index.cjs:6:15 users.cjs:4:16 GET /users 0 index.cjs:10:13 +mw index.cjs:6:15 users.cjs:5:17 POST /users 0 index.cjs:10:13 +mw index.cjs:6:15 index.cjs:14:30 GET /items/:id 0 index.cjs:10:13 +mw index.cjs:6:15 index.cjs:14:30 GET /items/:id 1 index.cjs:10:13 +submount /users index.cjs:13:5 +regby index.cjs:1:1 index.cjs:12:14 GET / index.cjs:10:13 +regby index.cjs:1:1 index.cjs:14:30 GET /items/:id index.cjs:10:13 +regby users.cjs:1:1 users.cjs:4:16 GET /users index.cjs:10:13 +regby users.cjs:1:1 users.cjs:5:17 POST /users index.cjs:10:13 diff --git a/graph/test/javascript/hono/expected/js/cjs-websocket-module.entries b/graph/test/javascript/hono/expected/js/cjs-websocket-module.entries new file mode 100644 index 000000000..2e573802a --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-websocket-module.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O10 residual (CommonJS): upgradeWebSocket from a project module's module.exports is a library-call handler keyed at the call; its callback is no entry +# census: cjs_require_hono_destructure (oracle gap O10 residual, CommonJS) +entry routes/container.js:6:17 http diff --git a/graph/test/javascript/hono/expected/js/cjs-websocket-module.tsv b/graph/test/javascript/hono/expected/js/cjs-websocket-module.tsv new file mode 100644 index 000000000..d53623465 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-websocket-module.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O10 residual (CommonJS): upgradeWebSocket from a project module's module.exports is a library-call handler keyed at the call; its callback is no entry +# census: cjs_require_hono_destructure (oracle gap O10 residual, CommonJS) +route GET /api/containers/:id routes/container.js:6:17 server.js:7:13 0 +route GET /api/containers/:id/logs/stream lib:hono/bun.createBunWebSocket().upgradeWebSocket@routes/container.js:8:29 server.js:7:13 1 +submount /api/containers server.js:8:5 +regby routes/container.js:1:1 lib:hono/bun.createBunWebSocket().upgradeWebSocket@routes/container.js:8:29 GET /api/containers/:id/logs/stream server.js:7:13 +regby routes/container.js:1:1 routes/container.js:6:17 GET /api/containers/:id server.js:7:13 diff --git a/graph/test/javascript/hono/expected/js/create-middleware-factory.entries b/graph/test/javascript/hono/expected/js/create-middleware-factory.entries new file mode 100644 index 000000000..efc96e3b2 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/create-middleware-factory.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-07: the createMiddleware arg inside the project factory is the middleware, the entry, and the session setter for /me +# census: create_middleware, mw_factory_returning_fn, ctx_set, ctx_get (violation hono-1-07, JavaScript) +entry app.js:9:16 http +entry session.js:4:27 http diff --git a/graph/test/javascript/hono/expected/js/create-middleware-factory.tsv b/graph/test/javascript/hono/expected/js/create-middleware-factory.tsv new file mode 100644 index 000000000..cb7b03e00 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/create-middleware-factory.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-07: the createMiddleware arg inside the project factory is the middleware, the entry, and the session setter for /me +# census: create_middleware, mw_factory_returning_fn, ctx_set, ctx_get (violation hono-1-07, JavaScript) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /me app.js:9:16 app.js:7:13 1 +mw session.js:4:27 app.js:9:16 GET /me 0 app.js:7:13 +ctx session.js:4:27 app.js:9:16 session +regby app.js:1:1 app.js:9:16 GET /me app.js:7:13 diff --git a/graph/test/javascript/hono/expected/js/esm-basic.entries b/graph/test/javascript/hono/expected/js/esm-basic.entries new file mode 100644 index 000000000..6a5560891 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-basic.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: ESM .js/.mjs: audit app-level on every route incl. the mounted ones, and again per-route on /items/:id (positions 0 and 1) +# census: import_hono_named, route_mount_literal, use_global, export_default_app (ESM JavaScript) +entry index.js:12:14 http +entry index.js:14:30 http +entry index.js:6:15 http +entry users.mjs:4:16 http +entry users.mjs:5:17 http diff --git a/graph/test/javascript/hono/expected/js/esm-basic.tsv b/graph/test/javascript/hono/expected/js/esm-basic.tsv new file mode 100644 index 000000000..85974fe78 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-basic.tsv @@ -0,0 +1,18 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: ESM .js/.mjs: audit app-level on every route incl. the mounted ones, and again per-route on /items/:id (positions 0 and 1) +# census: import_hono_named, route_mount_literal, use_global, export_default_app (ESM JavaScript) +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET / index.js:12:14 index.js:10:13 1 +route GET /users users.mjs:4:16 index.js:10:13 2 +route POST /users users.mjs:5:17 index.js:10:13 3 +route GET /items/:id index.js:14:30 index.js:10:13 5 +mw index.js:6:15 index.js:12:14 GET / 0 index.js:10:13 +mw index.js:6:15 users.mjs:4:16 GET /users 0 index.js:10:13 +mw index.js:6:15 users.mjs:5:17 POST /users 0 index.js:10:13 +mw index.js:6:15 index.js:14:30 GET /items/:id 0 index.js:10:13 +mw index.js:6:15 index.js:14:30 GET /items/:id 1 index.js:10:13 +submount /users index.js:13:5 +regby index.js:1:1 index.js:12:14 GET / index.js:10:13 +regby index.js:1:1 index.js:14:30 GET /items/:id index.js:10:13 +regby users.mjs:1:1 users.mjs:4:16 GET /users index.js:10:13 +regby users.mjs:1:1 users.mjs:5:17 POST /users index.js:10:13 diff --git a/graph/test/javascript/hono/expected/js/esm-context.entries b/graph/test/javascript/hono/expected/js/esm-context.entries new file mode 100644 index 000000000..1f43af757 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-context.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: user flows from withUser to both /me routes; /public outside the scope has no setter +# census: ctx_set, ctx_get, ctx_var, create_middleware (ESM JavaScript) +entry index.js:12:24 http +entry index.js:13:19 http +entry index.js:14:20 http +entry index.js:5:35 http diff --git a/graph/test/javascript/hono/expected/js/esm-context.tsv b/graph/test/javascript/hono/expected/js/esm-context.tsv new file mode 100644 index 000000000..c457099c9 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-context.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: user flows from withUser to both /me routes; /public outside the scope has no setter +# census: ctx_set, ctx_get, ctx_var, create_middleware (ESM JavaScript) +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /me/profile index.js:12:24 index.js:10:13 1 +route GET /me/id index.js:13:19 index.js:10:13 2 +route GET /public index.js:14:20 index.js:10:13 3 +mw index.js:5:35 index.js:12:24 GET /me/profile 0 index.js:10:13 +mw index.js:5:35 index.js:13:19 GET /me/id 0 index.js:10:13 +ctx index.js:5:35 index.js:12:24 user +ctx index.js:5:35 index.js:13:19 user +regby index.js:1:1 index.js:12:24 GET /me/profile index.js:10:13 +regby index.js:1:1 index.js:13:19 GET /me/id index.js:10:13 +regby index.js:1:1 index.js:14:20 GET /public index.js:10:13 +unknown index.js:14:20 no_setter_on_chain index.js:14:20 diff --git a/graph/test/javascript/hono/expected/js/esm-fetch-unknown-base.entries b/graph/test/javascript/hono/expected/js/esm-fetch-unknown-base.entries new file mode 100644 index 000000000..33d562704 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-fetch-unknown-base.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-5-09: R14/R14a in JavaScript: parameter and URL bases and a single-label host join as base_unknown; a dotted external host never joins +# census: request_fetch (violation hono-5-09, SPEC R14 / R14a in JavaScript) +entry api.js:7:23 http +entry api.js:8:26 http diff --git a/graph/test/javascript/hono/expected/js/esm-fetch-unknown-base.tsv b/graph/test/javascript/hono/expected/js/esm-fetch-unknown-base.tsv new file mode 100644 index 000000000..85e1f7bd8 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-fetch-unknown-base.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-5-09: R14/R14a in JavaScript: parameter and URL bases and a single-label host join as base_unknown; a dotted external host never joins +# census: request_fetch (violation hono-5-09, SPEC R14 / R14a in JavaScript) +route POST /api/sessions api.js:7:23 api.js:6:13 0 +route GET /api/sessions/:id api.js:8:26 api.js:6:13 1 +request client.js:1:1 POST /api/sessions api.js:7:23 base_unknown +request client.js:5:1 GET /api/sessions/${} api.js:8:26 base_unknown +request client.js:9:1 GET /api/sessions/1 api.js:8:26 base_unknown +regby api.js:1:1 api.js:7:23 POST /api/sessions api.js:6:13 +regby api.js:1:1 api.js:8:26 GET /api/sessions/:id api.js:6:13 diff --git a/graph/test/javascript/hono/expected/js/esm-hc-client.entries b/graph/test/javascript/hono/expected/js/esm-hc-client.entries new file mode 100644 index 000000000..8b0c9038d --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-hc-client.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a typed-less JS hc client: both calls reach the only served app's routes by path (route_shape) +# census: hc_client, hc_dollar_call, hc_element_param (JavaScript: no type argument, SPEC 3.5 route_shape) +entry server.js:4:19 http +entry server.js:5:29 http diff --git a/graph/test/javascript/hono/expected/js/esm-hc-client.tsv b/graph/test/javascript/hono/expected/js/esm-hc-client.tsv new file mode 100644 index 000000000..33596efe9 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-hc-client.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a typed-less JS hc client: both calls reach the only served app's routes by path (route_shape) +# census: hc_client, hc_dollar_call, hc_element_param (JavaScript: no type argument, SPEC 3.5 route_shape) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /tasks server.js:4:19 server.js:3:13 0 +route POST /tasks/:id/done server.js:5:29 server.js:3:13 1 +rpc client.js:10:1 POST /tasks/:id/done server.js:5:29 +rpc client.js:7:1 GET /tasks server.js:4:19 +regby server.js:1:1 server.js:4:19 GET /tasks server.js:3:13 +regby server.js:1:1 server.js:5:29 POST /tasks/:id/done server.js:3:13 diff --git a/graph/test/javascript/hono/expected/js/esm-twin-of-cjs-basic.entries b/graph/test/javascript/hono/expected/js/esm-twin-of-cjs-basic.entries new file mode 100644 index 000000000..f5ff7b719 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-twin-of-cjs-basic.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: ESM .mjs twin of cjs-basic: same routes and cors scope +# census: new_hono, route_mount_literal, builtin_mw_import; ESM .mjs twin of js/cjs-basic +entry index.mjs:9:20 http +entry users.mjs:4:19 http diff --git a/graph/test/javascript/hono/expected/js/esm-twin-of-cjs-basic.tsv b/graph/test/javascript/hono/expected/js/esm-twin-of-cjs-basic.tsv new file mode 100644 index 000000000..daf57edb5 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-twin-of-cjs-basic.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: ESM .mjs twin of cjs-basic: same routes and cors scope +# census: new_hono, route_mount_literal, builtin_mw_import; ESM .mjs twin of js/cjs-basic +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /api/users/:id users.mjs:4:19 index.mjs:6:13 1 +route GET /health index.mjs:9:20 index.mjs:6:13 2 +mw lib:hono/cors.cors@index.mjs:7:19 users.mjs:4:19 GET /api/users/:id 0 index.mjs:6:13 +submount /api/users index.mjs:8:5 +regby index.mjs:1:1 index.mjs:9:20 GET /health index.mjs:6:13 +regby users.mjs:1:1 users.mjs:4:19 GET /api/users/:id index.mjs:6:13 diff --git a/graph/test/javascript/hono/expected/js/negative-js.entries b/graph/test/javascript/hono/expected/js/negative-js.entries new file mode 100644 index 000000000..3c6de27fc --- /dev/null +++ b/graph/test/javascript/hono/expected/js/negative-js.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Map and a router-shaped object beside one app: only /real +# census: neg_map_get_in_hono_file, neg_route_on_nonhono (JavaScript) +entry index.js:18:18 http diff --git a/graph/test/javascript/hono/expected/js/negative-js.tsv b/graph/test/javascript/hono/expected/js/negative-js.tsv new file mode 100644 index 000000000..4de824664 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/negative-js.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Map and a router-shaped object beside one app: only /real +# census: neg_map_get_in_hono_file, neg_route_on_nonhono (JavaScript) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /real index.js:18:18 index.js:17:13 0 +regby index.js:1:1 index.js:18:18 GET /real index.js:17:13 +absent index.js:6:17 +absent index.js:14:25 +absent index.js:15:12 +absent index.js:10:3 +absent index.js:11:3 diff --git a/graph/test/javascript/hono/expected/js/unresolved-lib-mw.entries b/graph/test/javascript/hono/expected/js/unresolved-lib-mw.entries new file mode 100644 index 000000000..ccfa27b7a --- /dev/null +++ b/graph/test/javascript/hono/expected/js/unresolved-lib-mw.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O6: authSdk.middleware() from an unresolvable package stays at position 0 ahead of tag +# census: builtin_mw_import (oracle gap O6: a library middleware whose package cannot be resolved) +entry index.js:13:20 http +entry index.js:6:13 http diff --git a/graph/test/javascript/hono/expected/js/unresolved-lib-mw.tsv b/graph/test/javascript/hono/expected/js/unresolved-lib-mw.tsv new file mode 100644 index 000000000..6b68d328a --- /dev/null +++ b/graph/test/javascript/hono/expected/js/unresolved-lib-mw.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O6: authSdk.middleware() from an unresolvable package stays at position 0 ahead of tag +# census: builtin_mw_import (oracle gap O6: a library middleware whose package cannot be resolved) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /secure index.js:13:20 index.js:10:13 2 +mw lib:acme-auth-sdk.authSdk.middleware@index.js:11:9 index.js:13:20 GET /secure 0 index.js:10:13 +mw index.js:6:13 index.js:13:20 GET /secure 1 index.js:10:13 +regby index.js:1:1 index.js:13:20 GET /secure index.js:10:13 diff --git a/graph/test/javascript/hono/run-tests.sh b/graph/test/javascript/hono/run-tests.sh new file mode 100755 index 000000000..03fe76490 --- /dev/null +++ b/graph/test/javascript/hono/run-tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# axiom-code-graph: Hono torture suites, JavaScript twin. +# The same runner, oracle and compare as graph/test/typescript/hono (see its header), over this +# directory's cases/ and expected/, solved with the JavaScript engine. The oracle reads .js/.mjs/.cjs +# with allowJs, so the truth comes from the same compiler and the same pinned hono. +set -uo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +HONO_LANG=javascript HONO_SUITE_DIR="$HERE" exec bash "$HERE/../../typescript/hono/run-tests.sh" "$@" diff --git a/graph/test/typescript/hono/.gitignore b/graph/test/typescript/hono/.gitignore new file mode 100644 index 000000000..67cdb2e91 --- /dev/null +++ b/graph/test/typescript/hono/.gitignore @@ -0,0 +1,8 @@ +.work/ +*.draft +.oracle-all.log +.tip-ts.log +.tip-js.log +.tip-*.log +.rescore.log +.redraft.log diff --git a/graph/test/typescript/hono/cases/compose/abstract-strategy-mount/src/index.ts b/graph/test/typescript/hono/cases/compose/abstract-strategy-mount/src/index.ts new file mode 100644 index 000000000..fd3b6d158 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/abstract-strategy-mount/src/index.ts @@ -0,0 +1,31 @@ +// census: route_mount_nonliteral (violation hono-2-01) +// Sub-apps returned by subclass overrides of an abstract method, mounted in a loop over a const map under a prefix +// made from the loop key: each is a mount argument (never a root). The map's entries are known, so each instance's +// own override runs under its own key (/oauth/callback, /password/login; the runtime half agrees). +import { Hono } from 'hono' + +abstract class Strategy { + abstract controller(): Hono +} +class OAuth extends Strategy { + controller(): Hono { + const h = new Hono() + h.get('/callback', (c) => c.text('cb')) + return h + } +} +class Password extends Strategy { + controller(): Hono { + const h = new Hono() + h.post('/login', (c) => c.text('in')) + return h + } +} + +const strategies: Record = { oauth: new OAuth(), password: new Password() } +const app = new Hono() +for (const [name, s] of Object.entries(strategies)) { + app.route(`/${name}`, s.controller()) +} + +export default app diff --git a/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/index.ts b/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/index.ts new file mode 100644 index 000000000..b821fe365 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/index.ts @@ -0,0 +1,10 @@ +// census: route_mount_literal (stress-4 mutation m30 shape; violation hono-4-06) +// The mounted app is an imported default re-exported under another name (`export { local as alias }`): it is the +// same app, mounted exactly at /api/search. +import { Hono } from 'hono' +import { searchApiRoutes } from './search' + +const app = new Hono() +app.route('/api/search', searchApiRoutes) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/search/index.ts b/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/search/index.ts new file mode 100644 index 000000000..626ef3134 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/search/index.ts @@ -0,0 +1,5 @@ +import searchApi from './routes/api' + +export const searchModule = { id: 'search', title: 'Search' } + +export { searchApi as searchApiRoutes } diff --git a/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/search/routes/api.ts b/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/search/routes/api.ts new file mode 100644 index 000000000..def4feab6 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/aliased-reexport-mount/src/search/routes/api.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +const searchApi = new Hono() +searchApi.post('/', (c) => c.json({ hits: [] })) +searchApi.get('/suggest', (c) => c.json({ terms: [] })) + +export default searchApi diff --git a/graph/test/typescript/hono/cases/compose/async-wrapper-mount/src/main.ts b/graph/test/typescript/hono/cases/compose/async-wrapper-mount/src/main.ts new file mode 100644 index 000000000..73eedd238 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/async-wrapper-mount/src/main.ts @@ -0,0 +1,13 @@ +// census: bun_serve (stress-5 mutation m38 shape; violation hono-5-06) +// A sub-app passed to an async wrapper that mounts it under its own use('*') and returns the wrapper, which is +// served: the inner route is served by the wrapper, behind the wrapper's middleware. +import { Hono } from 'hono' +import { wrapApp } from './wrap' + +declare const Bun: { serve(o: { fetch: (r: Request) => Response | Promise; port: number }): void } + +const inner = new Hono() +inner.get('/hello', (c) => c.text('hi')) + +const app = await wrapApp(inner) +Bun.serve({ fetch: app.fetch, port: 3000 }) diff --git a/graph/test/typescript/hono/cases/compose/async-wrapper-mount/src/wrap.ts b/graph/test/typescript/hono/cases/compose/async-wrapper-mount/src/wrap.ts new file mode 100644 index 000000000..fdddbe825 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/async-wrapper-mount/src/wrap.ts @@ -0,0 +1,11 @@ +import { Hono } from 'hono' + +export async function wrapApp(inner: Hono): Promise { + const wrapper = new Hono() + wrapper.use('*', async (c, next) => { + c.header('x-wrapped', 'async') + await next() + }) + wrapper.route('/', inner) + return wrapper +} diff --git a/graph/test/typescript/hono/cases/compose/basepath-group/src/index.ts b/graph/test/typescript/hono/cases/compose/basepath-group/src/index.ts new file mode 100644 index 000000000..3b648a045 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/basepath-group/src/index.ts @@ -0,0 +1,13 @@ +// census: basepath +// basePath returns a NEW app sharing the route list: its routes get the prefix, the original app's do not. +import { Hono } from 'hono' + +const app = new Hono() +const api = app.basePath('/api') +const v2 = api.basePath('/v2') + +app.get('/', (c) => c.text('root has no prefix')) +api.get('/status', (c) => c.text('under /api')) +v2.get('/status', (c) => c.text('under /api/v2')) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/basepath-sub-mounted/src/index.ts b/graph/test/typescript/hono/cases/compose/basepath-sub-mounted/src/index.ts new file mode 100644 index 000000000..73eb597a5 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/basepath-sub-mounted/src/index.ts @@ -0,0 +1,11 @@ +// census: basepath, route_mount_literal +// A sub-app built with basePath, then mounted: prefix + basePath + local path. +import { Hono } from 'hono' + +const v1 = new Hono().basePath('/v1') +v1.get('/ping', (c) => c.text('pong')) + +const app = new Hono() +app.route('/svc', v1) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/builder-called-twice/src/main.ts b/graph/test/typescript/hono/cases/compose/builder-called-twice/src/main.ts new file mode 100644 index 000000000..50910e12a --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/builder-called-twice/src/main.ts @@ -0,0 +1,15 @@ +// census: app_factory_fn (violation hono-2-02, SPEC R6; oracle gap O9) +// One helper creates the root AND the sub-app: two apps, two route lists. The sub-app's route is mounted +// under a prefix known only at run time, so it is partial, never a root route. +import { makeApp } from './routes' +import { UserRoutes } from './users' + +declare const process: { env: Record } + +const app = makeApp() +app.get('/logo.png', (c) => c.text('png')) +for (const group of [new UserRoutes()]) { + app.route(group.prefix(process.env.API_VERSION ?? 'v1'), group.build()) +} + +export default app diff --git a/graph/test/typescript/hono/cases/compose/builder-called-twice/src/routes.ts b/graph/test/typescript/hono/cases/compose/builder-called-twice/src/routes.ts new file mode 100644 index 000000000..3ad1649df --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/builder-called-twice/src/routes.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +export function makeApp(opts?: { base?: string }) { + const r = new Hono() + if (opts?.base) { + r.basePath(opts.base) + } + return r +} diff --git a/graph/test/typescript/hono/cases/compose/builder-called-twice/src/users.ts b/graph/test/typescript/hono/cases/compose/builder-called-twice/src/users.ts new file mode 100644 index 000000000..5116c2ac5 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/builder-called-twice/src/users.ts @@ -0,0 +1,12 @@ +import { makeApp } from './routes' + +export class UserRoutes { + private routes = makeApp({ base: '/users' }) + prefix(version: string) { + return `/${version}/users` + } + build() { + this.routes.post('/import', (c) => c.text('ok')) + return this.routes + } +} diff --git a/graph/test/typescript/hono/cases/compose/declarative-routes-const/src/index.ts b/graph/test/typescript/hono/cases/compose/declarative-routes-const/src/index.ts new file mode 100644 index 000000000..0b67e1cc8 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/declarative-routes-const/src/index.ts @@ -0,0 +1,24 @@ +// census: route_mount_literal (SPEC R13: a const array of { path, handler } mounted in a loop) +// Declarative routes in a const array literal, mounted by `app.route(r.path, r.handler)` over that array: exact +// routes at the listed paths. A menu list of { path, label } that nothing mounts gives nothing. +import { Hono } from 'hono' + +const tickets = new Hono().get('/', (c) => c.json([])) +const tags = new Hono().get('/:tag', (c) => c.text(c.req.param('tag'))) + +const routes = [ + { path: '/tickets', handler: tickets }, + { path: '/tags', handler: tags }, +] + +export const menu = [ + { path: '/admin/tickets', label: 'Tickets' }, + { path: '/admin/tags', label: 'Tags' }, +] + +const app = new Hono() +for (const r of routes) { + app.route(r.path, r.handler) +} + +export default app diff --git a/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/app.ts b/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/app.ts new file mode 100644 index 000000000..0c617e00f --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/app.ts @@ -0,0 +1,13 @@ +import { Hono } from 'hono' +import { mountPlugin } from './mount' +import { inboxPlugin } from './plugins/inbox' + +declare const settings: { disabled: Set } + +const app = new Hono() +app.get('/', (c) => c.text('home')) +for (const p of [inboxPlugin].filter((x) => !settings.disabled.has(x.name))) { + mountPlugin(app, p) +} + +export default app diff --git a/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/mount.test.ts b/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/mount.test.ts new file mode 100644 index 000000000..f18c4236e --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/mount.test.ts @@ -0,0 +1,29 @@ +// census: app_request_test (SPEC R13 / R13b; violation hono-4-09 and its silent menu-list control) +// Declarative `{ path, handler }` routes whose list is reordered or filtered at run time (a priority sort, a settings +// filter): the handler apps are mounted at their element path, EXACT (R13b; conditional_registration where the list +// is filtered), never roots at `/`. The `{ path, label }` menu entries are mounted by nothing and produce nothing. +import { Hono } from 'hono' +import { mountPlugin, type MountablePlugin } from './mount' + +declare function it(name: string, fn: () => unknown): void + +function makeRoute(label: string): Hono { + const r = new Hono() + r.get('/', (c) => c.text(label)) + return r +} + +it('mounts one declarative route', async () => { + const app = new Hono() + const plugin: MountablePlugin = { + name: 'solo', + routes: [{ path: '/solo', handler: makeRoute('solo-ok') }], + menu: [{ path: '/admin/solo', label: 'Solo' }], + } + mountPlugin(app, plugin) +}) + +it('mounts an inline sub-app', async () => { + const app = new Hono() + mountPlugin(app, { name: 'inline', routes: [{ path: '/inline', handler: new Hono().get('/', (c) => c.text('in')) }] }) +}) diff --git a/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/mount.ts b/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/mount.ts new file mode 100644 index 000000000..d596826fa --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/mount.ts @@ -0,0 +1,27 @@ +import type { Hono } from 'hono' + +export interface MountableRoute { + path: string + handler: Hono + priority?: number +} +export interface MountablePlugin { + name: string + routes?: MountableRoute[] + menu?: { path: string; label: string }[] +} + +function byPriority(routes: MountableRoute[]): MountableRoute[] { + return routes + .map((route, index) => ({ route, index })) + .sort((a, b) => (b.route.priority ?? 0) - (a.route.priority ?? 0) || a.index - b.index) + .map((entry) => entry.route) +} + +// mounts a plugin's declarative routes in priority order (decided at run time) +export function mountPlugin(app: Hono, plugin: MountablePlugin) { + for (const route of byPriority(plugin.routes ?? [])) { + if (!route.path || !route.handler) continue + app.route(route.path, route.handler) + } +} diff --git a/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/plugins/inbox.ts b/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/plugins/inbox.ts new file mode 100644 index 000000000..27113adae --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/declarative-routes-derived/src/plugins/inbox.ts @@ -0,0 +1,10 @@ +import { Hono } from 'hono' +import type { MountablePlugin } from '../mount' + +const inboxApi = new Hono().get('/', (c) => c.json([])) + +export const inboxPlugin: MountablePlugin = { + name: 'inbox', + routes: [{ path: '/api/inbox', handler: inboxApi }], + menu: [{ path: '/admin/inbox', label: 'Inbox' }], +} diff --git a/graph/test/typescript/hono/cases/compose/discarded-basepath/src/index.ts b/graph/test/typescript/hono/cases/compose/discarded-basepath/src/index.ts new file mode 100644 index 000000000..08a03f31a --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/discarded-basepath/src/index.ts @@ -0,0 +1,13 @@ +// census: basepath (SPEC 2 [iter2]: a helper that discards basePath()'s result) +// basePath returns a NEW app; calling it as a statement changes nothing for later routes. +import { Hono } from 'hono' + +function withVersion(app: Hono, version: string) { + app.basePath(`/${version}`) + return app +} + +const app = withVersion(new Hono(), 'v2') +app.get('/items', (c) => c.json([])) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/dynamic-prefix/src/index.ts b/graph/test/typescript/hono/cases/compose/dynamic-prefix/src/index.ts new file mode 100644 index 000000000..954c98d72 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/dynamic-prefix/src/index.ts @@ -0,0 +1,13 @@ +// census: route_mount_nonliteral +// A mount prefix known only at run time is declared undetermined, never guessed. +import { Hono } from 'hono' + +const tenants = new Hono() +tenants.get('/dashboard', (c) => c.text('dash')) + +const prefix = ['/t', String(Date.now() % 3)].join('') +const app = new Hono() +app.route(prefix, tenants) +app.get('/static', (c) => c.text('static')) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/factory-basepath/src/index.ts b/graph/test/typescript/hono/cases/compose/factory-basepath/src/index.ts new file mode 100644 index 000000000..5eac3e6ad --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/factory-basepath/src/index.ts @@ -0,0 +1,12 @@ +// census: app_factory_fn, basepath (violation hono-1-02) +// An app returned from a function through basePath keeps its base: GET /v1/ping, and no base-less twin. +import { Hono } from 'hono' + +function makeServer() { + return new Hono().basePath('/v1') +} + +const srv = makeServer() +srv.get('/ping', (c) => c.text('pong')) + +export default srv diff --git a/graph/test/typescript/hono/cases/compose/factory-fn/src/app.ts b/graph/test/typescript/hono/cases/compose/factory-fn/src/app.ts new file mode 100644 index 000000000..42e0a469a --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/factory-fn/src/app.ts @@ -0,0 +1,11 @@ +import { Hono } from 'hono' + +export function createRouter() { + return new Hono() +} + +export function createApp() { + const app = createRouter() + app.get('/ready', (c) => c.text('ready')) + return app +} diff --git a/graph/test/typescript/hono/cases/compose/factory-fn/src/index.ts b/graph/test/typescript/hono/cases/compose/factory-fn/src/index.ts new file mode 100644 index 000000000..d143ad522 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/factory-fn/src/index.ts @@ -0,0 +1,10 @@ +// census: app_factory_fn +// Apps produced by project factory functions (a factory calling a factory) are app values. +import { createApp, createRouter } from './app' + +const app = createApp() +const items = createRouter() +items.get('/', (c) => c.json([])) +app.route('/items', items) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/field-app-class/src/routes/health.ts b/graph/test/typescript/hono/cases/compose/field-app-class/src/routes/health.ts new file mode 100644 index 000000000..4a77d84e8 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/field-app-class/src/routes/health.ts @@ -0,0 +1,3 @@ +import { Hono } from 'hono' + +export const health = new Hono().get('/', (c) => c.text('ok')) diff --git a/graph/test/typescript/hono/cases/compose/field-app-class/src/routes/users.ts b/graph/test/typescript/hono/cases/compose/field-app-class/src/routes/users.ts new file mode 100644 index 000000000..f7d8678ec --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/field-app-class/src/routes/users.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +export class UsersRoute { + getRoutes() { + const r = new Hono() + r.get('/:id', (c) => c.json({ id: c.req.param('id') })) + return r + } +} diff --git a/graph/test/typescript/hono/cases/compose/field-app-class/src/server.ts b/graph/test/typescript/hono/cases/compose/field-app-class/src/server.ts new file mode 100644 index 000000000..6baaec09f --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/field-app-class/src/server.ts @@ -0,0 +1,31 @@ +// census: route_mount_literal, app_factory_fn (SPEC 2 [iter2] flow through fields; a class whose +// app lives in a field, registrations in setupRoutes() called from the constructor, a mount argument built by a method) +import { Hono } from 'hono' +import { health } from './routes/health' +import { UsersRoute } from './routes/users' + +function createApp() { + return new Hono().basePath('/v1') +} + +export class Server { + private app: ReturnType + private extra: Hono = new Hono() + + constructor() { + this.app = createApp() + this.setupRoutes() + } + + private setupRoutes() { + this.app.route('/health', health) + this.app.route('/users', new UsersRoute().getRoutes()) + this.extra.get('/extra', (c) => c.text('extra')) + } + + get fetch() { + return this.app.fetch + } +} + +export const server = new Server() diff --git a/graph/test/typescript/hono/cases/compose/filtered-instance-loop/src/index.ts b/graph/test/typescript/hono/cases/compose/filtered-instance-loop/src/index.ts new file mode 100644 index 000000000..3876435a1 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/filtered-instance-loop/src/index.ts @@ -0,0 +1,31 @@ +// census: route_mount_root (stress-5 mutation m36 shape; violation hono-5-01, SPEC R13b) +// A loop over a FILTERED list of known class instances mounts each one's `routes()` at a literal prefix: exact +// routes (the path is right whenever the module is enabled), each with conditional_registration. +import { Hono } from 'hono' + +declare const process: { env: Record } + +class Accounts { + routes(): Hono { + const h = new Hono() + h.get('/accounts', (c) => c.text('accounts')) + return h + } +} +class Invoices { + routes(): Hono { + const h = new Hono() + h.get('/invoices', (c) => c.text('invoices')) + return h + } +} + +const enabled = (process.env.MODULES ?? '').split(',') +const modules = [new Accounts(), new Invoices()].filter((m) => enabled.includes(m.constructor.name)) + +const app = new Hono() +app.get('/', (c) => c.text('home')) +for (const m of modules) { + app.route('/', m.routes()) +} +export default app diff --git a/graph/test/typescript/hono/cases/compose/foreign-mount/src/index.ts b/graph/test/typescript/hono/cases/compose/foreign-mount/src/index.ts new file mode 100644 index 000000000..d327a0710 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/foreign-mount/src/index.ts @@ -0,0 +1,13 @@ +// census: mount_foreign +// mount() hands a prefix to a foreign fetch handler: verb ANY, path prefix/*, handler the function. +import { Hono } from 'hono' + +function legacyFetch(req: Request): Response { + return new Response('legacy ' + req.url) +} + +const app = new Hono() +app.mount('/legacy', legacyFetch) +app.get('/new', (c) => c.text('new')) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/getter-prefix-typed-holder/src/index.ts b/graph/test/typescript/hono/cases/compose/getter-prefix-typed-holder/src/index.ts new file mode 100644 index 000000000..d27ff3685 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/getter-prefix-typed-holder/src/index.ts @@ -0,0 +1,48 @@ +// census: prop_receiver_route (stress-4 mutation m25 shape; SPEC R2, hono-2-04 residue) +// A controller app mounted on a typed context holder at a prefix read through a getter of run-time config: +// ONE partial per route, never an exact row, never one row per hole depth. +import { Hono } from 'hono' + +class BaseController { + protected create(): Hono { + return BaseController.createServer() + } + static createServer(): Hono { + return new Hono() + } + getController(): Hono { + return this.create() + } +} + +class AssetsController extends BaseController { + override getController() { + const hono = this.create() + hono.delete('/asset/:name', (c) => c.body(null, 204)) + hono.get('/asset/:name', (c) => c.text(c.req.param('name'))) + return hono + } +} + +interface ServerCtx { + server: Hono +} + +class AssetsModule { + private ctx: ServerCtx + private config: { mountAt: string } + constructor(ctx: ServerCtx, config: { mountAt: string }) { + this.ctx = ctx + this.config = config + } + get mountAt() { + return this.config.mountAt + } + build() { + this.ctx.server.route(this.mountAt, new AssetsController().getController()) + } +} + +export function boot(ctx: ServerCtx, cfg: { mountAt: string }) { + new AssetsModule(ctx, cfg).build() +} diff --git a/graph/test/typescript/hono/cases/compose/glob-module-list/src/env.ts b/graph/test/typescript/hono/cases/compose/glob-module-list/src/env.ts new file mode 100644 index 000000000..c42a6ab20 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-module-list/src/env.ts @@ -0,0 +1,7 @@ +// the bundler's typing of import.meta.glob (a project declaration, as a vite-env file would add it) +declare global { + interface ImportMeta { + glob>(pattern: string | string[], options?: { eager?: boolean }): Record + } +} +export {} diff --git a/graph/test/typescript/hono/cases/compose/glob-module-list/src/index.ts b/graph/test/typescript/hono/cases/compose/glob-module-list/src/index.ts new file mode 100644 index 000000000..1e68c5278 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-module-list/src/index.ts @@ -0,0 +1,25 @@ +// census: route_mount_root (SPEC R12.1; QD16 shape: a tier app mounting `mod.default` over a literal glob) +// import.meta.glob with a literal pattern is resolved against the files: every matched module's default-exported +// app is mounted by the loop's route() (eager by for..of over Object.values, lazy by awaiting each loader). +import { Hono } from 'hono' + +const eagerRoutes = import.meta.glob<{ default: Hono }>('./routes/**/*.index.ts', { eager: true }) +const lazyPages = import.meta.glob<() => Promise<{ default: Hono }>>('./lazy/*.page.ts') + +const api = new Hono() +for (const mod of Object.values(eagerRoutes)) { + api.route('/', mod.default) +} + +export async function mountPages(app: Hono) { + for (const key in lazyPages) { + const mod = await lazyPages[key]() + app.route('/pages', mod.default) + } +} + +const app = new Hono() +app.route('/api', api) +await mountPages(app) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/glob-module-list/src/lazy/reports.page.ts b/graph/test/typescript/hono/cases/compose/glob-module-list/src/lazy/reports.page.ts new file mode 100644 index 000000000..48a58236a --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-module-list/src/lazy/reports.page.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const reports = new Hono() +reports.get('/daily', (c) => c.json([])) + +export default reports diff --git a/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/orders/helpers.ts b/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/orders/helpers.ts new file mode 100644 index 000000000..792c67475 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/orders/helpers.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +// not matched by the glob (*.index.ts): an app nothing mounts stays what it is (a root) +const scratch = new Hono() +scratch.get('/scratch', (c) => c.text('scratch')) + +export default scratch diff --git a/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/orders/orders.index.ts b/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/orders/orders.index.ts new file mode 100644 index 000000000..0060860ca --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/orders/orders.index.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const orders = new Hono() +orders.post('/orders', (c) => c.json({ ok: true }, 201)) + +export default orders diff --git a/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/users/users.index.ts b/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/users/users.index.ts new file mode 100644 index 000000000..fa3fb0223 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-module-list/src/routes/users/users.index.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +const users = new Hono() +users.get('/users', (c) => c.json([])) +users.get('/users/:id', (c) => c.json({ id: c.req.param('id') })) + +export default users diff --git a/graph/test/typescript/hono/cases/compose/late-sub-registration/src/index.ts b/graph/test/typescript/hono/cases/compose/late-sub-registration/src/index.ts new file mode 100644 index 000000000..c80d1186c --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/late-sub-registration/src/index.ts @@ -0,0 +1,14 @@ +// census: route_mount_literal +// route() copies the sub-app's routes AT THE CALL: a registration on the sub-app after the mount +// is not served under the prefix. +import { Hono } from 'hono' + +const sub = new Hono() +sub.get('/early', (c) => c.text('mounted')) + +const app = new Hono() +app.route('/sub', sub) + +sub.get('/late', (c) => c.text('registered after the mount: not under /sub')) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/let-rebase-conditional/src/index.ts b/graph/test/typescript/hono/cases/compose/let-rebase-conditional/src/index.ts new file mode 100644 index 000000000..ec61f8e7d --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/let-rebase-conditional/src/index.ts @@ -0,0 +1,29 @@ +// census: basepath (SPEC R8a/R8b, oracle note O27: a module-level app variable rebased under a condition) +// `let app = new Hono(); if (base) app = app.basePath(base)`: the routes registered through `app` afterwards are +// served at `/...` when the variable is set and at `/...` when it is not, so neither exact path is known: +// every route on it is partial (dynamic_mount_prefix), never exact. The same holds for a LITERAL base behind a +// condition (`legacy`). A route registered BEFORE the rebase is on the un-rebased app and stays exact. +import { Hono } from 'hono' + +declare const process: { env: Record } + +const users = new Hono() +users.get('/:id', (c) => c.text(c.req.param('id'))) + +let app = new Hono() +app.get('/health', (c) => c.text('ok')) +const base = process.env.API_BASE +if (base) { + app = app.basePath(base) +} +app.get('/version', (c) => c.text('1')) +app.route('/users', users) + +let legacy = new Hono() +if (process.env.LEGACY_PREFIX === 'on') { + legacy = legacy.basePath('/v1') +} +legacy.get('/ping', (c) => c.text('pong')) + +export { legacy } +export default app diff --git a/graph/test/typescript/hono/cases/compose/let-rebase-literal/src/index.ts b/graph/test/typescript/hono/cases/compose/let-rebase-literal/src/index.ts new file mode 100644 index 000000000..a1fa34e69 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/let-rebase-literal/src/index.ts @@ -0,0 +1,15 @@ +// census: basepath (SPEC R8a/R8b control: an app variable re-pointed at a LITERAL basePath with no condition) +// `let app = new Hono(); app = app.basePath('/api')` always runs, so the base is known: every route registered through +// `app` afterwards is exact under /api; the one registered before the reassignment stays at its own path. +import { Hono } from 'hono' + +const users = new Hono() +users.get('/:id', (c) => c.text(c.req.param('id'))) + +let app = new Hono() +app.get('/health', (c) => c.text('ok')) +app = app.basePath('/api') +app.get('/version', (c) => c.text('1')) +app.route('/users', users) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/lib-dirs/src/index.ts b/graph/test/typescript/hono/cases/compose/lib-dirs/src/index.ts new file mode 100644 index 000000000..a2bf7d373 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/lib-dirs/src/index.ts @@ -0,0 +1,8 @@ +// census: route_mount_literal (project sources under lib/ and libs/, a common monorepo layout) +// A project path that starts with lib/ or libs/ is project code, not a library key. +import { app } from './lib/app' +import { users } from './libs/users/routes' + +app.route('/users', users) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/lib-dirs/src/lib/app.ts b/graph/test/typescript/hono/cases/compose/lib-dirs/src/lib/app.ts new file mode 100644 index 000000000..564eeb5f4 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/lib-dirs/src/lib/app.ts @@ -0,0 +1,4 @@ +import { Hono } from 'hono' + +export const app = new Hono() +app.get('/from-lib', (c) => c.text('declared under lib/')) diff --git a/graph/test/typescript/hono/cases/compose/lib-dirs/src/libs/users/routes.ts b/graph/test/typescript/hono/cases/compose/lib-dirs/src/libs/users/routes.ts new file mode 100644 index 000000000..f7216bf3b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/lib-dirs/src/libs/users/routes.ts @@ -0,0 +1,4 @@ +import { Hono } from 'hono' + +export const users = new Hono() +users.get('/:id', (c) => c.json({ id: c.req.param('id') })) diff --git a/graph/test/typescript/hono/cases/compose/literal-prefix-method/src/index.ts b/graph/test/typescript/hono/cases/compose/literal-prefix-method/src/index.ts new file mode 100644 index 000000000..7761ef5b4 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/literal-prefix-method/src/index.ts @@ -0,0 +1,41 @@ +// census: mount_dynamic_prefix_call (violation hono-3-05, SPEC 3.2 G8) +// The mount prefix comes from a zero-argument method that returns a literal: it is that constant, so the routes +// are exact under /console. A method that builds its prefix from a field stays dynamic. +import { Hono } from 'hono' + +class ConsoleRoutes { + private hono = new Hono() + constructor() { + this.hono.get('/login', (c) => c.text('login')) + this.hono.get('/home', (c) => c.text('home')) + } + getBasePath(): string { + return '/console' + } + getRoutes() { + return this.hono + } +} + +class VersionedRoutes { + private hono = new Hono() + private version: string + constructor(version: string) { + this.version = version + this.hono.get('/items', (c) => c.json([])) + } + getBasePath(): string { + return `/${this.version}` + } + getRoutes() { + return this.hono + } +} + +const app = new Hono() +const ui = new ConsoleRoutes() +app.route(ui.getBasePath(), ui.getRoutes()) +const v = new VersionedRoutes(String(Date.now())) +app.route(v.getBasePath(), v.getRoutes()) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/loop-mount-method-fallback/src/index.ts b/graph/test/typescript/hono/cases/compose/loop-mount-method-fallback/src/index.ts new file mode 100644 index 000000000..6c3e98104 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/loop-mount-method-fallback/src/index.ts @@ -0,0 +1,43 @@ +// census: mount_dynamic_prefix_call (stress-4 mutation m29 shape; violation hono-4-05) +// A loop mounts the elements of a const list by method calls whose prefix depends on a run-time version; another +// class with methods of the same names is mounted exactly elsewhere. Only the loop's element class is partial. +import { Hono } from 'hono' + +declare const process: { env: Record } + +class HealthRoutes { + private hono = new Hono() + constructor() { + this.hono.get('/health', (c) => c.text('ok')) + } + getBasePath(version: string) { + return `/${version}/ops` + } + getRoutes() { + return this.hono + } +} + +class AdminRoutes { + private hono = new Hono() + constructor() { + this.hono.get('/signin', (c) => c.text('signin')) + } + getBasePath() { + return '/admin' + } + getRoutes() { + return this.hono + } +} + +const VERSION = process.env.API_VERSION ?? 'v1' +const app = new Hono() +const versioned = [new HealthRoutes()] +for (const r of versioned) { + app.route(r.getBasePath(VERSION), r.getRoutes()) +} +const admin = new AdminRoutes() +app.route(admin.getBasePath(), admin.getRoutes()) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/index.ts b/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/index.ts new file mode 100644 index 000000000..04668caed --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/index.ts @@ -0,0 +1,17 @@ +// census: mount_dynamic_prefix_call (violation hono-5-03) +// A loop over a const list of route groups mounts `g.build()` at `g.prefix(V)`, a method whose template only uses +// its argument, called with a const: the prefix is /v2/members, and the routes are exact (a discarded basePath in +// the factory changes nothing). +import { makeApp } from './routes' +import { MemberRoutes } from './members' + +declare const Deno: { serve(o: { port: number }, h: (r: Request) => Response | Promise): void } + +const V = 'v2' +const app = makeApp() +const groups = [new MemberRoutes()] +for (const g of groups) { + app.route(g.prefix(V), g.build()) +} +app.get('/logo.png', (c) => c.text('img')) +Deno.serve({ port: 3000 }, app.fetch) diff --git a/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/members.ts b/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/members.ts new file mode 100644 index 000000000..a5a33bd3c --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/members.ts @@ -0,0 +1,13 @@ +import { makeApp } from './routes' + +export class MemberRoutes { + private routes = makeApp({ base: '/members' }) + prefix(v: string) { + return `/${v}/members` + } + build() { + this.routes.post('/import', (c) => c.text('ok')) + this.routes.get('/:id', (c) => c.text(c.req.param('id'))) + return this.routes + } +} diff --git a/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/routes.ts b/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/routes.ts new file mode 100644 index 000000000..3ad1649df --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/method-prefix-const-list/src/routes.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +export function makeApp(opts?: { base?: string }) { + const r = new Hono() + if (opts?.base) { + r.basePath(opts.base) + } + return r +} diff --git a/graph/test/typescript/hono/cases/compose/mount-arg-never-root/src/index.ts b/graph/test/typescript/hono/cases/compose/mount-arg-never-root/src/index.ts new file mode 100644 index 000000000..aeca873e0 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/mount-arg-never-root/src/index.ts @@ -0,0 +1,13 @@ +// census: neg_route_on_nonhono, route_mount_literal (SPEC 2 [iter2]: a mount argument is never a root) +// `.route(p, B)` on a receiver that is not recognised as an app: B (a real Hono app) is not served bare; its +// route is partial with unresolved_mount. A `.route()` whose argument is not an app produces nothing. +import { Hono } from 'hono' + +const reports = new Hono() +reports.get('/daily', (c) => c.text('daily')) + +declare const registry: { route(prefix: string, app: unknown): void } +registry.route('/reports', reports) + +const workflow = { route(name: string, step: () => void) { step() } } +workflow.route('/not-an-app', () => {}) diff --git a/graph/test/typescript/hono/cases/compose/mount-chain/src/index.ts b/graph/test/typescript/hono/cases/compose/mount-chain/src/index.ts new file mode 100644 index 000000000..2b10210c1 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/mount-chain/src/index.ts @@ -0,0 +1,17 @@ +// census: mount_foreign, use_scoped (oracle gap O8) +// A mount() handler is a route (ANY prefix/*) with a middleware chain like any other route. +import { Hono } from 'hono' +import { cors } from 'hono/cors' +import type { MiddlewareHandler } from 'hono' + +const legacyAuth: MiddlewareHandler = async (c, next) => { await next() } +function legacyFetch(req: Request): Response { + return new Response('legacy ' + req.url) +} + +const app = new Hono() +app.use(cors()) +app.use('/legacy/*', legacyAuth) +app.mount('/legacy', legacyFetch) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/nested-three-deep/src/api.ts b/graph/test/typescript/hono/cases/compose/nested-three-deep/src/api.ts new file mode 100644 index 000000000..70781f44e --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/nested-three-deep/src/api.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' +import { posts } from './posts' + +export const api = new Hono().basePath('/api') + +api.route('/posts', posts) diff --git a/graph/test/typescript/hono/cases/compose/nested-three-deep/src/comments.ts b/graph/test/typescript/hono/cases/compose/nested-three-deep/src/comments.ts new file mode 100644 index 000000000..ad7968f09 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/nested-three-deep/src/comments.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +export const comments = new Hono() + +comments.get('/', (c) => c.json([])) +comments.delete('/:commentId', (c) => c.body(null, 204)) diff --git a/graph/test/typescript/hono/cases/compose/nested-three-deep/src/index.ts b/graph/test/typescript/hono/cases/compose/nested-three-deep/src/index.ts new file mode 100644 index 000000000..ff6b94fa3 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/nested-three-deep/src/index.ts @@ -0,0 +1,10 @@ +// census: route_mount_literal, basepath +// Three levels of route() across modules, named exports, a basePath at the middle level, mounted at /. +import { Hono } from 'hono' +import { api } from './api' + +const app = new Hono() + +app.route('/', api) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/nested-three-deep/src/posts.ts b/graph/test/typescript/hono/cases/compose/nested-three-deep/src/posts.ts new file mode 100644 index 000000000..0807e2898 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/nested-three-deep/src/posts.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' +import { comments } from './comments' + +export const posts = new Hono() + +posts.get('/:postId', (c) => c.json({})) +posts.route('/:postId/comments', comments) diff --git a/graph/test/typescript/hono/cases/compose/opaque-module-list/src/routes/billing.ts b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/routes/billing.ts new file mode 100644 index 000000000..07da1c383 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/routes/billing.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const billing = new Hono() +billing.get('/invoices', (c) => c.json([])) + +export default billing diff --git a/graph/test/typescript/hono/cases/compose/opaque-module-list/src/routes/status.ts b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/routes/status.ts new file mode 100644 index 000000000..e16c7c4c6 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/routes/status.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const status = new Hono() +status.get('/status', (c) => c.text('up')) + +export default status diff --git a/graph/test/typescript/hono/cases/compose/opaque-module-list/src/server.ts b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/server.ts new file mode 100644 index 000000000..a57fcf609 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/server.ts @@ -0,0 +1,20 @@ +// census: route_mount_root (SPEC R12.2: an opaque run-time module list) +// The server mounts `mod.default` for every file a directory listing finds at run time. A default-exported app that +// nothing imports, mounts or serves may be one of them: partial (unresolved_mount), never an exact root. The server's +// own app, an imported app and a separately served worker are unaffected. +import { Hono } from 'hono' +import shared from './shared' + +declare function listRouteFiles(dir: string): Promise + +const app = new Hono() +app.route('/', shared) + +export async function loadRoutes(dir: string) { + for (const file of await listRouteFiles(dir)) { + const mod = await import(file) + app.route('/', mod.default) + } +} + +export default app diff --git a/graph/test/typescript/hono/cases/compose/opaque-module-list/src/shared.ts b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/shared.ts new file mode 100644 index 000000000..cd5fc3867 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/shared.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +// imported by the server: not a module-list candidate, mounted exactly +const shared = new Hono() +shared.get('/version', (c) => c.text('1')) + +export default shared diff --git a/graph/test/typescript/hono/cases/compose/opaque-module-list/src/worker.ts b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/worker.ts new file mode 100644 index 000000000..21a0b0f2b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/opaque-module-list/src/worker.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +// a separate worker served on its own (its fetch is read): a served root, never a candidate +const worker = new Hono() +worker.get('/tick', (c) => c.text('tick')) + +export default { fetch: worker.fetch } diff --git a/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/cli.ts b/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/cli.ts new file mode 100644 index 000000000..c7b281a6e --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/cli.ts @@ -0,0 +1,39 @@ +// a command-line builder chained two dozen times at module level: linear work, not one evaluation per path +export class Command { + private flags: string[] = [] + command(name: string): Command { + this.flags.push(name) + return this + } + option(flag: string, help: string): Command { + this.flags.push(flag + ' ' + help) + return this + } +} + +export const deploy = new Command() + .command('deploy') + .option('--flag-0', 'flag 0') + .option('--flag-1', 'flag 1') + .option('--flag-2', 'flag 2') + .option('--flag-3', 'flag 3') + .option('--flag-4', 'flag 4') + .option('--flag-5', 'flag 5') + .option('--flag-6', 'flag 6') + .option('--flag-7', 'flag 7') + .option('--flag-8', 'flag 8') + .option('--flag-9', 'flag 9') + .option('--flag-10', 'flag 10') + .option('--flag-11', 'flag 11') + .option('--flag-12', 'flag 12') + .option('--flag-13', 'flag 13') + .option('--flag-14', 'flag 14') + .option('--flag-15', 'flag 15') + .option('--flag-16', 'flag 16') + .option('--flag-17', 'flag 17') + .option('--flag-18', 'flag 18') + .option('--flag-19', 'flag 19') + .option('--flag-20', 'flag 20') + .option('--flag-21', 'flag 21') + .option('--flag-22', 'flag 22') + .option('--flag-23', 'flag 23') diff --git a/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/index.ts b/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/index.ts new file mode 100644 index 000000000..65c4b0628 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/index.ts @@ -0,0 +1,21 @@ +// census: new_hono_inside_function (oracle gap O15) +// A large project's oracle run overflowed the stack on a destructure-and-rebuild cycle (saver.ts), and did not finish +// in 45 minutes on a long builder chain (cli.ts). The oracle must terminate, declare the value unknown, and still give +// the routes and middleware of the app around it. +import { Hono } from 'hono' +import { Saver, resume } from './saver' +import './cli' + +const saver = new Saver() + +const app = new Hono<{ Variables: { saver: Saver } }>() +app.use('/threads/*', async (c, next) => { + c.set('saver', saver) + await next() +}) +app.post('/threads/:id/resume', async (c) => { + const cfg = await resume(c.get('saver'), { configurable: { thread: c.req.param('id'), ns: '' } }) + return c.json(cfg) +}) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/saver.ts b/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/saver.ts new file mode 100644 index 000000000..724e86ac2 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/oracle-eval-cycle/src/saver.ts @@ -0,0 +1,24 @@ +export interface Configurable { + thread: string + ns: string + id?: string +} +export interface Cfg { + configurable: Configurable +} + +// destructures its argument and hands back an object built from those names: called on its own result, every +// read of `thread` goes through the previous call's object (an evaluation cycle for a naive evaluator) +export class Saver { + async put(config: Cfg, id: string): Promise { + const { thread, ns } = config.configurable + const next: Cfg = { configurable: { thread, ns, id } as Configurable } + return next + } +} + +export async function resume(saver: Saver, config: Cfg) { + const first = await saver.put(config, 'a') + const second = await saver.put(first, 'b') + return saver.put(second, 'c') +} diff --git a/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/app.test.ts b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/app.test.ts new file mode 100644 index 000000000..7a20e7dfe --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/app.test.ts @@ -0,0 +1,20 @@ +// census: app_request_test (oracle gap O20; SPEC R13 / R13b) +// Plugin apps reach a mount only through a helper over a filtered, dependency-sorted list (a ternary of two +// filtered lists, a push-built result): each is mounted at its element's path, exact plus conditional, never a +// root at its own local paths. +import { Hono } from 'hono' +import { createApp } from './app' +import type { Plugin } from './mount' + +declare function it(name: string, fn: () => unknown): void + +function makeCustomPlugin(): Plugin { + const routes = new Hono() + routes.get('/', (c) => c.json({ custom: true })) + return { id: 'custom', routes: [{ path: '/api/custom-test', handler: routes }] } +} + +it('mounts a custom plugin', async () => { + const app = createApp({ plugins: { register: [makeCustomPlugin()] } }) + await app.request('/api/custom-test') +}) diff --git a/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/app.ts b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/app.ts new file mode 100644 index 000000000..68d963f1e --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/app.ts @@ -0,0 +1,15 @@ +import { Hono } from 'hono' +import { registerPluginRoutes, type Plugin } from './mount' +import { magicLinkPlugin } from './plugins/magic-link' + +const coreAfterCatchAll: Plugin[] = [magicLinkPlugin] + +export function createApp(config: { plugins?: { register?: Plugin[] } } = {}) { + const app = new Hono() + app.get('/', (c) => c.text('home')) + if (config.plugins?.register) { + registerPluginRoutes(app, config.plugins.register) + } + registerPluginRoutes(app, coreAfterCatchAll) + return app +} diff --git a/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/mount.ts b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/mount.ts new file mode 100644 index 000000000..8a50f60a0 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/mount.ts @@ -0,0 +1,22 @@ +import type { Hono } from 'hono' +import { topoSort } from './sort' + +export interface Plugin { + id: string + dependencies?: string[] + routes?: { path: string; handler: Hono }[] +} + +export function mountPlugin(app: Hono, plugin: Plugin) { + for (const route of plugin.routes ?? []) { + if (!route.path || !route.handler) continue + app.route(route.path, route.handler) + } +} + +export function registerPluginRoutes(app: Hono, plugins: Array, opts: { sort?: boolean } = {}) { + const sorted = (opts.sort ?? true) + ? topoSort(plugins.filter((p): p is Plugin => !!p)) + : plugins.filter((p): p is Plugin => !!p) + for (const plugin of sorted) mountPlugin(app, plugin) +} diff --git a/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/plugins/magic-link.ts b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/plugins/magic-link.ts new file mode 100644 index 000000000..33d518c70 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/plugins/magic-link.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' +import type { Plugin } from '../mount' + +const magic = new Hono() +magic.post('/request', (c) => c.json({ sent: true })) +magic.get('/verify', (c) => c.text('ok')) + +export const magicLinkPlugin: Plugin = { id: 'magic-link', routes: [{ path: '/auth/magic-link', handler: magic }] } diff --git a/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/sort.ts b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/sort.ts new file mode 100644 index 000000000..ce5a7d056 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-helper-list/src/sort.ts @@ -0,0 +1,17 @@ +export interface Sortable { + id: string + dependencies?: string[] +} + +// dependency order (dependencies elided here): rebuilt into a new array at run time from the elements it is given +export function topoSort(plugins: ReadonlyArray): T[] { + const result: T[] = [] + const seen = new Set() + function visit(p: T): void { + if (seen.has(p.id)) return + seen.add(p.id) + result.push(p) + } + for (const p of plugins) visit(p) + return result +} diff --git a/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/app.ts b/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/app.ts new file mode 100644 index 000000000..4b8a859de --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/app.ts @@ -0,0 +1,18 @@ +import { Hono } from 'hono' +import { createMagicLinkPlugin, type Plugin } from './plugins/magic-link' + +function mountAll(app: Hono, plugins: Plugin[]) { + for (const p of plugins.filter((x) => !!x.name)) { + for (const r of p.routes ?? []) app.route(r.path, r.handler) + } +} + +export function createApp() { + const app = new Hono() + app.get('/', (c) => c.text('home')) + const magicLink = createMagicLinkPlugin() + mountAll(app, [magicLink]) + return app +} + +export default createApp() diff --git a/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/plugins/legacy-export.ts b/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/plugins/legacy-export.ts new file mode 100644 index 000000000..da4be31a3 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/plugins/legacy-export.ts @@ -0,0 +1,12 @@ +// A plugin module nothing imports: a loader the program does not show may mount its entry, at a prefix the program +// does not spell. Its handlers are routed nowhere else, so they are partial (unresolved_mount), never roots. +import { Hono } from 'hono' +import type { Plugin } from './magic-link' + +function createLegacyExportPlugin(): Plugin { + const legacy = new Hono() + legacy.get('/csv', (c) => c.text('a,b')) + return { name: 'legacy-export', routes: [{ path: '/legacy/export', handler: legacy }] } +} + +export default createLegacyExportPlugin() diff --git a/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/plugins/magic-link.ts b/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/plugins/magic-link.ts new file mode 100644 index 000000000..1cc3ec043 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-instance-never-mounted/src/plugins/magic-link.ts @@ -0,0 +1,21 @@ +// census: plugin_list_mount (oracle gap O24; SPEC R13 / R13b) +// A plugin factory builds its own app and writes it into a declarative route-list entry. The host calls the factory +// and mounts that instance under the entry's path. The module ALSO default-exports a second instance that nothing +// imports, mounts or serves: its app is never served at any path, so it gives no exact route (not a root at +// /request, /verify). The handlers are still served through the mounted instance. +import { Hono } from 'hono' + +export interface Plugin { name: string; dependencies?: string[]; routes?: { path: string; handler: Hono }[] } + +export function createMagicLinkPlugin(): Plugin { + const routes = new Hono() + routes.post('/request', (c) => c.json({ sent: true })) + routes.get('/verify', (c) => c.text('verified')) + return { + name: 'magic-link', + dependencies: ['email'], + routes: [{ path: '/auth/magic-link', handler: routes }], + } +} + +export default createMagicLinkPlugin() diff --git a/graph/test/typescript/hono/cases/compose/plugin-register-method/src/index.ts b/graph/test/typescript/hono/cases/compose/plugin-register-method/src/index.ts new file mode 100644 index 000000000..46c2f3585 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-register-method/src/index.ts @@ -0,0 +1,22 @@ +// census: route_mount_literal (oracle gap O11 residual; SPEC 2 a mount argument is never a root) +// Plugins mount their apps in a `register(app)` method of the object they hand to definePlugin; the host calls +// it over a FILTERED list of known plugins. R13b: each plugin app is mounted exactly at its literal prefix with +// conditional_registration, never a root. +import { Hono } from 'hono' +import type { PluginInput } from './sdk' +import { mediaPlugin } from './plugins/media' +import { variablesPlugin } from './plugins/variables' + +declare const settings: { enabled: Set } + +export function mountPlugins(app: Hono, plugins: PluginInput[]) { + for (const p of plugins) { + p.register?.(app) + } +} + +const app = new Hono() +app.get('/', (c) => c.text('home')) +mountPlugins(app, [mediaPlugin, variablesPlugin].filter((p) => settings.enabled.has(p.id))) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/plugin-register-method/src/plugins/media.ts b/graph/test/typescript/hono/cases/compose/plugin-register-method/src/plugins/media.ts new file mode 100644 index 000000000..6691ccf1f --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-register-method/src/plugins/media.ts @@ -0,0 +1,17 @@ +import { Hono } from 'hono' +import { definePlugin } from '../sdk' + +function buildMediaApi() { + const api = new Hono() + api.get('/', (c) => c.json([])) + api.delete('/:id', (c) => c.body(null, 204)) + return api +} + +export const mediaPlugin = definePlugin({ + id: 'media', + register(app) { + app.route('/api/media', buildMediaApi()) + }, + menu: [{ label: 'Media', path: '/admin/media' }], +}) diff --git a/graph/test/typescript/hono/cases/compose/plugin-register-method/src/plugins/variables.ts b/graph/test/typescript/hono/cases/compose/plugin-register-method/src/plugins/variables.ts new file mode 100644 index 000000000..646b14c2a --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-register-method/src/plugins/variables.ts @@ -0,0 +1,18 @@ +import { Hono } from 'hono' +import { definePlugin } from '../sdk' + +const apiRoutes = new Hono() +apiRoutes.get('/:key', (c) => c.json({ key: c.req.param('key') })) + +const adminRoutes = new Hono() +adminRoutes.post('/', (c) => c.text('saved', 201)) + +export const variablesPlugin = definePlugin({ + id: 'variables', + register: (app) => { + app.route('/api/variables', apiRoutes) + app.route('/admin/variables', adminRoutes) + }, +}) + +export { apiRoutes as variablesApiRoutes } diff --git a/graph/test/typescript/hono/cases/compose/plugin-register-method/src/sdk.ts b/graph/test/typescript/hono/cases/compose/plugin-register-method/src/sdk.ts new file mode 100644 index 000000000..a4914f8ba --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-register-method/src/sdk.ts @@ -0,0 +1,12 @@ +import type { Hono } from 'hono' + +export interface PluginInput { + id: string + register?(app: Hono): void + menu?: { label: string; path: string }[] +} + +export function definePlugin(input: PluginInput) { + if (!input.id) throw new Error('definePlugin: id is required') + return { ...input, defined: true } +} diff --git a/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins.ts b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins.ts new file mode 100644 index 000000000..60d6fe4cb --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins.ts @@ -0,0 +1,15 @@ +// census: app_request_test, plugin_list_mount (SPEC R16 control; R6, R13b) +// The control for R16: ONE plugin module is mounted by the production app AND by a test's own app. Both apps +// really serve its routes, so its routes are rows on BOTH apps. A plugin only the test mounts stays on the test app; +// a plugin only production mounts stays on the production app. +import type { Hono } from 'hono' + +export interface Plugin { name: string; routes?: { path: string; handler: Hono }[] } + +export function mountPlugin(app: Hono, plugin: Plugin) { + for (const route of plugin.routes ?? []) app.route(route.path, route.handler) +} + +export function registerPlugins(app: Hono, plugins: Plugin[]) { + for (const p of plugins.filter((x) => !!x.name)) mountPlugin(app, p) +} diff --git a/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins/billing.ts b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins/billing.ts new file mode 100644 index 000000000..36a89bda2 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins/billing.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' +import type { Plugin } from '../plugins' + +const billing = new Hono() +billing.get('/invoices', (c) => c.json([])) + +export const billingPlugin: Plugin = { name: 'billing', routes: [{ path: '/api/billing', handler: billing }] } diff --git a/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins/search.ts b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins/search.ts new file mode 100644 index 000000000..f01c3e776 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/plugins/search.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' +import type { Plugin } from '../plugins' + +const search = new Hono() +search.get('/', (c) => c.json({ hits: [] })) +search.post('/reindex', (c) => c.text('queued', 202)) + +export const searchPlugin: Plugin = { name: 'search', routes: [{ path: '/api/search', handler: search }] } diff --git a/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/server.ts b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/server.ts new file mode 100644 index 000000000..0a1c1ac7e --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/server.ts @@ -0,0 +1,10 @@ +import { Hono } from 'hono' +import { registerPlugins } from './plugins' +import { searchPlugin } from './plugins/search' +import { billingPlugin } from './plugins/billing' + +const app = new Hono() +app.get('/', (c) => c.text('home')) +registerPlugins(app, [searchPlugin, billingPlugin]) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/test/search.test.ts b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/test/search.test.ts new file mode 100644 index 000000000..f0cee673b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/plugin-shared-test-and-prod/src/test/search.test.ts @@ -0,0 +1,22 @@ +import { Hono } from 'hono' +import { mountPlugin, type Plugin } from '../plugins' +import { searchPlugin } from '../plugins/search' + +declare function it(name: string, fn: () => unknown): void + +it('serves the search plugin on a bare app', async () => { + const app = new Hono() + mountPlugin(app, searchPlugin) + await app.request('/api/search') + await app.request('/api/search/reindex', { method: 'POST' }) +}) + +it('serves a test-only plugin beside it', async () => { + const probe = new Hono().get('/', (c) => c.text('probe')) + const testOnly: Plugin = { name: 'probe', routes: [{ path: '/__probe', handler: probe }] } + const app = new Hono() + mountPlugin(app, searchPlugin) + mountPlugin(app, testOnly) + await app.request('/__probe') + await app.request('/api/billing/invoices') +}) diff --git a/graph/test/typescript/hono/cases/compose/rebased-holder-getter/src/index.ts b/graph/test/typescript/hono/cases/compose/rebased-holder-getter/src/index.ts new file mode 100644 index 000000000..4ff5a187b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/rebased-holder-getter/src/index.ts @@ -0,0 +1,42 @@ +// census: basepath (violation hono-3-01, SPEC R8a; holder read through a getter) +// The rebased holder is reached through a getter of another class, and the mounted app comes from a controller +// method: still ONE partial (dynamic_mount_prefix) per route, no exact row. +import { Hono } from 'hono' + +declare const process: { env: Record } + +class Modules { + server = new Hono() + constructor(private settings?: { prefix?: string }) {} + reset() { + this.server = new Hono() + if (this.settings?.prefix) { + this.server = this.server.basePath(this.settings.prefix) + } + } +} + +class Kernel { + modules = new Modules({ prefix: process.env.PREFIX }) + get server() { + return this.modules.server + } +} + +class HealthController { + getController() { + const h = new Hono() + h.get('/live', (c) => c.text('live')) + h.post('/drain', (c) => c.text('drained')) + return h + } + register(kernel: Kernel) { + kernel.server.route('/internal/health', this.getController()) + } +} + +const kernel = new Kernel() +kernel.modules.reset() +new HealthController().register(kernel) + +export default { fetch: kernel.server.fetch } diff --git a/graph/test/typescript/hono/cases/compose/rebased-holder-mount/src/index.ts b/graph/test/typescript/hono/cases/compose/rebased-holder-mount/src/index.ts new file mode 100644 index 000000000..15dd6656d --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/rebased-holder-mount/src/index.ts @@ -0,0 +1,26 @@ +// census: basepath (violation hono-3-01, SPEC R8a) +// A sub-app mounted with route() into an app holder that a run-time basePath can rebase: its route is ONE partial +// (dynamic_mount_prefix) and never an exact row, exactly as a route registered on the holder directly. +import { Hono } from 'hono' + +declare const process: { env: Record } + +const status = new Hono() +status.get('/ping', (c) => c.text('pong')) + +class Runtime { + server = new Hono() + constructor(private settings?: { prefix?: string }) {} + reset() { + this.server = new Hono() + if (this.settings?.prefix) { + this.server = this.server.basePath(this.settings.prefix) + } + } +} + +const rt = new Runtime({ prefix: process.env.PREFIX }) +rt.reset() +rt.server.route('/internal/status', status) + +export default rt.server diff --git a/graph/test/typescript/hono/cases/compose/rebased-holder-one-partial/src/index.ts b/graph/test/typescript/hono/cases/compose/rebased-holder-one-partial/src/index.ts new file mode 100644 index 000000000..1d1e937b8 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/rebased-holder-one-partial/src/index.ts @@ -0,0 +1,46 @@ +// census: prop_receiver_route (violation hono-2-04 residue, SPEC R8 / R8a) +// A holder rebuilt by a method that several call sites run (each rebuild may rebase it with a run-time prefix), +// with controllers mounted into it through a build context object. Each route is ONE partial with ONE reason +// (dynamic_mount_prefix), not one row per hole depth and not a second reason per route. +import { Hono } from 'hono' + +declare const process: { env: Record } + +interface BuildContext { + server: Hono +} + +class UsersController { + getController() { + const h = new Hono() + h.get('/', (c) => c.json([])) + h.get('/:id', (c) => c.json({ id: c.req.param('id') })) + return h + } +} + +class Modules { + server = new Hono() + constructor(private options?: { basePath?: string }) {} + private rebuildServer() { + this.server = new Hono() + if (this.options?.basePath) { + this.server = this.server.basePath(this.options.basePath) + } + } + ctx(rebuild?: boolean): BuildContext { + if (rebuild) this.rebuildServer() + return { server: this.server } + } + build() { + this.rebuildServer() + const ctx = this.ctx() + ctx.server.route('/api/users', new UsersController().getController()) + } +} + +const modules = new Modules({ basePath: process.env.BASE_PATH }) +modules.ctx(true) +modules.build() + +export default modules.server diff --git a/graph/test/typescript/hono/cases/compose/rebased-two-mounts/src/index.ts b/graph/test/typescript/hono/cases/compose/rebased-two-mounts/src/index.ts new file mode 100644 index 000000000..faae2199b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/rebased-two-mounts/src/index.ts @@ -0,0 +1,32 @@ +// census: basepath (stress-4 mutation m33 shape; violations hono-3-01, hono-4-04) +// Two sub-apps mounted into a holder a run-time basePath can rebase, one at a literal prefix and one at a run-time +// prefix: every route is ONE partial (dynamic_mount_prefix), no exact rows. +import { Hono } from 'hono' + +declare const process: { env: Record } + +const settings = new Hono() +settings.get('/config/raw', (c) => c.text('raw')) +const callbacks = new Hono() +callbacks.post('/:provider', (c) => c.text(c.req.param('provider'))) + +class Server { + app = new Hono() + private opts?: { prefix?: string } + constructor(opts?: { prefix?: string }) { + this.opts = opts + } + reset() { + this.app = new Hono() + if (this.opts?.prefix) { + this.app = this.app.basePath(this.opts.prefix) + } + } +} + +const srv = new Server({ prefix: process.env.PREFIX }) +srv.reset() +srv.app.route('/api/settings', settings) +srv.app.route(process.env.CALLBACK_PREFIX ?? '/callbacks', callbacks) + +export default srv.app diff --git a/graph/test/typescript/hono/cases/compose/reexport-barrel/src/index.ts b/graph/test/typescript/hono/cases/compose/reexport-barrel/src/index.ts new file mode 100644 index 000000000..83badd3b6 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/reexport-barrel/src/index.ts @@ -0,0 +1,9 @@ +// census: reexport_default_as, route_mount_literal +// A sub-app reached through a barrel re-export (`export { default as x } from`). +import { Hono } from 'hono' +import { books } from './routes' + +const app = new Hono() +app.route('/books', books) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/reexport-barrel/src/routes/books.ts b/graph/test/typescript/hono/cases/compose/reexport-barrel/src/routes/books.ts new file mode 100644 index 000000000..fb4fc1581 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/reexport-barrel/src/routes/books.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const books = new Hono() +books.get('/', (c) => c.json([])) + +export default books diff --git a/graph/test/typescript/hono/cases/compose/reexport-barrel/src/routes/index.ts b/graph/test/typescript/hono/cases/compose/reexport-barrel/src/routes/index.ts new file mode 100644 index 000000000..e30ba09e4 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/reexport-barrel/src/routes/index.ts @@ -0,0 +1 @@ +export { default as books } from './books' diff --git a/graph/test/typescript/hono/cases/compose/register-fn/src/index.ts b/graph/test/typescript/hono/cases/compose/register-fn/src/index.ts new file mode 100644 index 000000000..037a7aea7 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/register-fn/src/index.ts @@ -0,0 +1,15 @@ +// census: app_factory_fn +// The app reaches its routes through registration functions: a parameter typed `Hono`, and one typed +// `typeof `; the callee's registrations are spliced in at the call. +import { Hono } from 'hono' +import { registerUsers } from './users' +import { monitorsApi, registerMonitorRoutes } from './monitors' + +const app = new Hono() +app.get('/', (c) => c.text('first')) +registerUsers(app) +registerMonitorRoutes(monitorsApi) +app.route('/v1', monitorsApi) +app.get('/last', (c) => c.text('last')) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/register-fn/src/monitors.ts b/graph/test/typescript/hono/cases/compose/register-fn/src/monitors.ts new file mode 100644 index 000000000..20a03da91 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/register-fn/src/monitors.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +export const monitorsApi = new Hono() + +export function registerMonitorRoutes(api: typeof monitorsApi) { + api.get('/monitors', (c) => c.json([])) +} diff --git a/graph/test/typescript/hono/cases/compose/register-fn/src/users.ts b/graph/test/typescript/hono/cases/compose/register-fn/src/users.ts new file mode 100644 index 000000000..653205dac --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/register-fn/src/users.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +export function registerUsers(app: Hono) { + app.get('/users', (c) => c.json([])) + app.post('/users', (c) => c.json({}, 201)) +} diff --git a/graph/test/typescript/hono/cases/compose/register-loop-plugins/src/plugins.ts b/graph/test/typescript/hono/cases/compose/register-loop-plugins/src/plugins.ts new file mode 100644 index 000000000..ae21bfb8b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/register-loop-plugins/src/plugins.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +const billing = new Hono().get('/invoices', (c) => c.json([])) +const search = new Hono().get('/query', (c) => c.json([])) + +export const plugins = [ + { prefix: '/billing', app: billing }, + { prefix: '/search', app: search }, +] diff --git a/graph/test/typescript/hono/cases/compose/register-loop-plugins/src/register.ts b/graph/test/typescript/hono/cases/compose/register-loop-plugins/src/register.ts new file mode 100644 index 000000000..5962cdd6b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/register-loop-plugins/src/register.ts @@ -0,0 +1,11 @@ +// census: route_mount_literal (oracle gap O11; SPEC 2 a mount argument is never a root) +// Plugins mounted in a loop inside a register(app) that nothing in the program calls: the plugin apps are +// mount arguments, so they are not roots; the receiver is a parameter typed Hono (prefix unknown). +import type { Hono } from 'hono' +import { plugins } from './plugins' + +export function register(app: Hono) { + for (const p of plugins) { + app.route(p.prefix, p.app) + } +} diff --git a/graph/test/typescript/hono/cases/compose/registered-by/src/index.ts b/graph/test/typescript/hono/cases/compose/registered-by/src/index.ts new file mode 100644 index 000000000..3e9b376e4 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/registered-by/src/index.ts @@ -0,0 +1,14 @@ +// census: app_factory_fn (SPEC 4 [iter2] route_registered_by: the method enclosing each registering call) +// Routes registered by two registration functions, through a mount, and at module level. +import { Hono } from 'hono' +import { registerGetMonitor, registerListMonitors } from './monitors' + +const api = new Hono() +registerGetMonitor(api) +registerListMonitors(api) + +const app = new Hono() +app.get('/', (c) => c.text('module level')) +app.route('/v1', api) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/registered-by/src/monitors.ts b/graph/test/typescript/hono/cases/compose/registered-by/src/monitors.ts new file mode 100644 index 000000000..094351adb --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/registered-by/src/monitors.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +export function registerGetMonitor(api: Hono) { + api.get('/monitors/:id', (c) => c.json({ id: c.req.param('id') })) +} +export function registerListMonitors(api: Hono) { + api.get('/monitors', (c) => c.json([])) + api.post('/monitors', (c) => c.json({}, 201)) +} diff --git a/graph/test/typescript/hono/cases/compose/route-and-partial/src/index.ts b/graph/test/typescript/hono/cases/compose/route-and-partial/src/index.ts new file mode 100644 index 000000000..046a49284 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/route-and-partial/src/index.ts @@ -0,0 +1,19 @@ +// census: app_factory_fn (oracle gap O12) +// One registration function called with a known app and with an app of unknown prefix: the handler has its +// route, and is not also reported partial. +import { Hono } from 'hono' + +interface Ctx { server: Hono } + +export function registerMemory(api: Hono) { + api.get('/memory', (c) => c.json({ used: 0 })) +} + +const app = new Hono() +registerMemory(app) + +export function wire(ctx: Ctx) { + registerMemory(ctx.server) +} + +export default app diff --git a/graph/test/typescript/hono/cases/compose/route-cross-module/src/index.ts b/graph/test/typescript/hono/cases/compose/route-cross-module/src/index.ts new file mode 100644 index 000000000..99e9af792 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/route-cross-module/src/index.ts @@ -0,0 +1,11 @@ +// census: route_mount_literal, export_default_app +// A sub-app defined and default-exported in another module, mounted under a prefix. +import { Hono } from 'hono' +import users from './users' + +const app = new Hono() + +app.get('/', (c) => c.text('home')) +app.route('/users', users) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/route-cross-module/src/users.ts b/graph/test/typescript/hono/cases/compose/route-cross-module/src/users.ts new file mode 100644 index 000000000..78063a585 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/route-cross-module/src/users.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +const users = new Hono() + +users.get('/', (c) => c.json([])) +users.get('/:id', (c) => c.json({ id: c.req.param('id') })) +users.post('/', (c) => c.json({}, 201)) + +export default users diff --git a/graph/test/typescript/hono/cases/compose/route-root/src/index.ts b/graph/test/typescript/hono/cases/compose/route-root/src/index.ts new file mode 100644 index 000000000..5946b2dd6 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/route-root/src/index.ts @@ -0,0 +1,16 @@ +// census: route_mount_root +// route('/', other) merges the other app's routes unprefixed; chained route() calls. +import { Hono } from 'hono' + +const auth = new Hono() +auth.post('/login', (c) => c.text('in')) +auth.post('/logout', (c) => c.text('out')) + +const misc = new Hono() +misc.get('/about', (c) => c.text('about')) + +const app = new Hono() +const routes = app.route('/', auth).route('/', misc) + +export type AppType = typeof routes +export default app diff --git a/graph/test/typescript/hono/cases/compose/same-sub-twice/src/index.ts b/graph/test/typescript/hono/cases/compose/same-sub-twice/src/index.ts new file mode 100644 index 000000000..5bcc957c9 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/same-sub-twice/src/index.ts @@ -0,0 +1,12 @@ +// census: route_mount_literal +// One sub-app mounted under two prefixes serves its routes under both. +import { Hono } from 'hono' + +const health = new Hono() +health.get('/health', (c) => c.text('ok')) + +const app = new Hono() +app.route('/a', health) +app.route('/b', health) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/self-basepath-field/src/index.ts b/graph/test/typescript/hono/cases/compose/self-basepath-field/src/index.ts new file mode 100644 index 000000000..d9923d63f --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/self-basepath-field/src/index.ts @@ -0,0 +1,22 @@ +// census: basepath (violation hono-2-04, SPEC R8) +// A self-referential update of an app field, behind a condition with a run-time base: evaluated once per +// textual assignment; the route is declared ONCE (partial, dynamic_mount_prefix), never exploded. +import { Hono } from 'hono' + +declare const process: { env: Record } + +class Manager { + server = new Hono() + rebuild(base?: string) { + this.server = new Hono() + if (base) { + this.server = this.server.basePath(base) + } + } +} + +const m = new Manager() +m.rebuild(process.env.BASE) +m.server.get('/x', (c) => c.text('x')) + +export default m.server diff --git a/graph/test/typescript/hono/cases/compose/shared-factory-filtered-loop/src/index.ts b/graph/test/typescript/hono/cases/compose/shared-factory-filtered-loop/src/index.ts new file mode 100644 index 000000000..e6c6d013f --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/shared-factory-filtered-loop/src/index.ts @@ -0,0 +1,47 @@ +// census: route_mount_root (stress-5 mutation m35b shape; violation hono-5-02, SPEC R6 and R13b) +// Controllers built through one static factory, each adding its own use() calls, mounted at '/' over a filtered +// list. Each call of the factory is its own app (R6): /reports gets only its own guard; /exports gets the reports +// guard first (mounted at '/', its `*` scope reaches later routes) and its own two, and since that first guard +// belongs to another, filtered element, the chain is declared dynamic. Routes are exact plus conditional. +import { Hono } from 'hono' +import type { Context, Next } from 'hono' + +const guard = () => async (_c: Context, next: Next) => { + await next() +} +const quota = () => async (_c: Context, next: Next) => { + await next() +} + +class Controller { + protected create(): Hono { + return Controller.createServer() + } + static createServer(): Hono { + return new Hono() + } + getController(): Hono { + return this.create() + } +} +class Reports extends Controller { + override getController() { + const h = this.create().use(guard()) + h.get('/reports', (c) => c.text('reports')) + return h + } +} +class Exports extends Controller { + override getController() { + const h = this.create().use(guard()).use(quota()) + h.get('/exports', (c) => c.text('exports')) + return h + } +} + +declare const flags: { has(name: string): boolean } + +const app = new Hono() +const list: Controller[] = [new Reports(), new Exports()].filter((c) => flags.has(c.constructor.name)) +for (const c of list) app.route('/', c.getController()) +export default app diff --git a/graph/test/typescript/hono/cases/compose/sync-wrapper-mount/src/main.ts b/graph/test/typescript/hono/cases/compose/sync-wrapper-mount/src/main.ts new file mode 100644 index 000000000..659664027 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/sync-wrapper-mount/src/main.ts @@ -0,0 +1,13 @@ +// census: bun_serve (stress-5 mutation m38b shape; violation hono-5-06) +// The same wrapper, synchronous: the served wrapper serves the inner route behind its use('*'). +import { Hono } from 'hono' +import { wrapApp } from './wrap' + +declare const Bun: { serve(o: { fetch: (r: Request) => Response | Promise; port: number }): void } + +const inner = new Hono() +inner.get('/hello', (c) => c.text('hi')) +inner.post('/echo', async (c) => c.json(await c.req.json())) + +const app = wrapApp(inner) +Bun.serve({ fetch: app.fetch, port: 3000 }) diff --git a/graph/test/typescript/hono/cases/compose/sync-wrapper-mount/src/wrap.ts b/graph/test/typescript/hono/cases/compose/sync-wrapper-mount/src/wrap.ts new file mode 100644 index 000000000..ae7f8e3ba --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/sync-wrapper-mount/src/wrap.ts @@ -0,0 +1,11 @@ +import { Hono } from 'hono' + +export function wrapApp(inner: Hono): Hono { + const wrapper = new Hono() + wrapper.use('*', async (c, next) => { + c.header('x-wrapped', '1') + await next() + }) + wrapper.route('/', inner) + return wrapper +} diff --git a/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/__tests__/define-plugin.test.ts b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/__tests__/define-plugin.test.ts new file mode 100644 index 000000000..961c485d2 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/__tests__/define-plugin.test.ts @@ -0,0 +1,29 @@ +import { Hono } from 'hono' +import { definePlugin, registerPluginRoutes } from '../sdk' + +declare function it(name: string, fn: () => unknown): void +declare function describe(name: string, fn: () => unknown): void +declare function expect(v: unknown): { toBe(x: unknown): void } + +describe('definePlugin mounts like any plugin', () => { + it('declarative routes mount through registerPluginRoutes', async () => { + const sub = new Hono().get('/', (c) => c.text('defined')) + const p = definePlugin({ id: 'routed', routes: [{ path: '/api/defined', handler: sub }] }) + const app = new Hono() + registerPluginRoutes(app, [p]) + // oracle gap O26: a request inside an assertion's argument is still a request to THIS app + expect(await (await app.request('/api/defined')).text()).toBe('defined') + }) + + it('register(app) runs at mount', async () => { + const p = definePlugin({ + id: 'imperative', + register(app) { + app.route('/imp', new Hono().get('/', (c) => c.text('imp'))) + }, + }) + const app = new Hono() + registerPluginRoutes(app, [p]) + expect((await app.request('/imp')).status).toBe(200) + }) +}) diff --git a/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/__tests__/mount.test.ts b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/__tests__/mount.test.ts new file mode 100644 index 000000000..0dddc5532 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/__tests__/mount.test.ts @@ -0,0 +1,33 @@ +import { Hono } from 'hono' +import { mountPlugin, registerPluginRoutes, type MountLog, type Plugin } from '../sdk' + +declare function it(name: string, fn: () => unknown): void + +function routesFor(label: string) { + const r = new Hono() + r.get('/', (c) => c.text(label)) + return r +} + +it('mounts one plugin', async () => { + const app = new Hono() + const plugin: Plugin = { name: 'alpha', routes: [{ path: '/alpha', handler: routesFor('alpha') }] } + mountPlugin(app, plugin) + await app.request('/alpha') +}) + +it('records what it mounted', async () => { + const app = new Hono() + const log: MountLog = { mounted: [], skipped: [] } + mountPlugin(app, { name: 'beta', routes: [{ path: '/beta', handler: routesFor('beta'), priority: 5 }] }, log) + await app.request('/beta') +}) + +it('mounts a list in order', async () => { + const app = new Hono() + registerPluginRoutes(app, [ + { name: 'gamma', routes: [{ path: '/gamma', handler: routesFor('gamma') }] }, + null, + ]) + await app.request('/gamma') +}) diff --git a/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/app.ts b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/app.ts new file mode 100644 index 000000000..6982d6f85 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/app.ts @@ -0,0 +1,15 @@ +import { Hono } from 'hono' +import { registerPluginRoutes } from './sdk' +import { auditPlugin } from './plugins/audit' +import { tenantsPlugin } from './plugins/tenants' + +const corePlugins = [auditPlugin, tenantsPlugin] + +export function createApp() { + const app = new Hono() + app.get('/health', (c) => c.text('ok')) + registerPluginRoutes(app, corePlugins) + return app +} + +export default createApp() diff --git a/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/plugins/audit.ts b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/plugins/audit.ts new file mode 100644 index 000000000..949432128 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/plugins/audit.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' +import { definePlugin } from '../sdk' + +const audit = new Hono() +audit.get('/', (c) => c.json([])) +audit.get('/:id', (c) => c.json({ id: c.req.param('id') })) + +export const auditPlugin = definePlugin({ id: 'audit', routes: [{ path: '/admin/audit', handler: audit }] }) diff --git a/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/plugins/tenants.ts b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/plugins/tenants.ts new file mode 100644 index 000000000..a96bb1acf --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/plugins/tenants.ts @@ -0,0 +1,15 @@ +import { Hono } from 'hono' +import { definePlugin } from '../sdk' + +function tenantRoutes() { + const r = new Hono() + r.get('/', (c) => c.text('tenants')) + return r +} + +export const tenantsPlugin = definePlugin({ + id: 'tenants', + register(app) { + app.route('/admin/tenants', tenantRoutes()) + }, +}) diff --git a/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/sdk.ts b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/sdk.ts new file mode 100644 index 000000000..6e9ed04ab --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/test-plugin-own-app/src/sdk.ts @@ -0,0 +1,70 @@ +// census: app_request_test, plugin_list_mount (SPEC R16; R6, R13b; violation hono-6-02) +// A plugin SDK: definePlugin copies `routes` and `register` from its input; mountPlugin mounts one plugin's +// priority-sorted routes and calls its register(app); registerPluginRoutes mounts a filtered, dependency-sorted list. +// Production and several tests each call these helpers on their OWN app. A route belongs only to the app that the +// call which brought its plugin was given: test plugins never reach the production app or another test's app. +import type { Hono } from 'hono' + +export interface PluginRoute { path: string; handler: Hono; priority?: number } +export interface Plugin { + name: string + dependencies?: string[] + routes?: PluginRoute[] + register?: (app: Hono) => void +} +export interface MountLog { mounted: string[]; skipped: string[] } + +export function definePlugin(input: { id: string; routes?: PluginRoute[]; register?: (app: Hono) => void }): Plugin { + if (!input.id) throw new Error('id is required') + return { name: input.id, routes: input.routes, register: input.register } +} + +function byPriority(routes: PluginRoute[]): PluginRoute[] { + return routes + .map((route, index) => ({ route, index })) + .sort((a, b) => (b.route.priority ?? 0) - (a.route.priority ?? 0) || a.index - b.index) + .map((e) => e.route) +} + +export function mountPlugin(app: Hono, plugin: Plugin, log?: MountLog): void { + if (!plugin || !plugin.name) return + if (Array.isArray(plugin.routes) && plugin.routes.length > 0) { + for (const route of byPriority(plugin.routes)) { + if (!route.path || !route.handler) continue + app.route(route.path, route.handler) + log?.mounted.push(route.path) + } + } + if (typeof plugin.register === 'function') plugin.register(app) +} + +function dependencyOrder(plugins: ReadonlyArray): T[] { + const byName = new Map() + for (const p of plugins) byName.set(p.name, p) + const out: T[] = [] + const done = new Set() + function visit(p: T): void { + if (done.has(p.name)) return + done.add(p.name) + for (const d of p.dependencies ?? []) { + const dep = byName.get(d) + if (dep) visit(dep) + } + out.push(p) + } + for (const p of plugins) visit(p) + return out +} + +export function registerPluginRoutes(app: Hono, plugins: Array, opts: { sort?: boolean } = {}): MountLog { + const log: MountLog = { mounted: [], skipped: [] } + const kept = (opts.sort ?? true) + ? dependencyOrder(plugins.filter((p): p is Plugin => !!p)) + : plugins.filter((p): p is Plugin => !!p) + for (const plugin of kept) { + const before = log.mounted.length + mountPlugin(app, plugin, log) + if (log.mounted.length === before) log.skipped.push(plugin.name) + } + return log +} diff --git a/graph/test/typescript/hono/cases/compose/typed-property-receiver/src/index.ts b/graph/test/typescript/hono/cases/compose/typed-property-receiver/src/index.ts new file mode 100644 index 000000000..763e8ead5 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/typed-property-receiver/src/index.ts @@ -0,0 +1,20 @@ +// census: route_mount_literal (violation hono-1-01; SPEC R2) +// route() on a receiver DECLARED as Hono (an interface property) whose value the program never shows: a mount +// on an app of unknown prefix. The child's route is partial (unresolved_app_value), never a root route at /raw. +import { Hono } from 'hono' + +interface ServerCtx { + server: Hono +} + +class StatusController { + getController() { + const h = new Hono() + h.get('/raw', (c) => c.text('raw')) + return h + } +} + +export function register(ctx: ServerCtx) { + ctx.server.route('/api/status', new StatusController().getController()) +} diff --git a/graph/test/typescript/hono/cases/compose/uncalled-start-server/src/api.ts b/graph/test/typescript/hono/cases/compose/uncalled-start-server/src/api.ts new file mode 100644 index 000000000..2674e18c7 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/uncalled-start-server/src/api.ts @@ -0,0 +1,4 @@ +import { Hono } from 'hono' + +export const api = new Hono() +api.get('/status', (c) => c.text('up')) diff --git a/graph/test/typescript/hono/cases/compose/uncalled-start-server/src/server.ts b/graph/test/typescript/hono/cases/compose/uncalled-start-server/src/server.ts new file mode 100644 index 000000000..cf2a2c182 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/uncalled-start-server/src/server.ts @@ -0,0 +1,11 @@ +// census: app_factory_fn (oracle gap O2) +// The server is assembled inside a function no module in the program calls (its caller is a CLI outside it): +// the sub-app is mounted under /api, not served bare. +import { Hono } from 'hono' +import { api } from './api' + +export function startServer(port: number) { + const app = new Hono() + app.route('/api', api) + return { app, port } +} diff --git a/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/apps/api/src/index.ts b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/apps/api/src/index.ts new file mode 100644 index 000000000..b48b17f21 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/apps/api/src/index.ts @@ -0,0 +1,14 @@ +// census: workspace_subpath (SPEC R22a; violation hono-5-06's shape) +// The app imports `@acme/core/adapters`, a subpath the workspace package's `exports` does not list, under +// moduleResolution bundler. The TypeScript compiler enforces `exports` in this mode and refuses the import, so the +// binding is unknown: the mount is unresolved and nothing is served under /adapters. (The adapters module's own app, +// which nothing mounts, keeps the root row every never-mounted exported app gets; that is not R22a's to change.) +import { Hono } from 'hono' +// @ts-expect-error -- not exported by @acme/core under moduleResolution bundler (the compiler refuses it) +import { adapters } from '@acme/core/adapters' + +const app = new Hono() +app.get('/health', (c) => c.text('ok')) +app.route('/adapters', adapters) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/apps/api/tsconfig.json b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/apps/api/tsconfig.json new file mode 100644 index 000000000..b34b8ae7c --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/apps/api/tsconfig.json @@ -0,0 +1 @@ +{ "compilerOptions": { "target": "es2022", "module": "esnext", "moduleResolution": "bundler", "strict": true }, "include": ["src"] } diff --git a/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/adapters/index.ts b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/adapters/index.ts new file mode 100644 index 000000000..9532fe3cd --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/adapters/index.ts @@ -0,0 +1,5 @@ +import { Hono } from 'hono' + +// a sub-app in a subpath the package's `exports` does not list +export const adapters = new Hono() +adapters.get('/list', (c) => c.json([])) diff --git a/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/index.ts b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/index.ts new file mode 100644 index 000000000..168f0cfa2 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/index.ts @@ -0,0 +1 @@ +export const coreVersion = '1' diff --git a/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/package.json b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/package.json new file mode 100644 index 000000000..cf76582b8 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/workspace-unlisted-subpath-bundler/src/packages/core/package.json @@ -0,0 +1 @@ +{ "name": "@acme/core", "version": "1.0.0", "exports": { ".": "./index.ts" } } diff --git a/graph/test/typescript/hono/cases/context/chain-of-setters/src/index.ts b/graph/test/typescript/hono/cases/context/chain-of-setters/src/index.ts new file mode 100644 index 000000000..008f2d9cd --- /dev/null +++ b/graph/test/typescript/hono/cases/context/chain-of-setters/src/index.ts @@ -0,0 +1,20 @@ +// census: ctx_set, ctx_get, verb_multi_handler +// Per-route middleware sets a key a later per-route middleware and the handler read. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +type Env = { Variables: { lang: string; greeting: string } } + +const detectLang: MiddlewareHandler = async (c, next) => { + c.set('lang', 'en') + await next() +} +const greet: MiddlewareHandler = async (c, next) => { + c.set('greeting', c.get('lang') === 'en' ? 'hello' : 'hallo') + await next() +} + +const app = new Hono() +app.get('/hello', detectLang, greet, (c) => c.text(`${c.var.greeting} (${c.get('lang')})`)) + +export default app diff --git a/graph/test/typescript/hono/cases/context/cross-module/src/auth.ts b/graph/test/typescript/hono/cases/context/cross-module/src/auth.ts new file mode 100644 index 000000000..908c82472 --- /dev/null +++ b/graph/test/typescript/hono/cases/context/cross-module/src/auth.ts @@ -0,0 +1,8 @@ +import { createMiddleware } from 'hono/factory' + +export type AuthEnv = { Variables: { session: { id: string } } } + +export const session = createMiddleware(async (c, next) => { + c.set('session', { id: 's1' }) + await next() +}) diff --git a/graph/test/typescript/hono/cases/context/cross-module/src/index.ts b/graph/test/typescript/hono/cases/context/cross-module/src/index.ts new file mode 100644 index 000000000..5321950b0 --- /dev/null +++ b/graph/test/typescript/hono/cases/context/cross-module/src/index.ts @@ -0,0 +1,12 @@ +// census: ctx_set, ctx_get, route_mount_literal +// The setter is in one module, the readers in a sub-app in another; the flow follows the mounted chain. +// A function that sets then reads the same key flows to itself (SPEC 3.4, j == i only when it reads after). +import { Hono } from 'hono' +import { session, type AuthEnv } from './auth' +import { account } from './routes' + +const app = new Hono() +app.use('/account/*', session) +app.route('/account', account) + +export default app diff --git a/graph/test/typescript/hono/cases/context/cross-module/src/routes.ts b/graph/test/typescript/hono/cases/context/cross-module/src/routes.ts new file mode 100644 index 000000000..265223b2d --- /dev/null +++ b/graph/test/typescript/hono/cases/context/cross-module/src/routes.ts @@ -0,0 +1,11 @@ +import { Hono } from 'hono' +import type { AuthEnv } from './auth' + +export const account = new Hono() + +account.get('/', (c) => c.json(c.get('session'))) +account.delete('/', async (c, next) => { + const s = c.var.session + c.set('session', { id: '' }) + return c.json(s) +}) diff --git a/graph/test/typescript/hono/cases/context/set-get/src/index.ts b/graph/test/typescript/hono/cases/context/set-get/src/index.ts new file mode 100644 index 000000000..c754b46a7 --- /dev/null +++ b/graph/test/typescript/hono/cases/context/set-get/src/index.ts @@ -0,0 +1,22 @@ +// census: ctx_set, ctx_get, ctx_var, variables_type +// A middleware sets keys on the context; handlers read them by c.get and c.var; a reader with no +// setter on its chain is declared, not linked. +import { Hono } from 'hono' +import { createMiddleware } from 'hono/factory' + +type Env = { Variables: { user: string; tenant: string; traceId: string } } + +const loadUser = createMiddleware(async (c, next) => { + c.set('user', 'u1') + c.set('tenant', 't1') + await next() +}) + +const app = new Hono() +app.get('/before', (c) => c.text(c.get('user') ?? 'no setter ran before this route')) +app.use(loadUser) +app.get('/me', (c) => c.text(c.get('user'))) +app.get('/tenant', (c) => c.text(c.var.tenant)) +app.get('/trace', (c) => c.text(c.var.traceId ?? 'never set')) + +export default app diff --git a/graph/test/typescript/hono/cases/deno/denoland-url/src/deno.json b/graph/test/typescript/hono/cases/deno/denoland-url/src/deno.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/denoland-url/src/deno.json @@ -0,0 +1 @@ +{} diff --git a/graph/test/typescript/hono/cases/deno/denoland-url/src/main.ts b/graph/test/typescript/hono/cases/deno/denoland-url/src/main.ts new file mode 100644 index 000000000..10ae9aa9a --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/denoland-url/src/main.ts @@ -0,0 +1,10 @@ +// census: none (corpus gap). https://deno.land/x/hono@v3.x/mod.ts and /middleware.ts: shape from Hono's Deno docs (v3 era), corpus gap. +// Oracle maps mod.ts to the pinned hono and middleware.ts to a re-export of its built-ins. +import { Hono } from 'https://deno.land/x/hono@v3.12.11/mod.ts' +import { etag } from 'https://deno.land/x/hono@v3.12.11/middleware.ts' + +const app = new Hono() +app.use(etag()) +app.get('/url', (c) => c.text('url')) + +export default app diff --git a/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/deno.json b/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/deno.json new file mode 100644 index 000000000..7bf44f9bb --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/deno.json @@ -0,0 +1,7 @@ +{ + "imports": { + "hono": "jsr:@hono/hono@^4", + "hono/": "jsr:@hono/hono@^4/" + }, + "tasks": { "start": "deno serve main.ts" } +} diff --git a/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/main.ts b/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/main.ts new file mode 100644 index 000000000..47d1f82d2 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/main.ts @@ -0,0 +1,12 @@ +// census: route_mount_literal, export_default_app; bare 'hono' mapped by deno.json imports (exact and the +// "hono/" prefix map), .ts-extension relative imports, `export default app` run by `deno serve`. +// The one corpus Deno shape (deno.json import map). Oracle applies the import map. +import { Hono } from 'hono' +import { secureHeaders } from 'hono/secure-headers' +import { users } from './routes/users.ts' + +const app = new Hono() +app.use(secureHeaders()) +app.route('/users', users) + +export default app diff --git a/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/routes/users.ts b/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/routes/users.ts new file mode 100644 index 000000000..f219b0911 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/import-map-deno-json/src/routes/users.ts @@ -0,0 +1,5 @@ +import { Hono } from 'hono' + +export const users = new Hono() +users.get('/', (c) => c.json([])) +users.get('/:id', (c) => c.json({ id: c.req.param('id') })) diff --git a/graph/test/typescript/hono/cases/deno/import-map-file/src/import_map.json b/graph/test/typescript/hono/cases/deno/import-map-file/src/import_map.json new file mode 100644 index 000000000..19a8b0bc6 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/import-map-file/src/import_map.json @@ -0,0 +1,6 @@ +{ + "imports": { + "hono": "npm:hono@4", + "hono/": "npm:hono@4/" + } +} diff --git a/graph/test/typescript/hono/cases/deno/import-map-file/src/main.ts b/graph/test/typescript/hono/cases/deno/import-map-file/src/main.ts new file mode 100644 index 000000000..6d057b43f --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/import-map-file/src/main.ts @@ -0,0 +1,13 @@ +// census: none (corpus gap). a standalone import_map.json mapping bare hono to npm:hono@4 (prefix map for hono/*): shape from Hono's +// Deno docs, corpus gap. +import { Hono } from 'hono' +import { createMiddleware } from 'hono/factory' + +const timer = createMiddleware(async (c, next) => { + await next() +}) +const app = new Hono() +app.use(timer) +app.get('/mapped', (c) => c.text('mapped')) + +export default { fetch: app.fetch } diff --git a/graph/test/typescript/hono/cases/deno/import-map-jsonc/src/deno.jsonc b/graph/test/typescript/hono/cases/deno/import-map-jsonc/src/deno.jsonc new file mode 100644 index 000000000..a2f2a65f6 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/import-map-jsonc/src/deno.jsonc @@ -0,0 +1,6 @@ +{ + // comments are allowed here + "imports": { + "@hono/hono": "jsr:@hono/hono@^4" /* the bare JSR name */ + } +} diff --git a/graph/test/typescript/hono/cases/deno/import-map-jsonc/src/main.ts b/graph/test/typescript/hono/cases/deno/import-map-jsonc/src/main.ts new file mode 100644 index 000000000..2dfc10c3e --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/import-map-jsonc/src/main.ts @@ -0,0 +1,7 @@ +// census: none (corpus gap). bare JSR name `@hono/hono` mapped by deno.jsonc (comments stripped): shape from Hono's Deno docs, corpus gap. +import { Hono } from '@hono/hono' + +const app = new Hono() +app.get('/jsonc', (c) => c.text('jsonc')) + +Deno.serve(app.fetch) diff --git a/graph/test/typescript/hono/cases/deno/jsr-specifier/src/deno.json b/graph/test/typescript/hono/cases/deno/jsr-specifier/src/deno.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/jsr-specifier/src/deno.json @@ -0,0 +1 @@ +{} diff --git a/graph/test/typescript/hono/cases/deno/jsr-specifier/src/main.ts b/graph/test/typescript/hono/cases/deno/jsr-specifier/src/main.ts new file mode 100644 index 000000000..bc9a7d310 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/jsr-specifier/src/main.ts @@ -0,0 +1,11 @@ +// census: deno_serve. jsr:@hono/hono and jsr:@hono/hono@^4/logger specifiers are a shape from Hono's Deno +// docs, corpus gap. The oracle maps jsr:/npm:/URL hono specifiers to the pinned hono types (oracle-only +// resolution host); the runtime half cannot load them under node and says so. +import { Hono } from 'jsr:@hono/hono' +import { logger } from 'jsr:@hono/hono@^4/logger' + +const app = new Hono() +app.use(logger()) +app.get('/jsr', (c) => c.text('jsr')) + +Deno.serve(app.fetch) diff --git a/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/deno.json b/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/deno.json new file mode 100644 index 000000000..58b00fb7e --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/deno.json @@ -0,0 +1 @@ +{ "imports": { "hono": "jsr:@hono/hono@4.7.10", "hono/": "jsr:@hono/hono@4.7.10/" } } diff --git a/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/main.ts b/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/main.ts new file mode 100644 index 000000000..283d6b4e4 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/main.ts @@ -0,0 +1,8 @@ +// census: deno_serve (shape of a Deno holdout project: a deno.json import map to jsr:, below the repository root, +// and an app created by a static method; served with Deno.serve({ port }, fetch)) +import { Routes } from './routes.ts' + +const hono = Routes.initHono('/static') +hono.get('/logo.jpg', (c) => c.text('jpg')) + +Deno.serve({ port: 8000 }, hono.fetch) diff --git a/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/routes.ts b/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/routes.ts new file mode 100644 index 000000000..9a601f0e6 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/jsr-static-method/src/api/routes.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +export class Routes { + static initHono(base: string) { + return new Hono().basePath(base) + } +} diff --git a/graph/test/typescript/hono/cases/deno/npm-specifier/src/deno.json b/graph/test/typescript/hono/cases/deno/npm-specifier/src/deno.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/npm-specifier/src/deno.json @@ -0,0 +1 @@ +{} diff --git a/graph/test/typescript/hono/cases/deno/npm-specifier/src/main.ts b/graph/test/typescript/hono/cases/deno/npm-specifier/src/main.ts new file mode 100644 index 000000000..6e771f6ab --- /dev/null +++ b/graph/test/typescript/hono/cases/deno/npm-specifier/src/main.ts @@ -0,0 +1,10 @@ +// census: none (corpus gap). npm:hono@4 and npm:hono/cors specifiers: shape from Hono's Deno docs, corpus gap. Oracle maps them to the +// pinned hono types. +import { Hono } from 'npm:hono@4' +import { cors } from 'npm:hono/cors' + +const app = new Hono() +app.use('/api/*', cors()) +app.get('/api/npm', (c) => c.text('npm')) + +Deno.serve({ port: 8000 }, app.fetch) diff --git a/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/index.ts b/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/index.ts new file mode 100644 index 000000000..c63f18a5d --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/index.ts @@ -0,0 +1,13 @@ +// census: app_factory_fn, export_default_fetch_obj (oracle gap O4, SPEC R4) +// The app comes from a tsconfig-path-aliased module, and the default export is a custom object whose fetch +// forwards to the app's fetch: a served shape, and the forwarding call is no request of its own. +import { newApp } from '@/lib/app' + +const app = newApp() +app.get('/aliased', (c) => c.text('aliased')) + +export default { + fetch(req: Request, env: unknown, ctx: any) { + return app.fetch(req, env, ctx) + }, +} diff --git a/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/lib/app.ts b/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/lib/app.ts new file mode 100644 index 000000000..a628cb589 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/lib/app.ts @@ -0,0 +1,5 @@ +import { Hono } from 'hono' + +export function newApp() { + return new Hono() +} diff --git a/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/tsconfig.json b/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/tsconfig.json new file mode 100644 index 000000000..711f00614 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/aliased-app-fetch-wrapper/src/tsconfig.json @@ -0,0 +1 @@ +{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./*"] } } } diff --git a/graph/test/typescript/hono/cases/entry/app-request-test/src/app.test.ts b/graph/test/typescript/hono/cases/entry/app-request-test/src/app.test.ts new file mode 100644 index 000000000..55fcdafad --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/app-request-test/src/app.test.ts @@ -0,0 +1,30 @@ +// census: app_request_test, test_client +// Tests that hit routes through app.request and testClient: each test function reaches its route. +import { testClient } from 'hono/testing' +import { app } from './app' + +export async function healthIsOk() { + const res = await app.request('/health') + return res.status === 200 +} + +export async function createsItem() { + const res = await app.request('/items', { method: 'POST', body: '{}' }) + return res.status === 201 +} + +export async function readsOneItem() { + const res = await app.request('/items/42') + return res.status +} + +export async function viaTestClient() { + const client = testClient(app) + const res = await client.health.$get() + return res.status +} + +export async function missesEveryRoute() { + const res = await app.request('/nowhere') + return res.status +} diff --git a/graph/test/typescript/hono/cases/entry/app-request-test/src/app.ts b/graph/test/typescript/hono/cases/entry/app-request-test/src/app.ts new file mode 100644 index 000000000..31939f36b --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/app-request-test/src/app.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +export const app = new Hono() + .get('/health', (c) => c.text('ok')) + .post('/items', (c) => c.json({}, 201)) + .get('/items/:id', (c) => c.json({ id: c.req.param('id') })) diff --git a/graph/test/typescript/hono/cases/entry/deno-bun/src/index.ts b/graph/test/typescript/hono/cases/entry/deno-bun/src/index.ts new file mode 100644 index 000000000..adfd56e43 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/deno-bun/src/index.ts @@ -0,0 +1,14 @@ +// census: deno_serve, bun_serve +// Deno.serve(app.fetch) and Bun.serve({ fetch }) (runtime globals declared locally so the fixture typechecks). +import { Hono } from 'hono' + +declare const Deno: { serve(handler: (req: Request) => Response | Promise): void } +declare const Bun: { serve(opts: { fetch: (req: Request) => Response | Promise }): void } + +export const app = new Hono() +app.get('/deno', (c) => c.text('deno')) + +export function start(kind: 'deno' | 'bun') { + if (kind === 'deno') Deno.serve(app.fetch) + else Bun.serve({ fetch: app.fetch }) +} diff --git a/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/admin.ts b/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/admin.ts new file mode 100644 index 000000000..8891d2f4a --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/admin.ts @@ -0,0 +1,3 @@ +import { Hono } from 'hono' + +export const adminRoutes = new Hono().get('/stats', (c) => c.json({ n: 0 })) diff --git a/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/skills.test.ts b/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/skills.test.ts new file mode 100644 index 000000000..d3623afdf --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/skills.test.ts @@ -0,0 +1,29 @@ +// census: app_request_test (oracle gap O16; SPEC R1) +// Test apps mount sub-apps bound by `const { x } = await import('./m.ts')` (at module level and inside a test): +// the test apps are roots, the requests reach the mounted routes, nothing is unserved. +import { Hono } from 'hono' + +declare function it(name: string, fn: () => unknown): void + +const { skillsRoutes } = await import('./skills.ts') + +const testApp = new Hono() + .use('*', async (c, next) => { + c.header('x-test', '1') + await next() + }) + .route('/', skillsRoutes) + +it('reads a skill', async () => { + await testApp.request('/acme/review') +}) + +it('publishes a skill', async () => { + await testApp.request('/acme/review', { method: 'POST', body: '{}' }) +}) + +it('reads admin stats', async () => { + const { adminRoutes } = await import('./admin.ts') + const app = new Hono().route('/admin', adminRoutes) + await app.request('/admin/stats') +}) diff --git a/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/skills.ts b/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/skills.ts new file mode 100644 index 000000000..39d10bbb8 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/dynamic-import-test-app/src/skills.ts @@ -0,0 +1,5 @@ +import { Hono } from 'hono' + +export const skillsRoutes = new Hono() + .get('/:ns/:name', (c) => c.json({ ns: c.req.param('ns'), name: c.req.param('name') })) + .post('/:ns/:name', (c) => c.json({ ok: true }, 201)) diff --git a/graph/test/typescript/hono/cases/entry/fetch-object/src/index.ts b/graph/test/typescript/hono/cases/entry/fetch-object/src/index.ts new file mode 100644 index 000000000..2a3a61e90 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/fetch-object/src/index.ts @@ -0,0 +1,18 @@ +// census: export_default_fetch_obj, app_fetch_ref +// A Workers-style default export: fetch is the app; scheduled and queue are called by the platform. +import { Hono } from 'hono' + +const app = new Hono() +app.get('/', (c) => c.text('worker')) + +async function scheduled(event: unknown) { + return event +} + +export default { + fetch: app.fetch, + scheduled, + async queue(batch: unknown) { + return batch + }, +} diff --git a/graph/test/typescript/hono/cases/entry/fixture-prop-request/src/app.test.ts b/graph/test/typescript/hono/cases/entry/fixture-prop-request/src/app.test.ts new file mode 100644 index 000000000..33ab6d4b6 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/fixture-prop-request/src/app.test.ts @@ -0,0 +1,15 @@ +// census: app_request_test (violation hono-2-06) +// A request on an app held in a property of the object a test helper returns reaches the route. +import { createApp } from './app' + +declare function test(name: string, fn: () => Promise): void + +async function setupFixture() { + const dir = '/tmp/x' + return { dir, app: createApp(dir) } +} + +test('flow', async () => { + const fixture = await setupFixture() + await fixture.app.request('http://localhost/api/flow') +}) diff --git a/graph/test/typescript/hono/cases/entry/fixture-prop-request/src/app.ts b/graph/test/typescript/hono/cases/entry/fixture-prop-request/src/app.ts new file mode 100644 index 000000000..2a050f895 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/fixture-prop-request/src/app.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +export function createApp(dir: string) { + const app = new Hono() + app.get('/api/flow', (c) => c.text(dir)) + return app +} diff --git a/graph/test/typescript/hono/cases/entry/jsx-render/src/components.tsx b/graph/test/typescript/hono/cases/entry/jsx-render/src/components.tsx new file mode 100644 index 000000000..22bbeecdb --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/jsx-render/src/components.tsx @@ -0,0 +1,10 @@ +/** @jsxImportSource hono/jsx */ +import type { FC } from 'hono/jsx' + +export const Layout: FC = (props) => ( + + {props.children} + +) + +export const Greeting: FC<{ name: string }> = ({ name }) =>

Hello {name}

diff --git a/graph/test/typescript/hono/cases/entry/jsx-render/src/index.tsx b/graph/test/typescript/hono/cases/entry/jsx-render/src/index.tsx new file mode 100644 index 000000000..052e9d02a --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/jsx-render/src/index.tsx @@ -0,0 +1,15 @@ +/** @jsxImportSource hono/jsx */ +// census: c_render, jsx_import +// hono/jsx components rendered through c.html, and c.render under jsxRenderer: SPEC 3.8 gives them no Hono +// semantics, so the handlers are routes and entries, jsxRenderer is middleware, and the components are +// neither routes, middleware nor entries. +import { Hono } from 'hono' +import { jsxRenderer } from 'hono/jsx-renderer' +import { Layout, Greeting } from './components' + +const app = new Hono() +app.get('/html', (c) => c.html()) +app.use('/page/*', jsxRenderer(({ children }) => {children})) +app.get('/page/hello', (c) => c.render()) + +export default app diff --git a/graph/test/typescript/hono/cases/entry/node-serve/src/index.ts b/graph/test/typescript/hono/cases/entry/node-serve/src/index.ts new file mode 100644 index 000000000..0903ba2f5 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/node-serve/src/index.ts @@ -0,0 +1,9 @@ +// census: serve_fetch +// Served with serve({ fetch: app.fetch }) from the node adapter; nothing is exported. +import { Hono } from 'hono' +import { serve } from '@hono/node-server' + +const app = new Hono() +app.get('/served', (c) => c.text('served')) + +serve({ fetch: app.fetch, port: 8787 }) diff --git a/graph/test/typescript/hono/cases/entry/notfound-onerror/src/index.ts b/graph/test/typescript/hono/cases/entry/notfound-onerror/src/index.ts new file mode 100644 index 000000000..9c17ba302 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/notfound-onerror/src/index.ts @@ -0,0 +1,12 @@ +// census: not_found, on_error +// notFound and onError handlers are http entry points, not routes. +import { Hono } from 'hono' + +const app = new Hono() +app.get('/boom', () => { + throw new Error('boom') +}) +app.notFound((c) => c.text('custom 404', 404)) +app.onError((err, c) => c.text(`custom 500: ${err.message}`, 500)) + +export default app diff --git a/graph/test/typescript/hono/cases/entry/request-object-and-fetch/src/app.test.ts b/graph/test/typescript/hono/cases/entry/request-object-and-fetch/src/app.test.ts new file mode 100644 index 000000000..01bc6a3ae --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/request-object-and-fetch/src/app.test.ts @@ -0,0 +1,18 @@ +// census: app_request_test, app_fetch_ref +// Tests that build a Request object, through app.request(new Request(...)) and app.fetch(req, env). +import { app } from './app' + +export async function viaRequestObject() { + const res = await app.request(new Request('http://localhost/ping')) + return res.status +} + +export async function viaFetchWithInit() { + const res = await app.fetch(new Request('http://localhost/items/7', { method: 'PUT' }), { DB: 'test' }) + return res.status +} + +export async function viaRequestWithInit() { + const res = await app.request(new Request('http://localhost/items/8', { method: 'PUT' })) + return res.status +} diff --git a/graph/test/typescript/hono/cases/entry/request-object-and-fetch/src/app.ts b/graph/test/typescript/hono/cases/entry/request-object-and-fetch/src/app.ts new file mode 100644 index 000000000..0755de163 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/request-object-and-fetch/src/app.ts @@ -0,0 +1,5 @@ +import { Hono } from 'hono' + +export const app = new Hono() +app.get('/ping', (c) => c.text('pong')) +app.put('/items/:id', (c) => c.text('put')) diff --git a/graph/test/typescript/hono/cases/entry/test-built-app/src/app.test.ts b/graph/test/typescript/hono/cases/entry/test-built-app/src/app.test.ts new file mode 100644 index 000000000..f455fc302 --- /dev/null +++ b/graph/test/typescript/hono/cases/entry/test-built-app/src/app.test.ts @@ -0,0 +1,16 @@ +// census: app_request_test (SPEC R1: oracle gap O1/O5) +// An app built INSIDE a test callback and exercised with app.request is a served root: its routes are +// http_route rows and the request reaches them. +import { Hono } from 'hono' + +declare function describe(name: string, fn: () => void): void +declare function it(name: string, fn: () => Promise | void): void + +describe('health', () => { + it('answers', async () => { + const app = new Hono() + app.get('/health', (c) => c.text('ok')) + const res = await app.request('/health') + if (res.status !== 200) throw new Error('down') + }) +}) diff --git a/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/index.ts b/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/index.ts new file mode 100644 index 000000000..61483b7d1 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/index.ts @@ -0,0 +1,14 @@ +// census: use_global (oracle gap O14) +// Library middleware re-exported through a project barrel (`export { cors } from 'hono/cors'`, a renamed export, +// `export *`) is still the library middleware at its registering call, in order, with no dynamic list. +import { Hono } from 'hono' +import { audit, cors, requestLog, secureHeaders } from './middleware' + +const app = new Hono() +app.use('*', requestLog()) +app.use('*', cors({ origin: '*' })) +app.use('/api/*', secureHeaders(), audit) +app.get('/api/me', (c) => c.json({ id: 1 })) +app.get('/health', (c) => c.text('ok')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/middleware/audit.ts b/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/middleware/audit.ts new file mode 100644 index 000000000..94141fe11 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/middleware/audit.ts @@ -0,0 +1,6 @@ +import { createMiddleware } from 'hono/factory' + +export const audit = createMiddleware(async (c, next) => { + await next() + c.header('x-audited', '1') +}) diff --git a/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/middleware/index.ts b/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/middleware/index.ts new file mode 100644 index 000000000..6b1afd792 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/barrel-library-mw/src/middleware/index.ts @@ -0,0 +1,4 @@ +export { cors } from 'hono/cors' +export { logger as requestLog } from 'hono/logger' +export * from 'hono/secure-headers' +export { audit } from './audit' diff --git a/graph/test/typescript/hono/cases/middleware/builtin/src/index.ts b/graph/test/typescript/hono/cases/middleware/builtin/src/index.ts new file mode 100644 index 000000000..83ccf9d92 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/builtin/src/index.ts @@ -0,0 +1,16 @@ +// census: builtin_mw_import +// Built-in middleware are library calls: each is a middleware node at its registering expression. +import { Hono } from 'hono' +import { cors } from 'hono/cors' +import { logger } from 'hono/logger' +import { secureHeaders } from 'hono/secure-headers' +import { etag } from 'hono/etag' + +const app = new Hono() +app.use(logger()) +app.use('/api/*', cors()) +app.use('/api/*', secureHeaders(), etag()) +app.get('/api/data', (c) => c.json({})) +app.get('/', (c) => c.text('home')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/combine/src/index.ts b/graph/test/typescript/hono/cases/middleware/combine/src/index.ts new file mode 100644 index 000000000..8b58cb62b --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/combine/src/index.ts @@ -0,0 +1,19 @@ +// census: combine_every_some_except +// hono/combine: the combinator call is the middleware node at its registering expression. +import { Hono } from 'hono' +import { every, some, except } from 'hono/combine' +import type { MiddlewareHandler } from 'hono' + +const isAdmin: MiddlewareHandler = async (c, next) => { await next() } +const hasToken: MiddlewareHandler = async (c, next) => { await next() } +const isLocal: MiddlewareHandler = async (c, next) => { await next() } + +const app = new Hono() +app.use('/admin/*', every(isAdmin, hasToken)) +app.use('/ops/*', some(isLocal, hasToken)) +app.use('*', except('/health', hasToken)) +app.get('/admin/panel', (c) => c.text('panel')) +app.get('/ops/status', (c) => c.text('status')) +app.get('/health', (c) => c.text('ok')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/conditional-lib-mw/src/index.ts b/graph/test/typescript/hono/cases/middleware/conditional-lib-mw/src/index.ts new file mode 100644 index 000000000..20d8f2822 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/conditional-lib-mw/src/index.ts @@ -0,0 +1,16 @@ +// census: use_conditional_loop (stress-5 mutation m40 shape; violation hono-5-04) +// A library middleware registered in each branch of an if/else: both are on the chain with +// conditional_registration, each keyed at its own call. +import { Hono } from 'hono' +import { logger } from 'hono/logger' + +declare const process: { env: Record } + +const app = new Hono() +if (process.env.NODE_ENV !== 'production') { + app.use('*', logger()) +} else { + app.use('*', logger((m: string) => console.error(m))) +} +app.get('/x', (c) => c.text('x')) +export default app diff --git a/graph/test/typescript/hono/cases/middleware/const-library-mw/src/index.ts b/graph/test/typescript/hono/cases/middleware/const-library-mw/src/index.ts new file mode 100644 index 000000000..c77e4672a --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/const-library-mw/src/index.ts @@ -0,0 +1,35 @@ +// census: use_scoped (stress-4 mutation m31 shapes; violation hono-4-07, SPEC R10/R11) +// A library middleware bound to a const before use(): at module level, inside a builder, and inside a branch of a +// builder. Each is keyed at the jwt(...) call (library_call), never at the identifier. +import { Hono } from 'hono' +import { jwt } from 'hono/jwt' + +declare const process: { env: Record } + +const api = new Hono() +const apiJwt = jwt({ alg: 'HS256', secret: process.env.SECRET ?? 'dev' }) +api.use('/v1/*', apiJwt) +api.get('/v1/me', (c) => c.json({ me: true })) + +export function buildAdmin(secret: string) { + const admin = new Hono() + const adminJwt = jwt({ alg: 'HS256', secret }) + admin.use('/admin/*', adminJwt) + admin.get('/admin/users', (c) => c.json([])) + return admin +} + +export function buildPartner(cfg: { devMode: boolean; secret: string }) { + const partner = new Hono() + if (!cfg.devMode) { + const partnerJwt = jwt({ alg: 'HS256', secret: cfg.secret }) + partner.use('/partner/*', partnerJwt) + } + partner.get('/partner/orders', (c) => c.json([])) + return partner +} + +api.route('/', buildAdmin(process.env.ADMIN_SECRET ?? 'dev')) +api.route('/', buildPartner({ devMode: false, secret: 'p' })) + +export default api diff --git a/graph/test/typescript/hono/cases/middleware/detached-factory-method/src/factory.ts b/graph/test/typescript/hono/cases/middleware/detached-factory-method/src/factory.ts new file mode 100644 index 000000000..e220fdfbb --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/detached-factory-method/src/factory.ts @@ -0,0 +1,11 @@ +import { createFactory } from 'hono/factory' + +type AdminEnv = { Variables: { role: string } } + +export function createTierFactory() { + return createFactory() +} + +const adminFactory = createTierFactory() +export const createAdminMiddleware = adminFactory.createMiddleware +export const createAdminHandlers = adminFactory.createHandlers diff --git a/graph/test/typescript/hono/cases/middleware/detached-factory-method/src/index.ts b/graph/test/typescript/hono/cases/middleware/detached-factory-method/src/index.ts new file mode 100644 index 000000000..fe599d25d --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/detached-factory-method/src/index.ts @@ -0,0 +1,23 @@ +// census: create_middleware (oracle gap O17) +// `createMiddleware` / `createHandlers` read off a factory into consts and called later: the middleware is the +// function passed in, at its chain position; the handlers spread into the route in order. +import { Hono } from 'hono' +import { createAdminHandlers, createAdminMiddleware } from './factory' + +const requireAdmin = createAdminMiddleware(async (c, next) => { + c.set('role', 'admin') + await next() +}) + +const listUsers = createAdminHandlers( + async (_c, next) => { + await next() + }, + (c) => c.json({ role: c.get('role') }), +) + +const app = new Hono<{ Variables: { role: string } }>() +app.use('/admin/*', requireAdmin) +app.get('/admin/users', ...listUsers) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/factory-fn/src/index.ts b/graph/test/typescript/hono/cases/middleware/factory-fn/src/index.ts new file mode 100644 index 000000000..53ba22bc1 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/factory-fn/src/index.ts @@ -0,0 +1,10 @@ +// census: mw_factory_returning_fn, create_middleware +// A project middleware factory: the middleware is the INNER function the factory returns. +import { Hono } from 'hono' +import { rateLimit, requestId } from './mw' + +const app = new Hono() +app.use(requestId('req')) +app.get('/limited', rateLimit({ max: 10 }), (c) => c.text('ok')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/factory-fn/src/mw.ts b/graph/test/typescript/hono/cases/middleware/factory-fn/src/mw.ts new file mode 100644 index 000000000..371397e96 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/factory-fn/src/mw.ts @@ -0,0 +1,13 @@ +import type { MiddlewareHandler } from 'hono' + +export function rateLimit(opts: { max: number }): MiddlewareHandler { + return async (c, next) => { + if (opts.max < 0) return c.text('limited', 429) + await next() + } +} + +export const requestId = (prefix: string): MiddlewareHandler => async (c, next) => { + c.header('x-request-id', prefix) + await next() +} diff --git a/graph/test/typescript/hono/cases/middleware/factory-handlers/src/index.ts b/graph/test/typescript/hono/cases/middleware/factory-handlers/src/index.ts new file mode 100644 index 000000000..3d7a30fee --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/factory-handlers/src/index.ts @@ -0,0 +1,22 @@ +// census: create_factory, factory_create_app, create_middleware +// createFactory: initApp registrations run first on every app createApp makes; createHandlers spreads +// its arguments into the route (middleware then handler); factory.createMiddleware's arg is the middleware. +import { createFactory } from 'hono/factory' + +const factory = createFactory({ + initApp: (app) => { + app.use(async (c, next) => { + await next() + }) + }, +}) + +const check = factory.createMiddleware(async (c, next) => { + await next() +}) +const handlers = factory.createHandlers(check, (c) => c.text('from createHandlers')) + +const app = factory.createApp() +app.get('/factory', ...handlers) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/factory-two-returns/src/index.ts b/graph/test/typescript/hono/cases/middleware/factory-two-returns/src/index.ts new file mode 100644 index 000000000..7865f6fa3 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/factory-two-returns/src/index.ts @@ -0,0 +1,25 @@ +// census: use_scoped (violation hono-3-03, SPEC 3.1 "resolve the return; if several, all", R10) +// A middleware factory returns a pass-through arrow in one branch and a library middleware otherwise: both are +// middleware of the chain at the same position; the library one is keyed at its call, its options callback is no +// middleware. +import { Hono } from 'hono' +import type { Context, Next } from 'hono' +import { basicAuth } from 'hono/basic-auth' + +declare const process: { env: Record } + +export function guard(realm: string) { + if (process.env.NODE_ENV !== 'production') { + return async (_c: Context, next: Next) => { + await next() + } + } + return basicAuth({ realm, verifyUser: (user, pass) => user === 'admin' && pass === realm }) +} + +const app = new Hono() +app.use('/admin/*', guard('ops')) +app.get('/admin/stats', (c) => c.json({ n: 1 })) +app.get('/public', (c) => c.text('hi')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/helper-app-mounted-thrice/src/routes.test.ts b/graph/test/typescript/hono/cases/middleware/helper-app-mounted-thrice/src/routes.test.ts new file mode 100644 index 000000000..8bdcd849d --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/helper-app-mounted-thrice/src/routes.test.ts @@ -0,0 +1,40 @@ +// census: app_request_test (stress-4 mutation m28 shape; violation hono-4-02, SPEC R6) +// A test helper builds a fresh app per call and a second helper mounts the same routes into it; three tests do so. +// Each call is its own app: the chain is [use 0, param validator 1, json validator 2] on every one, never positions +// accumulated across calls. +import { Hono } from 'hono' +import { profileRoutes } from './routes' + +declare function it(name: string, fn: () => unknown): void + +function makeTestApp() { + const app = new Hono<{ Variables: { actor: string } }>() + app.use('*', async (c, next) => { + c.set('actor', 'tester') + await next() + }) + return { app } +} + +function mountRoutes(app: Hono<{ Variables: { actor: string } }>) { + app.route('/', profileRoutes) + return app +} + +it('renames one', async () => { + const { app } = makeTestApp() + mountRoutes(app) + await app.request('/1/rename', { method: 'POST' }) +}) + +it('renames two', async () => { + const { app } = makeTestApp() + mountRoutes(app) + await app.request('/2/rename', { method: 'POST' }) +}) + +it('renames three', async () => { + const { app } = makeTestApp() + mountRoutes(app) + await app.request('/3/rename', { method: 'POST' }) +}) diff --git a/graph/test/typescript/hono/cases/middleware/helper-app-mounted-thrice/src/routes.ts b/graph/test/typescript/hono/cases/middleware/helper-app-mounted-thrice/src/routes.ts new file mode 100644 index 000000000..0628204f8 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/helper-app-mounted-thrice/src/routes.ts @@ -0,0 +1,10 @@ +import { Hono } from 'hono' +import { zValidator } from '@hono/zod-validator' +import { z } from 'zod' + +export const profileRoutes = new Hono().post( + '/:id/rename', + zValidator('param', z.object({ id: z.string() })), + zValidator('json', z.object({ name: z.string() })), + (c) => c.json({ ok: true }), +) diff --git a/graph/test/typescript/hono/cases/middleware/if-else-unequal-mw/src/index.ts b/graph/test/typescript/hono/cases/middleware/if-else-unequal-mw/src/index.ts new file mode 100644 index 000000000..84857971e --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/if-else-unequal-mw/src/index.ts @@ -0,0 +1,32 @@ +// census: use_conditional_loop, use_scoped (SPEC R15 clause 2) +// An if/else whose branches register DIFFERENT numbers of middleware on the same app: the branch middleware start +// at the same position (one of the two runs), each conditional; every LATER position on a chain that holds them is +// decided at run time, so that chain is declared dynamic_middleware_list (positions after the branch continue after +// the LONGER branch). A chain with no middleware after the branch is not dynamic; a chain the branch does not reach +// is untouched. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +declare const process: { env: Record } + +const requestId: MiddlewareHandler = async (c, next) => { c.header('x-request-id', '1'); await next() } +const verifyToken: MiddlewareHandler = async (c, next) => { await next() } +const loadUser: MiddlewareHandler = async (c, next) => { await next() } +const apiKey: MiddlewareHandler = async (c, next) => { await next() } +const audit: MiddlewareHandler = async (c, next) => { await next() } + +const app = new Hono() +app.use('*', requestId) +if (process.env.AUTH_MODE === 'token') { + app.use('/api/*', verifyToken) + app.use('/api/*', loadUser) +} else { + app.use('/api/*', apiKey) +} +app.use('/api/items/*', audit) + +app.get('/health', (c) => c.text('ok')) +app.get('/api/ping', (c) => c.text('pong')) +app.get('/api/items/:id', (c) => c.json({ id: c.req.param('id') })) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/lib-mw-options/src/index.ts b/graph/test/typescript/hono/cases/middleware/lib-mw-options/src/index.ts new file mode 100644 index 000000000..a5fbf4d39 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/lib-mw-options/src/index.ts @@ -0,0 +1,18 @@ +// census: builtin_mw_import, mw_factory_returning_fn (violation hono-2-07, SPEC R10) +// A library middleware built with project callbacks in its options: the middleware is the library call; the +// callbacks are not middleware and have no chain position. +import { Hono } from 'hono' +import { cors } from 'hono/cors' +import { basicAuth } from 'hono/basic-auth' +import type { MiddlewareHandler } from 'hono' + +function guard(realm: string): MiddlewareHandler { + return basicAuth({ realm, verifyUser: (user, pass) => user === 'a' && pass === 'b' }) +} + +const app = new Hono() +app.use('*', cors({ origin: (origin) => (origin.endsWith('.example') ? origin : null) })) +app.use('*', guard('admin')) +app.get('/x', (c) => c.text('x')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/loop-use/src/index.ts b/graph/test/typescript/hono/cases/middleware/loop-use/src/index.ts new file mode 100644 index 000000000..f47c16406 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/loop-use/src/index.ts @@ -0,0 +1,26 @@ +// census: use_global, use_multi (violation hono-1-06, LOOP form; SPEC 3.3 dynamic chains) +// Middleware registered in a loop over a run-time list sits between two known middleware: positions are the +// ordinal among the known ones, and the chain is declared dynamic_middleware_list. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +export function build(config: { before?: MiddlewareHandler[] }) { + const app = new Hono() + app.use('*', async (c, next) => { + await next() + }) + if (config.before) { + for (const m of config.before) { + app.use('*', m) + } + } + app.use('*', async (c, next) => { + c.header('x-done', '1') + await next() + }) + app.get('/x', (c) => c.text('x')) + return app +} + +declare const loadedConfig: { before?: MiddlewareHandler[] } +export default build(loadedConfig) diff --git a/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/index.ts b/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/index.ts new file mode 100644 index 000000000..3b2e77ddc --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/index.ts @@ -0,0 +1,2 @@ +export { session } from './session' +export { scope } from './scope' diff --git a/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/scope.ts b/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/scope.ts new file mode 100644 index 000000000..4b050e1a2 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/scope.ts @@ -0,0 +1,9 @@ +import type { Context, Next } from 'hono' + +export function scope(name: string) { + const check = async (c: Context, next: Next) => { + if (!c.get('user')) return c.text('denied ' + name, 403) + await next() + } + return Object.assign(check, { scope: name }) +} diff --git a/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/session.ts b/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/session.ts new file mode 100644 index 000000000..76ecb34c1 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/guards/session.ts @@ -0,0 +1,7 @@ +import { createMiddleware } from 'hono/factory' + +export const session = (options?: { optional?: boolean }) => + createMiddleware(async (c, next) => { + c.set('user', { id: 'u1', optional: options?.optional ?? false }) + await next() + }) diff --git a/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/index.ts b/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/index.ts new file mode 100644 index 000000000..7229ea17f --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/mw-from-namespace-field/src/index.ts @@ -0,0 +1,31 @@ +// census: use_global (stress-4 mutation m32 shapes; violation hono-4-01) +// Project middleware factories reached three ways: destructured from a field holding a namespace import, destructured +// from the namespace at module level, and as namespace members. Each is the inner project function, never a library +// call. +import { Hono } from 'hono' +import * as guards from './guards' + +type Env = { Variables: { user: { id: string; optional: boolean } } } + +class Controller { + protected guards = guards + getController() { + const { session, scope } = this.guards + const hono = new Hono().use(session(), scope('read')) + hono.get('/info', (c) => c.json(c.get('user'))) + return hono + } +} + +const { session, scope } = guards +const reports = new Hono().use(session({ optional: true }), scope('reports')) +reports.get('/daily', (c) => c.json(c.get('user'))) + +const audit = new Hono().use(guards.session(), guards.scope('audit')) +audit.get('/log', (c) => c.json(c.get('user'))) + +const app = new Hono() +app.route('/api/system', new Controller().getController()) +app.route('/api/reports', reports) +app.route('/api/audit', audit) +export default app diff --git a/graph/test/typescript/hono/cases/middleware/partial-sub-app-chain/src/index.ts b/graph/test/typescript/hono/cases/middleware/partial-sub-app-chain/src/index.ts new file mode 100644 index 000000000..2df4520a9 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/partial-sub-app-chain/src/index.ts @@ -0,0 +1,55 @@ +// census: basepath (stress-4 mutation m34 shape; violation hono-4-03, SPEC 3.3 [iter2] partial routes) +// A controller with its own use(guard()) and a per-route middleware, mounted (through a getter) into a holder that a +// run-time basePath can rebase and that has a global use('*') and a scoped use('/api/*'). The route is partial; its +// decidable chain is [holder's use('*'), the controller's own use, the per-route middleware]; the scoped use is +// undecidable (partial_route_scope). +import { Hono } from 'hono' +import type { Context, Next } from 'hono' + +declare const process: { env: Record } + +const guard = () => async (_c: Context, next: Next) => { + await next() +} +const trace = async (_c: Context, next: Next) => { + await next() +} +const audit = async (_c: Context, next: Next) => { + await next() +} + +class Controller { + protected create(): Hono { + return new Hono() + } + getController(): Hono { + const hono = this.create().use(guard()) + hono.get('/config/raw', audit, (c) => c.text('raw')) + return hono + } +} + +class Holder { + server = new Hono() + private opts?: { prefix?: string } + constructor(opts?: { prefix?: string }) { + this.opts = opts + } + rebuild() { + this.server = new Hono() + if (this.opts?.prefix) { + this.server = this.server.basePath(this.opts.prefix) + } + this.server.use('*', trace) + this.server.use('/api/*', guard()) + } + get app() { + return this.server + } +} + +const h = new Holder({ prefix: process.env.PREFIX }) +h.rebuild() +h.app.route('/api/system', new Controller().getController()) + +export default h.server diff --git a/graph/test/typescript/hono/cases/middleware/runtime-list-except-ternary/src/index.ts b/graph/test/typescript/hono/cases/middleware/runtime-list-except-ternary/src/index.ts new file mode 100644 index 000000000..d18ca9036 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/runtime-list-except-ternary/src/index.ts @@ -0,0 +1,42 @@ +// census: use_conditional_loop, combine_except (stress-6 oracle note; SPEC 3.3 dynamic chains, SPEC 3.1 except) +// Middleware come from a list chosen at run time (the tier's own, or one looked up in a registry, or none); each is registered as itself or wrapped in except(..) with a +// run-time exclusion list. Which middleware run, in which order, is decided at run time: every chain on the app is +// declared dynamic_middleware_list. Each tier's base path is a run-time value, so its routes are partial +// (dynamic_mount_prefix) and the dynamic chain is declared on the partial route too (its decidable chain keeps the +// tier's own first middleware). The except(..) call is a library middleware node at its call; its excluded +// paths are not literal, so its scope is also declared (dynamic_except). +import { Hono } from 'hono' +import { except } from 'hono/combine' +import type { MiddlewareHandler, Context } from 'hono' + +type Guarded = { handler: MiddlewareHandler; except: (c: Context) => boolean } +type Entry = MiddlewareHandler | Guarded + +const isGuarded = (m: Entry): m is Guarded => typeof m === 'object' && 'except' in m + +// the tier's own list when it declares one, else one looked up by name in a run-time registry, else none +function tierEntries(tier: { name: string; entries?: Entry[] }, registry: Record): Entry[] { + if (tier.entries) return tier.entries + const key = Object.keys(registry).find((k) => k.includes(`/${tier.name}/`)) + return key ? registry[key].default : [] +} + +type Tier = { name: string; basePath: string; entries?: Entry[] } + +export function buildTier(tier: Tier, registry: Record) { + const app = new Hono().basePath(tier.basePath) + app.use('/*', async (c, next) => { c.header('x-tier', '1'); await next() }) + for (const mw of tierEntries(tier, registry)) { + app.use('/*', isGuarded(mw) ? except(mw.except, mw.handler) : mw) + } + app.get('/status', (c) => c.text('ok')) + return app +} + +declare const registry: Record +declare const tiers: Tier[] + +const root = new Hono() +root.get('/', (c) => c.text('root')) +for (const tier of tiers) root.route('/', buildTier(tier, registry)) +export default root diff --git a/graph/test/typescript/hono/cases/middleware/scoped-use/src/index.ts b/graph/test/typescript/hono/cases/middleware/scoped-use/src/index.ts new file mode 100644 index 000000000..9f1b8686c --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/scoped-use/src/index.ts @@ -0,0 +1,22 @@ +// census: use_scoped, path_wildcard +// Path-scoped middleware applies to the routes its pattern matches; a param segment in the scope matches any segment. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +const adminOnly: MiddlewareHandler = async (c, next) => { + await next() +} +const perOrg: MiddlewareHandler = async (c, next) => { + await next() +} + +const app = new Hono() +app.use('/admin/*', adminOnly) +app.use('/orgs/:org/*', perOrg) + +app.get('/admin/users', (c) => c.text('admin')) +app.get('/admin/users/:id', (c) => c.text('admin one')) +app.get('/orgs/:org/billing', (c) => c.text('billing')) +app.get('/public', (c) => c.text('public')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/spread-after-mount/src/index.ts b/graph/test/typescript/hono/cases/middleware/spread-after-mount/src/index.ts new file mode 100644 index 000000000..384c7004f --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/spread-after-mount/src/index.ts @@ -0,0 +1,19 @@ +// census: use_multi, route_mount_literal +// A const middleware array spread into use(); a use() AFTER mounting a sub-app does not reach the +// sub-app's routes, which were copied before it. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +const a: MiddlewareHandler = async (c, next) => { await next() } +const b: MiddlewareHandler = async (c, next) => { await next() } +const protectedMiddleware = [a, b] + +const sub = new Hono() +sub.get('/inside', (c) => c.text('mounted before the use')) + +const routers = new Hono() +routers.route('/sub', sub) +routers.use(...protectedMiddleware) +routers.get('/after', (c) => c.text('gets a then b')) + +export default routers diff --git a/graph/test/typescript/hono/cases/middleware/spread-call-result/src/index.ts b/graph/test/typescript/hono/cases/middleware/spread-call-result/src/index.ts new file mode 100644 index 000000000..b2acc6776 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/spread-call-result/src/index.ts @@ -0,0 +1,29 @@ +// census: use_multi, mw_factory_returning_fn (violation hono-1-06) +// A spread of a project function's returned array literal is seen through: the chain is +// [limiter's inner fn (1), limiter's inner fn (2), after] before the handler, positions exact. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +function limiter(n: number): MiddlewareHandler { + return async (c, next) => { + if (n < 0) return c.text('limited', 429) + await next() + } +} +function createLimits() { + const a = limiter(1) + const b = limiter(2) + return [a, b] +} + +const limits = createLimits() +const after: MiddlewareHandler = async (c, next) => { + await next() +} + +const app = new Hono() +app.use('*', ...limits) +app.use('*', after) +app.get('/x', (c) => c.text('x')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/sub-app-middleware/src/index.ts b/graph/test/typescript/hono/cases/middleware/sub-app-middleware/src/index.ts new file mode 100644 index 000000000..d817dd76f --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/sub-app-middleware/src/index.ts @@ -0,0 +1,18 @@ +// census: use_global, route_mount_literal +// Middleware registered on a sub-app is mounted with it: scoped to the prefix, in its relative order. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +const outer: MiddlewareHandler = async (c, next) => { await next() } +const inner: MiddlewareHandler = async (c, next) => { await next() } + +const users = new Hono() +users.use(inner) +users.get('/', (c) => c.text('users')) + +const app = new Hono() +app.use(outer) +app.route('/users', users) +app.get('/other', (c) => c.text('outer only')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/use-order/src/index.ts b/graph/test/typescript/hono/cases/middleware/use-order/src/index.ts new file mode 100644 index 000000000..acdee94b4 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/use-order/src/index.ts @@ -0,0 +1,20 @@ +// census: use_global, use_scoped, await_next +// Registration ORDER decides: routes registered before a later use('/*', auth) do NOT get auth. +import { Hono } from 'hono' +import { createMiddleware } from 'hono/factory' + +const timing = createMiddleware(async (c, next) => { + await next() +}) +const auth = createMiddleware(async (c, next) => { + await next() +}) + +const app = new Hono() +app.use(timing) +app.get('/public', (c) => c.text('timing only')) +app.use('/*', auth) +app.get('/private', (c) => c.text('timing then auth')) +app.post('/private', (c) => c.text('timing then auth too')) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/validator/src/index.ts b/graph/test/typescript/hono/cases/middleware/validator/src/index.ts new file mode 100644 index 000000000..2dde9f1ad --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/validator/src/index.ts @@ -0,0 +1,18 @@ +// census: zvalidator, validator_builtin, req_valid +// A validator argument is per-route middleware for that route. +import { Hono } from 'hono' +import { validator } from 'hono/validator' +import { zValidator } from '@hono/zod-validator' +import { z } from 'zod' + +const NewPost = z.object({ title: z.string() }) + +const app = new Hono() +app.post('/posts', zValidator('json', NewPost), (c) => c.json(c.req.valid('json'), 201)) +app.get( + '/search', + validator('query', (value) => value), + (c) => c.text('search') +) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/verb-next/src/index.ts b/graph/test/typescript/hono/cases/middleware/verb-next/src/index.ts new file mode 100644 index 000000000..f5398f1bc --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/verb-next/src/index.ts @@ -0,0 +1,11 @@ +// census: await_next, verb_literal_path +// A verb registration whose function takes next and calls it is middleware for later matching routes. +import { Hono } from 'hono' + +const app = new Hono() +app.all('*', async (c, next) => { + await next() +}) +app.get('/x', (c) => c.text('x')) + +export default app diff --git a/graph/test/typescript/hono/cases/negative/context-lookalike/src/index.ts b/graph/test/typescript/hono/cases/negative/context-lookalike/src/index.ts new file mode 100644 index 000000000..d100a50e8 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/context-lookalike/src/index.ts @@ -0,0 +1,18 @@ +// census: ctx_get (partition outside Hono contexts) (SPEC N5) +// set/get on a Map called `c` is not a context key flow, even inside a file with a real app. +import { Hono } from 'hono' + +function remember(c: Map) { + c.set('user', 'u1') +} +function recall(c: Map) { + return c.get('user') +} +const store = new Map() +remember(store) +recall(store) + +const app = new Hono() +app.get('/me', (c) => c.text('me')) + +export default app diff --git a/graph/test/typescript/hono/cases/negative/foreign-fetch/src/index.ts b/graph/test/typescript/hono/cases/negative/foreign-fetch/src/index.ts new file mode 100644 index 000000000..42b2a56c3 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/foreign-fetch/src/index.ts @@ -0,0 +1,16 @@ +// census: neg_verb_literal_nonhono (violation hono-2-12) +// fetch to another host's /health is not a request to this app's GET /health. +import { Hono } from 'hono' + +class ExternalApi { + #baseUrl = 'https://api.example.com' + async ping() { + return fetch(`${this.#baseUrl}/health`) + } +} +export const external = new ExternalApi() + +const app = new Hono() +app.get('/health', (c) => c.text('ok')) + +export default app diff --git a/graph/test/typescript/hono/cases/negative/http-client/src/client.ts b/graph/test/typescript/hono/cases/negative/http-client/src/client.ts new file mode 100644 index 000000000..a081eeff5 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/http-client/src/client.ts @@ -0,0 +1,13 @@ +// census: neg_verb_literal_nonhono (SPEC N2) +// An HTTP client spelled like a router: verbs with literal paths and callbacks. Not a Hono app. +type Cb = (body: unknown) => void + +export const api = { + get(url: string, cb?: Cb) { cb?.(url) }, + post(url: string, body: unknown, cb?: Cb) { cb?.(body) }, +} + +export function loadUsers() { + api.get('/users', (body) => { console.log(body) }) + api.post('/users', { name: 'x' }, (res) => { console.log(res) }) +} diff --git a/graph/test/typescript/hono/cases/negative/http-client/src/index.ts b/graph/test/typescript/hono/cases/negative/http-client/src/index.ts new file mode 100644 index 000000000..084ce3b07 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/http-client/src/index.ts @@ -0,0 +1,7 @@ +// census: neg_verb_literal_nonhono (SPEC N2): the one real app beside it +import { Hono } from 'hono' + +const app = new Hono() +app.get('/only', (c) => c.text('the only route')) + +export default app diff --git a/graph/test/typescript/hono/cases/negative/local-hono-class/src/index.ts b/graph/test/typescript/hono/cases/negative/local-hono-class/src/index.ts new file mode 100644 index 000000000..39a6b5bc1 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/local-hono-class/src/index.ts @@ -0,0 +1,19 @@ +// census: neg_route_on_nonhono (SPEC N4) +// A project class that happens to be called Hono, and .route() on a workflow builder: neither is an app. +class Hono { + get(path: string, h: () => string) { return this } + use(h: () => void) { return this } + route(path: string, other: Hono) { return this } +} + +const fake = new Hono() +fake.use(() => {}) +fake.get('/fake', () => 'not a route') +fake.route('/nested', new Hono()) + +const workflow = { + route(name: string, step: () => void) { step(); return this }, +} +workflow.route('/step-one', () => {}) + +export default fake diff --git a/graph/test/typescript/hono/cases/negative/map-get/src/index.ts b/graph/test/typescript/hono/cases/negative/map-get/src/index.ts new file mode 100644 index 000000000..9c13cc759 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/map-get/src/index.ts @@ -0,0 +1,22 @@ +// census: neg_map_get_in_hono_file (SPEC N1) +// get/set/use on Maps, a KV-like object and Headers, in the same file as a real app: only the app routes. +import { Hono } from 'hono' + +const cache = new Map() +cache.set('/users', 'cached') +cache.get('/users') + +const kv = { + get(key: string, onMiss: () => string) { return onMiss() }, + use(fn: () => void) { fn() }, +} +kv.get('/kv/key', () => 'computed on miss') +kv.use(() => {}) + +const headers = new Headers() +headers.get('accept') + +const app = new Hono() +app.get('/real', (c) => c.text(cache.get('/users') ?? '')) + +export default app diff --git a/graph/test/typescript/hono/cases/negative/other-router/src/index.ts b/graph/test/typescript/hono/cases/negative/other-router/src/index.ts new file mode 100644 index 000000000..e2ade76e7 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/other-router/src/index.ts @@ -0,0 +1,16 @@ +// census: neg_other_router_import (SPEC N3) +// Another router in the same project: none of its registrations is a Hono route or middleware. +import { Hono } from 'hono' +import { Router } from './minirouter' + +const legacy = new Router() +legacy.use((req, res) => {}) +legacy.get('/legacy/users', (req, res) => {}) +legacy.post('/legacy/users', (req, res) => {}) +const outer = new Router() +outer.route('/v0', legacy) + +const app = new Hono() +app.get('/hono', (c) => c.text('hono')) + +export default app diff --git a/graph/test/typescript/hono/cases/negative/other-router/src/minirouter.ts b/graph/test/typescript/hono/cases/negative/other-router/src/minirouter.ts new file mode 100644 index 000000000..10ad2ff15 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/other-router/src/minirouter.ts @@ -0,0 +1,8 @@ +// A router that is not Hono: same method names, same call shapes. +type H = (req: unknown, res: unknown) => void +export class Router { + get(path: string, h: H) { return this } + post(path: string, h: H) { return this } + use(pathOrH: string | H, h?: H) { return this } + route(path: string, r: Router) { return this } +} diff --git a/graph/test/typescript/hono/cases/negative/request-lookalike/src/index.ts b/graph/test/typescript/hono/cases/negative/request-lookalike/src/index.ts new file mode 100644 index 000000000..dd1bd478c --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/request-lookalike/src/index.ts @@ -0,0 +1,18 @@ +// census: app_request_test (partition where the receiver is not an app) (SPEC N7, N8) +// `.request()` on a test agent and a project function named hc: no remote edge to the route. +import { Hono } from 'hono' + +const app = new Hono() +app.get('/real', (c) => c.text('real')) + +const agent = { request(path: string) { return path } } +export function hitsWithAgent() { + return agent.request('/real') +} + +function hc(base: string): any { return {} } +export function callsLocalHc() { + return hc('/').real.$get() +} + +export default app diff --git a/graph/test/typescript/hono/cases/negative/zod-openapi-meta/src/index.ts b/graph/test/typescript/hono/cases/negative/zod-openapi-meta/src/index.ts new file mode 100644 index 000000000..20d16a882 --- /dev/null +++ b/graph/test/typescript/hono/cases/negative/zod-openapi-meta/src/index.ts @@ -0,0 +1,20 @@ +// census: openapi_register, apptype_typeof (SPEC N6, N9) +// zod .openapi('Name') / .openapi({...}) metadata calls are not registrations, and a type alias of a +// createRoute value is not an RPC root. The one real registration is app.openapi. +import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi' + +const User = z.object({ id: z.string().openapi({ example: '1' }) }).openapi('User') +const Params = z.object({ id: z.string() }).openapi('Params') + +const getUser = createRoute({ + method: 'get', + path: '/users/{id}', + request: { params: Params }, + responses: { 200: { description: 'a user', content: { 'application/json': { schema: User } } } }, +}) +export type GetUserRoute = typeof getUser + +const app = new OpenAPIHono() +app.openapi(getUser, (c) => c.json({ id: c.req.valid('param').id }, 200)) + +export default app diff --git a/graph/test/typescript/hono/cases/openapi/create-route/src/index.ts b/graph/test/typescript/hono/cases/openapi/create-route/src/index.ts new file mode 100644 index 000000000..cf5d4abf1 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/create-route/src/index.ts @@ -0,0 +1,20 @@ +// census: create_route, openapi_register, new_openapihono +// Route objects declared in another module; {id} registers as :id; the THIRD argument is a validation +// hook, not the handler. +import { OpenAPIHono } from '@hono/zod-openapi' +import { listTasks, getTask } from './routes' + +const app = new OpenAPIHono() + +app.openapi(listTasks, (c) => c.json([], 200)) +app.openapi( + getTask, + (c) => c.json({ id: c.req.valid('param').id, done: false }, 200), + (result, c) => { + if (!result.success) console.log('rejected', c.req.path) + return undefined + } +) +app.doc('/doc', { openapi: '3.0.0', info: { title: 'tasks', version: '1' } }) + +export default app diff --git a/graph/test/typescript/hono/cases/openapi/create-route/src/routes.ts b/graph/test/typescript/hono/cases/openapi/create-route/src/routes.ts new file mode 100644 index 000000000..3016c4b72 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/create-route/src/routes.ts @@ -0,0 +1,16 @@ +import { createRoute, z } from '@hono/zod-openapi' + +export const Task = z.object({ id: z.string(), done: z.boolean() }).openapi('Task') + +export const listTasks = createRoute({ + method: 'get', + path: '/tasks', + responses: { 200: { description: 'tasks', content: { 'application/json': { schema: z.array(Task) } } } }, +}) + +export const getTask = createRoute({ + method: 'get', + path: '/tasks/{id}', + request: { params: z.object({ id: z.string() }) }, + responses: { 200: { description: 'task', content: { 'application/json': { schema: Task } } } }, +}) diff --git a/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/auth.handlers.ts b/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/auth.handlers.ts new file mode 100644 index 000000000..4fff532a5 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/auth.handlers.ts @@ -0,0 +1,3 @@ +import type { Context } from 'hono' + +export const login = async (c: Context) => c.json({ ok: true }, 200) diff --git a/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/auth.routes.ts b/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/auth.routes.ts new file mode 100644 index 000000000..43e05c0c0 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/auth.routes.ts @@ -0,0 +1,3 @@ +import { createRoute } from '@hono/zod-openapi' + +export const login = createRoute({ path: `${'/auth'}/login`, method: 'post', responses: { 200: { description: 'ok' } } }) diff --git a/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/index.ts b/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/index.ts new file mode 100644 index 000000000..b50d5fcb5 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/namespace-handlers/src/index.ts @@ -0,0 +1,11 @@ +// census: openapi_register, create_route (violation hono-1-04) +// The handler is a member of a namespace import (`handlers.login`), the route object a named import whose +// path is a template over a literal. +import { OpenAPIHono } from '@hono/zod-openapi' +import * as handlers from './auth.handlers' +import { login } from './auth.routes' + +const auth = new OpenAPIHono() +auth.openapi(login, handlers.login) + +export default auth diff --git a/graph/test/typescript/hono/cases/openapi/response-helper/src/index.ts b/graph/test/typescript/hono/cases/openapi/response-helper/src/index.ts new file mode 100644 index 000000000..a7e40a117 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/response-helper/src/index.ts @@ -0,0 +1,42 @@ +// census: create_route (violation hono-3-04, SPEC 3.9) +// createRoute responses written through a project helper that wraps the schema ({ content: { json: { schema } } }): +// the response schema of each route is the helper's ARGUMENT, as for an inline response. +import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi' + +const jsonBody = (schema: T, description: string) => ({ + content: { 'application/json': { schema } }, + description, +}) +const jsonBodyRequired = (schema: T, description: string) => ({ + ...jsonBody(schema, description), + required: true, +}) + +const Account = z.object({ id: z.string(), email: z.string() }) +const ErrorBody = z.object({ message: z.string() }) +const AccountList = z.array(Account) + +const getAccount = createRoute({ + method: 'get', + path: '/accounts/{id}', + request: { params: z.object({ id: z.string() }) }, + responses: { 200: jsonBody(Account, 'the account'), 404: jsonBody(ErrorBody, 'not found') }, +}) +const listAccounts = createRoute({ + method: 'get', + path: '/accounts', + responses: { 200: { content: { 'application/json': { schema: AccountList } }, description: 'all' } }, +}) +const createAccount = createRoute({ + method: 'post', + path: '/accounts', + request: { body: jsonBodyRequired(Account, 'the new account') }, + responses: { 201: jsonBody(Account, 'created') }, +}) + +const app = new OpenAPIHono() +app.openapi(getAccount, (c) => c.json({ id: c.req.valid('param').id, email: 'a@b' }, 200)) +app.openapi(listAccounts, (c) => c.json([], 200)) +app.openapi(createAccount, (c) => c.json(c.req.valid('json'), 201)) + +export default app diff --git a/graph/test/typescript/hono/cases/openapi/route-middleware/src/index.ts b/graph/test/typescript/hono/cases/openapi/route-middleware/src/index.ts new file mode 100644 index 000000000..582ef78d6 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/route-middleware/src/index.ts @@ -0,0 +1,21 @@ +// census: create_route, openapi_register, use_global +// routeDef.middleware runs before the validators and the handler; app-level use before that. +import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi' +import type { MiddlewareHandler } from 'hono' + +const audit: MiddlewareHandler = async (c, next) => { await next() } +const guard: MiddlewareHandler = async (c, next) => { await next() } + +const create = createRoute({ + method: 'post', + path: '/notes', + middleware: [guard] as const, + request: { body: { content: { 'application/json': { schema: z.object({ text: z.string() }) } } } }, + responses: { 201: { description: 'created' } }, +}) + +const app = new OpenAPIHono() +app.use(audit) +app.openapi(create, (c) => c.body(null, 201)) + +export default app diff --git a/graph/test/typescript/hono/cases/openapi/spread-error-responses/src/errors.ts b/graph/test/typescript/hono/cases/openapi/spread-error-responses/src/errors.ts new file mode 100644 index 000000000..8d5d7d4ef --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/spread-error-responses/src/errors.ts @@ -0,0 +1,14 @@ +import { z } from 'zod' + +const errorBody = (code: string) => z.object({ code: z.literal(code), message: z.string() }) + +export const errorResponses = { + 400: { + description: 'bad request', + content: { 'application/json': { schema: errorBody('BAD_REQUEST') } }, + }, + 403: { + description: 'forbidden', + content: { 'application/json': { schema: errorBody('FORBIDDEN') } }, + }, +} diff --git a/graph/test/typescript/hono/cases/openapi/spread-error-responses/src/index.ts b/graph/test/typescript/hono/cases/openapi/spread-error-responses/src/index.ts new file mode 100644 index 000000000..4ded04ef3 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/spread-error-responses/src/index.ts @@ -0,0 +1,21 @@ +// census: create_route (stress-4 mutation m27 shape; violation hono-4-08, oracle gap O19) +// Error responses spread from a module-level object: each slot is the call `errorBody('...')` (named once, at the +// call), never the helper declaration as well. +import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi' +import { errorResponses } from './errors' + +const Secret = z.object({ id: z.string() }) + +const getSecret = createRoute({ + method: 'get', + path: '/secrets/{id}', + request: { params: z.object({ id: z.string() }) }, + responses: { + 200: { description: 'the secret', content: { 'application/json': { schema: Secret } } }, + ...errorResponses, + }, +}) + +const app = new OpenAPIHono() +app.openapi(getSecret, (c) => c.json({ id: c.req.valid('param').id }, 200)) +export default app diff --git a/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/handlers.ts b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/handlers.ts new file mode 100644 index 000000000..0a780d22a --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/handlers.ts @@ -0,0 +1,4 @@ +import type { Context } from 'hono' + +export const getTicket = async (c: Context) => c.json({ id: String(c.req.param('id')), title: 't' }, 200) +export const createTicket = async (c: Context) => c.json({ id: '2', title: 'new' }, 201) diff --git a/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/index.ts b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/index.ts new file mode 100644 index 000000000..f775dd8c7 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/index.ts @@ -0,0 +1,2 @@ +export { default as jsonBody } from './json-body' +export { default as jsonBodyRequired } from './json-body-required' diff --git a/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/json-body-required.ts b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/json-body-required.ts new file mode 100644 index 000000000..a21d77963 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/json-body-required.ts @@ -0,0 +1,10 @@ +import jsonBody from './json-body' + +const jsonBodyRequired = (schema: T, description: string) => { + return { + ...jsonBody(schema, description), + required: true, + } +} + +export default jsonBodyRequired diff --git a/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/json-body.ts b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/json-body.ts new file mode 100644 index 000000000..cbe3f4406 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/helpers/json-body.ts @@ -0,0 +1,12 @@ +const jsonBody = (schema: T, description: string) => { + return { + content: { + 'application/json': { + schema, + }, + }, + description, + } +} + +export default jsonBody diff --git a/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/index.ts b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/index.ts new file mode 100644 index 000000000..00a820eeb --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/index.ts @@ -0,0 +1,12 @@ +// census: create_route (stress-4 mutation m26 shape; violation hono-3-04 reopened) +// Responses keyed by a namespace constant (`[Status.OK]`) and written through helpers re-exported as named +// defaults from a barrel; routes and handlers come from namespace imports. Every slot names the helper's argument. +import { OpenAPIHono } from '@hono/zod-openapi' +import * as routes from './routes' +import * as handlers from './handlers' + +const app = new OpenAPIHono() + .openapi(routes.getTicket, handlers.getTicket) + .openapi(routes.createTicket, handlers.createTicket) + +export default app diff --git a/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/routes.ts b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/routes.ts new file mode 100644 index 000000000..50a8ab96b --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/routes.ts @@ -0,0 +1,26 @@ +import { createRoute, z } from '@hono/zod-openapi' +import * as Status from './status' +import { jsonBody, jsonBodyRequired } from './helpers' + +const Ticket = z.object({ id: z.string(), title: z.string() }) +const Missing = z.object({ message: z.string() }) +const TicketInput = z.object({ title: z.string() }) + +export const getTicket = createRoute({ + path: '/tickets/{id}', + method: 'get', + request: { params: z.object({ id: z.string() }) }, + responses: { + [Status.OK]: jsonBody(Ticket, 'the ticket'), + [Status.NOT_FOUND]: jsonBody(Missing, 'no such ticket'), + }, +}) + +export const createTicket = createRoute({ + path: '/tickets', + method: 'post', + request: { body: jsonBodyRequired(TicketInput, 'a new ticket') }, + responses: { + [Status.CREATED]: jsonBody(Ticket, 'created'), + }, +}) diff --git a/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/status.ts b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/status.ts new file mode 100644 index 000000000..3ed237128 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/status-key-helpers/src/status.ts @@ -0,0 +1,3 @@ +export const OK = 200 +export const CREATED = 201 +export const NOT_FOUND = 404 diff --git a/graph/test/typescript/hono/cases/routing/all-and-any/src/index.ts b/graph/test/typescript/hono/cases/routing/all-and-any/src/index.ts new file mode 100644 index 000000000..d28b63ced --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/all-and-any/src/index.ts @@ -0,0 +1,10 @@ +// census: verb_literal_path, path_wildcard +// `all` is verb ANY; an `all('*')` handler that never calls next is a handler, not middleware. +import { Hono } from 'hono' + +const app = new Hono() + +app.all('/proxy/*', (c) => c.text('proxied')) +app.get('/proxy/health', (c) => c.text('shadowed but registered')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/conditional-loop/src/index.ts b/graph/test/typescript/hono/cases/routing/conditional-loop/src/index.ts new file mode 100644 index 000000000..794efc742 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/conditional-loop/src/index.ts @@ -0,0 +1,20 @@ +// census: verb_literal_path (registrations behind a condition / in a loop, SPEC 3.1) +// Still emitted, each with a declared conditional_registration / loop_registration. +import { Hono } from 'hono' + +const app = new Hono() +const debug = Date.now() < 0 + +if (debug) { + app.get('/debug', (c) => c.text('debug only')) +} else { + app.get('/prod', (c) => c.text('prod only')) +} + +for (const name of ['a', 'b']) { + app.get('/loop', (c) => c.text('registered per iteration ' + name)) +} + +app.get('/always', (c) => c.text('always')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/const-paths/src/index.ts b/graph/test/typescript/hono/cases/routing/const-paths/src/index.ts new file mode 100644 index 000000000..5e4fe3948 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/const-paths/src/index.ts @@ -0,0 +1,14 @@ +// census: verb_const_path, verb_template_path +// A path built from consts (local, imported, a const-object member, a template over consts) is decidable. +import { Hono } from 'hono' +import { USERS, PATHS } from './paths' + +const ID = ':id' +const app = new Hono() + +app.get(USERS, (c) => c.text('users')) +app.get(`${USERS}/${ID}`, (c) => c.text('one user')) +app.get(PATHS.health, (c) => c.text('ok')) +app.get('/api' + PATHS.version, (c) => c.text('1')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/const-paths/src/paths.ts b/graph/test/typescript/hono/cases/routing/const-paths/src/paths.ts new file mode 100644 index 000000000..c3e291119 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/const-paths/src/paths.ts @@ -0,0 +1,2 @@ +export const USERS = '/users' +export const PATHS = { health: '/health', version: '/version' } as const diff --git a/graph/test/typescript/hono/cases/routing/dynamic-path/src/index.ts b/graph/test/typescript/hono/cases/routing/dynamic-path/src/index.ts new file mode 100644 index 000000000..91dd61da2 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/dynamic-path/src/index.ts @@ -0,0 +1,11 @@ +// census: verb_template_path (non-decidable partition) +// A path from a run-time value is declared undetermined, never guessed and never dropped. +import { Hono } from 'hono' + +const app = new Hono() +const version = String(Date.now() % 2) + +app.get(`/v${version}/status`, (c) => c.text('status')) +app.get('/fixed', (c) => c.text('fixed')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/every-verb/src/index.ts b/graph/test/typescript/hono/cases/routing/every-verb/src/index.ts new file mode 100644 index 000000000..8b8ed4a0d --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/every-verb/src/index.ts @@ -0,0 +1,20 @@ +// census: verb_literal_path, verb_chained, on_method, export_default_app +// One app, every verb Hono generates, `all`, a custom method through `on`, and a chain. +import { Hono } from 'hono' + +const app = new Hono() + +app.get('/items', (c) => c.json([])) +app.post('/items', (c) => c.json({ created: true }, 201)) +app.put('/items/:id', (c) => c.text('replaced')) +app.delete('/items/:id', (c) => c.body(null, 204)) +app.patch('/items/:id', (c) => c.text('patched')) +app.options('/items', (c) => c.body(null, 204)) +app.all('/health', (c) => c.text('ok')) +app.on('PURGE', '/cache', (c) => c.text('purged')) + +app + .get('/chain', (c) => c.text('get')) + .post((c) => c.text('post on the same path')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/library-handler/src/index.ts b/graph/test/typescript/hono/cases/routing/library-handler/src/index.ts new file mode 100644 index 000000000..96b0a0d03 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/library-handler/src/index.ts @@ -0,0 +1,14 @@ +// census: verb_literal_path (violation hono-1-03; SPEC R5) +// A route whose handler is a library call result: a route keyed at the call, never dropped. +import { Hono } from 'hono' +import { serveStatic } from '@hono/node-server/serve-static' +import { serveStatic as serveStaticAlt } from 'hono/serve-static' + +const app = new Hono() +app.get('/favicon.ico', serveStatic({ path: './favicon.ico' })) +app.all( + '/assets/*', + serveStaticAlt({ root: './public', getContent: async () => null }) +) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/mts-source/src/server.mts b/graph/test/typescript/hono/cases/routing/mts-source/src/server.mts new file mode 100644 index 000000000..69f09255f --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/mts-source/src/server.mts @@ -0,0 +1,7 @@ +// census: new_hono, verb_literal_path (an .mts module) +import { Hono } from 'hono' + +export const app = new Hono() + +app.get('/mts', (c) => c.text('from an mts module')) +app.post('/mts', (c) => c.text('posted')) diff --git a/graph/test/typescript/hono/cases/routing/multi-handler/src/index.ts b/graph/test/typescript/hono/cases/routing/multi-handler/src/index.ts new file mode 100644 index 000000000..e13950c79 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/multi-handler/src/index.ts @@ -0,0 +1,23 @@ +// census: verb_multi_handler, await_next +// Every argument but the last is per-route middleware; the last is the route handler. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +const requireJson: MiddlewareHandler = async (c, next) => { + await next() +} +const audit: MiddlewareHandler = async (c, next) => { + await next() +} + +function createOrder(c: any) { + return c.json({ ok: true }, 201) +} + +const app = new Hono() + +app.post('/orders', requireJson, audit, createOrder) +app.get('/orders', audit, (c) => c.json([])) +app.get('/plain', (c) => c.text('no middleware')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/named-handlers/src/handlers.ts b/graph/test/typescript/hono/cases/routing/named-handlers/src/handlers.ts new file mode 100644 index 000000000..1cfd61a7d --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/named-handlers/src/handlers.ts @@ -0,0 +1,11 @@ +import type { Context } from 'hono' + +export function listProjects(c: Context) { + return c.json([]) +} + +export const getProject = (c: Context) => c.json({ id: c.req.param('id') }) + +export default async function deleteProject(c: Context) { + return c.body(null, 204) +} diff --git a/graph/test/typescript/hono/cases/routing/named-handlers/src/index.ts b/graph/test/typescript/hono/cases/routing/named-handlers/src/index.ts new file mode 100644 index 000000000..288f5b25f --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/named-handlers/src/index.ts @@ -0,0 +1,12 @@ +// census: verb_literal_path, export_default_app +// Handlers declared elsewhere (function declaration, const arrow, default export) are the route handlers. +import { Hono } from 'hono' +import deleteProject, { listProjects, getProject } from './handlers' + +const app = new Hono() + +app.get('/projects', listProjects) +app.get('/projects/:id', getProject) +app.delete('/projects/:id', deleteProject) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/on-arrays/src/index.ts b/graph/test/typescript/hono/cases/routing/on-arrays/src/index.ts new file mode 100644 index 000000000..719675be8 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/on-arrays/src/index.ts @@ -0,0 +1,11 @@ +// census: on_method_array, on_method +// `on` with a verb list and a path list registers the cross product; a single custom verb too. +import { Hono } from 'hono' + +const app = new Hono() + +app.on(['GET', 'POST'], ['/form', '/form/legacy'], (c) => c.text('form')) +app.on('PURGE', '/cache/:key', (c) => c.text('purged')) +app.on(['put'], '/lower', (c) => c.text('upper-cased verb')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/partial-route-scope/src/index.ts b/graph/test/typescript/hono/cases/routing/partial-route-scope/src/index.ts new file mode 100644 index 000000000..00eb7ca5f --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/partial-route-scope/src/index.ts @@ -0,0 +1,16 @@ +// census: use_scoped, route_mount_nonliteral (SPEC 3.3 [iter2] partial_route_scope) +// A scoped use cannot be decided against routes mounted under an unknown prefix: declared, never "no middleware". +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +const versioned: MiddlewareHandler = async (c, next) => { await next() } + +const items = new Hono() +items.get('/list', (c) => c.json([])) + +const VERSION = String(Date.now() % 3) +const app = new Hono() +app.use('/v1/*', versioned) +app.route(`/v${VERSION}`, items) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/path-shapes/src/index.ts b/graph/test/typescript/hono/cases/routing/path-shapes/src/index.ts new file mode 100644 index 000000000..74448a6cd --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/path-shapes/src/index.ts @@ -0,0 +1,16 @@ +// census: path_param, path_optional_param, path_regex_param, path_wildcard +// Paths are kept as written: params, optional, regex, wildcard, a suffix after a param, a dot, a trailing slash. +import { Hono } from 'hono' + +const app = new Hono() + +app.get('/users/:id', (c) => c.text(c.req.param('id'))) +app.get('/posts/:year?', (c) => c.text('posts')) +app.get('/items/:id{[0-9]+}', (c) => c.text('numeric item')) +app.get('/files/*', (c) => c.text('any file')) +app.get('/img/:slug.png', (c) => c.text('png')) +app.get('/v1/keys.getKey', (c) => c.text('dotted')) +app.get('/trailing/', (c) => c.text('trailing slash kept')) +app.get('/a/:x/b/:y', (c) => c.text('two params')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/pathless-chain/src/index.ts b/graph/test/typescript/hono/cases/routing/pathless-chain/src/index.ts new file mode 100644 index 000000000..0fb926485 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/pathless-chain/src/index.ts @@ -0,0 +1,16 @@ +// census: verb_chained +// A path-less verb continues the previous path of the same app; the very first one is `/`. +import { Hono } from 'hono' + +const app = new Hono() + +app.get((c) => c.text('root, no path written')) + +app + .get('/book', (c) => c.text('list')) + .post((c) => c.text('create')) + .put((c) => c.text('replace')) + +app.delete('/book/:id', (c) => c.text('gone')).patch((c) => c.text('patched')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/scope-table/src/index.ts b/graph/test/typescript/hono/cases/routing/scope-table/src/index.ts new file mode 100644 index 000000000..e603d0671 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/scope-table/src/index.ts @@ -0,0 +1,23 @@ +// census: use_scoped, path_wildcard (SPEC 3.3 [iter2] QD09 table, settled against the pinned hono routers) +// /x/* reaches /x, /x/ and /x/a but not /xy; /x* reaches /xy; a mid-path * is exactly one segment. +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +const slashStar: MiddlewareHandler = async (c, next) => { await next() } +const bareStar: MiddlewareHandler = async (c, next) => { await next() } +const midStar: MiddlewareHandler = async (c, next) => { await next() } + +const app = new Hono() +app.use('/x/*', slashStar) +app.use('/x*', bareStar) +app.use('/a/*/c', midStar) + +app.get('/x', (c) => c.text('x')) +app.get('/x/', (c) => c.text('x slash')) +app.get('/x/a', (c) => c.text('x a')) +app.get('/xy', (c) => c.text('xy')) +app.get('/a/b/c', (c) => c.text('one segment')) +app.get('/a/c', (c) => c.text('zero segments')) +app.get('/a/b/d/c', (c) => c.text('two segments')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/strict-false/src/index.ts b/graph/test/typescript/hono/cases/routing/strict-false/src/index.ts new file mode 100644 index 000000000..e32ed631a --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/strict-false/src/index.ts @@ -0,0 +1,10 @@ +// census: strict_false, new_hono_options +// `strict: false` changes matching, not the registered path: the route table keeps what is written. +import { Hono } from 'hono' + +const app = new Hono({ strict: false }) + +app.get('/about/', (c) => c.text('about')) +app.get('/contact', (c) => c.text('contact')) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/websocket-project-module/src/chat.ts b/graph/test/typescript/hono/cases/routing/websocket-project-module/src/chat.ts new file mode 100644 index 000000000..e0be8783f --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/websocket-project-module/src/chat.ts @@ -0,0 +1,15 @@ +import { Hono } from 'hono' +import { upgradeWebSocket } from '@/utils/websocket' + +export const chat = new Hono().get( + '/ws', + async (c, next) => { + c.header('x-chat', '1') + await next() + }, + upgradeWebSocket(() => ({ + onMessage(event, ws) { + ws.send(String(event.data)) + }, + })) +) diff --git a/graph/test/typescript/hono/cases/routing/websocket-project-module/src/index.ts b/graph/test/typescript/hono/cases/routing/websocket-project-module/src/index.ts new file mode 100644 index 000000000..2080af800 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/websocket-project-module/src/index.ts @@ -0,0 +1,12 @@ +// census: verb_multi_handler (oracle gap O10 residual; SPEC R5) +// upgradeWebSocket taken from a PROJECT module that destructures createBunWebSocket() (imported relatively and +// through a tsconfig path alias): a library-call handler keyed at the call, after its per-route middleware. +import { Hono } from 'hono' +import { upgradeWebSocket } from './utils/websocket' +import { chat } from './chat' + +const app = new Hono() +app.get('/live', upgradeWebSocket(() => ({ onOpen() {} }))) +app.route('/chat', chat) + +export default app diff --git a/graph/test/typescript/hono/cases/routing/websocket-project-module/src/tsconfig.json b/graph/test/typescript/hono/cases/routing/websocket-project-module/src/tsconfig.json new file mode 100644 index 000000000..af19c36a1 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/websocket-project-module/src/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "baseUrl": ".", + "paths": { "@/*": ["./*"] } + } +} diff --git a/graph/test/typescript/hono/cases/routing/websocket-project-module/src/utils/websocket.ts b/graph/test/typescript/hono/cases/routing/websocket-project-module/src/utils/websocket.ts new file mode 100644 index 000000000..26e194bd7 --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/websocket-project-module/src/utils/websocket.ts @@ -0,0 +1,3 @@ +import { createBunWebSocket } from 'hono/bun' + +export const { upgradeWebSocket, websocket } = createBunWebSocket() diff --git a/graph/test/typescript/hono/cases/routing/websocket-sse/src/index.ts b/graph/test/typescript/hono/cases/routing/websocket-sse/src/index.ts new file mode 100644 index 000000000..f579ba20d --- /dev/null +++ b/graph/test/typescript/hono/cases/routing/websocket-sse/src/index.ts @@ -0,0 +1,23 @@ +// census: verb_literal_path (oracle gap O10; SPEC R5 library-call handlers) +// upgradeWebSocket(...) as the route handler is a library-call handler keyed at the call; a project handler +// returning streamSSE(...) is an ordinary project handler. +import { Hono } from 'hono' +import { upgradeWebSocket } from 'hono/cloudflare-workers' +import { streamSSE } from 'hono/streaming' + +const app = new Hono() +app.get( + '/ws', + upgradeWebSocket(() => ({ + onMessage(event, ws) { + ws.send(String(event.data)) + }, + })) +) +app.get('/sse', (c) => + streamSSE(c, async (stream) => { + await stream.writeSSE({ data: 'tick' }) + }) +) + +export default app diff --git a/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/client.ts b/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/client.ts new file mode 100644 index 000000000..1ed96a92e --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/client.ts @@ -0,0 +1,19 @@ +export async function listUsers(base: string) { + return fetch(`${base}/api/users`) +} + +export async function probe(base: string) { + return fetch(`${base}/__test/probe`) +} + +export async function landing(base: string) { + return fetch(`${base}/pricing/enterprise`) +} + +export async function loadFont() { + return fetch(new URL('./fonts/inter.woff2', import.meta.url)) +} + +export async function byKey(base: string, key: string) { + return fetch(`${base}/${key}`) +} diff --git a/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/server.test.ts b/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/server.test.ts new file mode 100644 index 000000000..47ea304f6 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/server.test.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +declare function it(name: string, fn: () => unknown): void + +it('serves a probe route on a test app', async () => { + const app = new Hono() + app.get('/__test/probe', (c) => c.text('probe')) + await app.request('/__test/probe') +}) diff --git a/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/server.ts b/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/server.ts new file mode 100644 index 000000000..6713c5ffb --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/base-unknown-implausible/src/server.ts @@ -0,0 +1,13 @@ +// census: request_fetch, app_request_test (SPEC R14; oracle rule O21) +// Requests whose base the program does not spell join a route on the composed full path as base_unknown (R14), but +// four shapes join nothing: (1) a non-test request never reaches an app only a test builds; (2) a request that only +// a catch-all handler would match names no route; (3) `new URL(rel, import.meta.url)` is a file next to the module, +// not a request; (4) a path with no literal segment (`${base}/${key}`) names no route. +import { Hono } from 'hono' + +const app = new Hono() +app.get('/api/users', (c) => c.json([])) +app.get('/api/users/:id', (c) => c.json({ id: c.req.param('id') })) +app.get('*', (c) => c.text('spa shell')) + +export default app diff --git a/graph/test/typescript/hono/cases/rpc/fetch-unknown-base/src/api.ts b/graph/test/typescript/hono/cases/rpc/fetch-unknown-base/src/api.ts new file mode 100644 index 000000000..faf298307 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/fetch-unknown-base/src/api.ts @@ -0,0 +1,11 @@ +// census: request_fetch (SPEC R14; oracle gap O18) +// Requests whose base the program does not spell may reach this program's routes: they join on the composed full +// path with confidence base_unknown. A literal external host never joins. +import { Hono } from 'hono' + +const app = new Hono().basePath('/api') +app.post('/oauth/register', (c) => c.json({ ok: true }, 201)) +app.get('/projects/:id', (c) => c.json({ id: c.req.param('id') })) +app.get('/health', (c) => c.text('ok')) + +export default app diff --git a/graph/test/typescript/hono/cases/rpc/fetch-unknown-base/src/cli.ts b/graph/test/typescript/hono/cases/rpc/fetch-unknown-base/src/cli.ts new file mode 100644 index 000000000..b1dc05d01 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/fetch-unknown-base/src/cli.ts @@ -0,0 +1,17 @@ +declare const process: { env: Record } + +export async function register(apiUrl: string) { + return fetch(`${apiUrl}/api/oauth/register`, { method: 'POST' }) +} + +export async function project(base: string, id: string) { + return fetch(new URL(`/api/projects/${id}`, base)) +} + +export async function health() { + return fetch((process.env.API_URL ?? 'http://localhost:3003') + '/api/health') +} + +export async function upstream() { + return fetch('https://status.example.com/api/health') +} diff --git a/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/discovery.ts b/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/discovery.ts new file mode 100644 index 000000000..0129db5e9 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/discovery.ts @@ -0,0 +1,8 @@ +// the method is set on the init only inside a branch: GET or POST, decided at run time +export async function poll(push: boolean) { + const init: RequestInit = { headers: { accept: 'application/json' } } + if (push) { + init.method = 'POST' + } + return fetch('http://localhost:8787/_discovery', init) +} diff --git a/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/server.test.ts b/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/server.test.ts new file mode 100644 index 000000000..c0ab1878f --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/server.test.ts @@ -0,0 +1,18 @@ +import app from './server' + +declare function it(name: string, fn: () => unknown): void + +async function hit(path: string, init?: RequestInit) { + return app.request(path, init) +} + +it('reads a skill', async () => { + await hit('/api/skills/1') +}) + +declare const recorded: string + +it('replays a recorded request', async () => { + const init = JSON.parse(recorded) as RequestInit + await hit('/api/skills/2', init) +}) diff --git a/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/server.ts b/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/server.ts new file mode 100644 index 000000000..854c5479c --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/server.ts @@ -0,0 +1,14 @@ +// census: request_fetch, app_request_test (SPEC R17; stress-6 oracle note O25; violation hono-6-03) +// A request whose HTTP method the program does not decide joins EVERY route on its composed path, on any verb, +// with Confidence method_unknown; when the base is also unknown, base_unknown wins. A literal method still joins +// only its own verb. +import { Hono } from 'hono' + +const app = new Hono() +app.get('/api/skills/:id', (c) => c.json({ id: c.req.param('id') })) +app.put('/api/skills/:id', (c) => c.json({ ok: true })) +app.delete('/api/skills/:id', (c) => c.body(null, 204)) +app.get('/_discovery', (c) => c.json([])) +app.post('/_discovery', (c) => c.json([])) + +export default app diff --git a/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/skills-client.ts b/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/skills-client.ts new file mode 100644 index 000000000..966fd04d0 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/fetch-verb-parameter/src/skills-client.ts @@ -0,0 +1,14 @@ +// the verb travels in an options object the helper is handed (and never inspects) +export async function skillRequest(baseUrl: string, path: string, options?: RequestInit) { + return fetch(`${baseUrl}/api/skills${path}`, options) +} + +// the verb is a parameter written into the init +export async function call(method: string, id: string) { + return fetch(`http://localhost:8787/api/skills/${id}`, { method, headers: { accept: 'application/json' } }) +} + +// control: a literal verb joins only its own route +export async function remove(id: string) { + return fetch(`http://localhost:8787/api/skills/${id}`, { method: 'DELETE' }) +} diff --git a/graph/test/typescript/hono/cases/rpc/hc-basic/src/client.ts b/graph/test/typescript/hono/cases/rpc/hc-basic/src/client.ts new file mode 100644 index 000000000..3864700c0 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/hc-basic/src/client.ts @@ -0,0 +1,22 @@ +// census: hc_client, hc_dollar_call, hc_element_param, apptype_typeof +// A typed client: each property is a path segment, [':id'] the literal param, index is `/`, $verb the call. +import { hc } from 'hono/client' +import type { AppType } from './server' + +const client = hc('http://localhost:8787') + +export async function listUsers() { + return (await client.users.$get()).json() +} +export async function oneUser(id: string) { + return (await client.users[':id'].$get({ param: { id } })).json() +} +export async function createUser() { + return client.users.$post() +} +export async function home() { + return client.index.$get() +} +export function userUrl() { + return client.users[':id'].$url({ param: { id: '1' } }) +} diff --git a/graph/test/typescript/hono/cases/rpc/hc-basic/src/server.ts b/graph/test/typescript/hono/cases/rpc/hc-basic/src/server.ts new file mode 100644 index 000000000..487852d4c --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/hc-basic/src/server.ts @@ -0,0 +1,12 @@ +import { Hono } from 'hono' + +const users = new Hono() + .get('/', (c) => c.json([{ id: '1' }])) + .get('/:id', (c) => c.json({ id: c.req.param('id') })) + .post('/', (c) => c.json({ id: '2' }, 201)) + +const app = new Hono() +const routes = app.route('/users', users).get('/', (c) => c.text('index')) + +export type AppType = typeof routes +export default app diff --git a/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/api.ts b/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/api.ts new file mode 100644 index 000000000..0f5432540 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/api.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +export const api = new Hono() + .basePath('/api') + .get('/ready', (c) => c.json({ ready: true })) + .get('/version', (c) => c.json({ v: 1 })) diff --git a/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/main.ts b/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/main.ts new file mode 100644 index 000000000..65562f962 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/main.ts @@ -0,0 +1,11 @@ +// census: route_mount_root (violation hono-3-02) +// A fetch to this machine is matched on the COMPOSED full paths: /ready is served only by the root app, the +// basePath'd sub-app serves /api/ready and /api/version. +import { Hono } from 'hono' +import { api } from './api' + +const root = new Hono() +root.get('/ready', (c) => c.text('ok')) +root.route('/', api) + +export default root diff --git a/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/probe.ts b/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/probe.ts new file mode 100644 index 000000000..c533e6085 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/local-fetch-full-path/src/probe.ts @@ -0,0 +1,17 @@ +declare const process: { env: Record } + +export async function waitReady(): Promise { + const port = process.env.PORT ?? '3000' + const res = await fetch(`http://localhost:${port}/ready`) + return res.ok +} + +export async function apiVersion(): Promise { + const res = await fetch('http://127.0.0.1:3000/api/version') + return res.json() +} + +export async function notServedHere(): Promise { + const res = await fetch('http://localhost:3000/version') + return res.ok +} diff --git a/graph/test/typescript/hono/cases/rpc/mixed-segment-hole/src/index.ts b/graph/test/typescript/hono/cases/rpc/mixed-segment-hole/src/index.ts new file mode 100644 index 000000000..5c455f76f --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/mixed-segment-hole/src/index.ts @@ -0,0 +1,16 @@ +// census: request_fetch (stress-5 mutation m39 shape; violation hono-5-07) +// A segment that mixes literal text and a hole (`/api/skills${path}`) matches routes whose segment starts with that +// text (/api/skills/:id), never a sibling (/api/agents). +import { Hono } from 'hono' + +const app = new Hono() +app.get('/api/agents', (c) => c.json([])) +app.get('/api/skills/:id', (c) => c.json({})) +export default app + +export async function skillsApi(base: string, path: string) { + return fetch(`${base}/api/skills${path}`) +} +export async function skillsLocal(path: string) { + return fetch(`http://localhost:3000/api/skills${path}`) +} diff --git a/graph/test/typescript/hono/cases/rpc/monorepo-client/src/server/app.ts b/graph/test/typescript/hono/cases/rpc/monorepo-client/src/server/app.ts new file mode 100644 index 000000000..8016393f1 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/monorepo-client/src/server/app.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +export const api = new Hono().basePath('/api') + .get('/orders', (c) => c.json([])) + .post('/orders/:orderId/cancel', (c) => c.json({ cancelled: c.req.param('orderId') })) + +export type Api = typeof api diff --git a/graph/test/typescript/hono/cases/rpc/monorepo-client/src/web/orders.ts b/graph/test/typescript/hono/cases/rpc/monorepo-client/src/web/orders.ts new file mode 100644 index 000000000..bc2ce8c60 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/monorepo-client/src/web/orders.ts @@ -0,0 +1,16 @@ +// census: hc_client, hc_dollar_call, hc_element_param +// The client lives in another package of the workspace and imports only the server's TYPE. +import { hc } from 'hono/client' +import type { Api } from '../server/app' + +function makeClient() { + return hc('/') +} + +export async function cancelOrder(orderId: string) { + const c = makeClient() + return c.api.orders[':orderId'].cancel.$post({ param: { orderId } }) +} +export async function listOrders() { + return hc('/').api.orders.$get() +} diff --git a/graph/test/typescript/hono/cases/rpc/query-string/src/app.test.ts b/graph/test/typescript/hono/cases/rpc/query-string/src/app.test.ts new file mode 100644 index 000000000..20682330e --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/query-string/src/app.test.ts @@ -0,0 +1,10 @@ +// census: app_request_test (violation hono-2-05, SPEC R9) +// The query string and fragment are stripped before matching: both requests reach the /file handler exactly. +import { app } from './app' + +export async function withEncodedQuery(rel: string) { + return app.request(`http://localhost/sessions/S1/file?path=${encodeURIComponent(rel)}`) +} +export async function withLiteralQuery() { + return app.request('http://localhost/sessions/S1/file?path=..%2Fsecret.png#top') +} diff --git a/graph/test/typescript/hono/cases/rpc/query-string/src/app.ts b/graph/test/typescript/hono/cases/rpc/query-string/src/app.ts new file mode 100644 index 000000000..dd9f76038 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/query-string/src/app.ts @@ -0,0 +1,5 @@ +import { Hono } from 'hono' + +export const app = new Hono() +app.get('/sessions/:id/file', (c) => c.text('file')) +app.get('/sessions/:id/objective', (c) => c.text('objective')) diff --git a/graph/test/typescript/hono/cases/rpc/reassigned-let/src/api.test.ts b/graph/test/typescript/hono/cases/rpc/reassigned-let/src/api.test.ts new file mode 100644 index 000000000..a01de4a32 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/reassigned-let/src/api.test.ts @@ -0,0 +1,21 @@ +// census: app_request_test (violation hono-2-03, SPEC R7; oracle gap O13) +// A `let` reassigned in another test is not a constant: in the request path it is a one-segment hole. +import api from './api' + +declare function test(name: string, fn: () => Promise): void + +let postId = '' +const fixed = 'p1' + +test('create', async () => { + postId = String(Date.now()) +}) +test('read', async () => { + await api.request(`/posts/${postId}`) +}) +test('update', async () => { + await api.request(`/posts/${postId}`, { method: 'PUT' }) +}) +test('fixed', async () => { + await api.request(`/posts/${fixed}`) +}) diff --git a/graph/test/typescript/hono/cases/rpc/reassigned-let/src/api.ts b/graph/test/typescript/hono/cases/rpc/reassigned-let/src/api.ts new file mode 100644 index 000000000..ec2b2c96d --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/reassigned-let/src/api.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +const api = new Hono() +api.get('/posts', (c) => c.json([])) +api.get('/posts/:id', (c) => c.json({})) +api.put('/posts/:id', (c) => c.json({})) + +export default api diff --git a/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/api.test.ts b/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/api.test.ts new file mode 100644 index 000000000..e5bc609dc --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/api.test.ts @@ -0,0 +1,10 @@ +import { hc } from 'hono/client' +import worker, { todos } from './api' + +export async function postsViaRequest() { + return worker.request('/posts', {}, {}) +} + +export async function todosViaClient() { + return hc('/api').todos.$get() +} diff --git a/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/api.ts b/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/api.ts new file mode 100644 index 000000000..506e7c750 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/api.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +const api = new Hono() +api.get('/posts', (c) => c.json([])) +export const todos = new Hono().get('/todos', (c) => c.json([])) +api.route('/', todos) + +export default api diff --git a/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/index.ts b/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/index.ts new file mode 100644 index 000000000..5926299cf --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/request-on-mounted-subapp/src/index.ts @@ -0,0 +1,10 @@ +// census: app_request_test, hc_client (violation hono-1-05) +// A request or client rooted at a sub-app that is ALSO mounted elsewhere is matched against that sub-app's +// own routes (relative to it), not the root's full paths. +import { Hono } from 'hono' +import api from './api' + +const app = new Hono() +app.route('/api', api) + +export default app diff --git a/graph/test/typescript/hono/cases/rpc/single-label-host/src/index.ts b/graph/test/typescript/hono/cases/rpc/single-label-host/src/index.ts new file mode 100644 index 000000000..f227658a7 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/single-label-host/src/index.ts @@ -0,0 +1,19 @@ +// census: request_fetch (stress-5 mutation m37 shape; violation hono-5-08, SPEC R14a) +// A literal single-label host (a service name inside the deployment) may reach this program's routes: joined with +// confidence base_unknown. A dotted external host never joins; an unknown base joins as base_unknown. +import { Hono } from 'hono' + +const app = new Hono() +app.get('/api/invoices', (c) => c.json([])) +app.get('/api/health', (c) => c.text('ok')) +export default app + +export async function listInvoices() { + return fetch('http://billing:9000/api/invoices') +} +export async function external() { + return fetch('https://api.example.com/api/health') +} +export async function viaBase(base: string) { + return fetch(`${base}/api/health`) +} diff --git a/graph/test/typescript/hono/cases/rpc/template-holes/src/app.test.ts b/graph/test/typescript/hono/cases/rpc/template-holes/src/app.test.ts new file mode 100644 index 000000000..38e974e0c --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/template-holes/src/app.test.ts @@ -0,0 +1,18 @@ +// census: app_request_test, hc_element_param (SPEC 3.5 [iter2] template holes and absolute URLs) +// A `${id}` hole is exactly one segment: it matches /posts/:id AND /posts/latest by shape; a hole no route +// shape fits is dynamic_rpc_path, never unserved; an absolute URL uses its pathname. +import { hc } from 'hono/client' +import { app } from './app' + +export async function oneByHole(id: string) { + return app.request(`/posts/${id}`) +} +export async function clientByHole(id: string) { + return (hc('http://api.example').posts as any)[id].$get() +} +export async function noShapeFits(id: string) { + return app.request(`/comments/${id}`) +} +export async function absoluteUrl() { + return app.request('http://localhost/posts') +} diff --git a/graph/test/typescript/hono/cases/rpc/template-holes/src/app.ts b/graph/test/typescript/hono/cases/rpc/template-holes/src/app.ts new file mode 100644 index 000000000..43a7e4c65 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/template-holes/src/app.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +export const app = new Hono() + .get('/posts', (c) => c.json([])) + .get('/posts/latest', (c) => c.json({})) + .get('/posts/:id', (c) => c.json({ id: c.req.param('id') })) diff --git a/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/apps/api/package.json b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/apps/api/package.json new file mode 100644 index 000000000..8232df626 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/apps/api/package.json @@ -0,0 +1 @@ +{ "name": "@acme/api", "private": true, "type": "module" } diff --git a/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/apps/api/src/index.ts b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/apps/api/src/index.ts new file mode 100644 index 000000000..d7fe3ec66 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/apps/api/src/index.ts @@ -0,0 +1,11 @@ +// census: route_mount_literal (oracle gap O3b) +// A workspace package whose `exports` point at an unbuilt ./dist/ file: in a checkout the import is the package's +// source (src/adapters/hono.ts), so the dashboard is mounted under /admin and is no root of its own. +import { Hono } from 'hono' +import { dashboard } from '@acme/routes/hono' + +const app = new Hono() +app.get('/health', (c) => c.text('ok')) +app.route('/admin', dashboard({ title: 'Ops' })) + +export default app diff --git a/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/package.json b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/package.json new file mode 100644 index 000000000..f99862cd5 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/package.json @@ -0,0 +1,11 @@ +{ + "name": "@acme/routes", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }, + "./hono": { "types": "./dist/adapters/hono.d.ts", "import": "./dist/adapters/hono.js" } + } +} diff --git a/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/src/adapters/hono.ts b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/src/adapters/hono.ts new file mode 100644 index 000000000..ff930fe60 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/src/adapters/hono.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function dashboard(opts: { title: string }) { + const ui = new Hono() + ui.get('/', (c) => c.html(`

${opts.title}

`)) + ui.get('/api/activity', (c) => c.json([])) + return ui +} diff --git a/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/src/index.ts b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/src/index.ts new file mode 100644 index 000000000..5a52d1123 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-dist-exports/src/packages/routes/src/index.ts @@ -0,0 +1 @@ +export const VERSION = '1.0.0' diff --git a/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/lib/client.ts b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/lib/client.ts new file mode 100644 index 000000000..f49261d95 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/lib/client.ts @@ -0,0 +1,6 @@ +// census: hc_client (violation hono-2-08; oracle gap O3: a workspace package subpath through package.json exports) +import { client as v2 } from '@scope/client/v2' + +export function listSessions() { + return v2.sessions.$get() +} diff --git a/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/lib/v2/index.ts b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/lib/v2/index.ts new file mode 100644 index 000000000..5b6cb50f2 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/lib/v2/index.ts @@ -0,0 +1,4 @@ +import { hc } from 'hono/client' +import type { AppType } from '@scope/server' + +export const client = hc('http://localhost') diff --git a/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/package.json b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/package.json new file mode 100644 index 000000000..6172e6542 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/client/package.json @@ -0,0 +1 @@ +{ "name": "@scope/client", "private": true, "exports": { "./v2": { "types": "./lib/v2/index.ts", "import": "./lib/v2/index.ts" } } } diff --git a/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/server/package.json b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/server/package.json new file mode 100644 index 000000000..7831a25bb --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/server/package.json @@ -0,0 +1 @@ +{ "name": "@scope/server", "private": true, "exports": { ".": { "types": "./src/app.ts" } } } diff --git a/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/server/src/app.ts b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/server/src/app.ts new file mode 100644 index 000000000..612877f55 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-exports/src/packages/server/src/app.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +const app = new Hono() +const routes = app.get('/sessions', (c) => c.json([])) + +export type AppType = typeof routes +export default app diff --git a/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/admin/package.json b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/admin/package.json new file mode 100644 index 000000000..0a0c85eb5 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/admin/package.json @@ -0,0 +1 @@ +{ "name": "@acme/admin", "private": true } diff --git a/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/admin/routes.ts b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/admin/routes.ts new file mode 100644 index 000000000..8f02ba76b --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/admin/routes.ts @@ -0,0 +1,3 @@ +import { Hono } from 'hono' + +export const admin = new Hono().get('/users', (c) => c.json([])) diff --git a/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/server/package.json b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/server/package.json new file mode 100644 index 000000000..399928b37 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/server/package.json @@ -0,0 +1 @@ +{ "name": "@acme/server", "private": true, "types": "src/index.ts" } diff --git a/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/server/src/index.ts b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/server/src/index.ts new file mode 100644 index 000000000..a6857d8b7 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/server/src/index.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' +import { admin } from '@acme/admin/routes' + +const app = new Hono() +const routes = app.get('/ping', (c) => c.text('pong')).route('/admin', admin) + +export type AppType = typeof routes +export default app diff --git a/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/web/client.ts b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/web/client.ts new file mode 100644 index 000000000..6a845f924 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/workspace-package/src/packages/web/client.ts @@ -0,0 +1,13 @@ +// census: hc_client, route_mount_literal (oracle gap O3: workspace package imports in a monorepo) +// Packages import each other by workspace package name: the mount and the typed client both cross them. +import { hc } from 'hono/client' +import type { AppType } from '@acme/server' + +const client = hc('/') + +export function ping() { + return client.ping.$get() +} +export function adminUsers() { + return client.admin.users.$get() +} diff --git a/graph/test/typescript/hono/cases/scale/deep-mount/budget b/graph/test/typescript/hono/cases/scale/deep-mount/budget new file mode 100644 index 000000000..e536b1826 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/deep-mount/budget @@ -0,0 +1,2 @@ +120 +rss_mb 2048 diff --git a/graph/test/typescript/hono/cases/scale/deep-mount/src/index.ts b/graph/test/typescript/hono/cases/scale/deep-mount/src/index.ts new file mode 100644 index 000000000..02cf67b37 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/deep-mount/src/index.ts @@ -0,0 +1,34 @@ +// census: route_mount_literal (scale: 10-deep mounting; generated by tools/gen-scale.mjs) +import { Hono } from 'hono' + +const l0 = new Hono() +l0.get('/here', (c) => c.text('level 0')) +const l1 = new Hono() +l1.get('/here', (c) => c.text('level 1')) +const l2 = new Hono() +l2.get('/here', (c) => c.text('level 2')) +const l3 = new Hono() +l3.get('/here', (c) => c.text('level 3')) +const l4 = new Hono() +l4.get('/here', (c) => c.text('level 4')) +const l5 = new Hono() +l5.get('/here', (c) => c.text('level 5')) +const l6 = new Hono() +l6.get('/here', (c) => c.text('level 6')) +const l7 = new Hono() +l7.get('/here', (c) => c.text('level 7')) +const l8 = new Hono() +l8.get('/here', (c) => c.text('level 8')) +const l9 = new Hono() +l9.get('/here', (c) => c.text('level 9')) +l8.route('/d9', l9) +l7.route('/d8', l8) +l6.route('/d7', l7) +l5.route('/d6', l6) +l4.route('/d5', l5) +l3.route('/d4', l4) +l2.route('/d3', l3) +l1.route('/d2', l2) +l0.route('/d1', l1) + +export default l0 diff --git a/graph/test/typescript/hono/cases/scale/mw-200/budget b/graph/test/typescript/hono/cases/scale/mw-200/budget new file mode 100644 index 000000000..ae729d3cc --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/mw-200/budget @@ -0,0 +1,2 @@ +300 +rss_mb 2048 diff --git a/graph/test/typescript/hono/cases/scale/mw-200/src/index.ts b/graph/test/typescript/hono/cases/scale/mw-200/src/index.ts new file mode 100644 index 000000000..366b8f2c4 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/mw-200/src/index.ts @@ -0,0 +1,417 @@ +// census: use_global, await_next (scale: 200 middleware x 10 routes; generated by tools/gen-scale.mjs) +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' + +const app = new Hono() +const m0: MiddlewareHandler = async (c, next) => { await next() } +app.use(m0) +const m1: MiddlewareHandler = async (c, next) => { await next() } +app.use(m1) +const m2: MiddlewareHandler = async (c, next) => { await next() } +app.use(m2) +const m3: MiddlewareHandler = async (c, next) => { await next() } +app.use(m3) +const m4: MiddlewareHandler = async (c, next) => { await next() } +app.use(m4) +const m5: MiddlewareHandler = async (c, next) => { await next() } +app.use(m5) +const m6: MiddlewareHandler = async (c, next) => { await next() } +app.use(m6) +const m7: MiddlewareHandler = async (c, next) => { await next() } +app.use(m7) +const m8: MiddlewareHandler = async (c, next) => { await next() } +app.use(m8) +const m9: MiddlewareHandler = async (c, next) => { await next() } +app.use(m9) +const m10: MiddlewareHandler = async (c, next) => { await next() } +app.use(m10) +const m11: MiddlewareHandler = async (c, next) => { await next() } +app.use(m11) +const m12: MiddlewareHandler = async (c, next) => { await next() } +app.use(m12) +const m13: MiddlewareHandler = async (c, next) => { await next() } +app.use(m13) +const m14: MiddlewareHandler = async (c, next) => { await next() } +app.use(m14) +const m15: MiddlewareHandler = async (c, next) => { await next() } +app.use(m15) +const m16: MiddlewareHandler = async (c, next) => { await next() } +app.use(m16) +const m17: MiddlewareHandler = async (c, next) => { await next() } +app.use(m17) +const m18: MiddlewareHandler = async (c, next) => { await next() } +app.use(m18) +const m19: MiddlewareHandler = async (c, next) => { await next() } +app.use(m19) +const m20: MiddlewareHandler = async (c, next) => { await next() } +app.use(m20) +const m21: MiddlewareHandler = async (c, next) => { await next() } +app.use(m21) +const m22: MiddlewareHandler = async (c, next) => { await next() } +app.use(m22) +const m23: MiddlewareHandler = async (c, next) => { await next() } +app.use(m23) +const m24: MiddlewareHandler = async (c, next) => { await next() } +app.use(m24) +const m25: MiddlewareHandler = async (c, next) => { await next() } +app.use(m25) +const m26: MiddlewareHandler = async (c, next) => { await next() } +app.use(m26) +const m27: MiddlewareHandler = async (c, next) => { await next() } +app.use(m27) +const m28: MiddlewareHandler = async (c, next) => { await next() } +app.use(m28) +const m29: MiddlewareHandler = async (c, next) => { await next() } +app.use(m29) +const m30: MiddlewareHandler = async (c, next) => { await next() } +app.use(m30) +const m31: MiddlewareHandler = async (c, next) => { await next() } +app.use(m31) +const m32: MiddlewareHandler = async (c, next) => { await next() } +app.use(m32) +const m33: MiddlewareHandler = async (c, next) => { await next() } +app.use(m33) +const m34: MiddlewareHandler = async (c, next) => { await next() } +app.use(m34) +const m35: MiddlewareHandler = async (c, next) => { await next() } +app.use(m35) +const m36: MiddlewareHandler = async (c, next) => { await next() } +app.use(m36) +const m37: MiddlewareHandler = async (c, next) => { await next() } +app.use(m37) +const m38: MiddlewareHandler = async (c, next) => { await next() } +app.use(m38) +const m39: MiddlewareHandler = async (c, next) => { await next() } +app.use(m39) +const m40: MiddlewareHandler = async (c, next) => { await next() } +app.use(m40) +const m41: MiddlewareHandler = async (c, next) => { await next() } +app.use(m41) +const m42: MiddlewareHandler = async (c, next) => { await next() } +app.use(m42) +const m43: MiddlewareHandler = async (c, next) => { await next() } +app.use(m43) +const m44: MiddlewareHandler = async (c, next) => { await next() } +app.use(m44) +const m45: MiddlewareHandler = async (c, next) => { await next() } +app.use(m45) +const m46: MiddlewareHandler = async (c, next) => { await next() } +app.use(m46) +const m47: MiddlewareHandler = async (c, next) => { await next() } +app.use(m47) +const m48: MiddlewareHandler = async (c, next) => { await next() } +app.use(m48) +const m49: MiddlewareHandler = async (c, next) => { await next() } +app.use(m49) +const m50: MiddlewareHandler = async (c, next) => { await next() } +app.use(m50) +const m51: MiddlewareHandler = async (c, next) => { await next() } +app.use(m51) +const m52: MiddlewareHandler = async (c, next) => { await next() } +app.use(m52) +const m53: MiddlewareHandler = async (c, next) => { await next() } +app.use(m53) +const m54: MiddlewareHandler = async (c, next) => { await next() } +app.use(m54) +const m55: MiddlewareHandler = async (c, next) => { await next() } +app.use(m55) +const m56: MiddlewareHandler = async (c, next) => { await next() } +app.use(m56) +const m57: MiddlewareHandler = async (c, next) => { await next() } +app.use(m57) +const m58: MiddlewareHandler = async (c, next) => { await next() } +app.use(m58) +const m59: MiddlewareHandler = async (c, next) => { await next() } +app.use(m59) +const m60: MiddlewareHandler = async (c, next) => { await next() } +app.use(m60) +const m61: MiddlewareHandler = async (c, next) => { await next() } +app.use(m61) +const m62: MiddlewareHandler = async (c, next) => { await next() } +app.use(m62) +const m63: MiddlewareHandler = async (c, next) => { await next() } +app.use(m63) +const m64: MiddlewareHandler = async (c, next) => { await next() } +app.use(m64) +const m65: MiddlewareHandler = async (c, next) => { await next() } +app.use(m65) +const m66: MiddlewareHandler = async (c, next) => { await next() } +app.use(m66) +const m67: MiddlewareHandler = async (c, next) => { await next() } +app.use(m67) +const m68: MiddlewareHandler = async (c, next) => { await next() } +app.use(m68) +const m69: MiddlewareHandler = async (c, next) => { await next() } +app.use(m69) +const m70: MiddlewareHandler = async (c, next) => { await next() } +app.use(m70) +const m71: MiddlewareHandler = async (c, next) => { await next() } +app.use(m71) +const m72: MiddlewareHandler = async (c, next) => { await next() } +app.use(m72) +const m73: MiddlewareHandler = async (c, next) => { await next() } +app.use(m73) +const m74: MiddlewareHandler = async (c, next) => { await next() } +app.use(m74) +const m75: MiddlewareHandler = async (c, next) => { await next() } +app.use(m75) +const m76: MiddlewareHandler = async (c, next) => { await next() } +app.use(m76) +const m77: MiddlewareHandler = async (c, next) => { await next() } +app.use(m77) +const m78: MiddlewareHandler = async (c, next) => { await next() } +app.use(m78) +const m79: MiddlewareHandler = async (c, next) => { await next() } +app.use(m79) +const m80: MiddlewareHandler = async (c, next) => { await next() } +app.use(m80) +const m81: MiddlewareHandler = async (c, next) => { await next() } +app.use(m81) +const m82: MiddlewareHandler = async (c, next) => { await next() } +app.use(m82) +const m83: MiddlewareHandler = async (c, next) => { await next() } +app.use(m83) +const m84: MiddlewareHandler = async (c, next) => { await next() } +app.use(m84) +const m85: MiddlewareHandler = async (c, next) => { await next() } +app.use(m85) +const m86: MiddlewareHandler = async (c, next) => { await next() } +app.use(m86) +const m87: MiddlewareHandler = async (c, next) => { await next() } +app.use(m87) +const m88: MiddlewareHandler = async (c, next) => { await next() } +app.use(m88) +const m89: MiddlewareHandler = async (c, next) => { await next() } +app.use(m89) +const m90: MiddlewareHandler = async (c, next) => { await next() } +app.use(m90) +const m91: MiddlewareHandler = async (c, next) => { await next() } +app.use(m91) +const m92: MiddlewareHandler = async (c, next) => { await next() } +app.use(m92) +const m93: MiddlewareHandler = async (c, next) => { await next() } +app.use(m93) +const m94: MiddlewareHandler = async (c, next) => { await next() } +app.use(m94) +const m95: MiddlewareHandler = async (c, next) => { await next() } +app.use(m95) +const m96: MiddlewareHandler = async (c, next) => { await next() } +app.use(m96) +const m97: MiddlewareHandler = async (c, next) => { await next() } +app.use(m97) +const m98: MiddlewareHandler = async (c, next) => { await next() } +app.use(m98) +const m99: MiddlewareHandler = async (c, next) => { await next() } +app.use(m99) +const m100: MiddlewareHandler = async (c, next) => { await next() } +app.use(m100) +const m101: MiddlewareHandler = async (c, next) => { await next() } +app.use(m101) +const m102: MiddlewareHandler = async (c, next) => { await next() } +app.use(m102) +const m103: MiddlewareHandler = async (c, next) => { await next() } +app.use(m103) +const m104: MiddlewareHandler = async (c, next) => { await next() } +app.use(m104) +const m105: MiddlewareHandler = async (c, next) => { await next() } +app.use(m105) +const m106: MiddlewareHandler = async (c, next) => { await next() } +app.use(m106) +const m107: MiddlewareHandler = async (c, next) => { await next() } +app.use(m107) +const m108: MiddlewareHandler = async (c, next) => { await next() } +app.use(m108) +const m109: MiddlewareHandler = async (c, next) => { await next() } +app.use(m109) +const m110: MiddlewareHandler = async (c, next) => { await next() } +app.use(m110) +const m111: MiddlewareHandler = async (c, next) => { await next() } +app.use(m111) +const m112: MiddlewareHandler = async (c, next) => { await next() } +app.use(m112) +const m113: MiddlewareHandler = async (c, next) => { await next() } +app.use(m113) +const m114: MiddlewareHandler = async (c, next) => { await next() } +app.use(m114) +const m115: MiddlewareHandler = async (c, next) => { await next() } +app.use(m115) +const m116: MiddlewareHandler = async (c, next) => { await next() } +app.use(m116) +const m117: MiddlewareHandler = async (c, next) => { await next() } +app.use(m117) +const m118: MiddlewareHandler = async (c, next) => { await next() } +app.use(m118) +const m119: MiddlewareHandler = async (c, next) => { await next() } +app.use(m119) +const m120: MiddlewareHandler = async (c, next) => { await next() } +app.use(m120) +const m121: MiddlewareHandler = async (c, next) => { await next() } +app.use(m121) +const m122: MiddlewareHandler = async (c, next) => { await next() } +app.use(m122) +const m123: MiddlewareHandler = async (c, next) => { await next() } +app.use(m123) +const m124: MiddlewareHandler = async (c, next) => { await next() } +app.use(m124) +const m125: MiddlewareHandler = async (c, next) => { await next() } +app.use(m125) +const m126: MiddlewareHandler = async (c, next) => { await next() } +app.use(m126) +const m127: MiddlewareHandler = async (c, next) => { await next() } +app.use(m127) +const m128: MiddlewareHandler = async (c, next) => { await next() } +app.use(m128) +const m129: MiddlewareHandler = async (c, next) => { await next() } +app.use(m129) +const m130: MiddlewareHandler = async (c, next) => { await next() } +app.use(m130) +const m131: MiddlewareHandler = async (c, next) => { await next() } +app.use(m131) +const m132: MiddlewareHandler = async (c, next) => { await next() } +app.use(m132) +const m133: MiddlewareHandler = async (c, next) => { await next() } +app.use(m133) +const m134: MiddlewareHandler = async (c, next) => { await next() } +app.use(m134) +const m135: MiddlewareHandler = async (c, next) => { await next() } +app.use(m135) +const m136: MiddlewareHandler = async (c, next) => { await next() } +app.use(m136) +const m137: MiddlewareHandler = async (c, next) => { await next() } +app.use(m137) +const m138: MiddlewareHandler = async (c, next) => { await next() } +app.use(m138) +const m139: MiddlewareHandler = async (c, next) => { await next() } +app.use(m139) +const m140: MiddlewareHandler = async (c, next) => { await next() } +app.use(m140) +const m141: MiddlewareHandler = async (c, next) => { await next() } +app.use(m141) +const m142: MiddlewareHandler = async (c, next) => { await next() } +app.use(m142) +const m143: MiddlewareHandler = async (c, next) => { await next() } +app.use(m143) +const m144: MiddlewareHandler = async (c, next) => { await next() } +app.use(m144) +const m145: MiddlewareHandler = async (c, next) => { await next() } +app.use(m145) +const m146: MiddlewareHandler = async (c, next) => { await next() } +app.use(m146) +const m147: MiddlewareHandler = async (c, next) => { await next() } +app.use(m147) +const m148: MiddlewareHandler = async (c, next) => { await next() } +app.use(m148) +const m149: MiddlewareHandler = async (c, next) => { await next() } +app.use(m149) +const m150: MiddlewareHandler = async (c, next) => { await next() } +app.use(m150) +const m151: MiddlewareHandler = async (c, next) => { await next() } +app.use(m151) +const m152: MiddlewareHandler = async (c, next) => { await next() } +app.use(m152) +const m153: MiddlewareHandler = async (c, next) => { await next() } +app.use(m153) +const m154: MiddlewareHandler = async (c, next) => { await next() } +app.use(m154) +const m155: MiddlewareHandler = async (c, next) => { await next() } +app.use(m155) +const m156: MiddlewareHandler = async (c, next) => { await next() } +app.use(m156) +const m157: MiddlewareHandler = async (c, next) => { await next() } +app.use(m157) +const m158: MiddlewareHandler = async (c, next) => { await next() } +app.use(m158) +const m159: MiddlewareHandler = async (c, next) => { await next() } +app.use(m159) +const m160: MiddlewareHandler = async (c, next) => { await next() } +app.use(m160) +const m161: MiddlewareHandler = async (c, next) => { await next() } +app.use(m161) +const m162: MiddlewareHandler = async (c, next) => { await next() } +app.use(m162) +const m163: MiddlewareHandler = async (c, next) => { await next() } +app.use(m163) +const m164: MiddlewareHandler = async (c, next) => { await next() } +app.use(m164) +const m165: MiddlewareHandler = async (c, next) => { await next() } +app.use(m165) +const m166: MiddlewareHandler = async (c, next) => { await next() } +app.use(m166) +const m167: MiddlewareHandler = async (c, next) => { await next() } +app.use(m167) +const m168: MiddlewareHandler = async (c, next) => { await next() } +app.use(m168) +const m169: MiddlewareHandler = async (c, next) => { await next() } +app.use(m169) +const m170: MiddlewareHandler = async (c, next) => { await next() } +app.use(m170) +const m171: MiddlewareHandler = async (c, next) => { await next() } +app.use(m171) +const m172: MiddlewareHandler = async (c, next) => { await next() } +app.use(m172) +const m173: MiddlewareHandler = async (c, next) => { await next() } +app.use(m173) +const m174: MiddlewareHandler = async (c, next) => { await next() } +app.use(m174) +const m175: MiddlewareHandler = async (c, next) => { await next() } +app.use(m175) +const m176: MiddlewareHandler = async (c, next) => { await next() } +app.use(m176) +const m177: MiddlewareHandler = async (c, next) => { await next() } +app.use(m177) +const m178: MiddlewareHandler = async (c, next) => { await next() } +app.use(m178) +const m179: MiddlewareHandler = async (c, next) => { await next() } +app.use(m179) +const m180: MiddlewareHandler = async (c, next) => { await next() } +app.use(m180) +const m181: MiddlewareHandler = async (c, next) => { await next() } +app.use(m181) +const m182: MiddlewareHandler = async (c, next) => { await next() } +app.use(m182) +const m183: MiddlewareHandler = async (c, next) => { await next() } +app.use(m183) +const m184: MiddlewareHandler = async (c, next) => { await next() } +app.use(m184) +const m185: MiddlewareHandler = async (c, next) => { await next() } +app.use(m185) +const m186: MiddlewareHandler = async (c, next) => { await next() } +app.use(m186) +const m187: MiddlewareHandler = async (c, next) => { await next() } +app.use(m187) +const m188: MiddlewareHandler = async (c, next) => { await next() } +app.use(m188) +const m189: MiddlewareHandler = async (c, next) => { await next() } +app.use(m189) +const m190: MiddlewareHandler = async (c, next) => { await next() } +app.use(m190) +const m191: MiddlewareHandler = async (c, next) => { await next() } +app.use(m191) +const m192: MiddlewareHandler = async (c, next) => { await next() } +app.use(m192) +const m193: MiddlewareHandler = async (c, next) => { await next() } +app.use(m193) +const m194: MiddlewareHandler = async (c, next) => { await next() } +app.use(m194) +const m195: MiddlewareHandler = async (c, next) => { await next() } +app.use(m195) +const m196: MiddlewareHandler = async (c, next) => { await next() } +app.use(m196) +const m197: MiddlewareHandler = async (c, next) => { await next() } +app.use(m197) +const m198: MiddlewareHandler = async (c, next) => { await next() } +app.use(m198) +const m199: MiddlewareHandler = async (c, next) => { await next() } +app.use(m199) +app.get('/r0', (c) => c.text('0')) +app.get('/r1', (c) => c.text('1')) +app.get('/r2', (c) => c.text('2')) +app.get('/r3', (c) => c.text('3')) +app.get('/r4', (c) => c.text('4')) +app.get('/r5', (c) => c.text('5')) +app.get('/r6', (c) => c.text('6')) +app.get('/r7', (c) => c.text('7')) +app.get('/r8', (c) => c.text('8')) +app.get('/r9', (c) => c.text('9')) + +export default app diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/budget b/graph/test/typescript/hono/cases/scale/routes-2k/budget new file mode 100644 index 000000000..ae729d3cc --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/budget @@ -0,0 +1,2 @@ +300 +rss_mb 2048 diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/index.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/index.ts new file mode 100644 index 000000000..9aaa7b83e --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/index.ts @@ -0,0 +1,46 @@ +// census: route_mount_literal, verb_literal_path (scale: 2,000 routes; generated by tools/gen-scale.mjs) +import { Hono } from 'hono' +import { r0 } from './mod0' +import { r1 } from './mod1' +import { r2 } from './mod2' +import { r3 } from './mod3' +import { r4 } from './mod4' +import { r5 } from './mod5' +import { r6 } from './mod6' +import { r7 } from './mod7' +import { r8 } from './mod8' +import { r9 } from './mod9' +import { r10 } from './mod10' +import { r11 } from './mod11' +import { r12 } from './mod12' +import { r13 } from './mod13' +import { r14 } from './mod14' +import { r15 } from './mod15' +import { r16 } from './mod16' +import { r17 } from './mod17' +import { r18 } from './mod18' +import { r19 } from './mod19' + +const app = new Hono() +app.route('/m0', r0) +app.route('/m1', r1) +app.route('/m2', r2) +app.route('/m3', r3) +app.route('/m4', r4) +app.route('/m5', r5) +app.route('/m6', r6) +app.route('/m7', r7) +app.route('/m8', r8) +app.route('/m9', r9) +app.route('/m10', r10) +app.route('/m11', r11) +app.route('/m12', r12) +app.route('/m13', r13) +app.route('/m14', r14) +app.route('/m15', r15) +app.route('/m16', r16) +app.route('/m17', r17) +app.route('/m18', r18) +app.route('/m19', r19) + +export default app diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod0.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod0.ts new file mode 100644 index 000000000..f488457ef --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod0.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r0 = new Hono() +r0.get('/res0/:id', (c) => c.text('m0 r0')) +r0.post('/res1/:id', (c) => c.text('m0 r1')) +r0.put('/res2/:id', (c) => c.text('m0 r2')) +r0.delete('/res3/:id', (c) => c.text('m0 r3')) +r0.patch('/res4/:id', (c) => c.text('m0 r4')) +r0.get('/res5/:id', (c) => c.text('m0 r5')) +r0.post('/res6/:id', (c) => c.text('m0 r6')) +r0.put('/res7/:id', (c) => c.text('m0 r7')) +r0.delete('/res8/:id', (c) => c.text('m0 r8')) +r0.patch('/res9/:id', (c) => c.text('m0 r9')) +r0.get('/res10/:id', (c) => c.text('m0 r10')) +r0.post('/res11/:id', (c) => c.text('m0 r11')) +r0.put('/res12/:id', (c) => c.text('m0 r12')) +r0.delete('/res13/:id', (c) => c.text('m0 r13')) +r0.patch('/res14/:id', (c) => c.text('m0 r14')) +r0.get('/res15/:id', (c) => c.text('m0 r15')) +r0.post('/res16/:id', (c) => c.text('m0 r16')) +r0.put('/res17/:id', (c) => c.text('m0 r17')) +r0.delete('/res18/:id', (c) => c.text('m0 r18')) +r0.patch('/res19/:id', (c) => c.text('m0 r19')) +r0.get('/res20/:id', (c) => c.text('m0 r20')) +r0.post('/res21/:id', (c) => c.text('m0 r21')) +r0.put('/res22/:id', (c) => c.text('m0 r22')) +r0.delete('/res23/:id', (c) => c.text('m0 r23')) +r0.patch('/res24/:id', (c) => c.text('m0 r24')) +r0.get('/res25/:id', (c) => c.text('m0 r25')) +r0.post('/res26/:id', (c) => c.text('m0 r26')) +r0.put('/res27/:id', (c) => c.text('m0 r27')) +r0.delete('/res28/:id', (c) => c.text('m0 r28')) +r0.patch('/res29/:id', (c) => c.text('m0 r29')) +r0.get('/res30/:id', (c) => c.text('m0 r30')) +r0.post('/res31/:id', (c) => c.text('m0 r31')) +r0.put('/res32/:id', (c) => c.text('m0 r32')) +r0.delete('/res33/:id', (c) => c.text('m0 r33')) +r0.patch('/res34/:id', (c) => c.text('m0 r34')) +r0.get('/res35/:id', (c) => c.text('m0 r35')) +r0.post('/res36/:id', (c) => c.text('m0 r36')) +r0.put('/res37/:id', (c) => c.text('m0 r37')) +r0.delete('/res38/:id', (c) => c.text('m0 r38')) +r0.patch('/res39/:id', (c) => c.text('m0 r39')) +r0.get('/res40/:id', (c) => c.text('m0 r40')) +r0.post('/res41/:id', (c) => c.text('m0 r41')) +r0.put('/res42/:id', (c) => c.text('m0 r42')) +r0.delete('/res43/:id', (c) => c.text('m0 r43')) +r0.patch('/res44/:id', (c) => c.text('m0 r44')) +r0.get('/res45/:id', (c) => c.text('m0 r45')) +r0.post('/res46/:id', (c) => c.text('m0 r46')) +r0.put('/res47/:id', (c) => c.text('m0 r47')) +r0.delete('/res48/:id', (c) => c.text('m0 r48')) +r0.patch('/res49/:id', (c) => c.text('m0 r49')) +r0.get('/res50/:id', (c) => c.text('m0 r50')) +r0.post('/res51/:id', (c) => c.text('m0 r51')) +r0.put('/res52/:id', (c) => c.text('m0 r52')) +r0.delete('/res53/:id', (c) => c.text('m0 r53')) +r0.patch('/res54/:id', (c) => c.text('m0 r54')) +r0.get('/res55/:id', (c) => c.text('m0 r55')) +r0.post('/res56/:id', (c) => c.text('m0 r56')) +r0.put('/res57/:id', (c) => c.text('m0 r57')) +r0.delete('/res58/:id', (c) => c.text('m0 r58')) +r0.patch('/res59/:id', (c) => c.text('m0 r59')) +r0.get('/res60/:id', (c) => c.text('m0 r60')) +r0.post('/res61/:id', (c) => c.text('m0 r61')) +r0.put('/res62/:id', (c) => c.text('m0 r62')) +r0.delete('/res63/:id', (c) => c.text('m0 r63')) +r0.patch('/res64/:id', (c) => c.text('m0 r64')) +r0.get('/res65/:id', (c) => c.text('m0 r65')) +r0.post('/res66/:id', (c) => c.text('m0 r66')) +r0.put('/res67/:id', (c) => c.text('m0 r67')) +r0.delete('/res68/:id', (c) => c.text('m0 r68')) +r0.patch('/res69/:id', (c) => c.text('m0 r69')) +r0.get('/res70/:id', (c) => c.text('m0 r70')) +r0.post('/res71/:id', (c) => c.text('m0 r71')) +r0.put('/res72/:id', (c) => c.text('m0 r72')) +r0.delete('/res73/:id', (c) => c.text('m0 r73')) +r0.patch('/res74/:id', (c) => c.text('m0 r74')) +r0.get('/res75/:id', (c) => c.text('m0 r75')) +r0.post('/res76/:id', (c) => c.text('m0 r76')) +r0.put('/res77/:id', (c) => c.text('m0 r77')) +r0.delete('/res78/:id', (c) => c.text('m0 r78')) +r0.patch('/res79/:id', (c) => c.text('m0 r79')) +r0.get('/res80/:id', (c) => c.text('m0 r80')) +r0.post('/res81/:id', (c) => c.text('m0 r81')) +r0.put('/res82/:id', (c) => c.text('m0 r82')) +r0.delete('/res83/:id', (c) => c.text('m0 r83')) +r0.patch('/res84/:id', (c) => c.text('m0 r84')) +r0.get('/res85/:id', (c) => c.text('m0 r85')) +r0.post('/res86/:id', (c) => c.text('m0 r86')) +r0.put('/res87/:id', (c) => c.text('m0 r87')) +r0.delete('/res88/:id', (c) => c.text('m0 r88')) +r0.patch('/res89/:id', (c) => c.text('m0 r89')) +r0.get('/res90/:id', (c) => c.text('m0 r90')) +r0.post('/res91/:id', (c) => c.text('m0 r91')) +r0.put('/res92/:id', (c) => c.text('m0 r92')) +r0.delete('/res93/:id', (c) => c.text('m0 r93')) +r0.patch('/res94/:id', (c) => c.text('m0 r94')) +r0.get('/res95/:id', (c) => c.text('m0 r95')) +r0.post('/res96/:id', (c) => c.text('m0 r96')) +r0.put('/res97/:id', (c) => c.text('m0 r97')) +r0.delete('/res98/:id', (c) => c.text('m0 r98')) +r0.patch('/res99/:id', (c) => c.text('m0 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod1.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod1.ts new file mode 100644 index 000000000..8f6c72ba7 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod1.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r1 = new Hono() +r1.get('/res0/:id', (c) => c.text('m1 r0')) +r1.post('/res1/:id', (c) => c.text('m1 r1')) +r1.put('/res2/:id', (c) => c.text('m1 r2')) +r1.delete('/res3/:id', (c) => c.text('m1 r3')) +r1.patch('/res4/:id', (c) => c.text('m1 r4')) +r1.get('/res5/:id', (c) => c.text('m1 r5')) +r1.post('/res6/:id', (c) => c.text('m1 r6')) +r1.put('/res7/:id', (c) => c.text('m1 r7')) +r1.delete('/res8/:id', (c) => c.text('m1 r8')) +r1.patch('/res9/:id', (c) => c.text('m1 r9')) +r1.get('/res10/:id', (c) => c.text('m1 r10')) +r1.post('/res11/:id', (c) => c.text('m1 r11')) +r1.put('/res12/:id', (c) => c.text('m1 r12')) +r1.delete('/res13/:id', (c) => c.text('m1 r13')) +r1.patch('/res14/:id', (c) => c.text('m1 r14')) +r1.get('/res15/:id', (c) => c.text('m1 r15')) +r1.post('/res16/:id', (c) => c.text('m1 r16')) +r1.put('/res17/:id', (c) => c.text('m1 r17')) +r1.delete('/res18/:id', (c) => c.text('m1 r18')) +r1.patch('/res19/:id', (c) => c.text('m1 r19')) +r1.get('/res20/:id', (c) => c.text('m1 r20')) +r1.post('/res21/:id', (c) => c.text('m1 r21')) +r1.put('/res22/:id', (c) => c.text('m1 r22')) +r1.delete('/res23/:id', (c) => c.text('m1 r23')) +r1.patch('/res24/:id', (c) => c.text('m1 r24')) +r1.get('/res25/:id', (c) => c.text('m1 r25')) +r1.post('/res26/:id', (c) => c.text('m1 r26')) +r1.put('/res27/:id', (c) => c.text('m1 r27')) +r1.delete('/res28/:id', (c) => c.text('m1 r28')) +r1.patch('/res29/:id', (c) => c.text('m1 r29')) +r1.get('/res30/:id', (c) => c.text('m1 r30')) +r1.post('/res31/:id', (c) => c.text('m1 r31')) +r1.put('/res32/:id', (c) => c.text('m1 r32')) +r1.delete('/res33/:id', (c) => c.text('m1 r33')) +r1.patch('/res34/:id', (c) => c.text('m1 r34')) +r1.get('/res35/:id', (c) => c.text('m1 r35')) +r1.post('/res36/:id', (c) => c.text('m1 r36')) +r1.put('/res37/:id', (c) => c.text('m1 r37')) +r1.delete('/res38/:id', (c) => c.text('m1 r38')) +r1.patch('/res39/:id', (c) => c.text('m1 r39')) +r1.get('/res40/:id', (c) => c.text('m1 r40')) +r1.post('/res41/:id', (c) => c.text('m1 r41')) +r1.put('/res42/:id', (c) => c.text('m1 r42')) +r1.delete('/res43/:id', (c) => c.text('m1 r43')) +r1.patch('/res44/:id', (c) => c.text('m1 r44')) +r1.get('/res45/:id', (c) => c.text('m1 r45')) +r1.post('/res46/:id', (c) => c.text('m1 r46')) +r1.put('/res47/:id', (c) => c.text('m1 r47')) +r1.delete('/res48/:id', (c) => c.text('m1 r48')) +r1.patch('/res49/:id', (c) => c.text('m1 r49')) +r1.get('/res50/:id', (c) => c.text('m1 r50')) +r1.post('/res51/:id', (c) => c.text('m1 r51')) +r1.put('/res52/:id', (c) => c.text('m1 r52')) +r1.delete('/res53/:id', (c) => c.text('m1 r53')) +r1.patch('/res54/:id', (c) => c.text('m1 r54')) +r1.get('/res55/:id', (c) => c.text('m1 r55')) +r1.post('/res56/:id', (c) => c.text('m1 r56')) +r1.put('/res57/:id', (c) => c.text('m1 r57')) +r1.delete('/res58/:id', (c) => c.text('m1 r58')) +r1.patch('/res59/:id', (c) => c.text('m1 r59')) +r1.get('/res60/:id', (c) => c.text('m1 r60')) +r1.post('/res61/:id', (c) => c.text('m1 r61')) +r1.put('/res62/:id', (c) => c.text('m1 r62')) +r1.delete('/res63/:id', (c) => c.text('m1 r63')) +r1.patch('/res64/:id', (c) => c.text('m1 r64')) +r1.get('/res65/:id', (c) => c.text('m1 r65')) +r1.post('/res66/:id', (c) => c.text('m1 r66')) +r1.put('/res67/:id', (c) => c.text('m1 r67')) +r1.delete('/res68/:id', (c) => c.text('m1 r68')) +r1.patch('/res69/:id', (c) => c.text('m1 r69')) +r1.get('/res70/:id', (c) => c.text('m1 r70')) +r1.post('/res71/:id', (c) => c.text('m1 r71')) +r1.put('/res72/:id', (c) => c.text('m1 r72')) +r1.delete('/res73/:id', (c) => c.text('m1 r73')) +r1.patch('/res74/:id', (c) => c.text('m1 r74')) +r1.get('/res75/:id', (c) => c.text('m1 r75')) +r1.post('/res76/:id', (c) => c.text('m1 r76')) +r1.put('/res77/:id', (c) => c.text('m1 r77')) +r1.delete('/res78/:id', (c) => c.text('m1 r78')) +r1.patch('/res79/:id', (c) => c.text('m1 r79')) +r1.get('/res80/:id', (c) => c.text('m1 r80')) +r1.post('/res81/:id', (c) => c.text('m1 r81')) +r1.put('/res82/:id', (c) => c.text('m1 r82')) +r1.delete('/res83/:id', (c) => c.text('m1 r83')) +r1.patch('/res84/:id', (c) => c.text('m1 r84')) +r1.get('/res85/:id', (c) => c.text('m1 r85')) +r1.post('/res86/:id', (c) => c.text('m1 r86')) +r1.put('/res87/:id', (c) => c.text('m1 r87')) +r1.delete('/res88/:id', (c) => c.text('m1 r88')) +r1.patch('/res89/:id', (c) => c.text('m1 r89')) +r1.get('/res90/:id', (c) => c.text('m1 r90')) +r1.post('/res91/:id', (c) => c.text('m1 r91')) +r1.put('/res92/:id', (c) => c.text('m1 r92')) +r1.delete('/res93/:id', (c) => c.text('m1 r93')) +r1.patch('/res94/:id', (c) => c.text('m1 r94')) +r1.get('/res95/:id', (c) => c.text('m1 r95')) +r1.post('/res96/:id', (c) => c.text('m1 r96')) +r1.put('/res97/:id', (c) => c.text('m1 r97')) +r1.delete('/res98/:id', (c) => c.text('m1 r98')) +r1.patch('/res99/:id', (c) => c.text('m1 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod10.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod10.ts new file mode 100644 index 000000000..675db665a --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod10.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r10 = new Hono() +r10.get('/res0/:id', (c) => c.text('m10 r0')) +r10.post('/res1/:id', (c) => c.text('m10 r1')) +r10.put('/res2/:id', (c) => c.text('m10 r2')) +r10.delete('/res3/:id', (c) => c.text('m10 r3')) +r10.patch('/res4/:id', (c) => c.text('m10 r4')) +r10.get('/res5/:id', (c) => c.text('m10 r5')) +r10.post('/res6/:id', (c) => c.text('m10 r6')) +r10.put('/res7/:id', (c) => c.text('m10 r7')) +r10.delete('/res8/:id', (c) => c.text('m10 r8')) +r10.patch('/res9/:id', (c) => c.text('m10 r9')) +r10.get('/res10/:id', (c) => c.text('m10 r10')) +r10.post('/res11/:id', (c) => c.text('m10 r11')) +r10.put('/res12/:id', (c) => c.text('m10 r12')) +r10.delete('/res13/:id', (c) => c.text('m10 r13')) +r10.patch('/res14/:id', (c) => c.text('m10 r14')) +r10.get('/res15/:id', (c) => c.text('m10 r15')) +r10.post('/res16/:id', (c) => c.text('m10 r16')) +r10.put('/res17/:id', (c) => c.text('m10 r17')) +r10.delete('/res18/:id', (c) => c.text('m10 r18')) +r10.patch('/res19/:id', (c) => c.text('m10 r19')) +r10.get('/res20/:id', (c) => c.text('m10 r20')) +r10.post('/res21/:id', (c) => c.text('m10 r21')) +r10.put('/res22/:id', (c) => c.text('m10 r22')) +r10.delete('/res23/:id', (c) => c.text('m10 r23')) +r10.patch('/res24/:id', (c) => c.text('m10 r24')) +r10.get('/res25/:id', (c) => c.text('m10 r25')) +r10.post('/res26/:id', (c) => c.text('m10 r26')) +r10.put('/res27/:id', (c) => c.text('m10 r27')) +r10.delete('/res28/:id', (c) => c.text('m10 r28')) +r10.patch('/res29/:id', (c) => c.text('m10 r29')) +r10.get('/res30/:id', (c) => c.text('m10 r30')) +r10.post('/res31/:id', (c) => c.text('m10 r31')) +r10.put('/res32/:id', (c) => c.text('m10 r32')) +r10.delete('/res33/:id', (c) => c.text('m10 r33')) +r10.patch('/res34/:id', (c) => c.text('m10 r34')) +r10.get('/res35/:id', (c) => c.text('m10 r35')) +r10.post('/res36/:id', (c) => c.text('m10 r36')) +r10.put('/res37/:id', (c) => c.text('m10 r37')) +r10.delete('/res38/:id', (c) => c.text('m10 r38')) +r10.patch('/res39/:id', (c) => c.text('m10 r39')) +r10.get('/res40/:id', (c) => c.text('m10 r40')) +r10.post('/res41/:id', (c) => c.text('m10 r41')) +r10.put('/res42/:id', (c) => c.text('m10 r42')) +r10.delete('/res43/:id', (c) => c.text('m10 r43')) +r10.patch('/res44/:id', (c) => c.text('m10 r44')) +r10.get('/res45/:id', (c) => c.text('m10 r45')) +r10.post('/res46/:id', (c) => c.text('m10 r46')) +r10.put('/res47/:id', (c) => c.text('m10 r47')) +r10.delete('/res48/:id', (c) => c.text('m10 r48')) +r10.patch('/res49/:id', (c) => c.text('m10 r49')) +r10.get('/res50/:id', (c) => c.text('m10 r50')) +r10.post('/res51/:id', (c) => c.text('m10 r51')) +r10.put('/res52/:id', (c) => c.text('m10 r52')) +r10.delete('/res53/:id', (c) => c.text('m10 r53')) +r10.patch('/res54/:id', (c) => c.text('m10 r54')) +r10.get('/res55/:id', (c) => c.text('m10 r55')) +r10.post('/res56/:id', (c) => c.text('m10 r56')) +r10.put('/res57/:id', (c) => c.text('m10 r57')) +r10.delete('/res58/:id', (c) => c.text('m10 r58')) +r10.patch('/res59/:id', (c) => c.text('m10 r59')) +r10.get('/res60/:id', (c) => c.text('m10 r60')) +r10.post('/res61/:id', (c) => c.text('m10 r61')) +r10.put('/res62/:id', (c) => c.text('m10 r62')) +r10.delete('/res63/:id', (c) => c.text('m10 r63')) +r10.patch('/res64/:id', (c) => c.text('m10 r64')) +r10.get('/res65/:id', (c) => c.text('m10 r65')) +r10.post('/res66/:id', (c) => c.text('m10 r66')) +r10.put('/res67/:id', (c) => c.text('m10 r67')) +r10.delete('/res68/:id', (c) => c.text('m10 r68')) +r10.patch('/res69/:id', (c) => c.text('m10 r69')) +r10.get('/res70/:id', (c) => c.text('m10 r70')) +r10.post('/res71/:id', (c) => c.text('m10 r71')) +r10.put('/res72/:id', (c) => c.text('m10 r72')) +r10.delete('/res73/:id', (c) => c.text('m10 r73')) +r10.patch('/res74/:id', (c) => c.text('m10 r74')) +r10.get('/res75/:id', (c) => c.text('m10 r75')) +r10.post('/res76/:id', (c) => c.text('m10 r76')) +r10.put('/res77/:id', (c) => c.text('m10 r77')) +r10.delete('/res78/:id', (c) => c.text('m10 r78')) +r10.patch('/res79/:id', (c) => c.text('m10 r79')) +r10.get('/res80/:id', (c) => c.text('m10 r80')) +r10.post('/res81/:id', (c) => c.text('m10 r81')) +r10.put('/res82/:id', (c) => c.text('m10 r82')) +r10.delete('/res83/:id', (c) => c.text('m10 r83')) +r10.patch('/res84/:id', (c) => c.text('m10 r84')) +r10.get('/res85/:id', (c) => c.text('m10 r85')) +r10.post('/res86/:id', (c) => c.text('m10 r86')) +r10.put('/res87/:id', (c) => c.text('m10 r87')) +r10.delete('/res88/:id', (c) => c.text('m10 r88')) +r10.patch('/res89/:id', (c) => c.text('m10 r89')) +r10.get('/res90/:id', (c) => c.text('m10 r90')) +r10.post('/res91/:id', (c) => c.text('m10 r91')) +r10.put('/res92/:id', (c) => c.text('m10 r92')) +r10.delete('/res93/:id', (c) => c.text('m10 r93')) +r10.patch('/res94/:id', (c) => c.text('m10 r94')) +r10.get('/res95/:id', (c) => c.text('m10 r95')) +r10.post('/res96/:id', (c) => c.text('m10 r96')) +r10.put('/res97/:id', (c) => c.text('m10 r97')) +r10.delete('/res98/:id', (c) => c.text('m10 r98')) +r10.patch('/res99/:id', (c) => c.text('m10 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod11.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod11.ts new file mode 100644 index 000000000..54bd32283 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod11.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r11 = new Hono() +r11.get('/res0/:id', (c) => c.text('m11 r0')) +r11.post('/res1/:id', (c) => c.text('m11 r1')) +r11.put('/res2/:id', (c) => c.text('m11 r2')) +r11.delete('/res3/:id', (c) => c.text('m11 r3')) +r11.patch('/res4/:id', (c) => c.text('m11 r4')) +r11.get('/res5/:id', (c) => c.text('m11 r5')) +r11.post('/res6/:id', (c) => c.text('m11 r6')) +r11.put('/res7/:id', (c) => c.text('m11 r7')) +r11.delete('/res8/:id', (c) => c.text('m11 r8')) +r11.patch('/res9/:id', (c) => c.text('m11 r9')) +r11.get('/res10/:id', (c) => c.text('m11 r10')) +r11.post('/res11/:id', (c) => c.text('m11 r11')) +r11.put('/res12/:id', (c) => c.text('m11 r12')) +r11.delete('/res13/:id', (c) => c.text('m11 r13')) +r11.patch('/res14/:id', (c) => c.text('m11 r14')) +r11.get('/res15/:id', (c) => c.text('m11 r15')) +r11.post('/res16/:id', (c) => c.text('m11 r16')) +r11.put('/res17/:id', (c) => c.text('m11 r17')) +r11.delete('/res18/:id', (c) => c.text('m11 r18')) +r11.patch('/res19/:id', (c) => c.text('m11 r19')) +r11.get('/res20/:id', (c) => c.text('m11 r20')) +r11.post('/res21/:id', (c) => c.text('m11 r21')) +r11.put('/res22/:id', (c) => c.text('m11 r22')) +r11.delete('/res23/:id', (c) => c.text('m11 r23')) +r11.patch('/res24/:id', (c) => c.text('m11 r24')) +r11.get('/res25/:id', (c) => c.text('m11 r25')) +r11.post('/res26/:id', (c) => c.text('m11 r26')) +r11.put('/res27/:id', (c) => c.text('m11 r27')) +r11.delete('/res28/:id', (c) => c.text('m11 r28')) +r11.patch('/res29/:id', (c) => c.text('m11 r29')) +r11.get('/res30/:id', (c) => c.text('m11 r30')) +r11.post('/res31/:id', (c) => c.text('m11 r31')) +r11.put('/res32/:id', (c) => c.text('m11 r32')) +r11.delete('/res33/:id', (c) => c.text('m11 r33')) +r11.patch('/res34/:id', (c) => c.text('m11 r34')) +r11.get('/res35/:id', (c) => c.text('m11 r35')) +r11.post('/res36/:id', (c) => c.text('m11 r36')) +r11.put('/res37/:id', (c) => c.text('m11 r37')) +r11.delete('/res38/:id', (c) => c.text('m11 r38')) +r11.patch('/res39/:id', (c) => c.text('m11 r39')) +r11.get('/res40/:id', (c) => c.text('m11 r40')) +r11.post('/res41/:id', (c) => c.text('m11 r41')) +r11.put('/res42/:id', (c) => c.text('m11 r42')) +r11.delete('/res43/:id', (c) => c.text('m11 r43')) +r11.patch('/res44/:id', (c) => c.text('m11 r44')) +r11.get('/res45/:id', (c) => c.text('m11 r45')) +r11.post('/res46/:id', (c) => c.text('m11 r46')) +r11.put('/res47/:id', (c) => c.text('m11 r47')) +r11.delete('/res48/:id', (c) => c.text('m11 r48')) +r11.patch('/res49/:id', (c) => c.text('m11 r49')) +r11.get('/res50/:id', (c) => c.text('m11 r50')) +r11.post('/res51/:id', (c) => c.text('m11 r51')) +r11.put('/res52/:id', (c) => c.text('m11 r52')) +r11.delete('/res53/:id', (c) => c.text('m11 r53')) +r11.patch('/res54/:id', (c) => c.text('m11 r54')) +r11.get('/res55/:id', (c) => c.text('m11 r55')) +r11.post('/res56/:id', (c) => c.text('m11 r56')) +r11.put('/res57/:id', (c) => c.text('m11 r57')) +r11.delete('/res58/:id', (c) => c.text('m11 r58')) +r11.patch('/res59/:id', (c) => c.text('m11 r59')) +r11.get('/res60/:id', (c) => c.text('m11 r60')) +r11.post('/res61/:id', (c) => c.text('m11 r61')) +r11.put('/res62/:id', (c) => c.text('m11 r62')) +r11.delete('/res63/:id', (c) => c.text('m11 r63')) +r11.patch('/res64/:id', (c) => c.text('m11 r64')) +r11.get('/res65/:id', (c) => c.text('m11 r65')) +r11.post('/res66/:id', (c) => c.text('m11 r66')) +r11.put('/res67/:id', (c) => c.text('m11 r67')) +r11.delete('/res68/:id', (c) => c.text('m11 r68')) +r11.patch('/res69/:id', (c) => c.text('m11 r69')) +r11.get('/res70/:id', (c) => c.text('m11 r70')) +r11.post('/res71/:id', (c) => c.text('m11 r71')) +r11.put('/res72/:id', (c) => c.text('m11 r72')) +r11.delete('/res73/:id', (c) => c.text('m11 r73')) +r11.patch('/res74/:id', (c) => c.text('m11 r74')) +r11.get('/res75/:id', (c) => c.text('m11 r75')) +r11.post('/res76/:id', (c) => c.text('m11 r76')) +r11.put('/res77/:id', (c) => c.text('m11 r77')) +r11.delete('/res78/:id', (c) => c.text('m11 r78')) +r11.patch('/res79/:id', (c) => c.text('m11 r79')) +r11.get('/res80/:id', (c) => c.text('m11 r80')) +r11.post('/res81/:id', (c) => c.text('m11 r81')) +r11.put('/res82/:id', (c) => c.text('m11 r82')) +r11.delete('/res83/:id', (c) => c.text('m11 r83')) +r11.patch('/res84/:id', (c) => c.text('m11 r84')) +r11.get('/res85/:id', (c) => c.text('m11 r85')) +r11.post('/res86/:id', (c) => c.text('m11 r86')) +r11.put('/res87/:id', (c) => c.text('m11 r87')) +r11.delete('/res88/:id', (c) => c.text('m11 r88')) +r11.patch('/res89/:id', (c) => c.text('m11 r89')) +r11.get('/res90/:id', (c) => c.text('m11 r90')) +r11.post('/res91/:id', (c) => c.text('m11 r91')) +r11.put('/res92/:id', (c) => c.text('m11 r92')) +r11.delete('/res93/:id', (c) => c.text('m11 r93')) +r11.patch('/res94/:id', (c) => c.text('m11 r94')) +r11.get('/res95/:id', (c) => c.text('m11 r95')) +r11.post('/res96/:id', (c) => c.text('m11 r96')) +r11.put('/res97/:id', (c) => c.text('m11 r97')) +r11.delete('/res98/:id', (c) => c.text('m11 r98')) +r11.patch('/res99/:id', (c) => c.text('m11 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod12.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod12.ts new file mode 100644 index 000000000..f5abe1102 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod12.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r12 = new Hono() +r12.get('/res0/:id', (c) => c.text('m12 r0')) +r12.post('/res1/:id', (c) => c.text('m12 r1')) +r12.put('/res2/:id', (c) => c.text('m12 r2')) +r12.delete('/res3/:id', (c) => c.text('m12 r3')) +r12.patch('/res4/:id', (c) => c.text('m12 r4')) +r12.get('/res5/:id', (c) => c.text('m12 r5')) +r12.post('/res6/:id', (c) => c.text('m12 r6')) +r12.put('/res7/:id', (c) => c.text('m12 r7')) +r12.delete('/res8/:id', (c) => c.text('m12 r8')) +r12.patch('/res9/:id', (c) => c.text('m12 r9')) +r12.get('/res10/:id', (c) => c.text('m12 r10')) +r12.post('/res11/:id', (c) => c.text('m12 r11')) +r12.put('/res12/:id', (c) => c.text('m12 r12')) +r12.delete('/res13/:id', (c) => c.text('m12 r13')) +r12.patch('/res14/:id', (c) => c.text('m12 r14')) +r12.get('/res15/:id', (c) => c.text('m12 r15')) +r12.post('/res16/:id', (c) => c.text('m12 r16')) +r12.put('/res17/:id', (c) => c.text('m12 r17')) +r12.delete('/res18/:id', (c) => c.text('m12 r18')) +r12.patch('/res19/:id', (c) => c.text('m12 r19')) +r12.get('/res20/:id', (c) => c.text('m12 r20')) +r12.post('/res21/:id', (c) => c.text('m12 r21')) +r12.put('/res22/:id', (c) => c.text('m12 r22')) +r12.delete('/res23/:id', (c) => c.text('m12 r23')) +r12.patch('/res24/:id', (c) => c.text('m12 r24')) +r12.get('/res25/:id', (c) => c.text('m12 r25')) +r12.post('/res26/:id', (c) => c.text('m12 r26')) +r12.put('/res27/:id', (c) => c.text('m12 r27')) +r12.delete('/res28/:id', (c) => c.text('m12 r28')) +r12.patch('/res29/:id', (c) => c.text('m12 r29')) +r12.get('/res30/:id', (c) => c.text('m12 r30')) +r12.post('/res31/:id', (c) => c.text('m12 r31')) +r12.put('/res32/:id', (c) => c.text('m12 r32')) +r12.delete('/res33/:id', (c) => c.text('m12 r33')) +r12.patch('/res34/:id', (c) => c.text('m12 r34')) +r12.get('/res35/:id', (c) => c.text('m12 r35')) +r12.post('/res36/:id', (c) => c.text('m12 r36')) +r12.put('/res37/:id', (c) => c.text('m12 r37')) +r12.delete('/res38/:id', (c) => c.text('m12 r38')) +r12.patch('/res39/:id', (c) => c.text('m12 r39')) +r12.get('/res40/:id', (c) => c.text('m12 r40')) +r12.post('/res41/:id', (c) => c.text('m12 r41')) +r12.put('/res42/:id', (c) => c.text('m12 r42')) +r12.delete('/res43/:id', (c) => c.text('m12 r43')) +r12.patch('/res44/:id', (c) => c.text('m12 r44')) +r12.get('/res45/:id', (c) => c.text('m12 r45')) +r12.post('/res46/:id', (c) => c.text('m12 r46')) +r12.put('/res47/:id', (c) => c.text('m12 r47')) +r12.delete('/res48/:id', (c) => c.text('m12 r48')) +r12.patch('/res49/:id', (c) => c.text('m12 r49')) +r12.get('/res50/:id', (c) => c.text('m12 r50')) +r12.post('/res51/:id', (c) => c.text('m12 r51')) +r12.put('/res52/:id', (c) => c.text('m12 r52')) +r12.delete('/res53/:id', (c) => c.text('m12 r53')) +r12.patch('/res54/:id', (c) => c.text('m12 r54')) +r12.get('/res55/:id', (c) => c.text('m12 r55')) +r12.post('/res56/:id', (c) => c.text('m12 r56')) +r12.put('/res57/:id', (c) => c.text('m12 r57')) +r12.delete('/res58/:id', (c) => c.text('m12 r58')) +r12.patch('/res59/:id', (c) => c.text('m12 r59')) +r12.get('/res60/:id', (c) => c.text('m12 r60')) +r12.post('/res61/:id', (c) => c.text('m12 r61')) +r12.put('/res62/:id', (c) => c.text('m12 r62')) +r12.delete('/res63/:id', (c) => c.text('m12 r63')) +r12.patch('/res64/:id', (c) => c.text('m12 r64')) +r12.get('/res65/:id', (c) => c.text('m12 r65')) +r12.post('/res66/:id', (c) => c.text('m12 r66')) +r12.put('/res67/:id', (c) => c.text('m12 r67')) +r12.delete('/res68/:id', (c) => c.text('m12 r68')) +r12.patch('/res69/:id', (c) => c.text('m12 r69')) +r12.get('/res70/:id', (c) => c.text('m12 r70')) +r12.post('/res71/:id', (c) => c.text('m12 r71')) +r12.put('/res72/:id', (c) => c.text('m12 r72')) +r12.delete('/res73/:id', (c) => c.text('m12 r73')) +r12.patch('/res74/:id', (c) => c.text('m12 r74')) +r12.get('/res75/:id', (c) => c.text('m12 r75')) +r12.post('/res76/:id', (c) => c.text('m12 r76')) +r12.put('/res77/:id', (c) => c.text('m12 r77')) +r12.delete('/res78/:id', (c) => c.text('m12 r78')) +r12.patch('/res79/:id', (c) => c.text('m12 r79')) +r12.get('/res80/:id', (c) => c.text('m12 r80')) +r12.post('/res81/:id', (c) => c.text('m12 r81')) +r12.put('/res82/:id', (c) => c.text('m12 r82')) +r12.delete('/res83/:id', (c) => c.text('m12 r83')) +r12.patch('/res84/:id', (c) => c.text('m12 r84')) +r12.get('/res85/:id', (c) => c.text('m12 r85')) +r12.post('/res86/:id', (c) => c.text('m12 r86')) +r12.put('/res87/:id', (c) => c.text('m12 r87')) +r12.delete('/res88/:id', (c) => c.text('m12 r88')) +r12.patch('/res89/:id', (c) => c.text('m12 r89')) +r12.get('/res90/:id', (c) => c.text('m12 r90')) +r12.post('/res91/:id', (c) => c.text('m12 r91')) +r12.put('/res92/:id', (c) => c.text('m12 r92')) +r12.delete('/res93/:id', (c) => c.text('m12 r93')) +r12.patch('/res94/:id', (c) => c.text('m12 r94')) +r12.get('/res95/:id', (c) => c.text('m12 r95')) +r12.post('/res96/:id', (c) => c.text('m12 r96')) +r12.put('/res97/:id', (c) => c.text('m12 r97')) +r12.delete('/res98/:id', (c) => c.text('m12 r98')) +r12.patch('/res99/:id', (c) => c.text('m12 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod13.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod13.ts new file mode 100644 index 000000000..b208542f7 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod13.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r13 = new Hono() +r13.get('/res0/:id', (c) => c.text('m13 r0')) +r13.post('/res1/:id', (c) => c.text('m13 r1')) +r13.put('/res2/:id', (c) => c.text('m13 r2')) +r13.delete('/res3/:id', (c) => c.text('m13 r3')) +r13.patch('/res4/:id', (c) => c.text('m13 r4')) +r13.get('/res5/:id', (c) => c.text('m13 r5')) +r13.post('/res6/:id', (c) => c.text('m13 r6')) +r13.put('/res7/:id', (c) => c.text('m13 r7')) +r13.delete('/res8/:id', (c) => c.text('m13 r8')) +r13.patch('/res9/:id', (c) => c.text('m13 r9')) +r13.get('/res10/:id', (c) => c.text('m13 r10')) +r13.post('/res11/:id', (c) => c.text('m13 r11')) +r13.put('/res12/:id', (c) => c.text('m13 r12')) +r13.delete('/res13/:id', (c) => c.text('m13 r13')) +r13.patch('/res14/:id', (c) => c.text('m13 r14')) +r13.get('/res15/:id', (c) => c.text('m13 r15')) +r13.post('/res16/:id', (c) => c.text('m13 r16')) +r13.put('/res17/:id', (c) => c.text('m13 r17')) +r13.delete('/res18/:id', (c) => c.text('m13 r18')) +r13.patch('/res19/:id', (c) => c.text('m13 r19')) +r13.get('/res20/:id', (c) => c.text('m13 r20')) +r13.post('/res21/:id', (c) => c.text('m13 r21')) +r13.put('/res22/:id', (c) => c.text('m13 r22')) +r13.delete('/res23/:id', (c) => c.text('m13 r23')) +r13.patch('/res24/:id', (c) => c.text('m13 r24')) +r13.get('/res25/:id', (c) => c.text('m13 r25')) +r13.post('/res26/:id', (c) => c.text('m13 r26')) +r13.put('/res27/:id', (c) => c.text('m13 r27')) +r13.delete('/res28/:id', (c) => c.text('m13 r28')) +r13.patch('/res29/:id', (c) => c.text('m13 r29')) +r13.get('/res30/:id', (c) => c.text('m13 r30')) +r13.post('/res31/:id', (c) => c.text('m13 r31')) +r13.put('/res32/:id', (c) => c.text('m13 r32')) +r13.delete('/res33/:id', (c) => c.text('m13 r33')) +r13.patch('/res34/:id', (c) => c.text('m13 r34')) +r13.get('/res35/:id', (c) => c.text('m13 r35')) +r13.post('/res36/:id', (c) => c.text('m13 r36')) +r13.put('/res37/:id', (c) => c.text('m13 r37')) +r13.delete('/res38/:id', (c) => c.text('m13 r38')) +r13.patch('/res39/:id', (c) => c.text('m13 r39')) +r13.get('/res40/:id', (c) => c.text('m13 r40')) +r13.post('/res41/:id', (c) => c.text('m13 r41')) +r13.put('/res42/:id', (c) => c.text('m13 r42')) +r13.delete('/res43/:id', (c) => c.text('m13 r43')) +r13.patch('/res44/:id', (c) => c.text('m13 r44')) +r13.get('/res45/:id', (c) => c.text('m13 r45')) +r13.post('/res46/:id', (c) => c.text('m13 r46')) +r13.put('/res47/:id', (c) => c.text('m13 r47')) +r13.delete('/res48/:id', (c) => c.text('m13 r48')) +r13.patch('/res49/:id', (c) => c.text('m13 r49')) +r13.get('/res50/:id', (c) => c.text('m13 r50')) +r13.post('/res51/:id', (c) => c.text('m13 r51')) +r13.put('/res52/:id', (c) => c.text('m13 r52')) +r13.delete('/res53/:id', (c) => c.text('m13 r53')) +r13.patch('/res54/:id', (c) => c.text('m13 r54')) +r13.get('/res55/:id', (c) => c.text('m13 r55')) +r13.post('/res56/:id', (c) => c.text('m13 r56')) +r13.put('/res57/:id', (c) => c.text('m13 r57')) +r13.delete('/res58/:id', (c) => c.text('m13 r58')) +r13.patch('/res59/:id', (c) => c.text('m13 r59')) +r13.get('/res60/:id', (c) => c.text('m13 r60')) +r13.post('/res61/:id', (c) => c.text('m13 r61')) +r13.put('/res62/:id', (c) => c.text('m13 r62')) +r13.delete('/res63/:id', (c) => c.text('m13 r63')) +r13.patch('/res64/:id', (c) => c.text('m13 r64')) +r13.get('/res65/:id', (c) => c.text('m13 r65')) +r13.post('/res66/:id', (c) => c.text('m13 r66')) +r13.put('/res67/:id', (c) => c.text('m13 r67')) +r13.delete('/res68/:id', (c) => c.text('m13 r68')) +r13.patch('/res69/:id', (c) => c.text('m13 r69')) +r13.get('/res70/:id', (c) => c.text('m13 r70')) +r13.post('/res71/:id', (c) => c.text('m13 r71')) +r13.put('/res72/:id', (c) => c.text('m13 r72')) +r13.delete('/res73/:id', (c) => c.text('m13 r73')) +r13.patch('/res74/:id', (c) => c.text('m13 r74')) +r13.get('/res75/:id', (c) => c.text('m13 r75')) +r13.post('/res76/:id', (c) => c.text('m13 r76')) +r13.put('/res77/:id', (c) => c.text('m13 r77')) +r13.delete('/res78/:id', (c) => c.text('m13 r78')) +r13.patch('/res79/:id', (c) => c.text('m13 r79')) +r13.get('/res80/:id', (c) => c.text('m13 r80')) +r13.post('/res81/:id', (c) => c.text('m13 r81')) +r13.put('/res82/:id', (c) => c.text('m13 r82')) +r13.delete('/res83/:id', (c) => c.text('m13 r83')) +r13.patch('/res84/:id', (c) => c.text('m13 r84')) +r13.get('/res85/:id', (c) => c.text('m13 r85')) +r13.post('/res86/:id', (c) => c.text('m13 r86')) +r13.put('/res87/:id', (c) => c.text('m13 r87')) +r13.delete('/res88/:id', (c) => c.text('m13 r88')) +r13.patch('/res89/:id', (c) => c.text('m13 r89')) +r13.get('/res90/:id', (c) => c.text('m13 r90')) +r13.post('/res91/:id', (c) => c.text('m13 r91')) +r13.put('/res92/:id', (c) => c.text('m13 r92')) +r13.delete('/res93/:id', (c) => c.text('m13 r93')) +r13.patch('/res94/:id', (c) => c.text('m13 r94')) +r13.get('/res95/:id', (c) => c.text('m13 r95')) +r13.post('/res96/:id', (c) => c.text('m13 r96')) +r13.put('/res97/:id', (c) => c.text('m13 r97')) +r13.delete('/res98/:id', (c) => c.text('m13 r98')) +r13.patch('/res99/:id', (c) => c.text('m13 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod14.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod14.ts new file mode 100644 index 000000000..234c69bfd --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod14.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r14 = new Hono() +r14.get('/res0/:id', (c) => c.text('m14 r0')) +r14.post('/res1/:id', (c) => c.text('m14 r1')) +r14.put('/res2/:id', (c) => c.text('m14 r2')) +r14.delete('/res3/:id', (c) => c.text('m14 r3')) +r14.patch('/res4/:id', (c) => c.text('m14 r4')) +r14.get('/res5/:id', (c) => c.text('m14 r5')) +r14.post('/res6/:id', (c) => c.text('m14 r6')) +r14.put('/res7/:id', (c) => c.text('m14 r7')) +r14.delete('/res8/:id', (c) => c.text('m14 r8')) +r14.patch('/res9/:id', (c) => c.text('m14 r9')) +r14.get('/res10/:id', (c) => c.text('m14 r10')) +r14.post('/res11/:id', (c) => c.text('m14 r11')) +r14.put('/res12/:id', (c) => c.text('m14 r12')) +r14.delete('/res13/:id', (c) => c.text('m14 r13')) +r14.patch('/res14/:id', (c) => c.text('m14 r14')) +r14.get('/res15/:id', (c) => c.text('m14 r15')) +r14.post('/res16/:id', (c) => c.text('m14 r16')) +r14.put('/res17/:id', (c) => c.text('m14 r17')) +r14.delete('/res18/:id', (c) => c.text('m14 r18')) +r14.patch('/res19/:id', (c) => c.text('m14 r19')) +r14.get('/res20/:id', (c) => c.text('m14 r20')) +r14.post('/res21/:id', (c) => c.text('m14 r21')) +r14.put('/res22/:id', (c) => c.text('m14 r22')) +r14.delete('/res23/:id', (c) => c.text('m14 r23')) +r14.patch('/res24/:id', (c) => c.text('m14 r24')) +r14.get('/res25/:id', (c) => c.text('m14 r25')) +r14.post('/res26/:id', (c) => c.text('m14 r26')) +r14.put('/res27/:id', (c) => c.text('m14 r27')) +r14.delete('/res28/:id', (c) => c.text('m14 r28')) +r14.patch('/res29/:id', (c) => c.text('m14 r29')) +r14.get('/res30/:id', (c) => c.text('m14 r30')) +r14.post('/res31/:id', (c) => c.text('m14 r31')) +r14.put('/res32/:id', (c) => c.text('m14 r32')) +r14.delete('/res33/:id', (c) => c.text('m14 r33')) +r14.patch('/res34/:id', (c) => c.text('m14 r34')) +r14.get('/res35/:id', (c) => c.text('m14 r35')) +r14.post('/res36/:id', (c) => c.text('m14 r36')) +r14.put('/res37/:id', (c) => c.text('m14 r37')) +r14.delete('/res38/:id', (c) => c.text('m14 r38')) +r14.patch('/res39/:id', (c) => c.text('m14 r39')) +r14.get('/res40/:id', (c) => c.text('m14 r40')) +r14.post('/res41/:id', (c) => c.text('m14 r41')) +r14.put('/res42/:id', (c) => c.text('m14 r42')) +r14.delete('/res43/:id', (c) => c.text('m14 r43')) +r14.patch('/res44/:id', (c) => c.text('m14 r44')) +r14.get('/res45/:id', (c) => c.text('m14 r45')) +r14.post('/res46/:id', (c) => c.text('m14 r46')) +r14.put('/res47/:id', (c) => c.text('m14 r47')) +r14.delete('/res48/:id', (c) => c.text('m14 r48')) +r14.patch('/res49/:id', (c) => c.text('m14 r49')) +r14.get('/res50/:id', (c) => c.text('m14 r50')) +r14.post('/res51/:id', (c) => c.text('m14 r51')) +r14.put('/res52/:id', (c) => c.text('m14 r52')) +r14.delete('/res53/:id', (c) => c.text('m14 r53')) +r14.patch('/res54/:id', (c) => c.text('m14 r54')) +r14.get('/res55/:id', (c) => c.text('m14 r55')) +r14.post('/res56/:id', (c) => c.text('m14 r56')) +r14.put('/res57/:id', (c) => c.text('m14 r57')) +r14.delete('/res58/:id', (c) => c.text('m14 r58')) +r14.patch('/res59/:id', (c) => c.text('m14 r59')) +r14.get('/res60/:id', (c) => c.text('m14 r60')) +r14.post('/res61/:id', (c) => c.text('m14 r61')) +r14.put('/res62/:id', (c) => c.text('m14 r62')) +r14.delete('/res63/:id', (c) => c.text('m14 r63')) +r14.patch('/res64/:id', (c) => c.text('m14 r64')) +r14.get('/res65/:id', (c) => c.text('m14 r65')) +r14.post('/res66/:id', (c) => c.text('m14 r66')) +r14.put('/res67/:id', (c) => c.text('m14 r67')) +r14.delete('/res68/:id', (c) => c.text('m14 r68')) +r14.patch('/res69/:id', (c) => c.text('m14 r69')) +r14.get('/res70/:id', (c) => c.text('m14 r70')) +r14.post('/res71/:id', (c) => c.text('m14 r71')) +r14.put('/res72/:id', (c) => c.text('m14 r72')) +r14.delete('/res73/:id', (c) => c.text('m14 r73')) +r14.patch('/res74/:id', (c) => c.text('m14 r74')) +r14.get('/res75/:id', (c) => c.text('m14 r75')) +r14.post('/res76/:id', (c) => c.text('m14 r76')) +r14.put('/res77/:id', (c) => c.text('m14 r77')) +r14.delete('/res78/:id', (c) => c.text('m14 r78')) +r14.patch('/res79/:id', (c) => c.text('m14 r79')) +r14.get('/res80/:id', (c) => c.text('m14 r80')) +r14.post('/res81/:id', (c) => c.text('m14 r81')) +r14.put('/res82/:id', (c) => c.text('m14 r82')) +r14.delete('/res83/:id', (c) => c.text('m14 r83')) +r14.patch('/res84/:id', (c) => c.text('m14 r84')) +r14.get('/res85/:id', (c) => c.text('m14 r85')) +r14.post('/res86/:id', (c) => c.text('m14 r86')) +r14.put('/res87/:id', (c) => c.text('m14 r87')) +r14.delete('/res88/:id', (c) => c.text('m14 r88')) +r14.patch('/res89/:id', (c) => c.text('m14 r89')) +r14.get('/res90/:id', (c) => c.text('m14 r90')) +r14.post('/res91/:id', (c) => c.text('m14 r91')) +r14.put('/res92/:id', (c) => c.text('m14 r92')) +r14.delete('/res93/:id', (c) => c.text('m14 r93')) +r14.patch('/res94/:id', (c) => c.text('m14 r94')) +r14.get('/res95/:id', (c) => c.text('m14 r95')) +r14.post('/res96/:id', (c) => c.text('m14 r96')) +r14.put('/res97/:id', (c) => c.text('m14 r97')) +r14.delete('/res98/:id', (c) => c.text('m14 r98')) +r14.patch('/res99/:id', (c) => c.text('m14 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod15.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod15.ts new file mode 100644 index 000000000..57475d2a5 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod15.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r15 = new Hono() +r15.get('/res0/:id', (c) => c.text('m15 r0')) +r15.post('/res1/:id', (c) => c.text('m15 r1')) +r15.put('/res2/:id', (c) => c.text('m15 r2')) +r15.delete('/res3/:id', (c) => c.text('m15 r3')) +r15.patch('/res4/:id', (c) => c.text('m15 r4')) +r15.get('/res5/:id', (c) => c.text('m15 r5')) +r15.post('/res6/:id', (c) => c.text('m15 r6')) +r15.put('/res7/:id', (c) => c.text('m15 r7')) +r15.delete('/res8/:id', (c) => c.text('m15 r8')) +r15.patch('/res9/:id', (c) => c.text('m15 r9')) +r15.get('/res10/:id', (c) => c.text('m15 r10')) +r15.post('/res11/:id', (c) => c.text('m15 r11')) +r15.put('/res12/:id', (c) => c.text('m15 r12')) +r15.delete('/res13/:id', (c) => c.text('m15 r13')) +r15.patch('/res14/:id', (c) => c.text('m15 r14')) +r15.get('/res15/:id', (c) => c.text('m15 r15')) +r15.post('/res16/:id', (c) => c.text('m15 r16')) +r15.put('/res17/:id', (c) => c.text('m15 r17')) +r15.delete('/res18/:id', (c) => c.text('m15 r18')) +r15.patch('/res19/:id', (c) => c.text('m15 r19')) +r15.get('/res20/:id', (c) => c.text('m15 r20')) +r15.post('/res21/:id', (c) => c.text('m15 r21')) +r15.put('/res22/:id', (c) => c.text('m15 r22')) +r15.delete('/res23/:id', (c) => c.text('m15 r23')) +r15.patch('/res24/:id', (c) => c.text('m15 r24')) +r15.get('/res25/:id', (c) => c.text('m15 r25')) +r15.post('/res26/:id', (c) => c.text('m15 r26')) +r15.put('/res27/:id', (c) => c.text('m15 r27')) +r15.delete('/res28/:id', (c) => c.text('m15 r28')) +r15.patch('/res29/:id', (c) => c.text('m15 r29')) +r15.get('/res30/:id', (c) => c.text('m15 r30')) +r15.post('/res31/:id', (c) => c.text('m15 r31')) +r15.put('/res32/:id', (c) => c.text('m15 r32')) +r15.delete('/res33/:id', (c) => c.text('m15 r33')) +r15.patch('/res34/:id', (c) => c.text('m15 r34')) +r15.get('/res35/:id', (c) => c.text('m15 r35')) +r15.post('/res36/:id', (c) => c.text('m15 r36')) +r15.put('/res37/:id', (c) => c.text('m15 r37')) +r15.delete('/res38/:id', (c) => c.text('m15 r38')) +r15.patch('/res39/:id', (c) => c.text('m15 r39')) +r15.get('/res40/:id', (c) => c.text('m15 r40')) +r15.post('/res41/:id', (c) => c.text('m15 r41')) +r15.put('/res42/:id', (c) => c.text('m15 r42')) +r15.delete('/res43/:id', (c) => c.text('m15 r43')) +r15.patch('/res44/:id', (c) => c.text('m15 r44')) +r15.get('/res45/:id', (c) => c.text('m15 r45')) +r15.post('/res46/:id', (c) => c.text('m15 r46')) +r15.put('/res47/:id', (c) => c.text('m15 r47')) +r15.delete('/res48/:id', (c) => c.text('m15 r48')) +r15.patch('/res49/:id', (c) => c.text('m15 r49')) +r15.get('/res50/:id', (c) => c.text('m15 r50')) +r15.post('/res51/:id', (c) => c.text('m15 r51')) +r15.put('/res52/:id', (c) => c.text('m15 r52')) +r15.delete('/res53/:id', (c) => c.text('m15 r53')) +r15.patch('/res54/:id', (c) => c.text('m15 r54')) +r15.get('/res55/:id', (c) => c.text('m15 r55')) +r15.post('/res56/:id', (c) => c.text('m15 r56')) +r15.put('/res57/:id', (c) => c.text('m15 r57')) +r15.delete('/res58/:id', (c) => c.text('m15 r58')) +r15.patch('/res59/:id', (c) => c.text('m15 r59')) +r15.get('/res60/:id', (c) => c.text('m15 r60')) +r15.post('/res61/:id', (c) => c.text('m15 r61')) +r15.put('/res62/:id', (c) => c.text('m15 r62')) +r15.delete('/res63/:id', (c) => c.text('m15 r63')) +r15.patch('/res64/:id', (c) => c.text('m15 r64')) +r15.get('/res65/:id', (c) => c.text('m15 r65')) +r15.post('/res66/:id', (c) => c.text('m15 r66')) +r15.put('/res67/:id', (c) => c.text('m15 r67')) +r15.delete('/res68/:id', (c) => c.text('m15 r68')) +r15.patch('/res69/:id', (c) => c.text('m15 r69')) +r15.get('/res70/:id', (c) => c.text('m15 r70')) +r15.post('/res71/:id', (c) => c.text('m15 r71')) +r15.put('/res72/:id', (c) => c.text('m15 r72')) +r15.delete('/res73/:id', (c) => c.text('m15 r73')) +r15.patch('/res74/:id', (c) => c.text('m15 r74')) +r15.get('/res75/:id', (c) => c.text('m15 r75')) +r15.post('/res76/:id', (c) => c.text('m15 r76')) +r15.put('/res77/:id', (c) => c.text('m15 r77')) +r15.delete('/res78/:id', (c) => c.text('m15 r78')) +r15.patch('/res79/:id', (c) => c.text('m15 r79')) +r15.get('/res80/:id', (c) => c.text('m15 r80')) +r15.post('/res81/:id', (c) => c.text('m15 r81')) +r15.put('/res82/:id', (c) => c.text('m15 r82')) +r15.delete('/res83/:id', (c) => c.text('m15 r83')) +r15.patch('/res84/:id', (c) => c.text('m15 r84')) +r15.get('/res85/:id', (c) => c.text('m15 r85')) +r15.post('/res86/:id', (c) => c.text('m15 r86')) +r15.put('/res87/:id', (c) => c.text('m15 r87')) +r15.delete('/res88/:id', (c) => c.text('m15 r88')) +r15.patch('/res89/:id', (c) => c.text('m15 r89')) +r15.get('/res90/:id', (c) => c.text('m15 r90')) +r15.post('/res91/:id', (c) => c.text('m15 r91')) +r15.put('/res92/:id', (c) => c.text('m15 r92')) +r15.delete('/res93/:id', (c) => c.text('m15 r93')) +r15.patch('/res94/:id', (c) => c.text('m15 r94')) +r15.get('/res95/:id', (c) => c.text('m15 r95')) +r15.post('/res96/:id', (c) => c.text('m15 r96')) +r15.put('/res97/:id', (c) => c.text('m15 r97')) +r15.delete('/res98/:id', (c) => c.text('m15 r98')) +r15.patch('/res99/:id', (c) => c.text('m15 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod16.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod16.ts new file mode 100644 index 000000000..a3d03cbe5 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod16.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r16 = new Hono() +r16.get('/res0/:id', (c) => c.text('m16 r0')) +r16.post('/res1/:id', (c) => c.text('m16 r1')) +r16.put('/res2/:id', (c) => c.text('m16 r2')) +r16.delete('/res3/:id', (c) => c.text('m16 r3')) +r16.patch('/res4/:id', (c) => c.text('m16 r4')) +r16.get('/res5/:id', (c) => c.text('m16 r5')) +r16.post('/res6/:id', (c) => c.text('m16 r6')) +r16.put('/res7/:id', (c) => c.text('m16 r7')) +r16.delete('/res8/:id', (c) => c.text('m16 r8')) +r16.patch('/res9/:id', (c) => c.text('m16 r9')) +r16.get('/res10/:id', (c) => c.text('m16 r10')) +r16.post('/res11/:id', (c) => c.text('m16 r11')) +r16.put('/res12/:id', (c) => c.text('m16 r12')) +r16.delete('/res13/:id', (c) => c.text('m16 r13')) +r16.patch('/res14/:id', (c) => c.text('m16 r14')) +r16.get('/res15/:id', (c) => c.text('m16 r15')) +r16.post('/res16/:id', (c) => c.text('m16 r16')) +r16.put('/res17/:id', (c) => c.text('m16 r17')) +r16.delete('/res18/:id', (c) => c.text('m16 r18')) +r16.patch('/res19/:id', (c) => c.text('m16 r19')) +r16.get('/res20/:id', (c) => c.text('m16 r20')) +r16.post('/res21/:id', (c) => c.text('m16 r21')) +r16.put('/res22/:id', (c) => c.text('m16 r22')) +r16.delete('/res23/:id', (c) => c.text('m16 r23')) +r16.patch('/res24/:id', (c) => c.text('m16 r24')) +r16.get('/res25/:id', (c) => c.text('m16 r25')) +r16.post('/res26/:id', (c) => c.text('m16 r26')) +r16.put('/res27/:id', (c) => c.text('m16 r27')) +r16.delete('/res28/:id', (c) => c.text('m16 r28')) +r16.patch('/res29/:id', (c) => c.text('m16 r29')) +r16.get('/res30/:id', (c) => c.text('m16 r30')) +r16.post('/res31/:id', (c) => c.text('m16 r31')) +r16.put('/res32/:id', (c) => c.text('m16 r32')) +r16.delete('/res33/:id', (c) => c.text('m16 r33')) +r16.patch('/res34/:id', (c) => c.text('m16 r34')) +r16.get('/res35/:id', (c) => c.text('m16 r35')) +r16.post('/res36/:id', (c) => c.text('m16 r36')) +r16.put('/res37/:id', (c) => c.text('m16 r37')) +r16.delete('/res38/:id', (c) => c.text('m16 r38')) +r16.patch('/res39/:id', (c) => c.text('m16 r39')) +r16.get('/res40/:id', (c) => c.text('m16 r40')) +r16.post('/res41/:id', (c) => c.text('m16 r41')) +r16.put('/res42/:id', (c) => c.text('m16 r42')) +r16.delete('/res43/:id', (c) => c.text('m16 r43')) +r16.patch('/res44/:id', (c) => c.text('m16 r44')) +r16.get('/res45/:id', (c) => c.text('m16 r45')) +r16.post('/res46/:id', (c) => c.text('m16 r46')) +r16.put('/res47/:id', (c) => c.text('m16 r47')) +r16.delete('/res48/:id', (c) => c.text('m16 r48')) +r16.patch('/res49/:id', (c) => c.text('m16 r49')) +r16.get('/res50/:id', (c) => c.text('m16 r50')) +r16.post('/res51/:id', (c) => c.text('m16 r51')) +r16.put('/res52/:id', (c) => c.text('m16 r52')) +r16.delete('/res53/:id', (c) => c.text('m16 r53')) +r16.patch('/res54/:id', (c) => c.text('m16 r54')) +r16.get('/res55/:id', (c) => c.text('m16 r55')) +r16.post('/res56/:id', (c) => c.text('m16 r56')) +r16.put('/res57/:id', (c) => c.text('m16 r57')) +r16.delete('/res58/:id', (c) => c.text('m16 r58')) +r16.patch('/res59/:id', (c) => c.text('m16 r59')) +r16.get('/res60/:id', (c) => c.text('m16 r60')) +r16.post('/res61/:id', (c) => c.text('m16 r61')) +r16.put('/res62/:id', (c) => c.text('m16 r62')) +r16.delete('/res63/:id', (c) => c.text('m16 r63')) +r16.patch('/res64/:id', (c) => c.text('m16 r64')) +r16.get('/res65/:id', (c) => c.text('m16 r65')) +r16.post('/res66/:id', (c) => c.text('m16 r66')) +r16.put('/res67/:id', (c) => c.text('m16 r67')) +r16.delete('/res68/:id', (c) => c.text('m16 r68')) +r16.patch('/res69/:id', (c) => c.text('m16 r69')) +r16.get('/res70/:id', (c) => c.text('m16 r70')) +r16.post('/res71/:id', (c) => c.text('m16 r71')) +r16.put('/res72/:id', (c) => c.text('m16 r72')) +r16.delete('/res73/:id', (c) => c.text('m16 r73')) +r16.patch('/res74/:id', (c) => c.text('m16 r74')) +r16.get('/res75/:id', (c) => c.text('m16 r75')) +r16.post('/res76/:id', (c) => c.text('m16 r76')) +r16.put('/res77/:id', (c) => c.text('m16 r77')) +r16.delete('/res78/:id', (c) => c.text('m16 r78')) +r16.patch('/res79/:id', (c) => c.text('m16 r79')) +r16.get('/res80/:id', (c) => c.text('m16 r80')) +r16.post('/res81/:id', (c) => c.text('m16 r81')) +r16.put('/res82/:id', (c) => c.text('m16 r82')) +r16.delete('/res83/:id', (c) => c.text('m16 r83')) +r16.patch('/res84/:id', (c) => c.text('m16 r84')) +r16.get('/res85/:id', (c) => c.text('m16 r85')) +r16.post('/res86/:id', (c) => c.text('m16 r86')) +r16.put('/res87/:id', (c) => c.text('m16 r87')) +r16.delete('/res88/:id', (c) => c.text('m16 r88')) +r16.patch('/res89/:id', (c) => c.text('m16 r89')) +r16.get('/res90/:id', (c) => c.text('m16 r90')) +r16.post('/res91/:id', (c) => c.text('m16 r91')) +r16.put('/res92/:id', (c) => c.text('m16 r92')) +r16.delete('/res93/:id', (c) => c.text('m16 r93')) +r16.patch('/res94/:id', (c) => c.text('m16 r94')) +r16.get('/res95/:id', (c) => c.text('m16 r95')) +r16.post('/res96/:id', (c) => c.text('m16 r96')) +r16.put('/res97/:id', (c) => c.text('m16 r97')) +r16.delete('/res98/:id', (c) => c.text('m16 r98')) +r16.patch('/res99/:id', (c) => c.text('m16 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod17.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod17.ts new file mode 100644 index 000000000..0925b07ca --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod17.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r17 = new Hono() +r17.get('/res0/:id', (c) => c.text('m17 r0')) +r17.post('/res1/:id', (c) => c.text('m17 r1')) +r17.put('/res2/:id', (c) => c.text('m17 r2')) +r17.delete('/res3/:id', (c) => c.text('m17 r3')) +r17.patch('/res4/:id', (c) => c.text('m17 r4')) +r17.get('/res5/:id', (c) => c.text('m17 r5')) +r17.post('/res6/:id', (c) => c.text('m17 r6')) +r17.put('/res7/:id', (c) => c.text('m17 r7')) +r17.delete('/res8/:id', (c) => c.text('m17 r8')) +r17.patch('/res9/:id', (c) => c.text('m17 r9')) +r17.get('/res10/:id', (c) => c.text('m17 r10')) +r17.post('/res11/:id', (c) => c.text('m17 r11')) +r17.put('/res12/:id', (c) => c.text('m17 r12')) +r17.delete('/res13/:id', (c) => c.text('m17 r13')) +r17.patch('/res14/:id', (c) => c.text('m17 r14')) +r17.get('/res15/:id', (c) => c.text('m17 r15')) +r17.post('/res16/:id', (c) => c.text('m17 r16')) +r17.put('/res17/:id', (c) => c.text('m17 r17')) +r17.delete('/res18/:id', (c) => c.text('m17 r18')) +r17.patch('/res19/:id', (c) => c.text('m17 r19')) +r17.get('/res20/:id', (c) => c.text('m17 r20')) +r17.post('/res21/:id', (c) => c.text('m17 r21')) +r17.put('/res22/:id', (c) => c.text('m17 r22')) +r17.delete('/res23/:id', (c) => c.text('m17 r23')) +r17.patch('/res24/:id', (c) => c.text('m17 r24')) +r17.get('/res25/:id', (c) => c.text('m17 r25')) +r17.post('/res26/:id', (c) => c.text('m17 r26')) +r17.put('/res27/:id', (c) => c.text('m17 r27')) +r17.delete('/res28/:id', (c) => c.text('m17 r28')) +r17.patch('/res29/:id', (c) => c.text('m17 r29')) +r17.get('/res30/:id', (c) => c.text('m17 r30')) +r17.post('/res31/:id', (c) => c.text('m17 r31')) +r17.put('/res32/:id', (c) => c.text('m17 r32')) +r17.delete('/res33/:id', (c) => c.text('m17 r33')) +r17.patch('/res34/:id', (c) => c.text('m17 r34')) +r17.get('/res35/:id', (c) => c.text('m17 r35')) +r17.post('/res36/:id', (c) => c.text('m17 r36')) +r17.put('/res37/:id', (c) => c.text('m17 r37')) +r17.delete('/res38/:id', (c) => c.text('m17 r38')) +r17.patch('/res39/:id', (c) => c.text('m17 r39')) +r17.get('/res40/:id', (c) => c.text('m17 r40')) +r17.post('/res41/:id', (c) => c.text('m17 r41')) +r17.put('/res42/:id', (c) => c.text('m17 r42')) +r17.delete('/res43/:id', (c) => c.text('m17 r43')) +r17.patch('/res44/:id', (c) => c.text('m17 r44')) +r17.get('/res45/:id', (c) => c.text('m17 r45')) +r17.post('/res46/:id', (c) => c.text('m17 r46')) +r17.put('/res47/:id', (c) => c.text('m17 r47')) +r17.delete('/res48/:id', (c) => c.text('m17 r48')) +r17.patch('/res49/:id', (c) => c.text('m17 r49')) +r17.get('/res50/:id', (c) => c.text('m17 r50')) +r17.post('/res51/:id', (c) => c.text('m17 r51')) +r17.put('/res52/:id', (c) => c.text('m17 r52')) +r17.delete('/res53/:id', (c) => c.text('m17 r53')) +r17.patch('/res54/:id', (c) => c.text('m17 r54')) +r17.get('/res55/:id', (c) => c.text('m17 r55')) +r17.post('/res56/:id', (c) => c.text('m17 r56')) +r17.put('/res57/:id', (c) => c.text('m17 r57')) +r17.delete('/res58/:id', (c) => c.text('m17 r58')) +r17.patch('/res59/:id', (c) => c.text('m17 r59')) +r17.get('/res60/:id', (c) => c.text('m17 r60')) +r17.post('/res61/:id', (c) => c.text('m17 r61')) +r17.put('/res62/:id', (c) => c.text('m17 r62')) +r17.delete('/res63/:id', (c) => c.text('m17 r63')) +r17.patch('/res64/:id', (c) => c.text('m17 r64')) +r17.get('/res65/:id', (c) => c.text('m17 r65')) +r17.post('/res66/:id', (c) => c.text('m17 r66')) +r17.put('/res67/:id', (c) => c.text('m17 r67')) +r17.delete('/res68/:id', (c) => c.text('m17 r68')) +r17.patch('/res69/:id', (c) => c.text('m17 r69')) +r17.get('/res70/:id', (c) => c.text('m17 r70')) +r17.post('/res71/:id', (c) => c.text('m17 r71')) +r17.put('/res72/:id', (c) => c.text('m17 r72')) +r17.delete('/res73/:id', (c) => c.text('m17 r73')) +r17.patch('/res74/:id', (c) => c.text('m17 r74')) +r17.get('/res75/:id', (c) => c.text('m17 r75')) +r17.post('/res76/:id', (c) => c.text('m17 r76')) +r17.put('/res77/:id', (c) => c.text('m17 r77')) +r17.delete('/res78/:id', (c) => c.text('m17 r78')) +r17.patch('/res79/:id', (c) => c.text('m17 r79')) +r17.get('/res80/:id', (c) => c.text('m17 r80')) +r17.post('/res81/:id', (c) => c.text('m17 r81')) +r17.put('/res82/:id', (c) => c.text('m17 r82')) +r17.delete('/res83/:id', (c) => c.text('m17 r83')) +r17.patch('/res84/:id', (c) => c.text('m17 r84')) +r17.get('/res85/:id', (c) => c.text('m17 r85')) +r17.post('/res86/:id', (c) => c.text('m17 r86')) +r17.put('/res87/:id', (c) => c.text('m17 r87')) +r17.delete('/res88/:id', (c) => c.text('m17 r88')) +r17.patch('/res89/:id', (c) => c.text('m17 r89')) +r17.get('/res90/:id', (c) => c.text('m17 r90')) +r17.post('/res91/:id', (c) => c.text('m17 r91')) +r17.put('/res92/:id', (c) => c.text('m17 r92')) +r17.delete('/res93/:id', (c) => c.text('m17 r93')) +r17.patch('/res94/:id', (c) => c.text('m17 r94')) +r17.get('/res95/:id', (c) => c.text('m17 r95')) +r17.post('/res96/:id', (c) => c.text('m17 r96')) +r17.put('/res97/:id', (c) => c.text('m17 r97')) +r17.delete('/res98/:id', (c) => c.text('m17 r98')) +r17.patch('/res99/:id', (c) => c.text('m17 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod18.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod18.ts new file mode 100644 index 000000000..a5feedcbb --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod18.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r18 = new Hono() +r18.get('/res0/:id', (c) => c.text('m18 r0')) +r18.post('/res1/:id', (c) => c.text('m18 r1')) +r18.put('/res2/:id', (c) => c.text('m18 r2')) +r18.delete('/res3/:id', (c) => c.text('m18 r3')) +r18.patch('/res4/:id', (c) => c.text('m18 r4')) +r18.get('/res5/:id', (c) => c.text('m18 r5')) +r18.post('/res6/:id', (c) => c.text('m18 r6')) +r18.put('/res7/:id', (c) => c.text('m18 r7')) +r18.delete('/res8/:id', (c) => c.text('m18 r8')) +r18.patch('/res9/:id', (c) => c.text('m18 r9')) +r18.get('/res10/:id', (c) => c.text('m18 r10')) +r18.post('/res11/:id', (c) => c.text('m18 r11')) +r18.put('/res12/:id', (c) => c.text('m18 r12')) +r18.delete('/res13/:id', (c) => c.text('m18 r13')) +r18.patch('/res14/:id', (c) => c.text('m18 r14')) +r18.get('/res15/:id', (c) => c.text('m18 r15')) +r18.post('/res16/:id', (c) => c.text('m18 r16')) +r18.put('/res17/:id', (c) => c.text('m18 r17')) +r18.delete('/res18/:id', (c) => c.text('m18 r18')) +r18.patch('/res19/:id', (c) => c.text('m18 r19')) +r18.get('/res20/:id', (c) => c.text('m18 r20')) +r18.post('/res21/:id', (c) => c.text('m18 r21')) +r18.put('/res22/:id', (c) => c.text('m18 r22')) +r18.delete('/res23/:id', (c) => c.text('m18 r23')) +r18.patch('/res24/:id', (c) => c.text('m18 r24')) +r18.get('/res25/:id', (c) => c.text('m18 r25')) +r18.post('/res26/:id', (c) => c.text('m18 r26')) +r18.put('/res27/:id', (c) => c.text('m18 r27')) +r18.delete('/res28/:id', (c) => c.text('m18 r28')) +r18.patch('/res29/:id', (c) => c.text('m18 r29')) +r18.get('/res30/:id', (c) => c.text('m18 r30')) +r18.post('/res31/:id', (c) => c.text('m18 r31')) +r18.put('/res32/:id', (c) => c.text('m18 r32')) +r18.delete('/res33/:id', (c) => c.text('m18 r33')) +r18.patch('/res34/:id', (c) => c.text('m18 r34')) +r18.get('/res35/:id', (c) => c.text('m18 r35')) +r18.post('/res36/:id', (c) => c.text('m18 r36')) +r18.put('/res37/:id', (c) => c.text('m18 r37')) +r18.delete('/res38/:id', (c) => c.text('m18 r38')) +r18.patch('/res39/:id', (c) => c.text('m18 r39')) +r18.get('/res40/:id', (c) => c.text('m18 r40')) +r18.post('/res41/:id', (c) => c.text('m18 r41')) +r18.put('/res42/:id', (c) => c.text('m18 r42')) +r18.delete('/res43/:id', (c) => c.text('m18 r43')) +r18.patch('/res44/:id', (c) => c.text('m18 r44')) +r18.get('/res45/:id', (c) => c.text('m18 r45')) +r18.post('/res46/:id', (c) => c.text('m18 r46')) +r18.put('/res47/:id', (c) => c.text('m18 r47')) +r18.delete('/res48/:id', (c) => c.text('m18 r48')) +r18.patch('/res49/:id', (c) => c.text('m18 r49')) +r18.get('/res50/:id', (c) => c.text('m18 r50')) +r18.post('/res51/:id', (c) => c.text('m18 r51')) +r18.put('/res52/:id', (c) => c.text('m18 r52')) +r18.delete('/res53/:id', (c) => c.text('m18 r53')) +r18.patch('/res54/:id', (c) => c.text('m18 r54')) +r18.get('/res55/:id', (c) => c.text('m18 r55')) +r18.post('/res56/:id', (c) => c.text('m18 r56')) +r18.put('/res57/:id', (c) => c.text('m18 r57')) +r18.delete('/res58/:id', (c) => c.text('m18 r58')) +r18.patch('/res59/:id', (c) => c.text('m18 r59')) +r18.get('/res60/:id', (c) => c.text('m18 r60')) +r18.post('/res61/:id', (c) => c.text('m18 r61')) +r18.put('/res62/:id', (c) => c.text('m18 r62')) +r18.delete('/res63/:id', (c) => c.text('m18 r63')) +r18.patch('/res64/:id', (c) => c.text('m18 r64')) +r18.get('/res65/:id', (c) => c.text('m18 r65')) +r18.post('/res66/:id', (c) => c.text('m18 r66')) +r18.put('/res67/:id', (c) => c.text('m18 r67')) +r18.delete('/res68/:id', (c) => c.text('m18 r68')) +r18.patch('/res69/:id', (c) => c.text('m18 r69')) +r18.get('/res70/:id', (c) => c.text('m18 r70')) +r18.post('/res71/:id', (c) => c.text('m18 r71')) +r18.put('/res72/:id', (c) => c.text('m18 r72')) +r18.delete('/res73/:id', (c) => c.text('m18 r73')) +r18.patch('/res74/:id', (c) => c.text('m18 r74')) +r18.get('/res75/:id', (c) => c.text('m18 r75')) +r18.post('/res76/:id', (c) => c.text('m18 r76')) +r18.put('/res77/:id', (c) => c.text('m18 r77')) +r18.delete('/res78/:id', (c) => c.text('m18 r78')) +r18.patch('/res79/:id', (c) => c.text('m18 r79')) +r18.get('/res80/:id', (c) => c.text('m18 r80')) +r18.post('/res81/:id', (c) => c.text('m18 r81')) +r18.put('/res82/:id', (c) => c.text('m18 r82')) +r18.delete('/res83/:id', (c) => c.text('m18 r83')) +r18.patch('/res84/:id', (c) => c.text('m18 r84')) +r18.get('/res85/:id', (c) => c.text('m18 r85')) +r18.post('/res86/:id', (c) => c.text('m18 r86')) +r18.put('/res87/:id', (c) => c.text('m18 r87')) +r18.delete('/res88/:id', (c) => c.text('m18 r88')) +r18.patch('/res89/:id', (c) => c.text('m18 r89')) +r18.get('/res90/:id', (c) => c.text('m18 r90')) +r18.post('/res91/:id', (c) => c.text('m18 r91')) +r18.put('/res92/:id', (c) => c.text('m18 r92')) +r18.delete('/res93/:id', (c) => c.text('m18 r93')) +r18.patch('/res94/:id', (c) => c.text('m18 r94')) +r18.get('/res95/:id', (c) => c.text('m18 r95')) +r18.post('/res96/:id', (c) => c.text('m18 r96')) +r18.put('/res97/:id', (c) => c.text('m18 r97')) +r18.delete('/res98/:id', (c) => c.text('m18 r98')) +r18.patch('/res99/:id', (c) => c.text('m18 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod19.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod19.ts new file mode 100644 index 000000000..e0b28ede7 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod19.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r19 = new Hono() +r19.get('/res0/:id', (c) => c.text('m19 r0')) +r19.post('/res1/:id', (c) => c.text('m19 r1')) +r19.put('/res2/:id', (c) => c.text('m19 r2')) +r19.delete('/res3/:id', (c) => c.text('m19 r3')) +r19.patch('/res4/:id', (c) => c.text('m19 r4')) +r19.get('/res5/:id', (c) => c.text('m19 r5')) +r19.post('/res6/:id', (c) => c.text('m19 r6')) +r19.put('/res7/:id', (c) => c.text('m19 r7')) +r19.delete('/res8/:id', (c) => c.text('m19 r8')) +r19.patch('/res9/:id', (c) => c.text('m19 r9')) +r19.get('/res10/:id', (c) => c.text('m19 r10')) +r19.post('/res11/:id', (c) => c.text('m19 r11')) +r19.put('/res12/:id', (c) => c.text('m19 r12')) +r19.delete('/res13/:id', (c) => c.text('m19 r13')) +r19.patch('/res14/:id', (c) => c.text('m19 r14')) +r19.get('/res15/:id', (c) => c.text('m19 r15')) +r19.post('/res16/:id', (c) => c.text('m19 r16')) +r19.put('/res17/:id', (c) => c.text('m19 r17')) +r19.delete('/res18/:id', (c) => c.text('m19 r18')) +r19.patch('/res19/:id', (c) => c.text('m19 r19')) +r19.get('/res20/:id', (c) => c.text('m19 r20')) +r19.post('/res21/:id', (c) => c.text('m19 r21')) +r19.put('/res22/:id', (c) => c.text('m19 r22')) +r19.delete('/res23/:id', (c) => c.text('m19 r23')) +r19.patch('/res24/:id', (c) => c.text('m19 r24')) +r19.get('/res25/:id', (c) => c.text('m19 r25')) +r19.post('/res26/:id', (c) => c.text('m19 r26')) +r19.put('/res27/:id', (c) => c.text('m19 r27')) +r19.delete('/res28/:id', (c) => c.text('m19 r28')) +r19.patch('/res29/:id', (c) => c.text('m19 r29')) +r19.get('/res30/:id', (c) => c.text('m19 r30')) +r19.post('/res31/:id', (c) => c.text('m19 r31')) +r19.put('/res32/:id', (c) => c.text('m19 r32')) +r19.delete('/res33/:id', (c) => c.text('m19 r33')) +r19.patch('/res34/:id', (c) => c.text('m19 r34')) +r19.get('/res35/:id', (c) => c.text('m19 r35')) +r19.post('/res36/:id', (c) => c.text('m19 r36')) +r19.put('/res37/:id', (c) => c.text('m19 r37')) +r19.delete('/res38/:id', (c) => c.text('m19 r38')) +r19.patch('/res39/:id', (c) => c.text('m19 r39')) +r19.get('/res40/:id', (c) => c.text('m19 r40')) +r19.post('/res41/:id', (c) => c.text('m19 r41')) +r19.put('/res42/:id', (c) => c.text('m19 r42')) +r19.delete('/res43/:id', (c) => c.text('m19 r43')) +r19.patch('/res44/:id', (c) => c.text('m19 r44')) +r19.get('/res45/:id', (c) => c.text('m19 r45')) +r19.post('/res46/:id', (c) => c.text('m19 r46')) +r19.put('/res47/:id', (c) => c.text('m19 r47')) +r19.delete('/res48/:id', (c) => c.text('m19 r48')) +r19.patch('/res49/:id', (c) => c.text('m19 r49')) +r19.get('/res50/:id', (c) => c.text('m19 r50')) +r19.post('/res51/:id', (c) => c.text('m19 r51')) +r19.put('/res52/:id', (c) => c.text('m19 r52')) +r19.delete('/res53/:id', (c) => c.text('m19 r53')) +r19.patch('/res54/:id', (c) => c.text('m19 r54')) +r19.get('/res55/:id', (c) => c.text('m19 r55')) +r19.post('/res56/:id', (c) => c.text('m19 r56')) +r19.put('/res57/:id', (c) => c.text('m19 r57')) +r19.delete('/res58/:id', (c) => c.text('m19 r58')) +r19.patch('/res59/:id', (c) => c.text('m19 r59')) +r19.get('/res60/:id', (c) => c.text('m19 r60')) +r19.post('/res61/:id', (c) => c.text('m19 r61')) +r19.put('/res62/:id', (c) => c.text('m19 r62')) +r19.delete('/res63/:id', (c) => c.text('m19 r63')) +r19.patch('/res64/:id', (c) => c.text('m19 r64')) +r19.get('/res65/:id', (c) => c.text('m19 r65')) +r19.post('/res66/:id', (c) => c.text('m19 r66')) +r19.put('/res67/:id', (c) => c.text('m19 r67')) +r19.delete('/res68/:id', (c) => c.text('m19 r68')) +r19.patch('/res69/:id', (c) => c.text('m19 r69')) +r19.get('/res70/:id', (c) => c.text('m19 r70')) +r19.post('/res71/:id', (c) => c.text('m19 r71')) +r19.put('/res72/:id', (c) => c.text('m19 r72')) +r19.delete('/res73/:id', (c) => c.text('m19 r73')) +r19.patch('/res74/:id', (c) => c.text('m19 r74')) +r19.get('/res75/:id', (c) => c.text('m19 r75')) +r19.post('/res76/:id', (c) => c.text('m19 r76')) +r19.put('/res77/:id', (c) => c.text('m19 r77')) +r19.delete('/res78/:id', (c) => c.text('m19 r78')) +r19.patch('/res79/:id', (c) => c.text('m19 r79')) +r19.get('/res80/:id', (c) => c.text('m19 r80')) +r19.post('/res81/:id', (c) => c.text('m19 r81')) +r19.put('/res82/:id', (c) => c.text('m19 r82')) +r19.delete('/res83/:id', (c) => c.text('m19 r83')) +r19.patch('/res84/:id', (c) => c.text('m19 r84')) +r19.get('/res85/:id', (c) => c.text('m19 r85')) +r19.post('/res86/:id', (c) => c.text('m19 r86')) +r19.put('/res87/:id', (c) => c.text('m19 r87')) +r19.delete('/res88/:id', (c) => c.text('m19 r88')) +r19.patch('/res89/:id', (c) => c.text('m19 r89')) +r19.get('/res90/:id', (c) => c.text('m19 r90')) +r19.post('/res91/:id', (c) => c.text('m19 r91')) +r19.put('/res92/:id', (c) => c.text('m19 r92')) +r19.delete('/res93/:id', (c) => c.text('m19 r93')) +r19.patch('/res94/:id', (c) => c.text('m19 r94')) +r19.get('/res95/:id', (c) => c.text('m19 r95')) +r19.post('/res96/:id', (c) => c.text('m19 r96')) +r19.put('/res97/:id', (c) => c.text('m19 r97')) +r19.delete('/res98/:id', (c) => c.text('m19 r98')) +r19.patch('/res99/:id', (c) => c.text('m19 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod2.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod2.ts new file mode 100644 index 000000000..a01320714 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod2.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r2 = new Hono() +r2.get('/res0/:id', (c) => c.text('m2 r0')) +r2.post('/res1/:id', (c) => c.text('m2 r1')) +r2.put('/res2/:id', (c) => c.text('m2 r2')) +r2.delete('/res3/:id', (c) => c.text('m2 r3')) +r2.patch('/res4/:id', (c) => c.text('m2 r4')) +r2.get('/res5/:id', (c) => c.text('m2 r5')) +r2.post('/res6/:id', (c) => c.text('m2 r6')) +r2.put('/res7/:id', (c) => c.text('m2 r7')) +r2.delete('/res8/:id', (c) => c.text('m2 r8')) +r2.patch('/res9/:id', (c) => c.text('m2 r9')) +r2.get('/res10/:id', (c) => c.text('m2 r10')) +r2.post('/res11/:id', (c) => c.text('m2 r11')) +r2.put('/res12/:id', (c) => c.text('m2 r12')) +r2.delete('/res13/:id', (c) => c.text('m2 r13')) +r2.patch('/res14/:id', (c) => c.text('m2 r14')) +r2.get('/res15/:id', (c) => c.text('m2 r15')) +r2.post('/res16/:id', (c) => c.text('m2 r16')) +r2.put('/res17/:id', (c) => c.text('m2 r17')) +r2.delete('/res18/:id', (c) => c.text('m2 r18')) +r2.patch('/res19/:id', (c) => c.text('m2 r19')) +r2.get('/res20/:id', (c) => c.text('m2 r20')) +r2.post('/res21/:id', (c) => c.text('m2 r21')) +r2.put('/res22/:id', (c) => c.text('m2 r22')) +r2.delete('/res23/:id', (c) => c.text('m2 r23')) +r2.patch('/res24/:id', (c) => c.text('m2 r24')) +r2.get('/res25/:id', (c) => c.text('m2 r25')) +r2.post('/res26/:id', (c) => c.text('m2 r26')) +r2.put('/res27/:id', (c) => c.text('m2 r27')) +r2.delete('/res28/:id', (c) => c.text('m2 r28')) +r2.patch('/res29/:id', (c) => c.text('m2 r29')) +r2.get('/res30/:id', (c) => c.text('m2 r30')) +r2.post('/res31/:id', (c) => c.text('m2 r31')) +r2.put('/res32/:id', (c) => c.text('m2 r32')) +r2.delete('/res33/:id', (c) => c.text('m2 r33')) +r2.patch('/res34/:id', (c) => c.text('m2 r34')) +r2.get('/res35/:id', (c) => c.text('m2 r35')) +r2.post('/res36/:id', (c) => c.text('m2 r36')) +r2.put('/res37/:id', (c) => c.text('m2 r37')) +r2.delete('/res38/:id', (c) => c.text('m2 r38')) +r2.patch('/res39/:id', (c) => c.text('m2 r39')) +r2.get('/res40/:id', (c) => c.text('m2 r40')) +r2.post('/res41/:id', (c) => c.text('m2 r41')) +r2.put('/res42/:id', (c) => c.text('m2 r42')) +r2.delete('/res43/:id', (c) => c.text('m2 r43')) +r2.patch('/res44/:id', (c) => c.text('m2 r44')) +r2.get('/res45/:id', (c) => c.text('m2 r45')) +r2.post('/res46/:id', (c) => c.text('m2 r46')) +r2.put('/res47/:id', (c) => c.text('m2 r47')) +r2.delete('/res48/:id', (c) => c.text('m2 r48')) +r2.patch('/res49/:id', (c) => c.text('m2 r49')) +r2.get('/res50/:id', (c) => c.text('m2 r50')) +r2.post('/res51/:id', (c) => c.text('m2 r51')) +r2.put('/res52/:id', (c) => c.text('m2 r52')) +r2.delete('/res53/:id', (c) => c.text('m2 r53')) +r2.patch('/res54/:id', (c) => c.text('m2 r54')) +r2.get('/res55/:id', (c) => c.text('m2 r55')) +r2.post('/res56/:id', (c) => c.text('m2 r56')) +r2.put('/res57/:id', (c) => c.text('m2 r57')) +r2.delete('/res58/:id', (c) => c.text('m2 r58')) +r2.patch('/res59/:id', (c) => c.text('m2 r59')) +r2.get('/res60/:id', (c) => c.text('m2 r60')) +r2.post('/res61/:id', (c) => c.text('m2 r61')) +r2.put('/res62/:id', (c) => c.text('m2 r62')) +r2.delete('/res63/:id', (c) => c.text('m2 r63')) +r2.patch('/res64/:id', (c) => c.text('m2 r64')) +r2.get('/res65/:id', (c) => c.text('m2 r65')) +r2.post('/res66/:id', (c) => c.text('m2 r66')) +r2.put('/res67/:id', (c) => c.text('m2 r67')) +r2.delete('/res68/:id', (c) => c.text('m2 r68')) +r2.patch('/res69/:id', (c) => c.text('m2 r69')) +r2.get('/res70/:id', (c) => c.text('m2 r70')) +r2.post('/res71/:id', (c) => c.text('m2 r71')) +r2.put('/res72/:id', (c) => c.text('m2 r72')) +r2.delete('/res73/:id', (c) => c.text('m2 r73')) +r2.patch('/res74/:id', (c) => c.text('m2 r74')) +r2.get('/res75/:id', (c) => c.text('m2 r75')) +r2.post('/res76/:id', (c) => c.text('m2 r76')) +r2.put('/res77/:id', (c) => c.text('m2 r77')) +r2.delete('/res78/:id', (c) => c.text('m2 r78')) +r2.patch('/res79/:id', (c) => c.text('m2 r79')) +r2.get('/res80/:id', (c) => c.text('m2 r80')) +r2.post('/res81/:id', (c) => c.text('m2 r81')) +r2.put('/res82/:id', (c) => c.text('m2 r82')) +r2.delete('/res83/:id', (c) => c.text('m2 r83')) +r2.patch('/res84/:id', (c) => c.text('m2 r84')) +r2.get('/res85/:id', (c) => c.text('m2 r85')) +r2.post('/res86/:id', (c) => c.text('m2 r86')) +r2.put('/res87/:id', (c) => c.text('m2 r87')) +r2.delete('/res88/:id', (c) => c.text('m2 r88')) +r2.patch('/res89/:id', (c) => c.text('m2 r89')) +r2.get('/res90/:id', (c) => c.text('m2 r90')) +r2.post('/res91/:id', (c) => c.text('m2 r91')) +r2.put('/res92/:id', (c) => c.text('m2 r92')) +r2.delete('/res93/:id', (c) => c.text('m2 r93')) +r2.patch('/res94/:id', (c) => c.text('m2 r94')) +r2.get('/res95/:id', (c) => c.text('m2 r95')) +r2.post('/res96/:id', (c) => c.text('m2 r96')) +r2.put('/res97/:id', (c) => c.text('m2 r97')) +r2.delete('/res98/:id', (c) => c.text('m2 r98')) +r2.patch('/res99/:id', (c) => c.text('m2 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod3.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod3.ts new file mode 100644 index 000000000..f5acf18e5 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod3.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r3 = new Hono() +r3.get('/res0/:id', (c) => c.text('m3 r0')) +r3.post('/res1/:id', (c) => c.text('m3 r1')) +r3.put('/res2/:id', (c) => c.text('m3 r2')) +r3.delete('/res3/:id', (c) => c.text('m3 r3')) +r3.patch('/res4/:id', (c) => c.text('m3 r4')) +r3.get('/res5/:id', (c) => c.text('m3 r5')) +r3.post('/res6/:id', (c) => c.text('m3 r6')) +r3.put('/res7/:id', (c) => c.text('m3 r7')) +r3.delete('/res8/:id', (c) => c.text('m3 r8')) +r3.patch('/res9/:id', (c) => c.text('m3 r9')) +r3.get('/res10/:id', (c) => c.text('m3 r10')) +r3.post('/res11/:id', (c) => c.text('m3 r11')) +r3.put('/res12/:id', (c) => c.text('m3 r12')) +r3.delete('/res13/:id', (c) => c.text('m3 r13')) +r3.patch('/res14/:id', (c) => c.text('m3 r14')) +r3.get('/res15/:id', (c) => c.text('m3 r15')) +r3.post('/res16/:id', (c) => c.text('m3 r16')) +r3.put('/res17/:id', (c) => c.text('m3 r17')) +r3.delete('/res18/:id', (c) => c.text('m3 r18')) +r3.patch('/res19/:id', (c) => c.text('m3 r19')) +r3.get('/res20/:id', (c) => c.text('m3 r20')) +r3.post('/res21/:id', (c) => c.text('m3 r21')) +r3.put('/res22/:id', (c) => c.text('m3 r22')) +r3.delete('/res23/:id', (c) => c.text('m3 r23')) +r3.patch('/res24/:id', (c) => c.text('m3 r24')) +r3.get('/res25/:id', (c) => c.text('m3 r25')) +r3.post('/res26/:id', (c) => c.text('m3 r26')) +r3.put('/res27/:id', (c) => c.text('m3 r27')) +r3.delete('/res28/:id', (c) => c.text('m3 r28')) +r3.patch('/res29/:id', (c) => c.text('m3 r29')) +r3.get('/res30/:id', (c) => c.text('m3 r30')) +r3.post('/res31/:id', (c) => c.text('m3 r31')) +r3.put('/res32/:id', (c) => c.text('m3 r32')) +r3.delete('/res33/:id', (c) => c.text('m3 r33')) +r3.patch('/res34/:id', (c) => c.text('m3 r34')) +r3.get('/res35/:id', (c) => c.text('m3 r35')) +r3.post('/res36/:id', (c) => c.text('m3 r36')) +r3.put('/res37/:id', (c) => c.text('m3 r37')) +r3.delete('/res38/:id', (c) => c.text('m3 r38')) +r3.patch('/res39/:id', (c) => c.text('m3 r39')) +r3.get('/res40/:id', (c) => c.text('m3 r40')) +r3.post('/res41/:id', (c) => c.text('m3 r41')) +r3.put('/res42/:id', (c) => c.text('m3 r42')) +r3.delete('/res43/:id', (c) => c.text('m3 r43')) +r3.patch('/res44/:id', (c) => c.text('m3 r44')) +r3.get('/res45/:id', (c) => c.text('m3 r45')) +r3.post('/res46/:id', (c) => c.text('m3 r46')) +r3.put('/res47/:id', (c) => c.text('m3 r47')) +r3.delete('/res48/:id', (c) => c.text('m3 r48')) +r3.patch('/res49/:id', (c) => c.text('m3 r49')) +r3.get('/res50/:id', (c) => c.text('m3 r50')) +r3.post('/res51/:id', (c) => c.text('m3 r51')) +r3.put('/res52/:id', (c) => c.text('m3 r52')) +r3.delete('/res53/:id', (c) => c.text('m3 r53')) +r3.patch('/res54/:id', (c) => c.text('m3 r54')) +r3.get('/res55/:id', (c) => c.text('m3 r55')) +r3.post('/res56/:id', (c) => c.text('m3 r56')) +r3.put('/res57/:id', (c) => c.text('m3 r57')) +r3.delete('/res58/:id', (c) => c.text('m3 r58')) +r3.patch('/res59/:id', (c) => c.text('m3 r59')) +r3.get('/res60/:id', (c) => c.text('m3 r60')) +r3.post('/res61/:id', (c) => c.text('m3 r61')) +r3.put('/res62/:id', (c) => c.text('m3 r62')) +r3.delete('/res63/:id', (c) => c.text('m3 r63')) +r3.patch('/res64/:id', (c) => c.text('m3 r64')) +r3.get('/res65/:id', (c) => c.text('m3 r65')) +r3.post('/res66/:id', (c) => c.text('m3 r66')) +r3.put('/res67/:id', (c) => c.text('m3 r67')) +r3.delete('/res68/:id', (c) => c.text('m3 r68')) +r3.patch('/res69/:id', (c) => c.text('m3 r69')) +r3.get('/res70/:id', (c) => c.text('m3 r70')) +r3.post('/res71/:id', (c) => c.text('m3 r71')) +r3.put('/res72/:id', (c) => c.text('m3 r72')) +r3.delete('/res73/:id', (c) => c.text('m3 r73')) +r3.patch('/res74/:id', (c) => c.text('m3 r74')) +r3.get('/res75/:id', (c) => c.text('m3 r75')) +r3.post('/res76/:id', (c) => c.text('m3 r76')) +r3.put('/res77/:id', (c) => c.text('m3 r77')) +r3.delete('/res78/:id', (c) => c.text('m3 r78')) +r3.patch('/res79/:id', (c) => c.text('m3 r79')) +r3.get('/res80/:id', (c) => c.text('m3 r80')) +r3.post('/res81/:id', (c) => c.text('m3 r81')) +r3.put('/res82/:id', (c) => c.text('m3 r82')) +r3.delete('/res83/:id', (c) => c.text('m3 r83')) +r3.patch('/res84/:id', (c) => c.text('m3 r84')) +r3.get('/res85/:id', (c) => c.text('m3 r85')) +r3.post('/res86/:id', (c) => c.text('m3 r86')) +r3.put('/res87/:id', (c) => c.text('m3 r87')) +r3.delete('/res88/:id', (c) => c.text('m3 r88')) +r3.patch('/res89/:id', (c) => c.text('m3 r89')) +r3.get('/res90/:id', (c) => c.text('m3 r90')) +r3.post('/res91/:id', (c) => c.text('m3 r91')) +r3.put('/res92/:id', (c) => c.text('m3 r92')) +r3.delete('/res93/:id', (c) => c.text('m3 r93')) +r3.patch('/res94/:id', (c) => c.text('m3 r94')) +r3.get('/res95/:id', (c) => c.text('m3 r95')) +r3.post('/res96/:id', (c) => c.text('m3 r96')) +r3.put('/res97/:id', (c) => c.text('m3 r97')) +r3.delete('/res98/:id', (c) => c.text('m3 r98')) +r3.patch('/res99/:id', (c) => c.text('m3 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod4.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod4.ts new file mode 100644 index 000000000..bcda49887 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod4.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r4 = new Hono() +r4.get('/res0/:id', (c) => c.text('m4 r0')) +r4.post('/res1/:id', (c) => c.text('m4 r1')) +r4.put('/res2/:id', (c) => c.text('m4 r2')) +r4.delete('/res3/:id', (c) => c.text('m4 r3')) +r4.patch('/res4/:id', (c) => c.text('m4 r4')) +r4.get('/res5/:id', (c) => c.text('m4 r5')) +r4.post('/res6/:id', (c) => c.text('m4 r6')) +r4.put('/res7/:id', (c) => c.text('m4 r7')) +r4.delete('/res8/:id', (c) => c.text('m4 r8')) +r4.patch('/res9/:id', (c) => c.text('m4 r9')) +r4.get('/res10/:id', (c) => c.text('m4 r10')) +r4.post('/res11/:id', (c) => c.text('m4 r11')) +r4.put('/res12/:id', (c) => c.text('m4 r12')) +r4.delete('/res13/:id', (c) => c.text('m4 r13')) +r4.patch('/res14/:id', (c) => c.text('m4 r14')) +r4.get('/res15/:id', (c) => c.text('m4 r15')) +r4.post('/res16/:id', (c) => c.text('m4 r16')) +r4.put('/res17/:id', (c) => c.text('m4 r17')) +r4.delete('/res18/:id', (c) => c.text('m4 r18')) +r4.patch('/res19/:id', (c) => c.text('m4 r19')) +r4.get('/res20/:id', (c) => c.text('m4 r20')) +r4.post('/res21/:id', (c) => c.text('m4 r21')) +r4.put('/res22/:id', (c) => c.text('m4 r22')) +r4.delete('/res23/:id', (c) => c.text('m4 r23')) +r4.patch('/res24/:id', (c) => c.text('m4 r24')) +r4.get('/res25/:id', (c) => c.text('m4 r25')) +r4.post('/res26/:id', (c) => c.text('m4 r26')) +r4.put('/res27/:id', (c) => c.text('m4 r27')) +r4.delete('/res28/:id', (c) => c.text('m4 r28')) +r4.patch('/res29/:id', (c) => c.text('m4 r29')) +r4.get('/res30/:id', (c) => c.text('m4 r30')) +r4.post('/res31/:id', (c) => c.text('m4 r31')) +r4.put('/res32/:id', (c) => c.text('m4 r32')) +r4.delete('/res33/:id', (c) => c.text('m4 r33')) +r4.patch('/res34/:id', (c) => c.text('m4 r34')) +r4.get('/res35/:id', (c) => c.text('m4 r35')) +r4.post('/res36/:id', (c) => c.text('m4 r36')) +r4.put('/res37/:id', (c) => c.text('m4 r37')) +r4.delete('/res38/:id', (c) => c.text('m4 r38')) +r4.patch('/res39/:id', (c) => c.text('m4 r39')) +r4.get('/res40/:id', (c) => c.text('m4 r40')) +r4.post('/res41/:id', (c) => c.text('m4 r41')) +r4.put('/res42/:id', (c) => c.text('m4 r42')) +r4.delete('/res43/:id', (c) => c.text('m4 r43')) +r4.patch('/res44/:id', (c) => c.text('m4 r44')) +r4.get('/res45/:id', (c) => c.text('m4 r45')) +r4.post('/res46/:id', (c) => c.text('m4 r46')) +r4.put('/res47/:id', (c) => c.text('m4 r47')) +r4.delete('/res48/:id', (c) => c.text('m4 r48')) +r4.patch('/res49/:id', (c) => c.text('m4 r49')) +r4.get('/res50/:id', (c) => c.text('m4 r50')) +r4.post('/res51/:id', (c) => c.text('m4 r51')) +r4.put('/res52/:id', (c) => c.text('m4 r52')) +r4.delete('/res53/:id', (c) => c.text('m4 r53')) +r4.patch('/res54/:id', (c) => c.text('m4 r54')) +r4.get('/res55/:id', (c) => c.text('m4 r55')) +r4.post('/res56/:id', (c) => c.text('m4 r56')) +r4.put('/res57/:id', (c) => c.text('m4 r57')) +r4.delete('/res58/:id', (c) => c.text('m4 r58')) +r4.patch('/res59/:id', (c) => c.text('m4 r59')) +r4.get('/res60/:id', (c) => c.text('m4 r60')) +r4.post('/res61/:id', (c) => c.text('m4 r61')) +r4.put('/res62/:id', (c) => c.text('m4 r62')) +r4.delete('/res63/:id', (c) => c.text('m4 r63')) +r4.patch('/res64/:id', (c) => c.text('m4 r64')) +r4.get('/res65/:id', (c) => c.text('m4 r65')) +r4.post('/res66/:id', (c) => c.text('m4 r66')) +r4.put('/res67/:id', (c) => c.text('m4 r67')) +r4.delete('/res68/:id', (c) => c.text('m4 r68')) +r4.patch('/res69/:id', (c) => c.text('m4 r69')) +r4.get('/res70/:id', (c) => c.text('m4 r70')) +r4.post('/res71/:id', (c) => c.text('m4 r71')) +r4.put('/res72/:id', (c) => c.text('m4 r72')) +r4.delete('/res73/:id', (c) => c.text('m4 r73')) +r4.patch('/res74/:id', (c) => c.text('m4 r74')) +r4.get('/res75/:id', (c) => c.text('m4 r75')) +r4.post('/res76/:id', (c) => c.text('m4 r76')) +r4.put('/res77/:id', (c) => c.text('m4 r77')) +r4.delete('/res78/:id', (c) => c.text('m4 r78')) +r4.patch('/res79/:id', (c) => c.text('m4 r79')) +r4.get('/res80/:id', (c) => c.text('m4 r80')) +r4.post('/res81/:id', (c) => c.text('m4 r81')) +r4.put('/res82/:id', (c) => c.text('m4 r82')) +r4.delete('/res83/:id', (c) => c.text('m4 r83')) +r4.patch('/res84/:id', (c) => c.text('m4 r84')) +r4.get('/res85/:id', (c) => c.text('m4 r85')) +r4.post('/res86/:id', (c) => c.text('m4 r86')) +r4.put('/res87/:id', (c) => c.text('m4 r87')) +r4.delete('/res88/:id', (c) => c.text('m4 r88')) +r4.patch('/res89/:id', (c) => c.text('m4 r89')) +r4.get('/res90/:id', (c) => c.text('m4 r90')) +r4.post('/res91/:id', (c) => c.text('m4 r91')) +r4.put('/res92/:id', (c) => c.text('m4 r92')) +r4.delete('/res93/:id', (c) => c.text('m4 r93')) +r4.patch('/res94/:id', (c) => c.text('m4 r94')) +r4.get('/res95/:id', (c) => c.text('m4 r95')) +r4.post('/res96/:id', (c) => c.text('m4 r96')) +r4.put('/res97/:id', (c) => c.text('m4 r97')) +r4.delete('/res98/:id', (c) => c.text('m4 r98')) +r4.patch('/res99/:id', (c) => c.text('m4 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod5.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod5.ts new file mode 100644 index 000000000..7ee8b7d0d --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod5.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r5 = new Hono() +r5.get('/res0/:id', (c) => c.text('m5 r0')) +r5.post('/res1/:id', (c) => c.text('m5 r1')) +r5.put('/res2/:id', (c) => c.text('m5 r2')) +r5.delete('/res3/:id', (c) => c.text('m5 r3')) +r5.patch('/res4/:id', (c) => c.text('m5 r4')) +r5.get('/res5/:id', (c) => c.text('m5 r5')) +r5.post('/res6/:id', (c) => c.text('m5 r6')) +r5.put('/res7/:id', (c) => c.text('m5 r7')) +r5.delete('/res8/:id', (c) => c.text('m5 r8')) +r5.patch('/res9/:id', (c) => c.text('m5 r9')) +r5.get('/res10/:id', (c) => c.text('m5 r10')) +r5.post('/res11/:id', (c) => c.text('m5 r11')) +r5.put('/res12/:id', (c) => c.text('m5 r12')) +r5.delete('/res13/:id', (c) => c.text('m5 r13')) +r5.patch('/res14/:id', (c) => c.text('m5 r14')) +r5.get('/res15/:id', (c) => c.text('m5 r15')) +r5.post('/res16/:id', (c) => c.text('m5 r16')) +r5.put('/res17/:id', (c) => c.text('m5 r17')) +r5.delete('/res18/:id', (c) => c.text('m5 r18')) +r5.patch('/res19/:id', (c) => c.text('m5 r19')) +r5.get('/res20/:id', (c) => c.text('m5 r20')) +r5.post('/res21/:id', (c) => c.text('m5 r21')) +r5.put('/res22/:id', (c) => c.text('m5 r22')) +r5.delete('/res23/:id', (c) => c.text('m5 r23')) +r5.patch('/res24/:id', (c) => c.text('m5 r24')) +r5.get('/res25/:id', (c) => c.text('m5 r25')) +r5.post('/res26/:id', (c) => c.text('m5 r26')) +r5.put('/res27/:id', (c) => c.text('m5 r27')) +r5.delete('/res28/:id', (c) => c.text('m5 r28')) +r5.patch('/res29/:id', (c) => c.text('m5 r29')) +r5.get('/res30/:id', (c) => c.text('m5 r30')) +r5.post('/res31/:id', (c) => c.text('m5 r31')) +r5.put('/res32/:id', (c) => c.text('m5 r32')) +r5.delete('/res33/:id', (c) => c.text('m5 r33')) +r5.patch('/res34/:id', (c) => c.text('m5 r34')) +r5.get('/res35/:id', (c) => c.text('m5 r35')) +r5.post('/res36/:id', (c) => c.text('m5 r36')) +r5.put('/res37/:id', (c) => c.text('m5 r37')) +r5.delete('/res38/:id', (c) => c.text('m5 r38')) +r5.patch('/res39/:id', (c) => c.text('m5 r39')) +r5.get('/res40/:id', (c) => c.text('m5 r40')) +r5.post('/res41/:id', (c) => c.text('m5 r41')) +r5.put('/res42/:id', (c) => c.text('m5 r42')) +r5.delete('/res43/:id', (c) => c.text('m5 r43')) +r5.patch('/res44/:id', (c) => c.text('m5 r44')) +r5.get('/res45/:id', (c) => c.text('m5 r45')) +r5.post('/res46/:id', (c) => c.text('m5 r46')) +r5.put('/res47/:id', (c) => c.text('m5 r47')) +r5.delete('/res48/:id', (c) => c.text('m5 r48')) +r5.patch('/res49/:id', (c) => c.text('m5 r49')) +r5.get('/res50/:id', (c) => c.text('m5 r50')) +r5.post('/res51/:id', (c) => c.text('m5 r51')) +r5.put('/res52/:id', (c) => c.text('m5 r52')) +r5.delete('/res53/:id', (c) => c.text('m5 r53')) +r5.patch('/res54/:id', (c) => c.text('m5 r54')) +r5.get('/res55/:id', (c) => c.text('m5 r55')) +r5.post('/res56/:id', (c) => c.text('m5 r56')) +r5.put('/res57/:id', (c) => c.text('m5 r57')) +r5.delete('/res58/:id', (c) => c.text('m5 r58')) +r5.patch('/res59/:id', (c) => c.text('m5 r59')) +r5.get('/res60/:id', (c) => c.text('m5 r60')) +r5.post('/res61/:id', (c) => c.text('m5 r61')) +r5.put('/res62/:id', (c) => c.text('m5 r62')) +r5.delete('/res63/:id', (c) => c.text('m5 r63')) +r5.patch('/res64/:id', (c) => c.text('m5 r64')) +r5.get('/res65/:id', (c) => c.text('m5 r65')) +r5.post('/res66/:id', (c) => c.text('m5 r66')) +r5.put('/res67/:id', (c) => c.text('m5 r67')) +r5.delete('/res68/:id', (c) => c.text('m5 r68')) +r5.patch('/res69/:id', (c) => c.text('m5 r69')) +r5.get('/res70/:id', (c) => c.text('m5 r70')) +r5.post('/res71/:id', (c) => c.text('m5 r71')) +r5.put('/res72/:id', (c) => c.text('m5 r72')) +r5.delete('/res73/:id', (c) => c.text('m5 r73')) +r5.patch('/res74/:id', (c) => c.text('m5 r74')) +r5.get('/res75/:id', (c) => c.text('m5 r75')) +r5.post('/res76/:id', (c) => c.text('m5 r76')) +r5.put('/res77/:id', (c) => c.text('m5 r77')) +r5.delete('/res78/:id', (c) => c.text('m5 r78')) +r5.patch('/res79/:id', (c) => c.text('m5 r79')) +r5.get('/res80/:id', (c) => c.text('m5 r80')) +r5.post('/res81/:id', (c) => c.text('m5 r81')) +r5.put('/res82/:id', (c) => c.text('m5 r82')) +r5.delete('/res83/:id', (c) => c.text('m5 r83')) +r5.patch('/res84/:id', (c) => c.text('m5 r84')) +r5.get('/res85/:id', (c) => c.text('m5 r85')) +r5.post('/res86/:id', (c) => c.text('m5 r86')) +r5.put('/res87/:id', (c) => c.text('m5 r87')) +r5.delete('/res88/:id', (c) => c.text('m5 r88')) +r5.patch('/res89/:id', (c) => c.text('m5 r89')) +r5.get('/res90/:id', (c) => c.text('m5 r90')) +r5.post('/res91/:id', (c) => c.text('m5 r91')) +r5.put('/res92/:id', (c) => c.text('m5 r92')) +r5.delete('/res93/:id', (c) => c.text('m5 r93')) +r5.patch('/res94/:id', (c) => c.text('m5 r94')) +r5.get('/res95/:id', (c) => c.text('m5 r95')) +r5.post('/res96/:id', (c) => c.text('m5 r96')) +r5.put('/res97/:id', (c) => c.text('m5 r97')) +r5.delete('/res98/:id', (c) => c.text('m5 r98')) +r5.patch('/res99/:id', (c) => c.text('m5 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod6.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod6.ts new file mode 100644 index 000000000..c8bc720de --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod6.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r6 = new Hono() +r6.get('/res0/:id', (c) => c.text('m6 r0')) +r6.post('/res1/:id', (c) => c.text('m6 r1')) +r6.put('/res2/:id', (c) => c.text('m6 r2')) +r6.delete('/res3/:id', (c) => c.text('m6 r3')) +r6.patch('/res4/:id', (c) => c.text('m6 r4')) +r6.get('/res5/:id', (c) => c.text('m6 r5')) +r6.post('/res6/:id', (c) => c.text('m6 r6')) +r6.put('/res7/:id', (c) => c.text('m6 r7')) +r6.delete('/res8/:id', (c) => c.text('m6 r8')) +r6.patch('/res9/:id', (c) => c.text('m6 r9')) +r6.get('/res10/:id', (c) => c.text('m6 r10')) +r6.post('/res11/:id', (c) => c.text('m6 r11')) +r6.put('/res12/:id', (c) => c.text('m6 r12')) +r6.delete('/res13/:id', (c) => c.text('m6 r13')) +r6.patch('/res14/:id', (c) => c.text('m6 r14')) +r6.get('/res15/:id', (c) => c.text('m6 r15')) +r6.post('/res16/:id', (c) => c.text('m6 r16')) +r6.put('/res17/:id', (c) => c.text('m6 r17')) +r6.delete('/res18/:id', (c) => c.text('m6 r18')) +r6.patch('/res19/:id', (c) => c.text('m6 r19')) +r6.get('/res20/:id', (c) => c.text('m6 r20')) +r6.post('/res21/:id', (c) => c.text('m6 r21')) +r6.put('/res22/:id', (c) => c.text('m6 r22')) +r6.delete('/res23/:id', (c) => c.text('m6 r23')) +r6.patch('/res24/:id', (c) => c.text('m6 r24')) +r6.get('/res25/:id', (c) => c.text('m6 r25')) +r6.post('/res26/:id', (c) => c.text('m6 r26')) +r6.put('/res27/:id', (c) => c.text('m6 r27')) +r6.delete('/res28/:id', (c) => c.text('m6 r28')) +r6.patch('/res29/:id', (c) => c.text('m6 r29')) +r6.get('/res30/:id', (c) => c.text('m6 r30')) +r6.post('/res31/:id', (c) => c.text('m6 r31')) +r6.put('/res32/:id', (c) => c.text('m6 r32')) +r6.delete('/res33/:id', (c) => c.text('m6 r33')) +r6.patch('/res34/:id', (c) => c.text('m6 r34')) +r6.get('/res35/:id', (c) => c.text('m6 r35')) +r6.post('/res36/:id', (c) => c.text('m6 r36')) +r6.put('/res37/:id', (c) => c.text('m6 r37')) +r6.delete('/res38/:id', (c) => c.text('m6 r38')) +r6.patch('/res39/:id', (c) => c.text('m6 r39')) +r6.get('/res40/:id', (c) => c.text('m6 r40')) +r6.post('/res41/:id', (c) => c.text('m6 r41')) +r6.put('/res42/:id', (c) => c.text('m6 r42')) +r6.delete('/res43/:id', (c) => c.text('m6 r43')) +r6.patch('/res44/:id', (c) => c.text('m6 r44')) +r6.get('/res45/:id', (c) => c.text('m6 r45')) +r6.post('/res46/:id', (c) => c.text('m6 r46')) +r6.put('/res47/:id', (c) => c.text('m6 r47')) +r6.delete('/res48/:id', (c) => c.text('m6 r48')) +r6.patch('/res49/:id', (c) => c.text('m6 r49')) +r6.get('/res50/:id', (c) => c.text('m6 r50')) +r6.post('/res51/:id', (c) => c.text('m6 r51')) +r6.put('/res52/:id', (c) => c.text('m6 r52')) +r6.delete('/res53/:id', (c) => c.text('m6 r53')) +r6.patch('/res54/:id', (c) => c.text('m6 r54')) +r6.get('/res55/:id', (c) => c.text('m6 r55')) +r6.post('/res56/:id', (c) => c.text('m6 r56')) +r6.put('/res57/:id', (c) => c.text('m6 r57')) +r6.delete('/res58/:id', (c) => c.text('m6 r58')) +r6.patch('/res59/:id', (c) => c.text('m6 r59')) +r6.get('/res60/:id', (c) => c.text('m6 r60')) +r6.post('/res61/:id', (c) => c.text('m6 r61')) +r6.put('/res62/:id', (c) => c.text('m6 r62')) +r6.delete('/res63/:id', (c) => c.text('m6 r63')) +r6.patch('/res64/:id', (c) => c.text('m6 r64')) +r6.get('/res65/:id', (c) => c.text('m6 r65')) +r6.post('/res66/:id', (c) => c.text('m6 r66')) +r6.put('/res67/:id', (c) => c.text('m6 r67')) +r6.delete('/res68/:id', (c) => c.text('m6 r68')) +r6.patch('/res69/:id', (c) => c.text('m6 r69')) +r6.get('/res70/:id', (c) => c.text('m6 r70')) +r6.post('/res71/:id', (c) => c.text('m6 r71')) +r6.put('/res72/:id', (c) => c.text('m6 r72')) +r6.delete('/res73/:id', (c) => c.text('m6 r73')) +r6.patch('/res74/:id', (c) => c.text('m6 r74')) +r6.get('/res75/:id', (c) => c.text('m6 r75')) +r6.post('/res76/:id', (c) => c.text('m6 r76')) +r6.put('/res77/:id', (c) => c.text('m6 r77')) +r6.delete('/res78/:id', (c) => c.text('m6 r78')) +r6.patch('/res79/:id', (c) => c.text('m6 r79')) +r6.get('/res80/:id', (c) => c.text('m6 r80')) +r6.post('/res81/:id', (c) => c.text('m6 r81')) +r6.put('/res82/:id', (c) => c.text('m6 r82')) +r6.delete('/res83/:id', (c) => c.text('m6 r83')) +r6.patch('/res84/:id', (c) => c.text('m6 r84')) +r6.get('/res85/:id', (c) => c.text('m6 r85')) +r6.post('/res86/:id', (c) => c.text('m6 r86')) +r6.put('/res87/:id', (c) => c.text('m6 r87')) +r6.delete('/res88/:id', (c) => c.text('m6 r88')) +r6.patch('/res89/:id', (c) => c.text('m6 r89')) +r6.get('/res90/:id', (c) => c.text('m6 r90')) +r6.post('/res91/:id', (c) => c.text('m6 r91')) +r6.put('/res92/:id', (c) => c.text('m6 r92')) +r6.delete('/res93/:id', (c) => c.text('m6 r93')) +r6.patch('/res94/:id', (c) => c.text('m6 r94')) +r6.get('/res95/:id', (c) => c.text('m6 r95')) +r6.post('/res96/:id', (c) => c.text('m6 r96')) +r6.put('/res97/:id', (c) => c.text('m6 r97')) +r6.delete('/res98/:id', (c) => c.text('m6 r98')) +r6.patch('/res99/:id', (c) => c.text('m6 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod7.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod7.ts new file mode 100644 index 000000000..37d51895b --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod7.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r7 = new Hono() +r7.get('/res0/:id', (c) => c.text('m7 r0')) +r7.post('/res1/:id', (c) => c.text('m7 r1')) +r7.put('/res2/:id', (c) => c.text('m7 r2')) +r7.delete('/res3/:id', (c) => c.text('m7 r3')) +r7.patch('/res4/:id', (c) => c.text('m7 r4')) +r7.get('/res5/:id', (c) => c.text('m7 r5')) +r7.post('/res6/:id', (c) => c.text('m7 r6')) +r7.put('/res7/:id', (c) => c.text('m7 r7')) +r7.delete('/res8/:id', (c) => c.text('m7 r8')) +r7.patch('/res9/:id', (c) => c.text('m7 r9')) +r7.get('/res10/:id', (c) => c.text('m7 r10')) +r7.post('/res11/:id', (c) => c.text('m7 r11')) +r7.put('/res12/:id', (c) => c.text('m7 r12')) +r7.delete('/res13/:id', (c) => c.text('m7 r13')) +r7.patch('/res14/:id', (c) => c.text('m7 r14')) +r7.get('/res15/:id', (c) => c.text('m7 r15')) +r7.post('/res16/:id', (c) => c.text('m7 r16')) +r7.put('/res17/:id', (c) => c.text('m7 r17')) +r7.delete('/res18/:id', (c) => c.text('m7 r18')) +r7.patch('/res19/:id', (c) => c.text('m7 r19')) +r7.get('/res20/:id', (c) => c.text('m7 r20')) +r7.post('/res21/:id', (c) => c.text('m7 r21')) +r7.put('/res22/:id', (c) => c.text('m7 r22')) +r7.delete('/res23/:id', (c) => c.text('m7 r23')) +r7.patch('/res24/:id', (c) => c.text('m7 r24')) +r7.get('/res25/:id', (c) => c.text('m7 r25')) +r7.post('/res26/:id', (c) => c.text('m7 r26')) +r7.put('/res27/:id', (c) => c.text('m7 r27')) +r7.delete('/res28/:id', (c) => c.text('m7 r28')) +r7.patch('/res29/:id', (c) => c.text('m7 r29')) +r7.get('/res30/:id', (c) => c.text('m7 r30')) +r7.post('/res31/:id', (c) => c.text('m7 r31')) +r7.put('/res32/:id', (c) => c.text('m7 r32')) +r7.delete('/res33/:id', (c) => c.text('m7 r33')) +r7.patch('/res34/:id', (c) => c.text('m7 r34')) +r7.get('/res35/:id', (c) => c.text('m7 r35')) +r7.post('/res36/:id', (c) => c.text('m7 r36')) +r7.put('/res37/:id', (c) => c.text('m7 r37')) +r7.delete('/res38/:id', (c) => c.text('m7 r38')) +r7.patch('/res39/:id', (c) => c.text('m7 r39')) +r7.get('/res40/:id', (c) => c.text('m7 r40')) +r7.post('/res41/:id', (c) => c.text('m7 r41')) +r7.put('/res42/:id', (c) => c.text('m7 r42')) +r7.delete('/res43/:id', (c) => c.text('m7 r43')) +r7.patch('/res44/:id', (c) => c.text('m7 r44')) +r7.get('/res45/:id', (c) => c.text('m7 r45')) +r7.post('/res46/:id', (c) => c.text('m7 r46')) +r7.put('/res47/:id', (c) => c.text('m7 r47')) +r7.delete('/res48/:id', (c) => c.text('m7 r48')) +r7.patch('/res49/:id', (c) => c.text('m7 r49')) +r7.get('/res50/:id', (c) => c.text('m7 r50')) +r7.post('/res51/:id', (c) => c.text('m7 r51')) +r7.put('/res52/:id', (c) => c.text('m7 r52')) +r7.delete('/res53/:id', (c) => c.text('m7 r53')) +r7.patch('/res54/:id', (c) => c.text('m7 r54')) +r7.get('/res55/:id', (c) => c.text('m7 r55')) +r7.post('/res56/:id', (c) => c.text('m7 r56')) +r7.put('/res57/:id', (c) => c.text('m7 r57')) +r7.delete('/res58/:id', (c) => c.text('m7 r58')) +r7.patch('/res59/:id', (c) => c.text('m7 r59')) +r7.get('/res60/:id', (c) => c.text('m7 r60')) +r7.post('/res61/:id', (c) => c.text('m7 r61')) +r7.put('/res62/:id', (c) => c.text('m7 r62')) +r7.delete('/res63/:id', (c) => c.text('m7 r63')) +r7.patch('/res64/:id', (c) => c.text('m7 r64')) +r7.get('/res65/:id', (c) => c.text('m7 r65')) +r7.post('/res66/:id', (c) => c.text('m7 r66')) +r7.put('/res67/:id', (c) => c.text('m7 r67')) +r7.delete('/res68/:id', (c) => c.text('m7 r68')) +r7.patch('/res69/:id', (c) => c.text('m7 r69')) +r7.get('/res70/:id', (c) => c.text('m7 r70')) +r7.post('/res71/:id', (c) => c.text('m7 r71')) +r7.put('/res72/:id', (c) => c.text('m7 r72')) +r7.delete('/res73/:id', (c) => c.text('m7 r73')) +r7.patch('/res74/:id', (c) => c.text('m7 r74')) +r7.get('/res75/:id', (c) => c.text('m7 r75')) +r7.post('/res76/:id', (c) => c.text('m7 r76')) +r7.put('/res77/:id', (c) => c.text('m7 r77')) +r7.delete('/res78/:id', (c) => c.text('m7 r78')) +r7.patch('/res79/:id', (c) => c.text('m7 r79')) +r7.get('/res80/:id', (c) => c.text('m7 r80')) +r7.post('/res81/:id', (c) => c.text('m7 r81')) +r7.put('/res82/:id', (c) => c.text('m7 r82')) +r7.delete('/res83/:id', (c) => c.text('m7 r83')) +r7.patch('/res84/:id', (c) => c.text('m7 r84')) +r7.get('/res85/:id', (c) => c.text('m7 r85')) +r7.post('/res86/:id', (c) => c.text('m7 r86')) +r7.put('/res87/:id', (c) => c.text('m7 r87')) +r7.delete('/res88/:id', (c) => c.text('m7 r88')) +r7.patch('/res89/:id', (c) => c.text('m7 r89')) +r7.get('/res90/:id', (c) => c.text('m7 r90')) +r7.post('/res91/:id', (c) => c.text('m7 r91')) +r7.put('/res92/:id', (c) => c.text('m7 r92')) +r7.delete('/res93/:id', (c) => c.text('m7 r93')) +r7.patch('/res94/:id', (c) => c.text('m7 r94')) +r7.get('/res95/:id', (c) => c.text('m7 r95')) +r7.post('/res96/:id', (c) => c.text('m7 r96')) +r7.put('/res97/:id', (c) => c.text('m7 r97')) +r7.delete('/res98/:id', (c) => c.text('m7 r98')) +r7.patch('/res99/:id', (c) => c.text('m7 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod8.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod8.ts new file mode 100644 index 000000000..8ee72ee64 --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod8.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r8 = new Hono() +r8.get('/res0/:id', (c) => c.text('m8 r0')) +r8.post('/res1/:id', (c) => c.text('m8 r1')) +r8.put('/res2/:id', (c) => c.text('m8 r2')) +r8.delete('/res3/:id', (c) => c.text('m8 r3')) +r8.patch('/res4/:id', (c) => c.text('m8 r4')) +r8.get('/res5/:id', (c) => c.text('m8 r5')) +r8.post('/res6/:id', (c) => c.text('m8 r6')) +r8.put('/res7/:id', (c) => c.text('m8 r7')) +r8.delete('/res8/:id', (c) => c.text('m8 r8')) +r8.patch('/res9/:id', (c) => c.text('m8 r9')) +r8.get('/res10/:id', (c) => c.text('m8 r10')) +r8.post('/res11/:id', (c) => c.text('m8 r11')) +r8.put('/res12/:id', (c) => c.text('m8 r12')) +r8.delete('/res13/:id', (c) => c.text('m8 r13')) +r8.patch('/res14/:id', (c) => c.text('m8 r14')) +r8.get('/res15/:id', (c) => c.text('m8 r15')) +r8.post('/res16/:id', (c) => c.text('m8 r16')) +r8.put('/res17/:id', (c) => c.text('m8 r17')) +r8.delete('/res18/:id', (c) => c.text('m8 r18')) +r8.patch('/res19/:id', (c) => c.text('m8 r19')) +r8.get('/res20/:id', (c) => c.text('m8 r20')) +r8.post('/res21/:id', (c) => c.text('m8 r21')) +r8.put('/res22/:id', (c) => c.text('m8 r22')) +r8.delete('/res23/:id', (c) => c.text('m8 r23')) +r8.patch('/res24/:id', (c) => c.text('m8 r24')) +r8.get('/res25/:id', (c) => c.text('m8 r25')) +r8.post('/res26/:id', (c) => c.text('m8 r26')) +r8.put('/res27/:id', (c) => c.text('m8 r27')) +r8.delete('/res28/:id', (c) => c.text('m8 r28')) +r8.patch('/res29/:id', (c) => c.text('m8 r29')) +r8.get('/res30/:id', (c) => c.text('m8 r30')) +r8.post('/res31/:id', (c) => c.text('m8 r31')) +r8.put('/res32/:id', (c) => c.text('m8 r32')) +r8.delete('/res33/:id', (c) => c.text('m8 r33')) +r8.patch('/res34/:id', (c) => c.text('m8 r34')) +r8.get('/res35/:id', (c) => c.text('m8 r35')) +r8.post('/res36/:id', (c) => c.text('m8 r36')) +r8.put('/res37/:id', (c) => c.text('m8 r37')) +r8.delete('/res38/:id', (c) => c.text('m8 r38')) +r8.patch('/res39/:id', (c) => c.text('m8 r39')) +r8.get('/res40/:id', (c) => c.text('m8 r40')) +r8.post('/res41/:id', (c) => c.text('m8 r41')) +r8.put('/res42/:id', (c) => c.text('m8 r42')) +r8.delete('/res43/:id', (c) => c.text('m8 r43')) +r8.patch('/res44/:id', (c) => c.text('m8 r44')) +r8.get('/res45/:id', (c) => c.text('m8 r45')) +r8.post('/res46/:id', (c) => c.text('m8 r46')) +r8.put('/res47/:id', (c) => c.text('m8 r47')) +r8.delete('/res48/:id', (c) => c.text('m8 r48')) +r8.patch('/res49/:id', (c) => c.text('m8 r49')) +r8.get('/res50/:id', (c) => c.text('m8 r50')) +r8.post('/res51/:id', (c) => c.text('m8 r51')) +r8.put('/res52/:id', (c) => c.text('m8 r52')) +r8.delete('/res53/:id', (c) => c.text('m8 r53')) +r8.patch('/res54/:id', (c) => c.text('m8 r54')) +r8.get('/res55/:id', (c) => c.text('m8 r55')) +r8.post('/res56/:id', (c) => c.text('m8 r56')) +r8.put('/res57/:id', (c) => c.text('m8 r57')) +r8.delete('/res58/:id', (c) => c.text('m8 r58')) +r8.patch('/res59/:id', (c) => c.text('m8 r59')) +r8.get('/res60/:id', (c) => c.text('m8 r60')) +r8.post('/res61/:id', (c) => c.text('m8 r61')) +r8.put('/res62/:id', (c) => c.text('m8 r62')) +r8.delete('/res63/:id', (c) => c.text('m8 r63')) +r8.patch('/res64/:id', (c) => c.text('m8 r64')) +r8.get('/res65/:id', (c) => c.text('m8 r65')) +r8.post('/res66/:id', (c) => c.text('m8 r66')) +r8.put('/res67/:id', (c) => c.text('m8 r67')) +r8.delete('/res68/:id', (c) => c.text('m8 r68')) +r8.patch('/res69/:id', (c) => c.text('m8 r69')) +r8.get('/res70/:id', (c) => c.text('m8 r70')) +r8.post('/res71/:id', (c) => c.text('m8 r71')) +r8.put('/res72/:id', (c) => c.text('m8 r72')) +r8.delete('/res73/:id', (c) => c.text('m8 r73')) +r8.patch('/res74/:id', (c) => c.text('m8 r74')) +r8.get('/res75/:id', (c) => c.text('m8 r75')) +r8.post('/res76/:id', (c) => c.text('m8 r76')) +r8.put('/res77/:id', (c) => c.text('m8 r77')) +r8.delete('/res78/:id', (c) => c.text('m8 r78')) +r8.patch('/res79/:id', (c) => c.text('m8 r79')) +r8.get('/res80/:id', (c) => c.text('m8 r80')) +r8.post('/res81/:id', (c) => c.text('m8 r81')) +r8.put('/res82/:id', (c) => c.text('m8 r82')) +r8.delete('/res83/:id', (c) => c.text('m8 r83')) +r8.patch('/res84/:id', (c) => c.text('m8 r84')) +r8.get('/res85/:id', (c) => c.text('m8 r85')) +r8.post('/res86/:id', (c) => c.text('m8 r86')) +r8.put('/res87/:id', (c) => c.text('m8 r87')) +r8.delete('/res88/:id', (c) => c.text('m8 r88')) +r8.patch('/res89/:id', (c) => c.text('m8 r89')) +r8.get('/res90/:id', (c) => c.text('m8 r90')) +r8.post('/res91/:id', (c) => c.text('m8 r91')) +r8.put('/res92/:id', (c) => c.text('m8 r92')) +r8.delete('/res93/:id', (c) => c.text('m8 r93')) +r8.patch('/res94/:id', (c) => c.text('m8 r94')) +r8.get('/res95/:id', (c) => c.text('m8 r95')) +r8.post('/res96/:id', (c) => c.text('m8 r96')) +r8.put('/res97/:id', (c) => c.text('m8 r97')) +r8.delete('/res98/:id', (c) => c.text('m8 r98')) +r8.patch('/res99/:id', (c) => c.text('m8 r99')) diff --git a/graph/test/typescript/hono/cases/scale/routes-2k/src/mod9.ts b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod9.ts new file mode 100644 index 000000000..3dfcf492f --- /dev/null +++ b/graph/test/typescript/hono/cases/scale/routes-2k/src/mod9.ts @@ -0,0 +1,103 @@ +import { Hono } from 'hono' + +export const r9 = new Hono() +r9.get('/res0/:id', (c) => c.text('m9 r0')) +r9.post('/res1/:id', (c) => c.text('m9 r1')) +r9.put('/res2/:id', (c) => c.text('m9 r2')) +r9.delete('/res3/:id', (c) => c.text('m9 r3')) +r9.patch('/res4/:id', (c) => c.text('m9 r4')) +r9.get('/res5/:id', (c) => c.text('m9 r5')) +r9.post('/res6/:id', (c) => c.text('m9 r6')) +r9.put('/res7/:id', (c) => c.text('m9 r7')) +r9.delete('/res8/:id', (c) => c.text('m9 r8')) +r9.patch('/res9/:id', (c) => c.text('m9 r9')) +r9.get('/res10/:id', (c) => c.text('m9 r10')) +r9.post('/res11/:id', (c) => c.text('m9 r11')) +r9.put('/res12/:id', (c) => c.text('m9 r12')) +r9.delete('/res13/:id', (c) => c.text('m9 r13')) +r9.patch('/res14/:id', (c) => c.text('m9 r14')) +r9.get('/res15/:id', (c) => c.text('m9 r15')) +r9.post('/res16/:id', (c) => c.text('m9 r16')) +r9.put('/res17/:id', (c) => c.text('m9 r17')) +r9.delete('/res18/:id', (c) => c.text('m9 r18')) +r9.patch('/res19/:id', (c) => c.text('m9 r19')) +r9.get('/res20/:id', (c) => c.text('m9 r20')) +r9.post('/res21/:id', (c) => c.text('m9 r21')) +r9.put('/res22/:id', (c) => c.text('m9 r22')) +r9.delete('/res23/:id', (c) => c.text('m9 r23')) +r9.patch('/res24/:id', (c) => c.text('m9 r24')) +r9.get('/res25/:id', (c) => c.text('m9 r25')) +r9.post('/res26/:id', (c) => c.text('m9 r26')) +r9.put('/res27/:id', (c) => c.text('m9 r27')) +r9.delete('/res28/:id', (c) => c.text('m9 r28')) +r9.patch('/res29/:id', (c) => c.text('m9 r29')) +r9.get('/res30/:id', (c) => c.text('m9 r30')) +r9.post('/res31/:id', (c) => c.text('m9 r31')) +r9.put('/res32/:id', (c) => c.text('m9 r32')) +r9.delete('/res33/:id', (c) => c.text('m9 r33')) +r9.patch('/res34/:id', (c) => c.text('m9 r34')) +r9.get('/res35/:id', (c) => c.text('m9 r35')) +r9.post('/res36/:id', (c) => c.text('m9 r36')) +r9.put('/res37/:id', (c) => c.text('m9 r37')) +r9.delete('/res38/:id', (c) => c.text('m9 r38')) +r9.patch('/res39/:id', (c) => c.text('m9 r39')) +r9.get('/res40/:id', (c) => c.text('m9 r40')) +r9.post('/res41/:id', (c) => c.text('m9 r41')) +r9.put('/res42/:id', (c) => c.text('m9 r42')) +r9.delete('/res43/:id', (c) => c.text('m9 r43')) +r9.patch('/res44/:id', (c) => c.text('m9 r44')) +r9.get('/res45/:id', (c) => c.text('m9 r45')) +r9.post('/res46/:id', (c) => c.text('m9 r46')) +r9.put('/res47/:id', (c) => c.text('m9 r47')) +r9.delete('/res48/:id', (c) => c.text('m9 r48')) +r9.patch('/res49/:id', (c) => c.text('m9 r49')) +r9.get('/res50/:id', (c) => c.text('m9 r50')) +r9.post('/res51/:id', (c) => c.text('m9 r51')) +r9.put('/res52/:id', (c) => c.text('m9 r52')) +r9.delete('/res53/:id', (c) => c.text('m9 r53')) +r9.patch('/res54/:id', (c) => c.text('m9 r54')) +r9.get('/res55/:id', (c) => c.text('m9 r55')) +r9.post('/res56/:id', (c) => c.text('m9 r56')) +r9.put('/res57/:id', (c) => c.text('m9 r57')) +r9.delete('/res58/:id', (c) => c.text('m9 r58')) +r9.patch('/res59/:id', (c) => c.text('m9 r59')) +r9.get('/res60/:id', (c) => c.text('m9 r60')) +r9.post('/res61/:id', (c) => c.text('m9 r61')) +r9.put('/res62/:id', (c) => c.text('m9 r62')) +r9.delete('/res63/:id', (c) => c.text('m9 r63')) +r9.patch('/res64/:id', (c) => c.text('m9 r64')) +r9.get('/res65/:id', (c) => c.text('m9 r65')) +r9.post('/res66/:id', (c) => c.text('m9 r66')) +r9.put('/res67/:id', (c) => c.text('m9 r67')) +r9.delete('/res68/:id', (c) => c.text('m9 r68')) +r9.patch('/res69/:id', (c) => c.text('m9 r69')) +r9.get('/res70/:id', (c) => c.text('m9 r70')) +r9.post('/res71/:id', (c) => c.text('m9 r71')) +r9.put('/res72/:id', (c) => c.text('m9 r72')) +r9.delete('/res73/:id', (c) => c.text('m9 r73')) +r9.patch('/res74/:id', (c) => c.text('m9 r74')) +r9.get('/res75/:id', (c) => c.text('m9 r75')) +r9.post('/res76/:id', (c) => c.text('m9 r76')) +r9.put('/res77/:id', (c) => c.text('m9 r77')) +r9.delete('/res78/:id', (c) => c.text('m9 r78')) +r9.patch('/res79/:id', (c) => c.text('m9 r79')) +r9.get('/res80/:id', (c) => c.text('m9 r80')) +r9.post('/res81/:id', (c) => c.text('m9 r81')) +r9.put('/res82/:id', (c) => c.text('m9 r82')) +r9.delete('/res83/:id', (c) => c.text('m9 r83')) +r9.patch('/res84/:id', (c) => c.text('m9 r84')) +r9.get('/res85/:id', (c) => c.text('m9 r85')) +r9.post('/res86/:id', (c) => c.text('m9 r86')) +r9.put('/res87/:id', (c) => c.text('m9 r87')) +r9.delete('/res88/:id', (c) => c.text('m9 r88')) +r9.patch('/res89/:id', (c) => c.text('m9 r89')) +r9.get('/res90/:id', (c) => c.text('m9 r90')) +r9.post('/res91/:id', (c) => c.text('m9 r91')) +r9.put('/res92/:id', (c) => c.text('m9 r92')) +r9.delete('/res93/:id', (c) => c.text('m9 r93')) +r9.patch('/res94/:id', (c) => c.text('m9 r94')) +r9.get('/res95/:id', (c) => c.text('m9 r95')) +r9.post('/res96/:id', (c) => c.text('m9 r96')) +r9.put('/res97/:id', (c) => c.text('m9 r97')) +r9.delete('/res98/:id', (c) => c.text('m9 r98')) +r9.patch('/res99/:id', (c) => c.text('m9 r99')) diff --git a/graph/test/typescript/hono/cases/zod/composition/src/index.ts b/graph/test/typescript/hono/cases/zod/composition/src/index.ts new file mode 100644 index 000000000..ea3722945 --- /dev/null +++ b/graph/test/typescript/hono/cases/zod/composition/src/index.ts @@ -0,0 +1,27 @@ +// census: zvalidator (SPEC 3.9 composition) +// A route validating a DERIVED schema is reached from its BASE: extend, merge, pick, omit, partial, +// z.array(Base), and .openapi('Name') metadata (still the same schema). +import { Hono } from 'hono' +import { zValidator } from '@hono/zod-validator' +import { z } from '@hono/zod-openapi' + +const Base = z.object({ id: z.string(), name: z.string(), age: z.number() }) +const Extra = z.object({ tag: z.string() }) +const Extended = Base.extend({ email: z.string() }) +const Merged = Base.merge(Extra) +const Picked = Base.pick({ id: true }) +const Omitted = Base.omit({ age: true }) +const Partial = Base.partial() +const Many = z.array(Base) +const Named = Base.openapi('Person') + +const app = new Hono() +app.post('/extended', zValidator('json', Extended), (c) => c.json(c.req.valid('json'))) +app.post('/merged', zValidator('json', Merged), (c) => c.json(c.req.valid('json'))) +app.post('/picked', zValidator('json', Picked), (c) => c.json(c.req.valid('json'))) +app.post('/omitted', zValidator('json', Omitted), (c) => c.json(c.req.valid('json'))) +app.patch('/partial', zValidator('json', Partial), (c) => c.json(c.req.valid('json'))) +app.put('/many', zValidator('json', Many), (c) => c.json(c.req.valid('json'))) +app.post('/named', zValidator('json', Named), (c) => c.json(c.req.valid('json'))) + +export default app diff --git a/graph/test/typescript/hono/cases/zod/decl-forms/src/index.ts b/graph/test/typescript/hono/cases/zod/decl-forms/src/index.ts new file mode 100644 index 000000000..02bc69864 --- /dev/null +++ b/graph/test/typescript/hono/cases/zod/decl-forms/src/index.ts @@ -0,0 +1,21 @@ +// census: zvalidator, req_valid (SPEC 3.9 declaration forms) +// The schema DECLARATION is found through inline, local, named, default, namespace and barrel forms. +import { Hono } from 'hono' +import { zValidator } from '@hono/zod-validator' +import { z } from 'zod' +import { User } from './schemas/user' +import Settings from './schemas/user' +import * as q from './schemas/query' +import { BarrelUser } from './schemas' + +const Local = z.object({ id: z.string() }) + +const app = new Hono() +app.post('/inline', zValidator('json', z.object({ x: z.number() })), (c) => c.json(c.req.valid('json'))) +app.post('/local', zValidator('json', Local), (c) => c.json(c.req.valid('json'))) +app.post('/named', zValidator('json', User), (c) => c.json(c.req.valid('json'))) +app.post('/default', zValidator('json', Settings), (c) => c.json(c.req.valid('json'))) +app.get('/namespace', zValidator('query', q.Paging), (c) => c.json(c.req.valid('query'))) +app.post('/barrel', zValidator('json', BarrelUser), (c) => c.json(c.req.valid('json'))) + +export default app diff --git a/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/index.ts b/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/index.ts new file mode 100644 index 000000000..b90b10f83 --- /dev/null +++ b/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/index.ts @@ -0,0 +1 @@ +export { User as BarrelUser } from './user' diff --git a/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/query.ts b/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/query.ts new file mode 100644 index 000000000..44f45e462 --- /dev/null +++ b/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/query.ts @@ -0,0 +1,3 @@ +import { z } from 'zod' + +export const Paging = z.object({ page: z.string() }) diff --git a/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/user.ts b/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/user.ts new file mode 100644 index 000000000..3c20378b4 --- /dev/null +++ b/graph/test/typescript/hono/cases/zod/decl-forms/src/schemas/user.ts @@ -0,0 +1,6 @@ +import { z } from 'zod' + +export const User = z.object({ name: z.string() }) + +const Settings = z.object({ theme: z.string() }) +export default Settings diff --git a/graph/test/typescript/hono/cases/zod/no-route/src/forms.ts b/graph/test/typescript/hono/cases/zod/no-route/src/forms.ts new file mode 100644 index 000000000..807ec1bc2 --- /dev/null +++ b/graph/test/typescript/hono/cases/zod/no-route/src/forms.ts @@ -0,0 +1,14 @@ +// census: zvalidator (negative partition: zod with no Hono route, SPEC 3.9) +// zod used for plain parsing in a project that has no Hono app: zero route_* rows of any kind. +import { z } from 'zod' + +export const Signup = z.object({ email: z.string(), password: z.string() }) + +export function parseSignup(input: unknown) { + return Signup.parse(input) +} + +export const Profile = Signup.omit({ password: true }).extend({ bio: z.string() }) +export function parseProfile(input: unknown) { + return Profile.safeParse(input) +} diff --git a/graph/test/typescript/hono/cases/zod/specifiers/src/index.ts b/graph/test/typescript/hono/cases/zod/specifiers/src/index.ts new file mode 100644 index 000000000..1b20b1d77 --- /dev/null +++ b/graph/test/typescript/hono/cases/zod/specifiers/src/index.ts @@ -0,0 +1,26 @@ +// census: zvalidator, req_valid (SPEC 3.9 import forms: zod, zod/v4, zod/v3, zod/v4-mini, @hono/zod-openapi's z) +// The same validator shape with `z` taken from every specifier the corpus uses. +import { Hono } from 'hono' +import { zValidator } from '@hono/zod-validator' +import { validator } from 'hono/validator' +import { z } from 'zod' +import { z as z4 } from 'zod/v4' +import { z as z3 } from 'zod/v3' +import * as zm from 'zod/v4-mini' +import { z as zo } from '@hono/zod-openapi' + +const FromZod = z.object({ a: z.string() }) +const FromV4 = z4.object({ b: z4.string() }) +const FromV3 = z3.object({ c: z3.string() }) +const FromMini = zm.object({ d: zm.string() }) +const FromOpenapi = zo.object({ e: zo.string() }) + +const app = new Hono() +app.post('/zod', zValidator('json', FromZod), (c) => c.json(c.req.valid('json'))) +app.post('/v4', zValidator('json', FromV4), (c) => c.json(c.req.valid('json'))) +app.post('/v3', zValidator('json', FromV3), (c) => c.json(c.req.valid('json'))) +app.post('/mini', zValidator('json', FromMini), (c) => c.json(c.req.valid('json'))) +app.post('/fn', validator('json', (v) => zm.parse(FromMini, v)), (c) => c.json(c.req.valid('json'))) +app.post('/openapi-z', zValidator('json', FromOpenapi), (c) => c.json(c.req.valid('json'))) + +export default app diff --git a/graph/test/typescript/hono/cases/zod/valid-targets/src/index.ts b/graph/test/typescript/hono/cases/zod/valid-targets/src/index.ts new file mode 100644 index 000000000..dc1f92b82 --- /dev/null +++ b/graph/test/typescript/hono/cases/zod/valid-targets/src/index.ts @@ -0,0 +1,35 @@ +// census: req_valid, zvalidator, validator_builtin (SPEC 3.9 targets) +// c.req.valid(target) for every target links to that target's validator on the same route; a target with +// no validator on the route is declared (valid_without_validator). +import { Hono } from 'hono' +import { zValidator } from '@hono/zod-validator' +import { z } from 'zod' + +const J = z.object({ j: z.string() }) +const Q = z.object({ q: z.string() }) +const P = z.object({ id: z.string() }) +const F = z.object({ f: z.string() }) +const H = z.object({ h: z.string() }) +const K = z.object({ k: z.string() }) + +const app = new Hono() +app.post( + '/all/:id', + zValidator('json', J), + zValidator('query', Q), + zValidator('param', P), + zValidator('form', F), + zValidator('header', H), + zValidator('cookie', K), + (c) => { + const all = [c.req.valid('json'), c.req.valid('query'), c.req.valid('param'), c.req.valid('form'), c.req.valid('header'), c.req.valid('cookie')] + return c.json(all) + } +) +app.get('/missing', zValidator('query', Q), (c) => { + // @ts-expect-error -- no json validator on this route: the read is declared, not linked + const body = c.req.valid('json') + return c.json([c.req.valid('query'), body]) +}) + +export default app diff --git a/graph/test/typescript/hono/expected/compose/abstract-strategy-mount.entries b/graph/test/typescript/hono/expected/compose/abstract-strategy-mount.entries new file mode 100644 index 000000000..3022b48b6 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/abstract-strategy-mount.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-2-01, re-derived: the map's instances are known, so each override mounts exactly under its key (runtime agrees); never a root +# census: route_mount_nonliteral (violation hono-2-01) +entry index.ts:13:24 http +entry index.ts:20:22 http diff --git a/graph/test/typescript/hono/expected/compose/abstract-strategy-mount.tsv b/graph/test/typescript/hono/expected/compose/abstract-strategy-mount.tsv new file mode 100644 index 000000000..34b44050e --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/abstract-strategy-mount.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-2-01, re-derived: the map's instances are known, so each override mounts exactly under its key (runtime agrees); never a root +# census: route_mount_nonliteral (violation hono-2-01) +route GET /oauth/callback index.ts:13:24 index.ts:26:13 0 +route POST /password/login index.ts:20:22 index.ts:26:13 1 +submount /oauth index.ts:28:7 +submount /password index.ts:28:7 +regby index.ts:11:3 index.ts:13:24 GET /oauth/callback index.ts:26:13 +regby index.ts:18:3 index.ts:20:22 POST /password/login index.ts:26:13 diff --git a/graph/test/typescript/hono/expected/compose/aliased-reexport-mount.entries b/graph/test/typescript/hono/expected/compose/aliased-reexport-mount.entries new file mode 100644 index 000000000..0b2333159 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/aliased-reexport-mount.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m30/hono-4-06: an export { local as alias } of an imported default mounts exactly at /api/search +# census: route_mount_literal (stress-4 mutation m30 shape; violation hono-4-06) +entry search/routes/api.ts:4:21 http +entry search/routes/api.ts:5:27 http diff --git a/graph/test/typescript/hono/expected/compose/aliased-reexport-mount.tsv b/graph/test/typescript/hono/expected/compose/aliased-reexport-mount.tsv new file mode 100644 index 000000000..ccd55775c --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/aliased-reexport-mount.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m30/hono-4-06: an export { local as alias } of an imported default mounts exactly at /api/search +# census: route_mount_literal (stress-4 mutation m30 shape; violation hono-4-06) +route POST /api/search search/routes/api.ts:4:21 index.ts:7:13 0 +route GET /api/search/suggest search/routes/api.ts:5:27 index.ts:7:13 1 +submount /api/search index.ts:8:5 +regby search/routes/api.ts:1:1 search/routes/api.ts:4:21 POST /api/search index.ts:7:13 +regby search/routes/api.ts:1:1 search/routes/api.ts:5:27 GET /api/search/suggest index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/compose/async-wrapper-mount.entries b/graph/test/typescript/hono/expected/compose/async-wrapper-mount.entries new file mode 100644 index 000000000..292249306 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/async-wrapper-mount.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m38/hono-5-06: the awaited async wrapper serves the inner route behind its use(*) +# census: bun_serve (stress-5 mutation m38 shape; violation hono-5-06) +entry main.ts:10:21 http +entry wrap.ts:5:20 http diff --git a/graph/test/typescript/hono/expected/compose/async-wrapper-mount.tsv b/graph/test/typescript/hono/expected/compose/async-wrapper-mount.tsv new file mode 100644 index 000000000..0a335d657 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/async-wrapper-mount.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m38/hono-5-06: the awaited async wrapper serves the inner route behind its use(*) +# census: bun_serve (stress-5 mutation m38 shape; violation hono-5-06) +route GET /hello main.ts:10:21 wrap.ts:4:19 1 +mw wrap.ts:5:20 main.ts:10:21 GET /hello 0 wrap.ts:4:19 +submount / wrap.ts:9:11 +regby main.ts:1:1 main.ts:10:21 GET /hello wrap.ts:4:19 diff --git a/graph/test/typescript/hono/expected/compose/basepath-group.entries b/graph/test/typescript/hono/expected/compose/basepath-group.entries new file mode 100644 index 000000000..3e57427ba --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/basepath-group.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: basePath clones share one route list; the original app's own route has no prefix +# census: basepath +entry index.ts:10:20 http +entry index.ts:11:19 http +entry index.ts:9:14 http diff --git a/graph/test/typescript/hono/expected/compose/basepath-group.tsv b/graph/test/typescript/hono/expected/compose/basepath-group.tsv new file mode 100644 index 000000000..e676163c8 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/basepath-group.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: basePath clones share one route list; the original app's own route has no prefix +# census: basepath +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET / index.ts:9:14 index.ts:5:13 0 +route GET /api/status index.ts:10:20 index.ts:5:13 1 +route GET /api/v2/status index.ts:11:19 index.ts:5:13 2 +regby index.ts:1:1 index.ts:10:20 GET /api/status index.ts:5:13 +regby index.ts:1:1 index.ts:11:19 GET /api/v2/status index.ts:5:13 +regby index.ts:1:1 index.ts:9:14 GET / index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/compose/basepath-sub-mounted.entries b/graph/test/typescript/hono/expected/compose/basepath-sub-mounted.entries new file mode 100644 index 000000000..d8e4a99d5 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/basepath-sub-mounted.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: mount prefix then the sub-app's basePath then the local path +# census: basepath, route_mount_literal +entry index.ts:6:17 http diff --git a/graph/test/typescript/hono/expected/compose/basepath-sub-mounted.tsv b/graph/test/typescript/hono/expected/compose/basepath-sub-mounted.tsv new file mode 100644 index 000000000..527282721 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/basepath-sub-mounted.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: mount prefix then the sub-app's basePath then the local path +# census: basepath, route_mount_literal +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /svc/v1/ping index.ts:6:17 index.ts:8:13 0 +submount /svc index.ts:9:5 +regby index.ts:1:1 index.ts:6:17 GET /svc/v1/ping index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/compose/builder-called-twice.entries b/graph/test/typescript/hono/expected/compose/builder-called-twice.entries new file mode 100644 index 000000000..6dcab8fe6 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/builder-called-twice.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-02/R6: the helper makes two apps; /logo.png on the root, POST /import partial dynamic_mount_prefix under the sub-app +# census: app_factory_fn (violation hono-2-02, SPEC R6; oracle gap O9) +entry main.ts:10:22 http +entry users.ts:9:33 http diff --git a/graph/test/typescript/hono/expected/compose/builder-called-twice.tsv b/graph/test/typescript/hono/expected/compose/builder-called-twice.tsv new file mode 100644 index 000000000..e6f570815 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/builder-called-twice.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-02/R6: the helper makes two apps; /logo.png on the root, POST /import partial dynamic_mount_prefix under the sub-app +# census: app_factory_fn (violation hono-2-02, SPEC R6; oracle gap O9) +route GET /logo.png main.ts:10:22 routes.ts:4:13 0 +partial users.ts:9:33 POST dynamic_mount_prefix +regby main.ts:1:1 main.ts:10:22 GET /logo.png routes.ts:4:13 +unknown main.ts:12:3 dynamic_mount_prefix users.ts:9:33 diff --git a/graph/test/typescript/hono/expected/compose/declarative-routes-const.entries b/graph/test/typescript/hono/expected/compose/declarative-routes-const.entries new file mode 100644 index 000000000..69753dfc9 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/declarative-routes-const.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R13: a const { path, handler } array mounted in a loop gives exact routes; the { path, label } menu gives nothing +# census: route_mount_literal (SPEC R13: a const array of { path, handler } mounted in a loop) +entry index.ts:6:37 http +entry index.ts:7:38 http diff --git a/graph/test/typescript/hono/expected/compose/declarative-routes-const.tsv b/graph/test/typescript/hono/expected/compose/declarative-routes-const.tsv new file mode 100644 index 000000000..8c0db38c8 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/declarative-routes-const.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R13: a const { path, handler } array mounted in a loop gives exact routes; the { path, label } menu gives nothing +# census: route_mount_literal (SPEC R13: a const array of { path, handler } mounted in a loop) +route GET /tickets index.ts:6:37 index.ts:19:13 0 +route GET /tags/:tag index.ts:7:38 index.ts:19:13 1 +submount /tickets index.ts:21:7 +submount /tags index.ts:21:7 +regby index.ts:1:1 index.ts:6:37 GET /tickets index.ts:19:13 +regby index.ts:1:1 index.ts:7:38 GET /tags/:tag index.ts:19:13 diff --git a/graph/test/typescript/hono/expected/compose/declarative-routes-derived.entries b/graph/test/typescript/hono/expected/compose/declarative-routes-derived.entries new file mode 100644 index 000000000..598e2f96d --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/declarative-routes-derived.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R13b re-derived: sorted lists mount exact at element paths; the settings-filtered list adds conditional_registration; never roots at /; menu silent +# census: app_request_test (SPEC R13 / R13b; violation hono-4-09 and its silent menu-list control) +entry app.ts:8:14 http +entry mount.test.ts:12:14 http +entry mount.test.ts:28:95 http +entry plugins/inbox.ts:4:38 http diff --git a/graph/test/typescript/hono/expected/compose/declarative-routes-derived.tsv b/graph/test/typescript/hono/expected/compose/declarative-routes-derived.tsv new file mode 100644 index 000000000..678b8ea26 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/declarative-routes-derived.tsv @@ -0,0 +1,14 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R13b re-derived: sorted lists mount exact at element paths; the settings-filtered list adds conditional_registration; never roots at /; menu silent +# census: app_request_test (SPEC R13 / R13b; violation hono-4-09 and its silent menu-list control) +route GET / app.ts:8:14 app.ts:7:13 0 +route GET /api/inbox plugins/inbox.ts:4:38 app.ts:7:13 1 +route GET /solo mount.test.ts:12:14 mount.test.ts:17:15 0 +route GET /inline mount.test.ts:28:95 mount.test.ts:27:15 0 +submount /api/inbox mount.ts:25:9 +submount /solo mount.ts:25:9 +submount /inline mount.ts:25:9 +regby app.ts:1:1 app.ts:8:14 GET / app.ts:7:13 +regby mount.test.ts:10:1 mount.test.ts:12:14 GET /solo mount.test.ts:17:15 +regby mount.test.ts:26:32 mount.test.ts:28:95 GET /inline mount.test.ts:27:15 +regby plugins/inbox.ts:1:1 plugins/inbox.ts:4:38 GET /api/inbox app.ts:7:13 +unknown mount.ts:25:5 conditional_registration plugins/inbox.ts:4:38 diff --git a/graph/test/typescript/hono/expected/compose/discarded-basepath.entries b/graph/test/typescript/hono/expected/compose/discarded-basepath.entries new file mode 100644 index 000000000..34762ef53 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/discarded-basepath.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a statement basePath() changes nothing: GET /items, not /v2/items +# census: basepath (SPEC 2 [iter2]: a helper that discards basePath()'s result) +entry index.ts:11:19 http diff --git a/graph/test/typescript/hono/expected/compose/discarded-basepath.tsv b/graph/test/typescript/hono/expected/compose/discarded-basepath.tsv new file mode 100644 index 000000000..b9759bf34 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/discarded-basepath.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a statement basePath() changes nothing: GET /items, not /v2/items +# census: basepath (SPEC 2 [iter2]: a helper that discards basePath()'s result) +route GET /items index.ts:11:19 index.ts:10:25 0 +regby index.ts:1:1 index.ts:11:19 GET /items index.ts:10:25 diff --git a/graph/test/typescript/hono/expected/compose/dynamic-prefix.entries b/graph/test/typescript/hono/expected/compose/dynamic-prefix.entries new file mode 100644 index 000000000..09ad792ab --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/dynamic-prefix.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: the mounted route is partial with dynamic_mount_prefix, attributed to its handler +# census: route_mount_nonliteral +entry index.ts:11:20 http +entry index.ts:6:27 http diff --git a/graph/test/typescript/hono/expected/compose/dynamic-prefix.tsv b/graph/test/typescript/hono/expected/compose/dynamic-prefix.tsv new file mode 100644 index 000000000..c4e14d8d0 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/dynamic-prefix.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: the mounted route is partial with dynamic_mount_prefix, attributed to its handler +# census: route_mount_nonliteral +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /static index.ts:11:20 index.ts:9:13 0 +partial index.ts:6:27 GET dynamic_mount_prefix +regby index.ts:1:1 index.ts:11:20 GET /static index.ts:9:13 +unknown index.ts:10:1 dynamic_mount_prefix index.ts:6:27 diff --git a/graph/test/typescript/hono/expected/compose/factory-basepath.entries b/graph/test/typescript/hono/expected/compose/factory-basepath.entries new file mode 100644 index 000000000..3bef534eb --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/factory-basepath.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-02: only GET /v1/ping; no base-less /ping +# census: app_factory_fn, basepath (violation hono-1-02) +entry index.ts:10:18 http diff --git a/graph/test/typescript/hono/expected/compose/factory-basepath.tsv b/graph/test/typescript/hono/expected/compose/factory-basepath.tsv new file mode 100644 index 000000000..8ffc37df3 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/factory-basepath.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-02: only GET /v1/ping; no base-less /ping +# census: app_factory_fn, basepath (violation hono-1-02) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /v1/ping index.ts:10:18 index.ts:6:10 0 +regby index.ts:1:1 index.ts:10:18 GET /v1/ping index.ts:6:10 diff --git a/graph/test/typescript/hono/expected/compose/factory-fn.entries b/graph/test/typescript/hono/expected/compose/factory-fn.entries new file mode 100644 index 000000000..e68509ad1 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/factory-fn.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: factory results are distinct apps; /items mounted on the factory-made app +# census: app_factory_fn +entry app.ts:9:21 http +entry index.ts:7:16 http diff --git a/graph/test/typescript/hono/expected/compose/factory-fn.tsv b/graph/test/typescript/hono/expected/compose/factory-fn.tsv new file mode 100644 index 000000000..69316dced --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/factory-fn.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: factory results are distinct apps; /items mounted on the factory-made app +# census: app_factory_fn +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /ready app.ts:9:21 app.ts:4:10 0 +route GET /items index.ts:7:16 app.ts:4:10 1 +submount /items index.ts:8:5 +regby app.ts:7:1 app.ts:9:21 GET /ready app.ts:4:10 +regby index.ts:1:1 index.ts:7:16 GET /items app.ts:4:10 diff --git a/graph/test/typescript/hono/expected/compose/field-app-class.entries b/graph/test/typescript/hono/expected/compose/field-app-class.entries new file mode 100644 index 000000000..8a519cd5f --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/field-app-class.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: SPEC 2 field flow: this.app (createApp() basePath /v1) gets both mounts from setupRoutes() called in the constructor; the users mount argument comes from a method; this.extra (field initializer) is its own served app +# census: route_mount_literal, app_factory_fn (SPEC 2 [iter2] flow through fields; a class whose +entry routes/health.ts:3:43 http +entry routes/users.ts:6:19 http +entry server.ts:23:30 http diff --git a/graph/test/typescript/hono/expected/compose/field-app-class.tsv b/graph/test/typescript/hono/expected/compose/field-app-class.tsv new file mode 100644 index 000000000..508d3dd40 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/field-app-class.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: SPEC 2 field flow: this.app (createApp() basePath /v1) gets both mounts from setupRoutes() called in the constructor; the users mount argument comes from a method; this.extra (field initializer) is its own served app +# census: route_mount_literal, app_factory_fn (SPEC 2 [iter2] flow through fields; a class whose +route GET /extra server.ts:23:30 server.ts:13:25 0 +route GET /v1/health routes/health.ts:3:43 server.ts:8:10 0 +route GET /v1/users/:id routes/users.ts:6:19 server.ts:8:10 1 +submount /v1/health server.ts:21:14 +submount /v1/users server.ts:22:14 +regby routes/health.ts:1:1 routes/health.ts:3:43 GET /v1/health server.ts:8:10 +regby routes/users.ts:4:3 routes/users.ts:6:19 GET /v1/users/:id server.ts:8:10 +regby server.ts:20:3 server.ts:23:30 GET /extra server.ts:13:25 diff --git a/graph/test/typescript/hono/expected/compose/filtered-instance-loop.entries b/graph/test/typescript/hono/expected/compose/filtered-instance-loop.entries new file mode 100644 index 000000000..b7d9ec4d5 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/filtered-instance-loop.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m36/hono-5-01 under R13b: exact /accounts and /invoices, each with conditional_registration +# census: route_mount_root (stress-5 mutation m36 shape; violation hono-5-01, SPEC R13b) +entry index.ts:11:24 http +entry index.ts:18:24 http +entry index.ts:27:14 http diff --git a/graph/test/typescript/hono/expected/compose/filtered-instance-loop.tsv b/graph/test/typescript/hono/expected/compose/filtered-instance-loop.tsv new file mode 100644 index 000000000..a210430ee --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/filtered-instance-loop.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m36/hono-5-01 under R13b: exact /accounts and /invoices, each with conditional_registration +# census: route_mount_root (stress-5 mutation m36 shape; violation hono-5-01, SPEC R13b) +route GET / index.ts:27:14 index.ts:26:13 0 +route GET /accounts index.ts:11:24 index.ts:26:13 1 +route GET /invoices index.ts:18:24 index.ts:26:13 2 +submount / index.ts:29:7 +submount / index.ts:29:7 +regby index.ts:16:3 index.ts:18:24 GET /invoices index.ts:26:13 +regby index.ts:1:1 index.ts:27:14 GET / index.ts:26:13 +regby index.ts:9:3 index.ts:11:24 GET /accounts index.ts:26:13 +unknown index.ts:29:3 conditional_registration index.ts:11:24 +unknown index.ts:29:3 conditional_registration index.ts:18:24 diff --git a/graph/test/typescript/hono/expected/compose/foreign-mount.entries b/graph/test/typescript/hono/expected/compose/foreign-mount.entries new file mode 100644 index 000000000..d727f45c7 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/foreign-mount.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: mount: ANY /legacy/* served by the foreign function, which is an http entry +# census: mount_foreign +entry index.ts:11:17 http +entry index.ts:5:1 http diff --git a/graph/test/typescript/hono/expected/compose/foreign-mount.tsv b/graph/test/typescript/hono/expected/compose/foreign-mount.tsv new file mode 100644 index 000000000..784bd3e17 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/foreign-mount.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: mount: ANY /legacy/* served by the foreign function, which is an http entry +# census: mount_foreign +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +# iteration 3: the mount is a store entry with a chain (O8); its route row and regby row re-derived +route ANY /legacy/* index.ts:5:1 index.ts:9:13 0 +route GET /new index.ts:11:17 index.ts:9:13 1 +regby index.ts:1:1 index.ts:11:17 GET /new index.ts:9:13 +regby index.ts:1:1 index.ts:5:1 ANY /legacy/* index.ts:9:13 diff --git a/graph/test/typescript/hono/expected/compose/getter-prefix-typed-holder.entries b/graph/test/typescript/hono/expected/compose/getter-prefix-typed-holder.entries new file mode 100644 index 000000000..29702fd97 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/getter-prefix-typed-holder.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m25: controller routes mounted on a typed holder at a getter-read prefix: one partial unresolved_app_value per route +# census: prop_receiver_route (stress-4 mutation m25 shape; SPEC R2, hono-2-04 residue) +entry index.ts:21:33 http +entry index.ts:22:30 http diff --git a/graph/test/typescript/hono/expected/compose/getter-prefix-typed-holder.tsv b/graph/test/typescript/hono/expected/compose/getter-prefix-typed-holder.tsv new file mode 100644 index 000000000..60d6f4fdf --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/getter-prefix-typed-holder.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m25: controller routes mounted on a typed holder at a getter-read prefix: one partial unresolved_app_value per route +# census: prop_receiver_route (stress-4 mutation m25 shape; SPEC R2, hono-2-04 residue) +partial index.ts:21:33 DELETE unresolved_app_value +partial index.ts:22:30 GET unresolved_app_value +unknown index.ts:42:5 unresolved_app_value index.ts:21:33 +unknown index.ts:42:5 unresolved_app_value index.ts:22:30 diff --git a/graph/test/typescript/hono/expected/compose/glob-module-list.entries b/graph/test/typescript/hono/expected/compose/glob-module-list.entries new file mode 100644 index 000000000..513595dc9 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/glob-module-list.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R12.1: literal import.meta.glob (eager and lazy) mounts each matched default-exported app; an unmatched module stays a root +# census: route_mount_root (SPEC R12.1; QD16 shape: a tier app mounting `mod.default` over a literal glob) +entry lazy/reports.page.ts:4:23 http +entry routes/orders/helpers.ts:5:25 http +entry routes/orders/orders.index.ts:4:24 http +entry routes/users/users.index.ts:4:21 http +entry routes/users/users.index.ts:5:25 http diff --git a/graph/test/typescript/hono/expected/compose/glob-module-list.tsv b/graph/test/typescript/hono/expected/compose/glob-module-list.tsv new file mode 100644 index 000000000..1a4cc9096 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/glob-module-list.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R12.1: literal import.meta.glob (eager and lazy) mounts each matched default-exported app; an unmatched module stays a root +# census: route_mount_root (SPEC R12.1; QD16 shape: a tier app mounting `mod.default` over a literal glob) +route POST /api/orders routes/orders/orders.index.ts:4:24 index.ts:21:13 0 +route GET /api/users routes/users/users.index.ts:4:21 index.ts:21:13 1 +route GET /api/users/:id routes/users/users.index.ts:5:25 index.ts:21:13 2 +route GET /pages/daily lazy/reports.page.ts:4:23 index.ts:21:13 3 +route GET /scratch routes/orders/helpers.ts:5:25 routes/orders/helpers.ts:4:17 0 +submount / index.ts:11:7 +submount / index.ts:11:7 +submount /api index.ts:22:5 +submount /pages index.ts:17:9 +regby lazy/reports.page.ts:1:1 lazy/reports.page.ts:4:23 GET /pages/daily index.ts:21:13 +regby routes/orders/helpers.ts:1:1 routes/orders/helpers.ts:5:25 GET /scratch routes/orders/helpers.ts:4:17 +regby routes/orders/orders.index.ts:1:1 routes/orders/orders.index.ts:4:24 POST /api/orders index.ts:21:13 +regby routes/users/users.index.ts:1:1 routes/users/users.index.ts:4:21 GET /api/users index.ts:21:13 +regby routes/users/users.index.ts:1:1 routes/users/users.index.ts:5:25 GET /api/users/:id index.ts:21:13 diff --git a/graph/test/typescript/hono/expected/compose/late-sub-registration.entries b/graph/test/typescript/hono/expected/compose/late-sub-registration.entries new file mode 100644 index 000000000..28a91553a --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/late-sub-registration.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: /late is registered after the copy: not a route of the served app, still an entry point +# census: route_mount_literal +entry index.ts:12:18 http +entry index.ts:7:19 http diff --git a/graph/test/typescript/hono/expected/compose/late-sub-registration.tsv b/graph/test/typescript/hono/expected/compose/late-sub-registration.tsv new file mode 100644 index 000000000..67b6cb588 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/late-sub-registration.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: /late is registered after the copy: not a route of the served app, still an entry point +# census: route_mount_literal +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /sub/early index.ts:7:19 index.ts:9:13 0 +submount /sub index.ts:10:5 +regby index.ts:1:1 index.ts:7:19 GET /sub/early index.ts:9:13 diff --git a/graph/test/typescript/hono/expected/compose/let-rebase-conditional.entries b/graph/test/typescript/hono/expected/compose/let-rebase-conditional.entries new file mode 100644 index 000000000..209c5d751 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/let-rebase-conditional.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: R8b (O27): routes registered through a module-level app variable after a conditional rebase (run-time base; literal base behind a condition) are partial dynamic_mount_prefix, never exact; /health, registered before the rebase, stays exact +# census: basepath (SPEC R8a/R8b, oracle note O27: a module-level app variable rebased under a condition) +entry index.ts:11:19 http +entry index.ts:14:20 http +entry index.ts:19:21 http +entry index.ts:26:21 http diff --git a/graph/test/typescript/hono/expected/compose/let-rebase-conditional.tsv b/graph/test/typescript/hono/expected/compose/let-rebase-conditional.tsv new file mode 100644 index 000000000..db989cbf5 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/let-rebase-conditional.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: R8b (O27): routes registered through a module-level app variable after a conditional rebase (run-time base; literal base behind a condition) are partial dynamic_mount_prefix, never exact; /health, registered before the rebase, stays exact +# census: basepath (SPEC R8a/R8b, oracle note O27: a module-level app variable rebased under a condition) +route GET /health index.ts:14:20 index.ts:13:11 0 +partial index.ts:11:19 GET dynamic_mount_prefix +partial index.ts:19:21 GET dynamic_mount_prefix +partial index.ts:26:21 GET dynamic_mount_prefix +regby index.ts:1:1 index.ts:14:20 GET /health index.ts:13:11 +unknown index.ts:19:1 dynamic_mount_prefix index.ts:19:21 +unknown index.ts:20:1 dynamic_mount_prefix index.ts:11:19 +unknown index.ts:26:1 dynamic_mount_prefix index.ts:26:21 diff --git a/graph/test/typescript/hono/expected/compose/let-rebase-literal.entries b/graph/test/typescript/hono/expected/compose/let-rebase-literal.entries new file mode 100644 index 000000000..b1a2c9558 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/let-rebase-literal.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: R8b control: an unconditional literal basePath reassignment is exact under /api for every later registration; /health before it stays at /health +# census: basepath (SPEC R8a/R8b control: an app variable re-pointed at a LITERAL basePath with no condition) +entry index.ts:10:20 http +entry index.ts:12:21 http +entry index.ts:7:19 http diff --git a/graph/test/typescript/hono/expected/compose/let-rebase-literal.tsv b/graph/test/typescript/hono/expected/compose/let-rebase-literal.tsv new file mode 100644 index 000000000..cc27c83bc --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/let-rebase-literal.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: R8b control: an unconditional literal basePath reassignment is exact under /api for every later registration; /health before it stays at /health +# census: basepath (SPEC R8a/R8b control: an app variable re-pointed at a LITERAL basePath with no condition) +route GET /health index.ts:10:20 index.ts:9:11 0 +route GET /api/version index.ts:12:21 index.ts:9:11 1 +route GET /api/users/:id index.ts:7:19 index.ts:9:11 2 +submount /api/users index.ts:13:5 +regby index.ts:1:1 index.ts:10:20 GET /health index.ts:9:11 +regby index.ts:1:1 index.ts:12:21 GET /api/version index.ts:9:11 +regby index.ts:1:1 index.ts:7:19 GET /api/users/:id index.ts:9:11 diff --git a/graph/test/typescript/hono/expected/compose/lib-dirs.entries b/graph/test/typescript/hono/expected/compose/lib-dirs.entries new file mode 100644 index 000000000..1e13bc688 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/lib-dirs.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: project files under lib/ and libs/ are project keys: /from-lib and /users/:id served by the lib/ app +# census: route_mount_literal (project sources under lib/ and libs/, a common monorepo layout) +entry lib/app.ts:4:22 http +entry libs/users/routes.ts:4:19 http diff --git a/graph/test/typescript/hono/expected/compose/lib-dirs.tsv b/graph/test/typescript/hono/expected/compose/lib-dirs.tsv new file mode 100644 index 000000000..cac74a7ad --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/lib-dirs.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: project files under lib/ and libs/ are project keys: /from-lib and /users/:id served by the lib/ app +# census: route_mount_literal (project sources under lib/ and libs/, a common monorepo layout) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /from-lib lib/app.ts:4:22 lib/app.ts:3:20 0 +route GET /users/:id libs/users/routes.ts:4:19 lib/app.ts:3:20 1 +submount /users index.ts:6:5 +regby lib/app.ts:1:1 lib/app.ts:4:22 GET /from-lib lib/app.ts:3:20 +regby libs/users/routes.ts:1:1 libs/users/routes.ts:4:19 GET /users/:id lib/app.ts:3:20 diff --git a/graph/test/typescript/hono/expected/compose/literal-prefix-method.entries b/graph/test/typescript/hono/expected/compose/literal-prefix-method.entries new file mode 100644 index 000000000..08c8cb56d --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/literal-prefix-method.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-05: a zero-argument method returning a literal is the constant /console; a field-built prefix stays partial +# census: mount_dynamic_prefix_call (violation hono-3-05, SPEC 3.2 G8) +entry index.ts:10:28 http +entry index.ts:25:29 http +entry index.ts:9:29 http diff --git a/graph/test/typescript/hono/expected/compose/literal-prefix-method.tsv b/graph/test/typescript/hono/expected/compose/literal-prefix-method.tsv new file mode 100644 index 000000000..736dc6849 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/literal-prefix-method.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-05: a zero-argument method returning a literal is the constant /console; a field-built prefix stays partial +# census: mount_dynamic_prefix_call (violation hono-3-05, SPEC 3.2 G8) +route GET /console/login index.ts:9:29 index.ts:35:13 0 +route GET /console/home index.ts:10:28 index.ts:35:13 1 +submount /console index.ts:37:5 +partial index.ts:25:29 GET dynamic_mount_prefix +regby index.ts:8:3 index.ts:10:28 GET /console/home index.ts:35:13 +regby index.ts:8:3 index.ts:9:29 GET /console/login index.ts:35:13 +unknown index.ts:39:1 dynamic_mount_prefix index.ts:25:29 diff --git a/graph/test/typescript/hono/expected/compose/loop-mount-method-fallback.entries b/graph/test/typescript/hono/expected/compose/loop-mount-method-fallback.entries new file mode 100644 index 000000000..146ae214a --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/loop-mount-method-fallback.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m29/hono-4-05: only the loop element class is partial; /admin/signin exact and not also partial +# census: mount_dynamic_prefix_call (stress-4 mutation m29 shape; violation hono-4-05) +entry index.ts:11:30 http +entry index.ts:24:30 http diff --git a/graph/test/typescript/hono/expected/compose/loop-mount-method-fallback.tsv b/graph/test/typescript/hono/expected/compose/loop-mount-method-fallback.tsv new file mode 100644 index 000000000..20cbf2b27 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/loop-mount-method-fallback.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m29/hono-4-05: only the loop element class is partial; /admin/signin exact and not also partial +# census: mount_dynamic_prefix_call (stress-4 mutation m29 shape; violation hono-4-05) +route GET /admin/signin index.ts:24:30 index.ts:35:13 0 +submount /admin index.ts:41:5 +partial index.ts:11:30 GET dynamic_mount_prefix +regby index.ts:23:3 index.ts:24:30 GET /admin/signin index.ts:35:13 +unknown index.ts:38:3 dynamic_mount_prefix index.ts:11:30 diff --git a/graph/test/typescript/hono/expected/compose/method-prefix-const-list.entries b/graph/test/typescript/hono/expected/compose/method-prefix-const-list.entries new file mode 100644 index 000000000..ac35c43e6 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/method-prefix-const-list.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-5-03: g.prefix(V) with a const V over a const list composes /v2/members exactly +# census: mount_dynamic_prefix_call (violation hono-5-03) +entry index.ts:16:22 http +entry members.ts:10:29 http +entry members.ts:9:33 http diff --git a/graph/test/typescript/hono/expected/compose/method-prefix-const-list.tsv b/graph/test/typescript/hono/expected/compose/method-prefix-const-list.tsv new file mode 100644 index 000000000..e565cdf78 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/method-prefix-const-list.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-5-03: g.prefix(V) with a const V over a const list composes /v2/members exactly +# census: mount_dynamic_prefix_call (violation hono-5-03) +route POST /v2/members/import members.ts:9:33 routes.ts:4:13 0 +route GET /v2/members/:id members.ts:10:29 routes.ts:4:13 1 +route GET /logo.png index.ts:16:22 routes.ts:4:13 2 +submount /v2/members index.ts:14:7 +regby index.ts:1:1 index.ts:16:22 GET /logo.png routes.ts:4:13 +regby members.ts:8:3 members.ts:10:29 GET /v2/members/:id routes.ts:4:13 +regby members.ts:8:3 members.ts:9:33 POST /v2/members/import routes.ts:4:13 diff --git a/graph/test/typescript/hono/expected/compose/mount-arg-never-root.entries b/graph/test/typescript/hono/expected/compose/mount-arg-never-root.entries new file mode 100644 index 000000000..6b177e927 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/mount-arg-never-root.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: route() on an unrecognised receiver: the mounted app's /daily is partial unresolved_mount, never a root; a non-app route() argument produces nothing +# census: neg_route_on_nonhono, route_mount_literal (SPEC 2 [iter2]: a mount argument is never a root) +entry index.ts:7:23 http diff --git a/graph/test/typescript/hono/expected/compose/mount-arg-never-root.tsv b/graph/test/typescript/hono/expected/compose/mount-arg-never-root.tsv new file mode 100644 index 000000000..b267ca7c6 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/mount-arg-never-root.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: route() on an unrecognised receiver: the mounted app's /daily is partial unresolved_mount, never a root; a non-app route() argument produces nothing +# census: neg_route_on_nonhono, route_mount_literal (SPEC 2 [iter2]: a mount argument is never a root) +partial index.ts:7:23 GET unresolved_mount +unknown index.ts:10:1 unresolved_mount index.ts:7:23 diff --git a/graph/test/typescript/hono/expected/compose/mount-chain.entries b/graph/test/typescript/hono/expected/compose/mount-chain.entries new file mode 100644 index 000000000..b14dd0fe0 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/mount-chain.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O8: the mount route ANY /legacy/* has its chain: cors then legacyAuth +# census: mount_foreign, use_scoped (oracle gap O8) +entry index.ts:7:39 http +entry index.ts:8:1 http diff --git a/graph/test/typescript/hono/expected/compose/mount-chain.tsv b/graph/test/typescript/hono/expected/compose/mount-chain.tsv new file mode 100644 index 000000000..e0449331b --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/mount-chain.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O8: the mount route ANY /legacy/* has its chain: cors then legacyAuth +# census: mount_foreign, use_scoped (oracle gap O8) +route ANY /legacy/* index.ts:8:1 index.ts:12:13 2 +mw lib:hono/cors.cors@index.ts:13:9 index.ts:8:1 ANY /legacy/* 0 index.ts:12:13 +mw index.ts:7:39 index.ts:8:1 ANY /legacy/* 1 index.ts:12:13 +regby index.ts:1:1 index.ts:8:1 ANY /legacy/* index.ts:12:13 diff --git a/graph/test/typescript/hono/expected/compose/nested-three-deep.entries b/graph/test/typescript/hono/expected/compose/nested-three-deep.entries new file mode 100644 index 000000000..c6c98fa86 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/nested-three-deep.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: comments under posts under /api basePath mounted at /: prefixes compose outer to inner +# census: route_mount_literal, basepath +entry comments.ts:5:19 http +entry comments.ts:6:32 http +entry posts.ts:6:23 http diff --git a/graph/test/typescript/hono/expected/compose/nested-three-deep.tsv b/graph/test/typescript/hono/expected/compose/nested-three-deep.tsv new file mode 100644 index 000000000..601907a2d --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/nested-three-deep.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: comments under posts under /api basePath mounted at /: prefixes compose outer to inner (http_mount prefix is the effective one: /api/posts under the basePath) +# census: route_mount_literal, basepath +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /api/posts/:postId posts.ts:6:23 index.ts:6:13 0 +route GET /api/posts/:postId/comments comments.ts:5:19 index.ts:6:13 1 +route DELETE /api/posts/:postId/comments/:commentId comments.ts:6:32 index.ts:6:13 2 +submount /:postId/comments posts.ts:7:7 +submount /api/posts api.ts:6:5 +submount / index.ts:8:5 +regby comments.ts:1:1 comments.ts:5:19 GET /api/posts/:postId/comments index.ts:6:13 +regby comments.ts:1:1 comments.ts:6:32 DELETE /api/posts/:postId/comments/:commentId index.ts:6:13 +regby posts.ts:1:1 posts.ts:6:23 GET /api/posts/:postId index.ts:6:13 diff --git a/graph/test/typescript/hono/expected/compose/opaque-module-list.entries b/graph/test/typescript/hono/expected/compose/opaque-module-list.entries new file mode 100644 index 000000000..e7c7e4c63 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/opaque-module-list.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R12.2: with an opaque mod.default mount, unimported unserved default-exported apps are partial unresolved_mount; imported, served and entry apps are not +# census: route_mount_root (SPEC R12.2: an opaque run-time module list) +entry routes/billing.ts:4:26 http +entry routes/status.ts:4:23 http +entry shared.ts:5:24 http +entry worker.ts:5:21 http diff --git a/graph/test/typescript/hono/expected/compose/opaque-module-list.tsv b/graph/test/typescript/hono/expected/compose/opaque-module-list.tsv new file mode 100644 index 000000000..88ffa4792 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/opaque-module-list.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R12.2: with an opaque mod.default mount, unimported unserved default-exported apps are partial unresolved_mount; imported, served and entry apps are not +# census: route_mount_root (SPEC R12.2: an opaque run-time module list) +route GET /version shared.ts:5:24 server.ts:10:13 0 +route GET /tick worker.ts:5:21 worker.ts:4:16 0 +submount / server.ts:11:5 +partial routes/billing.ts:4:26 GET unresolved_mount +partial routes/status.ts:4:23 GET unresolved_mount +regby shared.ts:1:1 shared.ts:5:24 GET /version server.ts:10:13 +regby worker.ts:1:1 worker.ts:5:21 GET /tick worker.ts:4:16 +unknown server.ts:16:5 unresolved_mount routes/billing.ts:4:26 +unknown server.ts:16:5 unresolved_mount routes/status.ts:4:23 +unknown server.ts:16:5 unresolved_mount server.ts:13:1 diff --git a/graph/test/typescript/hono/expected/compose/oracle-eval-cycle.entries b/graph/test/typescript/hono/expected/compose/oracle-eval-cycle.entries new file mode 100644 index 000000000..eb926d3f2 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/oracle-eval-cycle.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O15: the oracle terminates on the destructure-and-rebuild cycle and is linear on a 25-link builder chain; the app's route, middleware and ctx flow are unaffected +# census: new_hono_inside_function (oracle gap O15) +entry index.ts:12:23 http +entry index.ts:16:33 http diff --git a/graph/test/typescript/hono/expected/compose/oracle-eval-cycle.tsv b/graph/test/typescript/hono/expected/compose/oracle-eval-cycle.tsv new file mode 100644 index 000000000..cef0baf76 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/oracle-eval-cycle.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O15: the oracle terminates on the destructure-and-rebuild cycle and is linear on a 25-link builder chain; the app's route, middleware and ctx flow are unaffected +# census: new_hono_inside_function (oracle gap O15) +route POST /threads/:id/resume index.ts:16:33 index.ts:11:13 1 +mw index.ts:12:23 index.ts:16:33 POST /threads/:id/resume 0 index.ts:11:13 +ctx index.ts:12:23 index.ts:16:33 saver +regby index.ts:1:1 index.ts:16:33 POST /threads/:id/resume index.ts:11:13 diff --git a/graph/test/typescript/hono/expected/compose/plugin-helper-list.entries b/graph/test/typescript/hono/expected/compose/plugin-helper-list.entries new file mode 100644 index 000000000..8050cea71 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/plugin-helper-list.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O20/R13b: plugin apps reached through a filtered, push-sorted helper list mount at their element paths, exact plus conditional; never roots +# census: app_request_test (oracle gap O20; SPEC R13 / R13b) +entry app.test.ts:13:19 http +entry app.ts:9:16 http +entry plugins/magic-link.ts:5:24 http +entry plugins/magic-link.ts:6:22 http diff --git a/graph/test/typescript/hono/expected/compose/plugin-helper-list.tsv b/graph/test/typescript/hono/expected/compose/plugin-helper-list.tsv new file mode 100644 index 000000000..0803a00d1 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/plugin-helper-list.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O20/R13b: plugin apps reached through a filtered, push-sorted helper list mount at their element paths, exact plus conditional; never roots +# census: app_request_test (oracle gap O20; SPEC R13 / R13b) +route GET / app.ts:9:16 app.ts:8:15 0 +route GET /api/custom-test app.test.ts:13:19 app.ts:8:15 1 +route POST /auth/magic-link/request plugins/magic-link.ts:5:24 app.ts:8:15 2 +route GET /auth/magic-link/verify plugins/magic-link.ts:6:22 app.ts:8:15 3 +request app.test.ts:17:30 GET /api/custom-test app.test.ts:13:19 +submount /api/custom-test mount.ts:13:9 +submount /auth/magic-link mount.ts:13:9 +regby app.test.ts:11:1 app.test.ts:13:19 GET /api/custom-test app.ts:8:15 +regby app.ts:7:1 app.ts:9:16 GET / app.ts:8:15 +regby plugins/magic-link.ts:1:1 plugins/magic-link.ts:5:24 POST /auth/magic-link/request app.ts:8:15 +regby plugins/magic-link.ts:1:1 plugins/magic-link.ts:6:22 GET /auth/magic-link/verify app.ts:8:15 +unknown mount.ts:13:5 conditional_registration app.test.ts:13:19 +unknown mount.ts:13:5 conditional_registration plugins/magic-link.ts:5:24 +unknown mount.ts:13:5 conditional_registration plugins/magic-link.ts:6:22 diff --git a/graph/test/typescript/hono/expected/compose/plugin-instance-never-mounted.entries b/graph/test/typescript/hono/expected/compose/plugin-instance-never-mounted.entries new file mode 100644 index 000000000..7a84d3273 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/plugin-instance-never-mounted.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O24/R13: a plugin factory's second instance that nothing imports or mounts is no root at its local paths; a never-imported plugin module's handlers are partial unresolved_mount +# census: plugin_list_mount (oracle gap O24; SPEC R13 / R13b) +entry app.ts:12:16 http +entry plugins/legacy-export.ts:8:22 http +entry plugins/magic-link.ts:12:27 http +entry plugins/magic-link.ts:13:25 http diff --git a/graph/test/typescript/hono/expected/compose/plugin-instance-never-mounted.tsv b/graph/test/typescript/hono/expected/compose/plugin-instance-never-mounted.tsv new file mode 100644 index 000000000..b310a7f4f --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/plugin-instance-never-mounted.tsv @@ -0,0 +1,13 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O24/R13: a plugin factory's second instance that nothing imports or mounts is no root at its local paths; a never-imported plugin module's handlers are partial unresolved_mount +# census: plugin_list_mount (oracle gap O24; SPEC R13 / R13b) +route GET / app.ts:12:16 app.ts:11:15 0 +route POST /auth/magic-link/request plugins/magic-link.ts:12:27 app.ts:11:15 1 +route GET /auth/magic-link/verify plugins/magic-link.ts:13:25 app.ts:11:15 2 +submount /auth/magic-link app.ts:6:41 +partial plugins/legacy-export.ts:8:22 GET unresolved_mount +regby app.ts:10:1 app.ts:12:16 GET / app.ts:11:15 +regby plugins/magic-link.ts:10:1 plugins/magic-link.ts:12:27 POST /auth/magic-link/request app.ts:11:15 +regby plugins/magic-link.ts:10:1 plugins/magic-link.ts:13:25 GET /auth/magic-link/verify app.ts:11:15 +unknown app.ts:6:37 conditional_registration plugins/magic-link.ts:12:27 +unknown app.ts:6:37 conditional_registration plugins/magic-link.ts:13:25 +unknown plugins/legacy-export.ts:9:79 unresolved_mount plugins/legacy-export.ts:8:22 diff --git a/graph/test/typescript/hono/expected/compose/plugin-register-method.entries b/graph/test/typescript/hono/expected/compose/plugin-register-method.entries new file mode 100644 index 000000000..c4c998c5e --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/plugin-register-method.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R13b re-derived: plugin apps mounted by register(app) over a filtered list of known plugins are exact at their prefixes with conditional_registration +# census: route_mount_literal (oracle gap O11 residual; SPEC 2 a mount argument is never a root) +entry index.ts:19:14 http +entry plugins/media.ts:6:16 http +entry plugins/media.ts:7:22 http +entry plugins/variables.ts:5:24 http +entry plugins/variables.ts:8:23 http diff --git a/graph/test/typescript/hono/expected/compose/plugin-register-method.tsv b/graph/test/typescript/hono/expected/compose/plugin-register-method.tsv new file mode 100644 index 000000000..8e23db2c7 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/plugin-register-method.tsv @@ -0,0 +1,19 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R13b re-derived: plugin apps mounted by register(app) over a filtered list of known plugins are exact at their prefixes with conditional_registration +# census: route_mount_literal (oracle gap O11 residual; SPEC 2 a mount argument is never a root) +route GET / index.ts:19:14 index.ts:18:13 0 +route GET /api/media plugins/media.ts:6:16 index.ts:18:13 1 +route DELETE /api/media/:id plugins/media.ts:7:22 index.ts:18:13 2 +route GET /api/variables/:key plugins/variables.ts:5:24 index.ts:18:13 3 +route POST /admin/variables plugins/variables.ts:8:23 index.ts:18:13 4 +submount /api/media plugins/media.ts:14:9 +submount /api/variables plugins/variables.ts:13:9 +submount /admin/variables plugins/variables.ts:14:9 +regby index.ts:1:1 index.ts:19:14 GET / index.ts:18:13 +regby plugins/media.ts:4:1 plugins/media.ts:6:16 GET /api/media index.ts:18:13 +regby plugins/media.ts:4:1 plugins/media.ts:7:22 DELETE /api/media/:id index.ts:18:13 +regby plugins/variables.ts:1:1 plugins/variables.ts:5:24 GET /api/variables/:key index.ts:18:13 +regby plugins/variables.ts:1:1 plugins/variables.ts:8:23 POST /admin/variables index.ts:18:13 +unknown plugins/media.ts:14:5 conditional_registration plugins/media.ts:6:16 +unknown plugins/media.ts:14:5 conditional_registration plugins/media.ts:7:22 +unknown plugins/variables.ts:13:5 conditional_registration plugins/variables.ts:5:24 +unknown plugins/variables.ts:14:5 conditional_registration plugins/variables.ts:8:23 diff --git a/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.entries b/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.entries new file mode 100644 index 000000000..2046746e6 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R16 control: a plugin mounted by production AND by a test app is a route of both; a test-only plugin stays on the test app; a test request for a production-only plugin path is unserved on the test app +# census: app_request_test, plugin_list_mount (SPEC R16 control; R6, R13b) +entry plugins/billing.ts:5:26 http +entry plugins/search.ts:5:17 http +entry plugins/search.ts:6:25 http +entry server.ts:7:14 http +entry test/search.test.ts:15:37 http diff --git a/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.tsv b/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.tsv new file mode 100644 index 000000000..3867ed3ff --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.tsv @@ -0,0 +1,32 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R16 control: a plugin mounted by production AND by a test app is a route of both; a test-only plugin stays on the test app; a test request for a production-only plugin path is unserved on the test app +# census: app_request_test, plugin_list_mount (SPEC R16 control; R6, R13b) +route GET / server.ts:7:14 server.ts:6:13 0 +route GET /api/search plugins/search.ts:5:17 server.ts:6:13 1 +route POST /api/search/reindex plugins/search.ts:6:25 server.ts:6:13 2 +route GET /api/billing/invoices plugins/billing.ts:5:26 server.ts:6:13 3 +route GET /api/search plugins/search.ts:5:17 test/search.test.ts:8:15 0 +route POST /api/search/reindex plugins/search.ts:6:25 test/search.test.ts:8:15 1 +route GET /api/search plugins/search.ts:5:17 test/search.test.ts:17:15 0 +route POST /api/search/reindex plugins/search.ts:6:25 test/search.test.ts:17:15 1 +route GET /__probe test/search.test.ts:15:37 test/search.test.ts:17:15 2 +request test/search.test.ts:14:43 GET /__probe test/search.test.ts:15:37 +request test/search.test.ts:7:46 GET /api/search plugins/search.ts:5:17 +request test/search.test.ts:7:46 POST /api/search/reindex plugins/search.ts:6:25 +submount /api/search plugins.ts:10:48 +submount /api/billing plugins.ts:10:48 +submount /api/search plugins.ts:10:48 +submount /api/search plugins.ts:10:48 +submount /__probe plugins.ts:10:48 +regby plugins/billing.ts:1:1 plugins/billing.ts:5:26 GET /api/billing/invoices server.ts:6:13 +regby plugins/search.ts:1:1 plugins/search.ts:5:17 GET /api/search server.ts:6:13 +regby plugins/search.ts:1:1 plugins/search.ts:5:17 GET /api/search test/search.test.ts:17:15 +regby plugins/search.ts:1:1 plugins/search.ts:5:17 GET /api/search test/search.test.ts:8:15 +regby plugins/search.ts:1:1 plugins/search.ts:6:25 POST /api/search/reindex server.ts:6:13 +regby plugins/search.ts:1:1 plugins/search.ts:6:25 POST /api/search/reindex test/search.test.ts:17:15 +regby plugins/search.ts:1:1 plugins/search.ts:6:25 POST /api/search/reindex test/search.test.ts:8:15 +regby server.ts:1:1 server.ts:7:14 GET / server.ts:6:13 +regby test/search.test.ts:14:43 test/search.test.ts:15:37 GET /__probe test/search.test.ts:17:15 +unserved test/search.test.ts:14:43 GET /api/billing/invoices +unknown plugins.ts:10:44 conditional_registration plugins/billing.ts:5:26 +unknown plugins.ts:10:44 conditional_registration plugins/search.ts:5:17 +unknown plugins.ts:10:44 conditional_registration plugins/search.ts:6:25 diff --git a/graph/test/typescript/hono/expected/compose/rebased-holder-getter.entries b/graph/test/typescript/hono/expected/compose/rebased-holder-getter.entries new file mode 100644 index 000000000..e945ce087 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/rebased-holder-getter.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-01/R8a through a getter: both controller routes partial dynamic_mount_prefix, no exact rows +# census: basepath (violation hono-3-01, SPEC R8a; holder read through a getter) +entry index.ts:29:20 http +entry index.ts:30:22 http diff --git a/graph/test/typescript/hono/expected/compose/rebased-holder-getter.tsv b/graph/test/typescript/hono/expected/compose/rebased-holder-getter.tsv new file mode 100644 index 000000000..295673df5 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/rebased-holder-getter.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-01/R8a through a getter: both controller routes partial dynamic_mount_prefix, no exact rows +# census: basepath (violation hono-3-01, SPEC R8a; holder read through a getter) +partial index.ts:29:20 GET dynamic_mount_prefix +partial index.ts:30:22 POST dynamic_mount_prefix +unknown index.ts:34:5 dynamic_mount_prefix index.ts:29:20 +unknown index.ts:34:5 dynamic_mount_prefix index.ts:30:22 diff --git a/graph/test/typescript/hono/expected/compose/rebased-holder-mount.entries b/graph/test/typescript/hono/expected/compose/rebased-holder-mount.entries new file mode 100644 index 000000000..465dd14fe --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/rebased-holder-mount.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-01/R8a: a sub-app mounted with route() into a rebasable holder is one partial dynamic_mount_prefix per route, no exact row +# census: basepath (violation hono-3-01, SPEC R8a) +entry index.ts:9:21 http diff --git a/graph/test/typescript/hono/expected/compose/rebased-holder-mount.tsv b/graph/test/typescript/hono/expected/compose/rebased-holder-mount.tsv new file mode 100644 index 000000000..754d5101b --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/rebased-holder-mount.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-01/R8a: a sub-app mounted with route() into a rebasable holder is one partial dynamic_mount_prefix per route, no exact row +# census: basepath (violation hono-3-01, SPEC R8a) +partial index.ts:9:21 GET dynamic_mount_prefix +unknown index.ts:24:1 dynamic_mount_prefix index.ts:9:21 diff --git a/graph/test/typescript/hono/expected/compose/rebased-holder-one-partial.entries b/graph/test/typescript/hono/expected/compose/rebased-holder-one-partial.entries new file mode 100644 index 000000000..2427f54ae --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/rebased-holder-one-partial.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-2-04 residue/R8: one partial with one reason (dynamic_mount_prefix) per route, mounted through a build context +# census: prop_receiver_route (violation hono-2-04 residue, SPEC R8 / R8a) +entry index.ts:16:16 http +entry index.ts:17:19 http diff --git a/graph/test/typescript/hono/expected/compose/rebased-holder-one-partial.tsv b/graph/test/typescript/hono/expected/compose/rebased-holder-one-partial.tsv new file mode 100644 index 000000000..8ea4fe513 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/rebased-holder-one-partial.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-2-04 residue/R8: one partial with one reason (dynamic_mount_prefix) per route, mounted through a build context +# census: prop_receiver_route (violation hono-2-04 residue, SPEC R8 / R8a) +partial index.ts:16:16 GET dynamic_mount_prefix +partial index.ts:17:19 GET dynamic_mount_prefix +unknown index.ts:38:5 dynamic_mount_prefix index.ts:16:16 +unknown index.ts:38:5 dynamic_mount_prefix index.ts:17:19 diff --git a/graph/test/typescript/hono/expected/compose/rebased-two-mounts.entries b/graph/test/typescript/hono/expected/compose/rebased-two-mounts.entries new file mode 100644 index 000000000..162905f8a --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/rebased-two-mounts.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m33: both sub-apps on the rebasable holder are one partial each (dynamic_mount_prefix), no exact rows +# census: basepath (stress-4 mutation m33 shape; violations hono-3-01, hono-4-04) +entry index.ts:11:30 http +entry index.ts:9:29 http diff --git a/graph/test/typescript/hono/expected/compose/rebased-two-mounts.tsv b/graph/test/typescript/hono/expected/compose/rebased-two-mounts.tsv new file mode 100644 index 000000000..c934ad7be --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/rebased-two-mounts.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m33: both sub-apps on the rebasable holder are one partial each (dynamic_mount_prefix), no exact rows +# census: basepath (stress-4 mutation m33 shape; violations hono-3-01, hono-4-04) +partial index.ts:11:30 POST dynamic_mount_prefix +partial index.ts:9:29 GET dynamic_mount_prefix +unknown index.ts:29:1 dynamic_mount_prefix index.ts:9:29 +unknown index.ts:30:1 dynamic_mount_prefix index.ts:11:30 diff --git a/graph/test/typescript/hono/expected/compose/reexport-barrel.entries b/graph/test/typescript/hono/expected/compose/reexport-barrel.entries new file mode 100644 index 000000000..e0898650b --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/reexport-barrel.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: sub-app resolved through export { default as } from +# census: reexport_default_as, route_mount_literal +entry routes/books.ts:4:16 http diff --git a/graph/test/typescript/hono/expected/compose/reexport-barrel.tsv b/graph/test/typescript/hono/expected/compose/reexport-barrel.tsv new file mode 100644 index 000000000..768aac219 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/reexport-barrel.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: sub-app resolved through export { default as } from +# census: reexport_default_as, route_mount_literal +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /books routes/books.ts:4:16 index.ts:6:13 0 +submount /books index.ts:7:5 +regby routes/books.ts:1:1 routes/books.ts:4:16 GET /books index.ts:6:13 diff --git a/graph/test/typescript/hono/expected/compose/register-fn.entries b/graph/test/typescript/hono/expected/compose/register-fn.entries new file mode 100644 index 000000000..4182914ae --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/register-fn.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: registerUsers' routes spliced at the call (order 1-2); typeof-param registration mounted at /v1 +# census: app_factory_fn +entry index.ts:13:18 http +entry index.ts:9:14 http +entry monitors.ts:6:24 http +entry users.ts:4:21 http +entry users.ts:5:22 http diff --git a/graph/test/typescript/hono/expected/compose/register-fn.tsv b/graph/test/typescript/hono/expected/compose/register-fn.tsv new file mode 100644 index 000000000..9f3317782 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/register-fn.tsv @@ -0,0 +1,14 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: registerUsers' routes spliced at the call (order 1-2); typeof-param registration mounted at /v1 +# census: app_factory_fn +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET / index.ts:9:14 index.ts:8:13 0 +route GET /users users.ts:4:21 index.ts:8:13 1 +route POST /users users.ts:5:22 index.ts:8:13 2 +route GET /v1/monitors monitors.ts:6:24 index.ts:8:13 3 +route GET /last index.ts:13:18 index.ts:8:13 4 +submount /v1 index.ts:12:5 +regby index.ts:1:1 index.ts:13:18 GET /last index.ts:8:13 +regby index.ts:1:1 index.ts:9:14 GET / index.ts:8:13 +regby monitors.ts:5:1 monitors.ts:6:24 GET /v1/monitors index.ts:8:13 +regby users.ts:3:1 users.ts:4:21 GET /users index.ts:8:13 +regby users.ts:3:1 users.ts:5:22 POST /users index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/compose/register-loop-plugins.entries b/graph/test/typescript/hono/expected/compose/register-loop-plugins.entries new file mode 100644 index 000000000..17b46a775 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/register-loop-plugins.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O11: plugins mounted in a loop by an uncalled register(app: Hono): partial unresolved_app_value, never roots +# census: route_mount_literal (oracle gap O11; SPEC 2 a mount argument is never a root) +entry plugins.ts:3:45 http +entry plugins.ts:4:41 http diff --git a/graph/test/typescript/hono/expected/compose/register-loop-plugins.tsv b/graph/test/typescript/hono/expected/compose/register-loop-plugins.tsv new file mode 100644 index 000000000..a97d414b4 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/register-loop-plugins.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O11: plugins mounted in a loop by an uncalled register(app: Hono): partial unresolved_app_value, never roots +# census: route_mount_literal (oracle gap O11; SPEC 2 a mount argument is never a root) +partial plugins.ts:3:45 GET unresolved_app_value +partial plugins.ts:4:41 GET unresolved_app_value +unknown register.ts:9:5 unresolved_app_value plugins.ts:3:45 +unknown register.ts:9:5 unresolved_app_value plugins.ts:4:41 diff --git a/graph/test/typescript/hono/expected/compose/registered-by.entries b/graph/test/typescript/hono/expected/compose/registered-by.entries new file mode 100644 index 000000000..d10433862 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/registered-by.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: route_registered_by: registerGetMonitor and registerListMonitors for the /v1 routes, the module initializer for / +# census: app_factory_fn (SPEC 4 [iter2] route_registered_by: the method enclosing each registering call) +entry index.ts:11:14 http +entry monitors.ts:4:28 http +entry monitors.ts:7:24 http +entry monitors.ts:8:25 http diff --git a/graph/test/typescript/hono/expected/compose/registered-by.tsv b/graph/test/typescript/hono/expected/compose/registered-by.tsv new file mode 100644 index 000000000..a93b00ee9 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/registered-by.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: route_registered_by: registerGetMonitor and registerListMonitors for the /v1 routes, the module initializer for / +# census: app_factory_fn (SPEC 4 [iter2] route_registered_by: the method enclosing each registering call) +route GET / index.ts:11:14 index.ts:10:13 0 +route GET /v1/monitors/:id monitors.ts:4:28 index.ts:10:13 1 +route GET /v1/monitors monitors.ts:7:24 index.ts:10:13 2 +route POST /v1/monitors monitors.ts:8:25 index.ts:10:13 3 +submount /v1 index.ts:12:5 +regby index.ts:1:1 index.ts:11:14 GET / index.ts:10:13 +regby monitors.ts:3:1 monitors.ts:4:28 GET /v1/monitors/:id index.ts:10:13 +regby monitors.ts:6:1 monitors.ts:7:24 GET /v1/monitors index.ts:10:13 +regby monitors.ts:6:1 monitors.ts:8:25 POST /v1/monitors index.ts:10:13 diff --git a/graph/test/typescript/hono/expected/compose/route-and-partial.entries b/graph/test/typescript/hono/expected/compose/route-and-partial.entries new file mode 100644 index 000000000..f9a2b0768 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/route-and-partial.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O12: /memory is a route; the same handler under ctx.server is not also partial +# census: app_factory_fn (oracle gap O12) +entry index.ts:9:22 http diff --git a/graph/test/typescript/hono/expected/compose/route-and-partial.tsv b/graph/test/typescript/hono/expected/compose/route-and-partial.tsv new file mode 100644 index 000000000..f8c0427ad --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/route-and-partial.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O12: /memory is a route; the same handler under ctx.server is not also partial +# census: app_factory_fn (oracle gap O12) +route GET /memory index.ts:9:22 index.ts:12:13 0 +regby index.ts:8:1 index.ts:9:22 GET /memory index.ts:12:13 diff --git a/graph/test/typescript/hono/expected/compose/route-cross-module.entries b/graph/test/typescript/hono/expected/compose/route-cross-module.entries new file mode 100644 index 000000000..5fe97f441 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/route-cross-module.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: default-exported sub-app's three routes under /users; / local maps to /users +# census: route_mount_literal, export_default_app +entry index.ts:8:14 http +entry users.ts:5:16 http +entry users.ts:6:19 http +entry users.ts:7:17 http diff --git a/graph/test/typescript/hono/expected/compose/route-cross-module.tsv b/graph/test/typescript/hono/expected/compose/route-cross-module.tsv new file mode 100644 index 000000000..4c86cea9e --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/route-cross-module.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: default-exported sub-app's three routes under /users; / local maps to /users +# census: route_mount_literal, export_default_app +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET / index.ts:8:14 index.ts:6:13 0 +route GET /users users.ts:5:16 index.ts:6:13 1 +route GET /users/:id users.ts:6:19 index.ts:6:13 2 +route POST /users users.ts:7:17 index.ts:6:13 3 +submount /users index.ts:9:5 +regby index.ts:1:1 index.ts:8:14 GET / index.ts:6:13 +regby users.ts:1:1 users.ts:5:16 GET /users index.ts:6:13 +regby users.ts:1:1 users.ts:6:19 GET /users/:id index.ts:6:13 +regby users.ts:1:1 users.ts:7:17 POST /users index.ts:6:13 diff --git a/graph/test/typescript/hono/expected/compose/route-root.entries b/graph/test/typescript/hono/expected/compose/route-root.entries new file mode 100644 index 000000000..80ebefa61 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/route-root.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: route('/') merges unprefixed, chained +# census: route_mount_root +entry index.ts:10:20 http +entry index.ts:6:21 http +entry index.ts:7:22 http diff --git a/graph/test/typescript/hono/expected/compose/route-root.tsv b/graph/test/typescript/hono/expected/compose/route-root.tsv new file mode 100644 index 000000000..42249d8d0 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/route-root.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: route('/') merges unprefixed, chained +# census: route_mount_root +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /login index.ts:6:21 index.ts:12:13 0 +route POST /logout index.ts:7:22 index.ts:12:13 1 +route GET /about index.ts:10:20 index.ts:12:13 2 +submount / index.ts:13:20 +submount / index.ts:13:37 +regby index.ts:1:1 index.ts:10:20 GET /about index.ts:12:13 +regby index.ts:1:1 index.ts:6:21 POST /login index.ts:12:13 +regby index.ts:1:1 index.ts:7:22 POST /logout index.ts:12:13 diff --git a/graph/test/typescript/hono/expected/compose/same-sub-twice.entries b/graph/test/typescript/hono/expected/compose/same-sub-twice.entries new file mode 100644 index 000000000..d73a13e61 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/same-sub-twice.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: one handler, two routes +# census: route_mount_literal +entry index.ts:6:23 http diff --git a/graph/test/typescript/hono/expected/compose/same-sub-twice.tsv b/graph/test/typescript/hono/expected/compose/same-sub-twice.tsv new file mode 100644 index 000000000..e154ffa45 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/same-sub-twice.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: one handler, two routes +# census: route_mount_literal +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /a/health index.ts:6:23 index.ts:8:13 0 +route GET /b/health index.ts:6:23 index.ts:8:13 1 +submount /a index.ts:9:5 +submount /b index.ts:10:5 +regby index.ts:1:1 index.ts:6:23 GET /a/health index.ts:8:13 +regby index.ts:1:1 index.ts:6:23 GET /b/health index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/compose/self-basepath-field.entries b/graph/test/typescript/hono/expected/compose/self-basepath-field.entries new file mode 100644 index 000000000..5d634c2c3 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/self-basepath-field.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-04/R8: one partial for /x (dynamic_mount_prefix), not one row per path +# census: basepath (violation hono-2-04, SPEC R8) +entry index.ts:20:20 http diff --git a/graph/test/typescript/hono/expected/compose/self-basepath-field.tsv b/graph/test/typescript/hono/expected/compose/self-basepath-field.tsv new file mode 100644 index 000000000..95a84632f --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/self-basepath-field.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-04/R8: one partial for /x (dynamic_mount_prefix), not one row per path +# census: basepath (violation hono-2-04, SPEC R8) +partial index.ts:20:20 GET dynamic_mount_prefix +unknown index.ts:20:1 dynamic_mount_prefix index.ts:20:20 diff --git a/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.entries b/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.entries new file mode 100644 index 000000000..8ed00f3be --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m35b/hono-5-02: per-call apps; /reports guard 0; /exports guard 0 (from reports), guard 1, quota 2, declared dynamic_middleware_list; exact plus conditional +# census: route_mount_root (stress-5 mutation m35b shape; violation hono-5-02, SPEC R6 and R13b) +entry index.ts:12:21 http +entry index.ts:30:23 http +entry index.ts:37:23 http +entry index.ts:9:21 http diff --git a/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.tsv b/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.tsv new file mode 100644 index 000000000..d89579ebe --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m35b/hono-5-02: per-call apps; /reports guard 0; /exports guard 0 (from reports), guard 1, quota 2, declared dynamic_middleware_list; exact plus conditional +# census: route_mount_root (stress-5 mutation m35b shape; violation hono-5-02, SPEC R6 and R13b) +route GET /reports index.ts:30:23 index.ts:44:13 1 +route GET /exports index.ts:37:23 index.ts:44:13 4 +mw index.ts:9:21 index.ts:30:23 GET /reports 0 index.ts:44:13 +mw index.ts:9:21 index.ts:37:23 GET /exports 0 index.ts:44:13 +mw index.ts:9:21 index.ts:37:23 GET /exports 1 index.ts:44:13 +mw index.ts:12:21 index.ts:37:23 GET /exports 2 index.ts:44:13 +submount / index.ts:46:27 +submount / index.ts:46:27 +regby index.ts:28:3 index.ts:30:23 GET /reports index.ts:44:13 +regby index.ts:35:3 index.ts:37:23 GET /exports index.ts:44:13 +unknown index.ts:37:5 dynamic_middleware_list index.ts:37:23 +unknown index.ts:46:23 conditional_registration index.ts:30:23 +unknown index.ts:46:23 conditional_registration index.ts:37:23 diff --git a/graph/test/typescript/hono/expected/compose/sync-wrapper-mount.entries b/graph/test/typescript/hono/expected/compose/sync-wrapper-mount.entries new file mode 100644 index 000000000..ed91e70bc --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/sync-wrapper-mount.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m38b/hono-5-06: the sync wrapper serves both inner routes behind its use(*) +# census: bun_serve (stress-5 mutation m38b shape; violation hono-5-06) +entry main.ts:10:21 http +entry main.ts:9:21 http +entry wrap.ts:5:20 http diff --git a/graph/test/typescript/hono/expected/compose/sync-wrapper-mount.tsv b/graph/test/typescript/hono/expected/compose/sync-wrapper-mount.tsv new file mode 100644 index 000000000..2894ad635 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/sync-wrapper-mount.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m38b/hono-5-06: the sync wrapper serves both inner routes behind its use(*) +# census: bun_serve (stress-5 mutation m38b shape; violation hono-5-06) +route GET /hello main.ts:9:21 wrap.ts:4:19 1 +route POST /echo main.ts:10:21 wrap.ts:4:19 2 +mw wrap.ts:5:20 main.ts:9:21 GET /hello 0 wrap.ts:4:19 +mw wrap.ts:5:20 main.ts:10:21 POST /echo 0 wrap.ts:4:19 +submount / wrap.ts:9:11 +regby main.ts:1:1 main.ts:10:21 POST /echo wrap.ts:4:19 +regby main.ts:1:1 main.ts:9:21 GET /hello wrap.ts:4:19 diff --git a/graph/test/typescript/hono/expected/compose/test-plugin-own-app.entries b/graph/test/typescript/hono/expected/compose/test-plugin-own-app.entries new file mode 100644 index 000000000..89d9ef383 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/test-plugin-own-app.entries @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R16/hono-6-02: test-built plugins (declarative definePlugin routes, register(app), direct mountPlugin) are routes ONLY of the test app that mounted them; production serves only its core plugins; a request inside an assertion argument reaches its own app (O26); a register-only plugin's absent routes mount nothing (no unresolved_mount) +# census: app_request_test, plugin_list_mount (SPEC R16; R6, R13b; violation hono-6-02; oracle gap O26) +entry __tests__/define-plugin.test.ts:10:37 http +entry __tests__/define-plugin.test.ts:22:47 http +entry __tests__/mount.test.ts:8:14 http +entry app.ts:10:22 http +entry plugins/audit.ts:5:16 http +entry plugins/audit.ts:6:19 http +entry plugins/tenants.ts:6:14 http diff --git a/graph/test/typescript/hono/expected/compose/test-plugin-own-app.tsv b/graph/test/typescript/hono/expected/compose/test-plugin-own-app.tsv new file mode 100644 index 000000000..014a66998 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/test-plugin-own-app.tsv @@ -0,0 +1,38 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R16/hono-6-02: test-built plugins (declarative definePlugin routes, register(app), direct mountPlugin) are routes ONLY of the test app that mounted them; production serves only its core plugins; a request inside an assertion argument reaches its own app (O26); a register-only plugin's absent routes mount nothing (no unresolved_mount) +# census: app_request_test, plugin_list_mount (SPEC R16; R6, R13b; violation hono-6-02; oracle gap O26) +route GET /api/defined __tests__/define-plugin.test.ts:10:37 __tests__/define-plugin.test.ts:12:17 0 +route GET /imp __tests__/define-plugin.test.ts:22:47 __tests__/define-plugin.test.ts:25:17 0 +route GET /alpha __tests__/mount.test.ts:8:14 __tests__/mount.test.ts:13:15 0 +route GET /beta __tests__/mount.test.ts:8:14 __tests__/mount.test.ts:20:15 0 +route GET /gamma __tests__/mount.test.ts:8:14 __tests__/mount.test.ts:27:15 0 +route GET /health app.ts:10:22 app.ts:9:15 0 +route GET /admin/audit plugins/audit.ts:5:16 app.ts:9:15 1 +route GET /admin/audit/:id plugins/audit.ts:6:19 app.ts:9:15 2 +route GET /admin/tenants plugins/tenants.ts:6:14 app.ts:9:15 3 +request __tests__/define-plugin.test.ts:18:37 GET /imp __tests__/define-plugin.test.ts:22:47 +request __tests__/define-plugin.test.ts:9:63 GET /api/defined __tests__/define-plugin.test.ts:10:37 +request __tests__/mount.test.ts:12:25 GET /alpha __tests__/mount.test.ts:8:14 +request __tests__/mount.test.ts:19:31 GET /beta __tests__/mount.test.ts:8:14 +request __tests__/mount.test.ts:26:30 GET /gamma __tests__/mount.test.ts:8:14 +submount /api/defined sdk.ts:34:11 +submount /imp __tests__/define-plugin.test.ts:22:13 +submount /alpha sdk.ts:34:11 +submount /beta sdk.ts:34:11 +submount /gamma sdk.ts:34:11 +submount /admin/audit sdk.ts:34:11 +submount /admin/tenants plugins/tenants.ts:13:9 +regby __tests__/define-plugin.test.ts:21:7 __tests__/define-plugin.test.ts:22:47 GET /imp __tests__/define-plugin.test.ts:25:17 +regby __tests__/define-plugin.test.ts:9:63 __tests__/define-plugin.test.ts:10:37 GET /api/defined __tests__/define-plugin.test.ts:12:17 +regby __tests__/mount.test.ts:6:1 __tests__/mount.test.ts:8:14 GET /alpha __tests__/mount.test.ts:13:15 +regby __tests__/mount.test.ts:6:1 __tests__/mount.test.ts:8:14 GET /beta __tests__/mount.test.ts:20:15 +regby __tests__/mount.test.ts:6:1 __tests__/mount.test.ts:8:14 GET /gamma __tests__/mount.test.ts:27:15 +regby app.ts:8:1 app.ts:10:22 GET /health app.ts:9:15 +regby plugins/audit.ts:1:1 plugins/audit.ts:5:16 GET /admin/audit app.ts:9:15 +regby plugins/audit.ts:1:1 plugins/audit.ts:6:19 GET /admin/audit/:id app.ts:9:15 +regby plugins/tenants.ts:4:1 plugins/tenants.ts:6:14 GET /admin/tenants app.ts:9:15 +unknown __tests__/define-plugin.test.ts:22:9 conditional_registration __tests__/define-plugin.test.ts:22:47 +unknown plugins/tenants.ts:13:5 conditional_registration plugins/tenants.ts:6:14 +unknown sdk.ts:34:7 conditional_registration __tests__/define-plugin.test.ts:10:37 +unknown sdk.ts:34:7 conditional_registration __tests__/mount.test.ts:8:14 +unknown sdk.ts:34:7 conditional_registration plugins/audit.ts:5:16 +unknown sdk.ts:34:7 conditional_registration plugins/audit.ts:6:19 diff --git a/graph/test/typescript/hono/expected/compose/typed-property-receiver.entries b/graph/test/typescript/hono/expected/compose/typed-property-receiver.entries new file mode 100644 index 000000000..b0edd6dfa --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/typed-property-receiver.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-01/R2: route() on ctx.server (declared Hono, value unseen): /raw is partial unresolved_app_value, never a root route +# census: route_mount_literal (violation hono-1-01; SPEC R2) +entry index.ts:13:19 http diff --git a/graph/test/typescript/hono/expected/compose/typed-property-receiver.tsv b/graph/test/typescript/hono/expected/compose/typed-property-receiver.tsv new file mode 100644 index 000000000..90aef2497 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/typed-property-receiver.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-01/R2: route() on ctx.server (declared Hono, value unseen): /raw is partial unresolved_app_value, never a root route +# census: route_mount_literal (violation hono-1-01; SPEC R2) +partial index.ts:13:19 GET unresolved_app_value +unknown index.ts:19:3 unresolved_app_value index.ts:13:19 diff --git a/graph/test/typescript/hono/expected/compose/uncalled-start-server.entries b/graph/test/typescript/hono/expected/compose/uncalled-start-server.entries new file mode 100644 index 000000000..c65060e2c --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/uncalled-start-server.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O2: startServer is never called in the program yet its body runs: /api/status is mounted under /api, the sub-app is not served bare +# census: app_factory_fn (oracle gap O2) +entry api.ts:4:20 http diff --git a/graph/test/typescript/hono/expected/compose/uncalled-start-server.tsv b/graph/test/typescript/hono/expected/compose/uncalled-start-server.tsv new file mode 100644 index 000000000..4e8063e79 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/uncalled-start-server.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O2: startServer is never called in the program yet its body runs: /api/status is mounted under /api, the sub-app is not served bare +# census: app_factory_fn (oracle gap O2) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /api/status api.ts:4:20 server.ts:8:15 0 +submount /api server.ts:9:7 +regby api.ts:1:1 api.ts:4:20 GET /api/status server.ts:8:15 diff --git a/graph/test/typescript/hono/expected/compose/workspace-unlisted-subpath-bundler.entries b/graph/test/typescript/hono/expected/compose/workspace-unlisted-subpath-bundler.entries new file mode 100644 index 000000000..ad53e117e --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/workspace-unlisted-subpath-bundler.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and tsc --traceResolution on a real workspace link (not resolved under bundler): R22a: the unlisted subpath import is unbound, so app.route(/adapters) is unresolved_mount and nothing is served under /adapters; the never-mounted adapters app keeps its own root row (/list), as every never-mounted exported app does +# census: workspace_subpath (SPEC R22a; violation hono-5-06 shape) +entry apps/api/src/index.ts:11:20 http +entry packages/core/adapters/index.ts:5:23 http diff --git a/graph/test/typescript/hono/expected/compose/workspace-unlisted-subpath-bundler.tsv b/graph/test/typescript/hono/expected/compose/workspace-unlisted-subpath-bundler.tsv new file mode 100644 index 000000000..c07ea2f9e --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/workspace-unlisted-subpath-bundler.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and tsc --traceResolution on a real workspace link (not resolved under bundler): R22a: the unlisted subpath import is unbound, so app.route(/adapters) is unresolved_mount and nothing is served under /adapters; the never-mounted adapters app keeps its own root row (/list), as every never-mounted exported app does +# census: workspace_subpath (SPEC R22a; violation hono-5-06 shape) +route GET /health apps/api/src/index.ts:11:20 apps/api/src/index.ts:10:13 0 +route GET /list packages/core/adapters/index.ts:5:23 packages/core/adapters/index.ts:4:25 0 +regby apps/api/src/index.ts:1:1 apps/api/src/index.ts:11:20 GET /health apps/api/src/index.ts:10:13 +regby packages/core/adapters/index.ts:1:1 packages/core/adapters/index.ts:5:23 GET /list packages/core/adapters/index.ts:4:25 +unknown apps/api/src/index.ts:12:1 unresolved_mount apps/api/src/index.ts:1:1 diff --git a/graph/test/typescript/hono/expected/context/chain-of-setters.entries b/graph/test/typescript/hono/expected/context/chain-of-setters.entries new file mode 100644 index 000000000..2b8f6f217 --- /dev/null +++ b/graph/test/typescript/hono/expected/context/chain-of-setters.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: lang flows to greet and the handler; greeting flows to the handler +# census: ctx_set, ctx_get, verb_multi_handler +entry index.ts:12:39 http +entry index.ts:18:38 http +entry index.ts:8:44 http diff --git a/graph/test/typescript/hono/expected/context/chain-of-setters.tsv b/graph/test/typescript/hono/expected/context/chain-of-setters.tsv new file mode 100644 index 000000000..b9acc950a --- /dev/null +++ b/graph/test/typescript/hono/expected/context/chain-of-setters.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: lang flows to greet and the handler; greeting flows to the handler +# census: ctx_set, ctx_get, verb_multi_handler +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /hello index.ts:18:38 index.ts:17:13 2 +mw index.ts:8:44 index.ts:18:38 GET /hello 0 index.ts:17:13 +mw index.ts:12:39 index.ts:18:38 GET /hello 1 index.ts:17:13 +ctx index.ts:12:39 index.ts:18:38 greeting +ctx index.ts:8:44 index.ts:12:39 lang +ctx index.ts:8:44 index.ts:18:38 lang +regby index.ts:1:1 index.ts:18:38 GET /hello index.ts:17:13 diff --git a/graph/test/typescript/hono/expected/context/cross-module.entries b/graph/test/typescript/hono/expected/context/cross-module.entries new file mode 100644 index 000000000..870caf078 --- /dev/null +++ b/graph/test/typescript/hono/expected/context/cross-module.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: session flows from auth.ts to both mounted handlers; DELETE reads before it sets, so no self flow +# census: ctx_set, ctx_get, route_mount_literal +entry auth.ts:5:50 http +entry routes.ts:6:18 http +entry routes.ts:7:21 http diff --git a/graph/test/typescript/hono/expected/context/cross-module.tsv b/graph/test/typescript/hono/expected/context/cross-module.tsv new file mode 100644 index 000000000..314abab65 --- /dev/null +++ b/graph/test/typescript/hono/expected/context/cross-module.tsv @@ -0,0 +1,13 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: session flows from auth.ts to both mounted handlers; DELETE reads before it sets, so no self flow +# census: ctx_set, ctx_get, route_mount_literal +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /account routes.ts:6:18 index.ts:8:13 1 +route DELETE /account routes.ts:7:21 index.ts:8:13 2 +mw auth.ts:5:50 routes.ts:6:18 GET /account 0 index.ts:8:13 +mw auth.ts:5:50 routes.ts:7:21 DELETE /account 0 index.ts:8:13 +ctx auth.ts:5:50 routes.ts:6:18 session +ctx auth.ts:5:50 routes.ts:7:21 session +submount /account index.ts:10:5 +regby routes.ts:1:1 routes.ts:6:18 GET /account index.ts:8:13 +regby routes.ts:1:1 routes.ts:7:21 DELETE /account index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/context/set-get.entries b/graph/test/typescript/hono/expected/context/set-get.entries new file mode 100644 index 000000000..b419b4a1c --- /dev/null +++ b/graph/test/typescript/hono/expected/context/set-get.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: user/tenant flow from loadUser to /me and /tenant; /before (registered before the use) and traceId have no setter +# census: ctx_set, ctx_get, ctx_var, variables_type +entry index.ts:16:20 http +entry index.ts:18:16 http +entry index.ts:19:20 http +entry index.ts:20:19 http +entry index.ts:9:40 http diff --git a/graph/test/typescript/hono/expected/context/set-get.tsv b/graph/test/typescript/hono/expected/context/set-get.tsv new file mode 100644 index 000000000..be14ea3de --- /dev/null +++ b/graph/test/typescript/hono/expected/context/set-get.tsv @@ -0,0 +1,19 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: user/tenant flow from loadUser to /me and /tenant; /before (registered before the use) and traceId have no setter +# census: ctx_set, ctx_get, ctx_var, variables_type +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /before index.ts:16:20 index.ts:15:13 0 +route GET /me index.ts:18:16 index.ts:15:13 2 +route GET /tenant index.ts:19:20 index.ts:15:13 3 +route GET /trace index.ts:20:19 index.ts:15:13 4 +mw index.ts:9:40 index.ts:18:16 GET /me 0 index.ts:15:13 +mw index.ts:9:40 index.ts:19:20 GET /tenant 0 index.ts:15:13 +mw index.ts:9:40 index.ts:20:19 GET /trace 0 index.ts:15:13 +ctx index.ts:9:40 index.ts:18:16 user +ctx index.ts:9:40 index.ts:19:20 tenant +regby index.ts:1:1 index.ts:16:20 GET /before index.ts:15:13 +regby index.ts:1:1 index.ts:18:16 GET /me index.ts:15:13 +regby index.ts:1:1 index.ts:19:20 GET /tenant index.ts:15:13 +regby index.ts:1:1 index.ts:20:19 GET /trace index.ts:15:13 +unknown index.ts:16:20 no_setter_on_chain index.ts:16:20 +unknown index.ts:20:19 no_setter_on_chain index.ts:20:19 diff --git a/graph/test/typescript/hono/expected/deno/denoland-url.entries b/graph/test/typescript/hono/expected/deno/denoland-url.entries new file mode 100644 index 000000000..289dfcc59 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/denoland-url.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: deno.land/x URL mod.ts app, etag from middleware.ts; export default app +# census: none (corpus gap). https://deno.land/x/hono@v3.x/mod.ts and /middleware.ts: shape from Hono's Deno docs (v3 era), corpus gap. +entry main.ts:8:17 http diff --git a/graph/test/typescript/hono/expected/deno/denoland-url.tsv b/graph/test/typescript/hono/expected/deno/denoland-url.tsv new file mode 100644 index 000000000..e0ad2d612 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/denoland-url.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: deno.land/x URL mod.ts app, etag from middleware.ts; export default app +# census: none (corpus gap). https://deno.land/x/hono@v3.x/mod.ts and /middleware.ts: shape from Hono's Deno docs (v3 era), corpus gap. +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /url main.ts:8:17 main.ts:6:13 1 +mw lib:hono/middleware.etag@main.ts:7:9 main.ts:8:17 GET /url 0 main.ts:6:13 +regby main.ts:1:1 main.ts:8:17 GET /url main.ts:6:13 diff --git a/graph/test/typescript/hono/expected/deno/import-map-deno-json.entries b/graph/test/typescript/hono/expected/deno/import-map-deno-json.entries new file mode 100644 index 000000000..b2da2aea0 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/import-map-deno-json.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: deno.json exact and prefix maps; ./routes/users.ts with extension mounted at /users; secureHeaders before both +# census: route_mount_literal, export_default_app; bare 'hono' mapped by deno.json imports (exact and the +entry routes/users.ts:4:16 http +entry routes/users.ts:5:19 http diff --git a/graph/test/typescript/hono/expected/deno/import-map-deno-json.tsv b/graph/test/typescript/hono/expected/deno/import-map-deno-json.tsv new file mode 100644 index 000000000..90b280c3b --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/import-map-deno-json.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: deno.json exact and prefix maps; ./routes/users.ts with extension mounted at /users; secureHeaders before both +# census: route_mount_literal, export_default_app; bare 'hono' mapped by deno.json imports (exact and the +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /users routes/users.ts:4:16 main.ts:8:13 1 +route GET /users/:id routes/users.ts:5:19 main.ts:8:13 2 +mw lib:hono/secure-headers.secureHeaders@main.ts:9:9 routes/users.ts:4:16 GET /users 0 main.ts:8:13 +mw lib:hono/secure-headers.secureHeaders@main.ts:9:9 routes/users.ts:5:19 GET /users/:id 0 main.ts:8:13 +submount /users main.ts:10:5 +regby routes/users.ts:1:1 routes/users.ts:4:16 GET /users main.ts:8:13 +regby routes/users.ts:1:1 routes/users.ts:5:19 GET /users/:id main.ts:8:13 diff --git a/graph/test/typescript/hono/expected/deno/import-map-file.entries b/graph/test/typescript/hono/expected/deno/import-map-file.entries new file mode 100644 index 000000000..3afd66a0d --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/import-map-file.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: import_map.json to npm:hono@4; hono/factory via the prefix map; createMiddleware's arg is the middleware +# census: none (corpus gap). a standalone import_map.json mapping bare hono to npm:hono@4 (prefix map for hono/*): shape from Hono's +entry main.ts:11:20 http +entry main.ts:6:32 http diff --git a/graph/test/typescript/hono/expected/deno/import-map-file.tsv b/graph/test/typescript/hono/expected/deno/import-map-file.tsv new file mode 100644 index 000000000..af1775a11 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/import-map-file.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: import_map.json to npm:hono@4; hono/factory via the prefix map; createMiddleware's arg is the middleware +# census: none (corpus gap). a standalone import_map.json mapping bare hono to npm:hono@4 (prefix map for hono/*): shape from Hono's +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /mapped main.ts:11:20 main.ts:9:13 1 +mw main.ts:6:32 main.ts:11:20 GET /mapped 0 main.ts:9:13 +regby main.ts:1:1 main.ts:11:20 GET /mapped main.ts:9:13 diff --git a/graph/test/typescript/hono/expected/deno/import-map-jsonc.entries b/graph/test/typescript/hono/expected/deno/import-map-jsonc.entries new file mode 100644 index 000000000..f9cae2e1c --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/import-map-jsonc.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: bare @hono/hono through a commented deno.jsonc +# census: none (corpus gap). bare JSR name `@hono/hono` mapped by deno.jsonc (comments stripped): shape from Hono's Deno docs, corpus gap. +entry main.ts:5:19 http diff --git a/graph/test/typescript/hono/expected/deno/import-map-jsonc.tsv b/graph/test/typescript/hono/expected/deno/import-map-jsonc.tsv new file mode 100644 index 000000000..c36b0f6ae --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/import-map-jsonc.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: bare @hono/hono through a commented deno.jsonc +# census: none (corpus gap). bare JSR name `@hono/hono` mapped by deno.jsonc (comments stripped): shape from Hono's Deno docs, corpus gap. +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /jsonc main.ts:5:19 main.ts:4:13 0 +regby main.ts:1:1 main.ts:5:19 GET /jsonc main.ts:4:13 diff --git a/graph/test/typescript/hono/expected/deno/jsr-specifier.entries b/graph/test/typescript/hono/expected/deno/jsr-specifier.entries new file mode 100644 index 000000000..71475aa20 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/jsr-specifier.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: jsr:@hono/hono app with jsr logger: one route, logger before it; served by Deno.serve(app.fetch) +# census: deno_serve. jsr:@hono/hono and jsr:@hono/hono@^4/logger specifiers are a shape from Hono's Deno +entry main.ts:9:17 http diff --git a/graph/test/typescript/hono/expected/deno/jsr-specifier.tsv b/graph/test/typescript/hono/expected/deno/jsr-specifier.tsv new file mode 100644 index 000000000..bf5cdd1b4 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/jsr-specifier.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: jsr:@hono/hono app with jsr logger: one route, logger before it; served by Deno.serve(app.fetch) +# census: deno_serve. jsr:@hono/hono and jsr:@hono/hono@^4/logger specifiers are a shape from Hono's Deno +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /jsr main.ts:9:17 main.ts:7:13 1 +mw lib:hono/logger.logger@main.ts:8:9 main.ts:9:17 GET /jsr 0 main.ts:7:13 +regby main.ts:1:1 main.ts:9:17 GET /jsr main.ts:7:13 diff --git a/graph/test/typescript/hono/expected/deno/jsr-static-method.entries b/graph/test/typescript/hono/expected/deno/jsr-static-method.entries new file mode 100644 index 000000000..0a2ffda35 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/jsr-static-method.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Deno shape: import map below the root to jsr:, app from a static method with basePath /static: GET /static/logo.jpg +# census: deno_serve (shape of a Deno holdout project: a deno.json import map to jsr:, below the repository root, +entry api/main.ts:6:23 http diff --git a/graph/test/typescript/hono/expected/deno/jsr-static-method.tsv b/graph/test/typescript/hono/expected/deno/jsr-static-method.tsv new file mode 100644 index 000000000..cc4e9c65f --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/jsr-static-method.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Deno shape: import map below the root to jsr:, app from a static method with basePath /static: GET /static/logo.jpg +# census: deno_serve (shape of a Deno holdout project: a deno.json import map to jsr:, below the repository root, +route GET /static/logo.jpg api/main.ts:6:23 api/routes.ts:5:12 0 +regby api/main.ts:1:1 api/main.ts:6:23 GET /static/logo.jpg api/routes.ts:5:12 diff --git a/graph/test/typescript/hono/expected/deno/npm-specifier.entries b/graph/test/typescript/hono/expected/deno/npm-specifier.entries new file mode 100644 index 000000000..86b5f3eb1 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/npm-specifier.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: npm:hono@4 app with npm:hono/cors scoped to /api/*; Deno.serve({ port }, app.fetch) +# census: none (corpus gap). npm:hono@4 and npm:hono/cors specifiers: shape from Hono's Deno docs, corpus gap. Oracle maps them to the +entry main.ts:8:21 http diff --git a/graph/test/typescript/hono/expected/deno/npm-specifier.tsv b/graph/test/typescript/hono/expected/deno/npm-specifier.tsv new file mode 100644 index 000000000..8e7f4f3d4 --- /dev/null +++ b/graph/test/typescript/hono/expected/deno/npm-specifier.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: npm:hono@4 app with npm:hono/cors scoped to /api/*; Deno.serve({ port }, app.fetch) +# census: none (corpus gap). npm:hono@4 and npm:hono/cors specifiers: shape from Hono's Deno docs, corpus gap. Oracle maps them to the +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /api/npm main.ts:8:21 main.ts:6:13 1 +mw lib:hono/cors.cors@main.ts:7:19 main.ts:8:21 GET /api/npm 0 main.ts:6:13 +regby main.ts:1:1 main.ts:8:21 GET /api/npm main.ts:6:13 diff --git a/graph/test/typescript/hono/expected/entry/aliased-app-fetch-wrapper.entries b/graph/test/typescript/hono/expected/entry/aliased-app-fetch-wrapper.entries new file mode 100644 index 000000000..8e7a6ea1b --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/aliased-app-fetch-wrapper.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O4/R4: app from a tsconfig-path alias, served through a custom fetch object; app.fetch(req, env, ctx) forwards and is no request +# census: app_factory_fn, export_default_fetch_obj (oracle gap O4, SPEC R4) +entry index.ts:7:21 http diff --git a/graph/test/typescript/hono/expected/entry/aliased-app-fetch-wrapper.tsv b/graph/test/typescript/hono/expected/entry/aliased-app-fetch-wrapper.tsv new file mode 100644 index 000000000..b659389cf --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/aliased-app-fetch-wrapper.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O4/R4: app from a tsconfig-path alias, served through a custom fetch object; app.fetch(req, env, ctx) forwards and is no request +# census: app_factory_fn, export_default_fetch_obj (oracle gap O4, SPEC R4) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /aliased index.ts:7:21 lib/app.ts:4:10 0 +regby index.ts:1:1 index.ts:7:21 GET /aliased lib/app.ts:4:10 diff --git a/graph/test/typescript/hono/expected/entry/app-request-test.entries b/graph/test/typescript/hono/expected/entry/app-request-test.entries new file mode 100644 index 000000000..602e1a3d9 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/app-request-test.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: four test functions reach their routes (three app.request, one testClient); /nowhere is unserved +# census: app_request_test, test_client +entry app.ts:4:19 http +entry app.ts:5:19 http +entry app.ts:6:22 http diff --git a/graph/test/typescript/hono/expected/entry/app-request-test.tsv b/graph/test/typescript/hono/expected/entry/app-request-test.tsv new file mode 100644 index 000000000..941b7b15c --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/app-request-test.tsv @@ -0,0 +1,14 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: four test functions reach their routes (three app.request, one testClient); /nowhere is unserved +# census: app_request_test, test_client +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /health app.ts:4:19 app.ts:3:20 0 +route POST /items app.ts:5:19 app.ts:3:20 1 +route GET /items/:id app.ts:6:22 app.ts:3:20 2 +rpc app.test.ts:21:1 GET /health app.ts:4:19 +request app.test.ts:11:1 POST /items app.ts:5:19 +request app.test.ts:16:1 GET /items/42 app.ts:6:22 +request app.test.ts:6:1 GET /health app.ts:4:19 +regby app.ts:1:1 app.ts:4:19 GET /health app.ts:3:20 +regby app.ts:1:1 app.ts:5:19 POST /items app.ts:3:20 +regby app.ts:1:1 app.ts:6:22 GET /items/:id app.ts:3:20 +unserved app.test.ts:27:1 GET /nowhere diff --git a/graph/test/typescript/hono/expected/entry/deno-bun.entries b/graph/test/typescript/hono/expected/entry/deno-bun.entries new file mode 100644 index 000000000..b798074f8 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/deno-bun.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Deno.serve / Bun.serve inside a start function; the route from the exported app +# census: deno_serve, bun_serve +entry index.ts:9:18 http diff --git a/graph/test/typescript/hono/expected/entry/deno-bun.tsv b/graph/test/typescript/hono/expected/entry/deno-bun.tsv new file mode 100644 index 000000000..5718ac45d --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/deno-bun.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Deno.serve / Bun.serve inside a start function; the route from the exported app +# census: deno_serve, bun_serve +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /deno index.ts:9:18 index.ts:8:20 0 +regby index.ts:1:1 index.ts:9:18 GET /deno index.ts:8:20 diff --git a/graph/test/typescript/hono/expected/entry/dynamic-import-test-app.entries b/graph/test/typescript/hono/expected/entry/dynamic-import-test-app.entries new file mode 100644 index 000000000..86af346ec --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/dynamic-import-test-app.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O16: sub-apps bound by await import are mounted on R1 test roots; every request is served +# census: app_request_test (oracle gap O16; SPEC R1) +entry admin.ts:3:53 http +entry skills.test.ts:11:13 http +entry skills.ts:4:22 http +entry skills.ts:5:23 http diff --git a/graph/test/typescript/hono/expected/entry/dynamic-import-test-app.tsv b/graph/test/typescript/hono/expected/entry/dynamic-import-test-app.tsv new file mode 100644 index 000000000..832c6042d --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/dynamic-import-test-app.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O16: sub-apps bound by await import are mounted on R1 test roots; every request is served +# census: app_request_test (oracle gap O16; SPEC R1) +route GET /:ns/:name skills.ts:4:22 skills.test.ts:10:17 1 +route POST /:ns/:name skills.ts:5:23 skills.test.ts:10:17 2 +route GET /admin/stats admin.ts:3:53 skills.test.ts:27:15 0 +mw skills.test.ts:11:13 skills.ts:4:22 GET /:ns/:name 0 skills.test.ts:10:17 +mw skills.test.ts:11:13 skills.ts:5:23 POST /:ns/:name 0 skills.test.ts:10:17 +request skills.test.ts:17:21 GET /acme/review skills.ts:4:22 +request skills.test.ts:21:25 POST /acme/review skills.ts:5:23 +request skills.test.ts:25:25 GET /admin/stats admin.ts:3:53 +submount / skills.test.ts:15:4 +submount /admin skills.test.ts:27:26 +regby admin.ts:1:1 admin.ts:3:53 GET /admin/stats skills.test.ts:27:15 +regby skills.ts:1:1 skills.ts:4:22 GET /:ns/:name skills.test.ts:10:17 +regby skills.ts:1:1 skills.ts:5:23 POST /:ns/:name skills.test.ts:10:17 diff --git a/graph/test/typescript/hono/expected/entry/fetch-object.entries b/graph/test/typescript/hono/expected/entry/fetch-object.entries new file mode 100644 index 000000000..8fb3a84e5 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/fetch-object.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: fetch-object export: the route handler plus scheduled and queue are http entries +# census: export_default_fetch_obj, app_fetch_ref +entry index.ts:15:3 http +entry index.ts:6:14 http +entry index.ts:8:1 http diff --git a/graph/test/typescript/hono/expected/entry/fetch-object.tsv b/graph/test/typescript/hono/expected/entry/fetch-object.tsv new file mode 100644 index 000000000..32bc0511d --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/fetch-object.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: fetch-object export: the route handler plus scheduled and queue are http entries +# census: export_default_fetch_obj, app_fetch_ref +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET / index.ts:6:14 index.ts:5:13 0 +regby index.ts:1:1 index.ts:6:14 GET / index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/entry/fixture-prop-request.entries b/graph/test/typescript/hono/expected/entry/fixture-prop-request.entries new file mode 100644 index 000000000..eac075f1d --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/fixture-prop-request.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-06: the request on fixture.app reaches GET /api/flow +# census: app_request_test (violation hono-2-06) +entry app.ts:5:24 http diff --git a/graph/test/typescript/hono/expected/entry/fixture-prop-request.tsv b/graph/test/typescript/hono/expected/entry/fixture-prop-request.tsv new file mode 100644 index 000000000..39a9d6352 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/fixture-prop-request.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-06: the request on fixture.app reaches GET /api/flow +# census: app_request_test (violation hono-2-06) +route GET /api/flow app.ts:5:24 app.ts:4:15 0 +request app.test.ts:12:14 GET /api/flow app.ts:5:24 +regby app.ts:3:1 app.ts:5:24 GET /api/flow app.ts:4:15 diff --git a/graph/test/typescript/hono/expected/entry/jsx-render.entries b/graph/test/typescript/hono/expected/entry/jsx-render.entries new file mode 100644 index 000000000..519ac2c52 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/jsx-render.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: two routes and their entries, jsxRenderer as library middleware for /page/*; the components and the renderer callback are in no Hono relation (runtime half skips .tsx) +# census: c_render, jsx_import +entry index.tsx:11:18 http +entry index.tsx:13:24 http diff --git a/graph/test/typescript/hono/expected/entry/jsx-render.tsv b/graph/test/typescript/hono/expected/entry/jsx-render.tsv new file mode 100644 index 000000000..51310ce95 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/jsx-render.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: two routes and their entries, jsxRenderer as library middleware for /page/*; the components and the renderer callback are in no Hono relation (runtime half skips .tsx) +# census: c_render, jsx_import +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /html index.tsx:11:18 index.tsx:10:13 0 +route GET /page/hello index.tsx:13:24 index.tsx:10:13 2 +mw lib:hono/jsx-renderer.jsxRenderer@index.tsx:12:20 index.tsx:13:24 GET /page/hello 0 index.tsx:10:13 +regby index.tsx:1:1 index.tsx:11:18 GET /html index.tsx:10:13 +regby index.tsx:1:1 index.tsx:13:24 GET /page/hello index.tsx:10:13 +absent components.tsx:4:27 +absent components.tsx:10:47 +absent index.tsx:12:32 diff --git a/graph/test/typescript/hono/expected/entry/node-serve.entries b/graph/test/typescript/hono/expected/entry/node-serve.entries new file mode 100644 index 000000000..badb32c78 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/node-serve.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: an app only served through serve(): its route still in the table +# census: serve_fetch +entry index.ts:7:20 http diff --git a/graph/test/typescript/hono/expected/entry/node-serve.tsv b/graph/test/typescript/hono/expected/entry/node-serve.tsv new file mode 100644 index 000000000..3f6efcea7 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/node-serve.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: an app only served through serve(): its route still in the table +# census: serve_fetch +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /served index.ts:7:20 index.ts:6:13 0 +regby index.ts:1:1 index.ts:7:20 GET /served index.ts:6:13 diff --git a/graph/test/typescript/hono/expected/entry/notfound-onerror.entries b/graph/test/typescript/hono/expected/entry/notfound-onerror.entries new file mode 100644 index 000000000..07ee3f282 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/notfound-onerror.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: notFound and onError are entries, not routes +# census: not_found, on_error +entry index.ts:10:13 http +entry index.ts:6:18 http +entry index.ts:9:14 http diff --git a/graph/test/typescript/hono/expected/entry/notfound-onerror.tsv b/graph/test/typescript/hono/expected/entry/notfound-onerror.tsv new file mode 100644 index 000000000..b29054d97 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/notfound-onerror.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: notFound and onError are entries, not routes +# census: not_found, on_error +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /boom index.ts:6:18 index.ts:5:13 0 +regby index.ts:1:1 index.ts:6:18 GET /boom index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/entry/request-object-and-fetch.entries b/graph/test/typescript/hono/expected/entry/request-object-and-fetch.entries new file mode 100644 index 000000000..51c34d017 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/request-object-and-fetch.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: app.request(new Request(url)), app.fetch(new Request(url, {method}), env) and request with init each reach their route +# census: app_request_test, app_fetch_ref +entry app.ts:4:18 http +entry app.ts:5:23 http diff --git a/graph/test/typescript/hono/expected/entry/request-object-and-fetch.tsv b/graph/test/typescript/hono/expected/entry/request-object-and-fetch.tsv new file mode 100644 index 000000000..8c3e20da0 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/request-object-and-fetch.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: app.request(new Request(url)), app.fetch(new Request(url, {method}), env) and request with init each reach their route +# census: app_request_test, app_fetch_ref +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /ping app.ts:4:18 app.ts:3:20 0 +route PUT /items/:id app.ts:5:23 app.ts:3:20 1 +request app.test.ts:10:1 PUT /items/7 app.ts:5:23 +request app.test.ts:15:1 PUT /items/8 app.ts:5:23 +request app.test.ts:5:1 GET /ping app.ts:4:18 +regby app.ts:1:1 app.ts:4:18 GET /ping app.ts:3:20 +regby app.ts:1:1 app.ts:5:23 PUT /items/:id app.ts:3:20 diff --git a/graph/test/typescript/hono/expected/entry/test-built-app.entries b/graph/test/typescript/hono/expected/entry/test-built-app.entries new file mode 100644 index 000000000..4076fb41d --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/test-built-app.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: R1: the app built inside the it() callback is a served root; its /health route and the request from the callback reach it +# census: app_request_test (SPEC R1: oracle gap O1/O5) +entry app.test.ts:12:24 http diff --git a/graph/test/typescript/hono/expected/entry/test-built-app.tsv b/graph/test/typescript/hono/expected/entry/test-built-app.tsv new file mode 100644 index 000000000..bf0f15413 --- /dev/null +++ b/graph/test/typescript/hono/expected/entry/test-built-app.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: R1: the app built inside the it() callback is a served root; its /health route and the request from the callback reach it +# census: app_request_test (SPEC R1: oracle gap O1/O5) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /health app.test.ts:12:24 app.test.ts:11:17 0 +request app.test.ts:10:17 GET /health app.test.ts:12:24 +regby app.test.ts:10:17 app.test.ts:12:24 GET /health app.test.ts:11:17 diff --git a/graph/test/typescript/hono/expected/middleware/barrel-library-mw.entries b/graph/test/typescript/hono/expected/middleware/barrel-library-mw.entries new file mode 100644 index 000000000..74c21046d --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/barrel-library-mw.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O14: library middleware re-exported by a project barrel (named, renamed, export *) keyed at their calls, positions exact, no dynamic list +# census: use_global (oracle gap O14) +entry index.ts:11:20 http +entry index.ts:12:20 http +entry middleware/audit.ts:3:39 http diff --git a/graph/test/typescript/hono/expected/middleware/barrel-library-mw.tsv b/graph/test/typescript/hono/expected/middleware/barrel-library-mw.tsv new file mode 100644 index 000000000..15168837f --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/barrel-library-mw.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O14: library middleware re-exported by a project barrel (named, renamed, export *) keyed at their calls, positions exact, no dynamic list +# census: use_global (oracle gap O14) +route GET /api/me index.ts:11:20 index.ts:7:13 4 +route GET /health index.ts:12:20 index.ts:7:13 5 +mw lib:hono/logger.logger@index.ts:8:14 index.ts:11:20 GET /api/me 0 index.ts:7:13 +mw lib:hono/cors.cors@index.ts:9:14 index.ts:11:20 GET /api/me 1 index.ts:7:13 +mw lib:hono.secureHeaders@index.ts:10:19 index.ts:11:20 GET /api/me 2 index.ts:7:13 +mw middleware/audit.ts:3:39 index.ts:11:20 GET /api/me 3 index.ts:7:13 +mw lib:hono/logger.logger@index.ts:8:14 index.ts:12:20 GET /health 0 index.ts:7:13 +mw lib:hono/cors.cors@index.ts:9:14 index.ts:12:20 GET /health 1 index.ts:7:13 +regby index.ts:1:1 index.ts:11:20 GET /api/me index.ts:7:13 +regby index.ts:1:1 index.ts:12:20 GET /health index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/middleware/builtin.entries b/graph/test/typescript/hono/expected/middleware/builtin.entries new file mode 100644 index 000000000..8740b3205 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/builtin.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: library middleware keyed by registering call site; order logger, cors, secureHeaders, etag +# census: builtin_mw_import +entry index.ts:13:22 http +entry index.ts:14:14 http diff --git a/graph/test/typescript/hono/expected/middleware/builtin.tsv b/graph/test/typescript/hono/expected/middleware/builtin.tsv new file mode 100644 index 000000000..59ca8d3a5 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/builtin.tsv @@ -0,0 +1,13 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: library middleware keyed by registering call site; order logger, cors, secureHeaders, etag +# census: builtin_mw_import +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /api/data index.ts:13:22 index.ts:9:13 4 +route GET / index.ts:14:14 index.ts:9:13 5 +mw lib:hono/logger.logger@index.ts:10:9 index.ts:13:22 GET /api/data 0 index.ts:9:13 +mw lib:hono/cors.cors@index.ts:11:19 index.ts:13:22 GET /api/data 1 index.ts:9:13 +mw lib:hono/secure-headers.secureHeaders@index.ts:12:19 index.ts:13:22 GET /api/data 2 index.ts:9:13 +mw lib:hono/etag.etag@index.ts:12:36 index.ts:13:22 GET /api/data 3 index.ts:9:13 +mw lib:hono/logger.logger@index.ts:10:9 index.ts:14:14 GET / 0 index.ts:9:13 +regby index.ts:1:1 index.ts:13:22 GET /api/data index.ts:9:13 +regby index.ts:1:1 index.ts:14:14 GET / index.ts:9:13 diff --git a/graph/test/typescript/hono/expected/middleware/combine.entries b/graph/test/typescript/hono/expected/middleware/combine.entries new file mode 100644 index 000000000..ededc366d --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/combine.entries @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: combinator calls are the middleware; except('/health') skips /health; combined functions are entries +# census: combine_every_some_except +entry index.ts:15:25 http +entry index.ts:16:24 http +entry index.ts:17:20 http +entry index.ts:7:36 http +entry index.ts:8:37 http +entry index.ts:9:36 http diff --git a/graph/test/typescript/hono/expected/middleware/combine.tsv b/graph/test/typescript/hono/expected/middleware/combine.tsv new file mode 100644 index 000000000..046691138 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/combine.tsv @@ -0,0 +1,14 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: combinator calls are the middleware; except('/health') skips /health; combined functions are entries +# census: combine_every_some_except +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /admin/panel index.ts:15:25 index.ts:11:13 3 +route GET /ops/status index.ts:16:24 index.ts:11:13 4 +route GET /health index.ts:17:20 index.ts:11:13 5 +mw lib:hono/combine.every@index.ts:12:21 index.ts:15:25 GET /admin/panel 0 index.ts:11:13 +mw lib:hono/combine.except@index.ts:14:14 index.ts:15:25 GET /admin/panel 1 index.ts:11:13 +mw lib:hono/combine.some@index.ts:13:19 index.ts:16:24 GET /ops/status 0 index.ts:11:13 +mw lib:hono/combine.except@index.ts:14:14 index.ts:16:24 GET /ops/status 1 index.ts:11:13 +regby index.ts:1:1 index.ts:15:25 GET /admin/panel index.ts:11:13 +regby index.ts:1:1 index.ts:16:24 GET /ops/status index.ts:11:13 +regby index.ts:1:1 index.ts:17:20 GET /health index.ts:11:13 diff --git a/graph/test/typescript/hono/expected/middleware/conditional-lib-mw.entries b/graph/test/typescript/hono/expected/middleware/conditional-lib-mw.entries new file mode 100644 index 000000000..531ae31d7 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/conditional-lib-mw.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m40/hono-5-04: if/else library middleware are alternatives at position 0, each keyed at its call, each conditional +# census: use_conditional_loop (stress-5 mutation m40 shape; violation hono-5-04) +entry index.ts:15:15 http diff --git a/graph/test/typescript/hono/expected/middleware/conditional-lib-mw.tsv b/graph/test/typescript/hono/expected/middleware/conditional-lib-mw.tsv new file mode 100644 index 000000000..132eeadf3 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/conditional-lib-mw.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m40/hono-5-04: if/else library middleware are alternatives at position 0, each keyed at its call, each conditional +# census: use_conditional_loop (stress-5 mutation m40 shape; violation hono-5-04) +route GET /x index.ts:15:15 index.ts:9:13 2 +mw lib:hono/logger.logger@index.ts:11:16 index.ts:15:15 GET /x 0 index.ts:9:13 +mw lib:hono/logger.logger@index.ts:13:16 index.ts:15:15 GET /x 0 index.ts:9:13 +regby index.ts:1:1 index.ts:15:15 GET /x index.ts:9:13 +unknown index.ts:11:3 conditional_registration index.ts:1:1 +unknown index.ts:13:3 conditional_registration index.ts:1:1 diff --git a/graph/test/typescript/hono/expected/middleware/const-library-mw.entries b/graph/test/typescript/hono/expected/middleware/const-library-mw.entries new file mode 100644 index 000000000..1b339dfe5 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/const-library-mw.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m31/hono-4-07: a const bound to jwt(...) is keyed at the call at module level, in a builder and in a branch +# census: use_scoped (stress-4 mutation m31 shapes; violation hono-4-07, SPEC R10/R11) +entry index.ts:12:19 http +entry index.ts:18:29 http +entry index.ts:28:34 http diff --git a/graph/test/typescript/hono/expected/middleware/const-library-mw.tsv b/graph/test/typescript/hono/expected/middleware/const-library-mw.tsv new file mode 100644 index 000000000..ebd6bf5d0 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/const-library-mw.tsv @@ -0,0 +1,14 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m31/hono-4-07: a const bound to jwt(...) is keyed at the call at module level, in a builder and in a branch +# census: use_scoped (stress-4 mutation m31 shapes; violation hono-4-07, SPEC R10/R11) +route GET /v1/me index.ts:12:19 index.ts:9:13 1 +route GET /admin/users index.ts:18:29 index.ts:9:13 3 +route GET /partner/orders index.ts:28:34 index.ts:9:13 5 +mw lib:hono/jwt.jwt@index.ts:10:16 index.ts:12:19 GET /v1/me 0 index.ts:9:13 +mw lib:hono/jwt.jwt@index.ts:16:20 index.ts:18:29 GET /admin/users 0 index.ts:9:13 +mw lib:hono/jwt.jwt@index.ts:25:24 index.ts:28:34 GET /partner/orders 0 index.ts:9:13 +submount / index.ts:32:5 +submount / index.ts:33:5 +regby index.ts:14:1 index.ts:18:29 GET /admin/users index.ts:9:13 +regby index.ts:1:1 index.ts:12:19 GET /v1/me index.ts:9:13 +regby index.ts:22:1 index.ts:28:34 GET /partner/orders index.ts:9:13 +unknown index.ts:26:5 conditional_registration index.ts:22:1 diff --git a/graph/test/typescript/hono/expected/middleware/detached-factory-method.entries b/graph/test/typescript/hono/expected/middleware/detached-factory-method.entries new file mode 100644 index 000000000..b470c1ccb --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/detached-factory-method.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O17: createMiddleware/createHandlers read off a factory into consts are the chain middleware and spread handlers +# census: create_middleware (oracle gap O17) +entry index.ts:13:3 http +entry index.ts:16:3 http +entry index.ts:7:44 http diff --git a/graph/test/typescript/hono/expected/middleware/detached-factory-method.tsv b/graph/test/typescript/hono/expected/middleware/detached-factory-method.tsv new file mode 100644 index 000000000..9b22b520a --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/detached-factory-method.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O17: createMiddleware/createHandlers read off a factory into consts are the chain middleware and spread handlers +# census: create_middleware (oracle gap O17) +route GET /admin/users index.ts:16:3 index.ts:19:13 2 +mw index.ts:7:44 index.ts:16:3 GET /admin/users 0 index.ts:19:13 +mw index.ts:13:3 index.ts:16:3 GET /admin/users 1 index.ts:19:13 +ctx index.ts:7:44 index.ts:16:3 role +regby index.ts:1:1 index.ts:16:3 GET /admin/users index.ts:19:13 diff --git a/graph/test/typescript/hono/expected/middleware/factory-fn.entries b/graph/test/typescript/hono/expected/middleware/factory-fn.entries new file mode 100644 index 000000000..794540cfb --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/factory-fn.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: the inner functions the factories return are the middleware; app-level before per-route +# census: mw_factory_returning_fn, create_middleware +entry index.ts:8:45 http +entry mw.ts:10:65 http +entry mw.ts:4:10 http diff --git a/graph/test/typescript/hono/expected/middleware/factory-fn.tsv b/graph/test/typescript/hono/expected/middleware/factory-fn.tsv new file mode 100644 index 000000000..0ce1bb447 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/factory-fn.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: the inner functions the factories return are the middleware; app-level before per-route +# census: mw_factory_returning_fn, create_middleware +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /limited index.ts:8:45 index.ts:6:13 2 +mw mw.ts:10:65 index.ts:8:45 GET /limited 0 index.ts:6:13 +mw mw.ts:4:10 index.ts:8:45 GET /limited 1 index.ts:6:13 +regby index.ts:1:1 index.ts:8:45 GET /limited index.ts:6:13 diff --git a/graph/test/typescript/hono/expected/middleware/factory-handlers.entries b/graph/test/typescript/hono/expected/middleware/factory-handlers.entries new file mode 100644 index 000000000..fa60dfc20 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/factory-handlers.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: initApp's use first, then check from createHandlers, then the handler; all three entries +# census: create_factory, factory_create_app, create_middleware +entry index.ts:14:40 http +entry index.ts:17:48 http +entry index.ts:8:13 http diff --git a/graph/test/typescript/hono/expected/middleware/factory-handlers.tsv b/graph/test/typescript/hono/expected/middleware/factory-handlers.tsv new file mode 100644 index 000000000..dafdf342f --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/factory-handlers.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: initApp's use first, then check from createHandlers, then the handler; all three entries +# census: create_factory, factory_create_app, create_middleware +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /factory index.ts:17:48 index.ts:19:13 2 +mw index.ts:8:13 index.ts:17:48 GET /factory 0 index.ts:19:13 +mw index.ts:14:40 index.ts:17:48 GET /factory 1 index.ts:19:13 +regby index.ts:1:1 index.ts:17:48 GET /factory index.ts:19:13 diff --git a/graph/test/typescript/hono/expected/middleware/factory-two-returns.entries b/graph/test/typescript/hono/expected/middleware/factory-two-returns.entries new file mode 100644 index 000000000..4f2957405 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/factory-two-returns.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-03: both returns of the factory are middleware at position 0 (the arrow and basicAuth at its call); verifyUser is no middleware +# census: use_scoped (violation hono-3-03, SPEC 3.1 "resolve the return; if several, all", R10) +entry index.ts:13:12 http +entry index.ts:22:25 http +entry index.ts:23:20 http diff --git a/graph/test/typescript/hono/expected/middleware/factory-two-returns.tsv b/graph/test/typescript/hono/expected/middleware/factory-two-returns.tsv new file mode 100644 index 000000000..48ea99cdb --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/factory-two-returns.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-03: both returns of the factory are middleware at position 0 (the arrow and basicAuth at its call); verifyUser is no middleware +# census: use_scoped (violation hono-3-03, SPEC 3.1 "resolve the return; if several, all", R10) +route GET /admin/stats index.ts:22:25 index.ts:20:13 2 +route GET /public index.ts:23:20 index.ts:20:13 3 +mw index.ts:13:12 index.ts:22:25 GET /admin/stats 0 index.ts:20:13 +mw lib:hono/basic-auth.basicAuth@index.ts:17:10 index.ts:22:25 GET /admin/stats 0 index.ts:20:13 +regby index.ts:1:1 index.ts:22:25 GET /admin/stats index.ts:20:13 +regby index.ts:1:1 index.ts:23:20 GET /public index.ts:20:13 diff --git a/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.entries b/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.entries new file mode 100644 index 000000000..bda162c13 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m28/hono-4-02/R6: three per-call test apps, each chain use 0, param 1, json 2 +# census: app_request_test (stress-4 mutation m28 shape; violation hono-4-02, SPEC R6) +entry routes.test.ts:12:16 http +entry routes.ts:9:3 http diff --git a/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.tsv b/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.tsv new file mode 100644 index 000000000..6b753eb6a --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.tsv @@ -0,0 +1,23 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m28/hono-4-02/R6: three per-call test apps, each chain use 0, param 1, json 2 +# census: app_request_test (stress-4 mutation m28 shape; violation hono-4-02, SPEC R6) +route POST /:id/rename routes.ts:9:3 routes.test.ts:11:15 3 +route POST /:id/rename routes.ts:9:3 routes.test.ts:11:15 3 +route POST /:id/rename routes.ts:9:3 routes.test.ts:11:15 3 +mw routes.test.ts:12:16 routes.ts:9:3 POST /:id/rename 0 routes.test.ts:11:15 +mw lib:@hono/zod-validator.zValidator@routes.ts:7:3 routes.ts:9:3 POST /:id/rename 1 routes.test.ts:11:15 +mw lib:@hono/zod-validator.zValidator@routes.ts:8:3 routes.ts:9:3 POST /:id/rename 2 routes.test.ts:11:15 +mw routes.test.ts:12:16 routes.ts:9:3 POST /:id/rename 0 routes.test.ts:11:15 +mw lib:@hono/zod-validator.zValidator@routes.ts:7:3 routes.ts:9:3 POST /:id/rename 1 routes.test.ts:11:15 +mw lib:@hono/zod-validator.zValidator@routes.ts:8:3 routes.ts:9:3 POST /:id/rename 2 routes.test.ts:11:15 +mw routes.test.ts:12:16 routes.ts:9:3 POST /:id/rename 0 routes.test.ts:11:15 +mw lib:@hono/zod-validator.zValidator@routes.ts:7:3 routes.ts:9:3 POST /:id/rename 1 routes.test.ts:11:15 +mw lib:@hono/zod-validator.zValidator@routes.ts:8:3 routes.ts:9:3 POST /:id/rename 2 routes.test.ts:11:15 +request routes.test.ts:24:19 POST /1/rename routes.ts:9:3 +request routes.test.ts:30:19 POST /2/rename routes.ts:9:3 +request routes.test.ts:36:21 POST /3/rename routes.ts:9:3 +submount / routes.test.ts:20:7 +submount / routes.test.ts:20:7 +submount / routes.test.ts:20:7 +schema routes.ts:9:3 json expr@routes.ts:8:22 +schema routes.ts:9:3 param expr@routes.ts:7:23 +regby routes.ts:1:1 routes.ts:9:3 POST /:id/rename routes.test.ts:11:15 diff --git a/graph/test/typescript/hono/expected/middleware/if-else-unequal-mw.entries b/graph/test/typescript/hono/expected/middleware/if-else-unequal-mw.entries new file mode 100644 index 000000000..eb53fd55c --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/if-else-unequal-mw.entries @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R15 clause 2: unequal if/else branches start at the same position, each conditional; a later middleware continues after the longer branch and its chain is dynamic_middleware_list; /api/ping (nothing after the branch) and /health are not dynamic +# census: use_conditional_loop, use_scoped (SPEC R15 clause 2) +entry index.ts:12:38 http +entry index.ts:13:40 http +entry index.ts:14:37 http +entry index.ts:15:35 http +entry index.ts:16:34 http +entry index.ts:28:20 http +entry index.ts:29:22 http +entry index.ts:30:27 http diff --git a/graph/test/typescript/hono/expected/middleware/if-else-unequal-mw.tsv b/graph/test/typescript/hono/expected/middleware/if-else-unequal-mw.tsv new file mode 100644 index 000000000..aae2611d6 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/if-else-unequal-mw.tsv @@ -0,0 +1,22 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R15 clause 2: unequal if/else branches start at the same position, each conditional; a later middleware continues after the longer branch and its chain is dynamic_middleware_list; /api/ping (nothing after the branch) and /health are not dynamic +# census: use_conditional_loop, use_scoped (SPEC R15 clause 2) +route GET /health index.ts:28:20 index.ts:18:13 5 +route GET /api/ping index.ts:29:22 index.ts:18:13 6 +route GET /api/items/:id index.ts:30:27 index.ts:18:13 7 +mw index.ts:12:38 index.ts:28:20 GET /health 0 index.ts:18:13 +mw index.ts:12:38 index.ts:29:22 GET /api/ping 0 index.ts:18:13 +mw index.ts:13:40 index.ts:29:22 GET /api/ping 1 index.ts:18:13 +mw index.ts:14:37 index.ts:29:22 GET /api/ping 2 index.ts:18:13 +mw index.ts:15:35 index.ts:29:22 GET /api/ping 1 index.ts:18:13 +mw index.ts:12:38 index.ts:30:27 GET /api/items/:id 0 index.ts:18:13 +mw index.ts:13:40 index.ts:30:27 GET /api/items/:id 1 index.ts:18:13 +mw index.ts:14:37 index.ts:30:27 GET /api/items/:id 2 index.ts:18:13 +mw index.ts:15:35 index.ts:30:27 GET /api/items/:id 1 index.ts:18:13 +mw index.ts:16:34 index.ts:30:27 GET /api/items/:id 3 index.ts:18:13 +regby index.ts:1:1 index.ts:28:20 GET /health index.ts:18:13 +regby index.ts:1:1 index.ts:29:22 GET /api/ping index.ts:18:13 +regby index.ts:1:1 index.ts:30:27 GET /api/items/:id index.ts:18:13 +unknown index.ts:21:3 conditional_registration index.ts:13:40 +unknown index.ts:22:3 conditional_registration index.ts:14:37 +unknown index.ts:24:3 conditional_registration index.ts:15:35 +unknown index.ts:30:1 dynamic_middleware_list index.ts:30:27 diff --git a/graph/test/typescript/hono/expected/middleware/lib-mw-options.entries b/graph/test/typescript/hono/expected/middleware/lib-mw-options.entries new file mode 100644 index 000000000..45d1bf9d6 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/lib-mw-options.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-07/R10: cors(...) and the basicAuth(...) call inside guard are the middleware; their option callbacks are not +# census: builtin_mw_import, mw_factory_returning_fn (violation hono-2-07, SPEC R10) +entry index.ts:16:15 http diff --git a/graph/test/typescript/hono/expected/middleware/lib-mw-options.tsv b/graph/test/typescript/hono/expected/middleware/lib-mw-options.tsv new file mode 100644 index 000000000..cffde1be0 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/lib-mw-options.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-07/R10: cors(...) and the basicAuth(...) call inside guard are the middleware; their option callbacks are not +# census: builtin_mw_import, mw_factory_returning_fn (violation hono-2-07, SPEC R10) +route GET /x index.ts:16:15 index.ts:13:13 2 +mw lib:hono/cors.cors@index.ts:14:14 index.ts:16:15 GET /x 0 index.ts:13:13 +mw lib:hono/basic-auth.basicAuth@index.ts:10:10 index.ts:16:15 GET /x 1 index.ts:13:13 +regby index.ts:1:1 index.ts:16:15 GET /x index.ts:13:13 diff --git a/graph/test/typescript/hono/expected/middleware/loop-use.entries b/graph/test/typescript/hono/expected/middleware/loop-use.entries new file mode 100644 index 000000000..dd35e39e1 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/loop-use.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-06 loop form: the two known middleware at 0 and 1, the chain declared dynamic_middleware_list +# census: use_global, use_multi (violation hono-1-06, LOOP form; SPEC 3.3 dynamic chains) +entry index.ts:17:16 http +entry index.ts:21:17 http +entry index.ts:9:16 http diff --git a/graph/test/typescript/hono/expected/middleware/loop-use.tsv b/graph/test/typescript/hono/expected/middleware/loop-use.tsv new file mode 100644 index 000000000..fd71b67af --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/loop-use.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-06 loop form: the two known middleware at 0 and 1, the chain declared dynamic_middleware_list +# census: use_global, use_multi (violation hono-1-06, LOOP form; SPEC 3.3 dynamic chains) +route GET /x index.ts:21:17 index.ts:8:15 3 +mw index.ts:9:16 index.ts:21:17 GET /x 0 index.ts:8:15 +mw index.ts:17:16 index.ts:21:17 GET /x 1 index.ts:8:15 +regby index.ts:7:1 index.ts:21:17 GET /x index.ts:8:15 +unknown index.ts:21:3 dynamic_middleware_list index.ts:21:17 diff --git a/graph/test/typescript/hono/expected/middleware/mw-from-namespace-field.entries b/graph/test/typescript/hono/expected/middleware/mw-from-namespace-field.entries new file mode 100644 index 000000000..977a7004d --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/mw-from-namespace-field.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m32/hono-4-01: factories via a namespace held in a field, a module destructure and namespace members are the inner project functions +# census: use_global (stress-4 mutation m32 shapes; violation hono-4-01) +entry guards/scope.ts:4:17 http +entry guards/session.ts:4:20 http +entry index.ts:15:23 http +entry index.ts:22:23 http +entry index.ts:25:19 http diff --git a/graph/test/typescript/hono/expected/middleware/mw-from-namespace-field.tsv b/graph/test/typescript/hono/expected/middleware/mw-from-namespace-field.tsv new file mode 100644 index 000000000..3c39c1fad --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/mw-from-namespace-field.tsv @@ -0,0 +1,21 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m32/hono-4-01: factories via a namespace held in a field, a module destructure and namespace members are the inner project functions +# census: use_global (stress-4 mutation m32 shapes; violation hono-4-01) +route GET /api/system/info index.ts:15:23 index.ts:27:13 2 +route GET /api/reports/daily index.ts:22:23 index.ts:27:13 5 +route GET /api/audit/log index.ts:25:19 index.ts:27:13 8 +mw guards/session.ts:4:20 index.ts:15:23 GET /api/system/info 0 index.ts:27:13 +mw guards/scope.ts:4:17 index.ts:15:23 GET /api/system/info 1 index.ts:27:13 +mw guards/session.ts:4:20 index.ts:22:23 GET /api/reports/daily 0 index.ts:27:13 +mw guards/scope.ts:4:17 index.ts:22:23 GET /api/reports/daily 1 index.ts:27:13 +mw guards/session.ts:4:20 index.ts:25:19 GET /api/audit/log 0 index.ts:27:13 +mw guards/scope.ts:4:17 index.ts:25:19 GET /api/audit/log 1 index.ts:27:13 +ctx guards/session.ts:4:20 guards/scope.ts:4:17 user +ctx guards/session.ts:4:20 index.ts:15:23 user +ctx guards/session.ts:4:20 index.ts:22:23 user +ctx guards/session.ts:4:20 index.ts:25:19 user +submount /api/system index.ts:28:5 +submount /api/reports index.ts:29:5 +submount /api/audit index.ts:30:5 +regby index.ts:12:3 index.ts:15:23 GET /api/system/info index.ts:27:13 +regby index.ts:1:1 index.ts:22:23 GET /api/reports/daily index.ts:27:13 +regby index.ts:1:1 index.ts:25:19 GET /api/audit/log index.ts:27:13 diff --git a/graph/test/typescript/hono/expected/middleware/partial-sub-app-chain.entries b/graph/test/typescript/hono/expected/middleware/partial-sub-app-chain.entries new file mode 100644 index 000000000..7df6ee21c --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/partial-sub-app-chain.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m34/hono-4-03/SPEC 3.3: the partial route's chain is holder use(*) 0, controller use 1, per-route 2; the holder's use(*) function is an http entry; the scoped holder use is partial_route_scope +# census: basepath (stress-4 mutation m34 shape; violation hono-4-03, SPEC 3.3 [iter2] partial routes) +entry index.ts:11:21 http +entry index.ts:14:15 http +entry index.ts:17:15 http +entry index.ts:27:36 http diff --git a/graph/test/typescript/hono/expected/middleware/partial-sub-app-chain.tsv b/graph/test/typescript/hono/expected/middleware/partial-sub-app-chain.tsv new file mode 100644 index 000000000..3873ce685 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/partial-sub-app-chain.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m34/hono-4-03/SPEC 3.3: the partial route's chain is holder use(*) 0, controller use 1, per-route 2; the holder's use(*) function is an http entry; the scoped holder use is partial_route_scope +# census: basepath (stress-4 mutation m34 shape; violation hono-4-03, SPEC 3.3 [iter2] partial routes) +partial index.ts:27:36 GET dynamic_mount_prefix +mwp index.ts:11:21 index.ts:27:36 GET 1 +mwp index.ts:14:15 index.ts:27:36 GET 0 +mwp index.ts:17:15 index.ts:27:36 GET 2 +unknown index.ts:53:1 dynamic_mount_prefix index.ts:27:36 +unknown index.ts:53:1 partial_route_scope index.ts:11:21 diff --git a/graph/test/typescript/hono/expected/middleware/runtime-list-except-ternary.entries b/graph/test/typescript/hono/expected/middleware/runtime-list-except-ternary.entries new file mode 100644 index 000000000..90d8c5914 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/runtime-list-except-ternary.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: routes straight on a run-time-based tier app are partial with their decidable chain (the tier's own /* middleware at 0) and dynamic_middleware_list from the run-time middleware list; except(..) with a run-time exclusion is dynamic_except +# census: use_conditional_loop, combine_except (stress-6 oracle note; SPEC 3.3, SPEC 3.1) +entry index.ts:28:17 http +entry index.ts:32:22 http +entry index.ts:40:15 http diff --git a/graph/test/typescript/hono/expected/middleware/runtime-list-except-ternary.tsv b/graph/test/typescript/hono/expected/middleware/runtime-list-except-ternary.tsv new file mode 100644 index 000000000..f8b0a93e3 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/runtime-list-except-ternary.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: routes straight on a run-time-based tier app are partial with their decidable chain (the tier's own /* middleware at 0) and dynamic_middleware_list from the run-time middleware list; except(..) with a run-time exclusion is dynamic_except +# census: use_conditional_loop, combine_except (stress-6 oracle note; SPEC 3.3, SPEC 3.1) +route GET / index.ts:40:15 index.ts:39:14 0 +submount / index.ts:41:32 +partial index.ts:32:22 GET dynamic_mount_prefix +mwp index.ts:28:17 index.ts:32:22 GET 0 +regby index.ts:1:1 index.ts:40:15 GET / index.ts:39:14 +unknown index.ts:30:35 dynamic_except index.ts:26:1 +unknown index.ts:32:3 dynamic_middleware_list index.ts:32:22 +unknown index.ts:32:3 dynamic_mount_prefix index.ts:32:22 diff --git a/graph/test/typescript/hono/expected/middleware/scoped-use.entries b/graph/test/typescript/hono/expected/middleware/scoped-use.entries new file mode 100644 index 000000000..23bb42be0 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/scoped-use.entries @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: /admin/* reaches both admin routes; /orgs/:org/* reaches billing; /public gets nothing +# census: use_scoped, path_wildcard +entry index.ts:17:25 http +entry index.ts:18:29 http +entry index.ts:19:31 http +entry index.ts:20:20 http +entry index.ts:6:38 http +entry index.ts:9:35 http diff --git a/graph/test/typescript/hono/expected/middleware/scoped-use.tsv b/graph/test/typescript/hono/expected/middleware/scoped-use.tsv new file mode 100644 index 000000000..3f349df6d --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/scoped-use.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: /admin/* reaches both admin routes; /orgs/:org/* reaches billing; /public gets nothing +# census: use_scoped, path_wildcard +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /admin/users index.ts:17:25 index.ts:13:13 2 +route GET /admin/users/:id index.ts:18:29 index.ts:13:13 3 +route GET /orgs/:org/billing index.ts:19:31 index.ts:13:13 4 +route GET /public index.ts:20:20 index.ts:13:13 5 +mw index.ts:6:38 index.ts:17:25 GET /admin/users 0 index.ts:13:13 +mw index.ts:6:38 index.ts:18:29 GET /admin/users/:id 0 index.ts:13:13 +mw index.ts:9:35 index.ts:19:31 GET /orgs/:org/billing 0 index.ts:13:13 +regby index.ts:1:1 index.ts:17:25 GET /admin/users index.ts:13:13 +regby index.ts:1:1 index.ts:18:29 GET /admin/users/:id index.ts:13:13 +regby index.ts:1:1 index.ts:19:31 GET /orgs/:org/billing index.ts:13:13 +regby index.ts:1:1 index.ts:20:20 GET /public index.ts:13:13 diff --git a/graph/test/typescript/hono/expected/middleware/spread-after-mount.entries b/graph/test/typescript/hono/expected/middleware/spread-after-mount.entries new file mode 100644 index 000000000..6424cfc57 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/spread-after-mount.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: spread array a then b reaches /after only; the mounted /sub/inside was copied before the use +# census: use_multi, route_mount_literal +entry index.ts:12:20 http +entry index.ts:17:23 http +entry index.ts:7:30 http +entry index.ts:8:30 http diff --git a/graph/test/typescript/hono/expected/middleware/spread-after-mount.tsv b/graph/test/typescript/hono/expected/middleware/spread-after-mount.tsv new file mode 100644 index 000000000..ead5e8aa3 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/spread-after-mount.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: spread array a then b reaches /after only; the mounted /sub/inside was copied before the use +# census: use_multi, route_mount_literal +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /sub/inside index.ts:12:20 index.ts:14:17 0 +route GET /after index.ts:17:23 index.ts:14:17 3 +mw index.ts:7:30 index.ts:17:23 GET /after 0 index.ts:14:17 +mw index.ts:8:30 index.ts:17:23 GET /after 1 index.ts:14:17 +submount /sub index.ts:15:9 +regby index.ts:1:1 index.ts:12:20 GET /sub/inside index.ts:14:17 +regby index.ts:1:1 index.ts:17:23 GET /after index.ts:14:17 diff --git a/graph/test/typescript/hono/expected/middleware/spread-call-result.entries b/graph/test/typescript/hono/expected/middleware/spread-call-result.entries new file mode 100644 index 000000000..0d5a2fdff --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/spread-call-result.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-06: ...createLimits() seen through: limiter's inner fn at 0 and 1, after at 2 +# census: use_multi, mw_factory_returning_fn (violation hono-1-06) +entry index.ts:20:34 http +entry index.ts:27:15 http +entry index.ts:8:10 http diff --git a/graph/test/typescript/hono/expected/middleware/spread-call-result.tsv b/graph/test/typescript/hono/expected/middleware/spread-call-result.tsv new file mode 100644 index 000000000..a86c58522 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/spread-call-result.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-06: ...createLimits() seen through: limiter's inner fn at 0 and 1, after at 2 +# census: use_multi, mw_factory_returning_fn (violation hono-1-06) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /x index.ts:27:15 index.ts:24:13 3 +mw index.ts:8:10 index.ts:27:15 GET /x 0 index.ts:24:13 +mw index.ts:8:10 index.ts:27:15 GET /x 1 index.ts:24:13 +mw index.ts:20:34 index.ts:27:15 GET /x 2 index.ts:24:13 +regby index.ts:1:1 index.ts:27:15 GET /x index.ts:24:13 diff --git a/graph/test/typescript/hono/expected/middleware/sub-app-middleware.entries b/graph/test/typescript/hono/expected/middleware/sub-app-middleware.entries new file mode 100644 index 000000000..5031eaef1 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/sub-app-middleware.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: outer then inner on /users; inner never reaches /other +# census: use_global, route_mount_literal +entry index.ts:11:16 http +entry index.ts:16:19 http +entry index.ts:6:34 http +entry index.ts:7:34 http diff --git a/graph/test/typescript/hono/expected/middleware/sub-app-middleware.tsv b/graph/test/typescript/hono/expected/middleware/sub-app-middleware.tsv new file mode 100644 index 000000000..8ba3eae40 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/sub-app-middleware.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: outer then inner on /users; inner never reaches /other +# census: use_global, route_mount_literal +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /users index.ts:11:16 index.ts:13:13 2 +route GET /other index.ts:16:19 index.ts:13:13 3 +mw index.ts:6:34 index.ts:11:16 GET /users 0 index.ts:13:13 +mw index.ts:7:34 index.ts:11:16 GET /users 1 index.ts:13:13 +mw index.ts:6:34 index.ts:16:19 GET /other 0 index.ts:13:13 +submount /users index.ts:15:5 +regby index.ts:1:1 index.ts:11:16 GET /users index.ts:13:13 +regby index.ts:1:1 index.ts:16:19 GET /other index.ts:13:13 diff --git a/graph/test/typescript/hono/expected/middleware/use-order.entries b/graph/test/typescript/hono/expected/middleware/use-order.entries new file mode 100644 index 000000000..0d2f5e253 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/use-order.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: timing reaches all three; auth registered after /public reaches only the two later routes +# census: use_global, use_scoped, await_next +entry index.ts:15:20 http +entry index.ts:17:21 http +entry index.ts:18:22 http +entry index.ts:6:33 http +entry index.ts:9:31 http diff --git a/graph/test/typescript/hono/expected/middleware/use-order.tsv b/graph/test/typescript/hono/expected/middleware/use-order.tsv new file mode 100644 index 000000000..9a9b4ed51 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/use-order.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: timing reaches all three; auth registered after /public reaches only the two later routes +# census: use_global, use_scoped, await_next +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /public index.ts:15:20 index.ts:13:13 1 +route GET /private index.ts:17:21 index.ts:13:13 3 +route POST /private index.ts:18:22 index.ts:13:13 4 +mw index.ts:6:33 index.ts:15:20 GET /public 0 index.ts:13:13 +mw index.ts:6:33 index.ts:17:21 GET /private 0 index.ts:13:13 +mw index.ts:9:31 index.ts:17:21 GET /private 1 index.ts:13:13 +mw index.ts:6:33 index.ts:18:22 POST /private 0 index.ts:13:13 +mw index.ts:9:31 index.ts:18:22 POST /private 1 index.ts:13:13 +regby index.ts:1:1 index.ts:15:20 GET /public index.ts:13:13 +regby index.ts:1:1 index.ts:17:21 GET /private index.ts:13:13 +regby index.ts:1:1 index.ts:18:22 POST /private index.ts:13:13 diff --git a/graph/test/typescript/hono/expected/middleware/validator.entries b/graph/test/typescript/hono/expected/middleware/validator.entries new file mode 100644 index 000000000..bbaa3a435 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/validator.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: zValidator and validator are per-route middleware before the handler; NewPost is the json schema the handler's valid('json') reads +# census: zvalidator, validator_builtin, req_valid +entry index.ts:11:49 http +entry index.ts:15:3 http diff --git a/graph/test/typescript/hono/expected/middleware/validator.tsv b/graph/test/typescript/hono/expected/middleware/validator.tsv new file mode 100644 index 000000000..c6cc373fd --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/validator.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: zValidator and validator are per-route middleware before the handler; NewPost is the json schema the handler's valid('json') reads +# census: zvalidator, validator_builtin, req_valid +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /posts index.ts:11:49 index.ts:10:13 1 +route GET /search index.ts:15:3 index.ts:10:13 3 +mw lib:@hono/zod-validator.zValidator@index.ts:11:20 index.ts:11:49 POST /posts 0 index.ts:10:13 +mw lib:hono/validator.validator@index.ts:14:3 index.ts:15:3 GET /search 0 index.ts:10:13 +schema index.ts:11:49 json index.ts:8:7 +validread index.ts:11:63 index.ts:11:49 json index.ts:8:7 +regby index.ts:1:1 index.ts:11:49 POST /posts index.ts:10:13 +regby index.ts:1:1 index.ts:15:3 GET /search index.ts:10:13 diff --git a/graph/test/typescript/hono/expected/middleware/verb-next.entries b/graph/test/typescript/hono/expected/middleware/verb-next.entries new file mode 100644 index 000000000..3d9ca3783 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/verb-next.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: an all('*') function that calls next is middleware for /x +# census: await_next, verb_literal_path +entry index.ts:6:14 http +entry index.ts:9:15 http diff --git a/graph/test/typescript/hono/expected/middleware/verb-next.tsv b/graph/test/typescript/hono/expected/middleware/verb-next.tsv new file mode 100644 index 000000000..baf1b4482 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/verb-next.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: an all('*') function that calls next is middleware for /x +# census: await_next, verb_literal_path +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route ANY /* index.ts:6:14 index.ts:5:13 0 +route GET /x index.ts:9:15 index.ts:5:13 1 +mw index.ts:6:14 index.ts:9:15 GET /x 0 index.ts:5:13 +regby index.ts:1:1 index.ts:6:14 ANY /* index.ts:5:13 +regby index.ts:1:1 index.ts:9:15 GET /x index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/negative/context-lookalike.entries b/graph/test/typescript/hono/expected/negative/context-lookalike.entries new file mode 100644 index 000000000..688817887 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/context-lookalike.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Map set/get on a parameter named c: no ctx_key_flow +# census: ctx_get (partition outside Hono contexts) (SPEC N5) +entry index.ts:16:16 http diff --git a/graph/test/typescript/hono/expected/negative/context-lookalike.tsv b/graph/test/typescript/hono/expected/negative/context-lookalike.tsv new file mode 100644 index 000000000..da39cf854 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/context-lookalike.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Map set/get on a parameter named c: no ctx_key_flow +# census: ctx_get (partition outside Hono contexts) (SPEC N5) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /me index.ts:16:16 index.ts:15:13 0 +regby index.ts:1:1 index.ts:16:16 GET /me index.ts:15:13 +absent index.ts:5:1 +absent index.ts:8:1 diff --git a/graph/test/typescript/hono/expected/negative/foreign-fetch.entries b/graph/test/typescript/hono/expected/negative/foreign-fetch.entries new file mode 100644 index 000000000..556591c63 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/foreign-fetch.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-12: fetch to another host's /health is no request to GET /health; ping is in no Hono relation +# census: neg_verb_literal_nonhono (violation hono-2-12) +entry index.ts:14:20 http diff --git a/graph/test/typescript/hono/expected/negative/foreign-fetch.tsv b/graph/test/typescript/hono/expected/negative/foreign-fetch.tsv new file mode 100644 index 000000000..ed627f0a4 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/foreign-fetch.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-12: fetch to another host's /health is no request to GET /health; ping is in no Hono relation +# census: neg_verb_literal_nonhono (violation hono-2-12) +route GET /health index.ts:14:20 index.ts:13:13 0 +regby index.ts:1:1 index.ts:14:20 GET /health index.ts:13:13 +absent index.ts:7:3 diff --git a/graph/test/typescript/hono/expected/negative/http-client.entries b/graph/test/typescript/hono/expected/negative/http-client.entries new file mode 100644 index 000000000..ec622580b --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/http-client.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: an HTTP client object with get/post(url, cb): its callbacks and caller are not routes +# census: neg_verb_literal_nonhono (SPEC N2) +entry index.ts:5:18 http diff --git a/graph/test/typescript/hono/expected/negative/http-client.tsv b/graph/test/typescript/hono/expected/negative/http-client.tsv new file mode 100644 index 000000000..8fe18fc77 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/http-client.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: an HTTP client object with get/post(url, cb): its callbacks and caller are not routes +# census: neg_verb_literal_nonhono (SPEC N2) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /only index.ts:5:18 index.ts:4:13 0 +regby index.ts:1:1 index.ts:5:18 GET /only index.ts:4:13 +absent client.ts:11:21 +absent client.ts:12:37 +absent client.ts:10:1 diff --git a/graph/test/typescript/hono/expected/negative/local-hono-class.entries b/graph/test/typescript/hono/expected/negative/local-hono-class.entries new file mode 100644 index 000000000..b9cd1a7d1 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/local-hono-class.entries @@ -0,0 +1,2 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a project class named Hono and a workflow .route(): zero Hono rows; the route table must still be exported +# census: neg_route_on_nonhono (SPEC N4) diff --git a/graph/test/typescript/hono/expected/negative/local-hono-class.tsv b/graph/test/typescript/hono/expected/negative/local-hono-class.tsv new file mode 100644 index 000000000..eef279747 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/local-hono-class.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a project class named Hono and a workflow .route(): zero Hono rows; the route table must still be exported +# census: neg_route_on_nonhono (SPEC N4) +absent index.ts:10:10 +absent index.ts:11:19 +absent index.ts:17:29 diff --git a/graph/test/typescript/hono/expected/negative/map-get.entries b/graph/test/typescript/hono/expected/negative/map-get.entries new file mode 100644 index 000000000..8776bacee --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/map-get.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Map/KV/Headers get+use beside a real app: one route; the KV callbacks are absent everywhere +# census: neg_map_get_in_hono_file (SPEC N1) +entry index.ts:20:18 http diff --git a/graph/test/typescript/hono/expected/negative/map-get.tsv b/graph/test/typescript/hono/expected/negative/map-get.tsv new file mode 100644 index 000000000..88099fb0f --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/map-get.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: Map/KV/Headers get+use beside a real app: one route; the KV callbacks are absent everywhere +# census: neg_map_get_in_hono_file (SPEC N1) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /real index.ts:20:18 index.ts:19:13 0 +regby index.ts:1:1 index.ts:20:18 GET /real index.ts:19:13 +absent index.ts:13:19 +absent index.ts:14:8 +absent index.ts:10:3 +absent index.ts:11:3 diff --git a/graph/test/typescript/hono/expected/negative/other-router.entries b/graph/test/typescript/hono/expected/negative/other-router.entries new file mode 100644 index 000000000..60451bfb6 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/other-router.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a non-Hono Router class with get/post/use/route: none of its handlers is a route or middleware +# census: neg_other_router_import (SPEC N3) +entry index.ts:14:18 http diff --git a/graph/test/typescript/hono/expected/negative/other-router.tsv b/graph/test/typescript/hono/expected/negative/other-router.tsv new file mode 100644 index 000000000..39fe10c4b --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/other-router.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a non-Hono Router class with get/post/use/route: none of its handlers is a route or middleware +# census: neg_other_router_import (SPEC N3) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /hono index.ts:14:18 index.ts:13:13 0 +regby index.ts:1:1 index.ts:14:18 GET /hono index.ts:13:13 +absent index.ts:7:12 +absent index.ts:8:29 +absent index.ts:9:30 diff --git a/graph/test/typescript/hono/expected/negative/request-lookalike.entries b/graph/test/typescript/hono/expected/negative/request-lookalike.entries new file mode 100644 index 000000000..2402a6c07 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/request-lookalike.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: agent.request and a local hc: no remote edge from either caller +# census: app_request_test (partition where the receiver is not an app) (SPEC N7, N8) +entry index.ts:6:18 http diff --git a/graph/test/typescript/hono/expected/negative/request-lookalike.tsv b/graph/test/typescript/hono/expected/negative/request-lookalike.tsv new file mode 100644 index 000000000..19407edd2 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/request-lookalike.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: agent.request and a local hc: no remote edge from either caller +# census: app_request_test (partition where the receiver is not an app) (SPEC N7, N8) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /real index.ts:6:18 index.ts:5:13 0 +regby index.ts:1:1 index.ts:6:18 GET /real index.ts:5:13 +absent index.ts:9:1 +absent index.ts:14:1 diff --git a/graph/test/typescript/hono/expected/negative/zod-openapi-meta.entries b/graph/test/typescript/hono/expected/negative/zod-openapi-meta.entries new file mode 100644 index 000000000..3c1cb7e71 --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/zod-openapi-meta.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: only app.openapi registers; .openapi('Name') metadata and typeof createRoute are not routes or RPC roots; Params/User are its schemas +# census: openapi_register, apptype_typeof (SPEC N6, N9) +entry index.ts:18:22 http diff --git a/graph/test/typescript/hono/expected/negative/zod-openapi-meta.tsv b/graph/test/typescript/hono/expected/negative/zod-openapi-meta.tsv new file mode 100644 index 000000000..79b6591ef --- /dev/null +++ b/graph/test/typescript/hono/expected/negative/zod-openapi-meta.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: only app.openapi registers; .openapi('Name') metadata and typeof createRoute are not routes or RPC roots; Params/User are its schemas +# census: openapi_register, apptype_typeof (SPEC N6, N9) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /users/:id index.ts:18:22 index.ts:17:13 0 +schema index.ts:18:22 param index.ts:7:7 +schema index.ts:18:22 response index.ts:6:7 +validread index.ts:18:42 index.ts:18:22 param index.ts:7:7 +regby index.ts:1:1 index.ts:18:22 GET /users/:id index.ts:17:13 diff --git a/graph/test/typescript/hono/expected/openapi/create-route.entries b/graph/test/typescript/hono/expected/openapi/create-route.entries new file mode 100644 index 000000000..976cfd362 --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/create-route.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: route objects from routes.ts, {id} as :id, handler is arg 1 not the hook; param and response schemas, Task as a base of z.array(Task) +# census: create_route, openapi_register, new_openapihono +entry index.ts:12:3 http +entry index.ts:9:24 http diff --git a/graph/test/typescript/hono/expected/openapi/create-route.tsv b/graph/test/typescript/hono/expected/openapi/create-route.tsv new file mode 100644 index 000000000..6e2d83daf --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/create-route.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: route objects from routes.ts, {id} as :id, handler is arg 1 not the hook; param and response schemas, Task as a base of z.array(Task) +# census: create_route, openapi_register, new_openapihono +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /tasks index.ts:9:24 index.ts:7:13 0 +route GET /tasks/:id index.ts:12:3 index.ts:7:13 1 +schema index.ts:12:3 param expr@routes.ts:14:22 +schema index.ts:12:3 response routes.ts:3:14 +schema index.ts:9:24 response expr@routes.ts:8:86 +schema index.ts:9:24 response routes.ts:3:14 +validread index.ts:12:23 index.ts:12:3 param expr@routes.ts:14:22 +regby index.ts:1:1 index.ts:12:3 GET /tasks/:id index.ts:7:13 +regby index.ts:1:1 index.ts:9:24 GET /tasks index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/openapi/namespace-handlers.entries b/graph/test/typescript/hono/expected/openapi/namespace-handlers.entries new file mode 100644 index 000000000..1c7953dda --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/namespace-handlers.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-04: handlers.login (namespace member) handles POST /auth/login built from a template over a literal +# census: openapi_register, create_route (violation hono-1-04) +entry auth.handlers.ts:3:22 http diff --git a/graph/test/typescript/hono/expected/openapi/namespace-handlers.tsv b/graph/test/typescript/hono/expected/openapi/namespace-handlers.tsv new file mode 100644 index 000000000..544d7e39b --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/namespace-handlers.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-04: handlers.login (namespace member) handles POST /auth/login built from a template over a literal +# census: openapi_register, create_route (violation hono-1-04) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /auth/login auth.handlers.ts:3:22 index.ts:8:14 0 +regby index.ts:1:1 auth.handlers.ts:3:22 POST /auth/login index.ts:8:14 diff --git a/graph/test/typescript/hono/expected/openapi/response-helper.entries b/graph/test/typescript/hono/expected/openapi/response-helper.entries new file mode 100644 index 000000000..697e5cb89 --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/response-helper.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-04: response and body schemas written through helpers name the helper argument (Account, ErrorBody), as inline ones do +# census: create_route (violation hono-3-04, SPEC 3.9) +entry index.ts:38:25 http +entry index.ts:39:27 http +entry index.ts:40:28 http diff --git a/graph/test/typescript/hono/expected/openapi/response-helper.tsv b/graph/test/typescript/hono/expected/openapi/response-helper.tsv new file mode 100644 index 000000000..b7e1fa239 --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/response-helper.tsv @@ -0,0 +1,17 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-04: response and body schemas written through helpers name the helper argument (Account, ErrorBody), as inline ones do +# census: create_route (violation hono-3-04, SPEC 3.9) +route GET /accounts/:id index.ts:38:25 index.ts:37:13 0 +route GET /accounts index.ts:39:27 index.ts:37:13 1 +route POST /accounts index.ts:40:28 index.ts:37:13 2 +schema index.ts:38:25 param expr@index.ts:22:22 +schema index.ts:38:25 response index.ts:15:7 +schema index.ts:38:25 response index.ts:16:7 +schema index.ts:39:27 response index.ts:15:7 +schema index.ts:39:27 response index.ts:17:7 +schema index.ts:40:28 json index.ts:15:7 +schema index.ts:40:28 response index.ts:15:7 +validread index.ts:38:45 index.ts:38:25 param expr@index.ts:22:22 +validread index.ts:40:42 index.ts:40:28 json index.ts:15:7 +regby index.ts:1:1 index.ts:38:25 GET /accounts/:id index.ts:37:13 +regby index.ts:1:1 index.ts:39:27 GET /accounts index.ts:37:13 +regby index.ts:1:1 index.ts:40:28 POST /accounts index.ts:37:13 diff --git a/graph/test/typescript/hono/expected/openapi/route-middleware.entries b/graph/test/typescript/hono/expected/openapi/route-middleware.entries new file mode 100644 index 000000000..38e6f0d1e --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/route-middleware.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: audit (app-level) then guard (routeDef.middleware); the inline json body schema +# census: create_route, openapi_register, use_global +entry index.ts:19:21 http +entry index.ts:6:34 http +entry index.ts:7:34 http diff --git a/graph/test/typescript/hono/expected/openapi/route-middleware.tsv b/graph/test/typescript/hono/expected/openapi/route-middleware.tsv new file mode 100644 index 000000000..dbd16e1ed --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/route-middleware.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: audit (app-level) then guard (routeDef.middleware); the inline json body schema +# census: create_route, openapi_register, use_global +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /notes index.ts:19:21 index.ts:17:13 2 +mw index.ts:6:34 index.ts:19:21 POST /notes 0 index.ts:17:13 +mw index.ts:7:34 index.ts:19:21 POST /notes 1 index.ts:17:13 +schema index.ts:19:21 json expr@index.ts:13:63 +regby index.ts:1:1 index.ts:19:21 POST /notes index.ts:17:13 diff --git a/graph/test/typescript/hono/expected/openapi/spread-error-responses.entries b/graph/test/typescript/hono/expected/openapi/spread-error-responses.entries new file mode 100644 index 000000000..558abf03a --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/spread-error-responses.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m27/hono-4-08/O19: spread error responses name each errorBody(...) call once, never the helper declaration +# census: create_route (stress-4 mutation m27 shape; violation hono-4-08, oracle gap O19) +entry index.ts:20:24 http diff --git a/graph/test/typescript/hono/expected/openapi/spread-error-responses.tsv b/graph/test/typescript/hono/expected/openapi/spread-error-responses.tsv new file mode 100644 index 000000000..7d963e4d6 --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/spread-error-responses.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m27/hono-4-08/O19: spread error responses name each errorBody(...) call once, never the helper declaration +# census: create_route (stress-4 mutation m27 shape; violation hono-4-08, oracle gap O19) +route GET /secrets/:id index.ts:20:24 index.ts:19:13 0 +schema index.ts:20:24 param expr@index.ts:12:22 +schema index.ts:20:24 response expr@errors.ts:12:46 +schema index.ts:20:24 response expr@errors.ts:8:46 +schema index.ts:20:24 response index.ts:7:7 +validread index.ts:20:44 index.ts:20:24 param expr@index.ts:12:22 +regby index.ts:1:1 index.ts:20:24 GET /secrets/:id index.ts:19:13 diff --git a/graph/test/typescript/hono/expected/openapi/status-key-helpers.entries b/graph/test/typescript/hono/expected/openapi/status-key-helpers.entries new file mode 100644 index 000000000..e14a12350 --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/status-key-helpers.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m26/hono-3-04: [Status.X] keys and barrel-re-exported helpers name the helper argument for every response and body slot +# census: create_route (stress-4 mutation m26 shape; violation hono-3-04 reopened) +entry handlers.ts:3:26 http +entry handlers.ts:4:29 http diff --git a/graph/test/typescript/hono/expected/openapi/status-key-helpers.tsv b/graph/test/typescript/hono/expected/openapi/status-key-helpers.tsv new file mode 100644 index 000000000..215399187 --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/status-key-helpers.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m26/hono-3-04: [Status.X] keys and barrel-re-exported helpers name the helper argument for every response and body slot +# census: create_route (stress-4 mutation m26 shape; violation hono-3-04 reopened) +route GET /tickets/:id handlers.ts:3:26 index.ts:8:13 0 +route POST /tickets handlers.ts:4:29 index.ts:8:13 1 +schema handlers.ts:3:26 param expr@routes.ts:12:22 +schema handlers.ts:3:26 response routes.ts:5:7 +schema handlers.ts:3:26 response routes.ts:6:7 +schema handlers.ts:4:29 json routes.ts:7:7 +schema handlers.ts:4:29 response routes.ts:5:7 +regby index.ts:1:1 handlers.ts:3:26 GET /tickets/:id index.ts:8:13 +regby index.ts:1:1 handlers.ts:4:29 POST /tickets index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/routing/all-and-any.entries b/graph/test/typescript/hono/expected/routing/all-and-any.entries new file mode 100644 index 000000000..00d471519 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/all-and-any.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: all is ANY; a handler that never calls next is not middleware +# census: verb_literal_path, path_wildcard +entry index.ts:7:21 http +entry index.ts:8:26 http diff --git a/graph/test/typescript/hono/expected/routing/all-and-any.tsv b/graph/test/typescript/hono/expected/routing/all-and-any.tsv new file mode 100644 index 000000000..f13115e5c --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/all-and-any.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: all is ANY; a handler that never calls next is not middleware +# census: verb_literal_path, path_wildcard +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route ANY /proxy/* index.ts:7:21 index.ts:5:13 0 +route GET /proxy/health index.ts:8:26 index.ts:5:13 1 +regby index.ts:1:1 index.ts:7:21 ANY /proxy/* index.ts:5:13 +regby index.ts:1:1 index.ts:8:26 GET /proxy/health index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/routing/conditional-loop.entries b/graph/test/typescript/hono/expected/routing/conditional-loop.entries new file mode 100644 index 000000000..157776120 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/conditional-loop.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: both branches and the loop body are emitted, each declared conditional_registration / loop_registration; /always plain +# census: verb_literal_path (registrations behind a condition / in a loop, SPEC 3.1) +entry index.ts:11:20 http +entry index.ts:15:20 http +entry index.ts:18:20 http +entry index.ts:9:21 http diff --git a/graph/test/typescript/hono/expected/routing/conditional-loop.tsv b/graph/test/typescript/hono/expected/routing/conditional-loop.tsv new file mode 100644 index 000000000..1529f7685 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/conditional-loop.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: both branches and the loop body are emitted, each declared conditional_registration / loop_registration; /always plain +# census: verb_literal_path (registrations behind a condition / in a loop, SPEC 3.1) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +# iteration 3: the loop over a known list runs per element (O11 loop binding): /loop is registered twice, same route row +route GET /debug index.ts:9:21 index.ts:5:13 0 +route GET /prod index.ts:11:20 index.ts:5:13 1 +route GET /loop index.ts:15:20 index.ts:5:13 2 +route GET /loop index.ts:15:20 index.ts:5:13 3 +route GET /always index.ts:18:20 index.ts:5:13 4 +regby index.ts:1:1 index.ts:11:20 GET /prod index.ts:5:13 +regby index.ts:1:1 index.ts:15:20 GET /loop index.ts:5:13 +regby index.ts:1:1 index.ts:18:20 GET /always index.ts:5:13 +regby index.ts:1:1 index.ts:9:21 GET /debug index.ts:5:13 +unknown index.ts:11:3 conditional_registration index.ts:11:20 +unknown index.ts:15:3 loop_registration index.ts:15:20 +unknown index.ts:9:3 conditional_registration index.ts:9:21 diff --git a/graph/test/typescript/hono/expected/routing/const-paths.entries b/graph/test/typescript/hono/expected/routing/const-paths.entries new file mode 100644 index 000000000..54884662f --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/const-paths.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: imported const, const-object member, template and + over consts all decidable +# census: verb_const_path, verb_template_path +entry index.ts:10:27 http +entry index.ts:11:23 http +entry index.ts:12:33 http +entry index.ts:9:16 http diff --git a/graph/test/typescript/hono/expected/routing/const-paths.tsv b/graph/test/typescript/hono/expected/routing/const-paths.tsv new file mode 100644 index 000000000..9c4a66ca0 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/const-paths.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: imported const, const-object member, template and + over consts all decidable +# census: verb_const_path, verb_template_path +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /users index.ts:9:16 index.ts:7:13 0 +route GET /users/:id index.ts:10:27 index.ts:7:13 1 +route GET /health index.ts:11:23 index.ts:7:13 2 +route GET /api/version index.ts:12:33 index.ts:7:13 3 +regby index.ts:1:1 index.ts:10:27 GET /users/:id index.ts:7:13 +regby index.ts:1:1 index.ts:11:23 GET /health index.ts:7:13 +regby index.ts:1:1 index.ts:12:33 GET /api/version index.ts:7:13 +regby index.ts:1:1 index.ts:9:16 GET /users index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/routing/dynamic-path.entries b/graph/test/typescript/hono/expected/routing/dynamic-path.entries new file mode 100644 index 000000000..1507989e5 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/dynamic-path.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: run-time path: partial + dynamic_route_path, the literal sibling still a route (the partly-known /v${version}/status handler is still an http entry: orchestrator ruling, it is registered on a recognised app) +# census: verb_template_path (non-decidable partition) +entry index.ts:8:32 http +entry index.ts:9:19 http diff --git a/graph/test/typescript/hono/expected/routing/dynamic-path.tsv b/graph/test/typescript/hono/expected/routing/dynamic-path.tsv new file mode 100644 index 000000000..4e881b665 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/dynamic-path.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: run-time path: partial + dynamic_route_path, the literal sibling still a route +# census: verb_template_path (non-decidable partition) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /fixed index.ts:9:19 index.ts:5:13 0 +partial index.ts:8:32 GET dynamic_route_path +regby index.ts:1:1 index.ts:9:19 GET /fixed index.ts:5:13 +unknown index.ts:8:1 dynamic_route_path index.ts:8:32 diff --git a/graph/test/typescript/hono/expected/routing/every-verb.entries b/graph/test/typescript/hono/expected/routing/every-verb.entries new file mode 100644 index 000000000..680a6a75a --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/every-verb.entries @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: ten routes, PURGE via on, path-less post continues /chain; all is ANY +# census: verb_literal_path, verb_chained, on_method, export_default_app +entry index.ts:10:26 http +entry index.ts:11:25 http +entry index.ts:12:23 http +entry index.ts:13:20 http +entry index.ts:14:27 http +entry index.ts:17:18 http +entry index.ts:18:9 http +entry index.ts:7:19 http +entry index.ts:8:20 http +entry index.ts:9:23 http diff --git a/graph/test/typescript/hono/expected/routing/every-verb.tsv b/graph/test/typescript/hono/expected/routing/every-verb.tsv new file mode 100644 index 000000000..1d96dd495 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/every-verb.tsv @@ -0,0 +1,23 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: ten routes, PURGE via on, path-less post continues /chain; all is ANY +# census: verb_literal_path, verb_chained, on_method, export_default_app +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /items index.ts:7:19 index.ts:5:13 0 +route POST /items index.ts:8:20 index.ts:5:13 1 +route PUT /items/:id index.ts:9:23 index.ts:5:13 2 +route DELETE /items/:id index.ts:10:26 index.ts:5:13 3 +route PATCH /items/:id index.ts:11:25 index.ts:5:13 4 +route OPTIONS /items index.ts:12:23 index.ts:5:13 5 +route ANY /health index.ts:13:20 index.ts:5:13 6 +route PURGE /cache index.ts:14:27 index.ts:5:13 7 +route GET /chain index.ts:17:18 index.ts:5:13 8 +route POST /chain index.ts:18:9 index.ts:5:13 9 +regby index.ts:1:1 index.ts:10:26 DELETE /items/:id index.ts:5:13 +regby index.ts:1:1 index.ts:11:25 PATCH /items/:id index.ts:5:13 +regby index.ts:1:1 index.ts:12:23 OPTIONS /items index.ts:5:13 +regby index.ts:1:1 index.ts:13:20 ANY /health index.ts:5:13 +regby index.ts:1:1 index.ts:14:27 PURGE /cache index.ts:5:13 +regby index.ts:1:1 index.ts:17:18 GET /chain index.ts:5:13 +regby index.ts:1:1 index.ts:18:9 POST /chain index.ts:5:13 +regby index.ts:1:1 index.ts:7:19 GET /items index.ts:5:13 +regby index.ts:1:1 index.ts:8:20 POST /items index.ts:5:13 +regby index.ts:1:1 index.ts:9:23 PUT /items/:id index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/routing/library-handler.entries b/graph/test/typescript/hono/expected/routing/library-handler.entries new file mode 100644 index 000000000..70efe7228 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/library-handler.entries @@ -0,0 +1,2 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-03/R5: routes whose handler is a library call are keyed at the call (serveStatic x2); no project entry +# census: verb_literal_path (violation hono-1-03; SPEC R5) diff --git a/graph/test/typescript/hono/expected/routing/library-handler.tsv b/graph/test/typescript/hono/expected/routing/library-handler.tsv new file mode 100644 index 000000000..76ccb5ece --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/library-handler.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-03/R5: routes whose handler is a library call are keyed at the call (serveStatic x2); no project entry +# census: verb_literal_path (violation hono-1-03; SPEC R5) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /favicon.ico lib:@hono/node-server/serve-static.serveStatic@index.ts:8:25 index.ts:7:13 0 +route ANY /assets/* lib:hono/serve-static.serveStatic@index.ts:11:3 index.ts:7:13 1 +regby index.ts:1:1 lib:@hono/node-server/serve-static.serveStatic@index.ts:8:25 GET /favicon.ico index.ts:7:13 +regby index.ts:1:1 lib:hono/serve-static.serveStatic@index.ts:11:3 ANY /assets/* index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/routing/mts-source.entries b/graph/test/typescript/hono/expected/routing/mts-source.entries new file mode 100644 index 000000000..5ee7c489a --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/mts-source.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: an .mts module is parsed like .ts +# census: new_hono, verb_literal_path (an .mts module) +entry server.mts:6:17 http +entry server.mts:7:18 http diff --git a/graph/test/typescript/hono/expected/routing/mts-source.tsv b/graph/test/typescript/hono/expected/routing/mts-source.tsv new file mode 100644 index 000000000..67abf2915 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/mts-source.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: an .mts module is parsed like .ts +# census: new_hono, verb_literal_path (an .mts module) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /mts server.mts:6:17 server.mts:4:20 0 +route POST /mts server.mts:7:18 server.mts:4:20 1 +regby server.mts:1:1 server.mts:6:17 GET /mts server.mts:4:20 +regby server.mts:1:1 server.mts:7:18 POST /mts server.mts:4:20 diff --git a/graph/test/typescript/hono/expected/routing/multi-handler.entries b/graph/test/typescript/hono/expected/routing/multi-handler.entries new file mode 100644 index 000000000..10dbca62e --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/multi-handler.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: non-last args are per-route middleware in argument order; handler is the last +# census: verb_multi_handler, await_next +entry index.ts:13:1 http +entry index.ts:20:27 http +entry index.ts:21:19 http +entry index.ts:6:40 http +entry index.ts:9:34 http diff --git a/graph/test/typescript/hono/expected/routing/multi-handler.tsv b/graph/test/typescript/hono/expected/routing/multi-handler.tsv new file mode 100644 index 000000000..8fe260334 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/multi-handler.tsv @@ -0,0 +1,13 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: non-last args are per-route middleware in argument order; handler is the last +# census: verb_multi_handler, await_next +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /orders index.ts:13:1 index.ts:17:13 2 +route GET /orders index.ts:20:27 index.ts:17:13 4 +route GET /plain index.ts:21:19 index.ts:17:13 5 +mw index.ts:6:40 index.ts:13:1 POST /orders 0 index.ts:17:13 +mw index.ts:9:34 index.ts:13:1 POST /orders 1 index.ts:17:13 +mw index.ts:9:34 index.ts:20:27 GET /orders 0 index.ts:17:13 +regby index.ts:1:1 index.ts:13:1 POST /orders index.ts:17:13 +regby index.ts:1:1 index.ts:20:27 GET /orders index.ts:17:13 +regby index.ts:1:1 index.ts:21:19 GET /plain index.ts:17:13 diff --git a/graph/test/typescript/hono/expected/routing/named-handlers.entries b/graph/test/typescript/hono/expected/routing/named-handlers.entries new file mode 100644 index 000000000..761bf3311 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/named-handlers.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: handler is the declaration in handlers.ts, not the reference +# census: verb_literal_path, export_default_app +entry handlers.ts:3:1 http +entry handlers.ts:7:27 http +entry handlers.ts:9:1 http diff --git a/graph/test/typescript/hono/expected/routing/named-handlers.tsv b/graph/test/typescript/hono/expected/routing/named-handlers.tsv new file mode 100644 index 000000000..138a0f599 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/named-handlers.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: handler is the declaration in handlers.ts, not the reference +# census: verb_literal_path, export_default_app +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /projects handlers.ts:3:1 index.ts:6:13 0 +route GET /projects/:id handlers.ts:7:27 index.ts:6:13 1 +route DELETE /projects/:id handlers.ts:9:1 index.ts:6:13 2 +regby index.ts:1:1 handlers.ts:3:1 GET /projects index.ts:6:13 +regby index.ts:1:1 handlers.ts:7:27 GET /projects/:id index.ts:6:13 +regby index.ts:1:1 handlers.ts:9:1 DELETE /projects/:id index.ts:6:13 diff --git a/graph/test/typescript/hono/expected/routing/on-arrays.entries b/graph/test/typescript/hono/expected/routing/on-arrays.entries new file mode 100644 index 000000000..7b5bf6e2d --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/on-arrays.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: verbs x paths cross product, one handler four routes; lower-case verb upper-cased +# census: on_method_array, on_method +entry index.ts:7:52 http +entry index.ts:8:32 http +entry index.ts:9:27 http diff --git a/graph/test/typescript/hono/expected/routing/on-arrays.tsv b/graph/test/typescript/hono/expected/routing/on-arrays.tsv new file mode 100644 index 000000000..9938da957 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/on-arrays.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: verbs x paths cross product, one handler four routes; lower-case verb upper-cased +# census: on_method_array, on_method +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /form index.ts:7:52 index.ts:5:13 0 +route POST /form index.ts:7:52 index.ts:5:13 1 +route GET /form/legacy index.ts:7:52 index.ts:5:13 2 +route POST /form/legacy index.ts:7:52 index.ts:5:13 3 +route PURGE /cache/:key index.ts:8:32 index.ts:5:13 4 +route PUT /lower index.ts:9:27 index.ts:5:13 5 +regby index.ts:1:1 index.ts:7:52 GET /form index.ts:5:13 +regby index.ts:1:1 index.ts:7:52 GET /form/legacy index.ts:5:13 +regby index.ts:1:1 index.ts:7:52 POST /form index.ts:5:13 +regby index.ts:1:1 index.ts:7:52 POST /form/legacy index.ts:5:13 +regby index.ts:1:1 index.ts:8:32 PURGE /cache/:key index.ts:5:13 +regby index.ts:1:1 index.ts:9:27 PUT /lower index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/routing/partial-route-scope.entries b/graph/test/typescript/hono/expected/routing/partial-route-scope.entries new file mode 100644 index 000000000..8ef8b5b19 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/partial-route-scope.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: the /list route under an unknown prefix is partial; the /v1/* use against it is partial_route_scope +# census: use_scoped, route_mount_nonliteral (SPEC 3.3 [iter2] partial_route_scope) +entry index.ts:6:38 http +entry index.ts:9:20 http diff --git a/graph/test/typescript/hono/expected/routing/partial-route-scope.tsv b/graph/test/typescript/hono/expected/routing/partial-route-scope.tsv new file mode 100644 index 000000000..e1d288d34 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/partial-route-scope.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: the /list route under an unknown prefix is partial; the /v1/* use against it is partial_route_scope +# census: use_scoped, route_mount_nonliteral (SPEC 3.3 [iter2] partial_route_scope) +partial index.ts:9:20 GET dynamic_mount_prefix +unknown index.ts:14:1 dynamic_mount_prefix index.ts:9:20 +unknown index.ts:14:1 partial_route_scope index.ts:6:38 diff --git a/graph/test/typescript/hono/expected/routing/path-shapes.entries b/graph/test/typescript/hono/expected/routing/path-shapes.entries new file mode 100644 index 000000000..309ea615e --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/path-shapes.entries @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: every path kept as written +# census: path_param, path_optional_param, path_regex_param, path_wildcard +entry index.ts:10:21 http +entry index.ts:11:27 http +entry index.ts:12:28 http +entry index.ts:13:23 http +entry index.ts:14:23 http +entry index.ts:7:23 http +entry index.ts:8:26 http +entry index.ts:9:31 http diff --git a/graph/test/typescript/hono/expected/routing/path-shapes.tsv b/graph/test/typescript/hono/expected/routing/path-shapes.tsv new file mode 100644 index 000000000..02835770d --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/path-shapes.tsv @@ -0,0 +1,19 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: every path kept as written +# census: path_param, path_optional_param, path_regex_param, path_wildcard +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /users/:id index.ts:7:23 index.ts:5:13 0 +route GET /posts/:year? index.ts:8:26 index.ts:5:13 1 +route GET /items/:id{[0-9]+} index.ts:9:31 index.ts:5:13 2 +route GET /files/* index.ts:10:21 index.ts:5:13 3 +route GET /img/:slug.png index.ts:11:27 index.ts:5:13 4 +route GET /v1/keys.getKey index.ts:12:28 index.ts:5:13 5 +route GET /trailing/ index.ts:13:23 index.ts:5:13 6 +route GET /a/:x/b/:y index.ts:14:23 index.ts:5:13 7 +regby index.ts:1:1 index.ts:10:21 GET /files/* index.ts:5:13 +regby index.ts:1:1 index.ts:11:27 GET /img/:slug.png index.ts:5:13 +regby index.ts:1:1 index.ts:12:28 GET /v1/keys.getKey index.ts:5:13 +regby index.ts:1:1 index.ts:13:23 GET /trailing/ index.ts:5:13 +regby index.ts:1:1 index.ts:14:23 GET /a/:x/b/:y index.ts:5:13 +regby index.ts:1:1 index.ts:7:23 GET /users/:id index.ts:5:13 +regby index.ts:1:1 index.ts:8:26 GET /posts/:year? index.ts:5:13 +regby index.ts:1:1 index.ts:9:31 GET /items/:id{[0-9]+} index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/routing/pathless-chain.entries b/graph/test/typescript/hono/expected/routing/pathless-chain.entries new file mode 100644 index 000000000..cf1a21b1c --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/pathless-chain.entries @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: first path-less get is /, later path-less verbs continue the chain's path +# census: verb_chained +entry index.ts:10:17 http +entry index.ts:11:9 http +entry index.ts:12:8 http +entry index.ts:14:25 http +entry index.ts:14:54 http +entry index.ts:7:9 http diff --git a/graph/test/typescript/hono/expected/routing/pathless-chain.tsv b/graph/test/typescript/hono/expected/routing/pathless-chain.tsv new file mode 100644 index 000000000..a35d04cff --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/pathless-chain.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: first path-less get is /, later path-less verbs continue the chain's path +# census: verb_chained +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET / index.ts:7:9 index.ts:5:13 0 +route GET /book index.ts:10:17 index.ts:5:13 1 +route POST /book index.ts:11:9 index.ts:5:13 2 +route PUT /book index.ts:12:8 index.ts:5:13 3 +route DELETE /book/:id index.ts:14:25 index.ts:5:13 4 +route PATCH /book/:id index.ts:14:54 index.ts:5:13 5 +regby index.ts:1:1 index.ts:10:17 GET /book index.ts:5:13 +regby index.ts:1:1 index.ts:11:9 POST /book index.ts:5:13 +regby index.ts:1:1 index.ts:12:8 PUT /book index.ts:5:13 +regby index.ts:1:1 index.ts:14:25 DELETE /book/:id index.ts:5:13 +regby index.ts:1:1 index.ts:14:54 PATCH /book/:id index.ts:5:13 +regby index.ts:1:1 index.ts:7:9 GET / index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/routing/scope-table.entries b/graph/test/typescript/hono/expected/routing/scope-table.entries new file mode 100644 index 000000000..514904b0c --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/scope-table.entries @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: QD09: /x/* reaches /x, /x/, /x/a not /xy; /x* reaches all four; /a/*/c reaches only /a/b/c +# census: use_scoped, path_wildcard (SPEC 3.3 [iter2] QD09 table, settled against the pinned hono routers) +entry index.ts:15:15 http +entry index.ts:16:16 http +entry index.ts:17:17 http +entry index.ts:18:16 http +entry index.ts:19:19 http +entry index.ts:20:17 http +entry index.ts:21:21 http +entry index.ts:6:38 http +entry index.ts:7:37 http +entry index.ts:8:36 http diff --git a/graph/test/typescript/hono/expected/routing/scope-table.tsv b/graph/test/typescript/hono/expected/routing/scope-table.tsv new file mode 100644 index 000000000..d917ce6ae --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/scope-table.tsv @@ -0,0 +1,24 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: QD09: /x/* reaches /x, /x/, /x/a not /xy; /x* reaches all four; /a/*/c reaches only /a/b/c +# census: use_scoped, path_wildcard (SPEC 3.3 [iter2] QD09 table, settled against the pinned hono routers) +route GET /x index.ts:15:15 index.ts:10:13 3 +route GET /x/ index.ts:16:16 index.ts:10:13 4 +route GET /x/a index.ts:17:17 index.ts:10:13 5 +route GET /xy index.ts:18:16 index.ts:10:13 6 +route GET /a/b/c index.ts:19:19 index.ts:10:13 7 +route GET /a/c index.ts:20:17 index.ts:10:13 8 +route GET /a/b/d/c index.ts:21:21 index.ts:10:13 9 +mw index.ts:6:38 index.ts:15:15 GET /x 0 index.ts:10:13 +mw index.ts:7:37 index.ts:15:15 GET /x 1 index.ts:10:13 +mw index.ts:6:38 index.ts:16:16 GET /x/ 0 index.ts:10:13 +mw index.ts:7:37 index.ts:16:16 GET /x/ 1 index.ts:10:13 +mw index.ts:6:38 index.ts:17:17 GET /x/a 0 index.ts:10:13 +mw index.ts:7:37 index.ts:17:17 GET /x/a 1 index.ts:10:13 +mw index.ts:7:37 index.ts:18:16 GET /xy 0 index.ts:10:13 +mw index.ts:8:36 index.ts:19:19 GET /a/b/c 0 index.ts:10:13 +regby index.ts:1:1 index.ts:15:15 GET /x index.ts:10:13 +regby index.ts:1:1 index.ts:16:16 GET /x/ index.ts:10:13 +regby index.ts:1:1 index.ts:17:17 GET /x/a index.ts:10:13 +regby index.ts:1:1 index.ts:18:16 GET /xy index.ts:10:13 +regby index.ts:1:1 index.ts:19:19 GET /a/b/c index.ts:10:13 +regby index.ts:1:1 index.ts:20:17 GET /a/c index.ts:10:13 +regby index.ts:1:1 index.ts:21:21 GET /a/b/d/c index.ts:10:13 diff --git a/graph/test/typescript/hono/expected/routing/strict-false.entries b/graph/test/typescript/hono/expected/routing/strict-false.entries new file mode 100644 index 000000000..0a163f5ce --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/strict-false.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: registered paths unchanged by strict:false +# census: strict_false, new_hono_options +entry index.ts:7:20 http +entry index.ts:8:21 http diff --git a/graph/test/typescript/hono/expected/routing/strict-false.tsv b/graph/test/typescript/hono/expected/routing/strict-false.tsv new file mode 100644 index 000000000..b55df1fcc --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/strict-false.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: registered paths unchanged by strict:false +# census: strict_false, new_hono_options +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /about/ index.ts:7:20 index.ts:5:13 0 +route GET /contact index.ts:8:21 index.ts:5:13 1 +regby index.ts:1:1 index.ts:7:20 GET /about/ index.ts:5:13 +regby index.ts:1:1 index.ts:8:21 GET /contact index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/routing/websocket-project-module.entries b/graph/test/typescript/hono/expected/routing/websocket-project-module.entries new file mode 100644 index 000000000..e5299dc53 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/websocket-project-module.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O10 residual: upgradeWebSocket from a project module (relative and path alias) is a library-call handler; chat middleware precedes it +# census: verb_multi_handler (oracle gap O10 residual; SPEC R5) +entry chat.ts:6:3 http diff --git a/graph/test/typescript/hono/expected/routing/websocket-project-module.tsv b/graph/test/typescript/hono/expected/routing/websocket-project-module.tsv new file mode 100644 index 000000000..7f175a40b --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/websocket-project-module.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O10 residual: upgradeWebSocket from a project module (relative and path alias) is a library-call handler; chat middleware precedes it +# census: verb_multi_handler (oracle gap O10 residual; SPEC R5) +route GET /live lib:hono/bun.createBunWebSocket().upgradeWebSocket@index.ts:9:18 index.ts:8:13 0 +route GET /chat/ws lib:hono/bun.createBunWebSocket().upgradeWebSocket@chat.ts:10:3 index.ts:8:13 2 +mw chat.ts:6:3 lib:hono/bun.createBunWebSocket().upgradeWebSocket@chat.ts:10:3 GET /chat/ws 0 index.ts:8:13 +submount /chat index.ts:10:5 +regby chat.ts:1:1 lib:hono/bun.createBunWebSocket().upgradeWebSocket@chat.ts:10:3 GET /chat/ws index.ts:8:13 +regby index.ts:1:1 lib:hono/bun.createBunWebSocket().upgradeWebSocket@index.ts:9:18 GET /live index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/routing/websocket-sse.entries b/graph/test/typescript/hono/expected/routing/websocket-sse.entries new file mode 100644 index 000000000..7d6a5f030 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/websocket-sse.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O10/R5: upgradeWebSocket(...) is a library-call handler keyed at the call; the streamSSE route has a project handler +# census: verb_literal_path (oracle gap O10; SPEC R5 library-call handlers) +entry index.ts:17:17 http diff --git a/graph/test/typescript/hono/expected/routing/websocket-sse.tsv b/graph/test/typescript/hono/expected/routing/websocket-sse.tsv new file mode 100644 index 000000000..47cace524 --- /dev/null +++ b/graph/test/typescript/hono/expected/routing/websocket-sse.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O10/R5: upgradeWebSocket(...) is a library-call handler keyed at the call; the streamSSE route has a project handler +# census: verb_literal_path (oracle gap O10; SPEC R5 library-call handlers) +route GET /ws lib:hono/cloudflare-workers.upgradeWebSocket@index.ts:11:3 index.ts:8:13 0 +route GET /sse index.ts:17:17 index.ts:8:13 1 +regby index.ts:1:1 index.ts:17:17 GET /sse index.ts:8:13 +regby index.ts:1:1 lib:hono/cloudflare-workers.upgradeWebSocket@index.ts:11:3 GET /ws index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/rpc/base-unknown-implausible.entries b/graph/test/typescript/hono/expected/rpc/base-unknown-implausible.entries new file mode 100644 index 000000000..65d62ee02 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/base-unknown-implausible.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O21: an unknown-base request joins /api/users as base_unknown; never a test-only app from non-test code, never the catch-all, never import.meta.url, never a path with no literal segment +# census: request_fetch, app_request_test (SPEC R14; oracle rule O21) +entry server.test.ts:7:28 http +entry server.ts:10:27 http +entry server.ts:11:14 http +entry server.ts:9:23 http diff --git a/graph/test/typescript/hono/expected/rpc/base-unknown-implausible.tsv b/graph/test/typescript/hono/expected/rpc/base-unknown-implausible.tsv new file mode 100644 index 000000000..2ade2ecfa --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/base-unknown-implausible.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O21: an unknown-base request joins /api/users as base_unknown; never a test-only app from non-test code, never the catch-all, never import.meta.url, never a path with no literal segment +# census: request_fetch, app_request_test (SPEC R14; oracle rule O21) +route GET /__test/probe server.test.ts:7:28 server.test.ts:6:15 0 +route GET /api/users server.ts:9:23 server.ts:8:13 0 +route GET /api/users/:id server.ts:10:27 server.ts:8:13 1 +route GET /* server.ts:11:14 server.ts:8:13 2 +request client.ts:1:1 GET /api/users server.ts:9:23 base_unknown +request server.test.ts:5:42 GET /__test/probe server.test.ts:7:28 +regby server.test.ts:5:42 server.test.ts:7:28 GET /__test/probe server.test.ts:6:15 +regby server.ts:1:1 server.ts:10:27 GET /api/users/:id server.ts:8:13 +regby server.ts:1:1 server.ts:11:14 GET /* server.ts:8:13 +regby server.ts:1:1 server.ts:9:23 GET /api/users server.ts:8:13 diff --git a/graph/test/typescript/hono/expected/rpc/fetch-unknown-base.entries b/graph/test/typescript/hono/expected/rpc/fetch-unknown-base.entries new file mode 100644 index 000000000..d89e692fb --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/fetch-unknown-base.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R14/O18: parameter, URL-base and env-or-localhost bases join on full paths as base_unknown; a literal external host never joins +# census: request_fetch (SPEC R14; oracle gap O18) +entry api.ts:7:29 http +entry api.ts:8:26 http +entry api.ts:9:20 http diff --git a/graph/test/typescript/hono/expected/rpc/fetch-unknown-base.tsv b/graph/test/typescript/hono/expected/rpc/fetch-unknown-base.tsv new file mode 100644 index 000000000..6f9fdb1fb --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/fetch-unknown-base.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R14/O18: parameter, URL-base and env-or-localhost bases join on full paths as base_unknown; a literal external host never joins +# census: request_fetch (SPEC R14; oracle gap O18) +route POST /api/oauth/register api.ts:7:29 api.ts:6:13 0 +route GET /api/projects/:id api.ts:8:26 api.ts:6:13 1 +route GET /api/health api.ts:9:20 api.ts:6:13 2 +request cli.ts:11:1 GET /api/health api.ts:9:20 base_unknown +request cli.ts:3:1 POST /api/oauth/register api.ts:7:29 base_unknown +request cli.ts:7:1 GET /api/projects/${} api.ts:8:26 base_unknown +regby api.ts:1:1 api.ts:7:29 POST /api/oauth/register api.ts:6:13 +regby api.ts:1:1 api.ts:8:26 GET /api/projects/:id api.ts:6:13 +regby api.ts:1:1 api.ts:9:20 GET /api/health api.ts:6:13 diff --git a/graph/test/typescript/hono/expected/rpc/fetch-verb-parameter.entries b/graph/test/typescript/hono/expected/rpc/fetch-verb-parameter.entries new file mode 100644 index 000000000..02237e60d --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/fetch-verb-parameter.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R17/O25/hono-6-03: an undecidable method (an init the helper is handed, a parameter verb, a method set in a branch, a replayed init) joins every route on the path with method_unknown; base_unknown wins when the base is unknown too; a literal DELETE and an omitted init (GET) join one verb +# census: request_fetch, app_request_test (SPEC R17; stress-6 oracle note O25; violation hono-6-03) +entry server.ts:10:31 http +entry server.ts:11:24 http +entry server.ts:12:25 http +entry server.ts:8:28 http +entry server.ts:9:28 http diff --git a/graph/test/typescript/hono/expected/rpc/fetch-verb-parameter.tsv b/graph/test/typescript/hono/expected/rpc/fetch-verb-parameter.tsv new file mode 100644 index 000000000..650aa15ce --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/fetch-verb-parameter.tsv @@ -0,0 +1,25 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: R17/O25/hono-6-03: an undecidable method (an init the helper is handed, a parameter verb, a method set in a branch, a replayed init) joins every route on the path with method_unknown; base_unknown wins when the base is unknown too; a literal DELETE and an omitted init (GET) join one verb +# census: request_fetch, app_request_test (SPEC R17; stress-6 oracle note O25; violation hono-6-03) +route GET /api/skills/:id server.ts:8:28 server.ts:7:13 0 +route PUT /api/skills/:id server.ts:9:28 server.ts:7:13 1 +route DELETE /api/skills/:id server.ts:10:31 server.ts:7:13 2 +route GET /_discovery server.ts:11:24 server.ts:7:13 3 +route POST /_discovery server.ts:12:25 server.ts:7:13 4 +request discovery.ts:2:1 * /_discovery server.ts:11:24 method_unknown +request discovery.ts:2:1 * /_discovery server.ts:12:25 method_unknown +request server.test.ts:5:1 * /api/skills/2 server.ts:10:31 method_unknown +request server.test.ts:5:1 * /api/skills/2 server.ts:8:28 method_unknown +request server.test.ts:5:1 * /api/skills/2 server.ts:9:28 method_unknown +request server.test.ts:5:1 GET /api/skills/1 server.ts:8:28 +request skills-client.ts:12:1 DELETE /api/skills/${} server.ts:10:31 +request skills-client.ts:2:1 * /api/skills${} server.ts:10:31 base_unknown +request skills-client.ts:2:1 * /api/skills${} server.ts:8:28 base_unknown +request skills-client.ts:2:1 * /api/skills${} server.ts:9:28 base_unknown +request skills-client.ts:7:1 * /api/skills/${} server.ts:10:31 method_unknown +request skills-client.ts:7:1 * /api/skills/${} server.ts:8:28 method_unknown +request skills-client.ts:7:1 * /api/skills/${} server.ts:9:28 method_unknown +regby server.ts:1:1 server.ts:10:31 DELETE /api/skills/:id server.ts:7:13 +regby server.ts:1:1 server.ts:11:24 GET /_discovery server.ts:7:13 +regby server.ts:1:1 server.ts:12:25 POST /_discovery server.ts:7:13 +regby server.ts:1:1 server.ts:8:28 GET /api/skills/:id server.ts:7:13 +regby server.ts:1:1 server.ts:9:28 PUT /api/skills/:id server.ts:7:13 diff --git a/graph/test/typescript/hono/expected/rpc/hc-basic.entries b/graph/test/typescript/hono/expected/rpc/hc-basic.entries new file mode 100644 index 000000000..3ab3f9232 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/hc-basic.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: four client functions reach their routes; index is /; $url makes no edge +# census: hc_client, hc_dollar_call, hc_element_param, apptype_typeof +entry server.ts:4:13 http +entry server.ts:5:16 http +entry server.ts:6:14 http +entry server.ts:9:52 http diff --git a/graph/test/typescript/hono/expected/rpc/hc-basic.tsv b/graph/test/typescript/hono/expected/rpc/hc-basic.tsv new file mode 100644 index 000000000..acbf03e8c --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/hc-basic.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: four client functions reach their routes; index is /; $url makes no edge +# census: hc_client, hc_dollar_call, hc_element_param, apptype_typeof +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /users server.ts:4:13 server.ts:8:13 0 +route GET /users/:id server.ts:5:16 server.ts:8:13 1 +route POST /users server.ts:6:14 server.ts:8:13 2 +route GET / server.ts:9:52 server.ts:8:13 3 +rpc client.ts:11:1 GET /users/:id server.ts:5:16 +rpc client.ts:14:1 POST /users server.ts:6:14 +rpc client.ts:17:1 GET / server.ts:9:52 +rpc client.ts:8:1 GET /users server.ts:4:13 +submount /users server.ts:9:20 +regby server.ts:1:1 server.ts:4:13 GET /users server.ts:8:13 +regby server.ts:1:1 server.ts:5:16 GET /users/:id server.ts:8:13 +regby server.ts:1:1 server.ts:6:14 POST /users server.ts:8:13 +regby server.ts:1:1 server.ts:9:52 GET / server.ts:8:13 diff --git a/graph/test/typescript/hono/expected/rpc/local-fetch-full-path.entries b/graph/test/typescript/hono/expected/rpc/local-fetch-full-path.entries new file mode 100644 index 000000000..5d62c5fd3 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/local-fetch-full-path.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-02: localhost fetches match composed full paths: /ready only to the root handler, /api/version to the sub-app, /version to nothing +# census: route_mount_root (violation hono-3-02) +entry api.ts:5:18 http +entry api.ts:6:20 http +entry main.ts:8:20 http diff --git a/graph/test/typescript/hono/expected/rpc/local-fetch-full-path.tsv b/graph/test/typescript/hono/expected/rpc/local-fetch-full-path.tsv new file mode 100644 index 000000000..2f2a8b712 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/local-fetch-full-path.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: hono-3-02: localhost fetches match composed full paths: /ready only to the root handler, /api/version to the sub-app, /version to nothing +# census: route_mount_root (violation hono-3-02) +route GET /ready main.ts:8:20 main.ts:7:14 0 +route GET /api/ready api.ts:5:18 main.ts:7:14 1 +route GET /api/version api.ts:6:20 main.ts:7:14 2 +request probe.ts:3:1 GET /ready main.ts:8:20 +request probe.ts:9:1 GET /api/version api.ts:6:20 +submount / main.ts:9:6 +regby api.ts:1:1 api.ts:5:18 GET /api/ready main.ts:7:14 +regby api.ts:1:1 api.ts:6:20 GET /api/version main.ts:7:14 +regby main.ts:1:1 main.ts:8:20 GET /ready main.ts:7:14 diff --git a/graph/test/typescript/hono/expected/rpc/mixed-segment-hole.entries b/graph/test/typescript/hono/expected/rpc/mixed-segment-hole.entries new file mode 100644 index 000000000..6e008b6f8 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/mixed-segment-hole.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m39/hono-5-07: /api/skills${path} reaches /api/skills/:id (base_unknown and local), never /api/agents +# census: request_fetch (stress-5 mutation m39 shape; violation hono-5-07) +entry index.ts:7:24 http +entry index.ts:8:28 http diff --git a/graph/test/typescript/hono/expected/rpc/mixed-segment-hole.tsv b/graph/test/typescript/hono/expected/rpc/mixed-segment-hole.tsv new file mode 100644 index 000000000..9d44b442d --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/mixed-segment-hole.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m39/hono-5-07: /api/skills${path} reaches /api/skills/:id (base_unknown and local), never /api/agents +# census: request_fetch (stress-5 mutation m39 shape; violation hono-5-07) +route GET /api/agents index.ts:7:24 index.ts:6:13 0 +route GET /api/skills/:id index.ts:8:28 index.ts:6:13 1 +request index.ts:11:1 GET /api/skills${} index.ts:8:28 base_unknown +request index.ts:14:1 GET /api/skills${} index.ts:8:28 +regby index.ts:1:1 index.ts:7:24 GET /api/agents index.ts:6:13 +regby index.ts:1:1 index.ts:8:28 GET /api/skills/:id index.ts:6:13 diff --git a/graph/test/typescript/hono/expected/rpc/monorepo-client.entries b/graph/test/typescript/hono/expected/rpc/monorepo-client.entries new file mode 100644 index 000000000..4e501e252 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/monorepo-client.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: client through a wrapper function and inline hc reaches the basePath'd routes +# census: hc_client, hc_dollar_call, hc_element_param +entry server/app.ts:4:19 http +entry server/app.ts:5:36 http diff --git a/graph/test/typescript/hono/expected/rpc/monorepo-client.tsv b/graph/test/typescript/hono/expected/rpc/monorepo-client.tsv new file mode 100644 index 000000000..3beeceb6c --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/monorepo-client.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: client through a wrapper function and inline hc reaches the basePath'd routes +# census: hc_client, hc_dollar_call, hc_element_param +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /api/orders server/app.ts:4:19 server/app.ts:3:20 0 +route POST /api/orders/:orderId/cancel server/app.ts:5:36 server/app.ts:3:20 1 +rpc web/orders.ts:10:1 POST /api/orders/:orderId/cancel server/app.ts:5:36 +rpc web/orders.ts:14:1 GET /api/orders server/app.ts:4:19 +regby server/app.ts:1:1 server/app.ts:4:19 GET /api/orders server/app.ts:3:20 +regby server/app.ts:1:1 server/app.ts:5:36 POST /api/orders/:orderId/cancel server/app.ts:3:20 diff --git a/graph/test/typescript/hono/expected/rpc/query-string.entries b/graph/test/typescript/hono/expected/rpc/query-string.entries new file mode 100644 index 000000000..f49c524d2 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/query-string.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-05/R9: query string and fragment stripped: both requests reach /sessions/:id/file +# census: app_request_test (violation hono-2-05, SPEC R9) +entry app.ts:4:31 http +entry app.ts:5:36 http diff --git a/graph/test/typescript/hono/expected/rpc/query-string.tsv b/graph/test/typescript/hono/expected/rpc/query-string.tsv new file mode 100644 index 000000000..132f51a4e --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/query-string.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-05/R9: query string and fragment stripped: both requests reach /sessions/:id/file +# census: app_request_test (violation hono-2-05, SPEC R9) +route GET /sessions/:id/file app.ts:4:31 app.ts:3:20 0 +route GET /sessions/:id/objective app.ts:5:36 app.ts:3:20 1 +request app.test.ts:5:1 GET /sessions/S1/file app.ts:4:31 +request app.test.ts:8:1 GET /sessions/S1/file app.ts:4:31 +regby app.ts:1:1 app.ts:4:31 GET /sessions/:id/file app.ts:3:20 +regby app.ts:1:1 app.ts:5:36 GET /sessions/:id/objective app.ts:3:20 diff --git a/graph/test/typescript/hono/expected/rpc/reassigned-let.entries b/graph/test/typescript/hono/expected/rpc/reassigned-let.entries new file mode 100644 index 000000000..ef4fe7e70 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/reassigned-let.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-03/R7: postId is reassigned, so it is a hole: read -> GET /posts/:id, update -> PUT /posts/:id; the const fixed folds to /posts/p1 +# census: app_request_test (violation hono-2-03, SPEC R7; oracle gap O13) +entry api.ts:4:19 http +entry api.ts:5:23 http +entry api.ts:6:23 http diff --git a/graph/test/typescript/hono/expected/rpc/reassigned-let.tsv b/graph/test/typescript/hono/expected/rpc/reassigned-let.tsv new file mode 100644 index 000000000..769efe420 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/reassigned-let.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-03/R7: postId is reassigned, so it is a hole: read -> GET /posts/:id, update -> PUT /posts/:id; the const fixed folds to /posts/p1 +# census: app_request_test (violation hono-2-03, SPEC R7; oracle gap O13) +route GET /posts api.ts:4:19 api.ts:3:13 0 +route GET /posts/:id api.ts:5:23 api.ts:3:13 1 +route PUT /posts/:id api.ts:6:23 api.ts:3:13 2 +request api.test.ts:13:14 GET /posts/${} api.ts:5:23 +request api.test.ts:16:16 PUT /posts/${} api.ts:6:23 +request api.test.ts:19:15 GET /posts/p1 api.ts:5:23 +regby api.ts:1:1 api.ts:4:19 GET /posts api.ts:3:13 +regby api.ts:1:1 api.ts:5:23 GET /posts/:id api.ts:3:13 +regby api.ts:1:1 api.ts:6:23 PUT /posts/:id api.ts:3:13 diff --git a/graph/test/typescript/hono/expected/rpc/request-on-mounted-subapp.entries b/graph/test/typescript/hono/expected/rpc/request-on-mounted-subapp.entries new file mode 100644 index 000000000..521492dbc --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/request-on-mounted-subapp.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-05: worker.request('/posts') and hc.todos.$get match relative to the sub-app they root at +# census: app_request_test, hc_client (violation hono-1-05) +entry api.ts:4:19 http +entry api.ts:5:47 http diff --git a/graph/test/typescript/hono/expected/rpc/request-on-mounted-subapp.tsv b/graph/test/typescript/hono/expected/rpc/request-on-mounted-subapp.tsv new file mode 100644 index 000000000..932109e9e --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/request-on-mounted-subapp.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-1-05: worker.request('/posts') and hc.todos.$get match relative to the sub-app they root at +# census: app_request_test, hc_client (violation hono-1-05) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /api/posts api.ts:4:19 index.ts:7:13 0 +route GET /api/todos api.ts:5:47 index.ts:7:13 1 +rpc api.test.ts:8:1 GET /todos api.ts:5:47 +request api.test.ts:4:1 GET /posts api.ts:4:19 +submount / api.ts:6:5 +submount /api index.ts:8:5 +regby api.ts:1:1 api.ts:4:19 GET /api/posts index.ts:7:13 +regby api.ts:1:1 api.ts:5:47 GET /api/todos index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/rpc/single-label-host.entries b/graph/test/typescript/hono/expected/rpc/single-label-host.entries new file mode 100644 index 000000000..28d9fe87f --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/single-label-host.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m37/hono-5-08/R14a: billing:9000 and an unknown base join as base_unknown; the dotted external host never joins +# census: request_fetch (stress-5 mutation m37 shape; violation hono-5-08, SPEC R14a) +entry index.ts:7:26 http +entry index.ts:8:24 http diff --git a/graph/test/typescript/hono/expected/rpc/single-label-host.tsv b/graph/test/typescript/hono/expected/rpc/single-label-host.tsv new file mode 100644 index 000000000..a7d20c51c --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/single-label-host.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: m37/hono-5-08/R14a: billing:9000 and an unknown base join as base_unknown; the dotted external host never joins +# census: request_fetch (stress-5 mutation m37 shape; violation hono-5-08, SPEC R14a) +route GET /api/invoices index.ts:7:26 index.ts:6:13 0 +route GET /api/health index.ts:8:24 index.ts:6:13 1 +request index.ts:11:1 GET /api/invoices index.ts:7:26 base_unknown +request index.ts:17:1 GET /api/health index.ts:8:24 base_unknown +regby index.ts:1:1 index.ts:7:26 GET /api/invoices index.ts:6:13 +regby index.ts:1:1 index.ts:8:24 GET /api/health index.ts:6:13 diff --git a/graph/test/typescript/hono/expected/rpc/template-holes.entries b/graph/test/typescript/hono/expected/rpc/template-holes.entries new file mode 100644 index 000000000..8aca832a5 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/template-holes.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a hole is one segment: request and client both reach /posts/latest and /posts/:id by shape; /comments/${} is dynamic_rpc_path; the absolute URL uses its pathname +# census: app_request_test, hc_element_param (SPEC 3.5 [iter2] template holes and absolute URLs) +entry app.ts:4:18 http +entry app.ts:5:25 http +entry app.ts:6:22 http diff --git a/graph/test/typescript/hono/expected/rpc/template-holes.tsv b/graph/test/typescript/hono/expected/rpc/template-holes.tsv new file mode 100644 index 000000000..807e50666 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/template-holes.tsv @@ -0,0 +1,14 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: a hole is one segment: request and client both reach /posts/latest and /posts/:id by shape; /comments/${} is dynamic_rpc_path; the absolute URL uses its pathname +# census: app_request_test, hc_element_param (SPEC 3.5 [iter2] template holes and absolute URLs) +route GET /posts app.ts:4:18 app.ts:3:20 0 +route GET /posts/latest app.ts:5:25 app.ts:3:20 1 +route GET /posts/:id app.ts:6:22 app.ts:3:20 2 +rpc app.test.ts:10:1 GET /posts/${} app.ts:5:25 +rpc app.test.ts:10:1 GET /posts/${} app.ts:6:22 +request app.test.ts:16:1 GET /posts app.ts:4:18 +request app.test.ts:7:1 GET /posts/${} app.ts:5:25 +request app.test.ts:7:1 GET /posts/${} app.ts:6:22 +regby app.ts:1:1 app.ts:4:18 GET /posts app.ts:3:20 +regby app.ts:1:1 app.ts:5:25 GET /posts/latest app.ts:3:20 +regby app.ts:1:1 app.ts:6:22 GET /posts/:id app.ts:3:20 +unknown app.test.ts:14:10 dynamic_rpc_path app.test.ts:13:1 diff --git a/graph/test/typescript/hono/expected/rpc/workspace-dist-exports.entries b/graph/test/typescript/hono/expected/rpc/workspace-dist-exports.entries new file mode 100644 index 000000000..a71274137 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/workspace-dist-exports.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O3b: exports to an unbuilt dist file resolve to the package source; the dashboard is mounted at /admin, no root of its own +# census: route_mount_literal (oracle gap O3b) +entry apps/api/src/index.ts:8:20 http +entry packages/routes/src/adapters/hono.ts:5:15 http +entry packages/routes/src/adapters/hono.ts:6:27 http diff --git a/graph/test/typescript/hono/expected/rpc/workspace-dist-exports.tsv b/graph/test/typescript/hono/expected/rpc/workspace-dist-exports.tsv new file mode 100644 index 000000000..6a89e82c0 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/workspace-dist-exports.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: O3b: exports to an unbuilt dist file resolve to the package source; the dashboard is mounted at /admin, no root of its own +# census: route_mount_literal (oracle gap O3b) +route GET /health apps/api/src/index.ts:8:20 apps/api/src/index.ts:7:13 0 +route GET /admin packages/routes/src/adapters/hono.ts:5:15 apps/api/src/index.ts:7:13 1 +route GET /admin/api/activity packages/routes/src/adapters/hono.ts:6:27 apps/api/src/index.ts:7:13 2 +submount /admin apps/api/src/index.ts:9:5 +regby apps/api/src/index.ts:1:1 apps/api/src/index.ts:8:20 GET /health apps/api/src/index.ts:7:13 +regby packages/routes/src/adapters/hono.ts:3:1 packages/routes/src/adapters/hono.ts:5:15 GET /admin apps/api/src/index.ts:7:13 +regby packages/routes/src/adapters/hono.ts:3:1 packages/routes/src/adapters/hono.ts:6:27 GET /admin/api/activity apps/api/src/index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/rpc/workspace-exports.entries b/graph/test/typescript/hono/expected/rpc/workspace-exports.entries new file mode 100644 index 000000000..c6553d39b --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/workspace-exports.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-08/O3: @scope/client/v2 resolves through package.json exports; the client reaches GET /sessions +# census: hc_client (violation hono-2-08; oracle gap O3: a workspace package subpath through package.json exports) +entry packages/server/src/app.ts:4:37 http diff --git a/graph/test/typescript/hono/expected/rpc/workspace-exports.tsv b/graph/test/typescript/hono/expected/rpc/workspace-exports.tsv new file mode 100644 index 000000000..306a9d287 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/workspace-exports.tsv @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: hono-2-08/O3: @scope/client/v2 resolves through package.json exports; the client reaches GET /sessions +# census: hc_client (violation hono-2-08; oracle gap O3: a workspace package subpath through package.json exports) +route GET /sessions packages/server/src/app.ts:4:37 packages/server/src/app.ts:3:13 0 +rpc packages/client/lib/client.ts:4:1 GET /sessions packages/server/src/app.ts:4:37 +regby packages/server/src/app.ts:1:1 packages/server/src/app.ts:4:37 GET /sessions packages/server/src/app.ts:3:13 diff --git a/graph/test/typescript/hono/expected/rpc/workspace-package.entries b/graph/test/typescript/hono/expected/rpc/workspace-package.entries new file mode 100644 index 000000000..0a77739d5 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/workspace-package.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O3: @acme/server and @acme/admin resolve as workspace packages: the /admin mount and both typed client calls cross them +# census: hc_client, route_mount_literal (oracle gap O3: workspace package imports in a monorepo) +entry packages/admin/routes.ts:3:47 http +entry packages/server/src/index.ts:5:33 http diff --git a/graph/test/typescript/hono/expected/rpc/workspace-package.tsv b/graph/test/typescript/hono/expected/rpc/workspace-package.tsv new file mode 100644 index 000000000..2a41f326d --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/workspace-package.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: O3: @acme/server and @acme/admin resolve as workspace packages: the /admin mount and both typed client calls cross them +# census: hc_client, route_mount_literal (oracle gap O3: workspace package imports in a monorepo) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /ping packages/server/src/index.ts:5:33 packages/server/src/index.ts:4:13 0 +route GET /admin/users packages/admin/routes.ts:3:47 packages/server/src/index.ts:4:13 1 +rpc packages/web/client.ts:11:1 GET /admin/users packages/admin/routes.ts:3:47 +rpc packages/web/client.ts:8:1 GET /ping packages/server/src/index.ts:5:33 +submount /admin packages/server/src/index.ts:5:56 +regby packages/admin/routes.ts:1:1 packages/admin/routes.ts:3:47 GET /admin/users packages/server/src/index.ts:4:13 +regby packages/server/src/index.ts:1:1 packages/server/src/index.ts:5:33 GET /ping packages/server/src/index.ts:4:13 diff --git a/graph/test/typescript/hono/expected/scale/deep-mount.entries b/graph/test/typescript/hono/expected/scale/deep-mount.entries new file mode 100644 index 000000000..beb63fb00 --- /dev/null +++ b/graph/test/typescript/hono/expected/scale/deep-mount.entries @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: ten levels, /here at each depth /d1/.../d9; runtime agrees +# census: route_mount_literal (scale: 10-deep mounting; generated by tools/gen-scale.mjs) +entry index.ts:11:17 http +entry index.ts:13:17 http +entry index.ts:15:17 http +entry index.ts:17:17 http +entry index.ts:19:17 http +entry index.ts:21:17 http +entry index.ts:23:17 http +entry index.ts:5:17 http +entry index.ts:7:17 http +entry index.ts:9:17 http diff --git a/graph/test/typescript/hono/expected/scale/deep-mount.tsv b/graph/test/typescript/hono/expected/scale/deep-mount.tsv new file mode 100644 index 000000000..f35b8b687 --- /dev/null +++ b/graph/test/typescript/hono/expected/scale/deep-mount.tsv @@ -0,0 +1,32 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: ten levels, /here at each depth /d1/.../d9; runtime agrees +# census: route_mount_literal (scale: 10-deep mounting; generated by tools/gen-scale.mjs) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /here index.ts:5:17 index.ts:4:12 0 +route GET /d1/here index.ts:7:17 index.ts:4:12 1 +route GET /d1/d2/here index.ts:9:17 index.ts:4:12 2 +route GET /d1/d2/d3/here index.ts:11:17 index.ts:4:12 3 +route GET /d1/d2/d3/d4/here index.ts:13:17 index.ts:4:12 4 +route GET /d1/d2/d3/d4/d5/here index.ts:15:17 index.ts:4:12 5 +route GET /d1/d2/d3/d4/d5/d6/here index.ts:17:17 index.ts:4:12 6 +route GET /d1/d2/d3/d4/d5/d6/d7/here index.ts:19:17 index.ts:4:12 7 +route GET /d1/d2/d3/d4/d5/d6/d7/d8/here index.ts:21:17 index.ts:4:12 8 +route GET /d1/d2/d3/d4/d5/d6/d7/d8/d9/here index.ts:23:17 index.ts:4:12 9 +submount /d9 index.ts:24:4 +submount /d8 index.ts:25:4 +submount /d7 index.ts:26:4 +submount /d6 index.ts:27:4 +submount /d5 index.ts:28:4 +submount /d4 index.ts:29:4 +submount /d3 index.ts:30:4 +submount /d2 index.ts:31:4 +submount /d1 index.ts:32:4 +regby index.ts:1:1 index.ts:11:17 GET /d1/d2/d3/here index.ts:4:12 +regby index.ts:1:1 index.ts:13:17 GET /d1/d2/d3/d4/here index.ts:4:12 +regby index.ts:1:1 index.ts:15:17 GET /d1/d2/d3/d4/d5/here index.ts:4:12 +regby index.ts:1:1 index.ts:17:17 GET /d1/d2/d3/d4/d5/d6/here index.ts:4:12 +regby index.ts:1:1 index.ts:19:17 GET /d1/d2/d3/d4/d5/d6/d7/here index.ts:4:12 +regby index.ts:1:1 index.ts:21:17 GET /d1/d2/d3/d4/d5/d6/d7/d8/here index.ts:4:12 +regby index.ts:1:1 index.ts:23:17 GET /d1/d2/d3/d4/d5/d6/d7/d8/d9/here index.ts:4:12 +regby index.ts:1:1 index.ts:5:17 GET /here index.ts:4:12 +regby index.ts:1:1 index.ts:7:17 GET /d1/here index.ts:4:12 +regby index.ts:1:1 index.ts:9:17 GET /d1/d2/here index.ts:4:12 diff --git a/graph/test/typescript/hono/expected/scale/mw-200.entries b/graph/test/typescript/hono/expected/scale/mw-200.entries new file mode 100644 index 000000000..4eaa815d0 --- /dev/null +++ b/graph/test/typescript/hono/expected/scale/mw-200.entries @@ -0,0 +1,212 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: 200 middleware x 10 routes = 2,000 applications, positions 0..199 per route; runtime agrees +# census: use_global, await_next (scale: 200 middleware x 10 routes; generated by tools/gen-scale.mjs) +entry index.ts:100:32 http +entry index.ts:102:32 http +entry index.ts:104:32 http +entry index.ts:106:32 http +entry index.ts:108:32 http +entry index.ts:10:31 http +entry index.ts:110:32 http +entry index.ts:112:32 http +entry index.ts:114:32 http +entry index.ts:116:32 http +entry index.ts:118:32 http +entry index.ts:120:32 http +entry index.ts:122:32 http +entry index.ts:124:32 http +entry index.ts:126:32 http +entry index.ts:128:32 http +entry index.ts:12:31 http +entry index.ts:130:32 http +entry index.ts:132:32 http +entry index.ts:134:32 http +entry index.ts:136:32 http +entry index.ts:138:32 http +entry index.ts:140:32 http +entry index.ts:142:32 http +entry index.ts:144:32 http +entry index.ts:146:32 http +entry index.ts:148:32 http +entry index.ts:14:31 http +entry index.ts:150:32 http +entry index.ts:152:32 http +entry index.ts:154:32 http +entry index.ts:156:32 http +entry index.ts:158:32 http +entry index.ts:160:32 http +entry index.ts:162:32 http +entry index.ts:164:32 http +entry index.ts:166:32 http +entry index.ts:168:32 http +entry index.ts:16:31 http +entry index.ts:170:32 http +entry index.ts:172:32 http +entry index.ts:174:32 http +entry index.ts:176:32 http +entry index.ts:178:32 http +entry index.ts:180:32 http +entry index.ts:182:32 http +entry index.ts:184:32 http +entry index.ts:186:32 http +entry index.ts:188:32 http +entry index.ts:18:31 http +entry index.ts:190:32 http +entry index.ts:192:32 http +entry index.ts:194:32 http +entry index.ts:196:32 http +entry index.ts:198:32 http +entry index.ts:200:32 http +entry index.ts:202:32 http +entry index.ts:204:32 http +entry index.ts:206:33 http +entry index.ts:208:33 http +entry index.ts:20:31 http +entry index.ts:210:33 http +entry index.ts:212:33 http +entry index.ts:214:33 http +entry index.ts:216:33 http +entry index.ts:218:33 http +entry index.ts:220:33 http +entry index.ts:222:33 http +entry index.ts:224:33 http +entry index.ts:226:33 http +entry index.ts:228:33 http +entry index.ts:22:31 http +entry index.ts:230:33 http +entry index.ts:232:33 http +entry index.ts:234:33 http +entry index.ts:236:33 http +entry index.ts:238:33 http +entry index.ts:240:33 http +entry index.ts:242:33 http +entry index.ts:244:33 http +entry index.ts:246:33 http +entry index.ts:248:33 http +entry index.ts:24:31 http +entry index.ts:250:33 http +entry index.ts:252:33 http +entry index.ts:254:33 http +entry index.ts:256:33 http +entry index.ts:258:33 http +entry index.ts:260:33 http +entry index.ts:262:33 http +entry index.ts:264:33 http +entry index.ts:266:33 http +entry index.ts:268:33 http +entry index.ts:26:32 http +entry index.ts:270:33 http +entry index.ts:272:33 http +entry index.ts:274:33 http +entry index.ts:276:33 http +entry index.ts:278:33 http +entry index.ts:280:33 http +entry index.ts:282:33 http +entry index.ts:284:33 http +entry index.ts:286:33 http +entry index.ts:288:33 http +entry index.ts:28:32 http +entry index.ts:290:33 http +entry index.ts:292:33 http +entry index.ts:294:33 http +entry index.ts:296:33 http +entry index.ts:298:33 http +entry index.ts:300:33 http +entry index.ts:302:33 http +entry index.ts:304:33 http +entry index.ts:306:33 http +entry index.ts:308:33 http +entry index.ts:30:32 http +entry index.ts:310:33 http +entry index.ts:312:33 http +entry index.ts:314:33 http +entry index.ts:316:33 http +entry index.ts:318:33 http +entry index.ts:320:33 http +entry index.ts:322:33 http +entry index.ts:324:33 http +entry index.ts:326:33 http +entry index.ts:328:33 http +entry index.ts:32:32 http +entry index.ts:330:33 http +entry index.ts:332:33 http +entry index.ts:334:33 http +entry index.ts:336:33 http +entry index.ts:338:33 http +entry index.ts:340:33 http +entry index.ts:342:33 http +entry index.ts:344:33 http +entry index.ts:346:33 http +entry index.ts:348:33 http +entry index.ts:34:32 http +entry index.ts:350:33 http +entry index.ts:352:33 http +entry index.ts:354:33 http +entry index.ts:356:33 http +entry index.ts:358:33 http +entry index.ts:360:33 http +entry index.ts:362:33 http +entry index.ts:364:33 http +entry index.ts:366:33 http +entry index.ts:368:33 http +entry index.ts:36:32 http +entry index.ts:370:33 http +entry index.ts:372:33 http +entry index.ts:374:33 http +entry index.ts:376:33 http +entry index.ts:378:33 http +entry index.ts:380:33 http +entry index.ts:382:33 http +entry index.ts:384:33 http +entry index.ts:386:33 http +entry index.ts:388:33 http +entry index.ts:38:32 http +entry index.ts:390:33 http +entry index.ts:392:33 http +entry index.ts:394:33 http +entry index.ts:396:33 http +entry index.ts:398:33 http +entry index.ts:400:33 http +entry index.ts:402:33 http +entry index.ts:404:33 http +entry index.ts:406:16 http +entry index.ts:407:16 http +entry index.ts:408:16 http +entry index.ts:409:16 http +entry index.ts:40:32 http +entry index.ts:410:16 http +entry index.ts:411:16 http +entry index.ts:412:16 http +entry index.ts:413:16 http +entry index.ts:414:16 http +entry index.ts:415:16 http +entry index.ts:42:32 http +entry index.ts:44:32 http +entry index.ts:46:32 http +entry index.ts:48:32 http +entry index.ts:50:32 http +entry index.ts:52:32 http +entry index.ts:54:32 http +entry index.ts:56:32 http +entry index.ts:58:32 http +entry index.ts:60:32 http +entry index.ts:62:32 http +entry index.ts:64:32 http +entry index.ts:66:32 http +entry index.ts:68:32 http +entry index.ts:6:31 http +entry index.ts:70:32 http +entry index.ts:72:32 http +entry index.ts:74:32 http +entry index.ts:76:32 http +entry index.ts:78:32 http +entry index.ts:80:32 http +entry index.ts:82:32 http +entry index.ts:84:32 http +entry index.ts:86:32 http +entry index.ts:88:32 http +entry index.ts:8:31 http +entry index.ts:90:32 http +entry index.ts:92:32 http +entry index.ts:94:32 http +entry index.ts:96:32 http +entry index.ts:98:32 http diff --git a/graph/test/typescript/hono/expected/scale/mw-200.tsv b/graph/test/typescript/hono/expected/scale/mw-200.tsv new file mode 100644 index 000000000..a7ebfdf6f --- /dev/null +++ b/graph/test/typescript/hono/expected/scale/mw-200.tsv @@ -0,0 +1,2024 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: 200 middleware x 10 routes = 2,000 applications, positions 0..199 per route; runtime agrees +# census: use_global, await_next (scale: 200 middleware x 10 routes; generated by tools/gen-scale.mjs) +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /r0 index.ts:406:16 index.ts:5:13 200 +route GET /r1 index.ts:407:16 index.ts:5:13 201 +route GET /r2 index.ts:408:16 index.ts:5:13 202 +route GET /r3 index.ts:409:16 index.ts:5:13 203 +route GET /r4 index.ts:410:16 index.ts:5:13 204 +route GET /r5 index.ts:411:16 index.ts:5:13 205 +route GET /r6 index.ts:412:16 index.ts:5:13 206 +route GET /r7 index.ts:413:16 index.ts:5:13 207 +route GET /r8 index.ts:414:16 index.ts:5:13 208 +route GET /r9 index.ts:415:16 index.ts:5:13 209 +mw index.ts:6:31 index.ts:406:16 GET /r0 0 index.ts:5:13 +mw index.ts:8:31 index.ts:406:16 GET /r0 1 index.ts:5:13 +mw index.ts:10:31 index.ts:406:16 GET /r0 2 index.ts:5:13 +mw index.ts:12:31 index.ts:406:16 GET /r0 3 index.ts:5:13 +mw index.ts:14:31 index.ts:406:16 GET /r0 4 index.ts:5:13 +mw index.ts:16:31 index.ts:406:16 GET /r0 5 index.ts:5:13 +mw index.ts:18:31 index.ts:406:16 GET /r0 6 index.ts:5:13 +mw index.ts:20:31 index.ts:406:16 GET /r0 7 index.ts:5:13 +mw index.ts:22:31 index.ts:406:16 GET /r0 8 index.ts:5:13 +mw index.ts:24:31 index.ts:406:16 GET /r0 9 index.ts:5:13 +mw index.ts:26:32 index.ts:406:16 GET /r0 10 index.ts:5:13 +mw index.ts:28:32 index.ts:406:16 GET /r0 11 index.ts:5:13 +mw index.ts:30:32 index.ts:406:16 GET /r0 12 index.ts:5:13 +mw index.ts:32:32 index.ts:406:16 GET /r0 13 index.ts:5:13 +mw index.ts:34:32 index.ts:406:16 GET /r0 14 index.ts:5:13 +mw index.ts:36:32 index.ts:406:16 GET /r0 15 index.ts:5:13 +mw index.ts:38:32 index.ts:406:16 GET /r0 16 index.ts:5:13 +mw index.ts:40:32 index.ts:406:16 GET /r0 17 index.ts:5:13 +mw index.ts:42:32 index.ts:406:16 GET /r0 18 index.ts:5:13 +mw index.ts:44:32 index.ts:406:16 GET /r0 19 index.ts:5:13 +mw index.ts:46:32 index.ts:406:16 GET /r0 20 index.ts:5:13 +mw index.ts:48:32 index.ts:406:16 GET /r0 21 index.ts:5:13 +mw index.ts:50:32 index.ts:406:16 GET /r0 22 index.ts:5:13 +mw index.ts:52:32 index.ts:406:16 GET /r0 23 index.ts:5:13 +mw index.ts:54:32 index.ts:406:16 GET /r0 24 index.ts:5:13 +mw index.ts:56:32 index.ts:406:16 GET /r0 25 index.ts:5:13 +mw index.ts:58:32 index.ts:406:16 GET /r0 26 index.ts:5:13 +mw index.ts:60:32 index.ts:406:16 GET /r0 27 index.ts:5:13 +mw index.ts:62:32 index.ts:406:16 GET /r0 28 index.ts:5:13 +mw index.ts:64:32 index.ts:406:16 GET /r0 29 index.ts:5:13 +mw index.ts:66:32 index.ts:406:16 GET /r0 30 index.ts:5:13 +mw index.ts:68:32 index.ts:406:16 GET /r0 31 index.ts:5:13 +mw index.ts:70:32 index.ts:406:16 GET /r0 32 index.ts:5:13 +mw index.ts:72:32 index.ts:406:16 GET /r0 33 index.ts:5:13 +mw index.ts:74:32 index.ts:406:16 GET /r0 34 index.ts:5:13 +mw index.ts:76:32 index.ts:406:16 GET /r0 35 index.ts:5:13 +mw index.ts:78:32 index.ts:406:16 GET /r0 36 index.ts:5:13 +mw index.ts:80:32 index.ts:406:16 GET /r0 37 index.ts:5:13 +mw index.ts:82:32 index.ts:406:16 GET /r0 38 index.ts:5:13 +mw index.ts:84:32 index.ts:406:16 GET /r0 39 index.ts:5:13 +mw index.ts:86:32 index.ts:406:16 GET /r0 40 index.ts:5:13 +mw index.ts:88:32 index.ts:406:16 GET /r0 41 index.ts:5:13 +mw index.ts:90:32 index.ts:406:16 GET /r0 42 index.ts:5:13 +mw index.ts:92:32 index.ts:406:16 GET /r0 43 index.ts:5:13 +mw index.ts:94:32 index.ts:406:16 GET /r0 44 index.ts:5:13 +mw index.ts:96:32 index.ts:406:16 GET /r0 45 index.ts:5:13 +mw index.ts:98:32 index.ts:406:16 GET /r0 46 index.ts:5:13 +mw index.ts:100:32 index.ts:406:16 GET /r0 47 index.ts:5:13 +mw index.ts:102:32 index.ts:406:16 GET /r0 48 index.ts:5:13 +mw index.ts:104:32 index.ts:406:16 GET /r0 49 index.ts:5:13 +mw index.ts:106:32 index.ts:406:16 GET /r0 50 index.ts:5:13 +mw index.ts:108:32 index.ts:406:16 GET /r0 51 index.ts:5:13 +mw index.ts:110:32 index.ts:406:16 GET /r0 52 index.ts:5:13 +mw index.ts:112:32 index.ts:406:16 GET /r0 53 index.ts:5:13 +mw index.ts:114:32 index.ts:406:16 GET /r0 54 index.ts:5:13 +mw index.ts:116:32 index.ts:406:16 GET /r0 55 index.ts:5:13 +mw index.ts:118:32 index.ts:406:16 GET /r0 56 index.ts:5:13 +mw index.ts:120:32 index.ts:406:16 GET /r0 57 index.ts:5:13 +mw index.ts:122:32 index.ts:406:16 GET /r0 58 index.ts:5:13 +mw index.ts:124:32 index.ts:406:16 GET /r0 59 index.ts:5:13 +mw index.ts:126:32 index.ts:406:16 GET /r0 60 index.ts:5:13 +mw index.ts:128:32 index.ts:406:16 GET /r0 61 index.ts:5:13 +mw index.ts:130:32 index.ts:406:16 GET /r0 62 index.ts:5:13 +mw index.ts:132:32 index.ts:406:16 GET /r0 63 index.ts:5:13 +mw index.ts:134:32 index.ts:406:16 GET /r0 64 index.ts:5:13 +mw index.ts:136:32 index.ts:406:16 GET /r0 65 index.ts:5:13 +mw index.ts:138:32 index.ts:406:16 GET /r0 66 index.ts:5:13 +mw index.ts:140:32 index.ts:406:16 GET /r0 67 index.ts:5:13 +mw index.ts:142:32 index.ts:406:16 GET /r0 68 index.ts:5:13 +mw index.ts:144:32 index.ts:406:16 GET /r0 69 index.ts:5:13 +mw index.ts:146:32 index.ts:406:16 GET /r0 70 index.ts:5:13 +mw index.ts:148:32 index.ts:406:16 GET /r0 71 index.ts:5:13 +mw index.ts:150:32 index.ts:406:16 GET /r0 72 index.ts:5:13 +mw index.ts:152:32 index.ts:406:16 GET /r0 73 index.ts:5:13 +mw index.ts:154:32 index.ts:406:16 GET /r0 74 index.ts:5:13 +mw index.ts:156:32 index.ts:406:16 GET /r0 75 index.ts:5:13 +mw index.ts:158:32 index.ts:406:16 GET /r0 76 index.ts:5:13 +mw index.ts:160:32 index.ts:406:16 GET /r0 77 index.ts:5:13 +mw index.ts:162:32 index.ts:406:16 GET /r0 78 index.ts:5:13 +mw index.ts:164:32 index.ts:406:16 GET /r0 79 index.ts:5:13 +mw index.ts:166:32 index.ts:406:16 GET /r0 80 index.ts:5:13 +mw index.ts:168:32 index.ts:406:16 GET /r0 81 index.ts:5:13 +mw index.ts:170:32 index.ts:406:16 GET /r0 82 index.ts:5:13 +mw index.ts:172:32 index.ts:406:16 GET /r0 83 index.ts:5:13 +mw index.ts:174:32 index.ts:406:16 GET /r0 84 index.ts:5:13 +mw index.ts:176:32 index.ts:406:16 GET /r0 85 index.ts:5:13 +mw index.ts:178:32 index.ts:406:16 GET /r0 86 index.ts:5:13 +mw index.ts:180:32 index.ts:406:16 GET /r0 87 index.ts:5:13 +mw index.ts:182:32 index.ts:406:16 GET /r0 88 index.ts:5:13 +mw index.ts:184:32 index.ts:406:16 GET /r0 89 index.ts:5:13 +mw index.ts:186:32 index.ts:406:16 GET /r0 90 index.ts:5:13 +mw index.ts:188:32 index.ts:406:16 GET /r0 91 index.ts:5:13 +mw index.ts:190:32 index.ts:406:16 GET /r0 92 index.ts:5:13 +mw index.ts:192:32 index.ts:406:16 GET /r0 93 index.ts:5:13 +mw index.ts:194:32 index.ts:406:16 GET /r0 94 index.ts:5:13 +mw index.ts:196:32 index.ts:406:16 GET /r0 95 index.ts:5:13 +mw index.ts:198:32 index.ts:406:16 GET /r0 96 index.ts:5:13 +mw index.ts:200:32 index.ts:406:16 GET /r0 97 index.ts:5:13 +mw index.ts:202:32 index.ts:406:16 GET /r0 98 index.ts:5:13 +mw index.ts:204:32 index.ts:406:16 GET /r0 99 index.ts:5:13 +mw index.ts:206:33 index.ts:406:16 GET /r0 100 index.ts:5:13 +mw index.ts:208:33 index.ts:406:16 GET /r0 101 index.ts:5:13 +mw index.ts:210:33 index.ts:406:16 GET /r0 102 index.ts:5:13 +mw index.ts:212:33 index.ts:406:16 GET /r0 103 index.ts:5:13 +mw index.ts:214:33 index.ts:406:16 GET /r0 104 index.ts:5:13 +mw index.ts:216:33 index.ts:406:16 GET /r0 105 index.ts:5:13 +mw index.ts:218:33 index.ts:406:16 GET /r0 106 index.ts:5:13 +mw index.ts:220:33 index.ts:406:16 GET /r0 107 index.ts:5:13 +mw index.ts:222:33 index.ts:406:16 GET /r0 108 index.ts:5:13 +mw index.ts:224:33 index.ts:406:16 GET /r0 109 index.ts:5:13 +mw index.ts:226:33 index.ts:406:16 GET /r0 110 index.ts:5:13 +mw index.ts:228:33 index.ts:406:16 GET /r0 111 index.ts:5:13 +mw index.ts:230:33 index.ts:406:16 GET /r0 112 index.ts:5:13 +mw index.ts:232:33 index.ts:406:16 GET /r0 113 index.ts:5:13 +mw index.ts:234:33 index.ts:406:16 GET /r0 114 index.ts:5:13 +mw index.ts:236:33 index.ts:406:16 GET /r0 115 index.ts:5:13 +mw index.ts:238:33 index.ts:406:16 GET /r0 116 index.ts:5:13 +mw index.ts:240:33 index.ts:406:16 GET /r0 117 index.ts:5:13 +mw index.ts:242:33 index.ts:406:16 GET /r0 118 index.ts:5:13 +mw index.ts:244:33 index.ts:406:16 GET /r0 119 index.ts:5:13 +mw index.ts:246:33 index.ts:406:16 GET /r0 120 index.ts:5:13 +mw index.ts:248:33 index.ts:406:16 GET /r0 121 index.ts:5:13 +mw index.ts:250:33 index.ts:406:16 GET /r0 122 index.ts:5:13 +mw index.ts:252:33 index.ts:406:16 GET /r0 123 index.ts:5:13 +mw index.ts:254:33 index.ts:406:16 GET /r0 124 index.ts:5:13 +mw index.ts:256:33 index.ts:406:16 GET /r0 125 index.ts:5:13 +mw index.ts:258:33 index.ts:406:16 GET /r0 126 index.ts:5:13 +mw index.ts:260:33 index.ts:406:16 GET /r0 127 index.ts:5:13 +mw index.ts:262:33 index.ts:406:16 GET /r0 128 index.ts:5:13 +mw index.ts:264:33 index.ts:406:16 GET /r0 129 index.ts:5:13 +mw index.ts:266:33 index.ts:406:16 GET /r0 130 index.ts:5:13 +mw index.ts:268:33 index.ts:406:16 GET /r0 131 index.ts:5:13 +mw index.ts:270:33 index.ts:406:16 GET /r0 132 index.ts:5:13 +mw index.ts:272:33 index.ts:406:16 GET /r0 133 index.ts:5:13 +mw index.ts:274:33 index.ts:406:16 GET /r0 134 index.ts:5:13 +mw index.ts:276:33 index.ts:406:16 GET /r0 135 index.ts:5:13 +mw index.ts:278:33 index.ts:406:16 GET /r0 136 index.ts:5:13 +mw index.ts:280:33 index.ts:406:16 GET /r0 137 index.ts:5:13 +mw index.ts:282:33 index.ts:406:16 GET /r0 138 index.ts:5:13 +mw index.ts:284:33 index.ts:406:16 GET /r0 139 index.ts:5:13 +mw index.ts:286:33 index.ts:406:16 GET /r0 140 index.ts:5:13 +mw index.ts:288:33 index.ts:406:16 GET /r0 141 index.ts:5:13 +mw index.ts:290:33 index.ts:406:16 GET /r0 142 index.ts:5:13 +mw index.ts:292:33 index.ts:406:16 GET /r0 143 index.ts:5:13 +mw index.ts:294:33 index.ts:406:16 GET /r0 144 index.ts:5:13 +mw index.ts:296:33 index.ts:406:16 GET /r0 145 index.ts:5:13 +mw index.ts:298:33 index.ts:406:16 GET /r0 146 index.ts:5:13 +mw index.ts:300:33 index.ts:406:16 GET /r0 147 index.ts:5:13 +mw index.ts:302:33 index.ts:406:16 GET /r0 148 index.ts:5:13 +mw index.ts:304:33 index.ts:406:16 GET /r0 149 index.ts:5:13 +mw index.ts:306:33 index.ts:406:16 GET /r0 150 index.ts:5:13 +mw index.ts:308:33 index.ts:406:16 GET /r0 151 index.ts:5:13 +mw index.ts:310:33 index.ts:406:16 GET /r0 152 index.ts:5:13 +mw index.ts:312:33 index.ts:406:16 GET /r0 153 index.ts:5:13 +mw index.ts:314:33 index.ts:406:16 GET /r0 154 index.ts:5:13 +mw index.ts:316:33 index.ts:406:16 GET /r0 155 index.ts:5:13 +mw index.ts:318:33 index.ts:406:16 GET /r0 156 index.ts:5:13 +mw index.ts:320:33 index.ts:406:16 GET /r0 157 index.ts:5:13 +mw index.ts:322:33 index.ts:406:16 GET /r0 158 index.ts:5:13 +mw index.ts:324:33 index.ts:406:16 GET /r0 159 index.ts:5:13 +mw index.ts:326:33 index.ts:406:16 GET /r0 160 index.ts:5:13 +mw index.ts:328:33 index.ts:406:16 GET /r0 161 index.ts:5:13 +mw index.ts:330:33 index.ts:406:16 GET /r0 162 index.ts:5:13 +mw index.ts:332:33 index.ts:406:16 GET /r0 163 index.ts:5:13 +mw index.ts:334:33 index.ts:406:16 GET /r0 164 index.ts:5:13 +mw index.ts:336:33 index.ts:406:16 GET /r0 165 index.ts:5:13 +mw index.ts:338:33 index.ts:406:16 GET /r0 166 index.ts:5:13 +mw index.ts:340:33 index.ts:406:16 GET /r0 167 index.ts:5:13 +mw index.ts:342:33 index.ts:406:16 GET /r0 168 index.ts:5:13 +mw index.ts:344:33 index.ts:406:16 GET /r0 169 index.ts:5:13 +mw index.ts:346:33 index.ts:406:16 GET /r0 170 index.ts:5:13 +mw index.ts:348:33 index.ts:406:16 GET /r0 171 index.ts:5:13 +mw index.ts:350:33 index.ts:406:16 GET /r0 172 index.ts:5:13 +mw index.ts:352:33 index.ts:406:16 GET /r0 173 index.ts:5:13 +mw index.ts:354:33 index.ts:406:16 GET /r0 174 index.ts:5:13 +mw index.ts:356:33 index.ts:406:16 GET /r0 175 index.ts:5:13 +mw index.ts:358:33 index.ts:406:16 GET /r0 176 index.ts:5:13 +mw index.ts:360:33 index.ts:406:16 GET /r0 177 index.ts:5:13 +mw index.ts:362:33 index.ts:406:16 GET /r0 178 index.ts:5:13 +mw index.ts:364:33 index.ts:406:16 GET /r0 179 index.ts:5:13 +mw index.ts:366:33 index.ts:406:16 GET /r0 180 index.ts:5:13 +mw index.ts:368:33 index.ts:406:16 GET /r0 181 index.ts:5:13 +mw index.ts:370:33 index.ts:406:16 GET /r0 182 index.ts:5:13 +mw index.ts:372:33 index.ts:406:16 GET /r0 183 index.ts:5:13 +mw index.ts:374:33 index.ts:406:16 GET /r0 184 index.ts:5:13 +mw index.ts:376:33 index.ts:406:16 GET /r0 185 index.ts:5:13 +mw index.ts:378:33 index.ts:406:16 GET /r0 186 index.ts:5:13 +mw index.ts:380:33 index.ts:406:16 GET /r0 187 index.ts:5:13 +mw index.ts:382:33 index.ts:406:16 GET /r0 188 index.ts:5:13 +mw index.ts:384:33 index.ts:406:16 GET /r0 189 index.ts:5:13 +mw index.ts:386:33 index.ts:406:16 GET /r0 190 index.ts:5:13 +mw index.ts:388:33 index.ts:406:16 GET /r0 191 index.ts:5:13 +mw index.ts:390:33 index.ts:406:16 GET /r0 192 index.ts:5:13 +mw index.ts:392:33 index.ts:406:16 GET /r0 193 index.ts:5:13 +mw index.ts:394:33 index.ts:406:16 GET /r0 194 index.ts:5:13 +mw index.ts:396:33 index.ts:406:16 GET /r0 195 index.ts:5:13 +mw index.ts:398:33 index.ts:406:16 GET /r0 196 index.ts:5:13 +mw index.ts:400:33 index.ts:406:16 GET /r0 197 index.ts:5:13 +mw index.ts:402:33 index.ts:406:16 GET /r0 198 index.ts:5:13 +mw index.ts:404:33 index.ts:406:16 GET /r0 199 index.ts:5:13 +mw index.ts:6:31 index.ts:407:16 GET /r1 0 index.ts:5:13 +mw index.ts:8:31 index.ts:407:16 GET /r1 1 index.ts:5:13 +mw index.ts:10:31 index.ts:407:16 GET /r1 2 index.ts:5:13 +mw index.ts:12:31 index.ts:407:16 GET /r1 3 index.ts:5:13 +mw index.ts:14:31 index.ts:407:16 GET /r1 4 index.ts:5:13 +mw index.ts:16:31 index.ts:407:16 GET /r1 5 index.ts:5:13 +mw index.ts:18:31 index.ts:407:16 GET /r1 6 index.ts:5:13 +mw index.ts:20:31 index.ts:407:16 GET /r1 7 index.ts:5:13 +mw index.ts:22:31 index.ts:407:16 GET /r1 8 index.ts:5:13 +mw index.ts:24:31 index.ts:407:16 GET /r1 9 index.ts:5:13 +mw index.ts:26:32 index.ts:407:16 GET /r1 10 index.ts:5:13 +mw index.ts:28:32 index.ts:407:16 GET /r1 11 index.ts:5:13 +mw index.ts:30:32 index.ts:407:16 GET /r1 12 index.ts:5:13 +mw index.ts:32:32 index.ts:407:16 GET /r1 13 index.ts:5:13 +mw index.ts:34:32 index.ts:407:16 GET /r1 14 index.ts:5:13 +mw index.ts:36:32 index.ts:407:16 GET /r1 15 index.ts:5:13 +mw index.ts:38:32 index.ts:407:16 GET /r1 16 index.ts:5:13 +mw index.ts:40:32 index.ts:407:16 GET /r1 17 index.ts:5:13 +mw index.ts:42:32 index.ts:407:16 GET /r1 18 index.ts:5:13 +mw index.ts:44:32 index.ts:407:16 GET /r1 19 index.ts:5:13 +mw index.ts:46:32 index.ts:407:16 GET /r1 20 index.ts:5:13 +mw index.ts:48:32 index.ts:407:16 GET /r1 21 index.ts:5:13 +mw index.ts:50:32 index.ts:407:16 GET /r1 22 index.ts:5:13 +mw index.ts:52:32 index.ts:407:16 GET /r1 23 index.ts:5:13 +mw index.ts:54:32 index.ts:407:16 GET /r1 24 index.ts:5:13 +mw index.ts:56:32 index.ts:407:16 GET /r1 25 index.ts:5:13 +mw index.ts:58:32 index.ts:407:16 GET /r1 26 index.ts:5:13 +mw index.ts:60:32 index.ts:407:16 GET /r1 27 index.ts:5:13 +mw index.ts:62:32 index.ts:407:16 GET /r1 28 index.ts:5:13 +mw index.ts:64:32 index.ts:407:16 GET /r1 29 index.ts:5:13 +mw index.ts:66:32 index.ts:407:16 GET /r1 30 index.ts:5:13 +mw index.ts:68:32 index.ts:407:16 GET /r1 31 index.ts:5:13 +mw index.ts:70:32 index.ts:407:16 GET /r1 32 index.ts:5:13 +mw index.ts:72:32 index.ts:407:16 GET /r1 33 index.ts:5:13 +mw index.ts:74:32 index.ts:407:16 GET /r1 34 index.ts:5:13 +mw index.ts:76:32 index.ts:407:16 GET /r1 35 index.ts:5:13 +mw index.ts:78:32 index.ts:407:16 GET /r1 36 index.ts:5:13 +mw index.ts:80:32 index.ts:407:16 GET /r1 37 index.ts:5:13 +mw index.ts:82:32 index.ts:407:16 GET /r1 38 index.ts:5:13 +mw index.ts:84:32 index.ts:407:16 GET /r1 39 index.ts:5:13 +mw index.ts:86:32 index.ts:407:16 GET /r1 40 index.ts:5:13 +mw index.ts:88:32 index.ts:407:16 GET /r1 41 index.ts:5:13 +mw index.ts:90:32 index.ts:407:16 GET /r1 42 index.ts:5:13 +mw index.ts:92:32 index.ts:407:16 GET /r1 43 index.ts:5:13 +mw index.ts:94:32 index.ts:407:16 GET /r1 44 index.ts:5:13 +mw index.ts:96:32 index.ts:407:16 GET /r1 45 index.ts:5:13 +mw index.ts:98:32 index.ts:407:16 GET /r1 46 index.ts:5:13 +mw index.ts:100:32 index.ts:407:16 GET /r1 47 index.ts:5:13 +mw index.ts:102:32 index.ts:407:16 GET /r1 48 index.ts:5:13 +mw index.ts:104:32 index.ts:407:16 GET /r1 49 index.ts:5:13 +mw index.ts:106:32 index.ts:407:16 GET /r1 50 index.ts:5:13 +mw index.ts:108:32 index.ts:407:16 GET /r1 51 index.ts:5:13 +mw index.ts:110:32 index.ts:407:16 GET /r1 52 index.ts:5:13 +mw index.ts:112:32 index.ts:407:16 GET /r1 53 index.ts:5:13 +mw index.ts:114:32 index.ts:407:16 GET /r1 54 index.ts:5:13 +mw index.ts:116:32 index.ts:407:16 GET /r1 55 index.ts:5:13 +mw index.ts:118:32 index.ts:407:16 GET /r1 56 index.ts:5:13 +mw index.ts:120:32 index.ts:407:16 GET /r1 57 index.ts:5:13 +mw index.ts:122:32 index.ts:407:16 GET /r1 58 index.ts:5:13 +mw index.ts:124:32 index.ts:407:16 GET /r1 59 index.ts:5:13 +mw index.ts:126:32 index.ts:407:16 GET /r1 60 index.ts:5:13 +mw index.ts:128:32 index.ts:407:16 GET /r1 61 index.ts:5:13 +mw index.ts:130:32 index.ts:407:16 GET /r1 62 index.ts:5:13 +mw index.ts:132:32 index.ts:407:16 GET /r1 63 index.ts:5:13 +mw index.ts:134:32 index.ts:407:16 GET /r1 64 index.ts:5:13 +mw index.ts:136:32 index.ts:407:16 GET /r1 65 index.ts:5:13 +mw index.ts:138:32 index.ts:407:16 GET /r1 66 index.ts:5:13 +mw index.ts:140:32 index.ts:407:16 GET /r1 67 index.ts:5:13 +mw index.ts:142:32 index.ts:407:16 GET /r1 68 index.ts:5:13 +mw index.ts:144:32 index.ts:407:16 GET /r1 69 index.ts:5:13 +mw index.ts:146:32 index.ts:407:16 GET /r1 70 index.ts:5:13 +mw index.ts:148:32 index.ts:407:16 GET /r1 71 index.ts:5:13 +mw index.ts:150:32 index.ts:407:16 GET /r1 72 index.ts:5:13 +mw index.ts:152:32 index.ts:407:16 GET /r1 73 index.ts:5:13 +mw index.ts:154:32 index.ts:407:16 GET /r1 74 index.ts:5:13 +mw index.ts:156:32 index.ts:407:16 GET /r1 75 index.ts:5:13 +mw index.ts:158:32 index.ts:407:16 GET /r1 76 index.ts:5:13 +mw index.ts:160:32 index.ts:407:16 GET /r1 77 index.ts:5:13 +mw index.ts:162:32 index.ts:407:16 GET /r1 78 index.ts:5:13 +mw index.ts:164:32 index.ts:407:16 GET /r1 79 index.ts:5:13 +mw index.ts:166:32 index.ts:407:16 GET /r1 80 index.ts:5:13 +mw index.ts:168:32 index.ts:407:16 GET /r1 81 index.ts:5:13 +mw index.ts:170:32 index.ts:407:16 GET /r1 82 index.ts:5:13 +mw index.ts:172:32 index.ts:407:16 GET /r1 83 index.ts:5:13 +mw index.ts:174:32 index.ts:407:16 GET /r1 84 index.ts:5:13 +mw index.ts:176:32 index.ts:407:16 GET /r1 85 index.ts:5:13 +mw index.ts:178:32 index.ts:407:16 GET /r1 86 index.ts:5:13 +mw index.ts:180:32 index.ts:407:16 GET /r1 87 index.ts:5:13 +mw index.ts:182:32 index.ts:407:16 GET /r1 88 index.ts:5:13 +mw index.ts:184:32 index.ts:407:16 GET /r1 89 index.ts:5:13 +mw index.ts:186:32 index.ts:407:16 GET /r1 90 index.ts:5:13 +mw index.ts:188:32 index.ts:407:16 GET /r1 91 index.ts:5:13 +mw index.ts:190:32 index.ts:407:16 GET /r1 92 index.ts:5:13 +mw index.ts:192:32 index.ts:407:16 GET /r1 93 index.ts:5:13 +mw index.ts:194:32 index.ts:407:16 GET /r1 94 index.ts:5:13 +mw index.ts:196:32 index.ts:407:16 GET /r1 95 index.ts:5:13 +mw index.ts:198:32 index.ts:407:16 GET /r1 96 index.ts:5:13 +mw index.ts:200:32 index.ts:407:16 GET /r1 97 index.ts:5:13 +mw index.ts:202:32 index.ts:407:16 GET /r1 98 index.ts:5:13 +mw index.ts:204:32 index.ts:407:16 GET /r1 99 index.ts:5:13 +mw index.ts:206:33 index.ts:407:16 GET /r1 100 index.ts:5:13 +mw index.ts:208:33 index.ts:407:16 GET /r1 101 index.ts:5:13 +mw index.ts:210:33 index.ts:407:16 GET /r1 102 index.ts:5:13 +mw index.ts:212:33 index.ts:407:16 GET /r1 103 index.ts:5:13 +mw index.ts:214:33 index.ts:407:16 GET /r1 104 index.ts:5:13 +mw index.ts:216:33 index.ts:407:16 GET /r1 105 index.ts:5:13 +mw index.ts:218:33 index.ts:407:16 GET /r1 106 index.ts:5:13 +mw index.ts:220:33 index.ts:407:16 GET /r1 107 index.ts:5:13 +mw index.ts:222:33 index.ts:407:16 GET /r1 108 index.ts:5:13 +mw index.ts:224:33 index.ts:407:16 GET /r1 109 index.ts:5:13 +mw index.ts:226:33 index.ts:407:16 GET /r1 110 index.ts:5:13 +mw index.ts:228:33 index.ts:407:16 GET /r1 111 index.ts:5:13 +mw index.ts:230:33 index.ts:407:16 GET /r1 112 index.ts:5:13 +mw index.ts:232:33 index.ts:407:16 GET /r1 113 index.ts:5:13 +mw index.ts:234:33 index.ts:407:16 GET /r1 114 index.ts:5:13 +mw index.ts:236:33 index.ts:407:16 GET /r1 115 index.ts:5:13 +mw index.ts:238:33 index.ts:407:16 GET /r1 116 index.ts:5:13 +mw index.ts:240:33 index.ts:407:16 GET /r1 117 index.ts:5:13 +mw index.ts:242:33 index.ts:407:16 GET /r1 118 index.ts:5:13 +mw index.ts:244:33 index.ts:407:16 GET /r1 119 index.ts:5:13 +mw index.ts:246:33 index.ts:407:16 GET /r1 120 index.ts:5:13 +mw index.ts:248:33 index.ts:407:16 GET /r1 121 index.ts:5:13 +mw index.ts:250:33 index.ts:407:16 GET /r1 122 index.ts:5:13 +mw index.ts:252:33 index.ts:407:16 GET /r1 123 index.ts:5:13 +mw index.ts:254:33 index.ts:407:16 GET /r1 124 index.ts:5:13 +mw index.ts:256:33 index.ts:407:16 GET /r1 125 index.ts:5:13 +mw index.ts:258:33 index.ts:407:16 GET /r1 126 index.ts:5:13 +mw index.ts:260:33 index.ts:407:16 GET /r1 127 index.ts:5:13 +mw index.ts:262:33 index.ts:407:16 GET /r1 128 index.ts:5:13 +mw index.ts:264:33 index.ts:407:16 GET /r1 129 index.ts:5:13 +mw index.ts:266:33 index.ts:407:16 GET /r1 130 index.ts:5:13 +mw index.ts:268:33 index.ts:407:16 GET /r1 131 index.ts:5:13 +mw index.ts:270:33 index.ts:407:16 GET /r1 132 index.ts:5:13 +mw index.ts:272:33 index.ts:407:16 GET /r1 133 index.ts:5:13 +mw index.ts:274:33 index.ts:407:16 GET /r1 134 index.ts:5:13 +mw index.ts:276:33 index.ts:407:16 GET /r1 135 index.ts:5:13 +mw index.ts:278:33 index.ts:407:16 GET /r1 136 index.ts:5:13 +mw index.ts:280:33 index.ts:407:16 GET /r1 137 index.ts:5:13 +mw index.ts:282:33 index.ts:407:16 GET /r1 138 index.ts:5:13 +mw index.ts:284:33 index.ts:407:16 GET /r1 139 index.ts:5:13 +mw index.ts:286:33 index.ts:407:16 GET /r1 140 index.ts:5:13 +mw index.ts:288:33 index.ts:407:16 GET /r1 141 index.ts:5:13 +mw index.ts:290:33 index.ts:407:16 GET /r1 142 index.ts:5:13 +mw index.ts:292:33 index.ts:407:16 GET /r1 143 index.ts:5:13 +mw index.ts:294:33 index.ts:407:16 GET /r1 144 index.ts:5:13 +mw index.ts:296:33 index.ts:407:16 GET /r1 145 index.ts:5:13 +mw index.ts:298:33 index.ts:407:16 GET /r1 146 index.ts:5:13 +mw index.ts:300:33 index.ts:407:16 GET /r1 147 index.ts:5:13 +mw index.ts:302:33 index.ts:407:16 GET /r1 148 index.ts:5:13 +mw index.ts:304:33 index.ts:407:16 GET /r1 149 index.ts:5:13 +mw index.ts:306:33 index.ts:407:16 GET /r1 150 index.ts:5:13 +mw index.ts:308:33 index.ts:407:16 GET /r1 151 index.ts:5:13 +mw index.ts:310:33 index.ts:407:16 GET /r1 152 index.ts:5:13 +mw index.ts:312:33 index.ts:407:16 GET /r1 153 index.ts:5:13 +mw index.ts:314:33 index.ts:407:16 GET /r1 154 index.ts:5:13 +mw index.ts:316:33 index.ts:407:16 GET /r1 155 index.ts:5:13 +mw index.ts:318:33 index.ts:407:16 GET /r1 156 index.ts:5:13 +mw index.ts:320:33 index.ts:407:16 GET /r1 157 index.ts:5:13 +mw index.ts:322:33 index.ts:407:16 GET /r1 158 index.ts:5:13 +mw index.ts:324:33 index.ts:407:16 GET /r1 159 index.ts:5:13 +mw index.ts:326:33 index.ts:407:16 GET /r1 160 index.ts:5:13 +mw index.ts:328:33 index.ts:407:16 GET /r1 161 index.ts:5:13 +mw index.ts:330:33 index.ts:407:16 GET /r1 162 index.ts:5:13 +mw index.ts:332:33 index.ts:407:16 GET /r1 163 index.ts:5:13 +mw index.ts:334:33 index.ts:407:16 GET /r1 164 index.ts:5:13 +mw index.ts:336:33 index.ts:407:16 GET /r1 165 index.ts:5:13 +mw index.ts:338:33 index.ts:407:16 GET /r1 166 index.ts:5:13 +mw index.ts:340:33 index.ts:407:16 GET /r1 167 index.ts:5:13 +mw index.ts:342:33 index.ts:407:16 GET /r1 168 index.ts:5:13 +mw index.ts:344:33 index.ts:407:16 GET /r1 169 index.ts:5:13 +mw index.ts:346:33 index.ts:407:16 GET /r1 170 index.ts:5:13 +mw index.ts:348:33 index.ts:407:16 GET /r1 171 index.ts:5:13 +mw index.ts:350:33 index.ts:407:16 GET /r1 172 index.ts:5:13 +mw index.ts:352:33 index.ts:407:16 GET /r1 173 index.ts:5:13 +mw index.ts:354:33 index.ts:407:16 GET /r1 174 index.ts:5:13 +mw index.ts:356:33 index.ts:407:16 GET /r1 175 index.ts:5:13 +mw index.ts:358:33 index.ts:407:16 GET /r1 176 index.ts:5:13 +mw index.ts:360:33 index.ts:407:16 GET /r1 177 index.ts:5:13 +mw index.ts:362:33 index.ts:407:16 GET /r1 178 index.ts:5:13 +mw index.ts:364:33 index.ts:407:16 GET /r1 179 index.ts:5:13 +mw index.ts:366:33 index.ts:407:16 GET /r1 180 index.ts:5:13 +mw index.ts:368:33 index.ts:407:16 GET /r1 181 index.ts:5:13 +mw index.ts:370:33 index.ts:407:16 GET /r1 182 index.ts:5:13 +mw index.ts:372:33 index.ts:407:16 GET /r1 183 index.ts:5:13 +mw index.ts:374:33 index.ts:407:16 GET /r1 184 index.ts:5:13 +mw index.ts:376:33 index.ts:407:16 GET /r1 185 index.ts:5:13 +mw index.ts:378:33 index.ts:407:16 GET /r1 186 index.ts:5:13 +mw index.ts:380:33 index.ts:407:16 GET /r1 187 index.ts:5:13 +mw index.ts:382:33 index.ts:407:16 GET /r1 188 index.ts:5:13 +mw index.ts:384:33 index.ts:407:16 GET /r1 189 index.ts:5:13 +mw index.ts:386:33 index.ts:407:16 GET /r1 190 index.ts:5:13 +mw index.ts:388:33 index.ts:407:16 GET /r1 191 index.ts:5:13 +mw index.ts:390:33 index.ts:407:16 GET /r1 192 index.ts:5:13 +mw index.ts:392:33 index.ts:407:16 GET /r1 193 index.ts:5:13 +mw index.ts:394:33 index.ts:407:16 GET /r1 194 index.ts:5:13 +mw index.ts:396:33 index.ts:407:16 GET /r1 195 index.ts:5:13 +mw index.ts:398:33 index.ts:407:16 GET /r1 196 index.ts:5:13 +mw index.ts:400:33 index.ts:407:16 GET /r1 197 index.ts:5:13 +mw index.ts:402:33 index.ts:407:16 GET /r1 198 index.ts:5:13 +mw index.ts:404:33 index.ts:407:16 GET /r1 199 index.ts:5:13 +mw index.ts:6:31 index.ts:408:16 GET /r2 0 index.ts:5:13 +mw index.ts:8:31 index.ts:408:16 GET /r2 1 index.ts:5:13 +mw index.ts:10:31 index.ts:408:16 GET /r2 2 index.ts:5:13 +mw index.ts:12:31 index.ts:408:16 GET /r2 3 index.ts:5:13 +mw index.ts:14:31 index.ts:408:16 GET /r2 4 index.ts:5:13 +mw index.ts:16:31 index.ts:408:16 GET /r2 5 index.ts:5:13 +mw index.ts:18:31 index.ts:408:16 GET /r2 6 index.ts:5:13 +mw index.ts:20:31 index.ts:408:16 GET /r2 7 index.ts:5:13 +mw index.ts:22:31 index.ts:408:16 GET /r2 8 index.ts:5:13 +mw index.ts:24:31 index.ts:408:16 GET /r2 9 index.ts:5:13 +mw index.ts:26:32 index.ts:408:16 GET /r2 10 index.ts:5:13 +mw index.ts:28:32 index.ts:408:16 GET /r2 11 index.ts:5:13 +mw index.ts:30:32 index.ts:408:16 GET /r2 12 index.ts:5:13 +mw index.ts:32:32 index.ts:408:16 GET /r2 13 index.ts:5:13 +mw index.ts:34:32 index.ts:408:16 GET /r2 14 index.ts:5:13 +mw index.ts:36:32 index.ts:408:16 GET /r2 15 index.ts:5:13 +mw index.ts:38:32 index.ts:408:16 GET /r2 16 index.ts:5:13 +mw index.ts:40:32 index.ts:408:16 GET /r2 17 index.ts:5:13 +mw index.ts:42:32 index.ts:408:16 GET /r2 18 index.ts:5:13 +mw index.ts:44:32 index.ts:408:16 GET /r2 19 index.ts:5:13 +mw index.ts:46:32 index.ts:408:16 GET /r2 20 index.ts:5:13 +mw index.ts:48:32 index.ts:408:16 GET /r2 21 index.ts:5:13 +mw index.ts:50:32 index.ts:408:16 GET /r2 22 index.ts:5:13 +mw index.ts:52:32 index.ts:408:16 GET /r2 23 index.ts:5:13 +mw index.ts:54:32 index.ts:408:16 GET /r2 24 index.ts:5:13 +mw index.ts:56:32 index.ts:408:16 GET /r2 25 index.ts:5:13 +mw index.ts:58:32 index.ts:408:16 GET /r2 26 index.ts:5:13 +mw index.ts:60:32 index.ts:408:16 GET /r2 27 index.ts:5:13 +mw index.ts:62:32 index.ts:408:16 GET /r2 28 index.ts:5:13 +mw index.ts:64:32 index.ts:408:16 GET /r2 29 index.ts:5:13 +mw index.ts:66:32 index.ts:408:16 GET /r2 30 index.ts:5:13 +mw index.ts:68:32 index.ts:408:16 GET /r2 31 index.ts:5:13 +mw index.ts:70:32 index.ts:408:16 GET /r2 32 index.ts:5:13 +mw index.ts:72:32 index.ts:408:16 GET /r2 33 index.ts:5:13 +mw index.ts:74:32 index.ts:408:16 GET /r2 34 index.ts:5:13 +mw index.ts:76:32 index.ts:408:16 GET /r2 35 index.ts:5:13 +mw index.ts:78:32 index.ts:408:16 GET /r2 36 index.ts:5:13 +mw index.ts:80:32 index.ts:408:16 GET /r2 37 index.ts:5:13 +mw index.ts:82:32 index.ts:408:16 GET /r2 38 index.ts:5:13 +mw index.ts:84:32 index.ts:408:16 GET /r2 39 index.ts:5:13 +mw index.ts:86:32 index.ts:408:16 GET /r2 40 index.ts:5:13 +mw index.ts:88:32 index.ts:408:16 GET /r2 41 index.ts:5:13 +mw index.ts:90:32 index.ts:408:16 GET /r2 42 index.ts:5:13 +mw index.ts:92:32 index.ts:408:16 GET /r2 43 index.ts:5:13 +mw index.ts:94:32 index.ts:408:16 GET /r2 44 index.ts:5:13 +mw index.ts:96:32 index.ts:408:16 GET /r2 45 index.ts:5:13 +mw index.ts:98:32 index.ts:408:16 GET /r2 46 index.ts:5:13 +mw index.ts:100:32 index.ts:408:16 GET /r2 47 index.ts:5:13 +mw index.ts:102:32 index.ts:408:16 GET /r2 48 index.ts:5:13 +mw index.ts:104:32 index.ts:408:16 GET /r2 49 index.ts:5:13 +mw index.ts:106:32 index.ts:408:16 GET /r2 50 index.ts:5:13 +mw index.ts:108:32 index.ts:408:16 GET /r2 51 index.ts:5:13 +mw index.ts:110:32 index.ts:408:16 GET /r2 52 index.ts:5:13 +mw index.ts:112:32 index.ts:408:16 GET /r2 53 index.ts:5:13 +mw index.ts:114:32 index.ts:408:16 GET /r2 54 index.ts:5:13 +mw index.ts:116:32 index.ts:408:16 GET /r2 55 index.ts:5:13 +mw index.ts:118:32 index.ts:408:16 GET /r2 56 index.ts:5:13 +mw index.ts:120:32 index.ts:408:16 GET /r2 57 index.ts:5:13 +mw index.ts:122:32 index.ts:408:16 GET /r2 58 index.ts:5:13 +mw index.ts:124:32 index.ts:408:16 GET /r2 59 index.ts:5:13 +mw index.ts:126:32 index.ts:408:16 GET /r2 60 index.ts:5:13 +mw index.ts:128:32 index.ts:408:16 GET /r2 61 index.ts:5:13 +mw index.ts:130:32 index.ts:408:16 GET /r2 62 index.ts:5:13 +mw index.ts:132:32 index.ts:408:16 GET /r2 63 index.ts:5:13 +mw index.ts:134:32 index.ts:408:16 GET /r2 64 index.ts:5:13 +mw index.ts:136:32 index.ts:408:16 GET /r2 65 index.ts:5:13 +mw index.ts:138:32 index.ts:408:16 GET /r2 66 index.ts:5:13 +mw index.ts:140:32 index.ts:408:16 GET /r2 67 index.ts:5:13 +mw index.ts:142:32 index.ts:408:16 GET /r2 68 index.ts:5:13 +mw index.ts:144:32 index.ts:408:16 GET /r2 69 index.ts:5:13 +mw index.ts:146:32 index.ts:408:16 GET /r2 70 index.ts:5:13 +mw index.ts:148:32 index.ts:408:16 GET /r2 71 index.ts:5:13 +mw index.ts:150:32 index.ts:408:16 GET /r2 72 index.ts:5:13 +mw index.ts:152:32 index.ts:408:16 GET /r2 73 index.ts:5:13 +mw index.ts:154:32 index.ts:408:16 GET /r2 74 index.ts:5:13 +mw index.ts:156:32 index.ts:408:16 GET /r2 75 index.ts:5:13 +mw index.ts:158:32 index.ts:408:16 GET /r2 76 index.ts:5:13 +mw index.ts:160:32 index.ts:408:16 GET /r2 77 index.ts:5:13 +mw index.ts:162:32 index.ts:408:16 GET /r2 78 index.ts:5:13 +mw index.ts:164:32 index.ts:408:16 GET /r2 79 index.ts:5:13 +mw index.ts:166:32 index.ts:408:16 GET /r2 80 index.ts:5:13 +mw index.ts:168:32 index.ts:408:16 GET /r2 81 index.ts:5:13 +mw index.ts:170:32 index.ts:408:16 GET /r2 82 index.ts:5:13 +mw index.ts:172:32 index.ts:408:16 GET /r2 83 index.ts:5:13 +mw index.ts:174:32 index.ts:408:16 GET /r2 84 index.ts:5:13 +mw index.ts:176:32 index.ts:408:16 GET /r2 85 index.ts:5:13 +mw index.ts:178:32 index.ts:408:16 GET /r2 86 index.ts:5:13 +mw index.ts:180:32 index.ts:408:16 GET /r2 87 index.ts:5:13 +mw index.ts:182:32 index.ts:408:16 GET /r2 88 index.ts:5:13 +mw index.ts:184:32 index.ts:408:16 GET /r2 89 index.ts:5:13 +mw index.ts:186:32 index.ts:408:16 GET /r2 90 index.ts:5:13 +mw index.ts:188:32 index.ts:408:16 GET /r2 91 index.ts:5:13 +mw index.ts:190:32 index.ts:408:16 GET /r2 92 index.ts:5:13 +mw index.ts:192:32 index.ts:408:16 GET /r2 93 index.ts:5:13 +mw index.ts:194:32 index.ts:408:16 GET /r2 94 index.ts:5:13 +mw index.ts:196:32 index.ts:408:16 GET /r2 95 index.ts:5:13 +mw index.ts:198:32 index.ts:408:16 GET /r2 96 index.ts:5:13 +mw index.ts:200:32 index.ts:408:16 GET /r2 97 index.ts:5:13 +mw index.ts:202:32 index.ts:408:16 GET /r2 98 index.ts:5:13 +mw index.ts:204:32 index.ts:408:16 GET /r2 99 index.ts:5:13 +mw index.ts:206:33 index.ts:408:16 GET /r2 100 index.ts:5:13 +mw index.ts:208:33 index.ts:408:16 GET /r2 101 index.ts:5:13 +mw index.ts:210:33 index.ts:408:16 GET /r2 102 index.ts:5:13 +mw index.ts:212:33 index.ts:408:16 GET /r2 103 index.ts:5:13 +mw index.ts:214:33 index.ts:408:16 GET /r2 104 index.ts:5:13 +mw index.ts:216:33 index.ts:408:16 GET /r2 105 index.ts:5:13 +mw index.ts:218:33 index.ts:408:16 GET /r2 106 index.ts:5:13 +mw index.ts:220:33 index.ts:408:16 GET /r2 107 index.ts:5:13 +mw index.ts:222:33 index.ts:408:16 GET /r2 108 index.ts:5:13 +mw index.ts:224:33 index.ts:408:16 GET /r2 109 index.ts:5:13 +mw index.ts:226:33 index.ts:408:16 GET /r2 110 index.ts:5:13 +mw index.ts:228:33 index.ts:408:16 GET /r2 111 index.ts:5:13 +mw index.ts:230:33 index.ts:408:16 GET /r2 112 index.ts:5:13 +mw index.ts:232:33 index.ts:408:16 GET /r2 113 index.ts:5:13 +mw index.ts:234:33 index.ts:408:16 GET /r2 114 index.ts:5:13 +mw index.ts:236:33 index.ts:408:16 GET /r2 115 index.ts:5:13 +mw index.ts:238:33 index.ts:408:16 GET /r2 116 index.ts:5:13 +mw index.ts:240:33 index.ts:408:16 GET /r2 117 index.ts:5:13 +mw index.ts:242:33 index.ts:408:16 GET /r2 118 index.ts:5:13 +mw index.ts:244:33 index.ts:408:16 GET /r2 119 index.ts:5:13 +mw index.ts:246:33 index.ts:408:16 GET /r2 120 index.ts:5:13 +mw index.ts:248:33 index.ts:408:16 GET /r2 121 index.ts:5:13 +mw index.ts:250:33 index.ts:408:16 GET /r2 122 index.ts:5:13 +mw index.ts:252:33 index.ts:408:16 GET /r2 123 index.ts:5:13 +mw index.ts:254:33 index.ts:408:16 GET /r2 124 index.ts:5:13 +mw index.ts:256:33 index.ts:408:16 GET /r2 125 index.ts:5:13 +mw index.ts:258:33 index.ts:408:16 GET /r2 126 index.ts:5:13 +mw index.ts:260:33 index.ts:408:16 GET /r2 127 index.ts:5:13 +mw index.ts:262:33 index.ts:408:16 GET /r2 128 index.ts:5:13 +mw index.ts:264:33 index.ts:408:16 GET /r2 129 index.ts:5:13 +mw index.ts:266:33 index.ts:408:16 GET /r2 130 index.ts:5:13 +mw index.ts:268:33 index.ts:408:16 GET /r2 131 index.ts:5:13 +mw index.ts:270:33 index.ts:408:16 GET /r2 132 index.ts:5:13 +mw index.ts:272:33 index.ts:408:16 GET /r2 133 index.ts:5:13 +mw index.ts:274:33 index.ts:408:16 GET /r2 134 index.ts:5:13 +mw index.ts:276:33 index.ts:408:16 GET /r2 135 index.ts:5:13 +mw index.ts:278:33 index.ts:408:16 GET /r2 136 index.ts:5:13 +mw index.ts:280:33 index.ts:408:16 GET /r2 137 index.ts:5:13 +mw index.ts:282:33 index.ts:408:16 GET /r2 138 index.ts:5:13 +mw index.ts:284:33 index.ts:408:16 GET /r2 139 index.ts:5:13 +mw index.ts:286:33 index.ts:408:16 GET /r2 140 index.ts:5:13 +mw index.ts:288:33 index.ts:408:16 GET /r2 141 index.ts:5:13 +mw index.ts:290:33 index.ts:408:16 GET /r2 142 index.ts:5:13 +mw index.ts:292:33 index.ts:408:16 GET /r2 143 index.ts:5:13 +mw index.ts:294:33 index.ts:408:16 GET /r2 144 index.ts:5:13 +mw index.ts:296:33 index.ts:408:16 GET /r2 145 index.ts:5:13 +mw index.ts:298:33 index.ts:408:16 GET /r2 146 index.ts:5:13 +mw index.ts:300:33 index.ts:408:16 GET /r2 147 index.ts:5:13 +mw index.ts:302:33 index.ts:408:16 GET /r2 148 index.ts:5:13 +mw index.ts:304:33 index.ts:408:16 GET /r2 149 index.ts:5:13 +mw index.ts:306:33 index.ts:408:16 GET /r2 150 index.ts:5:13 +mw index.ts:308:33 index.ts:408:16 GET /r2 151 index.ts:5:13 +mw index.ts:310:33 index.ts:408:16 GET /r2 152 index.ts:5:13 +mw index.ts:312:33 index.ts:408:16 GET /r2 153 index.ts:5:13 +mw index.ts:314:33 index.ts:408:16 GET /r2 154 index.ts:5:13 +mw index.ts:316:33 index.ts:408:16 GET /r2 155 index.ts:5:13 +mw index.ts:318:33 index.ts:408:16 GET /r2 156 index.ts:5:13 +mw index.ts:320:33 index.ts:408:16 GET /r2 157 index.ts:5:13 +mw index.ts:322:33 index.ts:408:16 GET /r2 158 index.ts:5:13 +mw index.ts:324:33 index.ts:408:16 GET /r2 159 index.ts:5:13 +mw index.ts:326:33 index.ts:408:16 GET /r2 160 index.ts:5:13 +mw index.ts:328:33 index.ts:408:16 GET /r2 161 index.ts:5:13 +mw index.ts:330:33 index.ts:408:16 GET /r2 162 index.ts:5:13 +mw index.ts:332:33 index.ts:408:16 GET /r2 163 index.ts:5:13 +mw index.ts:334:33 index.ts:408:16 GET /r2 164 index.ts:5:13 +mw index.ts:336:33 index.ts:408:16 GET /r2 165 index.ts:5:13 +mw index.ts:338:33 index.ts:408:16 GET /r2 166 index.ts:5:13 +mw index.ts:340:33 index.ts:408:16 GET /r2 167 index.ts:5:13 +mw index.ts:342:33 index.ts:408:16 GET /r2 168 index.ts:5:13 +mw index.ts:344:33 index.ts:408:16 GET /r2 169 index.ts:5:13 +mw index.ts:346:33 index.ts:408:16 GET /r2 170 index.ts:5:13 +mw index.ts:348:33 index.ts:408:16 GET /r2 171 index.ts:5:13 +mw index.ts:350:33 index.ts:408:16 GET /r2 172 index.ts:5:13 +mw index.ts:352:33 index.ts:408:16 GET /r2 173 index.ts:5:13 +mw index.ts:354:33 index.ts:408:16 GET /r2 174 index.ts:5:13 +mw index.ts:356:33 index.ts:408:16 GET /r2 175 index.ts:5:13 +mw index.ts:358:33 index.ts:408:16 GET /r2 176 index.ts:5:13 +mw index.ts:360:33 index.ts:408:16 GET /r2 177 index.ts:5:13 +mw index.ts:362:33 index.ts:408:16 GET /r2 178 index.ts:5:13 +mw index.ts:364:33 index.ts:408:16 GET /r2 179 index.ts:5:13 +mw index.ts:366:33 index.ts:408:16 GET /r2 180 index.ts:5:13 +mw index.ts:368:33 index.ts:408:16 GET /r2 181 index.ts:5:13 +mw index.ts:370:33 index.ts:408:16 GET /r2 182 index.ts:5:13 +mw index.ts:372:33 index.ts:408:16 GET /r2 183 index.ts:5:13 +mw index.ts:374:33 index.ts:408:16 GET /r2 184 index.ts:5:13 +mw index.ts:376:33 index.ts:408:16 GET /r2 185 index.ts:5:13 +mw index.ts:378:33 index.ts:408:16 GET /r2 186 index.ts:5:13 +mw index.ts:380:33 index.ts:408:16 GET /r2 187 index.ts:5:13 +mw index.ts:382:33 index.ts:408:16 GET /r2 188 index.ts:5:13 +mw index.ts:384:33 index.ts:408:16 GET /r2 189 index.ts:5:13 +mw index.ts:386:33 index.ts:408:16 GET /r2 190 index.ts:5:13 +mw index.ts:388:33 index.ts:408:16 GET /r2 191 index.ts:5:13 +mw index.ts:390:33 index.ts:408:16 GET /r2 192 index.ts:5:13 +mw index.ts:392:33 index.ts:408:16 GET /r2 193 index.ts:5:13 +mw index.ts:394:33 index.ts:408:16 GET /r2 194 index.ts:5:13 +mw index.ts:396:33 index.ts:408:16 GET /r2 195 index.ts:5:13 +mw index.ts:398:33 index.ts:408:16 GET /r2 196 index.ts:5:13 +mw index.ts:400:33 index.ts:408:16 GET /r2 197 index.ts:5:13 +mw index.ts:402:33 index.ts:408:16 GET /r2 198 index.ts:5:13 +mw index.ts:404:33 index.ts:408:16 GET /r2 199 index.ts:5:13 +mw index.ts:6:31 index.ts:409:16 GET /r3 0 index.ts:5:13 +mw index.ts:8:31 index.ts:409:16 GET /r3 1 index.ts:5:13 +mw index.ts:10:31 index.ts:409:16 GET /r3 2 index.ts:5:13 +mw index.ts:12:31 index.ts:409:16 GET /r3 3 index.ts:5:13 +mw index.ts:14:31 index.ts:409:16 GET /r3 4 index.ts:5:13 +mw index.ts:16:31 index.ts:409:16 GET /r3 5 index.ts:5:13 +mw index.ts:18:31 index.ts:409:16 GET /r3 6 index.ts:5:13 +mw index.ts:20:31 index.ts:409:16 GET /r3 7 index.ts:5:13 +mw index.ts:22:31 index.ts:409:16 GET /r3 8 index.ts:5:13 +mw index.ts:24:31 index.ts:409:16 GET /r3 9 index.ts:5:13 +mw index.ts:26:32 index.ts:409:16 GET /r3 10 index.ts:5:13 +mw index.ts:28:32 index.ts:409:16 GET /r3 11 index.ts:5:13 +mw index.ts:30:32 index.ts:409:16 GET /r3 12 index.ts:5:13 +mw index.ts:32:32 index.ts:409:16 GET /r3 13 index.ts:5:13 +mw index.ts:34:32 index.ts:409:16 GET /r3 14 index.ts:5:13 +mw index.ts:36:32 index.ts:409:16 GET /r3 15 index.ts:5:13 +mw index.ts:38:32 index.ts:409:16 GET /r3 16 index.ts:5:13 +mw index.ts:40:32 index.ts:409:16 GET /r3 17 index.ts:5:13 +mw index.ts:42:32 index.ts:409:16 GET /r3 18 index.ts:5:13 +mw index.ts:44:32 index.ts:409:16 GET /r3 19 index.ts:5:13 +mw index.ts:46:32 index.ts:409:16 GET /r3 20 index.ts:5:13 +mw index.ts:48:32 index.ts:409:16 GET /r3 21 index.ts:5:13 +mw index.ts:50:32 index.ts:409:16 GET /r3 22 index.ts:5:13 +mw index.ts:52:32 index.ts:409:16 GET /r3 23 index.ts:5:13 +mw index.ts:54:32 index.ts:409:16 GET /r3 24 index.ts:5:13 +mw index.ts:56:32 index.ts:409:16 GET /r3 25 index.ts:5:13 +mw index.ts:58:32 index.ts:409:16 GET /r3 26 index.ts:5:13 +mw index.ts:60:32 index.ts:409:16 GET /r3 27 index.ts:5:13 +mw index.ts:62:32 index.ts:409:16 GET /r3 28 index.ts:5:13 +mw index.ts:64:32 index.ts:409:16 GET /r3 29 index.ts:5:13 +mw index.ts:66:32 index.ts:409:16 GET /r3 30 index.ts:5:13 +mw index.ts:68:32 index.ts:409:16 GET /r3 31 index.ts:5:13 +mw index.ts:70:32 index.ts:409:16 GET /r3 32 index.ts:5:13 +mw index.ts:72:32 index.ts:409:16 GET /r3 33 index.ts:5:13 +mw index.ts:74:32 index.ts:409:16 GET /r3 34 index.ts:5:13 +mw index.ts:76:32 index.ts:409:16 GET /r3 35 index.ts:5:13 +mw index.ts:78:32 index.ts:409:16 GET /r3 36 index.ts:5:13 +mw index.ts:80:32 index.ts:409:16 GET /r3 37 index.ts:5:13 +mw index.ts:82:32 index.ts:409:16 GET /r3 38 index.ts:5:13 +mw index.ts:84:32 index.ts:409:16 GET /r3 39 index.ts:5:13 +mw index.ts:86:32 index.ts:409:16 GET /r3 40 index.ts:5:13 +mw index.ts:88:32 index.ts:409:16 GET /r3 41 index.ts:5:13 +mw index.ts:90:32 index.ts:409:16 GET /r3 42 index.ts:5:13 +mw index.ts:92:32 index.ts:409:16 GET /r3 43 index.ts:5:13 +mw index.ts:94:32 index.ts:409:16 GET /r3 44 index.ts:5:13 +mw index.ts:96:32 index.ts:409:16 GET /r3 45 index.ts:5:13 +mw index.ts:98:32 index.ts:409:16 GET /r3 46 index.ts:5:13 +mw index.ts:100:32 index.ts:409:16 GET /r3 47 index.ts:5:13 +mw index.ts:102:32 index.ts:409:16 GET /r3 48 index.ts:5:13 +mw index.ts:104:32 index.ts:409:16 GET /r3 49 index.ts:5:13 +mw index.ts:106:32 index.ts:409:16 GET /r3 50 index.ts:5:13 +mw index.ts:108:32 index.ts:409:16 GET /r3 51 index.ts:5:13 +mw index.ts:110:32 index.ts:409:16 GET /r3 52 index.ts:5:13 +mw index.ts:112:32 index.ts:409:16 GET /r3 53 index.ts:5:13 +mw index.ts:114:32 index.ts:409:16 GET /r3 54 index.ts:5:13 +mw index.ts:116:32 index.ts:409:16 GET /r3 55 index.ts:5:13 +mw index.ts:118:32 index.ts:409:16 GET /r3 56 index.ts:5:13 +mw index.ts:120:32 index.ts:409:16 GET /r3 57 index.ts:5:13 +mw index.ts:122:32 index.ts:409:16 GET /r3 58 index.ts:5:13 +mw index.ts:124:32 index.ts:409:16 GET /r3 59 index.ts:5:13 +mw index.ts:126:32 index.ts:409:16 GET /r3 60 index.ts:5:13 +mw index.ts:128:32 index.ts:409:16 GET /r3 61 index.ts:5:13 +mw index.ts:130:32 index.ts:409:16 GET /r3 62 index.ts:5:13 +mw index.ts:132:32 index.ts:409:16 GET /r3 63 index.ts:5:13 +mw index.ts:134:32 index.ts:409:16 GET /r3 64 index.ts:5:13 +mw index.ts:136:32 index.ts:409:16 GET /r3 65 index.ts:5:13 +mw index.ts:138:32 index.ts:409:16 GET /r3 66 index.ts:5:13 +mw index.ts:140:32 index.ts:409:16 GET /r3 67 index.ts:5:13 +mw index.ts:142:32 index.ts:409:16 GET /r3 68 index.ts:5:13 +mw index.ts:144:32 index.ts:409:16 GET /r3 69 index.ts:5:13 +mw index.ts:146:32 index.ts:409:16 GET /r3 70 index.ts:5:13 +mw index.ts:148:32 index.ts:409:16 GET /r3 71 index.ts:5:13 +mw index.ts:150:32 index.ts:409:16 GET /r3 72 index.ts:5:13 +mw index.ts:152:32 index.ts:409:16 GET /r3 73 index.ts:5:13 +mw index.ts:154:32 index.ts:409:16 GET /r3 74 index.ts:5:13 +mw index.ts:156:32 index.ts:409:16 GET /r3 75 index.ts:5:13 +mw index.ts:158:32 index.ts:409:16 GET /r3 76 index.ts:5:13 +mw index.ts:160:32 index.ts:409:16 GET /r3 77 index.ts:5:13 +mw index.ts:162:32 index.ts:409:16 GET /r3 78 index.ts:5:13 +mw index.ts:164:32 index.ts:409:16 GET /r3 79 index.ts:5:13 +mw index.ts:166:32 index.ts:409:16 GET /r3 80 index.ts:5:13 +mw index.ts:168:32 index.ts:409:16 GET /r3 81 index.ts:5:13 +mw index.ts:170:32 index.ts:409:16 GET /r3 82 index.ts:5:13 +mw index.ts:172:32 index.ts:409:16 GET /r3 83 index.ts:5:13 +mw index.ts:174:32 index.ts:409:16 GET /r3 84 index.ts:5:13 +mw index.ts:176:32 index.ts:409:16 GET /r3 85 index.ts:5:13 +mw index.ts:178:32 index.ts:409:16 GET /r3 86 index.ts:5:13 +mw index.ts:180:32 index.ts:409:16 GET /r3 87 index.ts:5:13 +mw index.ts:182:32 index.ts:409:16 GET /r3 88 index.ts:5:13 +mw index.ts:184:32 index.ts:409:16 GET /r3 89 index.ts:5:13 +mw index.ts:186:32 index.ts:409:16 GET /r3 90 index.ts:5:13 +mw index.ts:188:32 index.ts:409:16 GET /r3 91 index.ts:5:13 +mw index.ts:190:32 index.ts:409:16 GET /r3 92 index.ts:5:13 +mw index.ts:192:32 index.ts:409:16 GET /r3 93 index.ts:5:13 +mw index.ts:194:32 index.ts:409:16 GET /r3 94 index.ts:5:13 +mw index.ts:196:32 index.ts:409:16 GET /r3 95 index.ts:5:13 +mw index.ts:198:32 index.ts:409:16 GET /r3 96 index.ts:5:13 +mw index.ts:200:32 index.ts:409:16 GET /r3 97 index.ts:5:13 +mw index.ts:202:32 index.ts:409:16 GET /r3 98 index.ts:5:13 +mw index.ts:204:32 index.ts:409:16 GET /r3 99 index.ts:5:13 +mw index.ts:206:33 index.ts:409:16 GET /r3 100 index.ts:5:13 +mw index.ts:208:33 index.ts:409:16 GET /r3 101 index.ts:5:13 +mw index.ts:210:33 index.ts:409:16 GET /r3 102 index.ts:5:13 +mw index.ts:212:33 index.ts:409:16 GET /r3 103 index.ts:5:13 +mw index.ts:214:33 index.ts:409:16 GET /r3 104 index.ts:5:13 +mw index.ts:216:33 index.ts:409:16 GET /r3 105 index.ts:5:13 +mw index.ts:218:33 index.ts:409:16 GET /r3 106 index.ts:5:13 +mw index.ts:220:33 index.ts:409:16 GET /r3 107 index.ts:5:13 +mw index.ts:222:33 index.ts:409:16 GET /r3 108 index.ts:5:13 +mw index.ts:224:33 index.ts:409:16 GET /r3 109 index.ts:5:13 +mw index.ts:226:33 index.ts:409:16 GET /r3 110 index.ts:5:13 +mw index.ts:228:33 index.ts:409:16 GET /r3 111 index.ts:5:13 +mw index.ts:230:33 index.ts:409:16 GET /r3 112 index.ts:5:13 +mw index.ts:232:33 index.ts:409:16 GET /r3 113 index.ts:5:13 +mw index.ts:234:33 index.ts:409:16 GET /r3 114 index.ts:5:13 +mw index.ts:236:33 index.ts:409:16 GET /r3 115 index.ts:5:13 +mw index.ts:238:33 index.ts:409:16 GET /r3 116 index.ts:5:13 +mw index.ts:240:33 index.ts:409:16 GET /r3 117 index.ts:5:13 +mw index.ts:242:33 index.ts:409:16 GET /r3 118 index.ts:5:13 +mw index.ts:244:33 index.ts:409:16 GET /r3 119 index.ts:5:13 +mw index.ts:246:33 index.ts:409:16 GET /r3 120 index.ts:5:13 +mw index.ts:248:33 index.ts:409:16 GET /r3 121 index.ts:5:13 +mw index.ts:250:33 index.ts:409:16 GET /r3 122 index.ts:5:13 +mw index.ts:252:33 index.ts:409:16 GET /r3 123 index.ts:5:13 +mw index.ts:254:33 index.ts:409:16 GET /r3 124 index.ts:5:13 +mw index.ts:256:33 index.ts:409:16 GET /r3 125 index.ts:5:13 +mw index.ts:258:33 index.ts:409:16 GET /r3 126 index.ts:5:13 +mw index.ts:260:33 index.ts:409:16 GET /r3 127 index.ts:5:13 +mw index.ts:262:33 index.ts:409:16 GET /r3 128 index.ts:5:13 +mw index.ts:264:33 index.ts:409:16 GET /r3 129 index.ts:5:13 +mw index.ts:266:33 index.ts:409:16 GET /r3 130 index.ts:5:13 +mw index.ts:268:33 index.ts:409:16 GET /r3 131 index.ts:5:13 +mw index.ts:270:33 index.ts:409:16 GET /r3 132 index.ts:5:13 +mw index.ts:272:33 index.ts:409:16 GET /r3 133 index.ts:5:13 +mw index.ts:274:33 index.ts:409:16 GET /r3 134 index.ts:5:13 +mw index.ts:276:33 index.ts:409:16 GET /r3 135 index.ts:5:13 +mw index.ts:278:33 index.ts:409:16 GET /r3 136 index.ts:5:13 +mw index.ts:280:33 index.ts:409:16 GET /r3 137 index.ts:5:13 +mw index.ts:282:33 index.ts:409:16 GET /r3 138 index.ts:5:13 +mw index.ts:284:33 index.ts:409:16 GET /r3 139 index.ts:5:13 +mw index.ts:286:33 index.ts:409:16 GET /r3 140 index.ts:5:13 +mw index.ts:288:33 index.ts:409:16 GET /r3 141 index.ts:5:13 +mw index.ts:290:33 index.ts:409:16 GET /r3 142 index.ts:5:13 +mw index.ts:292:33 index.ts:409:16 GET /r3 143 index.ts:5:13 +mw index.ts:294:33 index.ts:409:16 GET /r3 144 index.ts:5:13 +mw index.ts:296:33 index.ts:409:16 GET /r3 145 index.ts:5:13 +mw index.ts:298:33 index.ts:409:16 GET /r3 146 index.ts:5:13 +mw index.ts:300:33 index.ts:409:16 GET /r3 147 index.ts:5:13 +mw index.ts:302:33 index.ts:409:16 GET /r3 148 index.ts:5:13 +mw index.ts:304:33 index.ts:409:16 GET /r3 149 index.ts:5:13 +mw index.ts:306:33 index.ts:409:16 GET /r3 150 index.ts:5:13 +mw index.ts:308:33 index.ts:409:16 GET /r3 151 index.ts:5:13 +mw index.ts:310:33 index.ts:409:16 GET /r3 152 index.ts:5:13 +mw index.ts:312:33 index.ts:409:16 GET /r3 153 index.ts:5:13 +mw index.ts:314:33 index.ts:409:16 GET /r3 154 index.ts:5:13 +mw index.ts:316:33 index.ts:409:16 GET /r3 155 index.ts:5:13 +mw index.ts:318:33 index.ts:409:16 GET /r3 156 index.ts:5:13 +mw index.ts:320:33 index.ts:409:16 GET /r3 157 index.ts:5:13 +mw index.ts:322:33 index.ts:409:16 GET /r3 158 index.ts:5:13 +mw index.ts:324:33 index.ts:409:16 GET /r3 159 index.ts:5:13 +mw index.ts:326:33 index.ts:409:16 GET /r3 160 index.ts:5:13 +mw index.ts:328:33 index.ts:409:16 GET /r3 161 index.ts:5:13 +mw index.ts:330:33 index.ts:409:16 GET /r3 162 index.ts:5:13 +mw index.ts:332:33 index.ts:409:16 GET /r3 163 index.ts:5:13 +mw index.ts:334:33 index.ts:409:16 GET /r3 164 index.ts:5:13 +mw index.ts:336:33 index.ts:409:16 GET /r3 165 index.ts:5:13 +mw index.ts:338:33 index.ts:409:16 GET /r3 166 index.ts:5:13 +mw index.ts:340:33 index.ts:409:16 GET /r3 167 index.ts:5:13 +mw index.ts:342:33 index.ts:409:16 GET /r3 168 index.ts:5:13 +mw index.ts:344:33 index.ts:409:16 GET /r3 169 index.ts:5:13 +mw index.ts:346:33 index.ts:409:16 GET /r3 170 index.ts:5:13 +mw index.ts:348:33 index.ts:409:16 GET /r3 171 index.ts:5:13 +mw index.ts:350:33 index.ts:409:16 GET /r3 172 index.ts:5:13 +mw index.ts:352:33 index.ts:409:16 GET /r3 173 index.ts:5:13 +mw index.ts:354:33 index.ts:409:16 GET /r3 174 index.ts:5:13 +mw index.ts:356:33 index.ts:409:16 GET /r3 175 index.ts:5:13 +mw index.ts:358:33 index.ts:409:16 GET /r3 176 index.ts:5:13 +mw index.ts:360:33 index.ts:409:16 GET /r3 177 index.ts:5:13 +mw index.ts:362:33 index.ts:409:16 GET /r3 178 index.ts:5:13 +mw index.ts:364:33 index.ts:409:16 GET /r3 179 index.ts:5:13 +mw index.ts:366:33 index.ts:409:16 GET /r3 180 index.ts:5:13 +mw index.ts:368:33 index.ts:409:16 GET /r3 181 index.ts:5:13 +mw index.ts:370:33 index.ts:409:16 GET /r3 182 index.ts:5:13 +mw index.ts:372:33 index.ts:409:16 GET /r3 183 index.ts:5:13 +mw index.ts:374:33 index.ts:409:16 GET /r3 184 index.ts:5:13 +mw index.ts:376:33 index.ts:409:16 GET /r3 185 index.ts:5:13 +mw index.ts:378:33 index.ts:409:16 GET /r3 186 index.ts:5:13 +mw index.ts:380:33 index.ts:409:16 GET /r3 187 index.ts:5:13 +mw index.ts:382:33 index.ts:409:16 GET /r3 188 index.ts:5:13 +mw index.ts:384:33 index.ts:409:16 GET /r3 189 index.ts:5:13 +mw index.ts:386:33 index.ts:409:16 GET /r3 190 index.ts:5:13 +mw index.ts:388:33 index.ts:409:16 GET /r3 191 index.ts:5:13 +mw index.ts:390:33 index.ts:409:16 GET /r3 192 index.ts:5:13 +mw index.ts:392:33 index.ts:409:16 GET /r3 193 index.ts:5:13 +mw index.ts:394:33 index.ts:409:16 GET /r3 194 index.ts:5:13 +mw index.ts:396:33 index.ts:409:16 GET /r3 195 index.ts:5:13 +mw index.ts:398:33 index.ts:409:16 GET /r3 196 index.ts:5:13 +mw index.ts:400:33 index.ts:409:16 GET /r3 197 index.ts:5:13 +mw index.ts:402:33 index.ts:409:16 GET /r3 198 index.ts:5:13 +mw index.ts:404:33 index.ts:409:16 GET /r3 199 index.ts:5:13 +mw index.ts:6:31 index.ts:410:16 GET /r4 0 index.ts:5:13 +mw index.ts:8:31 index.ts:410:16 GET /r4 1 index.ts:5:13 +mw index.ts:10:31 index.ts:410:16 GET /r4 2 index.ts:5:13 +mw index.ts:12:31 index.ts:410:16 GET /r4 3 index.ts:5:13 +mw index.ts:14:31 index.ts:410:16 GET /r4 4 index.ts:5:13 +mw index.ts:16:31 index.ts:410:16 GET /r4 5 index.ts:5:13 +mw index.ts:18:31 index.ts:410:16 GET /r4 6 index.ts:5:13 +mw index.ts:20:31 index.ts:410:16 GET /r4 7 index.ts:5:13 +mw index.ts:22:31 index.ts:410:16 GET /r4 8 index.ts:5:13 +mw index.ts:24:31 index.ts:410:16 GET /r4 9 index.ts:5:13 +mw index.ts:26:32 index.ts:410:16 GET /r4 10 index.ts:5:13 +mw index.ts:28:32 index.ts:410:16 GET /r4 11 index.ts:5:13 +mw index.ts:30:32 index.ts:410:16 GET /r4 12 index.ts:5:13 +mw index.ts:32:32 index.ts:410:16 GET /r4 13 index.ts:5:13 +mw index.ts:34:32 index.ts:410:16 GET /r4 14 index.ts:5:13 +mw index.ts:36:32 index.ts:410:16 GET /r4 15 index.ts:5:13 +mw index.ts:38:32 index.ts:410:16 GET /r4 16 index.ts:5:13 +mw index.ts:40:32 index.ts:410:16 GET /r4 17 index.ts:5:13 +mw index.ts:42:32 index.ts:410:16 GET /r4 18 index.ts:5:13 +mw index.ts:44:32 index.ts:410:16 GET /r4 19 index.ts:5:13 +mw index.ts:46:32 index.ts:410:16 GET /r4 20 index.ts:5:13 +mw index.ts:48:32 index.ts:410:16 GET /r4 21 index.ts:5:13 +mw index.ts:50:32 index.ts:410:16 GET /r4 22 index.ts:5:13 +mw index.ts:52:32 index.ts:410:16 GET /r4 23 index.ts:5:13 +mw index.ts:54:32 index.ts:410:16 GET /r4 24 index.ts:5:13 +mw index.ts:56:32 index.ts:410:16 GET /r4 25 index.ts:5:13 +mw index.ts:58:32 index.ts:410:16 GET /r4 26 index.ts:5:13 +mw index.ts:60:32 index.ts:410:16 GET /r4 27 index.ts:5:13 +mw index.ts:62:32 index.ts:410:16 GET /r4 28 index.ts:5:13 +mw index.ts:64:32 index.ts:410:16 GET /r4 29 index.ts:5:13 +mw index.ts:66:32 index.ts:410:16 GET /r4 30 index.ts:5:13 +mw index.ts:68:32 index.ts:410:16 GET /r4 31 index.ts:5:13 +mw index.ts:70:32 index.ts:410:16 GET /r4 32 index.ts:5:13 +mw index.ts:72:32 index.ts:410:16 GET /r4 33 index.ts:5:13 +mw index.ts:74:32 index.ts:410:16 GET /r4 34 index.ts:5:13 +mw index.ts:76:32 index.ts:410:16 GET /r4 35 index.ts:5:13 +mw index.ts:78:32 index.ts:410:16 GET /r4 36 index.ts:5:13 +mw index.ts:80:32 index.ts:410:16 GET /r4 37 index.ts:5:13 +mw index.ts:82:32 index.ts:410:16 GET /r4 38 index.ts:5:13 +mw index.ts:84:32 index.ts:410:16 GET /r4 39 index.ts:5:13 +mw index.ts:86:32 index.ts:410:16 GET /r4 40 index.ts:5:13 +mw index.ts:88:32 index.ts:410:16 GET /r4 41 index.ts:5:13 +mw index.ts:90:32 index.ts:410:16 GET /r4 42 index.ts:5:13 +mw index.ts:92:32 index.ts:410:16 GET /r4 43 index.ts:5:13 +mw index.ts:94:32 index.ts:410:16 GET /r4 44 index.ts:5:13 +mw index.ts:96:32 index.ts:410:16 GET /r4 45 index.ts:5:13 +mw index.ts:98:32 index.ts:410:16 GET /r4 46 index.ts:5:13 +mw index.ts:100:32 index.ts:410:16 GET /r4 47 index.ts:5:13 +mw index.ts:102:32 index.ts:410:16 GET /r4 48 index.ts:5:13 +mw index.ts:104:32 index.ts:410:16 GET /r4 49 index.ts:5:13 +mw index.ts:106:32 index.ts:410:16 GET /r4 50 index.ts:5:13 +mw index.ts:108:32 index.ts:410:16 GET /r4 51 index.ts:5:13 +mw index.ts:110:32 index.ts:410:16 GET /r4 52 index.ts:5:13 +mw index.ts:112:32 index.ts:410:16 GET /r4 53 index.ts:5:13 +mw index.ts:114:32 index.ts:410:16 GET /r4 54 index.ts:5:13 +mw index.ts:116:32 index.ts:410:16 GET /r4 55 index.ts:5:13 +mw index.ts:118:32 index.ts:410:16 GET /r4 56 index.ts:5:13 +mw index.ts:120:32 index.ts:410:16 GET /r4 57 index.ts:5:13 +mw index.ts:122:32 index.ts:410:16 GET /r4 58 index.ts:5:13 +mw index.ts:124:32 index.ts:410:16 GET /r4 59 index.ts:5:13 +mw index.ts:126:32 index.ts:410:16 GET /r4 60 index.ts:5:13 +mw index.ts:128:32 index.ts:410:16 GET /r4 61 index.ts:5:13 +mw index.ts:130:32 index.ts:410:16 GET /r4 62 index.ts:5:13 +mw index.ts:132:32 index.ts:410:16 GET /r4 63 index.ts:5:13 +mw index.ts:134:32 index.ts:410:16 GET /r4 64 index.ts:5:13 +mw index.ts:136:32 index.ts:410:16 GET /r4 65 index.ts:5:13 +mw index.ts:138:32 index.ts:410:16 GET /r4 66 index.ts:5:13 +mw index.ts:140:32 index.ts:410:16 GET /r4 67 index.ts:5:13 +mw index.ts:142:32 index.ts:410:16 GET /r4 68 index.ts:5:13 +mw index.ts:144:32 index.ts:410:16 GET /r4 69 index.ts:5:13 +mw index.ts:146:32 index.ts:410:16 GET /r4 70 index.ts:5:13 +mw index.ts:148:32 index.ts:410:16 GET /r4 71 index.ts:5:13 +mw index.ts:150:32 index.ts:410:16 GET /r4 72 index.ts:5:13 +mw index.ts:152:32 index.ts:410:16 GET /r4 73 index.ts:5:13 +mw index.ts:154:32 index.ts:410:16 GET /r4 74 index.ts:5:13 +mw index.ts:156:32 index.ts:410:16 GET /r4 75 index.ts:5:13 +mw index.ts:158:32 index.ts:410:16 GET /r4 76 index.ts:5:13 +mw index.ts:160:32 index.ts:410:16 GET /r4 77 index.ts:5:13 +mw index.ts:162:32 index.ts:410:16 GET /r4 78 index.ts:5:13 +mw index.ts:164:32 index.ts:410:16 GET /r4 79 index.ts:5:13 +mw index.ts:166:32 index.ts:410:16 GET /r4 80 index.ts:5:13 +mw index.ts:168:32 index.ts:410:16 GET /r4 81 index.ts:5:13 +mw index.ts:170:32 index.ts:410:16 GET /r4 82 index.ts:5:13 +mw index.ts:172:32 index.ts:410:16 GET /r4 83 index.ts:5:13 +mw index.ts:174:32 index.ts:410:16 GET /r4 84 index.ts:5:13 +mw index.ts:176:32 index.ts:410:16 GET /r4 85 index.ts:5:13 +mw index.ts:178:32 index.ts:410:16 GET /r4 86 index.ts:5:13 +mw index.ts:180:32 index.ts:410:16 GET /r4 87 index.ts:5:13 +mw index.ts:182:32 index.ts:410:16 GET /r4 88 index.ts:5:13 +mw index.ts:184:32 index.ts:410:16 GET /r4 89 index.ts:5:13 +mw index.ts:186:32 index.ts:410:16 GET /r4 90 index.ts:5:13 +mw index.ts:188:32 index.ts:410:16 GET /r4 91 index.ts:5:13 +mw index.ts:190:32 index.ts:410:16 GET /r4 92 index.ts:5:13 +mw index.ts:192:32 index.ts:410:16 GET /r4 93 index.ts:5:13 +mw index.ts:194:32 index.ts:410:16 GET /r4 94 index.ts:5:13 +mw index.ts:196:32 index.ts:410:16 GET /r4 95 index.ts:5:13 +mw index.ts:198:32 index.ts:410:16 GET /r4 96 index.ts:5:13 +mw index.ts:200:32 index.ts:410:16 GET /r4 97 index.ts:5:13 +mw index.ts:202:32 index.ts:410:16 GET /r4 98 index.ts:5:13 +mw index.ts:204:32 index.ts:410:16 GET /r4 99 index.ts:5:13 +mw index.ts:206:33 index.ts:410:16 GET /r4 100 index.ts:5:13 +mw index.ts:208:33 index.ts:410:16 GET /r4 101 index.ts:5:13 +mw index.ts:210:33 index.ts:410:16 GET /r4 102 index.ts:5:13 +mw index.ts:212:33 index.ts:410:16 GET /r4 103 index.ts:5:13 +mw index.ts:214:33 index.ts:410:16 GET /r4 104 index.ts:5:13 +mw index.ts:216:33 index.ts:410:16 GET /r4 105 index.ts:5:13 +mw index.ts:218:33 index.ts:410:16 GET /r4 106 index.ts:5:13 +mw index.ts:220:33 index.ts:410:16 GET /r4 107 index.ts:5:13 +mw index.ts:222:33 index.ts:410:16 GET /r4 108 index.ts:5:13 +mw index.ts:224:33 index.ts:410:16 GET /r4 109 index.ts:5:13 +mw index.ts:226:33 index.ts:410:16 GET /r4 110 index.ts:5:13 +mw index.ts:228:33 index.ts:410:16 GET /r4 111 index.ts:5:13 +mw index.ts:230:33 index.ts:410:16 GET /r4 112 index.ts:5:13 +mw index.ts:232:33 index.ts:410:16 GET /r4 113 index.ts:5:13 +mw index.ts:234:33 index.ts:410:16 GET /r4 114 index.ts:5:13 +mw index.ts:236:33 index.ts:410:16 GET /r4 115 index.ts:5:13 +mw index.ts:238:33 index.ts:410:16 GET /r4 116 index.ts:5:13 +mw index.ts:240:33 index.ts:410:16 GET /r4 117 index.ts:5:13 +mw index.ts:242:33 index.ts:410:16 GET /r4 118 index.ts:5:13 +mw index.ts:244:33 index.ts:410:16 GET /r4 119 index.ts:5:13 +mw index.ts:246:33 index.ts:410:16 GET /r4 120 index.ts:5:13 +mw index.ts:248:33 index.ts:410:16 GET /r4 121 index.ts:5:13 +mw index.ts:250:33 index.ts:410:16 GET /r4 122 index.ts:5:13 +mw index.ts:252:33 index.ts:410:16 GET /r4 123 index.ts:5:13 +mw index.ts:254:33 index.ts:410:16 GET /r4 124 index.ts:5:13 +mw index.ts:256:33 index.ts:410:16 GET /r4 125 index.ts:5:13 +mw index.ts:258:33 index.ts:410:16 GET /r4 126 index.ts:5:13 +mw index.ts:260:33 index.ts:410:16 GET /r4 127 index.ts:5:13 +mw index.ts:262:33 index.ts:410:16 GET /r4 128 index.ts:5:13 +mw index.ts:264:33 index.ts:410:16 GET /r4 129 index.ts:5:13 +mw index.ts:266:33 index.ts:410:16 GET /r4 130 index.ts:5:13 +mw index.ts:268:33 index.ts:410:16 GET /r4 131 index.ts:5:13 +mw index.ts:270:33 index.ts:410:16 GET /r4 132 index.ts:5:13 +mw index.ts:272:33 index.ts:410:16 GET /r4 133 index.ts:5:13 +mw index.ts:274:33 index.ts:410:16 GET /r4 134 index.ts:5:13 +mw index.ts:276:33 index.ts:410:16 GET /r4 135 index.ts:5:13 +mw index.ts:278:33 index.ts:410:16 GET /r4 136 index.ts:5:13 +mw index.ts:280:33 index.ts:410:16 GET /r4 137 index.ts:5:13 +mw index.ts:282:33 index.ts:410:16 GET /r4 138 index.ts:5:13 +mw index.ts:284:33 index.ts:410:16 GET /r4 139 index.ts:5:13 +mw index.ts:286:33 index.ts:410:16 GET /r4 140 index.ts:5:13 +mw index.ts:288:33 index.ts:410:16 GET /r4 141 index.ts:5:13 +mw index.ts:290:33 index.ts:410:16 GET /r4 142 index.ts:5:13 +mw index.ts:292:33 index.ts:410:16 GET /r4 143 index.ts:5:13 +mw index.ts:294:33 index.ts:410:16 GET /r4 144 index.ts:5:13 +mw index.ts:296:33 index.ts:410:16 GET /r4 145 index.ts:5:13 +mw index.ts:298:33 index.ts:410:16 GET /r4 146 index.ts:5:13 +mw index.ts:300:33 index.ts:410:16 GET /r4 147 index.ts:5:13 +mw index.ts:302:33 index.ts:410:16 GET /r4 148 index.ts:5:13 +mw index.ts:304:33 index.ts:410:16 GET /r4 149 index.ts:5:13 +mw index.ts:306:33 index.ts:410:16 GET /r4 150 index.ts:5:13 +mw index.ts:308:33 index.ts:410:16 GET /r4 151 index.ts:5:13 +mw index.ts:310:33 index.ts:410:16 GET /r4 152 index.ts:5:13 +mw index.ts:312:33 index.ts:410:16 GET /r4 153 index.ts:5:13 +mw index.ts:314:33 index.ts:410:16 GET /r4 154 index.ts:5:13 +mw index.ts:316:33 index.ts:410:16 GET /r4 155 index.ts:5:13 +mw index.ts:318:33 index.ts:410:16 GET /r4 156 index.ts:5:13 +mw index.ts:320:33 index.ts:410:16 GET /r4 157 index.ts:5:13 +mw index.ts:322:33 index.ts:410:16 GET /r4 158 index.ts:5:13 +mw index.ts:324:33 index.ts:410:16 GET /r4 159 index.ts:5:13 +mw index.ts:326:33 index.ts:410:16 GET /r4 160 index.ts:5:13 +mw index.ts:328:33 index.ts:410:16 GET /r4 161 index.ts:5:13 +mw index.ts:330:33 index.ts:410:16 GET /r4 162 index.ts:5:13 +mw index.ts:332:33 index.ts:410:16 GET /r4 163 index.ts:5:13 +mw index.ts:334:33 index.ts:410:16 GET /r4 164 index.ts:5:13 +mw index.ts:336:33 index.ts:410:16 GET /r4 165 index.ts:5:13 +mw index.ts:338:33 index.ts:410:16 GET /r4 166 index.ts:5:13 +mw index.ts:340:33 index.ts:410:16 GET /r4 167 index.ts:5:13 +mw index.ts:342:33 index.ts:410:16 GET /r4 168 index.ts:5:13 +mw index.ts:344:33 index.ts:410:16 GET /r4 169 index.ts:5:13 +mw index.ts:346:33 index.ts:410:16 GET /r4 170 index.ts:5:13 +mw index.ts:348:33 index.ts:410:16 GET /r4 171 index.ts:5:13 +mw index.ts:350:33 index.ts:410:16 GET /r4 172 index.ts:5:13 +mw index.ts:352:33 index.ts:410:16 GET /r4 173 index.ts:5:13 +mw index.ts:354:33 index.ts:410:16 GET /r4 174 index.ts:5:13 +mw index.ts:356:33 index.ts:410:16 GET /r4 175 index.ts:5:13 +mw index.ts:358:33 index.ts:410:16 GET /r4 176 index.ts:5:13 +mw index.ts:360:33 index.ts:410:16 GET /r4 177 index.ts:5:13 +mw index.ts:362:33 index.ts:410:16 GET /r4 178 index.ts:5:13 +mw index.ts:364:33 index.ts:410:16 GET /r4 179 index.ts:5:13 +mw index.ts:366:33 index.ts:410:16 GET /r4 180 index.ts:5:13 +mw index.ts:368:33 index.ts:410:16 GET /r4 181 index.ts:5:13 +mw index.ts:370:33 index.ts:410:16 GET /r4 182 index.ts:5:13 +mw index.ts:372:33 index.ts:410:16 GET /r4 183 index.ts:5:13 +mw index.ts:374:33 index.ts:410:16 GET /r4 184 index.ts:5:13 +mw index.ts:376:33 index.ts:410:16 GET /r4 185 index.ts:5:13 +mw index.ts:378:33 index.ts:410:16 GET /r4 186 index.ts:5:13 +mw index.ts:380:33 index.ts:410:16 GET /r4 187 index.ts:5:13 +mw index.ts:382:33 index.ts:410:16 GET /r4 188 index.ts:5:13 +mw index.ts:384:33 index.ts:410:16 GET /r4 189 index.ts:5:13 +mw index.ts:386:33 index.ts:410:16 GET /r4 190 index.ts:5:13 +mw index.ts:388:33 index.ts:410:16 GET /r4 191 index.ts:5:13 +mw index.ts:390:33 index.ts:410:16 GET /r4 192 index.ts:5:13 +mw index.ts:392:33 index.ts:410:16 GET /r4 193 index.ts:5:13 +mw index.ts:394:33 index.ts:410:16 GET /r4 194 index.ts:5:13 +mw index.ts:396:33 index.ts:410:16 GET /r4 195 index.ts:5:13 +mw index.ts:398:33 index.ts:410:16 GET /r4 196 index.ts:5:13 +mw index.ts:400:33 index.ts:410:16 GET /r4 197 index.ts:5:13 +mw index.ts:402:33 index.ts:410:16 GET /r4 198 index.ts:5:13 +mw index.ts:404:33 index.ts:410:16 GET /r4 199 index.ts:5:13 +mw index.ts:6:31 index.ts:411:16 GET /r5 0 index.ts:5:13 +mw index.ts:8:31 index.ts:411:16 GET /r5 1 index.ts:5:13 +mw index.ts:10:31 index.ts:411:16 GET /r5 2 index.ts:5:13 +mw index.ts:12:31 index.ts:411:16 GET /r5 3 index.ts:5:13 +mw index.ts:14:31 index.ts:411:16 GET /r5 4 index.ts:5:13 +mw index.ts:16:31 index.ts:411:16 GET /r5 5 index.ts:5:13 +mw index.ts:18:31 index.ts:411:16 GET /r5 6 index.ts:5:13 +mw index.ts:20:31 index.ts:411:16 GET /r5 7 index.ts:5:13 +mw index.ts:22:31 index.ts:411:16 GET /r5 8 index.ts:5:13 +mw index.ts:24:31 index.ts:411:16 GET /r5 9 index.ts:5:13 +mw index.ts:26:32 index.ts:411:16 GET /r5 10 index.ts:5:13 +mw index.ts:28:32 index.ts:411:16 GET /r5 11 index.ts:5:13 +mw index.ts:30:32 index.ts:411:16 GET /r5 12 index.ts:5:13 +mw index.ts:32:32 index.ts:411:16 GET /r5 13 index.ts:5:13 +mw index.ts:34:32 index.ts:411:16 GET /r5 14 index.ts:5:13 +mw index.ts:36:32 index.ts:411:16 GET /r5 15 index.ts:5:13 +mw index.ts:38:32 index.ts:411:16 GET /r5 16 index.ts:5:13 +mw index.ts:40:32 index.ts:411:16 GET /r5 17 index.ts:5:13 +mw index.ts:42:32 index.ts:411:16 GET /r5 18 index.ts:5:13 +mw index.ts:44:32 index.ts:411:16 GET /r5 19 index.ts:5:13 +mw index.ts:46:32 index.ts:411:16 GET /r5 20 index.ts:5:13 +mw index.ts:48:32 index.ts:411:16 GET /r5 21 index.ts:5:13 +mw index.ts:50:32 index.ts:411:16 GET /r5 22 index.ts:5:13 +mw index.ts:52:32 index.ts:411:16 GET /r5 23 index.ts:5:13 +mw index.ts:54:32 index.ts:411:16 GET /r5 24 index.ts:5:13 +mw index.ts:56:32 index.ts:411:16 GET /r5 25 index.ts:5:13 +mw index.ts:58:32 index.ts:411:16 GET /r5 26 index.ts:5:13 +mw index.ts:60:32 index.ts:411:16 GET /r5 27 index.ts:5:13 +mw index.ts:62:32 index.ts:411:16 GET /r5 28 index.ts:5:13 +mw index.ts:64:32 index.ts:411:16 GET /r5 29 index.ts:5:13 +mw index.ts:66:32 index.ts:411:16 GET /r5 30 index.ts:5:13 +mw index.ts:68:32 index.ts:411:16 GET /r5 31 index.ts:5:13 +mw index.ts:70:32 index.ts:411:16 GET /r5 32 index.ts:5:13 +mw index.ts:72:32 index.ts:411:16 GET /r5 33 index.ts:5:13 +mw index.ts:74:32 index.ts:411:16 GET /r5 34 index.ts:5:13 +mw index.ts:76:32 index.ts:411:16 GET /r5 35 index.ts:5:13 +mw index.ts:78:32 index.ts:411:16 GET /r5 36 index.ts:5:13 +mw index.ts:80:32 index.ts:411:16 GET /r5 37 index.ts:5:13 +mw index.ts:82:32 index.ts:411:16 GET /r5 38 index.ts:5:13 +mw index.ts:84:32 index.ts:411:16 GET /r5 39 index.ts:5:13 +mw index.ts:86:32 index.ts:411:16 GET /r5 40 index.ts:5:13 +mw index.ts:88:32 index.ts:411:16 GET /r5 41 index.ts:5:13 +mw index.ts:90:32 index.ts:411:16 GET /r5 42 index.ts:5:13 +mw index.ts:92:32 index.ts:411:16 GET /r5 43 index.ts:5:13 +mw index.ts:94:32 index.ts:411:16 GET /r5 44 index.ts:5:13 +mw index.ts:96:32 index.ts:411:16 GET /r5 45 index.ts:5:13 +mw index.ts:98:32 index.ts:411:16 GET /r5 46 index.ts:5:13 +mw index.ts:100:32 index.ts:411:16 GET /r5 47 index.ts:5:13 +mw index.ts:102:32 index.ts:411:16 GET /r5 48 index.ts:5:13 +mw index.ts:104:32 index.ts:411:16 GET /r5 49 index.ts:5:13 +mw index.ts:106:32 index.ts:411:16 GET /r5 50 index.ts:5:13 +mw index.ts:108:32 index.ts:411:16 GET /r5 51 index.ts:5:13 +mw index.ts:110:32 index.ts:411:16 GET /r5 52 index.ts:5:13 +mw index.ts:112:32 index.ts:411:16 GET /r5 53 index.ts:5:13 +mw index.ts:114:32 index.ts:411:16 GET /r5 54 index.ts:5:13 +mw index.ts:116:32 index.ts:411:16 GET /r5 55 index.ts:5:13 +mw index.ts:118:32 index.ts:411:16 GET /r5 56 index.ts:5:13 +mw index.ts:120:32 index.ts:411:16 GET /r5 57 index.ts:5:13 +mw index.ts:122:32 index.ts:411:16 GET /r5 58 index.ts:5:13 +mw index.ts:124:32 index.ts:411:16 GET /r5 59 index.ts:5:13 +mw index.ts:126:32 index.ts:411:16 GET /r5 60 index.ts:5:13 +mw index.ts:128:32 index.ts:411:16 GET /r5 61 index.ts:5:13 +mw index.ts:130:32 index.ts:411:16 GET /r5 62 index.ts:5:13 +mw index.ts:132:32 index.ts:411:16 GET /r5 63 index.ts:5:13 +mw index.ts:134:32 index.ts:411:16 GET /r5 64 index.ts:5:13 +mw index.ts:136:32 index.ts:411:16 GET /r5 65 index.ts:5:13 +mw index.ts:138:32 index.ts:411:16 GET /r5 66 index.ts:5:13 +mw index.ts:140:32 index.ts:411:16 GET /r5 67 index.ts:5:13 +mw index.ts:142:32 index.ts:411:16 GET /r5 68 index.ts:5:13 +mw index.ts:144:32 index.ts:411:16 GET /r5 69 index.ts:5:13 +mw index.ts:146:32 index.ts:411:16 GET /r5 70 index.ts:5:13 +mw index.ts:148:32 index.ts:411:16 GET /r5 71 index.ts:5:13 +mw index.ts:150:32 index.ts:411:16 GET /r5 72 index.ts:5:13 +mw index.ts:152:32 index.ts:411:16 GET /r5 73 index.ts:5:13 +mw index.ts:154:32 index.ts:411:16 GET /r5 74 index.ts:5:13 +mw index.ts:156:32 index.ts:411:16 GET /r5 75 index.ts:5:13 +mw index.ts:158:32 index.ts:411:16 GET /r5 76 index.ts:5:13 +mw index.ts:160:32 index.ts:411:16 GET /r5 77 index.ts:5:13 +mw index.ts:162:32 index.ts:411:16 GET /r5 78 index.ts:5:13 +mw index.ts:164:32 index.ts:411:16 GET /r5 79 index.ts:5:13 +mw index.ts:166:32 index.ts:411:16 GET /r5 80 index.ts:5:13 +mw index.ts:168:32 index.ts:411:16 GET /r5 81 index.ts:5:13 +mw index.ts:170:32 index.ts:411:16 GET /r5 82 index.ts:5:13 +mw index.ts:172:32 index.ts:411:16 GET /r5 83 index.ts:5:13 +mw index.ts:174:32 index.ts:411:16 GET /r5 84 index.ts:5:13 +mw index.ts:176:32 index.ts:411:16 GET /r5 85 index.ts:5:13 +mw index.ts:178:32 index.ts:411:16 GET /r5 86 index.ts:5:13 +mw index.ts:180:32 index.ts:411:16 GET /r5 87 index.ts:5:13 +mw index.ts:182:32 index.ts:411:16 GET /r5 88 index.ts:5:13 +mw index.ts:184:32 index.ts:411:16 GET /r5 89 index.ts:5:13 +mw index.ts:186:32 index.ts:411:16 GET /r5 90 index.ts:5:13 +mw index.ts:188:32 index.ts:411:16 GET /r5 91 index.ts:5:13 +mw index.ts:190:32 index.ts:411:16 GET /r5 92 index.ts:5:13 +mw index.ts:192:32 index.ts:411:16 GET /r5 93 index.ts:5:13 +mw index.ts:194:32 index.ts:411:16 GET /r5 94 index.ts:5:13 +mw index.ts:196:32 index.ts:411:16 GET /r5 95 index.ts:5:13 +mw index.ts:198:32 index.ts:411:16 GET /r5 96 index.ts:5:13 +mw index.ts:200:32 index.ts:411:16 GET /r5 97 index.ts:5:13 +mw index.ts:202:32 index.ts:411:16 GET /r5 98 index.ts:5:13 +mw index.ts:204:32 index.ts:411:16 GET /r5 99 index.ts:5:13 +mw index.ts:206:33 index.ts:411:16 GET /r5 100 index.ts:5:13 +mw index.ts:208:33 index.ts:411:16 GET /r5 101 index.ts:5:13 +mw index.ts:210:33 index.ts:411:16 GET /r5 102 index.ts:5:13 +mw index.ts:212:33 index.ts:411:16 GET /r5 103 index.ts:5:13 +mw index.ts:214:33 index.ts:411:16 GET /r5 104 index.ts:5:13 +mw index.ts:216:33 index.ts:411:16 GET /r5 105 index.ts:5:13 +mw index.ts:218:33 index.ts:411:16 GET /r5 106 index.ts:5:13 +mw index.ts:220:33 index.ts:411:16 GET /r5 107 index.ts:5:13 +mw index.ts:222:33 index.ts:411:16 GET /r5 108 index.ts:5:13 +mw index.ts:224:33 index.ts:411:16 GET /r5 109 index.ts:5:13 +mw index.ts:226:33 index.ts:411:16 GET /r5 110 index.ts:5:13 +mw index.ts:228:33 index.ts:411:16 GET /r5 111 index.ts:5:13 +mw index.ts:230:33 index.ts:411:16 GET /r5 112 index.ts:5:13 +mw index.ts:232:33 index.ts:411:16 GET /r5 113 index.ts:5:13 +mw index.ts:234:33 index.ts:411:16 GET /r5 114 index.ts:5:13 +mw index.ts:236:33 index.ts:411:16 GET /r5 115 index.ts:5:13 +mw index.ts:238:33 index.ts:411:16 GET /r5 116 index.ts:5:13 +mw index.ts:240:33 index.ts:411:16 GET /r5 117 index.ts:5:13 +mw index.ts:242:33 index.ts:411:16 GET /r5 118 index.ts:5:13 +mw index.ts:244:33 index.ts:411:16 GET /r5 119 index.ts:5:13 +mw index.ts:246:33 index.ts:411:16 GET /r5 120 index.ts:5:13 +mw index.ts:248:33 index.ts:411:16 GET /r5 121 index.ts:5:13 +mw index.ts:250:33 index.ts:411:16 GET /r5 122 index.ts:5:13 +mw index.ts:252:33 index.ts:411:16 GET /r5 123 index.ts:5:13 +mw index.ts:254:33 index.ts:411:16 GET /r5 124 index.ts:5:13 +mw index.ts:256:33 index.ts:411:16 GET /r5 125 index.ts:5:13 +mw index.ts:258:33 index.ts:411:16 GET /r5 126 index.ts:5:13 +mw index.ts:260:33 index.ts:411:16 GET /r5 127 index.ts:5:13 +mw index.ts:262:33 index.ts:411:16 GET /r5 128 index.ts:5:13 +mw index.ts:264:33 index.ts:411:16 GET /r5 129 index.ts:5:13 +mw index.ts:266:33 index.ts:411:16 GET /r5 130 index.ts:5:13 +mw index.ts:268:33 index.ts:411:16 GET /r5 131 index.ts:5:13 +mw index.ts:270:33 index.ts:411:16 GET /r5 132 index.ts:5:13 +mw index.ts:272:33 index.ts:411:16 GET /r5 133 index.ts:5:13 +mw index.ts:274:33 index.ts:411:16 GET /r5 134 index.ts:5:13 +mw index.ts:276:33 index.ts:411:16 GET /r5 135 index.ts:5:13 +mw index.ts:278:33 index.ts:411:16 GET /r5 136 index.ts:5:13 +mw index.ts:280:33 index.ts:411:16 GET /r5 137 index.ts:5:13 +mw index.ts:282:33 index.ts:411:16 GET /r5 138 index.ts:5:13 +mw index.ts:284:33 index.ts:411:16 GET /r5 139 index.ts:5:13 +mw index.ts:286:33 index.ts:411:16 GET /r5 140 index.ts:5:13 +mw index.ts:288:33 index.ts:411:16 GET /r5 141 index.ts:5:13 +mw index.ts:290:33 index.ts:411:16 GET /r5 142 index.ts:5:13 +mw index.ts:292:33 index.ts:411:16 GET /r5 143 index.ts:5:13 +mw index.ts:294:33 index.ts:411:16 GET /r5 144 index.ts:5:13 +mw index.ts:296:33 index.ts:411:16 GET /r5 145 index.ts:5:13 +mw index.ts:298:33 index.ts:411:16 GET /r5 146 index.ts:5:13 +mw index.ts:300:33 index.ts:411:16 GET /r5 147 index.ts:5:13 +mw index.ts:302:33 index.ts:411:16 GET /r5 148 index.ts:5:13 +mw index.ts:304:33 index.ts:411:16 GET /r5 149 index.ts:5:13 +mw index.ts:306:33 index.ts:411:16 GET /r5 150 index.ts:5:13 +mw index.ts:308:33 index.ts:411:16 GET /r5 151 index.ts:5:13 +mw index.ts:310:33 index.ts:411:16 GET /r5 152 index.ts:5:13 +mw index.ts:312:33 index.ts:411:16 GET /r5 153 index.ts:5:13 +mw index.ts:314:33 index.ts:411:16 GET /r5 154 index.ts:5:13 +mw index.ts:316:33 index.ts:411:16 GET /r5 155 index.ts:5:13 +mw index.ts:318:33 index.ts:411:16 GET /r5 156 index.ts:5:13 +mw index.ts:320:33 index.ts:411:16 GET /r5 157 index.ts:5:13 +mw index.ts:322:33 index.ts:411:16 GET /r5 158 index.ts:5:13 +mw index.ts:324:33 index.ts:411:16 GET /r5 159 index.ts:5:13 +mw index.ts:326:33 index.ts:411:16 GET /r5 160 index.ts:5:13 +mw index.ts:328:33 index.ts:411:16 GET /r5 161 index.ts:5:13 +mw index.ts:330:33 index.ts:411:16 GET /r5 162 index.ts:5:13 +mw index.ts:332:33 index.ts:411:16 GET /r5 163 index.ts:5:13 +mw index.ts:334:33 index.ts:411:16 GET /r5 164 index.ts:5:13 +mw index.ts:336:33 index.ts:411:16 GET /r5 165 index.ts:5:13 +mw index.ts:338:33 index.ts:411:16 GET /r5 166 index.ts:5:13 +mw index.ts:340:33 index.ts:411:16 GET /r5 167 index.ts:5:13 +mw index.ts:342:33 index.ts:411:16 GET /r5 168 index.ts:5:13 +mw index.ts:344:33 index.ts:411:16 GET /r5 169 index.ts:5:13 +mw index.ts:346:33 index.ts:411:16 GET /r5 170 index.ts:5:13 +mw index.ts:348:33 index.ts:411:16 GET /r5 171 index.ts:5:13 +mw index.ts:350:33 index.ts:411:16 GET /r5 172 index.ts:5:13 +mw index.ts:352:33 index.ts:411:16 GET /r5 173 index.ts:5:13 +mw index.ts:354:33 index.ts:411:16 GET /r5 174 index.ts:5:13 +mw index.ts:356:33 index.ts:411:16 GET /r5 175 index.ts:5:13 +mw index.ts:358:33 index.ts:411:16 GET /r5 176 index.ts:5:13 +mw index.ts:360:33 index.ts:411:16 GET /r5 177 index.ts:5:13 +mw index.ts:362:33 index.ts:411:16 GET /r5 178 index.ts:5:13 +mw index.ts:364:33 index.ts:411:16 GET /r5 179 index.ts:5:13 +mw index.ts:366:33 index.ts:411:16 GET /r5 180 index.ts:5:13 +mw index.ts:368:33 index.ts:411:16 GET /r5 181 index.ts:5:13 +mw index.ts:370:33 index.ts:411:16 GET /r5 182 index.ts:5:13 +mw index.ts:372:33 index.ts:411:16 GET /r5 183 index.ts:5:13 +mw index.ts:374:33 index.ts:411:16 GET /r5 184 index.ts:5:13 +mw index.ts:376:33 index.ts:411:16 GET /r5 185 index.ts:5:13 +mw index.ts:378:33 index.ts:411:16 GET /r5 186 index.ts:5:13 +mw index.ts:380:33 index.ts:411:16 GET /r5 187 index.ts:5:13 +mw index.ts:382:33 index.ts:411:16 GET /r5 188 index.ts:5:13 +mw index.ts:384:33 index.ts:411:16 GET /r5 189 index.ts:5:13 +mw index.ts:386:33 index.ts:411:16 GET /r5 190 index.ts:5:13 +mw index.ts:388:33 index.ts:411:16 GET /r5 191 index.ts:5:13 +mw index.ts:390:33 index.ts:411:16 GET /r5 192 index.ts:5:13 +mw index.ts:392:33 index.ts:411:16 GET /r5 193 index.ts:5:13 +mw index.ts:394:33 index.ts:411:16 GET /r5 194 index.ts:5:13 +mw index.ts:396:33 index.ts:411:16 GET /r5 195 index.ts:5:13 +mw index.ts:398:33 index.ts:411:16 GET /r5 196 index.ts:5:13 +mw index.ts:400:33 index.ts:411:16 GET /r5 197 index.ts:5:13 +mw index.ts:402:33 index.ts:411:16 GET /r5 198 index.ts:5:13 +mw index.ts:404:33 index.ts:411:16 GET /r5 199 index.ts:5:13 +mw index.ts:6:31 index.ts:412:16 GET /r6 0 index.ts:5:13 +mw index.ts:8:31 index.ts:412:16 GET /r6 1 index.ts:5:13 +mw index.ts:10:31 index.ts:412:16 GET /r6 2 index.ts:5:13 +mw index.ts:12:31 index.ts:412:16 GET /r6 3 index.ts:5:13 +mw index.ts:14:31 index.ts:412:16 GET /r6 4 index.ts:5:13 +mw index.ts:16:31 index.ts:412:16 GET /r6 5 index.ts:5:13 +mw index.ts:18:31 index.ts:412:16 GET /r6 6 index.ts:5:13 +mw index.ts:20:31 index.ts:412:16 GET /r6 7 index.ts:5:13 +mw index.ts:22:31 index.ts:412:16 GET /r6 8 index.ts:5:13 +mw index.ts:24:31 index.ts:412:16 GET /r6 9 index.ts:5:13 +mw index.ts:26:32 index.ts:412:16 GET /r6 10 index.ts:5:13 +mw index.ts:28:32 index.ts:412:16 GET /r6 11 index.ts:5:13 +mw index.ts:30:32 index.ts:412:16 GET /r6 12 index.ts:5:13 +mw index.ts:32:32 index.ts:412:16 GET /r6 13 index.ts:5:13 +mw index.ts:34:32 index.ts:412:16 GET /r6 14 index.ts:5:13 +mw index.ts:36:32 index.ts:412:16 GET /r6 15 index.ts:5:13 +mw index.ts:38:32 index.ts:412:16 GET /r6 16 index.ts:5:13 +mw index.ts:40:32 index.ts:412:16 GET /r6 17 index.ts:5:13 +mw index.ts:42:32 index.ts:412:16 GET /r6 18 index.ts:5:13 +mw index.ts:44:32 index.ts:412:16 GET /r6 19 index.ts:5:13 +mw index.ts:46:32 index.ts:412:16 GET /r6 20 index.ts:5:13 +mw index.ts:48:32 index.ts:412:16 GET /r6 21 index.ts:5:13 +mw index.ts:50:32 index.ts:412:16 GET /r6 22 index.ts:5:13 +mw index.ts:52:32 index.ts:412:16 GET /r6 23 index.ts:5:13 +mw index.ts:54:32 index.ts:412:16 GET /r6 24 index.ts:5:13 +mw index.ts:56:32 index.ts:412:16 GET /r6 25 index.ts:5:13 +mw index.ts:58:32 index.ts:412:16 GET /r6 26 index.ts:5:13 +mw index.ts:60:32 index.ts:412:16 GET /r6 27 index.ts:5:13 +mw index.ts:62:32 index.ts:412:16 GET /r6 28 index.ts:5:13 +mw index.ts:64:32 index.ts:412:16 GET /r6 29 index.ts:5:13 +mw index.ts:66:32 index.ts:412:16 GET /r6 30 index.ts:5:13 +mw index.ts:68:32 index.ts:412:16 GET /r6 31 index.ts:5:13 +mw index.ts:70:32 index.ts:412:16 GET /r6 32 index.ts:5:13 +mw index.ts:72:32 index.ts:412:16 GET /r6 33 index.ts:5:13 +mw index.ts:74:32 index.ts:412:16 GET /r6 34 index.ts:5:13 +mw index.ts:76:32 index.ts:412:16 GET /r6 35 index.ts:5:13 +mw index.ts:78:32 index.ts:412:16 GET /r6 36 index.ts:5:13 +mw index.ts:80:32 index.ts:412:16 GET /r6 37 index.ts:5:13 +mw index.ts:82:32 index.ts:412:16 GET /r6 38 index.ts:5:13 +mw index.ts:84:32 index.ts:412:16 GET /r6 39 index.ts:5:13 +mw index.ts:86:32 index.ts:412:16 GET /r6 40 index.ts:5:13 +mw index.ts:88:32 index.ts:412:16 GET /r6 41 index.ts:5:13 +mw index.ts:90:32 index.ts:412:16 GET /r6 42 index.ts:5:13 +mw index.ts:92:32 index.ts:412:16 GET /r6 43 index.ts:5:13 +mw index.ts:94:32 index.ts:412:16 GET /r6 44 index.ts:5:13 +mw index.ts:96:32 index.ts:412:16 GET /r6 45 index.ts:5:13 +mw index.ts:98:32 index.ts:412:16 GET /r6 46 index.ts:5:13 +mw index.ts:100:32 index.ts:412:16 GET /r6 47 index.ts:5:13 +mw index.ts:102:32 index.ts:412:16 GET /r6 48 index.ts:5:13 +mw index.ts:104:32 index.ts:412:16 GET /r6 49 index.ts:5:13 +mw index.ts:106:32 index.ts:412:16 GET /r6 50 index.ts:5:13 +mw index.ts:108:32 index.ts:412:16 GET /r6 51 index.ts:5:13 +mw index.ts:110:32 index.ts:412:16 GET /r6 52 index.ts:5:13 +mw index.ts:112:32 index.ts:412:16 GET /r6 53 index.ts:5:13 +mw index.ts:114:32 index.ts:412:16 GET /r6 54 index.ts:5:13 +mw index.ts:116:32 index.ts:412:16 GET /r6 55 index.ts:5:13 +mw index.ts:118:32 index.ts:412:16 GET /r6 56 index.ts:5:13 +mw index.ts:120:32 index.ts:412:16 GET /r6 57 index.ts:5:13 +mw index.ts:122:32 index.ts:412:16 GET /r6 58 index.ts:5:13 +mw index.ts:124:32 index.ts:412:16 GET /r6 59 index.ts:5:13 +mw index.ts:126:32 index.ts:412:16 GET /r6 60 index.ts:5:13 +mw index.ts:128:32 index.ts:412:16 GET /r6 61 index.ts:5:13 +mw index.ts:130:32 index.ts:412:16 GET /r6 62 index.ts:5:13 +mw index.ts:132:32 index.ts:412:16 GET /r6 63 index.ts:5:13 +mw index.ts:134:32 index.ts:412:16 GET /r6 64 index.ts:5:13 +mw index.ts:136:32 index.ts:412:16 GET /r6 65 index.ts:5:13 +mw index.ts:138:32 index.ts:412:16 GET /r6 66 index.ts:5:13 +mw index.ts:140:32 index.ts:412:16 GET /r6 67 index.ts:5:13 +mw index.ts:142:32 index.ts:412:16 GET /r6 68 index.ts:5:13 +mw index.ts:144:32 index.ts:412:16 GET /r6 69 index.ts:5:13 +mw index.ts:146:32 index.ts:412:16 GET /r6 70 index.ts:5:13 +mw index.ts:148:32 index.ts:412:16 GET /r6 71 index.ts:5:13 +mw index.ts:150:32 index.ts:412:16 GET /r6 72 index.ts:5:13 +mw index.ts:152:32 index.ts:412:16 GET /r6 73 index.ts:5:13 +mw index.ts:154:32 index.ts:412:16 GET /r6 74 index.ts:5:13 +mw index.ts:156:32 index.ts:412:16 GET /r6 75 index.ts:5:13 +mw index.ts:158:32 index.ts:412:16 GET /r6 76 index.ts:5:13 +mw index.ts:160:32 index.ts:412:16 GET /r6 77 index.ts:5:13 +mw index.ts:162:32 index.ts:412:16 GET /r6 78 index.ts:5:13 +mw index.ts:164:32 index.ts:412:16 GET /r6 79 index.ts:5:13 +mw index.ts:166:32 index.ts:412:16 GET /r6 80 index.ts:5:13 +mw index.ts:168:32 index.ts:412:16 GET /r6 81 index.ts:5:13 +mw index.ts:170:32 index.ts:412:16 GET /r6 82 index.ts:5:13 +mw index.ts:172:32 index.ts:412:16 GET /r6 83 index.ts:5:13 +mw index.ts:174:32 index.ts:412:16 GET /r6 84 index.ts:5:13 +mw index.ts:176:32 index.ts:412:16 GET /r6 85 index.ts:5:13 +mw index.ts:178:32 index.ts:412:16 GET /r6 86 index.ts:5:13 +mw index.ts:180:32 index.ts:412:16 GET /r6 87 index.ts:5:13 +mw index.ts:182:32 index.ts:412:16 GET /r6 88 index.ts:5:13 +mw index.ts:184:32 index.ts:412:16 GET /r6 89 index.ts:5:13 +mw index.ts:186:32 index.ts:412:16 GET /r6 90 index.ts:5:13 +mw index.ts:188:32 index.ts:412:16 GET /r6 91 index.ts:5:13 +mw index.ts:190:32 index.ts:412:16 GET /r6 92 index.ts:5:13 +mw index.ts:192:32 index.ts:412:16 GET /r6 93 index.ts:5:13 +mw index.ts:194:32 index.ts:412:16 GET /r6 94 index.ts:5:13 +mw index.ts:196:32 index.ts:412:16 GET /r6 95 index.ts:5:13 +mw index.ts:198:32 index.ts:412:16 GET /r6 96 index.ts:5:13 +mw index.ts:200:32 index.ts:412:16 GET /r6 97 index.ts:5:13 +mw index.ts:202:32 index.ts:412:16 GET /r6 98 index.ts:5:13 +mw index.ts:204:32 index.ts:412:16 GET /r6 99 index.ts:5:13 +mw index.ts:206:33 index.ts:412:16 GET /r6 100 index.ts:5:13 +mw index.ts:208:33 index.ts:412:16 GET /r6 101 index.ts:5:13 +mw index.ts:210:33 index.ts:412:16 GET /r6 102 index.ts:5:13 +mw index.ts:212:33 index.ts:412:16 GET /r6 103 index.ts:5:13 +mw index.ts:214:33 index.ts:412:16 GET /r6 104 index.ts:5:13 +mw index.ts:216:33 index.ts:412:16 GET /r6 105 index.ts:5:13 +mw index.ts:218:33 index.ts:412:16 GET /r6 106 index.ts:5:13 +mw index.ts:220:33 index.ts:412:16 GET /r6 107 index.ts:5:13 +mw index.ts:222:33 index.ts:412:16 GET /r6 108 index.ts:5:13 +mw index.ts:224:33 index.ts:412:16 GET /r6 109 index.ts:5:13 +mw index.ts:226:33 index.ts:412:16 GET /r6 110 index.ts:5:13 +mw index.ts:228:33 index.ts:412:16 GET /r6 111 index.ts:5:13 +mw index.ts:230:33 index.ts:412:16 GET /r6 112 index.ts:5:13 +mw index.ts:232:33 index.ts:412:16 GET /r6 113 index.ts:5:13 +mw index.ts:234:33 index.ts:412:16 GET /r6 114 index.ts:5:13 +mw index.ts:236:33 index.ts:412:16 GET /r6 115 index.ts:5:13 +mw index.ts:238:33 index.ts:412:16 GET /r6 116 index.ts:5:13 +mw index.ts:240:33 index.ts:412:16 GET /r6 117 index.ts:5:13 +mw index.ts:242:33 index.ts:412:16 GET /r6 118 index.ts:5:13 +mw index.ts:244:33 index.ts:412:16 GET /r6 119 index.ts:5:13 +mw index.ts:246:33 index.ts:412:16 GET /r6 120 index.ts:5:13 +mw index.ts:248:33 index.ts:412:16 GET /r6 121 index.ts:5:13 +mw index.ts:250:33 index.ts:412:16 GET /r6 122 index.ts:5:13 +mw index.ts:252:33 index.ts:412:16 GET /r6 123 index.ts:5:13 +mw index.ts:254:33 index.ts:412:16 GET /r6 124 index.ts:5:13 +mw index.ts:256:33 index.ts:412:16 GET /r6 125 index.ts:5:13 +mw index.ts:258:33 index.ts:412:16 GET /r6 126 index.ts:5:13 +mw index.ts:260:33 index.ts:412:16 GET /r6 127 index.ts:5:13 +mw index.ts:262:33 index.ts:412:16 GET /r6 128 index.ts:5:13 +mw index.ts:264:33 index.ts:412:16 GET /r6 129 index.ts:5:13 +mw index.ts:266:33 index.ts:412:16 GET /r6 130 index.ts:5:13 +mw index.ts:268:33 index.ts:412:16 GET /r6 131 index.ts:5:13 +mw index.ts:270:33 index.ts:412:16 GET /r6 132 index.ts:5:13 +mw index.ts:272:33 index.ts:412:16 GET /r6 133 index.ts:5:13 +mw index.ts:274:33 index.ts:412:16 GET /r6 134 index.ts:5:13 +mw index.ts:276:33 index.ts:412:16 GET /r6 135 index.ts:5:13 +mw index.ts:278:33 index.ts:412:16 GET /r6 136 index.ts:5:13 +mw index.ts:280:33 index.ts:412:16 GET /r6 137 index.ts:5:13 +mw index.ts:282:33 index.ts:412:16 GET /r6 138 index.ts:5:13 +mw index.ts:284:33 index.ts:412:16 GET /r6 139 index.ts:5:13 +mw index.ts:286:33 index.ts:412:16 GET /r6 140 index.ts:5:13 +mw index.ts:288:33 index.ts:412:16 GET /r6 141 index.ts:5:13 +mw index.ts:290:33 index.ts:412:16 GET /r6 142 index.ts:5:13 +mw index.ts:292:33 index.ts:412:16 GET /r6 143 index.ts:5:13 +mw index.ts:294:33 index.ts:412:16 GET /r6 144 index.ts:5:13 +mw index.ts:296:33 index.ts:412:16 GET /r6 145 index.ts:5:13 +mw index.ts:298:33 index.ts:412:16 GET /r6 146 index.ts:5:13 +mw index.ts:300:33 index.ts:412:16 GET /r6 147 index.ts:5:13 +mw index.ts:302:33 index.ts:412:16 GET /r6 148 index.ts:5:13 +mw index.ts:304:33 index.ts:412:16 GET /r6 149 index.ts:5:13 +mw index.ts:306:33 index.ts:412:16 GET /r6 150 index.ts:5:13 +mw index.ts:308:33 index.ts:412:16 GET /r6 151 index.ts:5:13 +mw index.ts:310:33 index.ts:412:16 GET /r6 152 index.ts:5:13 +mw index.ts:312:33 index.ts:412:16 GET /r6 153 index.ts:5:13 +mw index.ts:314:33 index.ts:412:16 GET /r6 154 index.ts:5:13 +mw index.ts:316:33 index.ts:412:16 GET /r6 155 index.ts:5:13 +mw index.ts:318:33 index.ts:412:16 GET /r6 156 index.ts:5:13 +mw index.ts:320:33 index.ts:412:16 GET /r6 157 index.ts:5:13 +mw index.ts:322:33 index.ts:412:16 GET /r6 158 index.ts:5:13 +mw index.ts:324:33 index.ts:412:16 GET /r6 159 index.ts:5:13 +mw index.ts:326:33 index.ts:412:16 GET /r6 160 index.ts:5:13 +mw index.ts:328:33 index.ts:412:16 GET /r6 161 index.ts:5:13 +mw index.ts:330:33 index.ts:412:16 GET /r6 162 index.ts:5:13 +mw index.ts:332:33 index.ts:412:16 GET /r6 163 index.ts:5:13 +mw index.ts:334:33 index.ts:412:16 GET /r6 164 index.ts:5:13 +mw index.ts:336:33 index.ts:412:16 GET /r6 165 index.ts:5:13 +mw index.ts:338:33 index.ts:412:16 GET /r6 166 index.ts:5:13 +mw index.ts:340:33 index.ts:412:16 GET /r6 167 index.ts:5:13 +mw index.ts:342:33 index.ts:412:16 GET /r6 168 index.ts:5:13 +mw index.ts:344:33 index.ts:412:16 GET /r6 169 index.ts:5:13 +mw index.ts:346:33 index.ts:412:16 GET /r6 170 index.ts:5:13 +mw index.ts:348:33 index.ts:412:16 GET /r6 171 index.ts:5:13 +mw index.ts:350:33 index.ts:412:16 GET /r6 172 index.ts:5:13 +mw index.ts:352:33 index.ts:412:16 GET /r6 173 index.ts:5:13 +mw index.ts:354:33 index.ts:412:16 GET /r6 174 index.ts:5:13 +mw index.ts:356:33 index.ts:412:16 GET /r6 175 index.ts:5:13 +mw index.ts:358:33 index.ts:412:16 GET /r6 176 index.ts:5:13 +mw index.ts:360:33 index.ts:412:16 GET /r6 177 index.ts:5:13 +mw index.ts:362:33 index.ts:412:16 GET /r6 178 index.ts:5:13 +mw index.ts:364:33 index.ts:412:16 GET /r6 179 index.ts:5:13 +mw index.ts:366:33 index.ts:412:16 GET /r6 180 index.ts:5:13 +mw index.ts:368:33 index.ts:412:16 GET /r6 181 index.ts:5:13 +mw index.ts:370:33 index.ts:412:16 GET /r6 182 index.ts:5:13 +mw index.ts:372:33 index.ts:412:16 GET /r6 183 index.ts:5:13 +mw index.ts:374:33 index.ts:412:16 GET /r6 184 index.ts:5:13 +mw index.ts:376:33 index.ts:412:16 GET /r6 185 index.ts:5:13 +mw index.ts:378:33 index.ts:412:16 GET /r6 186 index.ts:5:13 +mw index.ts:380:33 index.ts:412:16 GET /r6 187 index.ts:5:13 +mw index.ts:382:33 index.ts:412:16 GET /r6 188 index.ts:5:13 +mw index.ts:384:33 index.ts:412:16 GET /r6 189 index.ts:5:13 +mw index.ts:386:33 index.ts:412:16 GET /r6 190 index.ts:5:13 +mw index.ts:388:33 index.ts:412:16 GET /r6 191 index.ts:5:13 +mw index.ts:390:33 index.ts:412:16 GET /r6 192 index.ts:5:13 +mw index.ts:392:33 index.ts:412:16 GET /r6 193 index.ts:5:13 +mw index.ts:394:33 index.ts:412:16 GET /r6 194 index.ts:5:13 +mw index.ts:396:33 index.ts:412:16 GET /r6 195 index.ts:5:13 +mw index.ts:398:33 index.ts:412:16 GET /r6 196 index.ts:5:13 +mw index.ts:400:33 index.ts:412:16 GET /r6 197 index.ts:5:13 +mw index.ts:402:33 index.ts:412:16 GET /r6 198 index.ts:5:13 +mw index.ts:404:33 index.ts:412:16 GET /r6 199 index.ts:5:13 +mw index.ts:6:31 index.ts:413:16 GET /r7 0 index.ts:5:13 +mw index.ts:8:31 index.ts:413:16 GET /r7 1 index.ts:5:13 +mw index.ts:10:31 index.ts:413:16 GET /r7 2 index.ts:5:13 +mw index.ts:12:31 index.ts:413:16 GET /r7 3 index.ts:5:13 +mw index.ts:14:31 index.ts:413:16 GET /r7 4 index.ts:5:13 +mw index.ts:16:31 index.ts:413:16 GET /r7 5 index.ts:5:13 +mw index.ts:18:31 index.ts:413:16 GET /r7 6 index.ts:5:13 +mw index.ts:20:31 index.ts:413:16 GET /r7 7 index.ts:5:13 +mw index.ts:22:31 index.ts:413:16 GET /r7 8 index.ts:5:13 +mw index.ts:24:31 index.ts:413:16 GET /r7 9 index.ts:5:13 +mw index.ts:26:32 index.ts:413:16 GET /r7 10 index.ts:5:13 +mw index.ts:28:32 index.ts:413:16 GET /r7 11 index.ts:5:13 +mw index.ts:30:32 index.ts:413:16 GET /r7 12 index.ts:5:13 +mw index.ts:32:32 index.ts:413:16 GET /r7 13 index.ts:5:13 +mw index.ts:34:32 index.ts:413:16 GET /r7 14 index.ts:5:13 +mw index.ts:36:32 index.ts:413:16 GET /r7 15 index.ts:5:13 +mw index.ts:38:32 index.ts:413:16 GET /r7 16 index.ts:5:13 +mw index.ts:40:32 index.ts:413:16 GET /r7 17 index.ts:5:13 +mw index.ts:42:32 index.ts:413:16 GET /r7 18 index.ts:5:13 +mw index.ts:44:32 index.ts:413:16 GET /r7 19 index.ts:5:13 +mw index.ts:46:32 index.ts:413:16 GET /r7 20 index.ts:5:13 +mw index.ts:48:32 index.ts:413:16 GET /r7 21 index.ts:5:13 +mw index.ts:50:32 index.ts:413:16 GET /r7 22 index.ts:5:13 +mw index.ts:52:32 index.ts:413:16 GET /r7 23 index.ts:5:13 +mw index.ts:54:32 index.ts:413:16 GET /r7 24 index.ts:5:13 +mw index.ts:56:32 index.ts:413:16 GET /r7 25 index.ts:5:13 +mw index.ts:58:32 index.ts:413:16 GET /r7 26 index.ts:5:13 +mw index.ts:60:32 index.ts:413:16 GET /r7 27 index.ts:5:13 +mw index.ts:62:32 index.ts:413:16 GET /r7 28 index.ts:5:13 +mw index.ts:64:32 index.ts:413:16 GET /r7 29 index.ts:5:13 +mw index.ts:66:32 index.ts:413:16 GET /r7 30 index.ts:5:13 +mw index.ts:68:32 index.ts:413:16 GET /r7 31 index.ts:5:13 +mw index.ts:70:32 index.ts:413:16 GET /r7 32 index.ts:5:13 +mw index.ts:72:32 index.ts:413:16 GET /r7 33 index.ts:5:13 +mw index.ts:74:32 index.ts:413:16 GET /r7 34 index.ts:5:13 +mw index.ts:76:32 index.ts:413:16 GET /r7 35 index.ts:5:13 +mw index.ts:78:32 index.ts:413:16 GET /r7 36 index.ts:5:13 +mw index.ts:80:32 index.ts:413:16 GET /r7 37 index.ts:5:13 +mw index.ts:82:32 index.ts:413:16 GET /r7 38 index.ts:5:13 +mw index.ts:84:32 index.ts:413:16 GET /r7 39 index.ts:5:13 +mw index.ts:86:32 index.ts:413:16 GET /r7 40 index.ts:5:13 +mw index.ts:88:32 index.ts:413:16 GET /r7 41 index.ts:5:13 +mw index.ts:90:32 index.ts:413:16 GET /r7 42 index.ts:5:13 +mw index.ts:92:32 index.ts:413:16 GET /r7 43 index.ts:5:13 +mw index.ts:94:32 index.ts:413:16 GET /r7 44 index.ts:5:13 +mw index.ts:96:32 index.ts:413:16 GET /r7 45 index.ts:5:13 +mw index.ts:98:32 index.ts:413:16 GET /r7 46 index.ts:5:13 +mw index.ts:100:32 index.ts:413:16 GET /r7 47 index.ts:5:13 +mw index.ts:102:32 index.ts:413:16 GET /r7 48 index.ts:5:13 +mw index.ts:104:32 index.ts:413:16 GET /r7 49 index.ts:5:13 +mw index.ts:106:32 index.ts:413:16 GET /r7 50 index.ts:5:13 +mw index.ts:108:32 index.ts:413:16 GET /r7 51 index.ts:5:13 +mw index.ts:110:32 index.ts:413:16 GET /r7 52 index.ts:5:13 +mw index.ts:112:32 index.ts:413:16 GET /r7 53 index.ts:5:13 +mw index.ts:114:32 index.ts:413:16 GET /r7 54 index.ts:5:13 +mw index.ts:116:32 index.ts:413:16 GET /r7 55 index.ts:5:13 +mw index.ts:118:32 index.ts:413:16 GET /r7 56 index.ts:5:13 +mw index.ts:120:32 index.ts:413:16 GET /r7 57 index.ts:5:13 +mw index.ts:122:32 index.ts:413:16 GET /r7 58 index.ts:5:13 +mw index.ts:124:32 index.ts:413:16 GET /r7 59 index.ts:5:13 +mw index.ts:126:32 index.ts:413:16 GET /r7 60 index.ts:5:13 +mw index.ts:128:32 index.ts:413:16 GET /r7 61 index.ts:5:13 +mw index.ts:130:32 index.ts:413:16 GET /r7 62 index.ts:5:13 +mw index.ts:132:32 index.ts:413:16 GET /r7 63 index.ts:5:13 +mw index.ts:134:32 index.ts:413:16 GET /r7 64 index.ts:5:13 +mw index.ts:136:32 index.ts:413:16 GET /r7 65 index.ts:5:13 +mw index.ts:138:32 index.ts:413:16 GET /r7 66 index.ts:5:13 +mw index.ts:140:32 index.ts:413:16 GET /r7 67 index.ts:5:13 +mw index.ts:142:32 index.ts:413:16 GET /r7 68 index.ts:5:13 +mw index.ts:144:32 index.ts:413:16 GET /r7 69 index.ts:5:13 +mw index.ts:146:32 index.ts:413:16 GET /r7 70 index.ts:5:13 +mw index.ts:148:32 index.ts:413:16 GET /r7 71 index.ts:5:13 +mw index.ts:150:32 index.ts:413:16 GET /r7 72 index.ts:5:13 +mw index.ts:152:32 index.ts:413:16 GET /r7 73 index.ts:5:13 +mw index.ts:154:32 index.ts:413:16 GET /r7 74 index.ts:5:13 +mw index.ts:156:32 index.ts:413:16 GET /r7 75 index.ts:5:13 +mw index.ts:158:32 index.ts:413:16 GET /r7 76 index.ts:5:13 +mw index.ts:160:32 index.ts:413:16 GET /r7 77 index.ts:5:13 +mw index.ts:162:32 index.ts:413:16 GET /r7 78 index.ts:5:13 +mw index.ts:164:32 index.ts:413:16 GET /r7 79 index.ts:5:13 +mw index.ts:166:32 index.ts:413:16 GET /r7 80 index.ts:5:13 +mw index.ts:168:32 index.ts:413:16 GET /r7 81 index.ts:5:13 +mw index.ts:170:32 index.ts:413:16 GET /r7 82 index.ts:5:13 +mw index.ts:172:32 index.ts:413:16 GET /r7 83 index.ts:5:13 +mw index.ts:174:32 index.ts:413:16 GET /r7 84 index.ts:5:13 +mw index.ts:176:32 index.ts:413:16 GET /r7 85 index.ts:5:13 +mw index.ts:178:32 index.ts:413:16 GET /r7 86 index.ts:5:13 +mw index.ts:180:32 index.ts:413:16 GET /r7 87 index.ts:5:13 +mw index.ts:182:32 index.ts:413:16 GET /r7 88 index.ts:5:13 +mw index.ts:184:32 index.ts:413:16 GET /r7 89 index.ts:5:13 +mw index.ts:186:32 index.ts:413:16 GET /r7 90 index.ts:5:13 +mw index.ts:188:32 index.ts:413:16 GET /r7 91 index.ts:5:13 +mw index.ts:190:32 index.ts:413:16 GET /r7 92 index.ts:5:13 +mw index.ts:192:32 index.ts:413:16 GET /r7 93 index.ts:5:13 +mw index.ts:194:32 index.ts:413:16 GET /r7 94 index.ts:5:13 +mw index.ts:196:32 index.ts:413:16 GET /r7 95 index.ts:5:13 +mw index.ts:198:32 index.ts:413:16 GET /r7 96 index.ts:5:13 +mw index.ts:200:32 index.ts:413:16 GET /r7 97 index.ts:5:13 +mw index.ts:202:32 index.ts:413:16 GET /r7 98 index.ts:5:13 +mw index.ts:204:32 index.ts:413:16 GET /r7 99 index.ts:5:13 +mw index.ts:206:33 index.ts:413:16 GET /r7 100 index.ts:5:13 +mw index.ts:208:33 index.ts:413:16 GET /r7 101 index.ts:5:13 +mw index.ts:210:33 index.ts:413:16 GET /r7 102 index.ts:5:13 +mw index.ts:212:33 index.ts:413:16 GET /r7 103 index.ts:5:13 +mw index.ts:214:33 index.ts:413:16 GET /r7 104 index.ts:5:13 +mw index.ts:216:33 index.ts:413:16 GET /r7 105 index.ts:5:13 +mw index.ts:218:33 index.ts:413:16 GET /r7 106 index.ts:5:13 +mw index.ts:220:33 index.ts:413:16 GET /r7 107 index.ts:5:13 +mw index.ts:222:33 index.ts:413:16 GET /r7 108 index.ts:5:13 +mw index.ts:224:33 index.ts:413:16 GET /r7 109 index.ts:5:13 +mw index.ts:226:33 index.ts:413:16 GET /r7 110 index.ts:5:13 +mw index.ts:228:33 index.ts:413:16 GET /r7 111 index.ts:5:13 +mw index.ts:230:33 index.ts:413:16 GET /r7 112 index.ts:5:13 +mw index.ts:232:33 index.ts:413:16 GET /r7 113 index.ts:5:13 +mw index.ts:234:33 index.ts:413:16 GET /r7 114 index.ts:5:13 +mw index.ts:236:33 index.ts:413:16 GET /r7 115 index.ts:5:13 +mw index.ts:238:33 index.ts:413:16 GET /r7 116 index.ts:5:13 +mw index.ts:240:33 index.ts:413:16 GET /r7 117 index.ts:5:13 +mw index.ts:242:33 index.ts:413:16 GET /r7 118 index.ts:5:13 +mw index.ts:244:33 index.ts:413:16 GET /r7 119 index.ts:5:13 +mw index.ts:246:33 index.ts:413:16 GET /r7 120 index.ts:5:13 +mw index.ts:248:33 index.ts:413:16 GET /r7 121 index.ts:5:13 +mw index.ts:250:33 index.ts:413:16 GET /r7 122 index.ts:5:13 +mw index.ts:252:33 index.ts:413:16 GET /r7 123 index.ts:5:13 +mw index.ts:254:33 index.ts:413:16 GET /r7 124 index.ts:5:13 +mw index.ts:256:33 index.ts:413:16 GET /r7 125 index.ts:5:13 +mw index.ts:258:33 index.ts:413:16 GET /r7 126 index.ts:5:13 +mw index.ts:260:33 index.ts:413:16 GET /r7 127 index.ts:5:13 +mw index.ts:262:33 index.ts:413:16 GET /r7 128 index.ts:5:13 +mw index.ts:264:33 index.ts:413:16 GET /r7 129 index.ts:5:13 +mw index.ts:266:33 index.ts:413:16 GET /r7 130 index.ts:5:13 +mw index.ts:268:33 index.ts:413:16 GET /r7 131 index.ts:5:13 +mw index.ts:270:33 index.ts:413:16 GET /r7 132 index.ts:5:13 +mw index.ts:272:33 index.ts:413:16 GET /r7 133 index.ts:5:13 +mw index.ts:274:33 index.ts:413:16 GET /r7 134 index.ts:5:13 +mw index.ts:276:33 index.ts:413:16 GET /r7 135 index.ts:5:13 +mw index.ts:278:33 index.ts:413:16 GET /r7 136 index.ts:5:13 +mw index.ts:280:33 index.ts:413:16 GET /r7 137 index.ts:5:13 +mw index.ts:282:33 index.ts:413:16 GET /r7 138 index.ts:5:13 +mw index.ts:284:33 index.ts:413:16 GET /r7 139 index.ts:5:13 +mw index.ts:286:33 index.ts:413:16 GET /r7 140 index.ts:5:13 +mw index.ts:288:33 index.ts:413:16 GET /r7 141 index.ts:5:13 +mw index.ts:290:33 index.ts:413:16 GET /r7 142 index.ts:5:13 +mw index.ts:292:33 index.ts:413:16 GET /r7 143 index.ts:5:13 +mw index.ts:294:33 index.ts:413:16 GET /r7 144 index.ts:5:13 +mw index.ts:296:33 index.ts:413:16 GET /r7 145 index.ts:5:13 +mw index.ts:298:33 index.ts:413:16 GET /r7 146 index.ts:5:13 +mw index.ts:300:33 index.ts:413:16 GET /r7 147 index.ts:5:13 +mw index.ts:302:33 index.ts:413:16 GET /r7 148 index.ts:5:13 +mw index.ts:304:33 index.ts:413:16 GET /r7 149 index.ts:5:13 +mw index.ts:306:33 index.ts:413:16 GET /r7 150 index.ts:5:13 +mw index.ts:308:33 index.ts:413:16 GET /r7 151 index.ts:5:13 +mw index.ts:310:33 index.ts:413:16 GET /r7 152 index.ts:5:13 +mw index.ts:312:33 index.ts:413:16 GET /r7 153 index.ts:5:13 +mw index.ts:314:33 index.ts:413:16 GET /r7 154 index.ts:5:13 +mw index.ts:316:33 index.ts:413:16 GET /r7 155 index.ts:5:13 +mw index.ts:318:33 index.ts:413:16 GET /r7 156 index.ts:5:13 +mw index.ts:320:33 index.ts:413:16 GET /r7 157 index.ts:5:13 +mw index.ts:322:33 index.ts:413:16 GET /r7 158 index.ts:5:13 +mw index.ts:324:33 index.ts:413:16 GET /r7 159 index.ts:5:13 +mw index.ts:326:33 index.ts:413:16 GET /r7 160 index.ts:5:13 +mw index.ts:328:33 index.ts:413:16 GET /r7 161 index.ts:5:13 +mw index.ts:330:33 index.ts:413:16 GET /r7 162 index.ts:5:13 +mw index.ts:332:33 index.ts:413:16 GET /r7 163 index.ts:5:13 +mw index.ts:334:33 index.ts:413:16 GET /r7 164 index.ts:5:13 +mw index.ts:336:33 index.ts:413:16 GET /r7 165 index.ts:5:13 +mw index.ts:338:33 index.ts:413:16 GET /r7 166 index.ts:5:13 +mw index.ts:340:33 index.ts:413:16 GET /r7 167 index.ts:5:13 +mw index.ts:342:33 index.ts:413:16 GET /r7 168 index.ts:5:13 +mw index.ts:344:33 index.ts:413:16 GET /r7 169 index.ts:5:13 +mw index.ts:346:33 index.ts:413:16 GET /r7 170 index.ts:5:13 +mw index.ts:348:33 index.ts:413:16 GET /r7 171 index.ts:5:13 +mw index.ts:350:33 index.ts:413:16 GET /r7 172 index.ts:5:13 +mw index.ts:352:33 index.ts:413:16 GET /r7 173 index.ts:5:13 +mw index.ts:354:33 index.ts:413:16 GET /r7 174 index.ts:5:13 +mw index.ts:356:33 index.ts:413:16 GET /r7 175 index.ts:5:13 +mw index.ts:358:33 index.ts:413:16 GET /r7 176 index.ts:5:13 +mw index.ts:360:33 index.ts:413:16 GET /r7 177 index.ts:5:13 +mw index.ts:362:33 index.ts:413:16 GET /r7 178 index.ts:5:13 +mw index.ts:364:33 index.ts:413:16 GET /r7 179 index.ts:5:13 +mw index.ts:366:33 index.ts:413:16 GET /r7 180 index.ts:5:13 +mw index.ts:368:33 index.ts:413:16 GET /r7 181 index.ts:5:13 +mw index.ts:370:33 index.ts:413:16 GET /r7 182 index.ts:5:13 +mw index.ts:372:33 index.ts:413:16 GET /r7 183 index.ts:5:13 +mw index.ts:374:33 index.ts:413:16 GET /r7 184 index.ts:5:13 +mw index.ts:376:33 index.ts:413:16 GET /r7 185 index.ts:5:13 +mw index.ts:378:33 index.ts:413:16 GET /r7 186 index.ts:5:13 +mw index.ts:380:33 index.ts:413:16 GET /r7 187 index.ts:5:13 +mw index.ts:382:33 index.ts:413:16 GET /r7 188 index.ts:5:13 +mw index.ts:384:33 index.ts:413:16 GET /r7 189 index.ts:5:13 +mw index.ts:386:33 index.ts:413:16 GET /r7 190 index.ts:5:13 +mw index.ts:388:33 index.ts:413:16 GET /r7 191 index.ts:5:13 +mw index.ts:390:33 index.ts:413:16 GET /r7 192 index.ts:5:13 +mw index.ts:392:33 index.ts:413:16 GET /r7 193 index.ts:5:13 +mw index.ts:394:33 index.ts:413:16 GET /r7 194 index.ts:5:13 +mw index.ts:396:33 index.ts:413:16 GET /r7 195 index.ts:5:13 +mw index.ts:398:33 index.ts:413:16 GET /r7 196 index.ts:5:13 +mw index.ts:400:33 index.ts:413:16 GET /r7 197 index.ts:5:13 +mw index.ts:402:33 index.ts:413:16 GET /r7 198 index.ts:5:13 +mw index.ts:404:33 index.ts:413:16 GET /r7 199 index.ts:5:13 +mw index.ts:6:31 index.ts:414:16 GET /r8 0 index.ts:5:13 +mw index.ts:8:31 index.ts:414:16 GET /r8 1 index.ts:5:13 +mw index.ts:10:31 index.ts:414:16 GET /r8 2 index.ts:5:13 +mw index.ts:12:31 index.ts:414:16 GET /r8 3 index.ts:5:13 +mw index.ts:14:31 index.ts:414:16 GET /r8 4 index.ts:5:13 +mw index.ts:16:31 index.ts:414:16 GET /r8 5 index.ts:5:13 +mw index.ts:18:31 index.ts:414:16 GET /r8 6 index.ts:5:13 +mw index.ts:20:31 index.ts:414:16 GET /r8 7 index.ts:5:13 +mw index.ts:22:31 index.ts:414:16 GET /r8 8 index.ts:5:13 +mw index.ts:24:31 index.ts:414:16 GET /r8 9 index.ts:5:13 +mw index.ts:26:32 index.ts:414:16 GET /r8 10 index.ts:5:13 +mw index.ts:28:32 index.ts:414:16 GET /r8 11 index.ts:5:13 +mw index.ts:30:32 index.ts:414:16 GET /r8 12 index.ts:5:13 +mw index.ts:32:32 index.ts:414:16 GET /r8 13 index.ts:5:13 +mw index.ts:34:32 index.ts:414:16 GET /r8 14 index.ts:5:13 +mw index.ts:36:32 index.ts:414:16 GET /r8 15 index.ts:5:13 +mw index.ts:38:32 index.ts:414:16 GET /r8 16 index.ts:5:13 +mw index.ts:40:32 index.ts:414:16 GET /r8 17 index.ts:5:13 +mw index.ts:42:32 index.ts:414:16 GET /r8 18 index.ts:5:13 +mw index.ts:44:32 index.ts:414:16 GET /r8 19 index.ts:5:13 +mw index.ts:46:32 index.ts:414:16 GET /r8 20 index.ts:5:13 +mw index.ts:48:32 index.ts:414:16 GET /r8 21 index.ts:5:13 +mw index.ts:50:32 index.ts:414:16 GET /r8 22 index.ts:5:13 +mw index.ts:52:32 index.ts:414:16 GET /r8 23 index.ts:5:13 +mw index.ts:54:32 index.ts:414:16 GET /r8 24 index.ts:5:13 +mw index.ts:56:32 index.ts:414:16 GET /r8 25 index.ts:5:13 +mw index.ts:58:32 index.ts:414:16 GET /r8 26 index.ts:5:13 +mw index.ts:60:32 index.ts:414:16 GET /r8 27 index.ts:5:13 +mw index.ts:62:32 index.ts:414:16 GET /r8 28 index.ts:5:13 +mw index.ts:64:32 index.ts:414:16 GET /r8 29 index.ts:5:13 +mw index.ts:66:32 index.ts:414:16 GET /r8 30 index.ts:5:13 +mw index.ts:68:32 index.ts:414:16 GET /r8 31 index.ts:5:13 +mw index.ts:70:32 index.ts:414:16 GET /r8 32 index.ts:5:13 +mw index.ts:72:32 index.ts:414:16 GET /r8 33 index.ts:5:13 +mw index.ts:74:32 index.ts:414:16 GET /r8 34 index.ts:5:13 +mw index.ts:76:32 index.ts:414:16 GET /r8 35 index.ts:5:13 +mw index.ts:78:32 index.ts:414:16 GET /r8 36 index.ts:5:13 +mw index.ts:80:32 index.ts:414:16 GET /r8 37 index.ts:5:13 +mw index.ts:82:32 index.ts:414:16 GET /r8 38 index.ts:5:13 +mw index.ts:84:32 index.ts:414:16 GET /r8 39 index.ts:5:13 +mw index.ts:86:32 index.ts:414:16 GET /r8 40 index.ts:5:13 +mw index.ts:88:32 index.ts:414:16 GET /r8 41 index.ts:5:13 +mw index.ts:90:32 index.ts:414:16 GET /r8 42 index.ts:5:13 +mw index.ts:92:32 index.ts:414:16 GET /r8 43 index.ts:5:13 +mw index.ts:94:32 index.ts:414:16 GET /r8 44 index.ts:5:13 +mw index.ts:96:32 index.ts:414:16 GET /r8 45 index.ts:5:13 +mw index.ts:98:32 index.ts:414:16 GET /r8 46 index.ts:5:13 +mw index.ts:100:32 index.ts:414:16 GET /r8 47 index.ts:5:13 +mw index.ts:102:32 index.ts:414:16 GET /r8 48 index.ts:5:13 +mw index.ts:104:32 index.ts:414:16 GET /r8 49 index.ts:5:13 +mw index.ts:106:32 index.ts:414:16 GET /r8 50 index.ts:5:13 +mw index.ts:108:32 index.ts:414:16 GET /r8 51 index.ts:5:13 +mw index.ts:110:32 index.ts:414:16 GET /r8 52 index.ts:5:13 +mw index.ts:112:32 index.ts:414:16 GET /r8 53 index.ts:5:13 +mw index.ts:114:32 index.ts:414:16 GET /r8 54 index.ts:5:13 +mw index.ts:116:32 index.ts:414:16 GET /r8 55 index.ts:5:13 +mw index.ts:118:32 index.ts:414:16 GET /r8 56 index.ts:5:13 +mw index.ts:120:32 index.ts:414:16 GET /r8 57 index.ts:5:13 +mw index.ts:122:32 index.ts:414:16 GET /r8 58 index.ts:5:13 +mw index.ts:124:32 index.ts:414:16 GET /r8 59 index.ts:5:13 +mw index.ts:126:32 index.ts:414:16 GET /r8 60 index.ts:5:13 +mw index.ts:128:32 index.ts:414:16 GET /r8 61 index.ts:5:13 +mw index.ts:130:32 index.ts:414:16 GET /r8 62 index.ts:5:13 +mw index.ts:132:32 index.ts:414:16 GET /r8 63 index.ts:5:13 +mw index.ts:134:32 index.ts:414:16 GET /r8 64 index.ts:5:13 +mw index.ts:136:32 index.ts:414:16 GET /r8 65 index.ts:5:13 +mw index.ts:138:32 index.ts:414:16 GET /r8 66 index.ts:5:13 +mw index.ts:140:32 index.ts:414:16 GET /r8 67 index.ts:5:13 +mw index.ts:142:32 index.ts:414:16 GET /r8 68 index.ts:5:13 +mw index.ts:144:32 index.ts:414:16 GET /r8 69 index.ts:5:13 +mw index.ts:146:32 index.ts:414:16 GET /r8 70 index.ts:5:13 +mw index.ts:148:32 index.ts:414:16 GET /r8 71 index.ts:5:13 +mw index.ts:150:32 index.ts:414:16 GET /r8 72 index.ts:5:13 +mw index.ts:152:32 index.ts:414:16 GET /r8 73 index.ts:5:13 +mw index.ts:154:32 index.ts:414:16 GET /r8 74 index.ts:5:13 +mw index.ts:156:32 index.ts:414:16 GET /r8 75 index.ts:5:13 +mw index.ts:158:32 index.ts:414:16 GET /r8 76 index.ts:5:13 +mw index.ts:160:32 index.ts:414:16 GET /r8 77 index.ts:5:13 +mw index.ts:162:32 index.ts:414:16 GET /r8 78 index.ts:5:13 +mw index.ts:164:32 index.ts:414:16 GET /r8 79 index.ts:5:13 +mw index.ts:166:32 index.ts:414:16 GET /r8 80 index.ts:5:13 +mw index.ts:168:32 index.ts:414:16 GET /r8 81 index.ts:5:13 +mw index.ts:170:32 index.ts:414:16 GET /r8 82 index.ts:5:13 +mw index.ts:172:32 index.ts:414:16 GET /r8 83 index.ts:5:13 +mw index.ts:174:32 index.ts:414:16 GET /r8 84 index.ts:5:13 +mw index.ts:176:32 index.ts:414:16 GET /r8 85 index.ts:5:13 +mw index.ts:178:32 index.ts:414:16 GET /r8 86 index.ts:5:13 +mw index.ts:180:32 index.ts:414:16 GET /r8 87 index.ts:5:13 +mw index.ts:182:32 index.ts:414:16 GET /r8 88 index.ts:5:13 +mw index.ts:184:32 index.ts:414:16 GET /r8 89 index.ts:5:13 +mw index.ts:186:32 index.ts:414:16 GET /r8 90 index.ts:5:13 +mw index.ts:188:32 index.ts:414:16 GET /r8 91 index.ts:5:13 +mw index.ts:190:32 index.ts:414:16 GET /r8 92 index.ts:5:13 +mw index.ts:192:32 index.ts:414:16 GET /r8 93 index.ts:5:13 +mw index.ts:194:32 index.ts:414:16 GET /r8 94 index.ts:5:13 +mw index.ts:196:32 index.ts:414:16 GET /r8 95 index.ts:5:13 +mw index.ts:198:32 index.ts:414:16 GET /r8 96 index.ts:5:13 +mw index.ts:200:32 index.ts:414:16 GET /r8 97 index.ts:5:13 +mw index.ts:202:32 index.ts:414:16 GET /r8 98 index.ts:5:13 +mw index.ts:204:32 index.ts:414:16 GET /r8 99 index.ts:5:13 +mw index.ts:206:33 index.ts:414:16 GET /r8 100 index.ts:5:13 +mw index.ts:208:33 index.ts:414:16 GET /r8 101 index.ts:5:13 +mw index.ts:210:33 index.ts:414:16 GET /r8 102 index.ts:5:13 +mw index.ts:212:33 index.ts:414:16 GET /r8 103 index.ts:5:13 +mw index.ts:214:33 index.ts:414:16 GET /r8 104 index.ts:5:13 +mw index.ts:216:33 index.ts:414:16 GET /r8 105 index.ts:5:13 +mw index.ts:218:33 index.ts:414:16 GET /r8 106 index.ts:5:13 +mw index.ts:220:33 index.ts:414:16 GET /r8 107 index.ts:5:13 +mw index.ts:222:33 index.ts:414:16 GET /r8 108 index.ts:5:13 +mw index.ts:224:33 index.ts:414:16 GET /r8 109 index.ts:5:13 +mw index.ts:226:33 index.ts:414:16 GET /r8 110 index.ts:5:13 +mw index.ts:228:33 index.ts:414:16 GET /r8 111 index.ts:5:13 +mw index.ts:230:33 index.ts:414:16 GET /r8 112 index.ts:5:13 +mw index.ts:232:33 index.ts:414:16 GET /r8 113 index.ts:5:13 +mw index.ts:234:33 index.ts:414:16 GET /r8 114 index.ts:5:13 +mw index.ts:236:33 index.ts:414:16 GET /r8 115 index.ts:5:13 +mw index.ts:238:33 index.ts:414:16 GET /r8 116 index.ts:5:13 +mw index.ts:240:33 index.ts:414:16 GET /r8 117 index.ts:5:13 +mw index.ts:242:33 index.ts:414:16 GET /r8 118 index.ts:5:13 +mw index.ts:244:33 index.ts:414:16 GET /r8 119 index.ts:5:13 +mw index.ts:246:33 index.ts:414:16 GET /r8 120 index.ts:5:13 +mw index.ts:248:33 index.ts:414:16 GET /r8 121 index.ts:5:13 +mw index.ts:250:33 index.ts:414:16 GET /r8 122 index.ts:5:13 +mw index.ts:252:33 index.ts:414:16 GET /r8 123 index.ts:5:13 +mw index.ts:254:33 index.ts:414:16 GET /r8 124 index.ts:5:13 +mw index.ts:256:33 index.ts:414:16 GET /r8 125 index.ts:5:13 +mw index.ts:258:33 index.ts:414:16 GET /r8 126 index.ts:5:13 +mw index.ts:260:33 index.ts:414:16 GET /r8 127 index.ts:5:13 +mw index.ts:262:33 index.ts:414:16 GET /r8 128 index.ts:5:13 +mw index.ts:264:33 index.ts:414:16 GET /r8 129 index.ts:5:13 +mw index.ts:266:33 index.ts:414:16 GET /r8 130 index.ts:5:13 +mw index.ts:268:33 index.ts:414:16 GET /r8 131 index.ts:5:13 +mw index.ts:270:33 index.ts:414:16 GET /r8 132 index.ts:5:13 +mw index.ts:272:33 index.ts:414:16 GET /r8 133 index.ts:5:13 +mw index.ts:274:33 index.ts:414:16 GET /r8 134 index.ts:5:13 +mw index.ts:276:33 index.ts:414:16 GET /r8 135 index.ts:5:13 +mw index.ts:278:33 index.ts:414:16 GET /r8 136 index.ts:5:13 +mw index.ts:280:33 index.ts:414:16 GET /r8 137 index.ts:5:13 +mw index.ts:282:33 index.ts:414:16 GET /r8 138 index.ts:5:13 +mw index.ts:284:33 index.ts:414:16 GET /r8 139 index.ts:5:13 +mw index.ts:286:33 index.ts:414:16 GET /r8 140 index.ts:5:13 +mw index.ts:288:33 index.ts:414:16 GET /r8 141 index.ts:5:13 +mw index.ts:290:33 index.ts:414:16 GET /r8 142 index.ts:5:13 +mw index.ts:292:33 index.ts:414:16 GET /r8 143 index.ts:5:13 +mw index.ts:294:33 index.ts:414:16 GET /r8 144 index.ts:5:13 +mw index.ts:296:33 index.ts:414:16 GET /r8 145 index.ts:5:13 +mw index.ts:298:33 index.ts:414:16 GET /r8 146 index.ts:5:13 +mw index.ts:300:33 index.ts:414:16 GET /r8 147 index.ts:5:13 +mw index.ts:302:33 index.ts:414:16 GET /r8 148 index.ts:5:13 +mw index.ts:304:33 index.ts:414:16 GET /r8 149 index.ts:5:13 +mw index.ts:306:33 index.ts:414:16 GET /r8 150 index.ts:5:13 +mw index.ts:308:33 index.ts:414:16 GET /r8 151 index.ts:5:13 +mw index.ts:310:33 index.ts:414:16 GET /r8 152 index.ts:5:13 +mw index.ts:312:33 index.ts:414:16 GET /r8 153 index.ts:5:13 +mw index.ts:314:33 index.ts:414:16 GET /r8 154 index.ts:5:13 +mw index.ts:316:33 index.ts:414:16 GET /r8 155 index.ts:5:13 +mw index.ts:318:33 index.ts:414:16 GET /r8 156 index.ts:5:13 +mw index.ts:320:33 index.ts:414:16 GET /r8 157 index.ts:5:13 +mw index.ts:322:33 index.ts:414:16 GET /r8 158 index.ts:5:13 +mw index.ts:324:33 index.ts:414:16 GET /r8 159 index.ts:5:13 +mw index.ts:326:33 index.ts:414:16 GET /r8 160 index.ts:5:13 +mw index.ts:328:33 index.ts:414:16 GET /r8 161 index.ts:5:13 +mw index.ts:330:33 index.ts:414:16 GET /r8 162 index.ts:5:13 +mw index.ts:332:33 index.ts:414:16 GET /r8 163 index.ts:5:13 +mw index.ts:334:33 index.ts:414:16 GET /r8 164 index.ts:5:13 +mw index.ts:336:33 index.ts:414:16 GET /r8 165 index.ts:5:13 +mw index.ts:338:33 index.ts:414:16 GET /r8 166 index.ts:5:13 +mw index.ts:340:33 index.ts:414:16 GET /r8 167 index.ts:5:13 +mw index.ts:342:33 index.ts:414:16 GET /r8 168 index.ts:5:13 +mw index.ts:344:33 index.ts:414:16 GET /r8 169 index.ts:5:13 +mw index.ts:346:33 index.ts:414:16 GET /r8 170 index.ts:5:13 +mw index.ts:348:33 index.ts:414:16 GET /r8 171 index.ts:5:13 +mw index.ts:350:33 index.ts:414:16 GET /r8 172 index.ts:5:13 +mw index.ts:352:33 index.ts:414:16 GET /r8 173 index.ts:5:13 +mw index.ts:354:33 index.ts:414:16 GET /r8 174 index.ts:5:13 +mw index.ts:356:33 index.ts:414:16 GET /r8 175 index.ts:5:13 +mw index.ts:358:33 index.ts:414:16 GET /r8 176 index.ts:5:13 +mw index.ts:360:33 index.ts:414:16 GET /r8 177 index.ts:5:13 +mw index.ts:362:33 index.ts:414:16 GET /r8 178 index.ts:5:13 +mw index.ts:364:33 index.ts:414:16 GET /r8 179 index.ts:5:13 +mw index.ts:366:33 index.ts:414:16 GET /r8 180 index.ts:5:13 +mw index.ts:368:33 index.ts:414:16 GET /r8 181 index.ts:5:13 +mw index.ts:370:33 index.ts:414:16 GET /r8 182 index.ts:5:13 +mw index.ts:372:33 index.ts:414:16 GET /r8 183 index.ts:5:13 +mw index.ts:374:33 index.ts:414:16 GET /r8 184 index.ts:5:13 +mw index.ts:376:33 index.ts:414:16 GET /r8 185 index.ts:5:13 +mw index.ts:378:33 index.ts:414:16 GET /r8 186 index.ts:5:13 +mw index.ts:380:33 index.ts:414:16 GET /r8 187 index.ts:5:13 +mw index.ts:382:33 index.ts:414:16 GET /r8 188 index.ts:5:13 +mw index.ts:384:33 index.ts:414:16 GET /r8 189 index.ts:5:13 +mw index.ts:386:33 index.ts:414:16 GET /r8 190 index.ts:5:13 +mw index.ts:388:33 index.ts:414:16 GET /r8 191 index.ts:5:13 +mw index.ts:390:33 index.ts:414:16 GET /r8 192 index.ts:5:13 +mw index.ts:392:33 index.ts:414:16 GET /r8 193 index.ts:5:13 +mw index.ts:394:33 index.ts:414:16 GET /r8 194 index.ts:5:13 +mw index.ts:396:33 index.ts:414:16 GET /r8 195 index.ts:5:13 +mw index.ts:398:33 index.ts:414:16 GET /r8 196 index.ts:5:13 +mw index.ts:400:33 index.ts:414:16 GET /r8 197 index.ts:5:13 +mw index.ts:402:33 index.ts:414:16 GET /r8 198 index.ts:5:13 +mw index.ts:404:33 index.ts:414:16 GET /r8 199 index.ts:5:13 +mw index.ts:6:31 index.ts:415:16 GET /r9 0 index.ts:5:13 +mw index.ts:8:31 index.ts:415:16 GET /r9 1 index.ts:5:13 +mw index.ts:10:31 index.ts:415:16 GET /r9 2 index.ts:5:13 +mw index.ts:12:31 index.ts:415:16 GET /r9 3 index.ts:5:13 +mw index.ts:14:31 index.ts:415:16 GET /r9 4 index.ts:5:13 +mw index.ts:16:31 index.ts:415:16 GET /r9 5 index.ts:5:13 +mw index.ts:18:31 index.ts:415:16 GET /r9 6 index.ts:5:13 +mw index.ts:20:31 index.ts:415:16 GET /r9 7 index.ts:5:13 +mw index.ts:22:31 index.ts:415:16 GET /r9 8 index.ts:5:13 +mw index.ts:24:31 index.ts:415:16 GET /r9 9 index.ts:5:13 +mw index.ts:26:32 index.ts:415:16 GET /r9 10 index.ts:5:13 +mw index.ts:28:32 index.ts:415:16 GET /r9 11 index.ts:5:13 +mw index.ts:30:32 index.ts:415:16 GET /r9 12 index.ts:5:13 +mw index.ts:32:32 index.ts:415:16 GET /r9 13 index.ts:5:13 +mw index.ts:34:32 index.ts:415:16 GET /r9 14 index.ts:5:13 +mw index.ts:36:32 index.ts:415:16 GET /r9 15 index.ts:5:13 +mw index.ts:38:32 index.ts:415:16 GET /r9 16 index.ts:5:13 +mw index.ts:40:32 index.ts:415:16 GET /r9 17 index.ts:5:13 +mw index.ts:42:32 index.ts:415:16 GET /r9 18 index.ts:5:13 +mw index.ts:44:32 index.ts:415:16 GET /r9 19 index.ts:5:13 +mw index.ts:46:32 index.ts:415:16 GET /r9 20 index.ts:5:13 +mw index.ts:48:32 index.ts:415:16 GET /r9 21 index.ts:5:13 +mw index.ts:50:32 index.ts:415:16 GET /r9 22 index.ts:5:13 +mw index.ts:52:32 index.ts:415:16 GET /r9 23 index.ts:5:13 +mw index.ts:54:32 index.ts:415:16 GET /r9 24 index.ts:5:13 +mw index.ts:56:32 index.ts:415:16 GET /r9 25 index.ts:5:13 +mw index.ts:58:32 index.ts:415:16 GET /r9 26 index.ts:5:13 +mw index.ts:60:32 index.ts:415:16 GET /r9 27 index.ts:5:13 +mw index.ts:62:32 index.ts:415:16 GET /r9 28 index.ts:5:13 +mw index.ts:64:32 index.ts:415:16 GET /r9 29 index.ts:5:13 +mw index.ts:66:32 index.ts:415:16 GET /r9 30 index.ts:5:13 +mw index.ts:68:32 index.ts:415:16 GET /r9 31 index.ts:5:13 +mw index.ts:70:32 index.ts:415:16 GET /r9 32 index.ts:5:13 +mw index.ts:72:32 index.ts:415:16 GET /r9 33 index.ts:5:13 +mw index.ts:74:32 index.ts:415:16 GET /r9 34 index.ts:5:13 +mw index.ts:76:32 index.ts:415:16 GET /r9 35 index.ts:5:13 +mw index.ts:78:32 index.ts:415:16 GET /r9 36 index.ts:5:13 +mw index.ts:80:32 index.ts:415:16 GET /r9 37 index.ts:5:13 +mw index.ts:82:32 index.ts:415:16 GET /r9 38 index.ts:5:13 +mw index.ts:84:32 index.ts:415:16 GET /r9 39 index.ts:5:13 +mw index.ts:86:32 index.ts:415:16 GET /r9 40 index.ts:5:13 +mw index.ts:88:32 index.ts:415:16 GET /r9 41 index.ts:5:13 +mw index.ts:90:32 index.ts:415:16 GET /r9 42 index.ts:5:13 +mw index.ts:92:32 index.ts:415:16 GET /r9 43 index.ts:5:13 +mw index.ts:94:32 index.ts:415:16 GET /r9 44 index.ts:5:13 +mw index.ts:96:32 index.ts:415:16 GET /r9 45 index.ts:5:13 +mw index.ts:98:32 index.ts:415:16 GET /r9 46 index.ts:5:13 +mw index.ts:100:32 index.ts:415:16 GET /r9 47 index.ts:5:13 +mw index.ts:102:32 index.ts:415:16 GET /r9 48 index.ts:5:13 +mw index.ts:104:32 index.ts:415:16 GET /r9 49 index.ts:5:13 +mw index.ts:106:32 index.ts:415:16 GET /r9 50 index.ts:5:13 +mw index.ts:108:32 index.ts:415:16 GET /r9 51 index.ts:5:13 +mw index.ts:110:32 index.ts:415:16 GET /r9 52 index.ts:5:13 +mw index.ts:112:32 index.ts:415:16 GET /r9 53 index.ts:5:13 +mw index.ts:114:32 index.ts:415:16 GET /r9 54 index.ts:5:13 +mw index.ts:116:32 index.ts:415:16 GET /r9 55 index.ts:5:13 +mw index.ts:118:32 index.ts:415:16 GET /r9 56 index.ts:5:13 +mw index.ts:120:32 index.ts:415:16 GET /r9 57 index.ts:5:13 +mw index.ts:122:32 index.ts:415:16 GET /r9 58 index.ts:5:13 +mw index.ts:124:32 index.ts:415:16 GET /r9 59 index.ts:5:13 +mw index.ts:126:32 index.ts:415:16 GET /r9 60 index.ts:5:13 +mw index.ts:128:32 index.ts:415:16 GET /r9 61 index.ts:5:13 +mw index.ts:130:32 index.ts:415:16 GET /r9 62 index.ts:5:13 +mw index.ts:132:32 index.ts:415:16 GET /r9 63 index.ts:5:13 +mw index.ts:134:32 index.ts:415:16 GET /r9 64 index.ts:5:13 +mw index.ts:136:32 index.ts:415:16 GET /r9 65 index.ts:5:13 +mw index.ts:138:32 index.ts:415:16 GET /r9 66 index.ts:5:13 +mw index.ts:140:32 index.ts:415:16 GET /r9 67 index.ts:5:13 +mw index.ts:142:32 index.ts:415:16 GET /r9 68 index.ts:5:13 +mw index.ts:144:32 index.ts:415:16 GET /r9 69 index.ts:5:13 +mw index.ts:146:32 index.ts:415:16 GET /r9 70 index.ts:5:13 +mw index.ts:148:32 index.ts:415:16 GET /r9 71 index.ts:5:13 +mw index.ts:150:32 index.ts:415:16 GET /r9 72 index.ts:5:13 +mw index.ts:152:32 index.ts:415:16 GET /r9 73 index.ts:5:13 +mw index.ts:154:32 index.ts:415:16 GET /r9 74 index.ts:5:13 +mw index.ts:156:32 index.ts:415:16 GET /r9 75 index.ts:5:13 +mw index.ts:158:32 index.ts:415:16 GET /r9 76 index.ts:5:13 +mw index.ts:160:32 index.ts:415:16 GET /r9 77 index.ts:5:13 +mw index.ts:162:32 index.ts:415:16 GET /r9 78 index.ts:5:13 +mw index.ts:164:32 index.ts:415:16 GET /r9 79 index.ts:5:13 +mw index.ts:166:32 index.ts:415:16 GET /r9 80 index.ts:5:13 +mw index.ts:168:32 index.ts:415:16 GET /r9 81 index.ts:5:13 +mw index.ts:170:32 index.ts:415:16 GET /r9 82 index.ts:5:13 +mw index.ts:172:32 index.ts:415:16 GET /r9 83 index.ts:5:13 +mw index.ts:174:32 index.ts:415:16 GET /r9 84 index.ts:5:13 +mw index.ts:176:32 index.ts:415:16 GET /r9 85 index.ts:5:13 +mw index.ts:178:32 index.ts:415:16 GET /r9 86 index.ts:5:13 +mw index.ts:180:32 index.ts:415:16 GET /r9 87 index.ts:5:13 +mw index.ts:182:32 index.ts:415:16 GET /r9 88 index.ts:5:13 +mw index.ts:184:32 index.ts:415:16 GET /r9 89 index.ts:5:13 +mw index.ts:186:32 index.ts:415:16 GET /r9 90 index.ts:5:13 +mw index.ts:188:32 index.ts:415:16 GET /r9 91 index.ts:5:13 +mw index.ts:190:32 index.ts:415:16 GET /r9 92 index.ts:5:13 +mw index.ts:192:32 index.ts:415:16 GET /r9 93 index.ts:5:13 +mw index.ts:194:32 index.ts:415:16 GET /r9 94 index.ts:5:13 +mw index.ts:196:32 index.ts:415:16 GET /r9 95 index.ts:5:13 +mw index.ts:198:32 index.ts:415:16 GET /r9 96 index.ts:5:13 +mw index.ts:200:32 index.ts:415:16 GET /r9 97 index.ts:5:13 +mw index.ts:202:32 index.ts:415:16 GET /r9 98 index.ts:5:13 +mw index.ts:204:32 index.ts:415:16 GET /r9 99 index.ts:5:13 +mw index.ts:206:33 index.ts:415:16 GET /r9 100 index.ts:5:13 +mw index.ts:208:33 index.ts:415:16 GET /r9 101 index.ts:5:13 +mw index.ts:210:33 index.ts:415:16 GET /r9 102 index.ts:5:13 +mw index.ts:212:33 index.ts:415:16 GET /r9 103 index.ts:5:13 +mw index.ts:214:33 index.ts:415:16 GET /r9 104 index.ts:5:13 +mw index.ts:216:33 index.ts:415:16 GET /r9 105 index.ts:5:13 +mw index.ts:218:33 index.ts:415:16 GET /r9 106 index.ts:5:13 +mw index.ts:220:33 index.ts:415:16 GET /r9 107 index.ts:5:13 +mw index.ts:222:33 index.ts:415:16 GET /r9 108 index.ts:5:13 +mw index.ts:224:33 index.ts:415:16 GET /r9 109 index.ts:5:13 +mw index.ts:226:33 index.ts:415:16 GET /r9 110 index.ts:5:13 +mw index.ts:228:33 index.ts:415:16 GET /r9 111 index.ts:5:13 +mw index.ts:230:33 index.ts:415:16 GET /r9 112 index.ts:5:13 +mw index.ts:232:33 index.ts:415:16 GET /r9 113 index.ts:5:13 +mw index.ts:234:33 index.ts:415:16 GET /r9 114 index.ts:5:13 +mw index.ts:236:33 index.ts:415:16 GET /r9 115 index.ts:5:13 +mw index.ts:238:33 index.ts:415:16 GET /r9 116 index.ts:5:13 +mw index.ts:240:33 index.ts:415:16 GET /r9 117 index.ts:5:13 +mw index.ts:242:33 index.ts:415:16 GET /r9 118 index.ts:5:13 +mw index.ts:244:33 index.ts:415:16 GET /r9 119 index.ts:5:13 +mw index.ts:246:33 index.ts:415:16 GET /r9 120 index.ts:5:13 +mw index.ts:248:33 index.ts:415:16 GET /r9 121 index.ts:5:13 +mw index.ts:250:33 index.ts:415:16 GET /r9 122 index.ts:5:13 +mw index.ts:252:33 index.ts:415:16 GET /r9 123 index.ts:5:13 +mw index.ts:254:33 index.ts:415:16 GET /r9 124 index.ts:5:13 +mw index.ts:256:33 index.ts:415:16 GET /r9 125 index.ts:5:13 +mw index.ts:258:33 index.ts:415:16 GET /r9 126 index.ts:5:13 +mw index.ts:260:33 index.ts:415:16 GET /r9 127 index.ts:5:13 +mw index.ts:262:33 index.ts:415:16 GET /r9 128 index.ts:5:13 +mw index.ts:264:33 index.ts:415:16 GET /r9 129 index.ts:5:13 +mw index.ts:266:33 index.ts:415:16 GET /r9 130 index.ts:5:13 +mw index.ts:268:33 index.ts:415:16 GET /r9 131 index.ts:5:13 +mw index.ts:270:33 index.ts:415:16 GET /r9 132 index.ts:5:13 +mw index.ts:272:33 index.ts:415:16 GET /r9 133 index.ts:5:13 +mw index.ts:274:33 index.ts:415:16 GET /r9 134 index.ts:5:13 +mw index.ts:276:33 index.ts:415:16 GET /r9 135 index.ts:5:13 +mw index.ts:278:33 index.ts:415:16 GET /r9 136 index.ts:5:13 +mw index.ts:280:33 index.ts:415:16 GET /r9 137 index.ts:5:13 +mw index.ts:282:33 index.ts:415:16 GET /r9 138 index.ts:5:13 +mw index.ts:284:33 index.ts:415:16 GET /r9 139 index.ts:5:13 +mw index.ts:286:33 index.ts:415:16 GET /r9 140 index.ts:5:13 +mw index.ts:288:33 index.ts:415:16 GET /r9 141 index.ts:5:13 +mw index.ts:290:33 index.ts:415:16 GET /r9 142 index.ts:5:13 +mw index.ts:292:33 index.ts:415:16 GET /r9 143 index.ts:5:13 +mw index.ts:294:33 index.ts:415:16 GET /r9 144 index.ts:5:13 +mw index.ts:296:33 index.ts:415:16 GET /r9 145 index.ts:5:13 +mw index.ts:298:33 index.ts:415:16 GET /r9 146 index.ts:5:13 +mw index.ts:300:33 index.ts:415:16 GET /r9 147 index.ts:5:13 +mw index.ts:302:33 index.ts:415:16 GET /r9 148 index.ts:5:13 +mw index.ts:304:33 index.ts:415:16 GET /r9 149 index.ts:5:13 +mw index.ts:306:33 index.ts:415:16 GET /r9 150 index.ts:5:13 +mw index.ts:308:33 index.ts:415:16 GET /r9 151 index.ts:5:13 +mw index.ts:310:33 index.ts:415:16 GET /r9 152 index.ts:5:13 +mw index.ts:312:33 index.ts:415:16 GET /r9 153 index.ts:5:13 +mw index.ts:314:33 index.ts:415:16 GET /r9 154 index.ts:5:13 +mw index.ts:316:33 index.ts:415:16 GET /r9 155 index.ts:5:13 +mw index.ts:318:33 index.ts:415:16 GET /r9 156 index.ts:5:13 +mw index.ts:320:33 index.ts:415:16 GET /r9 157 index.ts:5:13 +mw index.ts:322:33 index.ts:415:16 GET /r9 158 index.ts:5:13 +mw index.ts:324:33 index.ts:415:16 GET /r9 159 index.ts:5:13 +mw index.ts:326:33 index.ts:415:16 GET /r9 160 index.ts:5:13 +mw index.ts:328:33 index.ts:415:16 GET /r9 161 index.ts:5:13 +mw index.ts:330:33 index.ts:415:16 GET /r9 162 index.ts:5:13 +mw index.ts:332:33 index.ts:415:16 GET /r9 163 index.ts:5:13 +mw index.ts:334:33 index.ts:415:16 GET /r9 164 index.ts:5:13 +mw index.ts:336:33 index.ts:415:16 GET /r9 165 index.ts:5:13 +mw index.ts:338:33 index.ts:415:16 GET /r9 166 index.ts:5:13 +mw index.ts:340:33 index.ts:415:16 GET /r9 167 index.ts:5:13 +mw index.ts:342:33 index.ts:415:16 GET /r9 168 index.ts:5:13 +mw index.ts:344:33 index.ts:415:16 GET /r9 169 index.ts:5:13 +mw index.ts:346:33 index.ts:415:16 GET /r9 170 index.ts:5:13 +mw index.ts:348:33 index.ts:415:16 GET /r9 171 index.ts:5:13 +mw index.ts:350:33 index.ts:415:16 GET /r9 172 index.ts:5:13 +mw index.ts:352:33 index.ts:415:16 GET /r9 173 index.ts:5:13 +mw index.ts:354:33 index.ts:415:16 GET /r9 174 index.ts:5:13 +mw index.ts:356:33 index.ts:415:16 GET /r9 175 index.ts:5:13 +mw index.ts:358:33 index.ts:415:16 GET /r9 176 index.ts:5:13 +mw index.ts:360:33 index.ts:415:16 GET /r9 177 index.ts:5:13 +mw index.ts:362:33 index.ts:415:16 GET /r9 178 index.ts:5:13 +mw index.ts:364:33 index.ts:415:16 GET /r9 179 index.ts:5:13 +mw index.ts:366:33 index.ts:415:16 GET /r9 180 index.ts:5:13 +mw index.ts:368:33 index.ts:415:16 GET /r9 181 index.ts:5:13 +mw index.ts:370:33 index.ts:415:16 GET /r9 182 index.ts:5:13 +mw index.ts:372:33 index.ts:415:16 GET /r9 183 index.ts:5:13 +mw index.ts:374:33 index.ts:415:16 GET /r9 184 index.ts:5:13 +mw index.ts:376:33 index.ts:415:16 GET /r9 185 index.ts:5:13 +mw index.ts:378:33 index.ts:415:16 GET /r9 186 index.ts:5:13 +mw index.ts:380:33 index.ts:415:16 GET /r9 187 index.ts:5:13 +mw index.ts:382:33 index.ts:415:16 GET /r9 188 index.ts:5:13 +mw index.ts:384:33 index.ts:415:16 GET /r9 189 index.ts:5:13 +mw index.ts:386:33 index.ts:415:16 GET /r9 190 index.ts:5:13 +mw index.ts:388:33 index.ts:415:16 GET /r9 191 index.ts:5:13 +mw index.ts:390:33 index.ts:415:16 GET /r9 192 index.ts:5:13 +mw index.ts:392:33 index.ts:415:16 GET /r9 193 index.ts:5:13 +mw index.ts:394:33 index.ts:415:16 GET /r9 194 index.ts:5:13 +mw index.ts:396:33 index.ts:415:16 GET /r9 195 index.ts:5:13 +mw index.ts:398:33 index.ts:415:16 GET /r9 196 index.ts:5:13 +mw index.ts:400:33 index.ts:415:16 GET /r9 197 index.ts:5:13 +mw index.ts:402:33 index.ts:415:16 GET /r9 198 index.ts:5:13 +mw index.ts:404:33 index.ts:415:16 GET /r9 199 index.ts:5:13 +regby index.ts:1:1 index.ts:406:16 GET /r0 index.ts:5:13 +regby index.ts:1:1 index.ts:407:16 GET /r1 index.ts:5:13 +regby index.ts:1:1 index.ts:408:16 GET /r2 index.ts:5:13 +regby index.ts:1:1 index.ts:409:16 GET /r3 index.ts:5:13 +regby index.ts:1:1 index.ts:410:16 GET /r4 index.ts:5:13 +regby index.ts:1:1 index.ts:411:16 GET /r5 index.ts:5:13 +regby index.ts:1:1 index.ts:412:16 GET /r6 index.ts:5:13 +regby index.ts:1:1 index.ts:413:16 GET /r7 index.ts:5:13 +regby index.ts:1:1 index.ts:414:16 GET /r8 index.ts:5:13 +regby index.ts:1:1 index.ts:415:16 GET /r9 index.ts:5:13 diff --git a/graph/test/typescript/hono/expected/scale/routes-2k.entries b/graph/test/typescript/hono/expected/scale/routes-2k.entries new file mode 100644 index 000000000..03323bffb --- /dev/null +++ b/graph/test/typescript/hono/expected/scale/routes-2k.entries @@ -0,0 +1,2002 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: reviewed by count and endpoints: 2,000 routes over 20 mounted modules, first /m0/res0/:id, last PATCH /m19/res99/:id; runtime agrees on all +# census: route_mount_literal, verb_literal_path (scale: 2,000 routes; generated by tools/gen-scale.mjs) +entry mod0.ts:100:23 http +entry mod0.ts:101:22 http +entry mod0.ts:102:25 http +entry mod0.ts:103:24 http +entry mod0.ts:10:22 http +entry mod0.ts:11:21 http +entry mod0.ts:12:24 http +entry mod0.ts:13:23 http +entry mod0.ts:14:22 http +entry mod0.ts:15:23 http +entry mod0.ts:16:22 http +entry mod0.ts:17:25 http +entry mod0.ts:18:24 http +entry mod0.ts:19:22 http +entry mod0.ts:20:23 http +entry mod0.ts:21:22 http +entry mod0.ts:22:25 http +entry mod0.ts:23:24 http +entry mod0.ts:24:22 http +entry mod0.ts:25:23 http +entry mod0.ts:26:22 http +entry mod0.ts:27:25 http +entry mod0.ts:28:24 http +entry mod0.ts:29:22 http +entry mod0.ts:30:23 http +entry mod0.ts:31:22 http +entry mod0.ts:32:25 http +entry mod0.ts:33:24 http +entry mod0.ts:34:22 http +entry mod0.ts:35:23 http +entry mod0.ts:36:22 http +entry mod0.ts:37:25 http +entry mod0.ts:38:24 http +entry mod0.ts:39:22 http +entry mod0.ts:40:23 http +entry mod0.ts:41:22 http +entry mod0.ts:42:25 http +entry mod0.ts:43:24 http +entry mod0.ts:44:22 http +entry mod0.ts:45:23 http +entry mod0.ts:46:22 http +entry mod0.ts:47:25 http +entry mod0.ts:48:24 http +entry mod0.ts:49:22 http +entry mod0.ts:4:21 http +entry mod0.ts:50:23 http +entry mod0.ts:51:22 http +entry mod0.ts:52:25 http +entry mod0.ts:53:24 http +entry mod0.ts:54:22 http +entry mod0.ts:55:23 http +entry mod0.ts:56:22 http +entry mod0.ts:57:25 http +entry mod0.ts:58:24 http +entry mod0.ts:59:22 http +entry mod0.ts:5:22 http +entry mod0.ts:60:23 http +entry mod0.ts:61:22 http +entry mod0.ts:62:25 http +entry mod0.ts:63:24 http +entry mod0.ts:64:22 http +entry mod0.ts:65:23 http +entry mod0.ts:66:22 http +entry mod0.ts:67:25 http +entry mod0.ts:68:24 http +entry mod0.ts:69:22 http +entry mod0.ts:6:21 http +entry mod0.ts:70:23 http +entry mod0.ts:71:22 http +entry mod0.ts:72:25 http +entry mod0.ts:73:24 http +entry mod0.ts:74:22 http +entry mod0.ts:75:23 http +entry mod0.ts:76:22 http +entry mod0.ts:77:25 http +entry mod0.ts:78:24 http +entry mod0.ts:79:22 http +entry mod0.ts:7:24 http +entry mod0.ts:80:23 http +entry mod0.ts:81:22 http +entry mod0.ts:82:25 http +entry mod0.ts:83:24 http +entry mod0.ts:84:22 http +entry mod0.ts:85:23 http +entry mod0.ts:86:22 http +entry mod0.ts:87:25 http +entry mod0.ts:88:24 http +entry mod0.ts:89:22 http +entry mod0.ts:8:23 http +entry mod0.ts:90:23 http +entry mod0.ts:91:22 http +entry mod0.ts:92:25 http +entry mod0.ts:93:24 http +entry mod0.ts:94:22 http +entry mod0.ts:95:23 http +entry mod0.ts:96:22 http +entry mod0.ts:97:25 http +entry mod0.ts:98:24 http +entry mod0.ts:99:22 http +entry mod0.ts:9:21 http +entry mod1.ts:100:23 http +entry mod1.ts:101:22 http +entry mod1.ts:102:25 http +entry mod1.ts:103:24 http +entry mod1.ts:10:22 http +entry mod1.ts:11:21 http +entry mod1.ts:12:24 http +entry mod1.ts:13:23 http +entry mod1.ts:14:22 http +entry mod1.ts:15:23 http +entry mod1.ts:16:22 http +entry mod1.ts:17:25 http +entry mod1.ts:18:24 http +entry mod1.ts:19:22 http +entry mod1.ts:20:23 http +entry mod1.ts:21:22 http +entry mod1.ts:22:25 http +entry mod1.ts:23:24 http +entry mod1.ts:24:22 http +entry mod1.ts:25:23 http +entry mod1.ts:26:22 http +entry mod1.ts:27:25 http +entry mod1.ts:28:24 http +entry mod1.ts:29:22 http +entry mod1.ts:30:23 http +entry mod1.ts:31:22 http +entry mod1.ts:32:25 http +entry mod1.ts:33:24 http +entry mod1.ts:34:22 http +entry mod1.ts:35:23 http +entry mod1.ts:36:22 http +entry mod1.ts:37:25 http +entry mod1.ts:38:24 http +entry mod1.ts:39:22 http +entry mod1.ts:40:23 http +entry mod1.ts:41:22 http +entry mod1.ts:42:25 http +entry mod1.ts:43:24 http +entry mod1.ts:44:22 http +entry mod1.ts:45:23 http +entry mod1.ts:46:22 http +entry mod1.ts:47:25 http +entry mod1.ts:48:24 http +entry mod1.ts:49:22 http +entry mod1.ts:4:21 http +entry mod1.ts:50:23 http +entry mod1.ts:51:22 http +entry mod1.ts:52:25 http +entry mod1.ts:53:24 http +entry mod1.ts:54:22 http +entry mod1.ts:55:23 http +entry mod1.ts:56:22 http +entry mod1.ts:57:25 http +entry mod1.ts:58:24 http +entry mod1.ts:59:22 http +entry mod1.ts:5:22 http +entry mod1.ts:60:23 http +entry mod1.ts:61:22 http +entry mod1.ts:62:25 http +entry mod1.ts:63:24 http +entry mod1.ts:64:22 http +entry mod1.ts:65:23 http +entry mod1.ts:66:22 http +entry mod1.ts:67:25 http +entry mod1.ts:68:24 http +entry mod1.ts:69:22 http +entry mod1.ts:6:21 http +entry mod1.ts:70:23 http +entry mod1.ts:71:22 http +entry mod1.ts:72:25 http +entry mod1.ts:73:24 http +entry mod1.ts:74:22 http +entry mod1.ts:75:23 http +entry mod1.ts:76:22 http +entry mod1.ts:77:25 http +entry mod1.ts:78:24 http +entry mod1.ts:79:22 http +entry mod1.ts:7:24 http +entry mod1.ts:80:23 http +entry mod1.ts:81:22 http +entry mod1.ts:82:25 http +entry mod1.ts:83:24 http +entry mod1.ts:84:22 http +entry mod1.ts:85:23 http +entry mod1.ts:86:22 http +entry mod1.ts:87:25 http +entry mod1.ts:88:24 http +entry mod1.ts:89:22 http +entry mod1.ts:8:23 http +entry mod1.ts:90:23 http +entry mod1.ts:91:22 http +entry mod1.ts:92:25 http +entry mod1.ts:93:24 http +entry mod1.ts:94:22 http +entry mod1.ts:95:23 http +entry mod1.ts:96:22 http +entry mod1.ts:97:25 http +entry mod1.ts:98:24 http +entry mod1.ts:99:22 http +entry mod1.ts:9:21 http +entry mod10.ts:100:24 http +entry mod10.ts:101:23 http +entry mod10.ts:102:26 http +entry mod10.ts:103:25 http +entry mod10.ts:10:23 http +entry mod10.ts:11:22 http +entry mod10.ts:12:25 http +entry mod10.ts:13:24 http +entry mod10.ts:14:23 http +entry mod10.ts:15:24 http +entry mod10.ts:16:23 http +entry mod10.ts:17:26 http +entry mod10.ts:18:25 http +entry mod10.ts:19:23 http +entry mod10.ts:20:24 http +entry mod10.ts:21:23 http +entry mod10.ts:22:26 http +entry mod10.ts:23:25 http +entry mod10.ts:24:23 http +entry mod10.ts:25:24 http +entry mod10.ts:26:23 http +entry mod10.ts:27:26 http +entry mod10.ts:28:25 http +entry mod10.ts:29:23 http +entry mod10.ts:30:24 http +entry mod10.ts:31:23 http +entry mod10.ts:32:26 http +entry mod10.ts:33:25 http +entry mod10.ts:34:23 http +entry mod10.ts:35:24 http +entry mod10.ts:36:23 http +entry mod10.ts:37:26 http +entry mod10.ts:38:25 http +entry mod10.ts:39:23 http +entry mod10.ts:40:24 http +entry mod10.ts:41:23 http +entry mod10.ts:42:26 http +entry mod10.ts:43:25 http +entry mod10.ts:44:23 http +entry mod10.ts:45:24 http +entry mod10.ts:46:23 http +entry mod10.ts:47:26 http +entry mod10.ts:48:25 http +entry mod10.ts:49:23 http +entry mod10.ts:4:22 http +entry mod10.ts:50:24 http +entry mod10.ts:51:23 http +entry mod10.ts:52:26 http +entry mod10.ts:53:25 http +entry mod10.ts:54:23 http +entry mod10.ts:55:24 http +entry mod10.ts:56:23 http +entry mod10.ts:57:26 http +entry mod10.ts:58:25 http +entry mod10.ts:59:23 http +entry mod10.ts:5:23 http +entry mod10.ts:60:24 http +entry mod10.ts:61:23 http +entry mod10.ts:62:26 http +entry mod10.ts:63:25 http +entry mod10.ts:64:23 http +entry mod10.ts:65:24 http +entry mod10.ts:66:23 http +entry mod10.ts:67:26 http +entry mod10.ts:68:25 http +entry mod10.ts:69:23 http +entry mod10.ts:6:22 http +entry mod10.ts:70:24 http +entry mod10.ts:71:23 http +entry mod10.ts:72:26 http +entry mod10.ts:73:25 http +entry mod10.ts:74:23 http +entry mod10.ts:75:24 http +entry mod10.ts:76:23 http +entry mod10.ts:77:26 http +entry mod10.ts:78:25 http +entry mod10.ts:79:23 http +entry mod10.ts:7:25 http +entry mod10.ts:80:24 http +entry mod10.ts:81:23 http +entry mod10.ts:82:26 http +entry mod10.ts:83:25 http +entry mod10.ts:84:23 http +entry mod10.ts:85:24 http +entry mod10.ts:86:23 http +entry mod10.ts:87:26 http +entry mod10.ts:88:25 http +entry mod10.ts:89:23 http +entry mod10.ts:8:24 http +entry mod10.ts:90:24 http +entry mod10.ts:91:23 http +entry mod10.ts:92:26 http +entry mod10.ts:93:25 http +entry mod10.ts:94:23 http +entry mod10.ts:95:24 http +entry mod10.ts:96:23 http +entry mod10.ts:97:26 http +entry mod10.ts:98:25 http +entry mod10.ts:99:23 http +entry mod10.ts:9:22 http +entry mod11.ts:100:24 http +entry mod11.ts:101:23 http +entry mod11.ts:102:26 http +entry mod11.ts:103:25 http +entry mod11.ts:10:23 http +entry mod11.ts:11:22 http +entry mod11.ts:12:25 http +entry mod11.ts:13:24 http +entry mod11.ts:14:23 http +entry mod11.ts:15:24 http +entry mod11.ts:16:23 http +entry mod11.ts:17:26 http +entry mod11.ts:18:25 http +entry mod11.ts:19:23 http +entry mod11.ts:20:24 http +entry mod11.ts:21:23 http +entry mod11.ts:22:26 http +entry mod11.ts:23:25 http +entry mod11.ts:24:23 http +entry mod11.ts:25:24 http +entry mod11.ts:26:23 http +entry mod11.ts:27:26 http +entry mod11.ts:28:25 http +entry mod11.ts:29:23 http +entry mod11.ts:30:24 http +entry mod11.ts:31:23 http +entry mod11.ts:32:26 http +entry mod11.ts:33:25 http +entry mod11.ts:34:23 http +entry mod11.ts:35:24 http +entry mod11.ts:36:23 http +entry mod11.ts:37:26 http +entry mod11.ts:38:25 http +entry mod11.ts:39:23 http +entry mod11.ts:40:24 http +entry mod11.ts:41:23 http +entry mod11.ts:42:26 http +entry mod11.ts:43:25 http +entry mod11.ts:44:23 http +entry mod11.ts:45:24 http +entry mod11.ts:46:23 http +entry mod11.ts:47:26 http +entry mod11.ts:48:25 http +entry mod11.ts:49:23 http +entry mod11.ts:4:22 http +entry mod11.ts:50:24 http +entry mod11.ts:51:23 http +entry mod11.ts:52:26 http +entry mod11.ts:53:25 http +entry mod11.ts:54:23 http +entry mod11.ts:55:24 http +entry mod11.ts:56:23 http +entry mod11.ts:57:26 http +entry mod11.ts:58:25 http +entry mod11.ts:59:23 http +entry mod11.ts:5:23 http +entry mod11.ts:60:24 http +entry mod11.ts:61:23 http +entry mod11.ts:62:26 http +entry mod11.ts:63:25 http +entry mod11.ts:64:23 http +entry mod11.ts:65:24 http +entry mod11.ts:66:23 http +entry mod11.ts:67:26 http +entry mod11.ts:68:25 http +entry mod11.ts:69:23 http +entry mod11.ts:6:22 http +entry mod11.ts:70:24 http +entry mod11.ts:71:23 http +entry mod11.ts:72:26 http +entry mod11.ts:73:25 http +entry mod11.ts:74:23 http +entry mod11.ts:75:24 http +entry mod11.ts:76:23 http +entry mod11.ts:77:26 http +entry mod11.ts:78:25 http +entry mod11.ts:79:23 http +entry mod11.ts:7:25 http +entry mod11.ts:80:24 http +entry mod11.ts:81:23 http +entry mod11.ts:82:26 http +entry mod11.ts:83:25 http +entry mod11.ts:84:23 http +entry mod11.ts:85:24 http +entry mod11.ts:86:23 http +entry mod11.ts:87:26 http +entry mod11.ts:88:25 http +entry mod11.ts:89:23 http +entry mod11.ts:8:24 http +entry mod11.ts:90:24 http +entry mod11.ts:91:23 http +entry mod11.ts:92:26 http +entry mod11.ts:93:25 http +entry mod11.ts:94:23 http +entry mod11.ts:95:24 http +entry mod11.ts:96:23 http +entry mod11.ts:97:26 http +entry mod11.ts:98:25 http +entry mod11.ts:99:23 http +entry mod11.ts:9:22 http +entry mod12.ts:100:24 http +entry mod12.ts:101:23 http +entry mod12.ts:102:26 http +entry mod12.ts:103:25 http +entry mod12.ts:10:23 http +entry mod12.ts:11:22 http +entry mod12.ts:12:25 http +entry mod12.ts:13:24 http +entry mod12.ts:14:23 http +entry mod12.ts:15:24 http +entry mod12.ts:16:23 http +entry mod12.ts:17:26 http +entry mod12.ts:18:25 http +entry mod12.ts:19:23 http +entry mod12.ts:20:24 http +entry mod12.ts:21:23 http +entry mod12.ts:22:26 http +entry mod12.ts:23:25 http +entry mod12.ts:24:23 http +entry mod12.ts:25:24 http +entry mod12.ts:26:23 http +entry mod12.ts:27:26 http +entry mod12.ts:28:25 http +entry mod12.ts:29:23 http +entry mod12.ts:30:24 http +entry mod12.ts:31:23 http +entry mod12.ts:32:26 http +entry mod12.ts:33:25 http +entry mod12.ts:34:23 http +entry mod12.ts:35:24 http +entry mod12.ts:36:23 http +entry mod12.ts:37:26 http +entry mod12.ts:38:25 http +entry mod12.ts:39:23 http +entry mod12.ts:40:24 http +entry mod12.ts:41:23 http +entry mod12.ts:42:26 http +entry mod12.ts:43:25 http +entry mod12.ts:44:23 http +entry mod12.ts:45:24 http +entry mod12.ts:46:23 http +entry mod12.ts:47:26 http +entry mod12.ts:48:25 http +entry mod12.ts:49:23 http +entry mod12.ts:4:22 http +entry mod12.ts:50:24 http +entry mod12.ts:51:23 http +entry mod12.ts:52:26 http +entry mod12.ts:53:25 http +entry mod12.ts:54:23 http +entry mod12.ts:55:24 http +entry mod12.ts:56:23 http +entry mod12.ts:57:26 http +entry mod12.ts:58:25 http +entry mod12.ts:59:23 http +entry mod12.ts:5:23 http +entry mod12.ts:60:24 http +entry mod12.ts:61:23 http +entry mod12.ts:62:26 http +entry mod12.ts:63:25 http +entry mod12.ts:64:23 http +entry mod12.ts:65:24 http +entry mod12.ts:66:23 http +entry mod12.ts:67:26 http +entry mod12.ts:68:25 http +entry mod12.ts:69:23 http +entry mod12.ts:6:22 http +entry mod12.ts:70:24 http +entry mod12.ts:71:23 http +entry mod12.ts:72:26 http +entry mod12.ts:73:25 http +entry mod12.ts:74:23 http +entry mod12.ts:75:24 http +entry mod12.ts:76:23 http +entry mod12.ts:77:26 http +entry mod12.ts:78:25 http +entry mod12.ts:79:23 http +entry mod12.ts:7:25 http +entry mod12.ts:80:24 http +entry mod12.ts:81:23 http +entry mod12.ts:82:26 http +entry mod12.ts:83:25 http +entry mod12.ts:84:23 http +entry mod12.ts:85:24 http +entry mod12.ts:86:23 http +entry mod12.ts:87:26 http +entry mod12.ts:88:25 http +entry mod12.ts:89:23 http +entry mod12.ts:8:24 http +entry mod12.ts:90:24 http +entry mod12.ts:91:23 http +entry mod12.ts:92:26 http +entry mod12.ts:93:25 http +entry mod12.ts:94:23 http +entry mod12.ts:95:24 http +entry mod12.ts:96:23 http +entry mod12.ts:97:26 http +entry mod12.ts:98:25 http +entry mod12.ts:99:23 http +entry mod12.ts:9:22 http +entry mod13.ts:100:24 http +entry mod13.ts:101:23 http +entry mod13.ts:102:26 http +entry mod13.ts:103:25 http +entry mod13.ts:10:23 http +entry mod13.ts:11:22 http +entry mod13.ts:12:25 http +entry mod13.ts:13:24 http +entry mod13.ts:14:23 http +entry mod13.ts:15:24 http +entry mod13.ts:16:23 http +entry mod13.ts:17:26 http +entry mod13.ts:18:25 http +entry mod13.ts:19:23 http +entry mod13.ts:20:24 http +entry mod13.ts:21:23 http +entry mod13.ts:22:26 http +entry mod13.ts:23:25 http +entry mod13.ts:24:23 http +entry mod13.ts:25:24 http +entry mod13.ts:26:23 http +entry mod13.ts:27:26 http +entry mod13.ts:28:25 http +entry mod13.ts:29:23 http +entry mod13.ts:30:24 http +entry mod13.ts:31:23 http +entry mod13.ts:32:26 http +entry mod13.ts:33:25 http +entry mod13.ts:34:23 http +entry mod13.ts:35:24 http +entry mod13.ts:36:23 http +entry mod13.ts:37:26 http +entry mod13.ts:38:25 http +entry mod13.ts:39:23 http +entry mod13.ts:40:24 http +entry mod13.ts:41:23 http +entry mod13.ts:42:26 http +entry mod13.ts:43:25 http +entry mod13.ts:44:23 http +entry mod13.ts:45:24 http +entry mod13.ts:46:23 http +entry mod13.ts:47:26 http +entry mod13.ts:48:25 http +entry mod13.ts:49:23 http +entry mod13.ts:4:22 http +entry mod13.ts:50:24 http +entry mod13.ts:51:23 http +entry mod13.ts:52:26 http +entry mod13.ts:53:25 http +entry mod13.ts:54:23 http +entry mod13.ts:55:24 http +entry mod13.ts:56:23 http +entry mod13.ts:57:26 http +entry mod13.ts:58:25 http +entry mod13.ts:59:23 http +entry mod13.ts:5:23 http +entry mod13.ts:60:24 http +entry mod13.ts:61:23 http +entry mod13.ts:62:26 http +entry mod13.ts:63:25 http +entry mod13.ts:64:23 http +entry mod13.ts:65:24 http +entry mod13.ts:66:23 http +entry mod13.ts:67:26 http +entry mod13.ts:68:25 http +entry mod13.ts:69:23 http +entry mod13.ts:6:22 http +entry mod13.ts:70:24 http +entry mod13.ts:71:23 http +entry mod13.ts:72:26 http +entry mod13.ts:73:25 http +entry mod13.ts:74:23 http +entry mod13.ts:75:24 http +entry mod13.ts:76:23 http +entry mod13.ts:77:26 http +entry mod13.ts:78:25 http +entry mod13.ts:79:23 http +entry mod13.ts:7:25 http +entry mod13.ts:80:24 http +entry mod13.ts:81:23 http +entry mod13.ts:82:26 http +entry mod13.ts:83:25 http +entry mod13.ts:84:23 http +entry mod13.ts:85:24 http +entry mod13.ts:86:23 http +entry mod13.ts:87:26 http +entry mod13.ts:88:25 http +entry mod13.ts:89:23 http +entry mod13.ts:8:24 http +entry mod13.ts:90:24 http +entry mod13.ts:91:23 http +entry mod13.ts:92:26 http +entry mod13.ts:93:25 http +entry mod13.ts:94:23 http +entry mod13.ts:95:24 http +entry mod13.ts:96:23 http +entry mod13.ts:97:26 http +entry mod13.ts:98:25 http +entry mod13.ts:99:23 http +entry mod13.ts:9:22 http +entry mod14.ts:100:24 http +entry mod14.ts:101:23 http +entry mod14.ts:102:26 http +entry mod14.ts:103:25 http +entry mod14.ts:10:23 http +entry mod14.ts:11:22 http +entry mod14.ts:12:25 http +entry mod14.ts:13:24 http +entry mod14.ts:14:23 http +entry mod14.ts:15:24 http +entry mod14.ts:16:23 http +entry mod14.ts:17:26 http +entry mod14.ts:18:25 http +entry mod14.ts:19:23 http +entry mod14.ts:20:24 http +entry mod14.ts:21:23 http +entry mod14.ts:22:26 http +entry mod14.ts:23:25 http +entry mod14.ts:24:23 http +entry mod14.ts:25:24 http +entry mod14.ts:26:23 http +entry mod14.ts:27:26 http +entry mod14.ts:28:25 http +entry mod14.ts:29:23 http +entry mod14.ts:30:24 http +entry mod14.ts:31:23 http +entry mod14.ts:32:26 http +entry mod14.ts:33:25 http +entry mod14.ts:34:23 http +entry mod14.ts:35:24 http +entry mod14.ts:36:23 http +entry mod14.ts:37:26 http +entry mod14.ts:38:25 http +entry mod14.ts:39:23 http +entry mod14.ts:40:24 http +entry mod14.ts:41:23 http +entry mod14.ts:42:26 http +entry mod14.ts:43:25 http +entry mod14.ts:44:23 http +entry mod14.ts:45:24 http +entry mod14.ts:46:23 http +entry mod14.ts:47:26 http +entry mod14.ts:48:25 http +entry mod14.ts:49:23 http +entry mod14.ts:4:22 http +entry mod14.ts:50:24 http +entry mod14.ts:51:23 http +entry mod14.ts:52:26 http +entry mod14.ts:53:25 http +entry mod14.ts:54:23 http +entry mod14.ts:55:24 http +entry mod14.ts:56:23 http +entry mod14.ts:57:26 http +entry mod14.ts:58:25 http +entry mod14.ts:59:23 http +entry mod14.ts:5:23 http +entry mod14.ts:60:24 http +entry mod14.ts:61:23 http +entry mod14.ts:62:26 http +entry mod14.ts:63:25 http +entry mod14.ts:64:23 http +entry mod14.ts:65:24 http +entry mod14.ts:66:23 http +entry mod14.ts:67:26 http +entry mod14.ts:68:25 http +entry mod14.ts:69:23 http +entry mod14.ts:6:22 http +entry mod14.ts:70:24 http +entry mod14.ts:71:23 http +entry mod14.ts:72:26 http +entry mod14.ts:73:25 http +entry mod14.ts:74:23 http +entry mod14.ts:75:24 http +entry mod14.ts:76:23 http +entry mod14.ts:77:26 http +entry mod14.ts:78:25 http +entry mod14.ts:79:23 http +entry mod14.ts:7:25 http +entry mod14.ts:80:24 http +entry mod14.ts:81:23 http +entry mod14.ts:82:26 http +entry mod14.ts:83:25 http +entry mod14.ts:84:23 http +entry mod14.ts:85:24 http +entry mod14.ts:86:23 http +entry mod14.ts:87:26 http +entry mod14.ts:88:25 http +entry mod14.ts:89:23 http +entry mod14.ts:8:24 http +entry mod14.ts:90:24 http +entry mod14.ts:91:23 http +entry mod14.ts:92:26 http +entry mod14.ts:93:25 http +entry mod14.ts:94:23 http +entry mod14.ts:95:24 http +entry mod14.ts:96:23 http +entry mod14.ts:97:26 http +entry mod14.ts:98:25 http +entry mod14.ts:99:23 http +entry mod14.ts:9:22 http +entry mod15.ts:100:24 http +entry mod15.ts:101:23 http +entry mod15.ts:102:26 http +entry mod15.ts:103:25 http +entry mod15.ts:10:23 http +entry mod15.ts:11:22 http +entry mod15.ts:12:25 http +entry mod15.ts:13:24 http +entry mod15.ts:14:23 http +entry mod15.ts:15:24 http +entry mod15.ts:16:23 http +entry mod15.ts:17:26 http +entry mod15.ts:18:25 http +entry mod15.ts:19:23 http +entry mod15.ts:20:24 http +entry mod15.ts:21:23 http +entry mod15.ts:22:26 http +entry mod15.ts:23:25 http +entry mod15.ts:24:23 http +entry mod15.ts:25:24 http +entry mod15.ts:26:23 http +entry mod15.ts:27:26 http +entry mod15.ts:28:25 http +entry mod15.ts:29:23 http +entry mod15.ts:30:24 http +entry mod15.ts:31:23 http +entry mod15.ts:32:26 http +entry mod15.ts:33:25 http +entry mod15.ts:34:23 http +entry mod15.ts:35:24 http +entry mod15.ts:36:23 http +entry mod15.ts:37:26 http +entry mod15.ts:38:25 http +entry mod15.ts:39:23 http +entry mod15.ts:40:24 http +entry mod15.ts:41:23 http +entry mod15.ts:42:26 http +entry mod15.ts:43:25 http +entry mod15.ts:44:23 http +entry mod15.ts:45:24 http +entry mod15.ts:46:23 http +entry mod15.ts:47:26 http +entry mod15.ts:48:25 http +entry mod15.ts:49:23 http +entry mod15.ts:4:22 http +entry mod15.ts:50:24 http +entry mod15.ts:51:23 http +entry mod15.ts:52:26 http +entry mod15.ts:53:25 http +entry mod15.ts:54:23 http +entry mod15.ts:55:24 http +entry mod15.ts:56:23 http +entry mod15.ts:57:26 http +entry mod15.ts:58:25 http +entry mod15.ts:59:23 http +entry mod15.ts:5:23 http +entry mod15.ts:60:24 http +entry mod15.ts:61:23 http +entry mod15.ts:62:26 http +entry mod15.ts:63:25 http +entry mod15.ts:64:23 http +entry mod15.ts:65:24 http +entry mod15.ts:66:23 http +entry mod15.ts:67:26 http +entry mod15.ts:68:25 http +entry mod15.ts:69:23 http +entry mod15.ts:6:22 http +entry mod15.ts:70:24 http +entry mod15.ts:71:23 http +entry mod15.ts:72:26 http +entry mod15.ts:73:25 http +entry mod15.ts:74:23 http +entry mod15.ts:75:24 http +entry mod15.ts:76:23 http +entry mod15.ts:77:26 http +entry mod15.ts:78:25 http +entry mod15.ts:79:23 http +entry mod15.ts:7:25 http +entry mod15.ts:80:24 http +entry mod15.ts:81:23 http +entry mod15.ts:82:26 http +entry mod15.ts:83:25 http +entry mod15.ts:84:23 http +entry mod15.ts:85:24 http +entry mod15.ts:86:23 http +entry mod15.ts:87:26 http +entry mod15.ts:88:25 http +entry mod15.ts:89:23 http +entry mod15.ts:8:24 http +entry mod15.ts:90:24 http +entry mod15.ts:91:23 http +entry mod15.ts:92:26 http +entry mod15.ts:93:25 http +entry mod15.ts:94:23 http +entry mod15.ts:95:24 http +entry mod15.ts:96:23 http +entry mod15.ts:97:26 http +entry mod15.ts:98:25 http +entry mod15.ts:99:23 http +entry mod15.ts:9:22 http +entry mod16.ts:100:24 http +entry mod16.ts:101:23 http +entry mod16.ts:102:26 http +entry mod16.ts:103:25 http +entry mod16.ts:10:23 http +entry mod16.ts:11:22 http +entry mod16.ts:12:25 http +entry mod16.ts:13:24 http +entry mod16.ts:14:23 http +entry mod16.ts:15:24 http +entry mod16.ts:16:23 http +entry mod16.ts:17:26 http +entry mod16.ts:18:25 http +entry mod16.ts:19:23 http +entry mod16.ts:20:24 http +entry mod16.ts:21:23 http +entry mod16.ts:22:26 http +entry mod16.ts:23:25 http +entry mod16.ts:24:23 http +entry mod16.ts:25:24 http +entry mod16.ts:26:23 http +entry mod16.ts:27:26 http +entry mod16.ts:28:25 http +entry mod16.ts:29:23 http +entry mod16.ts:30:24 http +entry mod16.ts:31:23 http +entry mod16.ts:32:26 http +entry mod16.ts:33:25 http +entry mod16.ts:34:23 http +entry mod16.ts:35:24 http +entry mod16.ts:36:23 http +entry mod16.ts:37:26 http +entry mod16.ts:38:25 http +entry mod16.ts:39:23 http +entry mod16.ts:40:24 http +entry mod16.ts:41:23 http +entry mod16.ts:42:26 http +entry mod16.ts:43:25 http +entry mod16.ts:44:23 http +entry mod16.ts:45:24 http +entry mod16.ts:46:23 http +entry mod16.ts:47:26 http +entry mod16.ts:48:25 http +entry mod16.ts:49:23 http +entry mod16.ts:4:22 http +entry mod16.ts:50:24 http +entry mod16.ts:51:23 http +entry mod16.ts:52:26 http +entry mod16.ts:53:25 http +entry mod16.ts:54:23 http +entry mod16.ts:55:24 http +entry mod16.ts:56:23 http +entry mod16.ts:57:26 http +entry mod16.ts:58:25 http +entry mod16.ts:59:23 http +entry mod16.ts:5:23 http +entry mod16.ts:60:24 http +entry mod16.ts:61:23 http +entry mod16.ts:62:26 http +entry mod16.ts:63:25 http +entry mod16.ts:64:23 http +entry mod16.ts:65:24 http +entry mod16.ts:66:23 http +entry mod16.ts:67:26 http +entry mod16.ts:68:25 http +entry mod16.ts:69:23 http +entry mod16.ts:6:22 http +entry mod16.ts:70:24 http +entry mod16.ts:71:23 http +entry mod16.ts:72:26 http +entry mod16.ts:73:25 http +entry mod16.ts:74:23 http +entry mod16.ts:75:24 http +entry mod16.ts:76:23 http +entry mod16.ts:77:26 http +entry mod16.ts:78:25 http +entry mod16.ts:79:23 http +entry mod16.ts:7:25 http +entry mod16.ts:80:24 http +entry mod16.ts:81:23 http +entry mod16.ts:82:26 http +entry mod16.ts:83:25 http +entry mod16.ts:84:23 http +entry mod16.ts:85:24 http +entry mod16.ts:86:23 http +entry mod16.ts:87:26 http +entry mod16.ts:88:25 http +entry mod16.ts:89:23 http +entry mod16.ts:8:24 http +entry mod16.ts:90:24 http +entry mod16.ts:91:23 http +entry mod16.ts:92:26 http +entry mod16.ts:93:25 http +entry mod16.ts:94:23 http +entry mod16.ts:95:24 http +entry mod16.ts:96:23 http +entry mod16.ts:97:26 http +entry mod16.ts:98:25 http +entry mod16.ts:99:23 http +entry mod16.ts:9:22 http +entry mod17.ts:100:24 http +entry mod17.ts:101:23 http +entry mod17.ts:102:26 http +entry mod17.ts:103:25 http +entry mod17.ts:10:23 http +entry mod17.ts:11:22 http +entry mod17.ts:12:25 http +entry mod17.ts:13:24 http +entry mod17.ts:14:23 http +entry mod17.ts:15:24 http +entry mod17.ts:16:23 http +entry mod17.ts:17:26 http +entry mod17.ts:18:25 http +entry mod17.ts:19:23 http +entry mod17.ts:20:24 http +entry mod17.ts:21:23 http +entry mod17.ts:22:26 http +entry mod17.ts:23:25 http +entry mod17.ts:24:23 http +entry mod17.ts:25:24 http +entry mod17.ts:26:23 http +entry mod17.ts:27:26 http +entry mod17.ts:28:25 http +entry mod17.ts:29:23 http +entry mod17.ts:30:24 http +entry mod17.ts:31:23 http +entry mod17.ts:32:26 http +entry mod17.ts:33:25 http +entry mod17.ts:34:23 http +entry mod17.ts:35:24 http +entry mod17.ts:36:23 http +entry mod17.ts:37:26 http +entry mod17.ts:38:25 http +entry mod17.ts:39:23 http +entry mod17.ts:40:24 http +entry mod17.ts:41:23 http +entry mod17.ts:42:26 http +entry mod17.ts:43:25 http +entry mod17.ts:44:23 http +entry mod17.ts:45:24 http +entry mod17.ts:46:23 http +entry mod17.ts:47:26 http +entry mod17.ts:48:25 http +entry mod17.ts:49:23 http +entry mod17.ts:4:22 http +entry mod17.ts:50:24 http +entry mod17.ts:51:23 http +entry mod17.ts:52:26 http +entry mod17.ts:53:25 http +entry mod17.ts:54:23 http +entry mod17.ts:55:24 http +entry mod17.ts:56:23 http +entry mod17.ts:57:26 http +entry mod17.ts:58:25 http +entry mod17.ts:59:23 http +entry mod17.ts:5:23 http +entry mod17.ts:60:24 http +entry mod17.ts:61:23 http +entry mod17.ts:62:26 http +entry mod17.ts:63:25 http +entry mod17.ts:64:23 http +entry mod17.ts:65:24 http +entry mod17.ts:66:23 http +entry mod17.ts:67:26 http +entry mod17.ts:68:25 http +entry mod17.ts:69:23 http +entry mod17.ts:6:22 http +entry mod17.ts:70:24 http +entry mod17.ts:71:23 http +entry mod17.ts:72:26 http +entry mod17.ts:73:25 http +entry mod17.ts:74:23 http +entry mod17.ts:75:24 http +entry mod17.ts:76:23 http +entry mod17.ts:77:26 http +entry mod17.ts:78:25 http +entry mod17.ts:79:23 http +entry mod17.ts:7:25 http +entry mod17.ts:80:24 http +entry mod17.ts:81:23 http +entry mod17.ts:82:26 http +entry mod17.ts:83:25 http +entry mod17.ts:84:23 http +entry mod17.ts:85:24 http +entry mod17.ts:86:23 http +entry mod17.ts:87:26 http +entry mod17.ts:88:25 http +entry mod17.ts:89:23 http +entry mod17.ts:8:24 http +entry mod17.ts:90:24 http +entry mod17.ts:91:23 http +entry mod17.ts:92:26 http +entry mod17.ts:93:25 http +entry mod17.ts:94:23 http +entry mod17.ts:95:24 http +entry mod17.ts:96:23 http +entry mod17.ts:97:26 http +entry mod17.ts:98:25 http +entry mod17.ts:99:23 http +entry mod17.ts:9:22 http +entry mod18.ts:100:24 http +entry mod18.ts:101:23 http +entry mod18.ts:102:26 http +entry mod18.ts:103:25 http +entry mod18.ts:10:23 http +entry mod18.ts:11:22 http +entry mod18.ts:12:25 http +entry mod18.ts:13:24 http +entry mod18.ts:14:23 http +entry mod18.ts:15:24 http +entry mod18.ts:16:23 http +entry mod18.ts:17:26 http +entry mod18.ts:18:25 http +entry mod18.ts:19:23 http +entry mod18.ts:20:24 http +entry mod18.ts:21:23 http +entry mod18.ts:22:26 http +entry mod18.ts:23:25 http +entry mod18.ts:24:23 http +entry mod18.ts:25:24 http +entry mod18.ts:26:23 http +entry mod18.ts:27:26 http +entry mod18.ts:28:25 http +entry mod18.ts:29:23 http +entry mod18.ts:30:24 http +entry mod18.ts:31:23 http +entry mod18.ts:32:26 http +entry mod18.ts:33:25 http +entry mod18.ts:34:23 http +entry mod18.ts:35:24 http +entry mod18.ts:36:23 http +entry mod18.ts:37:26 http +entry mod18.ts:38:25 http +entry mod18.ts:39:23 http +entry mod18.ts:40:24 http +entry mod18.ts:41:23 http +entry mod18.ts:42:26 http +entry mod18.ts:43:25 http +entry mod18.ts:44:23 http +entry mod18.ts:45:24 http +entry mod18.ts:46:23 http +entry mod18.ts:47:26 http +entry mod18.ts:48:25 http +entry mod18.ts:49:23 http +entry mod18.ts:4:22 http +entry mod18.ts:50:24 http +entry mod18.ts:51:23 http +entry mod18.ts:52:26 http +entry mod18.ts:53:25 http +entry mod18.ts:54:23 http +entry mod18.ts:55:24 http +entry mod18.ts:56:23 http +entry mod18.ts:57:26 http +entry mod18.ts:58:25 http +entry mod18.ts:59:23 http +entry mod18.ts:5:23 http +entry mod18.ts:60:24 http +entry mod18.ts:61:23 http +entry mod18.ts:62:26 http +entry mod18.ts:63:25 http +entry mod18.ts:64:23 http +entry mod18.ts:65:24 http +entry mod18.ts:66:23 http +entry mod18.ts:67:26 http +entry mod18.ts:68:25 http +entry mod18.ts:69:23 http +entry mod18.ts:6:22 http +entry mod18.ts:70:24 http +entry mod18.ts:71:23 http +entry mod18.ts:72:26 http +entry mod18.ts:73:25 http +entry mod18.ts:74:23 http +entry mod18.ts:75:24 http +entry mod18.ts:76:23 http +entry mod18.ts:77:26 http +entry mod18.ts:78:25 http +entry mod18.ts:79:23 http +entry mod18.ts:7:25 http +entry mod18.ts:80:24 http +entry mod18.ts:81:23 http +entry mod18.ts:82:26 http +entry mod18.ts:83:25 http +entry mod18.ts:84:23 http +entry mod18.ts:85:24 http +entry mod18.ts:86:23 http +entry mod18.ts:87:26 http +entry mod18.ts:88:25 http +entry mod18.ts:89:23 http +entry mod18.ts:8:24 http +entry mod18.ts:90:24 http +entry mod18.ts:91:23 http +entry mod18.ts:92:26 http +entry mod18.ts:93:25 http +entry mod18.ts:94:23 http +entry mod18.ts:95:24 http +entry mod18.ts:96:23 http +entry mod18.ts:97:26 http +entry mod18.ts:98:25 http +entry mod18.ts:99:23 http +entry mod18.ts:9:22 http +entry mod19.ts:100:24 http +entry mod19.ts:101:23 http +entry mod19.ts:102:26 http +entry mod19.ts:103:25 http +entry mod19.ts:10:23 http +entry mod19.ts:11:22 http +entry mod19.ts:12:25 http +entry mod19.ts:13:24 http +entry mod19.ts:14:23 http +entry mod19.ts:15:24 http +entry mod19.ts:16:23 http +entry mod19.ts:17:26 http +entry mod19.ts:18:25 http +entry mod19.ts:19:23 http +entry mod19.ts:20:24 http +entry mod19.ts:21:23 http +entry mod19.ts:22:26 http +entry mod19.ts:23:25 http +entry mod19.ts:24:23 http +entry mod19.ts:25:24 http +entry mod19.ts:26:23 http +entry mod19.ts:27:26 http +entry mod19.ts:28:25 http +entry mod19.ts:29:23 http +entry mod19.ts:30:24 http +entry mod19.ts:31:23 http +entry mod19.ts:32:26 http +entry mod19.ts:33:25 http +entry mod19.ts:34:23 http +entry mod19.ts:35:24 http +entry mod19.ts:36:23 http +entry mod19.ts:37:26 http +entry mod19.ts:38:25 http +entry mod19.ts:39:23 http +entry mod19.ts:40:24 http +entry mod19.ts:41:23 http +entry mod19.ts:42:26 http +entry mod19.ts:43:25 http +entry mod19.ts:44:23 http +entry mod19.ts:45:24 http +entry mod19.ts:46:23 http +entry mod19.ts:47:26 http +entry mod19.ts:48:25 http +entry mod19.ts:49:23 http +entry mod19.ts:4:22 http +entry mod19.ts:50:24 http +entry mod19.ts:51:23 http +entry mod19.ts:52:26 http +entry mod19.ts:53:25 http +entry mod19.ts:54:23 http +entry mod19.ts:55:24 http +entry mod19.ts:56:23 http +entry mod19.ts:57:26 http +entry mod19.ts:58:25 http +entry mod19.ts:59:23 http +entry mod19.ts:5:23 http +entry mod19.ts:60:24 http +entry mod19.ts:61:23 http +entry mod19.ts:62:26 http +entry mod19.ts:63:25 http +entry mod19.ts:64:23 http +entry mod19.ts:65:24 http +entry mod19.ts:66:23 http +entry mod19.ts:67:26 http +entry mod19.ts:68:25 http +entry mod19.ts:69:23 http +entry mod19.ts:6:22 http +entry mod19.ts:70:24 http +entry mod19.ts:71:23 http +entry mod19.ts:72:26 http +entry mod19.ts:73:25 http +entry mod19.ts:74:23 http +entry mod19.ts:75:24 http +entry mod19.ts:76:23 http +entry mod19.ts:77:26 http +entry mod19.ts:78:25 http +entry mod19.ts:79:23 http +entry mod19.ts:7:25 http +entry mod19.ts:80:24 http +entry mod19.ts:81:23 http +entry mod19.ts:82:26 http +entry mod19.ts:83:25 http +entry mod19.ts:84:23 http +entry mod19.ts:85:24 http +entry mod19.ts:86:23 http +entry mod19.ts:87:26 http +entry mod19.ts:88:25 http +entry mod19.ts:89:23 http +entry mod19.ts:8:24 http +entry mod19.ts:90:24 http +entry mod19.ts:91:23 http +entry mod19.ts:92:26 http +entry mod19.ts:93:25 http +entry mod19.ts:94:23 http +entry mod19.ts:95:24 http +entry mod19.ts:96:23 http +entry mod19.ts:97:26 http +entry mod19.ts:98:25 http +entry mod19.ts:99:23 http +entry mod19.ts:9:22 http +entry mod2.ts:100:23 http +entry mod2.ts:101:22 http +entry mod2.ts:102:25 http +entry mod2.ts:103:24 http +entry mod2.ts:10:22 http +entry mod2.ts:11:21 http +entry mod2.ts:12:24 http +entry mod2.ts:13:23 http +entry mod2.ts:14:22 http +entry mod2.ts:15:23 http +entry mod2.ts:16:22 http +entry mod2.ts:17:25 http +entry mod2.ts:18:24 http +entry mod2.ts:19:22 http +entry mod2.ts:20:23 http +entry mod2.ts:21:22 http +entry mod2.ts:22:25 http +entry mod2.ts:23:24 http +entry mod2.ts:24:22 http +entry mod2.ts:25:23 http +entry mod2.ts:26:22 http +entry mod2.ts:27:25 http +entry mod2.ts:28:24 http +entry mod2.ts:29:22 http +entry mod2.ts:30:23 http +entry mod2.ts:31:22 http +entry mod2.ts:32:25 http +entry mod2.ts:33:24 http +entry mod2.ts:34:22 http +entry mod2.ts:35:23 http +entry mod2.ts:36:22 http +entry mod2.ts:37:25 http +entry mod2.ts:38:24 http +entry mod2.ts:39:22 http +entry mod2.ts:40:23 http +entry mod2.ts:41:22 http +entry mod2.ts:42:25 http +entry mod2.ts:43:24 http +entry mod2.ts:44:22 http +entry mod2.ts:45:23 http +entry mod2.ts:46:22 http +entry mod2.ts:47:25 http +entry mod2.ts:48:24 http +entry mod2.ts:49:22 http +entry mod2.ts:4:21 http +entry mod2.ts:50:23 http +entry mod2.ts:51:22 http +entry mod2.ts:52:25 http +entry mod2.ts:53:24 http +entry mod2.ts:54:22 http +entry mod2.ts:55:23 http +entry mod2.ts:56:22 http +entry mod2.ts:57:25 http +entry mod2.ts:58:24 http +entry mod2.ts:59:22 http +entry mod2.ts:5:22 http +entry mod2.ts:60:23 http +entry mod2.ts:61:22 http +entry mod2.ts:62:25 http +entry mod2.ts:63:24 http +entry mod2.ts:64:22 http +entry mod2.ts:65:23 http +entry mod2.ts:66:22 http +entry mod2.ts:67:25 http +entry mod2.ts:68:24 http +entry mod2.ts:69:22 http +entry mod2.ts:6:21 http +entry mod2.ts:70:23 http +entry mod2.ts:71:22 http +entry mod2.ts:72:25 http +entry mod2.ts:73:24 http +entry mod2.ts:74:22 http +entry mod2.ts:75:23 http +entry mod2.ts:76:22 http +entry mod2.ts:77:25 http +entry mod2.ts:78:24 http +entry mod2.ts:79:22 http +entry mod2.ts:7:24 http +entry mod2.ts:80:23 http +entry mod2.ts:81:22 http +entry mod2.ts:82:25 http +entry mod2.ts:83:24 http +entry mod2.ts:84:22 http +entry mod2.ts:85:23 http +entry mod2.ts:86:22 http +entry mod2.ts:87:25 http +entry mod2.ts:88:24 http +entry mod2.ts:89:22 http +entry mod2.ts:8:23 http +entry mod2.ts:90:23 http +entry mod2.ts:91:22 http +entry mod2.ts:92:25 http +entry mod2.ts:93:24 http +entry mod2.ts:94:22 http +entry mod2.ts:95:23 http +entry mod2.ts:96:22 http +entry mod2.ts:97:25 http +entry mod2.ts:98:24 http +entry mod2.ts:99:22 http +entry mod2.ts:9:21 http +entry mod3.ts:100:23 http +entry mod3.ts:101:22 http +entry mod3.ts:102:25 http +entry mod3.ts:103:24 http +entry mod3.ts:10:22 http +entry mod3.ts:11:21 http +entry mod3.ts:12:24 http +entry mod3.ts:13:23 http +entry mod3.ts:14:22 http +entry mod3.ts:15:23 http +entry mod3.ts:16:22 http +entry mod3.ts:17:25 http +entry mod3.ts:18:24 http +entry mod3.ts:19:22 http +entry mod3.ts:20:23 http +entry mod3.ts:21:22 http +entry mod3.ts:22:25 http +entry mod3.ts:23:24 http +entry mod3.ts:24:22 http +entry mod3.ts:25:23 http +entry mod3.ts:26:22 http +entry mod3.ts:27:25 http +entry mod3.ts:28:24 http +entry mod3.ts:29:22 http +entry mod3.ts:30:23 http +entry mod3.ts:31:22 http +entry mod3.ts:32:25 http +entry mod3.ts:33:24 http +entry mod3.ts:34:22 http +entry mod3.ts:35:23 http +entry mod3.ts:36:22 http +entry mod3.ts:37:25 http +entry mod3.ts:38:24 http +entry mod3.ts:39:22 http +entry mod3.ts:40:23 http +entry mod3.ts:41:22 http +entry mod3.ts:42:25 http +entry mod3.ts:43:24 http +entry mod3.ts:44:22 http +entry mod3.ts:45:23 http +entry mod3.ts:46:22 http +entry mod3.ts:47:25 http +entry mod3.ts:48:24 http +entry mod3.ts:49:22 http +entry mod3.ts:4:21 http +entry mod3.ts:50:23 http +entry mod3.ts:51:22 http +entry mod3.ts:52:25 http +entry mod3.ts:53:24 http +entry mod3.ts:54:22 http +entry mod3.ts:55:23 http +entry mod3.ts:56:22 http +entry mod3.ts:57:25 http +entry mod3.ts:58:24 http +entry mod3.ts:59:22 http +entry mod3.ts:5:22 http +entry mod3.ts:60:23 http +entry mod3.ts:61:22 http +entry mod3.ts:62:25 http +entry mod3.ts:63:24 http +entry mod3.ts:64:22 http +entry mod3.ts:65:23 http +entry mod3.ts:66:22 http +entry mod3.ts:67:25 http +entry mod3.ts:68:24 http +entry mod3.ts:69:22 http +entry mod3.ts:6:21 http +entry mod3.ts:70:23 http +entry mod3.ts:71:22 http +entry mod3.ts:72:25 http +entry mod3.ts:73:24 http +entry mod3.ts:74:22 http +entry mod3.ts:75:23 http +entry mod3.ts:76:22 http +entry mod3.ts:77:25 http +entry mod3.ts:78:24 http +entry mod3.ts:79:22 http +entry mod3.ts:7:24 http +entry mod3.ts:80:23 http +entry mod3.ts:81:22 http +entry mod3.ts:82:25 http +entry mod3.ts:83:24 http +entry mod3.ts:84:22 http +entry mod3.ts:85:23 http +entry mod3.ts:86:22 http +entry mod3.ts:87:25 http +entry mod3.ts:88:24 http +entry mod3.ts:89:22 http +entry mod3.ts:8:23 http +entry mod3.ts:90:23 http +entry mod3.ts:91:22 http +entry mod3.ts:92:25 http +entry mod3.ts:93:24 http +entry mod3.ts:94:22 http +entry mod3.ts:95:23 http +entry mod3.ts:96:22 http +entry mod3.ts:97:25 http +entry mod3.ts:98:24 http +entry mod3.ts:99:22 http +entry mod3.ts:9:21 http +entry mod4.ts:100:23 http +entry mod4.ts:101:22 http +entry mod4.ts:102:25 http +entry mod4.ts:103:24 http +entry mod4.ts:10:22 http +entry mod4.ts:11:21 http +entry mod4.ts:12:24 http +entry mod4.ts:13:23 http +entry mod4.ts:14:22 http +entry mod4.ts:15:23 http +entry mod4.ts:16:22 http +entry mod4.ts:17:25 http +entry mod4.ts:18:24 http +entry mod4.ts:19:22 http +entry mod4.ts:20:23 http +entry mod4.ts:21:22 http +entry mod4.ts:22:25 http +entry mod4.ts:23:24 http +entry mod4.ts:24:22 http +entry mod4.ts:25:23 http +entry mod4.ts:26:22 http +entry mod4.ts:27:25 http +entry mod4.ts:28:24 http +entry mod4.ts:29:22 http +entry mod4.ts:30:23 http +entry mod4.ts:31:22 http +entry mod4.ts:32:25 http +entry mod4.ts:33:24 http +entry mod4.ts:34:22 http +entry mod4.ts:35:23 http +entry mod4.ts:36:22 http +entry mod4.ts:37:25 http +entry mod4.ts:38:24 http +entry mod4.ts:39:22 http +entry mod4.ts:40:23 http +entry mod4.ts:41:22 http +entry mod4.ts:42:25 http +entry mod4.ts:43:24 http +entry mod4.ts:44:22 http +entry mod4.ts:45:23 http +entry mod4.ts:46:22 http +entry mod4.ts:47:25 http +entry mod4.ts:48:24 http +entry mod4.ts:49:22 http +entry mod4.ts:4:21 http +entry mod4.ts:50:23 http +entry mod4.ts:51:22 http +entry mod4.ts:52:25 http +entry mod4.ts:53:24 http +entry mod4.ts:54:22 http +entry mod4.ts:55:23 http +entry mod4.ts:56:22 http +entry mod4.ts:57:25 http +entry mod4.ts:58:24 http +entry mod4.ts:59:22 http +entry mod4.ts:5:22 http +entry mod4.ts:60:23 http +entry mod4.ts:61:22 http +entry mod4.ts:62:25 http +entry mod4.ts:63:24 http +entry mod4.ts:64:22 http +entry mod4.ts:65:23 http +entry mod4.ts:66:22 http +entry mod4.ts:67:25 http +entry mod4.ts:68:24 http +entry mod4.ts:69:22 http +entry mod4.ts:6:21 http +entry mod4.ts:70:23 http +entry mod4.ts:71:22 http +entry mod4.ts:72:25 http +entry mod4.ts:73:24 http +entry mod4.ts:74:22 http +entry mod4.ts:75:23 http +entry mod4.ts:76:22 http +entry mod4.ts:77:25 http +entry mod4.ts:78:24 http +entry mod4.ts:79:22 http +entry mod4.ts:7:24 http +entry mod4.ts:80:23 http +entry mod4.ts:81:22 http +entry mod4.ts:82:25 http +entry mod4.ts:83:24 http +entry mod4.ts:84:22 http +entry mod4.ts:85:23 http +entry mod4.ts:86:22 http +entry mod4.ts:87:25 http +entry mod4.ts:88:24 http +entry mod4.ts:89:22 http +entry mod4.ts:8:23 http +entry mod4.ts:90:23 http +entry mod4.ts:91:22 http +entry mod4.ts:92:25 http +entry mod4.ts:93:24 http +entry mod4.ts:94:22 http +entry mod4.ts:95:23 http +entry mod4.ts:96:22 http +entry mod4.ts:97:25 http +entry mod4.ts:98:24 http +entry mod4.ts:99:22 http +entry mod4.ts:9:21 http +entry mod5.ts:100:23 http +entry mod5.ts:101:22 http +entry mod5.ts:102:25 http +entry mod5.ts:103:24 http +entry mod5.ts:10:22 http +entry mod5.ts:11:21 http +entry mod5.ts:12:24 http +entry mod5.ts:13:23 http +entry mod5.ts:14:22 http +entry mod5.ts:15:23 http +entry mod5.ts:16:22 http +entry mod5.ts:17:25 http +entry mod5.ts:18:24 http +entry mod5.ts:19:22 http +entry mod5.ts:20:23 http +entry mod5.ts:21:22 http +entry mod5.ts:22:25 http +entry mod5.ts:23:24 http +entry mod5.ts:24:22 http +entry mod5.ts:25:23 http +entry mod5.ts:26:22 http +entry mod5.ts:27:25 http +entry mod5.ts:28:24 http +entry mod5.ts:29:22 http +entry mod5.ts:30:23 http +entry mod5.ts:31:22 http +entry mod5.ts:32:25 http +entry mod5.ts:33:24 http +entry mod5.ts:34:22 http +entry mod5.ts:35:23 http +entry mod5.ts:36:22 http +entry mod5.ts:37:25 http +entry mod5.ts:38:24 http +entry mod5.ts:39:22 http +entry mod5.ts:40:23 http +entry mod5.ts:41:22 http +entry mod5.ts:42:25 http +entry mod5.ts:43:24 http +entry mod5.ts:44:22 http +entry mod5.ts:45:23 http +entry mod5.ts:46:22 http +entry mod5.ts:47:25 http +entry mod5.ts:48:24 http +entry mod5.ts:49:22 http +entry mod5.ts:4:21 http +entry mod5.ts:50:23 http +entry mod5.ts:51:22 http +entry mod5.ts:52:25 http +entry mod5.ts:53:24 http +entry mod5.ts:54:22 http +entry mod5.ts:55:23 http +entry mod5.ts:56:22 http +entry mod5.ts:57:25 http +entry mod5.ts:58:24 http +entry mod5.ts:59:22 http +entry mod5.ts:5:22 http +entry mod5.ts:60:23 http +entry mod5.ts:61:22 http +entry mod5.ts:62:25 http +entry mod5.ts:63:24 http +entry mod5.ts:64:22 http +entry mod5.ts:65:23 http +entry mod5.ts:66:22 http +entry mod5.ts:67:25 http +entry mod5.ts:68:24 http +entry mod5.ts:69:22 http +entry mod5.ts:6:21 http +entry mod5.ts:70:23 http +entry mod5.ts:71:22 http +entry mod5.ts:72:25 http +entry mod5.ts:73:24 http +entry mod5.ts:74:22 http +entry mod5.ts:75:23 http +entry mod5.ts:76:22 http +entry mod5.ts:77:25 http +entry mod5.ts:78:24 http +entry mod5.ts:79:22 http +entry mod5.ts:7:24 http +entry mod5.ts:80:23 http +entry mod5.ts:81:22 http +entry mod5.ts:82:25 http +entry mod5.ts:83:24 http +entry mod5.ts:84:22 http +entry mod5.ts:85:23 http +entry mod5.ts:86:22 http +entry mod5.ts:87:25 http +entry mod5.ts:88:24 http +entry mod5.ts:89:22 http +entry mod5.ts:8:23 http +entry mod5.ts:90:23 http +entry mod5.ts:91:22 http +entry mod5.ts:92:25 http +entry mod5.ts:93:24 http +entry mod5.ts:94:22 http +entry mod5.ts:95:23 http +entry mod5.ts:96:22 http +entry mod5.ts:97:25 http +entry mod5.ts:98:24 http +entry mod5.ts:99:22 http +entry mod5.ts:9:21 http +entry mod6.ts:100:23 http +entry mod6.ts:101:22 http +entry mod6.ts:102:25 http +entry mod6.ts:103:24 http +entry mod6.ts:10:22 http +entry mod6.ts:11:21 http +entry mod6.ts:12:24 http +entry mod6.ts:13:23 http +entry mod6.ts:14:22 http +entry mod6.ts:15:23 http +entry mod6.ts:16:22 http +entry mod6.ts:17:25 http +entry mod6.ts:18:24 http +entry mod6.ts:19:22 http +entry mod6.ts:20:23 http +entry mod6.ts:21:22 http +entry mod6.ts:22:25 http +entry mod6.ts:23:24 http +entry mod6.ts:24:22 http +entry mod6.ts:25:23 http +entry mod6.ts:26:22 http +entry mod6.ts:27:25 http +entry mod6.ts:28:24 http +entry mod6.ts:29:22 http +entry mod6.ts:30:23 http +entry mod6.ts:31:22 http +entry mod6.ts:32:25 http +entry mod6.ts:33:24 http +entry mod6.ts:34:22 http +entry mod6.ts:35:23 http +entry mod6.ts:36:22 http +entry mod6.ts:37:25 http +entry mod6.ts:38:24 http +entry mod6.ts:39:22 http +entry mod6.ts:40:23 http +entry mod6.ts:41:22 http +entry mod6.ts:42:25 http +entry mod6.ts:43:24 http +entry mod6.ts:44:22 http +entry mod6.ts:45:23 http +entry mod6.ts:46:22 http +entry mod6.ts:47:25 http +entry mod6.ts:48:24 http +entry mod6.ts:49:22 http +entry mod6.ts:4:21 http +entry mod6.ts:50:23 http +entry mod6.ts:51:22 http +entry mod6.ts:52:25 http +entry mod6.ts:53:24 http +entry mod6.ts:54:22 http +entry mod6.ts:55:23 http +entry mod6.ts:56:22 http +entry mod6.ts:57:25 http +entry mod6.ts:58:24 http +entry mod6.ts:59:22 http +entry mod6.ts:5:22 http +entry mod6.ts:60:23 http +entry mod6.ts:61:22 http +entry mod6.ts:62:25 http +entry mod6.ts:63:24 http +entry mod6.ts:64:22 http +entry mod6.ts:65:23 http +entry mod6.ts:66:22 http +entry mod6.ts:67:25 http +entry mod6.ts:68:24 http +entry mod6.ts:69:22 http +entry mod6.ts:6:21 http +entry mod6.ts:70:23 http +entry mod6.ts:71:22 http +entry mod6.ts:72:25 http +entry mod6.ts:73:24 http +entry mod6.ts:74:22 http +entry mod6.ts:75:23 http +entry mod6.ts:76:22 http +entry mod6.ts:77:25 http +entry mod6.ts:78:24 http +entry mod6.ts:79:22 http +entry mod6.ts:7:24 http +entry mod6.ts:80:23 http +entry mod6.ts:81:22 http +entry mod6.ts:82:25 http +entry mod6.ts:83:24 http +entry mod6.ts:84:22 http +entry mod6.ts:85:23 http +entry mod6.ts:86:22 http +entry mod6.ts:87:25 http +entry mod6.ts:88:24 http +entry mod6.ts:89:22 http +entry mod6.ts:8:23 http +entry mod6.ts:90:23 http +entry mod6.ts:91:22 http +entry mod6.ts:92:25 http +entry mod6.ts:93:24 http +entry mod6.ts:94:22 http +entry mod6.ts:95:23 http +entry mod6.ts:96:22 http +entry mod6.ts:97:25 http +entry mod6.ts:98:24 http +entry mod6.ts:99:22 http +entry mod6.ts:9:21 http +entry mod7.ts:100:23 http +entry mod7.ts:101:22 http +entry mod7.ts:102:25 http +entry mod7.ts:103:24 http +entry mod7.ts:10:22 http +entry mod7.ts:11:21 http +entry mod7.ts:12:24 http +entry mod7.ts:13:23 http +entry mod7.ts:14:22 http +entry mod7.ts:15:23 http +entry mod7.ts:16:22 http +entry mod7.ts:17:25 http +entry mod7.ts:18:24 http +entry mod7.ts:19:22 http +entry mod7.ts:20:23 http +entry mod7.ts:21:22 http +entry mod7.ts:22:25 http +entry mod7.ts:23:24 http +entry mod7.ts:24:22 http +entry mod7.ts:25:23 http +entry mod7.ts:26:22 http +entry mod7.ts:27:25 http +entry mod7.ts:28:24 http +entry mod7.ts:29:22 http +entry mod7.ts:30:23 http +entry mod7.ts:31:22 http +entry mod7.ts:32:25 http +entry mod7.ts:33:24 http +entry mod7.ts:34:22 http +entry mod7.ts:35:23 http +entry mod7.ts:36:22 http +entry mod7.ts:37:25 http +entry mod7.ts:38:24 http +entry mod7.ts:39:22 http +entry mod7.ts:40:23 http +entry mod7.ts:41:22 http +entry mod7.ts:42:25 http +entry mod7.ts:43:24 http +entry mod7.ts:44:22 http +entry mod7.ts:45:23 http +entry mod7.ts:46:22 http +entry mod7.ts:47:25 http +entry mod7.ts:48:24 http +entry mod7.ts:49:22 http +entry mod7.ts:4:21 http +entry mod7.ts:50:23 http +entry mod7.ts:51:22 http +entry mod7.ts:52:25 http +entry mod7.ts:53:24 http +entry mod7.ts:54:22 http +entry mod7.ts:55:23 http +entry mod7.ts:56:22 http +entry mod7.ts:57:25 http +entry mod7.ts:58:24 http +entry mod7.ts:59:22 http +entry mod7.ts:5:22 http +entry mod7.ts:60:23 http +entry mod7.ts:61:22 http +entry mod7.ts:62:25 http +entry mod7.ts:63:24 http +entry mod7.ts:64:22 http +entry mod7.ts:65:23 http +entry mod7.ts:66:22 http +entry mod7.ts:67:25 http +entry mod7.ts:68:24 http +entry mod7.ts:69:22 http +entry mod7.ts:6:21 http +entry mod7.ts:70:23 http +entry mod7.ts:71:22 http +entry mod7.ts:72:25 http +entry mod7.ts:73:24 http +entry mod7.ts:74:22 http +entry mod7.ts:75:23 http +entry mod7.ts:76:22 http +entry mod7.ts:77:25 http +entry mod7.ts:78:24 http +entry mod7.ts:79:22 http +entry mod7.ts:7:24 http +entry mod7.ts:80:23 http +entry mod7.ts:81:22 http +entry mod7.ts:82:25 http +entry mod7.ts:83:24 http +entry mod7.ts:84:22 http +entry mod7.ts:85:23 http +entry mod7.ts:86:22 http +entry mod7.ts:87:25 http +entry mod7.ts:88:24 http +entry mod7.ts:89:22 http +entry mod7.ts:8:23 http +entry mod7.ts:90:23 http +entry mod7.ts:91:22 http +entry mod7.ts:92:25 http +entry mod7.ts:93:24 http +entry mod7.ts:94:22 http +entry mod7.ts:95:23 http +entry mod7.ts:96:22 http +entry mod7.ts:97:25 http +entry mod7.ts:98:24 http +entry mod7.ts:99:22 http +entry mod7.ts:9:21 http +entry mod8.ts:100:23 http +entry mod8.ts:101:22 http +entry mod8.ts:102:25 http +entry mod8.ts:103:24 http +entry mod8.ts:10:22 http +entry mod8.ts:11:21 http +entry mod8.ts:12:24 http +entry mod8.ts:13:23 http +entry mod8.ts:14:22 http +entry mod8.ts:15:23 http +entry mod8.ts:16:22 http +entry mod8.ts:17:25 http +entry mod8.ts:18:24 http +entry mod8.ts:19:22 http +entry mod8.ts:20:23 http +entry mod8.ts:21:22 http +entry mod8.ts:22:25 http +entry mod8.ts:23:24 http +entry mod8.ts:24:22 http +entry mod8.ts:25:23 http +entry mod8.ts:26:22 http +entry mod8.ts:27:25 http +entry mod8.ts:28:24 http +entry mod8.ts:29:22 http +entry mod8.ts:30:23 http +entry mod8.ts:31:22 http +entry mod8.ts:32:25 http +entry mod8.ts:33:24 http +entry mod8.ts:34:22 http +entry mod8.ts:35:23 http +entry mod8.ts:36:22 http +entry mod8.ts:37:25 http +entry mod8.ts:38:24 http +entry mod8.ts:39:22 http +entry mod8.ts:40:23 http +entry mod8.ts:41:22 http +entry mod8.ts:42:25 http +entry mod8.ts:43:24 http +entry mod8.ts:44:22 http +entry mod8.ts:45:23 http +entry mod8.ts:46:22 http +entry mod8.ts:47:25 http +entry mod8.ts:48:24 http +entry mod8.ts:49:22 http +entry mod8.ts:4:21 http +entry mod8.ts:50:23 http +entry mod8.ts:51:22 http +entry mod8.ts:52:25 http +entry mod8.ts:53:24 http +entry mod8.ts:54:22 http +entry mod8.ts:55:23 http +entry mod8.ts:56:22 http +entry mod8.ts:57:25 http +entry mod8.ts:58:24 http +entry mod8.ts:59:22 http +entry mod8.ts:5:22 http +entry mod8.ts:60:23 http +entry mod8.ts:61:22 http +entry mod8.ts:62:25 http +entry mod8.ts:63:24 http +entry mod8.ts:64:22 http +entry mod8.ts:65:23 http +entry mod8.ts:66:22 http +entry mod8.ts:67:25 http +entry mod8.ts:68:24 http +entry mod8.ts:69:22 http +entry mod8.ts:6:21 http +entry mod8.ts:70:23 http +entry mod8.ts:71:22 http +entry mod8.ts:72:25 http +entry mod8.ts:73:24 http +entry mod8.ts:74:22 http +entry mod8.ts:75:23 http +entry mod8.ts:76:22 http +entry mod8.ts:77:25 http +entry mod8.ts:78:24 http +entry mod8.ts:79:22 http +entry mod8.ts:7:24 http +entry mod8.ts:80:23 http +entry mod8.ts:81:22 http +entry mod8.ts:82:25 http +entry mod8.ts:83:24 http +entry mod8.ts:84:22 http +entry mod8.ts:85:23 http +entry mod8.ts:86:22 http +entry mod8.ts:87:25 http +entry mod8.ts:88:24 http +entry mod8.ts:89:22 http +entry mod8.ts:8:23 http +entry mod8.ts:90:23 http +entry mod8.ts:91:22 http +entry mod8.ts:92:25 http +entry mod8.ts:93:24 http +entry mod8.ts:94:22 http +entry mod8.ts:95:23 http +entry mod8.ts:96:22 http +entry mod8.ts:97:25 http +entry mod8.ts:98:24 http +entry mod8.ts:99:22 http +entry mod8.ts:9:21 http +entry mod9.ts:100:23 http +entry mod9.ts:101:22 http +entry mod9.ts:102:25 http +entry mod9.ts:103:24 http +entry mod9.ts:10:22 http +entry mod9.ts:11:21 http +entry mod9.ts:12:24 http +entry mod9.ts:13:23 http +entry mod9.ts:14:22 http +entry mod9.ts:15:23 http +entry mod9.ts:16:22 http +entry mod9.ts:17:25 http +entry mod9.ts:18:24 http +entry mod9.ts:19:22 http +entry mod9.ts:20:23 http +entry mod9.ts:21:22 http +entry mod9.ts:22:25 http +entry mod9.ts:23:24 http +entry mod9.ts:24:22 http +entry mod9.ts:25:23 http +entry mod9.ts:26:22 http +entry mod9.ts:27:25 http +entry mod9.ts:28:24 http +entry mod9.ts:29:22 http +entry mod9.ts:30:23 http +entry mod9.ts:31:22 http +entry mod9.ts:32:25 http +entry mod9.ts:33:24 http +entry mod9.ts:34:22 http +entry mod9.ts:35:23 http +entry mod9.ts:36:22 http +entry mod9.ts:37:25 http +entry mod9.ts:38:24 http +entry mod9.ts:39:22 http +entry mod9.ts:40:23 http +entry mod9.ts:41:22 http +entry mod9.ts:42:25 http +entry mod9.ts:43:24 http +entry mod9.ts:44:22 http +entry mod9.ts:45:23 http +entry mod9.ts:46:22 http +entry mod9.ts:47:25 http +entry mod9.ts:48:24 http +entry mod9.ts:49:22 http +entry mod9.ts:4:21 http +entry mod9.ts:50:23 http +entry mod9.ts:51:22 http +entry mod9.ts:52:25 http +entry mod9.ts:53:24 http +entry mod9.ts:54:22 http +entry mod9.ts:55:23 http +entry mod9.ts:56:22 http +entry mod9.ts:57:25 http +entry mod9.ts:58:24 http +entry mod9.ts:59:22 http +entry mod9.ts:5:22 http +entry mod9.ts:60:23 http +entry mod9.ts:61:22 http +entry mod9.ts:62:25 http +entry mod9.ts:63:24 http +entry mod9.ts:64:22 http +entry mod9.ts:65:23 http +entry mod9.ts:66:22 http +entry mod9.ts:67:25 http +entry mod9.ts:68:24 http +entry mod9.ts:69:22 http +entry mod9.ts:6:21 http +entry mod9.ts:70:23 http +entry mod9.ts:71:22 http +entry mod9.ts:72:25 http +entry mod9.ts:73:24 http +entry mod9.ts:74:22 http +entry mod9.ts:75:23 http +entry mod9.ts:76:22 http +entry mod9.ts:77:25 http +entry mod9.ts:78:24 http +entry mod9.ts:79:22 http +entry mod9.ts:7:24 http +entry mod9.ts:80:23 http +entry mod9.ts:81:22 http +entry mod9.ts:82:25 http +entry mod9.ts:83:24 http +entry mod9.ts:84:22 http +entry mod9.ts:85:23 http +entry mod9.ts:86:22 http +entry mod9.ts:87:25 http +entry mod9.ts:88:24 http +entry mod9.ts:89:22 http +entry mod9.ts:8:23 http +entry mod9.ts:90:23 http +entry mod9.ts:91:22 http +entry mod9.ts:92:25 http +entry mod9.ts:93:24 http +entry mod9.ts:94:22 http +entry mod9.ts:95:23 http +entry mod9.ts:96:22 http +entry mod9.ts:97:25 http +entry mod9.ts:98:24 http +entry mod9.ts:99:22 http +entry mod9.ts:9:21 http diff --git a/graph/test/typescript/hono/expected/scale/routes-2k.tsv b/graph/test/typescript/hono/expected/scale/routes-2k.tsv new file mode 100644 index 000000000..f2909148f --- /dev/null +++ b/graph/test/typescript/hono/expected/scale/routes-2k.tsv @@ -0,0 +1,4023 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: reviewed by count and endpoints: 2,000 routes over 20 mounted modules, first /m0/res0/:id, last PATCH /m19/res99/:id; runtime agrees on all +# census: route_mount_literal, verb_literal_path (scale: 2,000 routes; generated by tools/gen-scale.mjs) +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route GET /m0/res0/:id mod0.ts:4:21 index.ts:24:13 0 +route POST /m0/res1/:id mod0.ts:5:22 index.ts:24:13 1 +route PUT /m0/res2/:id mod0.ts:6:21 index.ts:24:13 2 +route DELETE /m0/res3/:id mod0.ts:7:24 index.ts:24:13 3 +route PATCH /m0/res4/:id mod0.ts:8:23 index.ts:24:13 4 +route GET /m0/res5/:id mod0.ts:9:21 index.ts:24:13 5 +route POST /m0/res6/:id mod0.ts:10:22 index.ts:24:13 6 +route PUT /m0/res7/:id mod0.ts:11:21 index.ts:24:13 7 +route DELETE /m0/res8/:id mod0.ts:12:24 index.ts:24:13 8 +route PATCH /m0/res9/:id mod0.ts:13:23 index.ts:24:13 9 +route GET /m0/res10/:id mod0.ts:14:22 index.ts:24:13 10 +route POST /m0/res11/:id mod0.ts:15:23 index.ts:24:13 11 +route PUT /m0/res12/:id mod0.ts:16:22 index.ts:24:13 12 +route DELETE /m0/res13/:id mod0.ts:17:25 index.ts:24:13 13 +route PATCH /m0/res14/:id mod0.ts:18:24 index.ts:24:13 14 +route GET /m0/res15/:id mod0.ts:19:22 index.ts:24:13 15 +route POST /m0/res16/:id mod0.ts:20:23 index.ts:24:13 16 +route PUT /m0/res17/:id mod0.ts:21:22 index.ts:24:13 17 +route DELETE /m0/res18/:id mod0.ts:22:25 index.ts:24:13 18 +route PATCH /m0/res19/:id mod0.ts:23:24 index.ts:24:13 19 +route GET /m0/res20/:id mod0.ts:24:22 index.ts:24:13 20 +route POST /m0/res21/:id mod0.ts:25:23 index.ts:24:13 21 +route PUT /m0/res22/:id mod0.ts:26:22 index.ts:24:13 22 +route DELETE /m0/res23/:id mod0.ts:27:25 index.ts:24:13 23 +route PATCH /m0/res24/:id mod0.ts:28:24 index.ts:24:13 24 +route GET /m0/res25/:id mod0.ts:29:22 index.ts:24:13 25 +route POST /m0/res26/:id mod0.ts:30:23 index.ts:24:13 26 +route PUT /m0/res27/:id mod0.ts:31:22 index.ts:24:13 27 +route DELETE /m0/res28/:id mod0.ts:32:25 index.ts:24:13 28 +route PATCH /m0/res29/:id mod0.ts:33:24 index.ts:24:13 29 +route GET /m0/res30/:id mod0.ts:34:22 index.ts:24:13 30 +route POST /m0/res31/:id mod0.ts:35:23 index.ts:24:13 31 +route PUT /m0/res32/:id mod0.ts:36:22 index.ts:24:13 32 +route DELETE /m0/res33/:id mod0.ts:37:25 index.ts:24:13 33 +route PATCH /m0/res34/:id mod0.ts:38:24 index.ts:24:13 34 +route GET /m0/res35/:id mod0.ts:39:22 index.ts:24:13 35 +route POST /m0/res36/:id mod0.ts:40:23 index.ts:24:13 36 +route PUT /m0/res37/:id mod0.ts:41:22 index.ts:24:13 37 +route DELETE /m0/res38/:id mod0.ts:42:25 index.ts:24:13 38 +route PATCH /m0/res39/:id mod0.ts:43:24 index.ts:24:13 39 +route GET /m0/res40/:id mod0.ts:44:22 index.ts:24:13 40 +route POST /m0/res41/:id mod0.ts:45:23 index.ts:24:13 41 +route PUT /m0/res42/:id mod0.ts:46:22 index.ts:24:13 42 +route DELETE /m0/res43/:id mod0.ts:47:25 index.ts:24:13 43 +route PATCH /m0/res44/:id mod0.ts:48:24 index.ts:24:13 44 +route GET /m0/res45/:id mod0.ts:49:22 index.ts:24:13 45 +route POST /m0/res46/:id mod0.ts:50:23 index.ts:24:13 46 +route PUT /m0/res47/:id mod0.ts:51:22 index.ts:24:13 47 +route DELETE /m0/res48/:id mod0.ts:52:25 index.ts:24:13 48 +route PATCH /m0/res49/:id mod0.ts:53:24 index.ts:24:13 49 +route GET /m0/res50/:id mod0.ts:54:22 index.ts:24:13 50 +route POST /m0/res51/:id mod0.ts:55:23 index.ts:24:13 51 +route PUT /m0/res52/:id mod0.ts:56:22 index.ts:24:13 52 +route DELETE /m0/res53/:id mod0.ts:57:25 index.ts:24:13 53 +route PATCH /m0/res54/:id mod0.ts:58:24 index.ts:24:13 54 +route GET /m0/res55/:id mod0.ts:59:22 index.ts:24:13 55 +route POST /m0/res56/:id mod0.ts:60:23 index.ts:24:13 56 +route PUT /m0/res57/:id mod0.ts:61:22 index.ts:24:13 57 +route DELETE /m0/res58/:id mod0.ts:62:25 index.ts:24:13 58 +route PATCH /m0/res59/:id mod0.ts:63:24 index.ts:24:13 59 +route GET /m0/res60/:id mod0.ts:64:22 index.ts:24:13 60 +route POST /m0/res61/:id mod0.ts:65:23 index.ts:24:13 61 +route PUT /m0/res62/:id mod0.ts:66:22 index.ts:24:13 62 +route DELETE /m0/res63/:id mod0.ts:67:25 index.ts:24:13 63 +route PATCH /m0/res64/:id mod0.ts:68:24 index.ts:24:13 64 +route GET /m0/res65/:id mod0.ts:69:22 index.ts:24:13 65 +route POST /m0/res66/:id mod0.ts:70:23 index.ts:24:13 66 +route PUT /m0/res67/:id mod0.ts:71:22 index.ts:24:13 67 +route DELETE /m0/res68/:id mod0.ts:72:25 index.ts:24:13 68 +route PATCH /m0/res69/:id mod0.ts:73:24 index.ts:24:13 69 +route GET /m0/res70/:id mod0.ts:74:22 index.ts:24:13 70 +route POST /m0/res71/:id mod0.ts:75:23 index.ts:24:13 71 +route PUT /m0/res72/:id mod0.ts:76:22 index.ts:24:13 72 +route DELETE /m0/res73/:id mod0.ts:77:25 index.ts:24:13 73 +route PATCH /m0/res74/:id mod0.ts:78:24 index.ts:24:13 74 +route GET /m0/res75/:id mod0.ts:79:22 index.ts:24:13 75 +route POST /m0/res76/:id mod0.ts:80:23 index.ts:24:13 76 +route PUT /m0/res77/:id mod0.ts:81:22 index.ts:24:13 77 +route DELETE /m0/res78/:id mod0.ts:82:25 index.ts:24:13 78 +route PATCH /m0/res79/:id mod0.ts:83:24 index.ts:24:13 79 +route GET /m0/res80/:id mod0.ts:84:22 index.ts:24:13 80 +route POST /m0/res81/:id mod0.ts:85:23 index.ts:24:13 81 +route PUT /m0/res82/:id mod0.ts:86:22 index.ts:24:13 82 +route DELETE /m0/res83/:id mod0.ts:87:25 index.ts:24:13 83 +route PATCH /m0/res84/:id mod0.ts:88:24 index.ts:24:13 84 +route GET /m0/res85/:id mod0.ts:89:22 index.ts:24:13 85 +route POST /m0/res86/:id mod0.ts:90:23 index.ts:24:13 86 +route PUT /m0/res87/:id mod0.ts:91:22 index.ts:24:13 87 +route DELETE /m0/res88/:id mod0.ts:92:25 index.ts:24:13 88 +route PATCH /m0/res89/:id mod0.ts:93:24 index.ts:24:13 89 +route GET /m0/res90/:id mod0.ts:94:22 index.ts:24:13 90 +route POST /m0/res91/:id mod0.ts:95:23 index.ts:24:13 91 +route PUT /m0/res92/:id mod0.ts:96:22 index.ts:24:13 92 +route DELETE /m0/res93/:id mod0.ts:97:25 index.ts:24:13 93 +route PATCH /m0/res94/:id mod0.ts:98:24 index.ts:24:13 94 +route GET /m0/res95/:id mod0.ts:99:22 index.ts:24:13 95 +route POST /m0/res96/:id mod0.ts:100:23 index.ts:24:13 96 +route PUT /m0/res97/:id mod0.ts:101:22 index.ts:24:13 97 +route DELETE /m0/res98/:id mod0.ts:102:25 index.ts:24:13 98 +route PATCH /m0/res99/:id mod0.ts:103:24 index.ts:24:13 99 +route GET /m1/res0/:id mod1.ts:4:21 index.ts:24:13 100 +route POST /m1/res1/:id mod1.ts:5:22 index.ts:24:13 101 +route PUT /m1/res2/:id mod1.ts:6:21 index.ts:24:13 102 +route DELETE /m1/res3/:id mod1.ts:7:24 index.ts:24:13 103 +route PATCH /m1/res4/:id mod1.ts:8:23 index.ts:24:13 104 +route GET /m1/res5/:id mod1.ts:9:21 index.ts:24:13 105 +route POST /m1/res6/:id mod1.ts:10:22 index.ts:24:13 106 +route PUT /m1/res7/:id mod1.ts:11:21 index.ts:24:13 107 +route DELETE /m1/res8/:id mod1.ts:12:24 index.ts:24:13 108 +route PATCH /m1/res9/:id mod1.ts:13:23 index.ts:24:13 109 +route GET /m1/res10/:id mod1.ts:14:22 index.ts:24:13 110 +route POST /m1/res11/:id mod1.ts:15:23 index.ts:24:13 111 +route PUT /m1/res12/:id mod1.ts:16:22 index.ts:24:13 112 +route DELETE /m1/res13/:id mod1.ts:17:25 index.ts:24:13 113 +route PATCH /m1/res14/:id mod1.ts:18:24 index.ts:24:13 114 +route GET /m1/res15/:id mod1.ts:19:22 index.ts:24:13 115 +route POST /m1/res16/:id mod1.ts:20:23 index.ts:24:13 116 +route PUT /m1/res17/:id mod1.ts:21:22 index.ts:24:13 117 +route DELETE /m1/res18/:id mod1.ts:22:25 index.ts:24:13 118 +route PATCH /m1/res19/:id mod1.ts:23:24 index.ts:24:13 119 +route GET /m1/res20/:id mod1.ts:24:22 index.ts:24:13 120 +route POST /m1/res21/:id mod1.ts:25:23 index.ts:24:13 121 +route PUT /m1/res22/:id mod1.ts:26:22 index.ts:24:13 122 +route DELETE /m1/res23/:id mod1.ts:27:25 index.ts:24:13 123 +route PATCH /m1/res24/:id mod1.ts:28:24 index.ts:24:13 124 +route GET /m1/res25/:id mod1.ts:29:22 index.ts:24:13 125 +route POST /m1/res26/:id mod1.ts:30:23 index.ts:24:13 126 +route PUT /m1/res27/:id mod1.ts:31:22 index.ts:24:13 127 +route DELETE /m1/res28/:id mod1.ts:32:25 index.ts:24:13 128 +route PATCH /m1/res29/:id mod1.ts:33:24 index.ts:24:13 129 +route GET /m1/res30/:id mod1.ts:34:22 index.ts:24:13 130 +route POST /m1/res31/:id mod1.ts:35:23 index.ts:24:13 131 +route PUT /m1/res32/:id mod1.ts:36:22 index.ts:24:13 132 +route DELETE /m1/res33/:id mod1.ts:37:25 index.ts:24:13 133 +route PATCH /m1/res34/:id mod1.ts:38:24 index.ts:24:13 134 +route GET /m1/res35/:id mod1.ts:39:22 index.ts:24:13 135 +route POST /m1/res36/:id mod1.ts:40:23 index.ts:24:13 136 +route PUT /m1/res37/:id mod1.ts:41:22 index.ts:24:13 137 +route DELETE /m1/res38/:id mod1.ts:42:25 index.ts:24:13 138 +route PATCH /m1/res39/:id mod1.ts:43:24 index.ts:24:13 139 +route GET /m1/res40/:id mod1.ts:44:22 index.ts:24:13 140 +route POST /m1/res41/:id mod1.ts:45:23 index.ts:24:13 141 +route PUT /m1/res42/:id mod1.ts:46:22 index.ts:24:13 142 +route DELETE /m1/res43/:id mod1.ts:47:25 index.ts:24:13 143 +route PATCH /m1/res44/:id mod1.ts:48:24 index.ts:24:13 144 +route GET /m1/res45/:id mod1.ts:49:22 index.ts:24:13 145 +route POST /m1/res46/:id mod1.ts:50:23 index.ts:24:13 146 +route PUT /m1/res47/:id mod1.ts:51:22 index.ts:24:13 147 +route DELETE /m1/res48/:id mod1.ts:52:25 index.ts:24:13 148 +route PATCH /m1/res49/:id mod1.ts:53:24 index.ts:24:13 149 +route GET /m1/res50/:id mod1.ts:54:22 index.ts:24:13 150 +route POST /m1/res51/:id mod1.ts:55:23 index.ts:24:13 151 +route PUT /m1/res52/:id mod1.ts:56:22 index.ts:24:13 152 +route DELETE /m1/res53/:id mod1.ts:57:25 index.ts:24:13 153 +route PATCH /m1/res54/:id mod1.ts:58:24 index.ts:24:13 154 +route GET /m1/res55/:id mod1.ts:59:22 index.ts:24:13 155 +route POST /m1/res56/:id mod1.ts:60:23 index.ts:24:13 156 +route PUT /m1/res57/:id mod1.ts:61:22 index.ts:24:13 157 +route DELETE /m1/res58/:id mod1.ts:62:25 index.ts:24:13 158 +route PATCH /m1/res59/:id mod1.ts:63:24 index.ts:24:13 159 +route GET /m1/res60/:id mod1.ts:64:22 index.ts:24:13 160 +route POST /m1/res61/:id mod1.ts:65:23 index.ts:24:13 161 +route PUT /m1/res62/:id mod1.ts:66:22 index.ts:24:13 162 +route DELETE /m1/res63/:id mod1.ts:67:25 index.ts:24:13 163 +route PATCH /m1/res64/:id mod1.ts:68:24 index.ts:24:13 164 +route GET /m1/res65/:id mod1.ts:69:22 index.ts:24:13 165 +route POST /m1/res66/:id mod1.ts:70:23 index.ts:24:13 166 +route PUT /m1/res67/:id mod1.ts:71:22 index.ts:24:13 167 +route DELETE /m1/res68/:id mod1.ts:72:25 index.ts:24:13 168 +route PATCH /m1/res69/:id mod1.ts:73:24 index.ts:24:13 169 +route GET /m1/res70/:id mod1.ts:74:22 index.ts:24:13 170 +route POST /m1/res71/:id mod1.ts:75:23 index.ts:24:13 171 +route PUT /m1/res72/:id mod1.ts:76:22 index.ts:24:13 172 +route DELETE /m1/res73/:id mod1.ts:77:25 index.ts:24:13 173 +route PATCH /m1/res74/:id mod1.ts:78:24 index.ts:24:13 174 +route GET /m1/res75/:id mod1.ts:79:22 index.ts:24:13 175 +route POST /m1/res76/:id mod1.ts:80:23 index.ts:24:13 176 +route PUT /m1/res77/:id mod1.ts:81:22 index.ts:24:13 177 +route DELETE /m1/res78/:id mod1.ts:82:25 index.ts:24:13 178 +route PATCH /m1/res79/:id mod1.ts:83:24 index.ts:24:13 179 +route GET /m1/res80/:id mod1.ts:84:22 index.ts:24:13 180 +route POST /m1/res81/:id mod1.ts:85:23 index.ts:24:13 181 +route PUT /m1/res82/:id mod1.ts:86:22 index.ts:24:13 182 +route DELETE /m1/res83/:id mod1.ts:87:25 index.ts:24:13 183 +route PATCH /m1/res84/:id mod1.ts:88:24 index.ts:24:13 184 +route GET /m1/res85/:id mod1.ts:89:22 index.ts:24:13 185 +route POST /m1/res86/:id mod1.ts:90:23 index.ts:24:13 186 +route PUT /m1/res87/:id mod1.ts:91:22 index.ts:24:13 187 +route DELETE /m1/res88/:id mod1.ts:92:25 index.ts:24:13 188 +route PATCH /m1/res89/:id mod1.ts:93:24 index.ts:24:13 189 +route GET /m1/res90/:id mod1.ts:94:22 index.ts:24:13 190 +route POST /m1/res91/:id mod1.ts:95:23 index.ts:24:13 191 +route PUT /m1/res92/:id mod1.ts:96:22 index.ts:24:13 192 +route DELETE /m1/res93/:id mod1.ts:97:25 index.ts:24:13 193 +route PATCH /m1/res94/:id mod1.ts:98:24 index.ts:24:13 194 +route GET /m1/res95/:id mod1.ts:99:22 index.ts:24:13 195 +route POST /m1/res96/:id mod1.ts:100:23 index.ts:24:13 196 +route PUT /m1/res97/:id mod1.ts:101:22 index.ts:24:13 197 +route DELETE /m1/res98/:id mod1.ts:102:25 index.ts:24:13 198 +route PATCH /m1/res99/:id mod1.ts:103:24 index.ts:24:13 199 +route GET /m2/res0/:id mod2.ts:4:21 index.ts:24:13 200 +route POST /m2/res1/:id mod2.ts:5:22 index.ts:24:13 201 +route PUT /m2/res2/:id mod2.ts:6:21 index.ts:24:13 202 +route DELETE /m2/res3/:id mod2.ts:7:24 index.ts:24:13 203 +route PATCH /m2/res4/:id mod2.ts:8:23 index.ts:24:13 204 +route GET /m2/res5/:id mod2.ts:9:21 index.ts:24:13 205 +route POST /m2/res6/:id mod2.ts:10:22 index.ts:24:13 206 +route PUT /m2/res7/:id mod2.ts:11:21 index.ts:24:13 207 +route DELETE /m2/res8/:id mod2.ts:12:24 index.ts:24:13 208 +route PATCH /m2/res9/:id mod2.ts:13:23 index.ts:24:13 209 +route GET /m2/res10/:id mod2.ts:14:22 index.ts:24:13 210 +route POST /m2/res11/:id mod2.ts:15:23 index.ts:24:13 211 +route PUT /m2/res12/:id mod2.ts:16:22 index.ts:24:13 212 +route DELETE /m2/res13/:id mod2.ts:17:25 index.ts:24:13 213 +route PATCH /m2/res14/:id mod2.ts:18:24 index.ts:24:13 214 +route GET /m2/res15/:id mod2.ts:19:22 index.ts:24:13 215 +route POST /m2/res16/:id mod2.ts:20:23 index.ts:24:13 216 +route PUT /m2/res17/:id mod2.ts:21:22 index.ts:24:13 217 +route DELETE /m2/res18/:id mod2.ts:22:25 index.ts:24:13 218 +route PATCH /m2/res19/:id mod2.ts:23:24 index.ts:24:13 219 +route GET /m2/res20/:id mod2.ts:24:22 index.ts:24:13 220 +route POST /m2/res21/:id mod2.ts:25:23 index.ts:24:13 221 +route PUT /m2/res22/:id mod2.ts:26:22 index.ts:24:13 222 +route DELETE /m2/res23/:id mod2.ts:27:25 index.ts:24:13 223 +route PATCH /m2/res24/:id mod2.ts:28:24 index.ts:24:13 224 +route GET /m2/res25/:id mod2.ts:29:22 index.ts:24:13 225 +route POST /m2/res26/:id mod2.ts:30:23 index.ts:24:13 226 +route PUT /m2/res27/:id mod2.ts:31:22 index.ts:24:13 227 +route DELETE /m2/res28/:id mod2.ts:32:25 index.ts:24:13 228 +route PATCH /m2/res29/:id mod2.ts:33:24 index.ts:24:13 229 +route GET /m2/res30/:id mod2.ts:34:22 index.ts:24:13 230 +route POST /m2/res31/:id mod2.ts:35:23 index.ts:24:13 231 +route PUT /m2/res32/:id mod2.ts:36:22 index.ts:24:13 232 +route DELETE /m2/res33/:id mod2.ts:37:25 index.ts:24:13 233 +route PATCH /m2/res34/:id mod2.ts:38:24 index.ts:24:13 234 +route GET /m2/res35/:id mod2.ts:39:22 index.ts:24:13 235 +route POST /m2/res36/:id mod2.ts:40:23 index.ts:24:13 236 +route PUT /m2/res37/:id mod2.ts:41:22 index.ts:24:13 237 +route DELETE /m2/res38/:id mod2.ts:42:25 index.ts:24:13 238 +route PATCH /m2/res39/:id mod2.ts:43:24 index.ts:24:13 239 +route GET /m2/res40/:id mod2.ts:44:22 index.ts:24:13 240 +route POST /m2/res41/:id mod2.ts:45:23 index.ts:24:13 241 +route PUT /m2/res42/:id mod2.ts:46:22 index.ts:24:13 242 +route DELETE /m2/res43/:id mod2.ts:47:25 index.ts:24:13 243 +route PATCH /m2/res44/:id mod2.ts:48:24 index.ts:24:13 244 +route GET /m2/res45/:id mod2.ts:49:22 index.ts:24:13 245 +route POST /m2/res46/:id mod2.ts:50:23 index.ts:24:13 246 +route PUT /m2/res47/:id mod2.ts:51:22 index.ts:24:13 247 +route DELETE /m2/res48/:id mod2.ts:52:25 index.ts:24:13 248 +route PATCH /m2/res49/:id mod2.ts:53:24 index.ts:24:13 249 +route GET /m2/res50/:id mod2.ts:54:22 index.ts:24:13 250 +route POST /m2/res51/:id mod2.ts:55:23 index.ts:24:13 251 +route PUT /m2/res52/:id mod2.ts:56:22 index.ts:24:13 252 +route DELETE /m2/res53/:id mod2.ts:57:25 index.ts:24:13 253 +route PATCH /m2/res54/:id mod2.ts:58:24 index.ts:24:13 254 +route GET /m2/res55/:id mod2.ts:59:22 index.ts:24:13 255 +route POST /m2/res56/:id mod2.ts:60:23 index.ts:24:13 256 +route PUT /m2/res57/:id mod2.ts:61:22 index.ts:24:13 257 +route DELETE /m2/res58/:id mod2.ts:62:25 index.ts:24:13 258 +route PATCH /m2/res59/:id mod2.ts:63:24 index.ts:24:13 259 +route GET /m2/res60/:id mod2.ts:64:22 index.ts:24:13 260 +route POST /m2/res61/:id mod2.ts:65:23 index.ts:24:13 261 +route PUT /m2/res62/:id mod2.ts:66:22 index.ts:24:13 262 +route DELETE /m2/res63/:id mod2.ts:67:25 index.ts:24:13 263 +route PATCH /m2/res64/:id mod2.ts:68:24 index.ts:24:13 264 +route GET /m2/res65/:id mod2.ts:69:22 index.ts:24:13 265 +route POST /m2/res66/:id mod2.ts:70:23 index.ts:24:13 266 +route PUT /m2/res67/:id mod2.ts:71:22 index.ts:24:13 267 +route DELETE /m2/res68/:id mod2.ts:72:25 index.ts:24:13 268 +route PATCH /m2/res69/:id mod2.ts:73:24 index.ts:24:13 269 +route GET /m2/res70/:id mod2.ts:74:22 index.ts:24:13 270 +route POST /m2/res71/:id mod2.ts:75:23 index.ts:24:13 271 +route PUT /m2/res72/:id mod2.ts:76:22 index.ts:24:13 272 +route DELETE /m2/res73/:id mod2.ts:77:25 index.ts:24:13 273 +route PATCH /m2/res74/:id mod2.ts:78:24 index.ts:24:13 274 +route GET /m2/res75/:id mod2.ts:79:22 index.ts:24:13 275 +route POST /m2/res76/:id mod2.ts:80:23 index.ts:24:13 276 +route PUT /m2/res77/:id mod2.ts:81:22 index.ts:24:13 277 +route DELETE /m2/res78/:id mod2.ts:82:25 index.ts:24:13 278 +route PATCH /m2/res79/:id mod2.ts:83:24 index.ts:24:13 279 +route GET /m2/res80/:id mod2.ts:84:22 index.ts:24:13 280 +route POST /m2/res81/:id mod2.ts:85:23 index.ts:24:13 281 +route PUT /m2/res82/:id mod2.ts:86:22 index.ts:24:13 282 +route DELETE /m2/res83/:id mod2.ts:87:25 index.ts:24:13 283 +route PATCH /m2/res84/:id mod2.ts:88:24 index.ts:24:13 284 +route GET /m2/res85/:id mod2.ts:89:22 index.ts:24:13 285 +route POST /m2/res86/:id mod2.ts:90:23 index.ts:24:13 286 +route PUT /m2/res87/:id mod2.ts:91:22 index.ts:24:13 287 +route DELETE /m2/res88/:id mod2.ts:92:25 index.ts:24:13 288 +route PATCH /m2/res89/:id mod2.ts:93:24 index.ts:24:13 289 +route GET /m2/res90/:id mod2.ts:94:22 index.ts:24:13 290 +route POST /m2/res91/:id mod2.ts:95:23 index.ts:24:13 291 +route PUT /m2/res92/:id mod2.ts:96:22 index.ts:24:13 292 +route DELETE /m2/res93/:id mod2.ts:97:25 index.ts:24:13 293 +route PATCH /m2/res94/:id mod2.ts:98:24 index.ts:24:13 294 +route GET /m2/res95/:id mod2.ts:99:22 index.ts:24:13 295 +route POST /m2/res96/:id mod2.ts:100:23 index.ts:24:13 296 +route PUT /m2/res97/:id mod2.ts:101:22 index.ts:24:13 297 +route DELETE /m2/res98/:id mod2.ts:102:25 index.ts:24:13 298 +route PATCH /m2/res99/:id mod2.ts:103:24 index.ts:24:13 299 +route GET /m3/res0/:id mod3.ts:4:21 index.ts:24:13 300 +route POST /m3/res1/:id mod3.ts:5:22 index.ts:24:13 301 +route PUT /m3/res2/:id mod3.ts:6:21 index.ts:24:13 302 +route DELETE /m3/res3/:id mod3.ts:7:24 index.ts:24:13 303 +route PATCH /m3/res4/:id mod3.ts:8:23 index.ts:24:13 304 +route GET /m3/res5/:id mod3.ts:9:21 index.ts:24:13 305 +route POST /m3/res6/:id mod3.ts:10:22 index.ts:24:13 306 +route PUT /m3/res7/:id mod3.ts:11:21 index.ts:24:13 307 +route DELETE /m3/res8/:id mod3.ts:12:24 index.ts:24:13 308 +route PATCH /m3/res9/:id mod3.ts:13:23 index.ts:24:13 309 +route GET /m3/res10/:id mod3.ts:14:22 index.ts:24:13 310 +route POST /m3/res11/:id mod3.ts:15:23 index.ts:24:13 311 +route PUT /m3/res12/:id mod3.ts:16:22 index.ts:24:13 312 +route DELETE /m3/res13/:id mod3.ts:17:25 index.ts:24:13 313 +route PATCH /m3/res14/:id mod3.ts:18:24 index.ts:24:13 314 +route GET /m3/res15/:id mod3.ts:19:22 index.ts:24:13 315 +route POST /m3/res16/:id mod3.ts:20:23 index.ts:24:13 316 +route PUT /m3/res17/:id mod3.ts:21:22 index.ts:24:13 317 +route DELETE /m3/res18/:id mod3.ts:22:25 index.ts:24:13 318 +route PATCH /m3/res19/:id mod3.ts:23:24 index.ts:24:13 319 +route GET /m3/res20/:id mod3.ts:24:22 index.ts:24:13 320 +route POST /m3/res21/:id mod3.ts:25:23 index.ts:24:13 321 +route PUT /m3/res22/:id mod3.ts:26:22 index.ts:24:13 322 +route DELETE /m3/res23/:id mod3.ts:27:25 index.ts:24:13 323 +route PATCH /m3/res24/:id mod3.ts:28:24 index.ts:24:13 324 +route GET /m3/res25/:id mod3.ts:29:22 index.ts:24:13 325 +route POST /m3/res26/:id mod3.ts:30:23 index.ts:24:13 326 +route PUT /m3/res27/:id mod3.ts:31:22 index.ts:24:13 327 +route DELETE /m3/res28/:id mod3.ts:32:25 index.ts:24:13 328 +route PATCH /m3/res29/:id mod3.ts:33:24 index.ts:24:13 329 +route GET /m3/res30/:id mod3.ts:34:22 index.ts:24:13 330 +route POST /m3/res31/:id mod3.ts:35:23 index.ts:24:13 331 +route PUT /m3/res32/:id mod3.ts:36:22 index.ts:24:13 332 +route DELETE /m3/res33/:id mod3.ts:37:25 index.ts:24:13 333 +route PATCH /m3/res34/:id mod3.ts:38:24 index.ts:24:13 334 +route GET /m3/res35/:id mod3.ts:39:22 index.ts:24:13 335 +route POST /m3/res36/:id mod3.ts:40:23 index.ts:24:13 336 +route PUT /m3/res37/:id mod3.ts:41:22 index.ts:24:13 337 +route DELETE /m3/res38/:id mod3.ts:42:25 index.ts:24:13 338 +route PATCH /m3/res39/:id mod3.ts:43:24 index.ts:24:13 339 +route GET /m3/res40/:id mod3.ts:44:22 index.ts:24:13 340 +route POST /m3/res41/:id mod3.ts:45:23 index.ts:24:13 341 +route PUT /m3/res42/:id mod3.ts:46:22 index.ts:24:13 342 +route DELETE /m3/res43/:id mod3.ts:47:25 index.ts:24:13 343 +route PATCH /m3/res44/:id mod3.ts:48:24 index.ts:24:13 344 +route GET /m3/res45/:id mod3.ts:49:22 index.ts:24:13 345 +route POST /m3/res46/:id mod3.ts:50:23 index.ts:24:13 346 +route PUT /m3/res47/:id mod3.ts:51:22 index.ts:24:13 347 +route DELETE /m3/res48/:id mod3.ts:52:25 index.ts:24:13 348 +route PATCH /m3/res49/:id mod3.ts:53:24 index.ts:24:13 349 +route GET /m3/res50/:id mod3.ts:54:22 index.ts:24:13 350 +route POST /m3/res51/:id mod3.ts:55:23 index.ts:24:13 351 +route PUT /m3/res52/:id mod3.ts:56:22 index.ts:24:13 352 +route DELETE /m3/res53/:id mod3.ts:57:25 index.ts:24:13 353 +route PATCH /m3/res54/:id mod3.ts:58:24 index.ts:24:13 354 +route GET /m3/res55/:id mod3.ts:59:22 index.ts:24:13 355 +route POST /m3/res56/:id mod3.ts:60:23 index.ts:24:13 356 +route PUT /m3/res57/:id mod3.ts:61:22 index.ts:24:13 357 +route DELETE /m3/res58/:id mod3.ts:62:25 index.ts:24:13 358 +route PATCH /m3/res59/:id mod3.ts:63:24 index.ts:24:13 359 +route GET /m3/res60/:id mod3.ts:64:22 index.ts:24:13 360 +route POST /m3/res61/:id mod3.ts:65:23 index.ts:24:13 361 +route PUT /m3/res62/:id mod3.ts:66:22 index.ts:24:13 362 +route DELETE /m3/res63/:id mod3.ts:67:25 index.ts:24:13 363 +route PATCH /m3/res64/:id mod3.ts:68:24 index.ts:24:13 364 +route GET /m3/res65/:id mod3.ts:69:22 index.ts:24:13 365 +route POST /m3/res66/:id mod3.ts:70:23 index.ts:24:13 366 +route PUT /m3/res67/:id mod3.ts:71:22 index.ts:24:13 367 +route DELETE /m3/res68/:id mod3.ts:72:25 index.ts:24:13 368 +route PATCH /m3/res69/:id mod3.ts:73:24 index.ts:24:13 369 +route GET /m3/res70/:id mod3.ts:74:22 index.ts:24:13 370 +route POST /m3/res71/:id mod3.ts:75:23 index.ts:24:13 371 +route PUT /m3/res72/:id mod3.ts:76:22 index.ts:24:13 372 +route DELETE /m3/res73/:id mod3.ts:77:25 index.ts:24:13 373 +route PATCH /m3/res74/:id mod3.ts:78:24 index.ts:24:13 374 +route GET /m3/res75/:id mod3.ts:79:22 index.ts:24:13 375 +route POST /m3/res76/:id mod3.ts:80:23 index.ts:24:13 376 +route PUT /m3/res77/:id mod3.ts:81:22 index.ts:24:13 377 +route DELETE /m3/res78/:id mod3.ts:82:25 index.ts:24:13 378 +route PATCH /m3/res79/:id mod3.ts:83:24 index.ts:24:13 379 +route GET /m3/res80/:id mod3.ts:84:22 index.ts:24:13 380 +route POST /m3/res81/:id mod3.ts:85:23 index.ts:24:13 381 +route PUT /m3/res82/:id mod3.ts:86:22 index.ts:24:13 382 +route DELETE /m3/res83/:id mod3.ts:87:25 index.ts:24:13 383 +route PATCH /m3/res84/:id mod3.ts:88:24 index.ts:24:13 384 +route GET /m3/res85/:id mod3.ts:89:22 index.ts:24:13 385 +route POST /m3/res86/:id mod3.ts:90:23 index.ts:24:13 386 +route PUT /m3/res87/:id mod3.ts:91:22 index.ts:24:13 387 +route DELETE /m3/res88/:id mod3.ts:92:25 index.ts:24:13 388 +route PATCH /m3/res89/:id mod3.ts:93:24 index.ts:24:13 389 +route GET /m3/res90/:id mod3.ts:94:22 index.ts:24:13 390 +route POST /m3/res91/:id mod3.ts:95:23 index.ts:24:13 391 +route PUT /m3/res92/:id mod3.ts:96:22 index.ts:24:13 392 +route DELETE /m3/res93/:id mod3.ts:97:25 index.ts:24:13 393 +route PATCH /m3/res94/:id mod3.ts:98:24 index.ts:24:13 394 +route GET /m3/res95/:id mod3.ts:99:22 index.ts:24:13 395 +route POST /m3/res96/:id mod3.ts:100:23 index.ts:24:13 396 +route PUT /m3/res97/:id mod3.ts:101:22 index.ts:24:13 397 +route DELETE /m3/res98/:id mod3.ts:102:25 index.ts:24:13 398 +route PATCH /m3/res99/:id mod3.ts:103:24 index.ts:24:13 399 +route GET /m4/res0/:id mod4.ts:4:21 index.ts:24:13 400 +route POST /m4/res1/:id mod4.ts:5:22 index.ts:24:13 401 +route PUT /m4/res2/:id mod4.ts:6:21 index.ts:24:13 402 +route DELETE /m4/res3/:id mod4.ts:7:24 index.ts:24:13 403 +route PATCH /m4/res4/:id mod4.ts:8:23 index.ts:24:13 404 +route GET /m4/res5/:id mod4.ts:9:21 index.ts:24:13 405 +route POST /m4/res6/:id mod4.ts:10:22 index.ts:24:13 406 +route PUT /m4/res7/:id mod4.ts:11:21 index.ts:24:13 407 +route DELETE /m4/res8/:id mod4.ts:12:24 index.ts:24:13 408 +route PATCH /m4/res9/:id mod4.ts:13:23 index.ts:24:13 409 +route GET /m4/res10/:id mod4.ts:14:22 index.ts:24:13 410 +route POST /m4/res11/:id mod4.ts:15:23 index.ts:24:13 411 +route PUT /m4/res12/:id mod4.ts:16:22 index.ts:24:13 412 +route DELETE /m4/res13/:id mod4.ts:17:25 index.ts:24:13 413 +route PATCH /m4/res14/:id mod4.ts:18:24 index.ts:24:13 414 +route GET /m4/res15/:id mod4.ts:19:22 index.ts:24:13 415 +route POST /m4/res16/:id mod4.ts:20:23 index.ts:24:13 416 +route PUT /m4/res17/:id mod4.ts:21:22 index.ts:24:13 417 +route DELETE /m4/res18/:id mod4.ts:22:25 index.ts:24:13 418 +route PATCH /m4/res19/:id mod4.ts:23:24 index.ts:24:13 419 +route GET /m4/res20/:id mod4.ts:24:22 index.ts:24:13 420 +route POST /m4/res21/:id mod4.ts:25:23 index.ts:24:13 421 +route PUT /m4/res22/:id mod4.ts:26:22 index.ts:24:13 422 +route DELETE /m4/res23/:id mod4.ts:27:25 index.ts:24:13 423 +route PATCH /m4/res24/:id mod4.ts:28:24 index.ts:24:13 424 +route GET /m4/res25/:id mod4.ts:29:22 index.ts:24:13 425 +route POST /m4/res26/:id mod4.ts:30:23 index.ts:24:13 426 +route PUT /m4/res27/:id mod4.ts:31:22 index.ts:24:13 427 +route DELETE /m4/res28/:id mod4.ts:32:25 index.ts:24:13 428 +route PATCH /m4/res29/:id mod4.ts:33:24 index.ts:24:13 429 +route GET /m4/res30/:id mod4.ts:34:22 index.ts:24:13 430 +route POST /m4/res31/:id mod4.ts:35:23 index.ts:24:13 431 +route PUT /m4/res32/:id mod4.ts:36:22 index.ts:24:13 432 +route DELETE /m4/res33/:id mod4.ts:37:25 index.ts:24:13 433 +route PATCH /m4/res34/:id mod4.ts:38:24 index.ts:24:13 434 +route GET /m4/res35/:id mod4.ts:39:22 index.ts:24:13 435 +route POST /m4/res36/:id mod4.ts:40:23 index.ts:24:13 436 +route PUT /m4/res37/:id mod4.ts:41:22 index.ts:24:13 437 +route DELETE /m4/res38/:id mod4.ts:42:25 index.ts:24:13 438 +route PATCH /m4/res39/:id mod4.ts:43:24 index.ts:24:13 439 +route GET /m4/res40/:id mod4.ts:44:22 index.ts:24:13 440 +route POST /m4/res41/:id mod4.ts:45:23 index.ts:24:13 441 +route PUT /m4/res42/:id mod4.ts:46:22 index.ts:24:13 442 +route DELETE /m4/res43/:id mod4.ts:47:25 index.ts:24:13 443 +route PATCH /m4/res44/:id mod4.ts:48:24 index.ts:24:13 444 +route GET /m4/res45/:id mod4.ts:49:22 index.ts:24:13 445 +route POST /m4/res46/:id mod4.ts:50:23 index.ts:24:13 446 +route PUT /m4/res47/:id mod4.ts:51:22 index.ts:24:13 447 +route DELETE /m4/res48/:id mod4.ts:52:25 index.ts:24:13 448 +route PATCH /m4/res49/:id mod4.ts:53:24 index.ts:24:13 449 +route GET /m4/res50/:id mod4.ts:54:22 index.ts:24:13 450 +route POST /m4/res51/:id mod4.ts:55:23 index.ts:24:13 451 +route PUT /m4/res52/:id mod4.ts:56:22 index.ts:24:13 452 +route DELETE /m4/res53/:id mod4.ts:57:25 index.ts:24:13 453 +route PATCH /m4/res54/:id mod4.ts:58:24 index.ts:24:13 454 +route GET /m4/res55/:id mod4.ts:59:22 index.ts:24:13 455 +route POST /m4/res56/:id mod4.ts:60:23 index.ts:24:13 456 +route PUT /m4/res57/:id mod4.ts:61:22 index.ts:24:13 457 +route DELETE /m4/res58/:id mod4.ts:62:25 index.ts:24:13 458 +route PATCH /m4/res59/:id mod4.ts:63:24 index.ts:24:13 459 +route GET /m4/res60/:id mod4.ts:64:22 index.ts:24:13 460 +route POST /m4/res61/:id mod4.ts:65:23 index.ts:24:13 461 +route PUT /m4/res62/:id mod4.ts:66:22 index.ts:24:13 462 +route DELETE /m4/res63/:id mod4.ts:67:25 index.ts:24:13 463 +route PATCH /m4/res64/:id mod4.ts:68:24 index.ts:24:13 464 +route GET /m4/res65/:id mod4.ts:69:22 index.ts:24:13 465 +route POST /m4/res66/:id mod4.ts:70:23 index.ts:24:13 466 +route PUT /m4/res67/:id mod4.ts:71:22 index.ts:24:13 467 +route DELETE /m4/res68/:id mod4.ts:72:25 index.ts:24:13 468 +route PATCH /m4/res69/:id mod4.ts:73:24 index.ts:24:13 469 +route GET /m4/res70/:id mod4.ts:74:22 index.ts:24:13 470 +route POST /m4/res71/:id mod4.ts:75:23 index.ts:24:13 471 +route PUT /m4/res72/:id mod4.ts:76:22 index.ts:24:13 472 +route DELETE /m4/res73/:id mod4.ts:77:25 index.ts:24:13 473 +route PATCH /m4/res74/:id mod4.ts:78:24 index.ts:24:13 474 +route GET /m4/res75/:id mod4.ts:79:22 index.ts:24:13 475 +route POST /m4/res76/:id mod4.ts:80:23 index.ts:24:13 476 +route PUT /m4/res77/:id mod4.ts:81:22 index.ts:24:13 477 +route DELETE /m4/res78/:id mod4.ts:82:25 index.ts:24:13 478 +route PATCH /m4/res79/:id mod4.ts:83:24 index.ts:24:13 479 +route GET /m4/res80/:id mod4.ts:84:22 index.ts:24:13 480 +route POST /m4/res81/:id mod4.ts:85:23 index.ts:24:13 481 +route PUT /m4/res82/:id mod4.ts:86:22 index.ts:24:13 482 +route DELETE /m4/res83/:id mod4.ts:87:25 index.ts:24:13 483 +route PATCH /m4/res84/:id mod4.ts:88:24 index.ts:24:13 484 +route GET /m4/res85/:id mod4.ts:89:22 index.ts:24:13 485 +route POST /m4/res86/:id mod4.ts:90:23 index.ts:24:13 486 +route PUT /m4/res87/:id mod4.ts:91:22 index.ts:24:13 487 +route DELETE /m4/res88/:id mod4.ts:92:25 index.ts:24:13 488 +route PATCH /m4/res89/:id mod4.ts:93:24 index.ts:24:13 489 +route GET /m4/res90/:id mod4.ts:94:22 index.ts:24:13 490 +route POST /m4/res91/:id mod4.ts:95:23 index.ts:24:13 491 +route PUT /m4/res92/:id mod4.ts:96:22 index.ts:24:13 492 +route DELETE /m4/res93/:id mod4.ts:97:25 index.ts:24:13 493 +route PATCH /m4/res94/:id mod4.ts:98:24 index.ts:24:13 494 +route GET /m4/res95/:id mod4.ts:99:22 index.ts:24:13 495 +route POST /m4/res96/:id mod4.ts:100:23 index.ts:24:13 496 +route PUT /m4/res97/:id mod4.ts:101:22 index.ts:24:13 497 +route DELETE /m4/res98/:id mod4.ts:102:25 index.ts:24:13 498 +route PATCH /m4/res99/:id mod4.ts:103:24 index.ts:24:13 499 +route GET /m5/res0/:id mod5.ts:4:21 index.ts:24:13 500 +route POST /m5/res1/:id mod5.ts:5:22 index.ts:24:13 501 +route PUT /m5/res2/:id mod5.ts:6:21 index.ts:24:13 502 +route DELETE /m5/res3/:id mod5.ts:7:24 index.ts:24:13 503 +route PATCH /m5/res4/:id mod5.ts:8:23 index.ts:24:13 504 +route GET /m5/res5/:id mod5.ts:9:21 index.ts:24:13 505 +route POST /m5/res6/:id mod5.ts:10:22 index.ts:24:13 506 +route PUT /m5/res7/:id mod5.ts:11:21 index.ts:24:13 507 +route DELETE /m5/res8/:id mod5.ts:12:24 index.ts:24:13 508 +route PATCH /m5/res9/:id mod5.ts:13:23 index.ts:24:13 509 +route GET /m5/res10/:id mod5.ts:14:22 index.ts:24:13 510 +route POST /m5/res11/:id mod5.ts:15:23 index.ts:24:13 511 +route PUT /m5/res12/:id mod5.ts:16:22 index.ts:24:13 512 +route DELETE /m5/res13/:id mod5.ts:17:25 index.ts:24:13 513 +route PATCH /m5/res14/:id mod5.ts:18:24 index.ts:24:13 514 +route GET /m5/res15/:id mod5.ts:19:22 index.ts:24:13 515 +route POST /m5/res16/:id mod5.ts:20:23 index.ts:24:13 516 +route PUT /m5/res17/:id mod5.ts:21:22 index.ts:24:13 517 +route DELETE /m5/res18/:id mod5.ts:22:25 index.ts:24:13 518 +route PATCH /m5/res19/:id mod5.ts:23:24 index.ts:24:13 519 +route GET /m5/res20/:id mod5.ts:24:22 index.ts:24:13 520 +route POST /m5/res21/:id mod5.ts:25:23 index.ts:24:13 521 +route PUT /m5/res22/:id mod5.ts:26:22 index.ts:24:13 522 +route DELETE /m5/res23/:id mod5.ts:27:25 index.ts:24:13 523 +route PATCH /m5/res24/:id mod5.ts:28:24 index.ts:24:13 524 +route GET /m5/res25/:id mod5.ts:29:22 index.ts:24:13 525 +route POST /m5/res26/:id mod5.ts:30:23 index.ts:24:13 526 +route PUT /m5/res27/:id mod5.ts:31:22 index.ts:24:13 527 +route DELETE /m5/res28/:id mod5.ts:32:25 index.ts:24:13 528 +route PATCH /m5/res29/:id mod5.ts:33:24 index.ts:24:13 529 +route GET /m5/res30/:id mod5.ts:34:22 index.ts:24:13 530 +route POST /m5/res31/:id mod5.ts:35:23 index.ts:24:13 531 +route PUT /m5/res32/:id mod5.ts:36:22 index.ts:24:13 532 +route DELETE /m5/res33/:id mod5.ts:37:25 index.ts:24:13 533 +route PATCH /m5/res34/:id mod5.ts:38:24 index.ts:24:13 534 +route GET /m5/res35/:id mod5.ts:39:22 index.ts:24:13 535 +route POST /m5/res36/:id mod5.ts:40:23 index.ts:24:13 536 +route PUT /m5/res37/:id mod5.ts:41:22 index.ts:24:13 537 +route DELETE /m5/res38/:id mod5.ts:42:25 index.ts:24:13 538 +route PATCH /m5/res39/:id mod5.ts:43:24 index.ts:24:13 539 +route GET /m5/res40/:id mod5.ts:44:22 index.ts:24:13 540 +route POST /m5/res41/:id mod5.ts:45:23 index.ts:24:13 541 +route PUT /m5/res42/:id mod5.ts:46:22 index.ts:24:13 542 +route DELETE /m5/res43/:id mod5.ts:47:25 index.ts:24:13 543 +route PATCH /m5/res44/:id mod5.ts:48:24 index.ts:24:13 544 +route GET /m5/res45/:id mod5.ts:49:22 index.ts:24:13 545 +route POST /m5/res46/:id mod5.ts:50:23 index.ts:24:13 546 +route PUT /m5/res47/:id mod5.ts:51:22 index.ts:24:13 547 +route DELETE /m5/res48/:id mod5.ts:52:25 index.ts:24:13 548 +route PATCH /m5/res49/:id mod5.ts:53:24 index.ts:24:13 549 +route GET /m5/res50/:id mod5.ts:54:22 index.ts:24:13 550 +route POST /m5/res51/:id mod5.ts:55:23 index.ts:24:13 551 +route PUT /m5/res52/:id mod5.ts:56:22 index.ts:24:13 552 +route DELETE /m5/res53/:id mod5.ts:57:25 index.ts:24:13 553 +route PATCH /m5/res54/:id mod5.ts:58:24 index.ts:24:13 554 +route GET /m5/res55/:id mod5.ts:59:22 index.ts:24:13 555 +route POST /m5/res56/:id mod5.ts:60:23 index.ts:24:13 556 +route PUT /m5/res57/:id mod5.ts:61:22 index.ts:24:13 557 +route DELETE /m5/res58/:id mod5.ts:62:25 index.ts:24:13 558 +route PATCH /m5/res59/:id mod5.ts:63:24 index.ts:24:13 559 +route GET /m5/res60/:id mod5.ts:64:22 index.ts:24:13 560 +route POST /m5/res61/:id mod5.ts:65:23 index.ts:24:13 561 +route PUT /m5/res62/:id mod5.ts:66:22 index.ts:24:13 562 +route DELETE /m5/res63/:id mod5.ts:67:25 index.ts:24:13 563 +route PATCH /m5/res64/:id mod5.ts:68:24 index.ts:24:13 564 +route GET /m5/res65/:id mod5.ts:69:22 index.ts:24:13 565 +route POST /m5/res66/:id mod5.ts:70:23 index.ts:24:13 566 +route PUT /m5/res67/:id mod5.ts:71:22 index.ts:24:13 567 +route DELETE /m5/res68/:id mod5.ts:72:25 index.ts:24:13 568 +route PATCH /m5/res69/:id mod5.ts:73:24 index.ts:24:13 569 +route GET /m5/res70/:id mod5.ts:74:22 index.ts:24:13 570 +route POST /m5/res71/:id mod5.ts:75:23 index.ts:24:13 571 +route PUT /m5/res72/:id mod5.ts:76:22 index.ts:24:13 572 +route DELETE /m5/res73/:id mod5.ts:77:25 index.ts:24:13 573 +route PATCH /m5/res74/:id mod5.ts:78:24 index.ts:24:13 574 +route GET /m5/res75/:id mod5.ts:79:22 index.ts:24:13 575 +route POST /m5/res76/:id mod5.ts:80:23 index.ts:24:13 576 +route PUT /m5/res77/:id mod5.ts:81:22 index.ts:24:13 577 +route DELETE /m5/res78/:id mod5.ts:82:25 index.ts:24:13 578 +route PATCH /m5/res79/:id mod5.ts:83:24 index.ts:24:13 579 +route GET /m5/res80/:id mod5.ts:84:22 index.ts:24:13 580 +route POST /m5/res81/:id mod5.ts:85:23 index.ts:24:13 581 +route PUT /m5/res82/:id mod5.ts:86:22 index.ts:24:13 582 +route DELETE /m5/res83/:id mod5.ts:87:25 index.ts:24:13 583 +route PATCH /m5/res84/:id mod5.ts:88:24 index.ts:24:13 584 +route GET /m5/res85/:id mod5.ts:89:22 index.ts:24:13 585 +route POST /m5/res86/:id mod5.ts:90:23 index.ts:24:13 586 +route PUT /m5/res87/:id mod5.ts:91:22 index.ts:24:13 587 +route DELETE /m5/res88/:id mod5.ts:92:25 index.ts:24:13 588 +route PATCH /m5/res89/:id mod5.ts:93:24 index.ts:24:13 589 +route GET /m5/res90/:id mod5.ts:94:22 index.ts:24:13 590 +route POST /m5/res91/:id mod5.ts:95:23 index.ts:24:13 591 +route PUT /m5/res92/:id mod5.ts:96:22 index.ts:24:13 592 +route DELETE /m5/res93/:id mod5.ts:97:25 index.ts:24:13 593 +route PATCH /m5/res94/:id mod5.ts:98:24 index.ts:24:13 594 +route GET /m5/res95/:id mod5.ts:99:22 index.ts:24:13 595 +route POST /m5/res96/:id mod5.ts:100:23 index.ts:24:13 596 +route PUT /m5/res97/:id mod5.ts:101:22 index.ts:24:13 597 +route DELETE /m5/res98/:id mod5.ts:102:25 index.ts:24:13 598 +route PATCH /m5/res99/:id mod5.ts:103:24 index.ts:24:13 599 +route GET /m6/res0/:id mod6.ts:4:21 index.ts:24:13 600 +route POST /m6/res1/:id mod6.ts:5:22 index.ts:24:13 601 +route PUT /m6/res2/:id mod6.ts:6:21 index.ts:24:13 602 +route DELETE /m6/res3/:id mod6.ts:7:24 index.ts:24:13 603 +route PATCH /m6/res4/:id mod6.ts:8:23 index.ts:24:13 604 +route GET /m6/res5/:id mod6.ts:9:21 index.ts:24:13 605 +route POST /m6/res6/:id mod6.ts:10:22 index.ts:24:13 606 +route PUT /m6/res7/:id mod6.ts:11:21 index.ts:24:13 607 +route DELETE /m6/res8/:id mod6.ts:12:24 index.ts:24:13 608 +route PATCH /m6/res9/:id mod6.ts:13:23 index.ts:24:13 609 +route GET /m6/res10/:id mod6.ts:14:22 index.ts:24:13 610 +route POST /m6/res11/:id mod6.ts:15:23 index.ts:24:13 611 +route PUT /m6/res12/:id mod6.ts:16:22 index.ts:24:13 612 +route DELETE /m6/res13/:id mod6.ts:17:25 index.ts:24:13 613 +route PATCH /m6/res14/:id mod6.ts:18:24 index.ts:24:13 614 +route GET /m6/res15/:id mod6.ts:19:22 index.ts:24:13 615 +route POST /m6/res16/:id mod6.ts:20:23 index.ts:24:13 616 +route PUT /m6/res17/:id mod6.ts:21:22 index.ts:24:13 617 +route DELETE /m6/res18/:id mod6.ts:22:25 index.ts:24:13 618 +route PATCH /m6/res19/:id mod6.ts:23:24 index.ts:24:13 619 +route GET /m6/res20/:id mod6.ts:24:22 index.ts:24:13 620 +route POST /m6/res21/:id mod6.ts:25:23 index.ts:24:13 621 +route PUT /m6/res22/:id mod6.ts:26:22 index.ts:24:13 622 +route DELETE /m6/res23/:id mod6.ts:27:25 index.ts:24:13 623 +route PATCH /m6/res24/:id mod6.ts:28:24 index.ts:24:13 624 +route GET /m6/res25/:id mod6.ts:29:22 index.ts:24:13 625 +route POST /m6/res26/:id mod6.ts:30:23 index.ts:24:13 626 +route PUT /m6/res27/:id mod6.ts:31:22 index.ts:24:13 627 +route DELETE /m6/res28/:id mod6.ts:32:25 index.ts:24:13 628 +route PATCH /m6/res29/:id mod6.ts:33:24 index.ts:24:13 629 +route GET /m6/res30/:id mod6.ts:34:22 index.ts:24:13 630 +route POST /m6/res31/:id mod6.ts:35:23 index.ts:24:13 631 +route PUT /m6/res32/:id mod6.ts:36:22 index.ts:24:13 632 +route DELETE /m6/res33/:id mod6.ts:37:25 index.ts:24:13 633 +route PATCH /m6/res34/:id mod6.ts:38:24 index.ts:24:13 634 +route GET /m6/res35/:id mod6.ts:39:22 index.ts:24:13 635 +route POST /m6/res36/:id mod6.ts:40:23 index.ts:24:13 636 +route PUT /m6/res37/:id mod6.ts:41:22 index.ts:24:13 637 +route DELETE /m6/res38/:id mod6.ts:42:25 index.ts:24:13 638 +route PATCH /m6/res39/:id mod6.ts:43:24 index.ts:24:13 639 +route GET /m6/res40/:id mod6.ts:44:22 index.ts:24:13 640 +route POST /m6/res41/:id mod6.ts:45:23 index.ts:24:13 641 +route PUT /m6/res42/:id mod6.ts:46:22 index.ts:24:13 642 +route DELETE /m6/res43/:id mod6.ts:47:25 index.ts:24:13 643 +route PATCH /m6/res44/:id mod6.ts:48:24 index.ts:24:13 644 +route GET /m6/res45/:id mod6.ts:49:22 index.ts:24:13 645 +route POST /m6/res46/:id mod6.ts:50:23 index.ts:24:13 646 +route PUT /m6/res47/:id mod6.ts:51:22 index.ts:24:13 647 +route DELETE /m6/res48/:id mod6.ts:52:25 index.ts:24:13 648 +route PATCH /m6/res49/:id mod6.ts:53:24 index.ts:24:13 649 +route GET /m6/res50/:id mod6.ts:54:22 index.ts:24:13 650 +route POST /m6/res51/:id mod6.ts:55:23 index.ts:24:13 651 +route PUT /m6/res52/:id mod6.ts:56:22 index.ts:24:13 652 +route DELETE /m6/res53/:id mod6.ts:57:25 index.ts:24:13 653 +route PATCH /m6/res54/:id mod6.ts:58:24 index.ts:24:13 654 +route GET /m6/res55/:id mod6.ts:59:22 index.ts:24:13 655 +route POST /m6/res56/:id mod6.ts:60:23 index.ts:24:13 656 +route PUT /m6/res57/:id mod6.ts:61:22 index.ts:24:13 657 +route DELETE /m6/res58/:id mod6.ts:62:25 index.ts:24:13 658 +route PATCH /m6/res59/:id mod6.ts:63:24 index.ts:24:13 659 +route GET /m6/res60/:id mod6.ts:64:22 index.ts:24:13 660 +route POST /m6/res61/:id mod6.ts:65:23 index.ts:24:13 661 +route PUT /m6/res62/:id mod6.ts:66:22 index.ts:24:13 662 +route DELETE /m6/res63/:id mod6.ts:67:25 index.ts:24:13 663 +route PATCH /m6/res64/:id mod6.ts:68:24 index.ts:24:13 664 +route GET /m6/res65/:id mod6.ts:69:22 index.ts:24:13 665 +route POST /m6/res66/:id mod6.ts:70:23 index.ts:24:13 666 +route PUT /m6/res67/:id mod6.ts:71:22 index.ts:24:13 667 +route DELETE /m6/res68/:id mod6.ts:72:25 index.ts:24:13 668 +route PATCH /m6/res69/:id mod6.ts:73:24 index.ts:24:13 669 +route GET /m6/res70/:id mod6.ts:74:22 index.ts:24:13 670 +route POST /m6/res71/:id mod6.ts:75:23 index.ts:24:13 671 +route PUT /m6/res72/:id mod6.ts:76:22 index.ts:24:13 672 +route DELETE /m6/res73/:id mod6.ts:77:25 index.ts:24:13 673 +route PATCH /m6/res74/:id mod6.ts:78:24 index.ts:24:13 674 +route GET /m6/res75/:id mod6.ts:79:22 index.ts:24:13 675 +route POST /m6/res76/:id mod6.ts:80:23 index.ts:24:13 676 +route PUT /m6/res77/:id mod6.ts:81:22 index.ts:24:13 677 +route DELETE /m6/res78/:id mod6.ts:82:25 index.ts:24:13 678 +route PATCH /m6/res79/:id mod6.ts:83:24 index.ts:24:13 679 +route GET /m6/res80/:id mod6.ts:84:22 index.ts:24:13 680 +route POST /m6/res81/:id mod6.ts:85:23 index.ts:24:13 681 +route PUT /m6/res82/:id mod6.ts:86:22 index.ts:24:13 682 +route DELETE /m6/res83/:id mod6.ts:87:25 index.ts:24:13 683 +route PATCH /m6/res84/:id mod6.ts:88:24 index.ts:24:13 684 +route GET /m6/res85/:id mod6.ts:89:22 index.ts:24:13 685 +route POST /m6/res86/:id mod6.ts:90:23 index.ts:24:13 686 +route PUT /m6/res87/:id mod6.ts:91:22 index.ts:24:13 687 +route DELETE /m6/res88/:id mod6.ts:92:25 index.ts:24:13 688 +route PATCH /m6/res89/:id mod6.ts:93:24 index.ts:24:13 689 +route GET /m6/res90/:id mod6.ts:94:22 index.ts:24:13 690 +route POST /m6/res91/:id mod6.ts:95:23 index.ts:24:13 691 +route PUT /m6/res92/:id mod6.ts:96:22 index.ts:24:13 692 +route DELETE /m6/res93/:id mod6.ts:97:25 index.ts:24:13 693 +route PATCH /m6/res94/:id mod6.ts:98:24 index.ts:24:13 694 +route GET /m6/res95/:id mod6.ts:99:22 index.ts:24:13 695 +route POST /m6/res96/:id mod6.ts:100:23 index.ts:24:13 696 +route PUT /m6/res97/:id mod6.ts:101:22 index.ts:24:13 697 +route DELETE /m6/res98/:id mod6.ts:102:25 index.ts:24:13 698 +route PATCH /m6/res99/:id mod6.ts:103:24 index.ts:24:13 699 +route GET /m7/res0/:id mod7.ts:4:21 index.ts:24:13 700 +route POST /m7/res1/:id mod7.ts:5:22 index.ts:24:13 701 +route PUT /m7/res2/:id mod7.ts:6:21 index.ts:24:13 702 +route DELETE /m7/res3/:id mod7.ts:7:24 index.ts:24:13 703 +route PATCH /m7/res4/:id mod7.ts:8:23 index.ts:24:13 704 +route GET /m7/res5/:id mod7.ts:9:21 index.ts:24:13 705 +route POST /m7/res6/:id mod7.ts:10:22 index.ts:24:13 706 +route PUT /m7/res7/:id mod7.ts:11:21 index.ts:24:13 707 +route DELETE /m7/res8/:id mod7.ts:12:24 index.ts:24:13 708 +route PATCH /m7/res9/:id mod7.ts:13:23 index.ts:24:13 709 +route GET /m7/res10/:id mod7.ts:14:22 index.ts:24:13 710 +route POST /m7/res11/:id mod7.ts:15:23 index.ts:24:13 711 +route PUT /m7/res12/:id mod7.ts:16:22 index.ts:24:13 712 +route DELETE /m7/res13/:id mod7.ts:17:25 index.ts:24:13 713 +route PATCH /m7/res14/:id mod7.ts:18:24 index.ts:24:13 714 +route GET /m7/res15/:id mod7.ts:19:22 index.ts:24:13 715 +route POST /m7/res16/:id mod7.ts:20:23 index.ts:24:13 716 +route PUT /m7/res17/:id mod7.ts:21:22 index.ts:24:13 717 +route DELETE /m7/res18/:id mod7.ts:22:25 index.ts:24:13 718 +route PATCH /m7/res19/:id mod7.ts:23:24 index.ts:24:13 719 +route GET /m7/res20/:id mod7.ts:24:22 index.ts:24:13 720 +route POST /m7/res21/:id mod7.ts:25:23 index.ts:24:13 721 +route PUT /m7/res22/:id mod7.ts:26:22 index.ts:24:13 722 +route DELETE /m7/res23/:id mod7.ts:27:25 index.ts:24:13 723 +route PATCH /m7/res24/:id mod7.ts:28:24 index.ts:24:13 724 +route GET /m7/res25/:id mod7.ts:29:22 index.ts:24:13 725 +route POST /m7/res26/:id mod7.ts:30:23 index.ts:24:13 726 +route PUT /m7/res27/:id mod7.ts:31:22 index.ts:24:13 727 +route DELETE /m7/res28/:id mod7.ts:32:25 index.ts:24:13 728 +route PATCH /m7/res29/:id mod7.ts:33:24 index.ts:24:13 729 +route GET /m7/res30/:id mod7.ts:34:22 index.ts:24:13 730 +route POST /m7/res31/:id mod7.ts:35:23 index.ts:24:13 731 +route PUT /m7/res32/:id mod7.ts:36:22 index.ts:24:13 732 +route DELETE /m7/res33/:id mod7.ts:37:25 index.ts:24:13 733 +route PATCH /m7/res34/:id mod7.ts:38:24 index.ts:24:13 734 +route GET /m7/res35/:id mod7.ts:39:22 index.ts:24:13 735 +route POST /m7/res36/:id mod7.ts:40:23 index.ts:24:13 736 +route PUT /m7/res37/:id mod7.ts:41:22 index.ts:24:13 737 +route DELETE /m7/res38/:id mod7.ts:42:25 index.ts:24:13 738 +route PATCH /m7/res39/:id mod7.ts:43:24 index.ts:24:13 739 +route GET /m7/res40/:id mod7.ts:44:22 index.ts:24:13 740 +route POST /m7/res41/:id mod7.ts:45:23 index.ts:24:13 741 +route PUT /m7/res42/:id mod7.ts:46:22 index.ts:24:13 742 +route DELETE /m7/res43/:id mod7.ts:47:25 index.ts:24:13 743 +route PATCH /m7/res44/:id mod7.ts:48:24 index.ts:24:13 744 +route GET /m7/res45/:id mod7.ts:49:22 index.ts:24:13 745 +route POST /m7/res46/:id mod7.ts:50:23 index.ts:24:13 746 +route PUT /m7/res47/:id mod7.ts:51:22 index.ts:24:13 747 +route DELETE /m7/res48/:id mod7.ts:52:25 index.ts:24:13 748 +route PATCH /m7/res49/:id mod7.ts:53:24 index.ts:24:13 749 +route GET /m7/res50/:id mod7.ts:54:22 index.ts:24:13 750 +route POST /m7/res51/:id mod7.ts:55:23 index.ts:24:13 751 +route PUT /m7/res52/:id mod7.ts:56:22 index.ts:24:13 752 +route DELETE /m7/res53/:id mod7.ts:57:25 index.ts:24:13 753 +route PATCH /m7/res54/:id mod7.ts:58:24 index.ts:24:13 754 +route GET /m7/res55/:id mod7.ts:59:22 index.ts:24:13 755 +route POST /m7/res56/:id mod7.ts:60:23 index.ts:24:13 756 +route PUT /m7/res57/:id mod7.ts:61:22 index.ts:24:13 757 +route DELETE /m7/res58/:id mod7.ts:62:25 index.ts:24:13 758 +route PATCH /m7/res59/:id mod7.ts:63:24 index.ts:24:13 759 +route GET /m7/res60/:id mod7.ts:64:22 index.ts:24:13 760 +route POST /m7/res61/:id mod7.ts:65:23 index.ts:24:13 761 +route PUT /m7/res62/:id mod7.ts:66:22 index.ts:24:13 762 +route DELETE /m7/res63/:id mod7.ts:67:25 index.ts:24:13 763 +route PATCH /m7/res64/:id mod7.ts:68:24 index.ts:24:13 764 +route GET /m7/res65/:id mod7.ts:69:22 index.ts:24:13 765 +route POST /m7/res66/:id mod7.ts:70:23 index.ts:24:13 766 +route PUT /m7/res67/:id mod7.ts:71:22 index.ts:24:13 767 +route DELETE /m7/res68/:id mod7.ts:72:25 index.ts:24:13 768 +route PATCH /m7/res69/:id mod7.ts:73:24 index.ts:24:13 769 +route GET /m7/res70/:id mod7.ts:74:22 index.ts:24:13 770 +route POST /m7/res71/:id mod7.ts:75:23 index.ts:24:13 771 +route PUT /m7/res72/:id mod7.ts:76:22 index.ts:24:13 772 +route DELETE /m7/res73/:id mod7.ts:77:25 index.ts:24:13 773 +route PATCH /m7/res74/:id mod7.ts:78:24 index.ts:24:13 774 +route GET /m7/res75/:id mod7.ts:79:22 index.ts:24:13 775 +route POST /m7/res76/:id mod7.ts:80:23 index.ts:24:13 776 +route PUT /m7/res77/:id mod7.ts:81:22 index.ts:24:13 777 +route DELETE /m7/res78/:id mod7.ts:82:25 index.ts:24:13 778 +route PATCH /m7/res79/:id mod7.ts:83:24 index.ts:24:13 779 +route GET /m7/res80/:id mod7.ts:84:22 index.ts:24:13 780 +route POST /m7/res81/:id mod7.ts:85:23 index.ts:24:13 781 +route PUT /m7/res82/:id mod7.ts:86:22 index.ts:24:13 782 +route DELETE /m7/res83/:id mod7.ts:87:25 index.ts:24:13 783 +route PATCH /m7/res84/:id mod7.ts:88:24 index.ts:24:13 784 +route GET /m7/res85/:id mod7.ts:89:22 index.ts:24:13 785 +route POST /m7/res86/:id mod7.ts:90:23 index.ts:24:13 786 +route PUT /m7/res87/:id mod7.ts:91:22 index.ts:24:13 787 +route DELETE /m7/res88/:id mod7.ts:92:25 index.ts:24:13 788 +route PATCH /m7/res89/:id mod7.ts:93:24 index.ts:24:13 789 +route GET /m7/res90/:id mod7.ts:94:22 index.ts:24:13 790 +route POST /m7/res91/:id mod7.ts:95:23 index.ts:24:13 791 +route PUT /m7/res92/:id mod7.ts:96:22 index.ts:24:13 792 +route DELETE /m7/res93/:id mod7.ts:97:25 index.ts:24:13 793 +route PATCH /m7/res94/:id mod7.ts:98:24 index.ts:24:13 794 +route GET /m7/res95/:id mod7.ts:99:22 index.ts:24:13 795 +route POST /m7/res96/:id mod7.ts:100:23 index.ts:24:13 796 +route PUT /m7/res97/:id mod7.ts:101:22 index.ts:24:13 797 +route DELETE /m7/res98/:id mod7.ts:102:25 index.ts:24:13 798 +route PATCH /m7/res99/:id mod7.ts:103:24 index.ts:24:13 799 +route GET /m8/res0/:id mod8.ts:4:21 index.ts:24:13 800 +route POST /m8/res1/:id mod8.ts:5:22 index.ts:24:13 801 +route PUT /m8/res2/:id mod8.ts:6:21 index.ts:24:13 802 +route DELETE /m8/res3/:id mod8.ts:7:24 index.ts:24:13 803 +route PATCH /m8/res4/:id mod8.ts:8:23 index.ts:24:13 804 +route GET /m8/res5/:id mod8.ts:9:21 index.ts:24:13 805 +route POST /m8/res6/:id mod8.ts:10:22 index.ts:24:13 806 +route PUT /m8/res7/:id mod8.ts:11:21 index.ts:24:13 807 +route DELETE /m8/res8/:id mod8.ts:12:24 index.ts:24:13 808 +route PATCH /m8/res9/:id mod8.ts:13:23 index.ts:24:13 809 +route GET /m8/res10/:id mod8.ts:14:22 index.ts:24:13 810 +route POST /m8/res11/:id mod8.ts:15:23 index.ts:24:13 811 +route PUT /m8/res12/:id mod8.ts:16:22 index.ts:24:13 812 +route DELETE /m8/res13/:id mod8.ts:17:25 index.ts:24:13 813 +route PATCH /m8/res14/:id mod8.ts:18:24 index.ts:24:13 814 +route GET /m8/res15/:id mod8.ts:19:22 index.ts:24:13 815 +route POST /m8/res16/:id mod8.ts:20:23 index.ts:24:13 816 +route PUT /m8/res17/:id mod8.ts:21:22 index.ts:24:13 817 +route DELETE /m8/res18/:id mod8.ts:22:25 index.ts:24:13 818 +route PATCH /m8/res19/:id mod8.ts:23:24 index.ts:24:13 819 +route GET /m8/res20/:id mod8.ts:24:22 index.ts:24:13 820 +route POST /m8/res21/:id mod8.ts:25:23 index.ts:24:13 821 +route PUT /m8/res22/:id mod8.ts:26:22 index.ts:24:13 822 +route DELETE /m8/res23/:id mod8.ts:27:25 index.ts:24:13 823 +route PATCH /m8/res24/:id mod8.ts:28:24 index.ts:24:13 824 +route GET /m8/res25/:id mod8.ts:29:22 index.ts:24:13 825 +route POST /m8/res26/:id mod8.ts:30:23 index.ts:24:13 826 +route PUT /m8/res27/:id mod8.ts:31:22 index.ts:24:13 827 +route DELETE /m8/res28/:id mod8.ts:32:25 index.ts:24:13 828 +route PATCH /m8/res29/:id mod8.ts:33:24 index.ts:24:13 829 +route GET /m8/res30/:id mod8.ts:34:22 index.ts:24:13 830 +route POST /m8/res31/:id mod8.ts:35:23 index.ts:24:13 831 +route PUT /m8/res32/:id mod8.ts:36:22 index.ts:24:13 832 +route DELETE /m8/res33/:id mod8.ts:37:25 index.ts:24:13 833 +route PATCH /m8/res34/:id mod8.ts:38:24 index.ts:24:13 834 +route GET /m8/res35/:id mod8.ts:39:22 index.ts:24:13 835 +route POST /m8/res36/:id mod8.ts:40:23 index.ts:24:13 836 +route PUT /m8/res37/:id mod8.ts:41:22 index.ts:24:13 837 +route DELETE /m8/res38/:id mod8.ts:42:25 index.ts:24:13 838 +route PATCH /m8/res39/:id mod8.ts:43:24 index.ts:24:13 839 +route GET /m8/res40/:id mod8.ts:44:22 index.ts:24:13 840 +route POST /m8/res41/:id mod8.ts:45:23 index.ts:24:13 841 +route PUT /m8/res42/:id mod8.ts:46:22 index.ts:24:13 842 +route DELETE /m8/res43/:id mod8.ts:47:25 index.ts:24:13 843 +route PATCH /m8/res44/:id mod8.ts:48:24 index.ts:24:13 844 +route GET /m8/res45/:id mod8.ts:49:22 index.ts:24:13 845 +route POST /m8/res46/:id mod8.ts:50:23 index.ts:24:13 846 +route PUT /m8/res47/:id mod8.ts:51:22 index.ts:24:13 847 +route DELETE /m8/res48/:id mod8.ts:52:25 index.ts:24:13 848 +route PATCH /m8/res49/:id mod8.ts:53:24 index.ts:24:13 849 +route GET /m8/res50/:id mod8.ts:54:22 index.ts:24:13 850 +route POST /m8/res51/:id mod8.ts:55:23 index.ts:24:13 851 +route PUT /m8/res52/:id mod8.ts:56:22 index.ts:24:13 852 +route DELETE /m8/res53/:id mod8.ts:57:25 index.ts:24:13 853 +route PATCH /m8/res54/:id mod8.ts:58:24 index.ts:24:13 854 +route GET /m8/res55/:id mod8.ts:59:22 index.ts:24:13 855 +route POST /m8/res56/:id mod8.ts:60:23 index.ts:24:13 856 +route PUT /m8/res57/:id mod8.ts:61:22 index.ts:24:13 857 +route DELETE /m8/res58/:id mod8.ts:62:25 index.ts:24:13 858 +route PATCH /m8/res59/:id mod8.ts:63:24 index.ts:24:13 859 +route GET /m8/res60/:id mod8.ts:64:22 index.ts:24:13 860 +route POST /m8/res61/:id mod8.ts:65:23 index.ts:24:13 861 +route PUT /m8/res62/:id mod8.ts:66:22 index.ts:24:13 862 +route DELETE /m8/res63/:id mod8.ts:67:25 index.ts:24:13 863 +route PATCH /m8/res64/:id mod8.ts:68:24 index.ts:24:13 864 +route GET /m8/res65/:id mod8.ts:69:22 index.ts:24:13 865 +route POST /m8/res66/:id mod8.ts:70:23 index.ts:24:13 866 +route PUT /m8/res67/:id mod8.ts:71:22 index.ts:24:13 867 +route DELETE /m8/res68/:id mod8.ts:72:25 index.ts:24:13 868 +route PATCH /m8/res69/:id mod8.ts:73:24 index.ts:24:13 869 +route GET /m8/res70/:id mod8.ts:74:22 index.ts:24:13 870 +route POST /m8/res71/:id mod8.ts:75:23 index.ts:24:13 871 +route PUT /m8/res72/:id mod8.ts:76:22 index.ts:24:13 872 +route DELETE /m8/res73/:id mod8.ts:77:25 index.ts:24:13 873 +route PATCH /m8/res74/:id mod8.ts:78:24 index.ts:24:13 874 +route GET /m8/res75/:id mod8.ts:79:22 index.ts:24:13 875 +route POST /m8/res76/:id mod8.ts:80:23 index.ts:24:13 876 +route PUT /m8/res77/:id mod8.ts:81:22 index.ts:24:13 877 +route DELETE /m8/res78/:id mod8.ts:82:25 index.ts:24:13 878 +route PATCH /m8/res79/:id mod8.ts:83:24 index.ts:24:13 879 +route GET /m8/res80/:id mod8.ts:84:22 index.ts:24:13 880 +route POST /m8/res81/:id mod8.ts:85:23 index.ts:24:13 881 +route PUT /m8/res82/:id mod8.ts:86:22 index.ts:24:13 882 +route DELETE /m8/res83/:id mod8.ts:87:25 index.ts:24:13 883 +route PATCH /m8/res84/:id mod8.ts:88:24 index.ts:24:13 884 +route GET /m8/res85/:id mod8.ts:89:22 index.ts:24:13 885 +route POST /m8/res86/:id mod8.ts:90:23 index.ts:24:13 886 +route PUT /m8/res87/:id mod8.ts:91:22 index.ts:24:13 887 +route DELETE /m8/res88/:id mod8.ts:92:25 index.ts:24:13 888 +route PATCH /m8/res89/:id mod8.ts:93:24 index.ts:24:13 889 +route GET /m8/res90/:id mod8.ts:94:22 index.ts:24:13 890 +route POST /m8/res91/:id mod8.ts:95:23 index.ts:24:13 891 +route PUT /m8/res92/:id mod8.ts:96:22 index.ts:24:13 892 +route DELETE /m8/res93/:id mod8.ts:97:25 index.ts:24:13 893 +route PATCH /m8/res94/:id mod8.ts:98:24 index.ts:24:13 894 +route GET /m8/res95/:id mod8.ts:99:22 index.ts:24:13 895 +route POST /m8/res96/:id mod8.ts:100:23 index.ts:24:13 896 +route PUT /m8/res97/:id mod8.ts:101:22 index.ts:24:13 897 +route DELETE /m8/res98/:id mod8.ts:102:25 index.ts:24:13 898 +route PATCH /m8/res99/:id mod8.ts:103:24 index.ts:24:13 899 +route GET /m9/res0/:id mod9.ts:4:21 index.ts:24:13 900 +route POST /m9/res1/:id mod9.ts:5:22 index.ts:24:13 901 +route PUT /m9/res2/:id mod9.ts:6:21 index.ts:24:13 902 +route DELETE /m9/res3/:id mod9.ts:7:24 index.ts:24:13 903 +route PATCH /m9/res4/:id mod9.ts:8:23 index.ts:24:13 904 +route GET /m9/res5/:id mod9.ts:9:21 index.ts:24:13 905 +route POST /m9/res6/:id mod9.ts:10:22 index.ts:24:13 906 +route PUT /m9/res7/:id mod9.ts:11:21 index.ts:24:13 907 +route DELETE /m9/res8/:id mod9.ts:12:24 index.ts:24:13 908 +route PATCH /m9/res9/:id mod9.ts:13:23 index.ts:24:13 909 +route GET /m9/res10/:id mod9.ts:14:22 index.ts:24:13 910 +route POST /m9/res11/:id mod9.ts:15:23 index.ts:24:13 911 +route PUT /m9/res12/:id mod9.ts:16:22 index.ts:24:13 912 +route DELETE /m9/res13/:id mod9.ts:17:25 index.ts:24:13 913 +route PATCH /m9/res14/:id mod9.ts:18:24 index.ts:24:13 914 +route GET /m9/res15/:id mod9.ts:19:22 index.ts:24:13 915 +route POST /m9/res16/:id mod9.ts:20:23 index.ts:24:13 916 +route PUT /m9/res17/:id mod9.ts:21:22 index.ts:24:13 917 +route DELETE /m9/res18/:id mod9.ts:22:25 index.ts:24:13 918 +route PATCH /m9/res19/:id mod9.ts:23:24 index.ts:24:13 919 +route GET /m9/res20/:id mod9.ts:24:22 index.ts:24:13 920 +route POST /m9/res21/:id mod9.ts:25:23 index.ts:24:13 921 +route PUT /m9/res22/:id mod9.ts:26:22 index.ts:24:13 922 +route DELETE /m9/res23/:id mod9.ts:27:25 index.ts:24:13 923 +route PATCH /m9/res24/:id mod9.ts:28:24 index.ts:24:13 924 +route GET /m9/res25/:id mod9.ts:29:22 index.ts:24:13 925 +route POST /m9/res26/:id mod9.ts:30:23 index.ts:24:13 926 +route PUT /m9/res27/:id mod9.ts:31:22 index.ts:24:13 927 +route DELETE /m9/res28/:id mod9.ts:32:25 index.ts:24:13 928 +route PATCH /m9/res29/:id mod9.ts:33:24 index.ts:24:13 929 +route GET /m9/res30/:id mod9.ts:34:22 index.ts:24:13 930 +route POST /m9/res31/:id mod9.ts:35:23 index.ts:24:13 931 +route PUT /m9/res32/:id mod9.ts:36:22 index.ts:24:13 932 +route DELETE /m9/res33/:id mod9.ts:37:25 index.ts:24:13 933 +route PATCH /m9/res34/:id mod9.ts:38:24 index.ts:24:13 934 +route GET /m9/res35/:id mod9.ts:39:22 index.ts:24:13 935 +route POST /m9/res36/:id mod9.ts:40:23 index.ts:24:13 936 +route PUT /m9/res37/:id mod9.ts:41:22 index.ts:24:13 937 +route DELETE /m9/res38/:id mod9.ts:42:25 index.ts:24:13 938 +route PATCH /m9/res39/:id mod9.ts:43:24 index.ts:24:13 939 +route GET /m9/res40/:id mod9.ts:44:22 index.ts:24:13 940 +route POST /m9/res41/:id mod9.ts:45:23 index.ts:24:13 941 +route PUT /m9/res42/:id mod9.ts:46:22 index.ts:24:13 942 +route DELETE /m9/res43/:id mod9.ts:47:25 index.ts:24:13 943 +route PATCH /m9/res44/:id mod9.ts:48:24 index.ts:24:13 944 +route GET /m9/res45/:id mod9.ts:49:22 index.ts:24:13 945 +route POST /m9/res46/:id mod9.ts:50:23 index.ts:24:13 946 +route PUT /m9/res47/:id mod9.ts:51:22 index.ts:24:13 947 +route DELETE /m9/res48/:id mod9.ts:52:25 index.ts:24:13 948 +route PATCH /m9/res49/:id mod9.ts:53:24 index.ts:24:13 949 +route GET /m9/res50/:id mod9.ts:54:22 index.ts:24:13 950 +route POST /m9/res51/:id mod9.ts:55:23 index.ts:24:13 951 +route PUT /m9/res52/:id mod9.ts:56:22 index.ts:24:13 952 +route DELETE /m9/res53/:id mod9.ts:57:25 index.ts:24:13 953 +route PATCH /m9/res54/:id mod9.ts:58:24 index.ts:24:13 954 +route GET /m9/res55/:id mod9.ts:59:22 index.ts:24:13 955 +route POST /m9/res56/:id mod9.ts:60:23 index.ts:24:13 956 +route PUT /m9/res57/:id mod9.ts:61:22 index.ts:24:13 957 +route DELETE /m9/res58/:id mod9.ts:62:25 index.ts:24:13 958 +route PATCH /m9/res59/:id mod9.ts:63:24 index.ts:24:13 959 +route GET /m9/res60/:id mod9.ts:64:22 index.ts:24:13 960 +route POST /m9/res61/:id mod9.ts:65:23 index.ts:24:13 961 +route PUT /m9/res62/:id mod9.ts:66:22 index.ts:24:13 962 +route DELETE /m9/res63/:id mod9.ts:67:25 index.ts:24:13 963 +route PATCH /m9/res64/:id mod9.ts:68:24 index.ts:24:13 964 +route GET /m9/res65/:id mod9.ts:69:22 index.ts:24:13 965 +route POST /m9/res66/:id mod9.ts:70:23 index.ts:24:13 966 +route PUT /m9/res67/:id mod9.ts:71:22 index.ts:24:13 967 +route DELETE /m9/res68/:id mod9.ts:72:25 index.ts:24:13 968 +route PATCH /m9/res69/:id mod9.ts:73:24 index.ts:24:13 969 +route GET /m9/res70/:id mod9.ts:74:22 index.ts:24:13 970 +route POST /m9/res71/:id mod9.ts:75:23 index.ts:24:13 971 +route PUT /m9/res72/:id mod9.ts:76:22 index.ts:24:13 972 +route DELETE /m9/res73/:id mod9.ts:77:25 index.ts:24:13 973 +route PATCH /m9/res74/:id mod9.ts:78:24 index.ts:24:13 974 +route GET /m9/res75/:id mod9.ts:79:22 index.ts:24:13 975 +route POST /m9/res76/:id mod9.ts:80:23 index.ts:24:13 976 +route PUT /m9/res77/:id mod9.ts:81:22 index.ts:24:13 977 +route DELETE /m9/res78/:id mod9.ts:82:25 index.ts:24:13 978 +route PATCH /m9/res79/:id mod9.ts:83:24 index.ts:24:13 979 +route GET /m9/res80/:id mod9.ts:84:22 index.ts:24:13 980 +route POST /m9/res81/:id mod9.ts:85:23 index.ts:24:13 981 +route PUT /m9/res82/:id mod9.ts:86:22 index.ts:24:13 982 +route DELETE /m9/res83/:id mod9.ts:87:25 index.ts:24:13 983 +route PATCH /m9/res84/:id mod9.ts:88:24 index.ts:24:13 984 +route GET /m9/res85/:id mod9.ts:89:22 index.ts:24:13 985 +route POST /m9/res86/:id mod9.ts:90:23 index.ts:24:13 986 +route PUT /m9/res87/:id mod9.ts:91:22 index.ts:24:13 987 +route DELETE /m9/res88/:id mod9.ts:92:25 index.ts:24:13 988 +route PATCH /m9/res89/:id mod9.ts:93:24 index.ts:24:13 989 +route GET /m9/res90/:id mod9.ts:94:22 index.ts:24:13 990 +route POST /m9/res91/:id mod9.ts:95:23 index.ts:24:13 991 +route PUT /m9/res92/:id mod9.ts:96:22 index.ts:24:13 992 +route DELETE /m9/res93/:id mod9.ts:97:25 index.ts:24:13 993 +route PATCH /m9/res94/:id mod9.ts:98:24 index.ts:24:13 994 +route GET /m9/res95/:id mod9.ts:99:22 index.ts:24:13 995 +route POST /m9/res96/:id mod9.ts:100:23 index.ts:24:13 996 +route PUT /m9/res97/:id mod9.ts:101:22 index.ts:24:13 997 +route DELETE /m9/res98/:id mod9.ts:102:25 index.ts:24:13 998 +route PATCH /m9/res99/:id mod9.ts:103:24 index.ts:24:13 999 +route GET /m10/res0/:id mod10.ts:4:22 index.ts:24:13 1000 +route POST /m10/res1/:id mod10.ts:5:23 index.ts:24:13 1001 +route PUT /m10/res2/:id mod10.ts:6:22 index.ts:24:13 1002 +route DELETE /m10/res3/:id mod10.ts:7:25 index.ts:24:13 1003 +route PATCH /m10/res4/:id mod10.ts:8:24 index.ts:24:13 1004 +route GET /m10/res5/:id mod10.ts:9:22 index.ts:24:13 1005 +route POST /m10/res6/:id mod10.ts:10:23 index.ts:24:13 1006 +route PUT /m10/res7/:id mod10.ts:11:22 index.ts:24:13 1007 +route DELETE /m10/res8/:id mod10.ts:12:25 index.ts:24:13 1008 +route PATCH /m10/res9/:id mod10.ts:13:24 index.ts:24:13 1009 +route GET /m10/res10/:id mod10.ts:14:23 index.ts:24:13 1010 +route POST /m10/res11/:id mod10.ts:15:24 index.ts:24:13 1011 +route PUT /m10/res12/:id mod10.ts:16:23 index.ts:24:13 1012 +route DELETE /m10/res13/:id mod10.ts:17:26 index.ts:24:13 1013 +route PATCH /m10/res14/:id mod10.ts:18:25 index.ts:24:13 1014 +route GET /m10/res15/:id mod10.ts:19:23 index.ts:24:13 1015 +route POST /m10/res16/:id mod10.ts:20:24 index.ts:24:13 1016 +route PUT /m10/res17/:id mod10.ts:21:23 index.ts:24:13 1017 +route DELETE /m10/res18/:id mod10.ts:22:26 index.ts:24:13 1018 +route PATCH /m10/res19/:id mod10.ts:23:25 index.ts:24:13 1019 +route GET /m10/res20/:id mod10.ts:24:23 index.ts:24:13 1020 +route POST /m10/res21/:id mod10.ts:25:24 index.ts:24:13 1021 +route PUT /m10/res22/:id mod10.ts:26:23 index.ts:24:13 1022 +route DELETE /m10/res23/:id mod10.ts:27:26 index.ts:24:13 1023 +route PATCH /m10/res24/:id mod10.ts:28:25 index.ts:24:13 1024 +route GET /m10/res25/:id mod10.ts:29:23 index.ts:24:13 1025 +route POST /m10/res26/:id mod10.ts:30:24 index.ts:24:13 1026 +route PUT /m10/res27/:id mod10.ts:31:23 index.ts:24:13 1027 +route DELETE /m10/res28/:id mod10.ts:32:26 index.ts:24:13 1028 +route PATCH /m10/res29/:id mod10.ts:33:25 index.ts:24:13 1029 +route GET /m10/res30/:id mod10.ts:34:23 index.ts:24:13 1030 +route POST /m10/res31/:id mod10.ts:35:24 index.ts:24:13 1031 +route PUT /m10/res32/:id mod10.ts:36:23 index.ts:24:13 1032 +route DELETE /m10/res33/:id mod10.ts:37:26 index.ts:24:13 1033 +route PATCH /m10/res34/:id mod10.ts:38:25 index.ts:24:13 1034 +route GET /m10/res35/:id mod10.ts:39:23 index.ts:24:13 1035 +route POST /m10/res36/:id mod10.ts:40:24 index.ts:24:13 1036 +route PUT /m10/res37/:id mod10.ts:41:23 index.ts:24:13 1037 +route DELETE /m10/res38/:id mod10.ts:42:26 index.ts:24:13 1038 +route PATCH /m10/res39/:id mod10.ts:43:25 index.ts:24:13 1039 +route GET /m10/res40/:id mod10.ts:44:23 index.ts:24:13 1040 +route POST /m10/res41/:id mod10.ts:45:24 index.ts:24:13 1041 +route PUT /m10/res42/:id mod10.ts:46:23 index.ts:24:13 1042 +route DELETE /m10/res43/:id mod10.ts:47:26 index.ts:24:13 1043 +route PATCH /m10/res44/:id mod10.ts:48:25 index.ts:24:13 1044 +route GET /m10/res45/:id mod10.ts:49:23 index.ts:24:13 1045 +route POST /m10/res46/:id mod10.ts:50:24 index.ts:24:13 1046 +route PUT /m10/res47/:id mod10.ts:51:23 index.ts:24:13 1047 +route DELETE /m10/res48/:id mod10.ts:52:26 index.ts:24:13 1048 +route PATCH /m10/res49/:id mod10.ts:53:25 index.ts:24:13 1049 +route GET /m10/res50/:id mod10.ts:54:23 index.ts:24:13 1050 +route POST /m10/res51/:id mod10.ts:55:24 index.ts:24:13 1051 +route PUT /m10/res52/:id mod10.ts:56:23 index.ts:24:13 1052 +route DELETE /m10/res53/:id mod10.ts:57:26 index.ts:24:13 1053 +route PATCH /m10/res54/:id mod10.ts:58:25 index.ts:24:13 1054 +route GET /m10/res55/:id mod10.ts:59:23 index.ts:24:13 1055 +route POST /m10/res56/:id mod10.ts:60:24 index.ts:24:13 1056 +route PUT /m10/res57/:id mod10.ts:61:23 index.ts:24:13 1057 +route DELETE /m10/res58/:id mod10.ts:62:26 index.ts:24:13 1058 +route PATCH /m10/res59/:id mod10.ts:63:25 index.ts:24:13 1059 +route GET /m10/res60/:id mod10.ts:64:23 index.ts:24:13 1060 +route POST /m10/res61/:id mod10.ts:65:24 index.ts:24:13 1061 +route PUT /m10/res62/:id mod10.ts:66:23 index.ts:24:13 1062 +route DELETE /m10/res63/:id mod10.ts:67:26 index.ts:24:13 1063 +route PATCH /m10/res64/:id mod10.ts:68:25 index.ts:24:13 1064 +route GET /m10/res65/:id mod10.ts:69:23 index.ts:24:13 1065 +route POST /m10/res66/:id mod10.ts:70:24 index.ts:24:13 1066 +route PUT /m10/res67/:id mod10.ts:71:23 index.ts:24:13 1067 +route DELETE /m10/res68/:id mod10.ts:72:26 index.ts:24:13 1068 +route PATCH /m10/res69/:id mod10.ts:73:25 index.ts:24:13 1069 +route GET /m10/res70/:id mod10.ts:74:23 index.ts:24:13 1070 +route POST /m10/res71/:id mod10.ts:75:24 index.ts:24:13 1071 +route PUT /m10/res72/:id mod10.ts:76:23 index.ts:24:13 1072 +route DELETE /m10/res73/:id mod10.ts:77:26 index.ts:24:13 1073 +route PATCH /m10/res74/:id mod10.ts:78:25 index.ts:24:13 1074 +route GET /m10/res75/:id mod10.ts:79:23 index.ts:24:13 1075 +route POST /m10/res76/:id mod10.ts:80:24 index.ts:24:13 1076 +route PUT /m10/res77/:id mod10.ts:81:23 index.ts:24:13 1077 +route DELETE /m10/res78/:id mod10.ts:82:26 index.ts:24:13 1078 +route PATCH /m10/res79/:id mod10.ts:83:25 index.ts:24:13 1079 +route GET /m10/res80/:id mod10.ts:84:23 index.ts:24:13 1080 +route POST /m10/res81/:id mod10.ts:85:24 index.ts:24:13 1081 +route PUT /m10/res82/:id mod10.ts:86:23 index.ts:24:13 1082 +route DELETE /m10/res83/:id mod10.ts:87:26 index.ts:24:13 1083 +route PATCH /m10/res84/:id mod10.ts:88:25 index.ts:24:13 1084 +route GET /m10/res85/:id mod10.ts:89:23 index.ts:24:13 1085 +route POST /m10/res86/:id mod10.ts:90:24 index.ts:24:13 1086 +route PUT /m10/res87/:id mod10.ts:91:23 index.ts:24:13 1087 +route DELETE /m10/res88/:id mod10.ts:92:26 index.ts:24:13 1088 +route PATCH /m10/res89/:id mod10.ts:93:25 index.ts:24:13 1089 +route GET /m10/res90/:id mod10.ts:94:23 index.ts:24:13 1090 +route POST /m10/res91/:id mod10.ts:95:24 index.ts:24:13 1091 +route PUT /m10/res92/:id mod10.ts:96:23 index.ts:24:13 1092 +route DELETE /m10/res93/:id mod10.ts:97:26 index.ts:24:13 1093 +route PATCH /m10/res94/:id mod10.ts:98:25 index.ts:24:13 1094 +route GET /m10/res95/:id mod10.ts:99:23 index.ts:24:13 1095 +route POST /m10/res96/:id mod10.ts:100:24 index.ts:24:13 1096 +route PUT /m10/res97/:id mod10.ts:101:23 index.ts:24:13 1097 +route DELETE /m10/res98/:id mod10.ts:102:26 index.ts:24:13 1098 +route PATCH /m10/res99/:id mod10.ts:103:25 index.ts:24:13 1099 +route GET /m11/res0/:id mod11.ts:4:22 index.ts:24:13 1100 +route POST /m11/res1/:id mod11.ts:5:23 index.ts:24:13 1101 +route PUT /m11/res2/:id mod11.ts:6:22 index.ts:24:13 1102 +route DELETE /m11/res3/:id mod11.ts:7:25 index.ts:24:13 1103 +route PATCH /m11/res4/:id mod11.ts:8:24 index.ts:24:13 1104 +route GET /m11/res5/:id mod11.ts:9:22 index.ts:24:13 1105 +route POST /m11/res6/:id mod11.ts:10:23 index.ts:24:13 1106 +route PUT /m11/res7/:id mod11.ts:11:22 index.ts:24:13 1107 +route DELETE /m11/res8/:id mod11.ts:12:25 index.ts:24:13 1108 +route PATCH /m11/res9/:id mod11.ts:13:24 index.ts:24:13 1109 +route GET /m11/res10/:id mod11.ts:14:23 index.ts:24:13 1110 +route POST /m11/res11/:id mod11.ts:15:24 index.ts:24:13 1111 +route PUT /m11/res12/:id mod11.ts:16:23 index.ts:24:13 1112 +route DELETE /m11/res13/:id mod11.ts:17:26 index.ts:24:13 1113 +route PATCH /m11/res14/:id mod11.ts:18:25 index.ts:24:13 1114 +route GET /m11/res15/:id mod11.ts:19:23 index.ts:24:13 1115 +route POST /m11/res16/:id mod11.ts:20:24 index.ts:24:13 1116 +route PUT /m11/res17/:id mod11.ts:21:23 index.ts:24:13 1117 +route DELETE /m11/res18/:id mod11.ts:22:26 index.ts:24:13 1118 +route PATCH /m11/res19/:id mod11.ts:23:25 index.ts:24:13 1119 +route GET /m11/res20/:id mod11.ts:24:23 index.ts:24:13 1120 +route POST /m11/res21/:id mod11.ts:25:24 index.ts:24:13 1121 +route PUT /m11/res22/:id mod11.ts:26:23 index.ts:24:13 1122 +route DELETE /m11/res23/:id mod11.ts:27:26 index.ts:24:13 1123 +route PATCH /m11/res24/:id mod11.ts:28:25 index.ts:24:13 1124 +route GET /m11/res25/:id mod11.ts:29:23 index.ts:24:13 1125 +route POST /m11/res26/:id mod11.ts:30:24 index.ts:24:13 1126 +route PUT /m11/res27/:id mod11.ts:31:23 index.ts:24:13 1127 +route DELETE /m11/res28/:id mod11.ts:32:26 index.ts:24:13 1128 +route PATCH /m11/res29/:id mod11.ts:33:25 index.ts:24:13 1129 +route GET /m11/res30/:id mod11.ts:34:23 index.ts:24:13 1130 +route POST /m11/res31/:id mod11.ts:35:24 index.ts:24:13 1131 +route PUT /m11/res32/:id mod11.ts:36:23 index.ts:24:13 1132 +route DELETE /m11/res33/:id mod11.ts:37:26 index.ts:24:13 1133 +route PATCH /m11/res34/:id mod11.ts:38:25 index.ts:24:13 1134 +route GET /m11/res35/:id mod11.ts:39:23 index.ts:24:13 1135 +route POST /m11/res36/:id mod11.ts:40:24 index.ts:24:13 1136 +route PUT /m11/res37/:id mod11.ts:41:23 index.ts:24:13 1137 +route DELETE /m11/res38/:id mod11.ts:42:26 index.ts:24:13 1138 +route PATCH /m11/res39/:id mod11.ts:43:25 index.ts:24:13 1139 +route GET /m11/res40/:id mod11.ts:44:23 index.ts:24:13 1140 +route POST /m11/res41/:id mod11.ts:45:24 index.ts:24:13 1141 +route PUT /m11/res42/:id mod11.ts:46:23 index.ts:24:13 1142 +route DELETE /m11/res43/:id mod11.ts:47:26 index.ts:24:13 1143 +route PATCH /m11/res44/:id mod11.ts:48:25 index.ts:24:13 1144 +route GET /m11/res45/:id mod11.ts:49:23 index.ts:24:13 1145 +route POST /m11/res46/:id mod11.ts:50:24 index.ts:24:13 1146 +route PUT /m11/res47/:id mod11.ts:51:23 index.ts:24:13 1147 +route DELETE /m11/res48/:id mod11.ts:52:26 index.ts:24:13 1148 +route PATCH /m11/res49/:id mod11.ts:53:25 index.ts:24:13 1149 +route GET /m11/res50/:id mod11.ts:54:23 index.ts:24:13 1150 +route POST /m11/res51/:id mod11.ts:55:24 index.ts:24:13 1151 +route PUT /m11/res52/:id mod11.ts:56:23 index.ts:24:13 1152 +route DELETE /m11/res53/:id mod11.ts:57:26 index.ts:24:13 1153 +route PATCH /m11/res54/:id mod11.ts:58:25 index.ts:24:13 1154 +route GET /m11/res55/:id mod11.ts:59:23 index.ts:24:13 1155 +route POST /m11/res56/:id mod11.ts:60:24 index.ts:24:13 1156 +route PUT /m11/res57/:id mod11.ts:61:23 index.ts:24:13 1157 +route DELETE /m11/res58/:id mod11.ts:62:26 index.ts:24:13 1158 +route PATCH /m11/res59/:id mod11.ts:63:25 index.ts:24:13 1159 +route GET /m11/res60/:id mod11.ts:64:23 index.ts:24:13 1160 +route POST /m11/res61/:id mod11.ts:65:24 index.ts:24:13 1161 +route PUT /m11/res62/:id mod11.ts:66:23 index.ts:24:13 1162 +route DELETE /m11/res63/:id mod11.ts:67:26 index.ts:24:13 1163 +route PATCH /m11/res64/:id mod11.ts:68:25 index.ts:24:13 1164 +route GET /m11/res65/:id mod11.ts:69:23 index.ts:24:13 1165 +route POST /m11/res66/:id mod11.ts:70:24 index.ts:24:13 1166 +route PUT /m11/res67/:id mod11.ts:71:23 index.ts:24:13 1167 +route DELETE /m11/res68/:id mod11.ts:72:26 index.ts:24:13 1168 +route PATCH /m11/res69/:id mod11.ts:73:25 index.ts:24:13 1169 +route GET /m11/res70/:id mod11.ts:74:23 index.ts:24:13 1170 +route POST /m11/res71/:id mod11.ts:75:24 index.ts:24:13 1171 +route PUT /m11/res72/:id mod11.ts:76:23 index.ts:24:13 1172 +route DELETE /m11/res73/:id mod11.ts:77:26 index.ts:24:13 1173 +route PATCH /m11/res74/:id mod11.ts:78:25 index.ts:24:13 1174 +route GET /m11/res75/:id mod11.ts:79:23 index.ts:24:13 1175 +route POST /m11/res76/:id mod11.ts:80:24 index.ts:24:13 1176 +route PUT /m11/res77/:id mod11.ts:81:23 index.ts:24:13 1177 +route DELETE /m11/res78/:id mod11.ts:82:26 index.ts:24:13 1178 +route PATCH /m11/res79/:id mod11.ts:83:25 index.ts:24:13 1179 +route GET /m11/res80/:id mod11.ts:84:23 index.ts:24:13 1180 +route POST /m11/res81/:id mod11.ts:85:24 index.ts:24:13 1181 +route PUT /m11/res82/:id mod11.ts:86:23 index.ts:24:13 1182 +route DELETE /m11/res83/:id mod11.ts:87:26 index.ts:24:13 1183 +route PATCH /m11/res84/:id mod11.ts:88:25 index.ts:24:13 1184 +route GET /m11/res85/:id mod11.ts:89:23 index.ts:24:13 1185 +route POST /m11/res86/:id mod11.ts:90:24 index.ts:24:13 1186 +route PUT /m11/res87/:id mod11.ts:91:23 index.ts:24:13 1187 +route DELETE /m11/res88/:id mod11.ts:92:26 index.ts:24:13 1188 +route PATCH /m11/res89/:id mod11.ts:93:25 index.ts:24:13 1189 +route GET /m11/res90/:id mod11.ts:94:23 index.ts:24:13 1190 +route POST /m11/res91/:id mod11.ts:95:24 index.ts:24:13 1191 +route PUT /m11/res92/:id mod11.ts:96:23 index.ts:24:13 1192 +route DELETE /m11/res93/:id mod11.ts:97:26 index.ts:24:13 1193 +route PATCH /m11/res94/:id mod11.ts:98:25 index.ts:24:13 1194 +route GET /m11/res95/:id mod11.ts:99:23 index.ts:24:13 1195 +route POST /m11/res96/:id mod11.ts:100:24 index.ts:24:13 1196 +route PUT /m11/res97/:id mod11.ts:101:23 index.ts:24:13 1197 +route DELETE /m11/res98/:id mod11.ts:102:26 index.ts:24:13 1198 +route PATCH /m11/res99/:id mod11.ts:103:25 index.ts:24:13 1199 +route GET /m12/res0/:id mod12.ts:4:22 index.ts:24:13 1200 +route POST /m12/res1/:id mod12.ts:5:23 index.ts:24:13 1201 +route PUT /m12/res2/:id mod12.ts:6:22 index.ts:24:13 1202 +route DELETE /m12/res3/:id mod12.ts:7:25 index.ts:24:13 1203 +route PATCH /m12/res4/:id mod12.ts:8:24 index.ts:24:13 1204 +route GET /m12/res5/:id mod12.ts:9:22 index.ts:24:13 1205 +route POST /m12/res6/:id mod12.ts:10:23 index.ts:24:13 1206 +route PUT /m12/res7/:id mod12.ts:11:22 index.ts:24:13 1207 +route DELETE /m12/res8/:id mod12.ts:12:25 index.ts:24:13 1208 +route PATCH /m12/res9/:id mod12.ts:13:24 index.ts:24:13 1209 +route GET /m12/res10/:id mod12.ts:14:23 index.ts:24:13 1210 +route POST /m12/res11/:id mod12.ts:15:24 index.ts:24:13 1211 +route PUT /m12/res12/:id mod12.ts:16:23 index.ts:24:13 1212 +route DELETE /m12/res13/:id mod12.ts:17:26 index.ts:24:13 1213 +route PATCH /m12/res14/:id mod12.ts:18:25 index.ts:24:13 1214 +route GET /m12/res15/:id mod12.ts:19:23 index.ts:24:13 1215 +route POST /m12/res16/:id mod12.ts:20:24 index.ts:24:13 1216 +route PUT /m12/res17/:id mod12.ts:21:23 index.ts:24:13 1217 +route DELETE /m12/res18/:id mod12.ts:22:26 index.ts:24:13 1218 +route PATCH /m12/res19/:id mod12.ts:23:25 index.ts:24:13 1219 +route GET /m12/res20/:id mod12.ts:24:23 index.ts:24:13 1220 +route POST /m12/res21/:id mod12.ts:25:24 index.ts:24:13 1221 +route PUT /m12/res22/:id mod12.ts:26:23 index.ts:24:13 1222 +route DELETE /m12/res23/:id mod12.ts:27:26 index.ts:24:13 1223 +route PATCH /m12/res24/:id mod12.ts:28:25 index.ts:24:13 1224 +route GET /m12/res25/:id mod12.ts:29:23 index.ts:24:13 1225 +route POST /m12/res26/:id mod12.ts:30:24 index.ts:24:13 1226 +route PUT /m12/res27/:id mod12.ts:31:23 index.ts:24:13 1227 +route DELETE /m12/res28/:id mod12.ts:32:26 index.ts:24:13 1228 +route PATCH /m12/res29/:id mod12.ts:33:25 index.ts:24:13 1229 +route GET /m12/res30/:id mod12.ts:34:23 index.ts:24:13 1230 +route POST /m12/res31/:id mod12.ts:35:24 index.ts:24:13 1231 +route PUT /m12/res32/:id mod12.ts:36:23 index.ts:24:13 1232 +route DELETE /m12/res33/:id mod12.ts:37:26 index.ts:24:13 1233 +route PATCH /m12/res34/:id mod12.ts:38:25 index.ts:24:13 1234 +route GET /m12/res35/:id mod12.ts:39:23 index.ts:24:13 1235 +route POST /m12/res36/:id mod12.ts:40:24 index.ts:24:13 1236 +route PUT /m12/res37/:id mod12.ts:41:23 index.ts:24:13 1237 +route DELETE /m12/res38/:id mod12.ts:42:26 index.ts:24:13 1238 +route PATCH /m12/res39/:id mod12.ts:43:25 index.ts:24:13 1239 +route GET /m12/res40/:id mod12.ts:44:23 index.ts:24:13 1240 +route POST /m12/res41/:id mod12.ts:45:24 index.ts:24:13 1241 +route PUT /m12/res42/:id mod12.ts:46:23 index.ts:24:13 1242 +route DELETE /m12/res43/:id mod12.ts:47:26 index.ts:24:13 1243 +route PATCH /m12/res44/:id mod12.ts:48:25 index.ts:24:13 1244 +route GET /m12/res45/:id mod12.ts:49:23 index.ts:24:13 1245 +route POST /m12/res46/:id mod12.ts:50:24 index.ts:24:13 1246 +route PUT /m12/res47/:id mod12.ts:51:23 index.ts:24:13 1247 +route DELETE /m12/res48/:id mod12.ts:52:26 index.ts:24:13 1248 +route PATCH /m12/res49/:id mod12.ts:53:25 index.ts:24:13 1249 +route GET /m12/res50/:id mod12.ts:54:23 index.ts:24:13 1250 +route POST /m12/res51/:id mod12.ts:55:24 index.ts:24:13 1251 +route PUT /m12/res52/:id mod12.ts:56:23 index.ts:24:13 1252 +route DELETE /m12/res53/:id mod12.ts:57:26 index.ts:24:13 1253 +route PATCH /m12/res54/:id mod12.ts:58:25 index.ts:24:13 1254 +route GET /m12/res55/:id mod12.ts:59:23 index.ts:24:13 1255 +route POST /m12/res56/:id mod12.ts:60:24 index.ts:24:13 1256 +route PUT /m12/res57/:id mod12.ts:61:23 index.ts:24:13 1257 +route DELETE /m12/res58/:id mod12.ts:62:26 index.ts:24:13 1258 +route PATCH /m12/res59/:id mod12.ts:63:25 index.ts:24:13 1259 +route GET /m12/res60/:id mod12.ts:64:23 index.ts:24:13 1260 +route POST /m12/res61/:id mod12.ts:65:24 index.ts:24:13 1261 +route PUT /m12/res62/:id mod12.ts:66:23 index.ts:24:13 1262 +route DELETE /m12/res63/:id mod12.ts:67:26 index.ts:24:13 1263 +route PATCH /m12/res64/:id mod12.ts:68:25 index.ts:24:13 1264 +route GET /m12/res65/:id mod12.ts:69:23 index.ts:24:13 1265 +route POST /m12/res66/:id mod12.ts:70:24 index.ts:24:13 1266 +route PUT /m12/res67/:id mod12.ts:71:23 index.ts:24:13 1267 +route DELETE /m12/res68/:id mod12.ts:72:26 index.ts:24:13 1268 +route PATCH /m12/res69/:id mod12.ts:73:25 index.ts:24:13 1269 +route GET /m12/res70/:id mod12.ts:74:23 index.ts:24:13 1270 +route POST /m12/res71/:id mod12.ts:75:24 index.ts:24:13 1271 +route PUT /m12/res72/:id mod12.ts:76:23 index.ts:24:13 1272 +route DELETE /m12/res73/:id mod12.ts:77:26 index.ts:24:13 1273 +route PATCH /m12/res74/:id mod12.ts:78:25 index.ts:24:13 1274 +route GET /m12/res75/:id mod12.ts:79:23 index.ts:24:13 1275 +route POST /m12/res76/:id mod12.ts:80:24 index.ts:24:13 1276 +route PUT /m12/res77/:id mod12.ts:81:23 index.ts:24:13 1277 +route DELETE /m12/res78/:id mod12.ts:82:26 index.ts:24:13 1278 +route PATCH /m12/res79/:id mod12.ts:83:25 index.ts:24:13 1279 +route GET /m12/res80/:id mod12.ts:84:23 index.ts:24:13 1280 +route POST /m12/res81/:id mod12.ts:85:24 index.ts:24:13 1281 +route PUT /m12/res82/:id mod12.ts:86:23 index.ts:24:13 1282 +route DELETE /m12/res83/:id mod12.ts:87:26 index.ts:24:13 1283 +route PATCH /m12/res84/:id mod12.ts:88:25 index.ts:24:13 1284 +route GET /m12/res85/:id mod12.ts:89:23 index.ts:24:13 1285 +route POST /m12/res86/:id mod12.ts:90:24 index.ts:24:13 1286 +route PUT /m12/res87/:id mod12.ts:91:23 index.ts:24:13 1287 +route DELETE /m12/res88/:id mod12.ts:92:26 index.ts:24:13 1288 +route PATCH /m12/res89/:id mod12.ts:93:25 index.ts:24:13 1289 +route GET /m12/res90/:id mod12.ts:94:23 index.ts:24:13 1290 +route POST /m12/res91/:id mod12.ts:95:24 index.ts:24:13 1291 +route PUT /m12/res92/:id mod12.ts:96:23 index.ts:24:13 1292 +route DELETE /m12/res93/:id mod12.ts:97:26 index.ts:24:13 1293 +route PATCH /m12/res94/:id mod12.ts:98:25 index.ts:24:13 1294 +route GET /m12/res95/:id mod12.ts:99:23 index.ts:24:13 1295 +route POST /m12/res96/:id mod12.ts:100:24 index.ts:24:13 1296 +route PUT /m12/res97/:id mod12.ts:101:23 index.ts:24:13 1297 +route DELETE /m12/res98/:id mod12.ts:102:26 index.ts:24:13 1298 +route PATCH /m12/res99/:id mod12.ts:103:25 index.ts:24:13 1299 +route GET /m13/res0/:id mod13.ts:4:22 index.ts:24:13 1300 +route POST /m13/res1/:id mod13.ts:5:23 index.ts:24:13 1301 +route PUT /m13/res2/:id mod13.ts:6:22 index.ts:24:13 1302 +route DELETE /m13/res3/:id mod13.ts:7:25 index.ts:24:13 1303 +route PATCH /m13/res4/:id mod13.ts:8:24 index.ts:24:13 1304 +route GET /m13/res5/:id mod13.ts:9:22 index.ts:24:13 1305 +route POST /m13/res6/:id mod13.ts:10:23 index.ts:24:13 1306 +route PUT /m13/res7/:id mod13.ts:11:22 index.ts:24:13 1307 +route DELETE /m13/res8/:id mod13.ts:12:25 index.ts:24:13 1308 +route PATCH /m13/res9/:id mod13.ts:13:24 index.ts:24:13 1309 +route GET /m13/res10/:id mod13.ts:14:23 index.ts:24:13 1310 +route POST /m13/res11/:id mod13.ts:15:24 index.ts:24:13 1311 +route PUT /m13/res12/:id mod13.ts:16:23 index.ts:24:13 1312 +route DELETE /m13/res13/:id mod13.ts:17:26 index.ts:24:13 1313 +route PATCH /m13/res14/:id mod13.ts:18:25 index.ts:24:13 1314 +route GET /m13/res15/:id mod13.ts:19:23 index.ts:24:13 1315 +route POST /m13/res16/:id mod13.ts:20:24 index.ts:24:13 1316 +route PUT /m13/res17/:id mod13.ts:21:23 index.ts:24:13 1317 +route DELETE /m13/res18/:id mod13.ts:22:26 index.ts:24:13 1318 +route PATCH /m13/res19/:id mod13.ts:23:25 index.ts:24:13 1319 +route GET /m13/res20/:id mod13.ts:24:23 index.ts:24:13 1320 +route POST /m13/res21/:id mod13.ts:25:24 index.ts:24:13 1321 +route PUT /m13/res22/:id mod13.ts:26:23 index.ts:24:13 1322 +route DELETE /m13/res23/:id mod13.ts:27:26 index.ts:24:13 1323 +route PATCH /m13/res24/:id mod13.ts:28:25 index.ts:24:13 1324 +route GET /m13/res25/:id mod13.ts:29:23 index.ts:24:13 1325 +route POST /m13/res26/:id mod13.ts:30:24 index.ts:24:13 1326 +route PUT /m13/res27/:id mod13.ts:31:23 index.ts:24:13 1327 +route DELETE /m13/res28/:id mod13.ts:32:26 index.ts:24:13 1328 +route PATCH /m13/res29/:id mod13.ts:33:25 index.ts:24:13 1329 +route GET /m13/res30/:id mod13.ts:34:23 index.ts:24:13 1330 +route POST /m13/res31/:id mod13.ts:35:24 index.ts:24:13 1331 +route PUT /m13/res32/:id mod13.ts:36:23 index.ts:24:13 1332 +route DELETE /m13/res33/:id mod13.ts:37:26 index.ts:24:13 1333 +route PATCH /m13/res34/:id mod13.ts:38:25 index.ts:24:13 1334 +route GET /m13/res35/:id mod13.ts:39:23 index.ts:24:13 1335 +route POST /m13/res36/:id mod13.ts:40:24 index.ts:24:13 1336 +route PUT /m13/res37/:id mod13.ts:41:23 index.ts:24:13 1337 +route DELETE /m13/res38/:id mod13.ts:42:26 index.ts:24:13 1338 +route PATCH /m13/res39/:id mod13.ts:43:25 index.ts:24:13 1339 +route GET /m13/res40/:id mod13.ts:44:23 index.ts:24:13 1340 +route POST /m13/res41/:id mod13.ts:45:24 index.ts:24:13 1341 +route PUT /m13/res42/:id mod13.ts:46:23 index.ts:24:13 1342 +route DELETE /m13/res43/:id mod13.ts:47:26 index.ts:24:13 1343 +route PATCH /m13/res44/:id mod13.ts:48:25 index.ts:24:13 1344 +route GET /m13/res45/:id mod13.ts:49:23 index.ts:24:13 1345 +route POST /m13/res46/:id mod13.ts:50:24 index.ts:24:13 1346 +route PUT /m13/res47/:id mod13.ts:51:23 index.ts:24:13 1347 +route DELETE /m13/res48/:id mod13.ts:52:26 index.ts:24:13 1348 +route PATCH /m13/res49/:id mod13.ts:53:25 index.ts:24:13 1349 +route GET /m13/res50/:id mod13.ts:54:23 index.ts:24:13 1350 +route POST /m13/res51/:id mod13.ts:55:24 index.ts:24:13 1351 +route PUT /m13/res52/:id mod13.ts:56:23 index.ts:24:13 1352 +route DELETE /m13/res53/:id mod13.ts:57:26 index.ts:24:13 1353 +route PATCH /m13/res54/:id mod13.ts:58:25 index.ts:24:13 1354 +route GET /m13/res55/:id mod13.ts:59:23 index.ts:24:13 1355 +route POST /m13/res56/:id mod13.ts:60:24 index.ts:24:13 1356 +route PUT /m13/res57/:id mod13.ts:61:23 index.ts:24:13 1357 +route DELETE /m13/res58/:id mod13.ts:62:26 index.ts:24:13 1358 +route PATCH /m13/res59/:id mod13.ts:63:25 index.ts:24:13 1359 +route GET /m13/res60/:id mod13.ts:64:23 index.ts:24:13 1360 +route POST /m13/res61/:id mod13.ts:65:24 index.ts:24:13 1361 +route PUT /m13/res62/:id mod13.ts:66:23 index.ts:24:13 1362 +route DELETE /m13/res63/:id mod13.ts:67:26 index.ts:24:13 1363 +route PATCH /m13/res64/:id mod13.ts:68:25 index.ts:24:13 1364 +route GET /m13/res65/:id mod13.ts:69:23 index.ts:24:13 1365 +route POST /m13/res66/:id mod13.ts:70:24 index.ts:24:13 1366 +route PUT /m13/res67/:id mod13.ts:71:23 index.ts:24:13 1367 +route DELETE /m13/res68/:id mod13.ts:72:26 index.ts:24:13 1368 +route PATCH /m13/res69/:id mod13.ts:73:25 index.ts:24:13 1369 +route GET /m13/res70/:id mod13.ts:74:23 index.ts:24:13 1370 +route POST /m13/res71/:id mod13.ts:75:24 index.ts:24:13 1371 +route PUT /m13/res72/:id mod13.ts:76:23 index.ts:24:13 1372 +route DELETE /m13/res73/:id mod13.ts:77:26 index.ts:24:13 1373 +route PATCH /m13/res74/:id mod13.ts:78:25 index.ts:24:13 1374 +route GET /m13/res75/:id mod13.ts:79:23 index.ts:24:13 1375 +route POST /m13/res76/:id mod13.ts:80:24 index.ts:24:13 1376 +route PUT /m13/res77/:id mod13.ts:81:23 index.ts:24:13 1377 +route DELETE /m13/res78/:id mod13.ts:82:26 index.ts:24:13 1378 +route PATCH /m13/res79/:id mod13.ts:83:25 index.ts:24:13 1379 +route GET /m13/res80/:id mod13.ts:84:23 index.ts:24:13 1380 +route POST /m13/res81/:id mod13.ts:85:24 index.ts:24:13 1381 +route PUT /m13/res82/:id mod13.ts:86:23 index.ts:24:13 1382 +route DELETE /m13/res83/:id mod13.ts:87:26 index.ts:24:13 1383 +route PATCH /m13/res84/:id mod13.ts:88:25 index.ts:24:13 1384 +route GET /m13/res85/:id mod13.ts:89:23 index.ts:24:13 1385 +route POST /m13/res86/:id mod13.ts:90:24 index.ts:24:13 1386 +route PUT /m13/res87/:id mod13.ts:91:23 index.ts:24:13 1387 +route DELETE /m13/res88/:id mod13.ts:92:26 index.ts:24:13 1388 +route PATCH /m13/res89/:id mod13.ts:93:25 index.ts:24:13 1389 +route GET /m13/res90/:id mod13.ts:94:23 index.ts:24:13 1390 +route POST /m13/res91/:id mod13.ts:95:24 index.ts:24:13 1391 +route PUT /m13/res92/:id mod13.ts:96:23 index.ts:24:13 1392 +route DELETE /m13/res93/:id mod13.ts:97:26 index.ts:24:13 1393 +route PATCH /m13/res94/:id mod13.ts:98:25 index.ts:24:13 1394 +route GET /m13/res95/:id mod13.ts:99:23 index.ts:24:13 1395 +route POST /m13/res96/:id mod13.ts:100:24 index.ts:24:13 1396 +route PUT /m13/res97/:id mod13.ts:101:23 index.ts:24:13 1397 +route DELETE /m13/res98/:id mod13.ts:102:26 index.ts:24:13 1398 +route PATCH /m13/res99/:id mod13.ts:103:25 index.ts:24:13 1399 +route GET /m14/res0/:id mod14.ts:4:22 index.ts:24:13 1400 +route POST /m14/res1/:id mod14.ts:5:23 index.ts:24:13 1401 +route PUT /m14/res2/:id mod14.ts:6:22 index.ts:24:13 1402 +route DELETE /m14/res3/:id mod14.ts:7:25 index.ts:24:13 1403 +route PATCH /m14/res4/:id mod14.ts:8:24 index.ts:24:13 1404 +route GET /m14/res5/:id mod14.ts:9:22 index.ts:24:13 1405 +route POST /m14/res6/:id mod14.ts:10:23 index.ts:24:13 1406 +route PUT /m14/res7/:id mod14.ts:11:22 index.ts:24:13 1407 +route DELETE /m14/res8/:id mod14.ts:12:25 index.ts:24:13 1408 +route PATCH /m14/res9/:id mod14.ts:13:24 index.ts:24:13 1409 +route GET /m14/res10/:id mod14.ts:14:23 index.ts:24:13 1410 +route POST /m14/res11/:id mod14.ts:15:24 index.ts:24:13 1411 +route PUT /m14/res12/:id mod14.ts:16:23 index.ts:24:13 1412 +route DELETE /m14/res13/:id mod14.ts:17:26 index.ts:24:13 1413 +route PATCH /m14/res14/:id mod14.ts:18:25 index.ts:24:13 1414 +route GET /m14/res15/:id mod14.ts:19:23 index.ts:24:13 1415 +route POST /m14/res16/:id mod14.ts:20:24 index.ts:24:13 1416 +route PUT /m14/res17/:id mod14.ts:21:23 index.ts:24:13 1417 +route DELETE /m14/res18/:id mod14.ts:22:26 index.ts:24:13 1418 +route PATCH /m14/res19/:id mod14.ts:23:25 index.ts:24:13 1419 +route GET /m14/res20/:id mod14.ts:24:23 index.ts:24:13 1420 +route POST /m14/res21/:id mod14.ts:25:24 index.ts:24:13 1421 +route PUT /m14/res22/:id mod14.ts:26:23 index.ts:24:13 1422 +route DELETE /m14/res23/:id mod14.ts:27:26 index.ts:24:13 1423 +route PATCH /m14/res24/:id mod14.ts:28:25 index.ts:24:13 1424 +route GET /m14/res25/:id mod14.ts:29:23 index.ts:24:13 1425 +route POST /m14/res26/:id mod14.ts:30:24 index.ts:24:13 1426 +route PUT /m14/res27/:id mod14.ts:31:23 index.ts:24:13 1427 +route DELETE /m14/res28/:id mod14.ts:32:26 index.ts:24:13 1428 +route PATCH /m14/res29/:id mod14.ts:33:25 index.ts:24:13 1429 +route GET /m14/res30/:id mod14.ts:34:23 index.ts:24:13 1430 +route POST /m14/res31/:id mod14.ts:35:24 index.ts:24:13 1431 +route PUT /m14/res32/:id mod14.ts:36:23 index.ts:24:13 1432 +route DELETE /m14/res33/:id mod14.ts:37:26 index.ts:24:13 1433 +route PATCH /m14/res34/:id mod14.ts:38:25 index.ts:24:13 1434 +route GET /m14/res35/:id mod14.ts:39:23 index.ts:24:13 1435 +route POST /m14/res36/:id mod14.ts:40:24 index.ts:24:13 1436 +route PUT /m14/res37/:id mod14.ts:41:23 index.ts:24:13 1437 +route DELETE /m14/res38/:id mod14.ts:42:26 index.ts:24:13 1438 +route PATCH /m14/res39/:id mod14.ts:43:25 index.ts:24:13 1439 +route GET /m14/res40/:id mod14.ts:44:23 index.ts:24:13 1440 +route POST /m14/res41/:id mod14.ts:45:24 index.ts:24:13 1441 +route PUT /m14/res42/:id mod14.ts:46:23 index.ts:24:13 1442 +route DELETE /m14/res43/:id mod14.ts:47:26 index.ts:24:13 1443 +route PATCH /m14/res44/:id mod14.ts:48:25 index.ts:24:13 1444 +route GET /m14/res45/:id mod14.ts:49:23 index.ts:24:13 1445 +route POST /m14/res46/:id mod14.ts:50:24 index.ts:24:13 1446 +route PUT /m14/res47/:id mod14.ts:51:23 index.ts:24:13 1447 +route DELETE /m14/res48/:id mod14.ts:52:26 index.ts:24:13 1448 +route PATCH /m14/res49/:id mod14.ts:53:25 index.ts:24:13 1449 +route GET /m14/res50/:id mod14.ts:54:23 index.ts:24:13 1450 +route POST /m14/res51/:id mod14.ts:55:24 index.ts:24:13 1451 +route PUT /m14/res52/:id mod14.ts:56:23 index.ts:24:13 1452 +route DELETE /m14/res53/:id mod14.ts:57:26 index.ts:24:13 1453 +route PATCH /m14/res54/:id mod14.ts:58:25 index.ts:24:13 1454 +route GET /m14/res55/:id mod14.ts:59:23 index.ts:24:13 1455 +route POST /m14/res56/:id mod14.ts:60:24 index.ts:24:13 1456 +route PUT /m14/res57/:id mod14.ts:61:23 index.ts:24:13 1457 +route DELETE /m14/res58/:id mod14.ts:62:26 index.ts:24:13 1458 +route PATCH /m14/res59/:id mod14.ts:63:25 index.ts:24:13 1459 +route GET /m14/res60/:id mod14.ts:64:23 index.ts:24:13 1460 +route POST /m14/res61/:id mod14.ts:65:24 index.ts:24:13 1461 +route PUT /m14/res62/:id mod14.ts:66:23 index.ts:24:13 1462 +route DELETE /m14/res63/:id mod14.ts:67:26 index.ts:24:13 1463 +route PATCH /m14/res64/:id mod14.ts:68:25 index.ts:24:13 1464 +route GET /m14/res65/:id mod14.ts:69:23 index.ts:24:13 1465 +route POST /m14/res66/:id mod14.ts:70:24 index.ts:24:13 1466 +route PUT /m14/res67/:id mod14.ts:71:23 index.ts:24:13 1467 +route DELETE /m14/res68/:id mod14.ts:72:26 index.ts:24:13 1468 +route PATCH /m14/res69/:id mod14.ts:73:25 index.ts:24:13 1469 +route GET /m14/res70/:id mod14.ts:74:23 index.ts:24:13 1470 +route POST /m14/res71/:id mod14.ts:75:24 index.ts:24:13 1471 +route PUT /m14/res72/:id mod14.ts:76:23 index.ts:24:13 1472 +route DELETE /m14/res73/:id mod14.ts:77:26 index.ts:24:13 1473 +route PATCH /m14/res74/:id mod14.ts:78:25 index.ts:24:13 1474 +route GET /m14/res75/:id mod14.ts:79:23 index.ts:24:13 1475 +route POST /m14/res76/:id mod14.ts:80:24 index.ts:24:13 1476 +route PUT /m14/res77/:id mod14.ts:81:23 index.ts:24:13 1477 +route DELETE /m14/res78/:id mod14.ts:82:26 index.ts:24:13 1478 +route PATCH /m14/res79/:id mod14.ts:83:25 index.ts:24:13 1479 +route GET /m14/res80/:id mod14.ts:84:23 index.ts:24:13 1480 +route POST /m14/res81/:id mod14.ts:85:24 index.ts:24:13 1481 +route PUT /m14/res82/:id mod14.ts:86:23 index.ts:24:13 1482 +route DELETE /m14/res83/:id mod14.ts:87:26 index.ts:24:13 1483 +route PATCH /m14/res84/:id mod14.ts:88:25 index.ts:24:13 1484 +route GET /m14/res85/:id mod14.ts:89:23 index.ts:24:13 1485 +route POST /m14/res86/:id mod14.ts:90:24 index.ts:24:13 1486 +route PUT /m14/res87/:id mod14.ts:91:23 index.ts:24:13 1487 +route DELETE /m14/res88/:id mod14.ts:92:26 index.ts:24:13 1488 +route PATCH /m14/res89/:id mod14.ts:93:25 index.ts:24:13 1489 +route GET /m14/res90/:id mod14.ts:94:23 index.ts:24:13 1490 +route POST /m14/res91/:id mod14.ts:95:24 index.ts:24:13 1491 +route PUT /m14/res92/:id mod14.ts:96:23 index.ts:24:13 1492 +route DELETE /m14/res93/:id mod14.ts:97:26 index.ts:24:13 1493 +route PATCH /m14/res94/:id mod14.ts:98:25 index.ts:24:13 1494 +route GET /m14/res95/:id mod14.ts:99:23 index.ts:24:13 1495 +route POST /m14/res96/:id mod14.ts:100:24 index.ts:24:13 1496 +route PUT /m14/res97/:id mod14.ts:101:23 index.ts:24:13 1497 +route DELETE /m14/res98/:id mod14.ts:102:26 index.ts:24:13 1498 +route PATCH /m14/res99/:id mod14.ts:103:25 index.ts:24:13 1499 +route GET /m15/res0/:id mod15.ts:4:22 index.ts:24:13 1500 +route POST /m15/res1/:id mod15.ts:5:23 index.ts:24:13 1501 +route PUT /m15/res2/:id mod15.ts:6:22 index.ts:24:13 1502 +route DELETE /m15/res3/:id mod15.ts:7:25 index.ts:24:13 1503 +route PATCH /m15/res4/:id mod15.ts:8:24 index.ts:24:13 1504 +route GET /m15/res5/:id mod15.ts:9:22 index.ts:24:13 1505 +route POST /m15/res6/:id mod15.ts:10:23 index.ts:24:13 1506 +route PUT /m15/res7/:id mod15.ts:11:22 index.ts:24:13 1507 +route DELETE /m15/res8/:id mod15.ts:12:25 index.ts:24:13 1508 +route PATCH /m15/res9/:id mod15.ts:13:24 index.ts:24:13 1509 +route GET /m15/res10/:id mod15.ts:14:23 index.ts:24:13 1510 +route POST /m15/res11/:id mod15.ts:15:24 index.ts:24:13 1511 +route PUT /m15/res12/:id mod15.ts:16:23 index.ts:24:13 1512 +route DELETE /m15/res13/:id mod15.ts:17:26 index.ts:24:13 1513 +route PATCH /m15/res14/:id mod15.ts:18:25 index.ts:24:13 1514 +route GET /m15/res15/:id mod15.ts:19:23 index.ts:24:13 1515 +route POST /m15/res16/:id mod15.ts:20:24 index.ts:24:13 1516 +route PUT /m15/res17/:id mod15.ts:21:23 index.ts:24:13 1517 +route DELETE /m15/res18/:id mod15.ts:22:26 index.ts:24:13 1518 +route PATCH /m15/res19/:id mod15.ts:23:25 index.ts:24:13 1519 +route GET /m15/res20/:id mod15.ts:24:23 index.ts:24:13 1520 +route POST /m15/res21/:id mod15.ts:25:24 index.ts:24:13 1521 +route PUT /m15/res22/:id mod15.ts:26:23 index.ts:24:13 1522 +route DELETE /m15/res23/:id mod15.ts:27:26 index.ts:24:13 1523 +route PATCH /m15/res24/:id mod15.ts:28:25 index.ts:24:13 1524 +route GET /m15/res25/:id mod15.ts:29:23 index.ts:24:13 1525 +route POST /m15/res26/:id mod15.ts:30:24 index.ts:24:13 1526 +route PUT /m15/res27/:id mod15.ts:31:23 index.ts:24:13 1527 +route DELETE /m15/res28/:id mod15.ts:32:26 index.ts:24:13 1528 +route PATCH /m15/res29/:id mod15.ts:33:25 index.ts:24:13 1529 +route GET /m15/res30/:id mod15.ts:34:23 index.ts:24:13 1530 +route POST /m15/res31/:id mod15.ts:35:24 index.ts:24:13 1531 +route PUT /m15/res32/:id mod15.ts:36:23 index.ts:24:13 1532 +route DELETE /m15/res33/:id mod15.ts:37:26 index.ts:24:13 1533 +route PATCH /m15/res34/:id mod15.ts:38:25 index.ts:24:13 1534 +route GET /m15/res35/:id mod15.ts:39:23 index.ts:24:13 1535 +route POST /m15/res36/:id mod15.ts:40:24 index.ts:24:13 1536 +route PUT /m15/res37/:id mod15.ts:41:23 index.ts:24:13 1537 +route DELETE /m15/res38/:id mod15.ts:42:26 index.ts:24:13 1538 +route PATCH /m15/res39/:id mod15.ts:43:25 index.ts:24:13 1539 +route GET /m15/res40/:id mod15.ts:44:23 index.ts:24:13 1540 +route POST /m15/res41/:id mod15.ts:45:24 index.ts:24:13 1541 +route PUT /m15/res42/:id mod15.ts:46:23 index.ts:24:13 1542 +route DELETE /m15/res43/:id mod15.ts:47:26 index.ts:24:13 1543 +route PATCH /m15/res44/:id mod15.ts:48:25 index.ts:24:13 1544 +route GET /m15/res45/:id mod15.ts:49:23 index.ts:24:13 1545 +route POST /m15/res46/:id mod15.ts:50:24 index.ts:24:13 1546 +route PUT /m15/res47/:id mod15.ts:51:23 index.ts:24:13 1547 +route DELETE /m15/res48/:id mod15.ts:52:26 index.ts:24:13 1548 +route PATCH /m15/res49/:id mod15.ts:53:25 index.ts:24:13 1549 +route GET /m15/res50/:id mod15.ts:54:23 index.ts:24:13 1550 +route POST /m15/res51/:id mod15.ts:55:24 index.ts:24:13 1551 +route PUT /m15/res52/:id mod15.ts:56:23 index.ts:24:13 1552 +route DELETE /m15/res53/:id mod15.ts:57:26 index.ts:24:13 1553 +route PATCH /m15/res54/:id mod15.ts:58:25 index.ts:24:13 1554 +route GET /m15/res55/:id mod15.ts:59:23 index.ts:24:13 1555 +route POST /m15/res56/:id mod15.ts:60:24 index.ts:24:13 1556 +route PUT /m15/res57/:id mod15.ts:61:23 index.ts:24:13 1557 +route DELETE /m15/res58/:id mod15.ts:62:26 index.ts:24:13 1558 +route PATCH /m15/res59/:id mod15.ts:63:25 index.ts:24:13 1559 +route GET /m15/res60/:id mod15.ts:64:23 index.ts:24:13 1560 +route POST /m15/res61/:id mod15.ts:65:24 index.ts:24:13 1561 +route PUT /m15/res62/:id mod15.ts:66:23 index.ts:24:13 1562 +route DELETE /m15/res63/:id mod15.ts:67:26 index.ts:24:13 1563 +route PATCH /m15/res64/:id mod15.ts:68:25 index.ts:24:13 1564 +route GET /m15/res65/:id mod15.ts:69:23 index.ts:24:13 1565 +route POST /m15/res66/:id mod15.ts:70:24 index.ts:24:13 1566 +route PUT /m15/res67/:id mod15.ts:71:23 index.ts:24:13 1567 +route DELETE /m15/res68/:id mod15.ts:72:26 index.ts:24:13 1568 +route PATCH /m15/res69/:id mod15.ts:73:25 index.ts:24:13 1569 +route GET /m15/res70/:id mod15.ts:74:23 index.ts:24:13 1570 +route POST /m15/res71/:id mod15.ts:75:24 index.ts:24:13 1571 +route PUT /m15/res72/:id mod15.ts:76:23 index.ts:24:13 1572 +route DELETE /m15/res73/:id mod15.ts:77:26 index.ts:24:13 1573 +route PATCH /m15/res74/:id mod15.ts:78:25 index.ts:24:13 1574 +route GET /m15/res75/:id mod15.ts:79:23 index.ts:24:13 1575 +route POST /m15/res76/:id mod15.ts:80:24 index.ts:24:13 1576 +route PUT /m15/res77/:id mod15.ts:81:23 index.ts:24:13 1577 +route DELETE /m15/res78/:id mod15.ts:82:26 index.ts:24:13 1578 +route PATCH /m15/res79/:id mod15.ts:83:25 index.ts:24:13 1579 +route GET /m15/res80/:id mod15.ts:84:23 index.ts:24:13 1580 +route POST /m15/res81/:id mod15.ts:85:24 index.ts:24:13 1581 +route PUT /m15/res82/:id mod15.ts:86:23 index.ts:24:13 1582 +route DELETE /m15/res83/:id mod15.ts:87:26 index.ts:24:13 1583 +route PATCH /m15/res84/:id mod15.ts:88:25 index.ts:24:13 1584 +route GET /m15/res85/:id mod15.ts:89:23 index.ts:24:13 1585 +route POST /m15/res86/:id mod15.ts:90:24 index.ts:24:13 1586 +route PUT /m15/res87/:id mod15.ts:91:23 index.ts:24:13 1587 +route DELETE /m15/res88/:id mod15.ts:92:26 index.ts:24:13 1588 +route PATCH /m15/res89/:id mod15.ts:93:25 index.ts:24:13 1589 +route GET /m15/res90/:id mod15.ts:94:23 index.ts:24:13 1590 +route POST /m15/res91/:id mod15.ts:95:24 index.ts:24:13 1591 +route PUT /m15/res92/:id mod15.ts:96:23 index.ts:24:13 1592 +route DELETE /m15/res93/:id mod15.ts:97:26 index.ts:24:13 1593 +route PATCH /m15/res94/:id mod15.ts:98:25 index.ts:24:13 1594 +route GET /m15/res95/:id mod15.ts:99:23 index.ts:24:13 1595 +route POST /m15/res96/:id mod15.ts:100:24 index.ts:24:13 1596 +route PUT /m15/res97/:id mod15.ts:101:23 index.ts:24:13 1597 +route DELETE /m15/res98/:id mod15.ts:102:26 index.ts:24:13 1598 +route PATCH /m15/res99/:id mod15.ts:103:25 index.ts:24:13 1599 +route GET /m16/res0/:id mod16.ts:4:22 index.ts:24:13 1600 +route POST /m16/res1/:id mod16.ts:5:23 index.ts:24:13 1601 +route PUT /m16/res2/:id mod16.ts:6:22 index.ts:24:13 1602 +route DELETE /m16/res3/:id mod16.ts:7:25 index.ts:24:13 1603 +route PATCH /m16/res4/:id mod16.ts:8:24 index.ts:24:13 1604 +route GET /m16/res5/:id mod16.ts:9:22 index.ts:24:13 1605 +route POST /m16/res6/:id mod16.ts:10:23 index.ts:24:13 1606 +route PUT /m16/res7/:id mod16.ts:11:22 index.ts:24:13 1607 +route DELETE /m16/res8/:id mod16.ts:12:25 index.ts:24:13 1608 +route PATCH /m16/res9/:id mod16.ts:13:24 index.ts:24:13 1609 +route GET /m16/res10/:id mod16.ts:14:23 index.ts:24:13 1610 +route POST /m16/res11/:id mod16.ts:15:24 index.ts:24:13 1611 +route PUT /m16/res12/:id mod16.ts:16:23 index.ts:24:13 1612 +route DELETE /m16/res13/:id mod16.ts:17:26 index.ts:24:13 1613 +route PATCH /m16/res14/:id mod16.ts:18:25 index.ts:24:13 1614 +route GET /m16/res15/:id mod16.ts:19:23 index.ts:24:13 1615 +route POST /m16/res16/:id mod16.ts:20:24 index.ts:24:13 1616 +route PUT /m16/res17/:id mod16.ts:21:23 index.ts:24:13 1617 +route DELETE /m16/res18/:id mod16.ts:22:26 index.ts:24:13 1618 +route PATCH /m16/res19/:id mod16.ts:23:25 index.ts:24:13 1619 +route GET /m16/res20/:id mod16.ts:24:23 index.ts:24:13 1620 +route POST /m16/res21/:id mod16.ts:25:24 index.ts:24:13 1621 +route PUT /m16/res22/:id mod16.ts:26:23 index.ts:24:13 1622 +route DELETE /m16/res23/:id mod16.ts:27:26 index.ts:24:13 1623 +route PATCH /m16/res24/:id mod16.ts:28:25 index.ts:24:13 1624 +route GET /m16/res25/:id mod16.ts:29:23 index.ts:24:13 1625 +route POST /m16/res26/:id mod16.ts:30:24 index.ts:24:13 1626 +route PUT /m16/res27/:id mod16.ts:31:23 index.ts:24:13 1627 +route DELETE /m16/res28/:id mod16.ts:32:26 index.ts:24:13 1628 +route PATCH /m16/res29/:id mod16.ts:33:25 index.ts:24:13 1629 +route GET /m16/res30/:id mod16.ts:34:23 index.ts:24:13 1630 +route POST /m16/res31/:id mod16.ts:35:24 index.ts:24:13 1631 +route PUT /m16/res32/:id mod16.ts:36:23 index.ts:24:13 1632 +route DELETE /m16/res33/:id mod16.ts:37:26 index.ts:24:13 1633 +route PATCH /m16/res34/:id mod16.ts:38:25 index.ts:24:13 1634 +route GET /m16/res35/:id mod16.ts:39:23 index.ts:24:13 1635 +route POST /m16/res36/:id mod16.ts:40:24 index.ts:24:13 1636 +route PUT /m16/res37/:id mod16.ts:41:23 index.ts:24:13 1637 +route DELETE /m16/res38/:id mod16.ts:42:26 index.ts:24:13 1638 +route PATCH /m16/res39/:id mod16.ts:43:25 index.ts:24:13 1639 +route GET /m16/res40/:id mod16.ts:44:23 index.ts:24:13 1640 +route POST /m16/res41/:id mod16.ts:45:24 index.ts:24:13 1641 +route PUT /m16/res42/:id mod16.ts:46:23 index.ts:24:13 1642 +route DELETE /m16/res43/:id mod16.ts:47:26 index.ts:24:13 1643 +route PATCH /m16/res44/:id mod16.ts:48:25 index.ts:24:13 1644 +route GET /m16/res45/:id mod16.ts:49:23 index.ts:24:13 1645 +route POST /m16/res46/:id mod16.ts:50:24 index.ts:24:13 1646 +route PUT /m16/res47/:id mod16.ts:51:23 index.ts:24:13 1647 +route DELETE /m16/res48/:id mod16.ts:52:26 index.ts:24:13 1648 +route PATCH /m16/res49/:id mod16.ts:53:25 index.ts:24:13 1649 +route GET /m16/res50/:id mod16.ts:54:23 index.ts:24:13 1650 +route POST /m16/res51/:id mod16.ts:55:24 index.ts:24:13 1651 +route PUT /m16/res52/:id mod16.ts:56:23 index.ts:24:13 1652 +route DELETE /m16/res53/:id mod16.ts:57:26 index.ts:24:13 1653 +route PATCH /m16/res54/:id mod16.ts:58:25 index.ts:24:13 1654 +route GET /m16/res55/:id mod16.ts:59:23 index.ts:24:13 1655 +route POST /m16/res56/:id mod16.ts:60:24 index.ts:24:13 1656 +route PUT /m16/res57/:id mod16.ts:61:23 index.ts:24:13 1657 +route DELETE /m16/res58/:id mod16.ts:62:26 index.ts:24:13 1658 +route PATCH /m16/res59/:id mod16.ts:63:25 index.ts:24:13 1659 +route GET /m16/res60/:id mod16.ts:64:23 index.ts:24:13 1660 +route POST /m16/res61/:id mod16.ts:65:24 index.ts:24:13 1661 +route PUT /m16/res62/:id mod16.ts:66:23 index.ts:24:13 1662 +route DELETE /m16/res63/:id mod16.ts:67:26 index.ts:24:13 1663 +route PATCH /m16/res64/:id mod16.ts:68:25 index.ts:24:13 1664 +route GET /m16/res65/:id mod16.ts:69:23 index.ts:24:13 1665 +route POST /m16/res66/:id mod16.ts:70:24 index.ts:24:13 1666 +route PUT /m16/res67/:id mod16.ts:71:23 index.ts:24:13 1667 +route DELETE /m16/res68/:id mod16.ts:72:26 index.ts:24:13 1668 +route PATCH /m16/res69/:id mod16.ts:73:25 index.ts:24:13 1669 +route GET /m16/res70/:id mod16.ts:74:23 index.ts:24:13 1670 +route POST /m16/res71/:id mod16.ts:75:24 index.ts:24:13 1671 +route PUT /m16/res72/:id mod16.ts:76:23 index.ts:24:13 1672 +route DELETE /m16/res73/:id mod16.ts:77:26 index.ts:24:13 1673 +route PATCH /m16/res74/:id mod16.ts:78:25 index.ts:24:13 1674 +route GET /m16/res75/:id mod16.ts:79:23 index.ts:24:13 1675 +route POST /m16/res76/:id mod16.ts:80:24 index.ts:24:13 1676 +route PUT /m16/res77/:id mod16.ts:81:23 index.ts:24:13 1677 +route DELETE /m16/res78/:id mod16.ts:82:26 index.ts:24:13 1678 +route PATCH /m16/res79/:id mod16.ts:83:25 index.ts:24:13 1679 +route GET /m16/res80/:id mod16.ts:84:23 index.ts:24:13 1680 +route POST /m16/res81/:id mod16.ts:85:24 index.ts:24:13 1681 +route PUT /m16/res82/:id mod16.ts:86:23 index.ts:24:13 1682 +route DELETE /m16/res83/:id mod16.ts:87:26 index.ts:24:13 1683 +route PATCH /m16/res84/:id mod16.ts:88:25 index.ts:24:13 1684 +route GET /m16/res85/:id mod16.ts:89:23 index.ts:24:13 1685 +route POST /m16/res86/:id mod16.ts:90:24 index.ts:24:13 1686 +route PUT /m16/res87/:id mod16.ts:91:23 index.ts:24:13 1687 +route DELETE /m16/res88/:id mod16.ts:92:26 index.ts:24:13 1688 +route PATCH /m16/res89/:id mod16.ts:93:25 index.ts:24:13 1689 +route GET /m16/res90/:id mod16.ts:94:23 index.ts:24:13 1690 +route POST /m16/res91/:id mod16.ts:95:24 index.ts:24:13 1691 +route PUT /m16/res92/:id mod16.ts:96:23 index.ts:24:13 1692 +route DELETE /m16/res93/:id mod16.ts:97:26 index.ts:24:13 1693 +route PATCH /m16/res94/:id mod16.ts:98:25 index.ts:24:13 1694 +route GET /m16/res95/:id mod16.ts:99:23 index.ts:24:13 1695 +route POST /m16/res96/:id mod16.ts:100:24 index.ts:24:13 1696 +route PUT /m16/res97/:id mod16.ts:101:23 index.ts:24:13 1697 +route DELETE /m16/res98/:id mod16.ts:102:26 index.ts:24:13 1698 +route PATCH /m16/res99/:id mod16.ts:103:25 index.ts:24:13 1699 +route GET /m17/res0/:id mod17.ts:4:22 index.ts:24:13 1700 +route POST /m17/res1/:id mod17.ts:5:23 index.ts:24:13 1701 +route PUT /m17/res2/:id mod17.ts:6:22 index.ts:24:13 1702 +route DELETE /m17/res3/:id mod17.ts:7:25 index.ts:24:13 1703 +route PATCH /m17/res4/:id mod17.ts:8:24 index.ts:24:13 1704 +route GET /m17/res5/:id mod17.ts:9:22 index.ts:24:13 1705 +route POST /m17/res6/:id mod17.ts:10:23 index.ts:24:13 1706 +route PUT /m17/res7/:id mod17.ts:11:22 index.ts:24:13 1707 +route DELETE /m17/res8/:id mod17.ts:12:25 index.ts:24:13 1708 +route PATCH /m17/res9/:id mod17.ts:13:24 index.ts:24:13 1709 +route GET /m17/res10/:id mod17.ts:14:23 index.ts:24:13 1710 +route POST /m17/res11/:id mod17.ts:15:24 index.ts:24:13 1711 +route PUT /m17/res12/:id mod17.ts:16:23 index.ts:24:13 1712 +route DELETE /m17/res13/:id mod17.ts:17:26 index.ts:24:13 1713 +route PATCH /m17/res14/:id mod17.ts:18:25 index.ts:24:13 1714 +route GET /m17/res15/:id mod17.ts:19:23 index.ts:24:13 1715 +route POST /m17/res16/:id mod17.ts:20:24 index.ts:24:13 1716 +route PUT /m17/res17/:id mod17.ts:21:23 index.ts:24:13 1717 +route DELETE /m17/res18/:id mod17.ts:22:26 index.ts:24:13 1718 +route PATCH /m17/res19/:id mod17.ts:23:25 index.ts:24:13 1719 +route GET /m17/res20/:id mod17.ts:24:23 index.ts:24:13 1720 +route POST /m17/res21/:id mod17.ts:25:24 index.ts:24:13 1721 +route PUT /m17/res22/:id mod17.ts:26:23 index.ts:24:13 1722 +route DELETE /m17/res23/:id mod17.ts:27:26 index.ts:24:13 1723 +route PATCH /m17/res24/:id mod17.ts:28:25 index.ts:24:13 1724 +route GET /m17/res25/:id mod17.ts:29:23 index.ts:24:13 1725 +route POST /m17/res26/:id mod17.ts:30:24 index.ts:24:13 1726 +route PUT /m17/res27/:id mod17.ts:31:23 index.ts:24:13 1727 +route DELETE /m17/res28/:id mod17.ts:32:26 index.ts:24:13 1728 +route PATCH /m17/res29/:id mod17.ts:33:25 index.ts:24:13 1729 +route GET /m17/res30/:id mod17.ts:34:23 index.ts:24:13 1730 +route POST /m17/res31/:id mod17.ts:35:24 index.ts:24:13 1731 +route PUT /m17/res32/:id mod17.ts:36:23 index.ts:24:13 1732 +route DELETE /m17/res33/:id mod17.ts:37:26 index.ts:24:13 1733 +route PATCH /m17/res34/:id mod17.ts:38:25 index.ts:24:13 1734 +route GET /m17/res35/:id mod17.ts:39:23 index.ts:24:13 1735 +route POST /m17/res36/:id mod17.ts:40:24 index.ts:24:13 1736 +route PUT /m17/res37/:id mod17.ts:41:23 index.ts:24:13 1737 +route DELETE /m17/res38/:id mod17.ts:42:26 index.ts:24:13 1738 +route PATCH /m17/res39/:id mod17.ts:43:25 index.ts:24:13 1739 +route GET /m17/res40/:id mod17.ts:44:23 index.ts:24:13 1740 +route POST /m17/res41/:id mod17.ts:45:24 index.ts:24:13 1741 +route PUT /m17/res42/:id mod17.ts:46:23 index.ts:24:13 1742 +route DELETE /m17/res43/:id mod17.ts:47:26 index.ts:24:13 1743 +route PATCH /m17/res44/:id mod17.ts:48:25 index.ts:24:13 1744 +route GET /m17/res45/:id mod17.ts:49:23 index.ts:24:13 1745 +route POST /m17/res46/:id mod17.ts:50:24 index.ts:24:13 1746 +route PUT /m17/res47/:id mod17.ts:51:23 index.ts:24:13 1747 +route DELETE /m17/res48/:id mod17.ts:52:26 index.ts:24:13 1748 +route PATCH /m17/res49/:id mod17.ts:53:25 index.ts:24:13 1749 +route GET /m17/res50/:id mod17.ts:54:23 index.ts:24:13 1750 +route POST /m17/res51/:id mod17.ts:55:24 index.ts:24:13 1751 +route PUT /m17/res52/:id mod17.ts:56:23 index.ts:24:13 1752 +route DELETE /m17/res53/:id mod17.ts:57:26 index.ts:24:13 1753 +route PATCH /m17/res54/:id mod17.ts:58:25 index.ts:24:13 1754 +route GET /m17/res55/:id mod17.ts:59:23 index.ts:24:13 1755 +route POST /m17/res56/:id mod17.ts:60:24 index.ts:24:13 1756 +route PUT /m17/res57/:id mod17.ts:61:23 index.ts:24:13 1757 +route DELETE /m17/res58/:id mod17.ts:62:26 index.ts:24:13 1758 +route PATCH /m17/res59/:id mod17.ts:63:25 index.ts:24:13 1759 +route GET /m17/res60/:id mod17.ts:64:23 index.ts:24:13 1760 +route POST /m17/res61/:id mod17.ts:65:24 index.ts:24:13 1761 +route PUT /m17/res62/:id mod17.ts:66:23 index.ts:24:13 1762 +route DELETE /m17/res63/:id mod17.ts:67:26 index.ts:24:13 1763 +route PATCH /m17/res64/:id mod17.ts:68:25 index.ts:24:13 1764 +route GET /m17/res65/:id mod17.ts:69:23 index.ts:24:13 1765 +route POST /m17/res66/:id mod17.ts:70:24 index.ts:24:13 1766 +route PUT /m17/res67/:id mod17.ts:71:23 index.ts:24:13 1767 +route DELETE /m17/res68/:id mod17.ts:72:26 index.ts:24:13 1768 +route PATCH /m17/res69/:id mod17.ts:73:25 index.ts:24:13 1769 +route GET /m17/res70/:id mod17.ts:74:23 index.ts:24:13 1770 +route POST /m17/res71/:id mod17.ts:75:24 index.ts:24:13 1771 +route PUT /m17/res72/:id mod17.ts:76:23 index.ts:24:13 1772 +route DELETE /m17/res73/:id mod17.ts:77:26 index.ts:24:13 1773 +route PATCH /m17/res74/:id mod17.ts:78:25 index.ts:24:13 1774 +route GET /m17/res75/:id mod17.ts:79:23 index.ts:24:13 1775 +route POST /m17/res76/:id mod17.ts:80:24 index.ts:24:13 1776 +route PUT /m17/res77/:id mod17.ts:81:23 index.ts:24:13 1777 +route DELETE /m17/res78/:id mod17.ts:82:26 index.ts:24:13 1778 +route PATCH /m17/res79/:id mod17.ts:83:25 index.ts:24:13 1779 +route GET /m17/res80/:id mod17.ts:84:23 index.ts:24:13 1780 +route POST /m17/res81/:id mod17.ts:85:24 index.ts:24:13 1781 +route PUT /m17/res82/:id mod17.ts:86:23 index.ts:24:13 1782 +route DELETE /m17/res83/:id mod17.ts:87:26 index.ts:24:13 1783 +route PATCH /m17/res84/:id mod17.ts:88:25 index.ts:24:13 1784 +route GET /m17/res85/:id mod17.ts:89:23 index.ts:24:13 1785 +route POST /m17/res86/:id mod17.ts:90:24 index.ts:24:13 1786 +route PUT /m17/res87/:id mod17.ts:91:23 index.ts:24:13 1787 +route DELETE /m17/res88/:id mod17.ts:92:26 index.ts:24:13 1788 +route PATCH /m17/res89/:id mod17.ts:93:25 index.ts:24:13 1789 +route GET /m17/res90/:id mod17.ts:94:23 index.ts:24:13 1790 +route POST /m17/res91/:id mod17.ts:95:24 index.ts:24:13 1791 +route PUT /m17/res92/:id mod17.ts:96:23 index.ts:24:13 1792 +route DELETE /m17/res93/:id mod17.ts:97:26 index.ts:24:13 1793 +route PATCH /m17/res94/:id mod17.ts:98:25 index.ts:24:13 1794 +route GET /m17/res95/:id mod17.ts:99:23 index.ts:24:13 1795 +route POST /m17/res96/:id mod17.ts:100:24 index.ts:24:13 1796 +route PUT /m17/res97/:id mod17.ts:101:23 index.ts:24:13 1797 +route DELETE /m17/res98/:id mod17.ts:102:26 index.ts:24:13 1798 +route PATCH /m17/res99/:id mod17.ts:103:25 index.ts:24:13 1799 +route GET /m18/res0/:id mod18.ts:4:22 index.ts:24:13 1800 +route POST /m18/res1/:id mod18.ts:5:23 index.ts:24:13 1801 +route PUT /m18/res2/:id mod18.ts:6:22 index.ts:24:13 1802 +route DELETE /m18/res3/:id mod18.ts:7:25 index.ts:24:13 1803 +route PATCH /m18/res4/:id mod18.ts:8:24 index.ts:24:13 1804 +route GET /m18/res5/:id mod18.ts:9:22 index.ts:24:13 1805 +route POST /m18/res6/:id mod18.ts:10:23 index.ts:24:13 1806 +route PUT /m18/res7/:id mod18.ts:11:22 index.ts:24:13 1807 +route DELETE /m18/res8/:id mod18.ts:12:25 index.ts:24:13 1808 +route PATCH /m18/res9/:id mod18.ts:13:24 index.ts:24:13 1809 +route GET /m18/res10/:id mod18.ts:14:23 index.ts:24:13 1810 +route POST /m18/res11/:id mod18.ts:15:24 index.ts:24:13 1811 +route PUT /m18/res12/:id mod18.ts:16:23 index.ts:24:13 1812 +route DELETE /m18/res13/:id mod18.ts:17:26 index.ts:24:13 1813 +route PATCH /m18/res14/:id mod18.ts:18:25 index.ts:24:13 1814 +route GET /m18/res15/:id mod18.ts:19:23 index.ts:24:13 1815 +route POST /m18/res16/:id mod18.ts:20:24 index.ts:24:13 1816 +route PUT /m18/res17/:id mod18.ts:21:23 index.ts:24:13 1817 +route DELETE /m18/res18/:id mod18.ts:22:26 index.ts:24:13 1818 +route PATCH /m18/res19/:id mod18.ts:23:25 index.ts:24:13 1819 +route GET /m18/res20/:id mod18.ts:24:23 index.ts:24:13 1820 +route POST /m18/res21/:id mod18.ts:25:24 index.ts:24:13 1821 +route PUT /m18/res22/:id mod18.ts:26:23 index.ts:24:13 1822 +route DELETE /m18/res23/:id mod18.ts:27:26 index.ts:24:13 1823 +route PATCH /m18/res24/:id mod18.ts:28:25 index.ts:24:13 1824 +route GET /m18/res25/:id mod18.ts:29:23 index.ts:24:13 1825 +route POST /m18/res26/:id mod18.ts:30:24 index.ts:24:13 1826 +route PUT /m18/res27/:id mod18.ts:31:23 index.ts:24:13 1827 +route DELETE /m18/res28/:id mod18.ts:32:26 index.ts:24:13 1828 +route PATCH /m18/res29/:id mod18.ts:33:25 index.ts:24:13 1829 +route GET /m18/res30/:id mod18.ts:34:23 index.ts:24:13 1830 +route POST /m18/res31/:id mod18.ts:35:24 index.ts:24:13 1831 +route PUT /m18/res32/:id mod18.ts:36:23 index.ts:24:13 1832 +route DELETE /m18/res33/:id mod18.ts:37:26 index.ts:24:13 1833 +route PATCH /m18/res34/:id mod18.ts:38:25 index.ts:24:13 1834 +route GET /m18/res35/:id mod18.ts:39:23 index.ts:24:13 1835 +route POST /m18/res36/:id mod18.ts:40:24 index.ts:24:13 1836 +route PUT /m18/res37/:id mod18.ts:41:23 index.ts:24:13 1837 +route DELETE /m18/res38/:id mod18.ts:42:26 index.ts:24:13 1838 +route PATCH /m18/res39/:id mod18.ts:43:25 index.ts:24:13 1839 +route GET /m18/res40/:id mod18.ts:44:23 index.ts:24:13 1840 +route POST /m18/res41/:id mod18.ts:45:24 index.ts:24:13 1841 +route PUT /m18/res42/:id mod18.ts:46:23 index.ts:24:13 1842 +route DELETE /m18/res43/:id mod18.ts:47:26 index.ts:24:13 1843 +route PATCH /m18/res44/:id mod18.ts:48:25 index.ts:24:13 1844 +route GET /m18/res45/:id mod18.ts:49:23 index.ts:24:13 1845 +route POST /m18/res46/:id mod18.ts:50:24 index.ts:24:13 1846 +route PUT /m18/res47/:id mod18.ts:51:23 index.ts:24:13 1847 +route DELETE /m18/res48/:id mod18.ts:52:26 index.ts:24:13 1848 +route PATCH /m18/res49/:id mod18.ts:53:25 index.ts:24:13 1849 +route GET /m18/res50/:id mod18.ts:54:23 index.ts:24:13 1850 +route POST /m18/res51/:id mod18.ts:55:24 index.ts:24:13 1851 +route PUT /m18/res52/:id mod18.ts:56:23 index.ts:24:13 1852 +route DELETE /m18/res53/:id mod18.ts:57:26 index.ts:24:13 1853 +route PATCH /m18/res54/:id mod18.ts:58:25 index.ts:24:13 1854 +route GET /m18/res55/:id mod18.ts:59:23 index.ts:24:13 1855 +route POST /m18/res56/:id mod18.ts:60:24 index.ts:24:13 1856 +route PUT /m18/res57/:id mod18.ts:61:23 index.ts:24:13 1857 +route DELETE /m18/res58/:id mod18.ts:62:26 index.ts:24:13 1858 +route PATCH /m18/res59/:id mod18.ts:63:25 index.ts:24:13 1859 +route GET /m18/res60/:id mod18.ts:64:23 index.ts:24:13 1860 +route POST /m18/res61/:id mod18.ts:65:24 index.ts:24:13 1861 +route PUT /m18/res62/:id mod18.ts:66:23 index.ts:24:13 1862 +route DELETE /m18/res63/:id mod18.ts:67:26 index.ts:24:13 1863 +route PATCH /m18/res64/:id mod18.ts:68:25 index.ts:24:13 1864 +route GET /m18/res65/:id mod18.ts:69:23 index.ts:24:13 1865 +route POST /m18/res66/:id mod18.ts:70:24 index.ts:24:13 1866 +route PUT /m18/res67/:id mod18.ts:71:23 index.ts:24:13 1867 +route DELETE /m18/res68/:id mod18.ts:72:26 index.ts:24:13 1868 +route PATCH /m18/res69/:id mod18.ts:73:25 index.ts:24:13 1869 +route GET /m18/res70/:id mod18.ts:74:23 index.ts:24:13 1870 +route POST /m18/res71/:id mod18.ts:75:24 index.ts:24:13 1871 +route PUT /m18/res72/:id mod18.ts:76:23 index.ts:24:13 1872 +route DELETE /m18/res73/:id mod18.ts:77:26 index.ts:24:13 1873 +route PATCH /m18/res74/:id mod18.ts:78:25 index.ts:24:13 1874 +route GET /m18/res75/:id mod18.ts:79:23 index.ts:24:13 1875 +route POST /m18/res76/:id mod18.ts:80:24 index.ts:24:13 1876 +route PUT /m18/res77/:id mod18.ts:81:23 index.ts:24:13 1877 +route DELETE /m18/res78/:id mod18.ts:82:26 index.ts:24:13 1878 +route PATCH /m18/res79/:id mod18.ts:83:25 index.ts:24:13 1879 +route GET /m18/res80/:id mod18.ts:84:23 index.ts:24:13 1880 +route POST /m18/res81/:id mod18.ts:85:24 index.ts:24:13 1881 +route PUT /m18/res82/:id mod18.ts:86:23 index.ts:24:13 1882 +route DELETE /m18/res83/:id mod18.ts:87:26 index.ts:24:13 1883 +route PATCH /m18/res84/:id mod18.ts:88:25 index.ts:24:13 1884 +route GET /m18/res85/:id mod18.ts:89:23 index.ts:24:13 1885 +route POST /m18/res86/:id mod18.ts:90:24 index.ts:24:13 1886 +route PUT /m18/res87/:id mod18.ts:91:23 index.ts:24:13 1887 +route DELETE /m18/res88/:id mod18.ts:92:26 index.ts:24:13 1888 +route PATCH /m18/res89/:id mod18.ts:93:25 index.ts:24:13 1889 +route GET /m18/res90/:id mod18.ts:94:23 index.ts:24:13 1890 +route POST /m18/res91/:id mod18.ts:95:24 index.ts:24:13 1891 +route PUT /m18/res92/:id mod18.ts:96:23 index.ts:24:13 1892 +route DELETE /m18/res93/:id mod18.ts:97:26 index.ts:24:13 1893 +route PATCH /m18/res94/:id mod18.ts:98:25 index.ts:24:13 1894 +route GET /m18/res95/:id mod18.ts:99:23 index.ts:24:13 1895 +route POST /m18/res96/:id mod18.ts:100:24 index.ts:24:13 1896 +route PUT /m18/res97/:id mod18.ts:101:23 index.ts:24:13 1897 +route DELETE /m18/res98/:id mod18.ts:102:26 index.ts:24:13 1898 +route PATCH /m18/res99/:id mod18.ts:103:25 index.ts:24:13 1899 +route GET /m19/res0/:id mod19.ts:4:22 index.ts:24:13 1900 +route POST /m19/res1/:id mod19.ts:5:23 index.ts:24:13 1901 +route PUT /m19/res2/:id mod19.ts:6:22 index.ts:24:13 1902 +route DELETE /m19/res3/:id mod19.ts:7:25 index.ts:24:13 1903 +route PATCH /m19/res4/:id mod19.ts:8:24 index.ts:24:13 1904 +route GET /m19/res5/:id mod19.ts:9:22 index.ts:24:13 1905 +route POST /m19/res6/:id mod19.ts:10:23 index.ts:24:13 1906 +route PUT /m19/res7/:id mod19.ts:11:22 index.ts:24:13 1907 +route DELETE /m19/res8/:id mod19.ts:12:25 index.ts:24:13 1908 +route PATCH /m19/res9/:id mod19.ts:13:24 index.ts:24:13 1909 +route GET /m19/res10/:id mod19.ts:14:23 index.ts:24:13 1910 +route POST /m19/res11/:id mod19.ts:15:24 index.ts:24:13 1911 +route PUT /m19/res12/:id mod19.ts:16:23 index.ts:24:13 1912 +route DELETE /m19/res13/:id mod19.ts:17:26 index.ts:24:13 1913 +route PATCH /m19/res14/:id mod19.ts:18:25 index.ts:24:13 1914 +route GET /m19/res15/:id mod19.ts:19:23 index.ts:24:13 1915 +route POST /m19/res16/:id mod19.ts:20:24 index.ts:24:13 1916 +route PUT /m19/res17/:id mod19.ts:21:23 index.ts:24:13 1917 +route DELETE /m19/res18/:id mod19.ts:22:26 index.ts:24:13 1918 +route PATCH /m19/res19/:id mod19.ts:23:25 index.ts:24:13 1919 +route GET /m19/res20/:id mod19.ts:24:23 index.ts:24:13 1920 +route POST /m19/res21/:id mod19.ts:25:24 index.ts:24:13 1921 +route PUT /m19/res22/:id mod19.ts:26:23 index.ts:24:13 1922 +route DELETE /m19/res23/:id mod19.ts:27:26 index.ts:24:13 1923 +route PATCH /m19/res24/:id mod19.ts:28:25 index.ts:24:13 1924 +route GET /m19/res25/:id mod19.ts:29:23 index.ts:24:13 1925 +route POST /m19/res26/:id mod19.ts:30:24 index.ts:24:13 1926 +route PUT /m19/res27/:id mod19.ts:31:23 index.ts:24:13 1927 +route DELETE /m19/res28/:id mod19.ts:32:26 index.ts:24:13 1928 +route PATCH /m19/res29/:id mod19.ts:33:25 index.ts:24:13 1929 +route GET /m19/res30/:id mod19.ts:34:23 index.ts:24:13 1930 +route POST /m19/res31/:id mod19.ts:35:24 index.ts:24:13 1931 +route PUT /m19/res32/:id mod19.ts:36:23 index.ts:24:13 1932 +route DELETE /m19/res33/:id mod19.ts:37:26 index.ts:24:13 1933 +route PATCH /m19/res34/:id mod19.ts:38:25 index.ts:24:13 1934 +route GET /m19/res35/:id mod19.ts:39:23 index.ts:24:13 1935 +route POST /m19/res36/:id mod19.ts:40:24 index.ts:24:13 1936 +route PUT /m19/res37/:id mod19.ts:41:23 index.ts:24:13 1937 +route DELETE /m19/res38/:id mod19.ts:42:26 index.ts:24:13 1938 +route PATCH /m19/res39/:id mod19.ts:43:25 index.ts:24:13 1939 +route GET /m19/res40/:id mod19.ts:44:23 index.ts:24:13 1940 +route POST /m19/res41/:id mod19.ts:45:24 index.ts:24:13 1941 +route PUT /m19/res42/:id mod19.ts:46:23 index.ts:24:13 1942 +route DELETE /m19/res43/:id mod19.ts:47:26 index.ts:24:13 1943 +route PATCH /m19/res44/:id mod19.ts:48:25 index.ts:24:13 1944 +route GET /m19/res45/:id mod19.ts:49:23 index.ts:24:13 1945 +route POST /m19/res46/:id mod19.ts:50:24 index.ts:24:13 1946 +route PUT /m19/res47/:id mod19.ts:51:23 index.ts:24:13 1947 +route DELETE /m19/res48/:id mod19.ts:52:26 index.ts:24:13 1948 +route PATCH /m19/res49/:id mod19.ts:53:25 index.ts:24:13 1949 +route GET /m19/res50/:id mod19.ts:54:23 index.ts:24:13 1950 +route POST /m19/res51/:id mod19.ts:55:24 index.ts:24:13 1951 +route PUT /m19/res52/:id mod19.ts:56:23 index.ts:24:13 1952 +route DELETE /m19/res53/:id mod19.ts:57:26 index.ts:24:13 1953 +route PATCH /m19/res54/:id mod19.ts:58:25 index.ts:24:13 1954 +route GET /m19/res55/:id mod19.ts:59:23 index.ts:24:13 1955 +route POST /m19/res56/:id mod19.ts:60:24 index.ts:24:13 1956 +route PUT /m19/res57/:id mod19.ts:61:23 index.ts:24:13 1957 +route DELETE /m19/res58/:id mod19.ts:62:26 index.ts:24:13 1958 +route PATCH /m19/res59/:id mod19.ts:63:25 index.ts:24:13 1959 +route GET /m19/res60/:id mod19.ts:64:23 index.ts:24:13 1960 +route POST /m19/res61/:id mod19.ts:65:24 index.ts:24:13 1961 +route PUT /m19/res62/:id mod19.ts:66:23 index.ts:24:13 1962 +route DELETE /m19/res63/:id mod19.ts:67:26 index.ts:24:13 1963 +route PATCH /m19/res64/:id mod19.ts:68:25 index.ts:24:13 1964 +route GET /m19/res65/:id mod19.ts:69:23 index.ts:24:13 1965 +route POST /m19/res66/:id mod19.ts:70:24 index.ts:24:13 1966 +route PUT /m19/res67/:id mod19.ts:71:23 index.ts:24:13 1967 +route DELETE /m19/res68/:id mod19.ts:72:26 index.ts:24:13 1968 +route PATCH /m19/res69/:id mod19.ts:73:25 index.ts:24:13 1969 +route GET /m19/res70/:id mod19.ts:74:23 index.ts:24:13 1970 +route POST /m19/res71/:id mod19.ts:75:24 index.ts:24:13 1971 +route PUT /m19/res72/:id mod19.ts:76:23 index.ts:24:13 1972 +route DELETE /m19/res73/:id mod19.ts:77:26 index.ts:24:13 1973 +route PATCH /m19/res74/:id mod19.ts:78:25 index.ts:24:13 1974 +route GET /m19/res75/:id mod19.ts:79:23 index.ts:24:13 1975 +route POST /m19/res76/:id mod19.ts:80:24 index.ts:24:13 1976 +route PUT /m19/res77/:id mod19.ts:81:23 index.ts:24:13 1977 +route DELETE /m19/res78/:id mod19.ts:82:26 index.ts:24:13 1978 +route PATCH /m19/res79/:id mod19.ts:83:25 index.ts:24:13 1979 +route GET /m19/res80/:id mod19.ts:84:23 index.ts:24:13 1980 +route POST /m19/res81/:id mod19.ts:85:24 index.ts:24:13 1981 +route PUT /m19/res82/:id mod19.ts:86:23 index.ts:24:13 1982 +route DELETE /m19/res83/:id mod19.ts:87:26 index.ts:24:13 1983 +route PATCH /m19/res84/:id mod19.ts:88:25 index.ts:24:13 1984 +route GET /m19/res85/:id mod19.ts:89:23 index.ts:24:13 1985 +route POST /m19/res86/:id mod19.ts:90:24 index.ts:24:13 1986 +route PUT /m19/res87/:id mod19.ts:91:23 index.ts:24:13 1987 +route DELETE /m19/res88/:id mod19.ts:92:26 index.ts:24:13 1988 +route PATCH /m19/res89/:id mod19.ts:93:25 index.ts:24:13 1989 +route GET /m19/res90/:id mod19.ts:94:23 index.ts:24:13 1990 +route POST /m19/res91/:id mod19.ts:95:24 index.ts:24:13 1991 +route PUT /m19/res92/:id mod19.ts:96:23 index.ts:24:13 1992 +route DELETE /m19/res93/:id mod19.ts:97:26 index.ts:24:13 1993 +route PATCH /m19/res94/:id mod19.ts:98:25 index.ts:24:13 1994 +route GET /m19/res95/:id mod19.ts:99:23 index.ts:24:13 1995 +route POST /m19/res96/:id mod19.ts:100:24 index.ts:24:13 1996 +route PUT /m19/res97/:id mod19.ts:101:23 index.ts:24:13 1997 +route DELETE /m19/res98/:id mod19.ts:102:26 index.ts:24:13 1998 +route PATCH /m19/res99/:id mod19.ts:103:25 index.ts:24:13 1999 +submount /m0 index.ts:25:5 +submount /m1 index.ts:26:5 +submount /m2 index.ts:27:5 +submount /m3 index.ts:28:5 +submount /m4 index.ts:29:5 +submount /m5 index.ts:30:5 +submount /m6 index.ts:31:5 +submount /m7 index.ts:32:5 +submount /m8 index.ts:33:5 +submount /m9 index.ts:34:5 +submount /m10 index.ts:35:5 +submount /m11 index.ts:36:5 +submount /m12 index.ts:37:5 +submount /m13 index.ts:38:5 +submount /m14 index.ts:39:5 +submount /m15 index.ts:40:5 +submount /m16 index.ts:41:5 +submount /m17 index.ts:42:5 +submount /m18 index.ts:43:5 +submount /m19 index.ts:44:5 +regby mod0.ts:1:1 mod0.ts:100:23 POST /m0/res96/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:101:22 PUT /m0/res97/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:102:25 DELETE /m0/res98/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:103:24 PATCH /m0/res99/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:10:22 POST /m0/res6/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:11:21 PUT /m0/res7/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:12:24 DELETE /m0/res8/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:13:23 PATCH /m0/res9/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:14:22 GET /m0/res10/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:15:23 POST /m0/res11/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:16:22 PUT /m0/res12/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:17:25 DELETE /m0/res13/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:18:24 PATCH /m0/res14/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:19:22 GET /m0/res15/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:20:23 POST /m0/res16/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:21:22 PUT /m0/res17/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:22:25 DELETE /m0/res18/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:23:24 PATCH /m0/res19/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:24:22 GET /m0/res20/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:25:23 POST /m0/res21/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:26:22 PUT /m0/res22/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:27:25 DELETE /m0/res23/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:28:24 PATCH /m0/res24/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:29:22 GET /m0/res25/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:30:23 POST /m0/res26/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:31:22 PUT /m0/res27/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:32:25 DELETE /m0/res28/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:33:24 PATCH /m0/res29/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:34:22 GET /m0/res30/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:35:23 POST /m0/res31/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:36:22 PUT /m0/res32/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:37:25 DELETE /m0/res33/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:38:24 PATCH /m0/res34/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:39:22 GET /m0/res35/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:40:23 POST /m0/res36/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:41:22 PUT /m0/res37/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:42:25 DELETE /m0/res38/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:43:24 PATCH /m0/res39/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:44:22 GET /m0/res40/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:45:23 POST /m0/res41/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:46:22 PUT /m0/res42/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:47:25 DELETE /m0/res43/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:48:24 PATCH /m0/res44/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:49:22 GET /m0/res45/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:4:21 GET /m0/res0/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:50:23 POST /m0/res46/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:51:22 PUT /m0/res47/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:52:25 DELETE /m0/res48/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:53:24 PATCH /m0/res49/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:54:22 GET /m0/res50/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:55:23 POST /m0/res51/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:56:22 PUT /m0/res52/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:57:25 DELETE /m0/res53/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:58:24 PATCH /m0/res54/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:59:22 GET /m0/res55/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:5:22 POST /m0/res1/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:60:23 POST /m0/res56/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:61:22 PUT /m0/res57/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:62:25 DELETE /m0/res58/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:63:24 PATCH /m0/res59/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:64:22 GET /m0/res60/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:65:23 POST /m0/res61/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:66:22 PUT /m0/res62/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:67:25 DELETE /m0/res63/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:68:24 PATCH /m0/res64/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:69:22 GET /m0/res65/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:6:21 PUT /m0/res2/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:70:23 POST /m0/res66/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:71:22 PUT /m0/res67/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:72:25 DELETE /m0/res68/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:73:24 PATCH /m0/res69/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:74:22 GET /m0/res70/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:75:23 POST /m0/res71/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:76:22 PUT /m0/res72/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:77:25 DELETE /m0/res73/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:78:24 PATCH /m0/res74/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:79:22 GET /m0/res75/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:7:24 DELETE /m0/res3/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:80:23 POST /m0/res76/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:81:22 PUT /m0/res77/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:82:25 DELETE /m0/res78/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:83:24 PATCH /m0/res79/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:84:22 GET /m0/res80/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:85:23 POST /m0/res81/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:86:22 PUT /m0/res82/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:87:25 DELETE /m0/res83/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:88:24 PATCH /m0/res84/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:89:22 GET /m0/res85/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:8:23 PATCH /m0/res4/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:90:23 POST /m0/res86/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:91:22 PUT /m0/res87/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:92:25 DELETE /m0/res88/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:93:24 PATCH /m0/res89/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:94:22 GET /m0/res90/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:95:23 POST /m0/res91/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:96:22 PUT /m0/res92/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:97:25 DELETE /m0/res93/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:98:24 PATCH /m0/res94/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:99:22 GET /m0/res95/:id index.ts:24:13 +regby mod0.ts:1:1 mod0.ts:9:21 GET /m0/res5/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:100:23 POST /m1/res96/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:101:22 PUT /m1/res97/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:102:25 DELETE /m1/res98/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:103:24 PATCH /m1/res99/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:10:22 POST /m1/res6/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:11:21 PUT /m1/res7/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:12:24 DELETE /m1/res8/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:13:23 PATCH /m1/res9/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:14:22 GET /m1/res10/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:15:23 POST /m1/res11/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:16:22 PUT /m1/res12/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:17:25 DELETE /m1/res13/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:18:24 PATCH /m1/res14/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:19:22 GET /m1/res15/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:20:23 POST /m1/res16/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:21:22 PUT /m1/res17/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:22:25 DELETE /m1/res18/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:23:24 PATCH /m1/res19/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:24:22 GET /m1/res20/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:25:23 POST /m1/res21/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:26:22 PUT /m1/res22/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:27:25 DELETE /m1/res23/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:28:24 PATCH /m1/res24/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:29:22 GET /m1/res25/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:30:23 POST /m1/res26/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:31:22 PUT /m1/res27/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:32:25 DELETE /m1/res28/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:33:24 PATCH /m1/res29/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:34:22 GET /m1/res30/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:35:23 POST /m1/res31/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:36:22 PUT /m1/res32/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:37:25 DELETE /m1/res33/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:38:24 PATCH /m1/res34/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:39:22 GET /m1/res35/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:40:23 POST /m1/res36/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:41:22 PUT /m1/res37/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:42:25 DELETE /m1/res38/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:43:24 PATCH /m1/res39/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:44:22 GET /m1/res40/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:45:23 POST /m1/res41/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:46:22 PUT /m1/res42/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:47:25 DELETE /m1/res43/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:48:24 PATCH /m1/res44/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:49:22 GET /m1/res45/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:4:21 GET /m1/res0/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:50:23 POST /m1/res46/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:51:22 PUT /m1/res47/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:52:25 DELETE /m1/res48/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:53:24 PATCH /m1/res49/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:54:22 GET /m1/res50/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:55:23 POST /m1/res51/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:56:22 PUT /m1/res52/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:57:25 DELETE /m1/res53/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:58:24 PATCH /m1/res54/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:59:22 GET /m1/res55/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:5:22 POST /m1/res1/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:60:23 POST /m1/res56/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:61:22 PUT /m1/res57/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:62:25 DELETE /m1/res58/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:63:24 PATCH /m1/res59/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:64:22 GET /m1/res60/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:65:23 POST /m1/res61/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:66:22 PUT /m1/res62/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:67:25 DELETE /m1/res63/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:68:24 PATCH /m1/res64/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:69:22 GET /m1/res65/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:6:21 PUT /m1/res2/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:70:23 POST /m1/res66/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:71:22 PUT /m1/res67/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:72:25 DELETE /m1/res68/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:73:24 PATCH /m1/res69/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:74:22 GET /m1/res70/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:75:23 POST /m1/res71/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:76:22 PUT /m1/res72/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:77:25 DELETE /m1/res73/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:78:24 PATCH /m1/res74/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:79:22 GET /m1/res75/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:7:24 DELETE /m1/res3/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:80:23 POST /m1/res76/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:81:22 PUT /m1/res77/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:82:25 DELETE /m1/res78/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:83:24 PATCH /m1/res79/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:84:22 GET /m1/res80/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:85:23 POST /m1/res81/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:86:22 PUT /m1/res82/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:87:25 DELETE /m1/res83/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:88:24 PATCH /m1/res84/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:89:22 GET /m1/res85/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:8:23 PATCH /m1/res4/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:90:23 POST /m1/res86/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:91:22 PUT /m1/res87/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:92:25 DELETE /m1/res88/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:93:24 PATCH /m1/res89/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:94:22 GET /m1/res90/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:95:23 POST /m1/res91/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:96:22 PUT /m1/res92/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:97:25 DELETE /m1/res93/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:98:24 PATCH /m1/res94/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:99:22 GET /m1/res95/:id index.ts:24:13 +regby mod1.ts:1:1 mod1.ts:9:21 GET /m1/res5/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:100:24 POST /m10/res96/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:101:23 PUT /m10/res97/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:102:26 DELETE /m10/res98/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:103:25 PATCH /m10/res99/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:10:23 POST /m10/res6/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:11:22 PUT /m10/res7/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:12:25 DELETE /m10/res8/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:13:24 PATCH /m10/res9/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:14:23 GET /m10/res10/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:15:24 POST /m10/res11/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:16:23 PUT /m10/res12/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:17:26 DELETE /m10/res13/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:18:25 PATCH /m10/res14/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:19:23 GET /m10/res15/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:20:24 POST /m10/res16/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:21:23 PUT /m10/res17/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:22:26 DELETE /m10/res18/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:23:25 PATCH /m10/res19/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:24:23 GET /m10/res20/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:25:24 POST /m10/res21/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:26:23 PUT /m10/res22/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:27:26 DELETE /m10/res23/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:28:25 PATCH /m10/res24/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:29:23 GET /m10/res25/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:30:24 POST /m10/res26/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:31:23 PUT /m10/res27/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:32:26 DELETE /m10/res28/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:33:25 PATCH /m10/res29/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:34:23 GET /m10/res30/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:35:24 POST /m10/res31/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:36:23 PUT /m10/res32/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:37:26 DELETE /m10/res33/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:38:25 PATCH /m10/res34/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:39:23 GET /m10/res35/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:40:24 POST /m10/res36/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:41:23 PUT /m10/res37/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:42:26 DELETE /m10/res38/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:43:25 PATCH /m10/res39/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:44:23 GET /m10/res40/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:45:24 POST /m10/res41/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:46:23 PUT /m10/res42/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:47:26 DELETE /m10/res43/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:48:25 PATCH /m10/res44/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:49:23 GET /m10/res45/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:4:22 GET /m10/res0/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:50:24 POST /m10/res46/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:51:23 PUT /m10/res47/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:52:26 DELETE /m10/res48/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:53:25 PATCH /m10/res49/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:54:23 GET /m10/res50/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:55:24 POST /m10/res51/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:56:23 PUT /m10/res52/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:57:26 DELETE /m10/res53/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:58:25 PATCH /m10/res54/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:59:23 GET /m10/res55/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:5:23 POST /m10/res1/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:60:24 POST /m10/res56/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:61:23 PUT /m10/res57/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:62:26 DELETE /m10/res58/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:63:25 PATCH /m10/res59/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:64:23 GET /m10/res60/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:65:24 POST /m10/res61/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:66:23 PUT /m10/res62/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:67:26 DELETE /m10/res63/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:68:25 PATCH /m10/res64/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:69:23 GET /m10/res65/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:6:22 PUT /m10/res2/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:70:24 POST /m10/res66/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:71:23 PUT /m10/res67/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:72:26 DELETE /m10/res68/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:73:25 PATCH /m10/res69/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:74:23 GET /m10/res70/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:75:24 POST /m10/res71/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:76:23 PUT /m10/res72/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:77:26 DELETE /m10/res73/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:78:25 PATCH /m10/res74/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:79:23 GET /m10/res75/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:7:25 DELETE /m10/res3/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:80:24 POST /m10/res76/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:81:23 PUT /m10/res77/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:82:26 DELETE /m10/res78/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:83:25 PATCH /m10/res79/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:84:23 GET /m10/res80/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:85:24 POST /m10/res81/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:86:23 PUT /m10/res82/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:87:26 DELETE /m10/res83/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:88:25 PATCH /m10/res84/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:89:23 GET /m10/res85/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:8:24 PATCH /m10/res4/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:90:24 POST /m10/res86/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:91:23 PUT /m10/res87/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:92:26 DELETE /m10/res88/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:93:25 PATCH /m10/res89/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:94:23 GET /m10/res90/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:95:24 POST /m10/res91/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:96:23 PUT /m10/res92/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:97:26 DELETE /m10/res93/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:98:25 PATCH /m10/res94/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:99:23 GET /m10/res95/:id index.ts:24:13 +regby mod10.ts:1:1 mod10.ts:9:22 GET /m10/res5/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:100:24 POST /m11/res96/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:101:23 PUT /m11/res97/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:102:26 DELETE /m11/res98/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:103:25 PATCH /m11/res99/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:10:23 POST /m11/res6/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:11:22 PUT /m11/res7/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:12:25 DELETE /m11/res8/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:13:24 PATCH /m11/res9/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:14:23 GET /m11/res10/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:15:24 POST /m11/res11/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:16:23 PUT /m11/res12/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:17:26 DELETE /m11/res13/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:18:25 PATCH /m11/res14/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:19:23 GET /m11/res15/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:20:24 POST /m11/res16/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:21:23 PUT /m11/res17/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:22:26 DELETE /m11/res18/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:23:25 PATCH /m11/res19/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:24:23 GET /m11/res20/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:25:24 POST /m11/res21/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:26:23 PUT /m11/res22/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:27:26 DELETE /m11/res23/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:28:25 PATCH /m11/res24/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:29:23 GET /m11/res25/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:30:24 POST /m11/res26/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:31:23 PUT /m11/res27/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:32:26 DELETE /m11/res28/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:33:25 PATCH /m11/res29/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:34:23 GET /m11/res30/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:35:24 POST /m11/res31/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:36:23 PUT /m11/res32/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:37:26 DELETE /m11/res33/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:38:25 PATCH /m11/res34/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:39:23 GET /m11/res35/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:40:24 POST /m11/res36/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:41:23 PUT /m11/res37/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:42:26 DELETE /m11/res38/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:43:25 PATCH /m11/res39/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:44:23 GET /m11/res40/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:45:24 POST /m11/res41/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:46:23 PUT /m11/res42/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:47:26 DELETE /m11/res43/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:48:25 PATCH /m11/res44/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:49:23 GET /m11/res45/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:4:22 GET /m11/res0/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:50:24 POST /m11/res46/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:51:23 PUT /m11/res47/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:52:26 DELETE /m11/res48/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:53:25 PATCH /m11/res49/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:54:23 GET /m11/res50/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:55:24 POST /m11/res51/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:56:23 PUT /m11/res52/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:57:26 DELETE /m11/res53/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:58:25 PATCH /m11/res54/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:59:23 GET /m11/res55/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:5:23 POST /m11/res1/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:60:24 POST /m11/res56/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:61:23 PUT /m11/res57/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:62:26 DELETE /m11/res58/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:63:25 PATCH /m11/res59/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:64:23 GET /m11/res60/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:65:24 POST /m11/res61/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:66:23 PUT /m11/res62/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:67:26 DELETE /m11/res63/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:68:25 PATCH /m11/res64/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:69:23 GET /m11/res65/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:6:22 PUT /m11/res2/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:70:24 POST /m11/res66/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:71:23 PUT /m11/res67/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:72:26 DELETE /m11/res68/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:73:25 PATCH /m11/res69/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:74:23 GET /m11/res70/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:75:24 POST /m11/res71/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:76:23 PUT /m11/res72/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:77:26 DELETE /m11/res73/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:78:25 PATCH /m11/res74/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:79:23 GET /m11/res75/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:7:25 DELETE /m11/res3/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:80:24 POST /m11/res76/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:81:23 PUT /m11/res77/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:82:26 DELETE /m11/res78/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:83:25 PATCH /m11/res79/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:84:23 GET /m11/res80/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:85:24 POST /m11/res81/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:86:23 PUT /m11/res82/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:87:26 DELETE /m11/res83/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:88:25 PATCH /m11/res84/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:89:23 GET /m11/res85/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:8:24 PATCH /m11/res4/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:90:24 POST /m11/res86/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:91:23 PUT /m11/res87/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:92:26 DELETE /m11/res88/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:93:25 PATCH /m11/res89/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:94:23 GET /m11/res90/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:95:24 POST /m11/res91/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:96:23 PUT /m11/res92/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:97:26 DELETE /m11/res93/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:98:25 PATCH /m11/res94/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:99:23 GET /m11/res95/:id index.ts:24:13 +regby mod11.ts:1:1 mod11.ts:9:22 GET /m11/res5/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:100:24 POST /m12/res96/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:101:23 PUT /m12/res97/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:102:26 DELETE /m12/res98/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:103:25 PATCH /m12/res99/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:10:23 POST /m12/res6/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:11:22 PUT /m12/res7/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:12:25 DELETE /m12/res8/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:13:24 PATCH /m12/res9/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:14:23 GET /m12/res10/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:15:24 POST /m12/res11/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:16:23 PUT /m12/res12/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:17:26 DELETE /m12/res13/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:18:25 PATCH /m12/res14/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:19:23 GET /m12/res15/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:20:24 POST /m12/res16/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:21:23 PUT /m12/res17/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:22:26 DELETE /m12/res18/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:23:25 PATCH /m12/res19/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:24:23 GET /m12/res20/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:25:24 POST /m12/res21/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:26:23 PUT /m12/res22/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:27:26 DELETE /m12/res23/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:28:25 PATCH /m12/res24/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:29:23 GET /m12/res25/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:30:24 POST /m12/res26/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:31:23 PUT /m12/res27/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:32:26 DELETE /m12/res28/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:33:25 PATCH /m12/res29/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:34:23 GET /m12/res30/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:35:24 POST /m12/res31/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:36:23 PUT /m12/res32/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:37:26 DELETE /m12/res33/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:38:25 PATCH /m12/res34/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:39:23 GET /m12/res35/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:40:24 POST /m12/res36/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:41:23 PUT /m12/res37/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:42:26 DELETE /m12/res38/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:43:25 PATCH /m12/res39/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:44:23 GET /m12/res40/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:45:24 POST /m12/res41/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:46:23 PUT /m12/res42/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:47:26 DELETE /m12/res43/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:48:25 PATCH /m12/res44/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:49:23 GET /m12/res45/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:4:22 GET /m12/res0/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:50:24 POST /m12/res46/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:51:23 PUT /m12/res47/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:52:26 DELETE /m12/res48/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:53:25 PATCH /m12/res49/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:54:23 GET /m12/res50/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:55:24 POST /m12/res51/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:56:23 PUT /m12/res52/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:57:26 DELETE /m12/res53/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:58:25 PATCH /m12/res54/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:59:23 GET /m12/res55/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:5:23 POST /m12/res1/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:60:24 POST /m12/res56/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:61:23 PUT /m12/res57/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:62:26 DELETE /m12/res58/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:63:25 PATCH /m12/res59/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:64:23 GET /m12/res60/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:65:24 POST /m12/res61/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:66:23 PUT /m12/res62/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:67:26 DELETE /m12/res63/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:68:25 PATCH /m12/res64/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:69:23 GET /m12/res65/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:6:22 PUT /m12/res2/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:70:24 POST /m12/res66/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:71:23 PUT /m12/res67/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:72:26 DELETE /m12/res68/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:73:25 PATCH /m12/res69/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:74:23 GET /m12/res70/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:75:24 POST /m12/res71/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:76:23 PUT /m12/res72/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:77:26 DELETE /m12/res73/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:78:25 PATCH /m12/res74/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:79:23 GET /m12/res75/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:7:25 DELETE /m12/res3/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:80:24 POST /m12/res76/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:81:23 PUT /m12/res77/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:82:26 DELETE /m12/res78/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:83:25 PATCH /m12/res79/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:84:23 GET /m12/res80/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:85:24 POST /m12/res81/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:86:23 PUT /m12/res82/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:87:26 DELETE /m12/res83/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:88:25 PATCH /m12/res84/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:89:23 GET /m12/res85/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:8:24 PATCH /m12/res4/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:90:24 POST /m12/res86/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:91:23 PUT /m12/res87/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:92:26 DELETE /m12/res88/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:93:25 PATCH /m12/res89/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:94:23 GET /m12/res90/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:95:24 POST /m12/res91/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:96:23 PUT /m12/res92/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:97:26 DELETE /m12/res93/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:98:25 PATCH /m12/res94/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:99:23 GET /m12/res95/:id index.ts:24:13 +regby mod12.ts:1:1 mod12.ts:9:22 GET /m12/res5/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:100:24 POST /m13/res96/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:101:23 PUT /m13/res97/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:102:26 DELETE /m13/res98/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:103:25 PATCH /m13/res99/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:10:23 POST /m13/res6/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:11:22 PUT /m13/res7/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:12:25 DELETE /m13/res8/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:13:24 PATCH /m13/res9/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:14:23 GET /m13/res10/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:15:24 POST /m13/res11/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:16:23 PUT /m13/res12/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:17:26 DELETE /m13/res13/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:18:25 PATCH /m13/res14/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:19:23 GET /m13/res15/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:20:24 POST /m13/res16/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:21:23 PUT /m13/res17/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:22:26 DELETE /m13/res18/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:23:25 PATCH /m13/res19/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:24:23 GET /m13/res20/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:25:24 POST /m13/res21/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:26:23 PUT /m13/res22/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:27:26 DELETE /m13/res23/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:28:25 PATCH /m13/res24/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:29:23 GET /m13/res25/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:30:24 POST /m13/res26/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:31:23 PUT /m13/res27/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:32:26 DELETE /m13/res28/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:33:25 PATCH /m13/res29/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:34:23 GET /m13/res30/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:35:24 POST /m13/res31/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:36:23 PUT /m13/res32/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:37:26 DELETE /m13/res33/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:38:25 PATCH /m13/res34/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:39:23 GET /m13/res35/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:40:24 POST /m13/res36/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:41:23 PUT /m13/res37/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:42:26 DELETE /m13/res38/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:43:25 PATCH /m13/res39/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:44:23 GET /m13/res40/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:45:24 POST /m13/res41/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:46:23 PUT /m13/res42/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:47:26 DELETE /m13/res43/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:48:25 PATCH /m13/res44/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:49:23 GET /m13/res45/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:4:22 GET /m13/res0/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:50:24 POST /m13/res46/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:51:23 PUT /m13/res47/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:52:26 DELETE /m13/res48/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:53:25 PATCH /m13/res49/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:54:23 GET /m13/res50/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:55:24 POST /m13/res51/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:56:23 PUT /m13/res52/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:57:26 DELETE /m13/res53/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:58:25 PATCH /m13/res54/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:59:23 GET /m13/res55/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:5:23 POST /m13/res1/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:60:24 POST /m13/res56/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:61:23 PUT /m13/res57/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:62:26 DELETE /m13/res58/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:63:25 PATCH /m13/res59/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:64:23 GET /m13/res60/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:65:24 POST /m13/res61/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:66:23 PUT /m13/res62/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:67:26 DELETE /m13/res63/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:68:25 PATCH /m13/res64/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:69:23 GET /m13/res65/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:6:22 PUT /m13/res2/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:70:24 POST /m13/res66/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:71:23 PUT /m13/res67/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:72:26 DELETE /m13/res68/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:73:25 PATCH /m13/res69/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:74:23 GET /m13/res70/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:75:24 POST /m13/res71/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:76:23 PUT /m13/res72/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:77:26 DELETE /m13/res73/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:78:25 PATCH /m13/res74/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:79:23 GET /m13/res75/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:7:25 DELETE /m13/res3/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:80:24 POST /m13/res76/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:81:23 PUT /m13/res77/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:82:26 DELETE /m13/res78/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:83:25 PATCH /m13/res79/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:84:23 GET /m13/res80/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:85:24 POST /m13/res81/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:86:23 PUT /m13/res82/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:87:26 DELETE /m13/res83/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:88:25 PATCH /m13/res84/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:89:23 GET /m13/res85/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:8:24 PATCH /m13/res4/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:90:24 POST /m13/res86/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:91:23 PUT /m13/res87/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:92:26 DELETE /m13/res88/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:93:25 PATCH /m13/res89/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:94:23 GET /m13/res90/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:95:24 POST /m13/res91/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:96:23 PUT /m13/res92/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:97:26 DELETE /m13/res93/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:98:25 PATCH /m13/res94/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:99:23 GET /m13/res95/:id index.ts:24:13 +regby mod13.ts:1:1 mod13.ts:9:22 GET /m13/res5/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:100:24 POST /m14/res96/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:101:23 PUT /m14/res97/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:102:26 DELETE /m14/res98/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:103:25 PATCH /m14/res99/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:10:23 POST /m14/res6/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:11:22 PUT /m14/res7/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:12:25 DELETE /m14/res8/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:13:24 PATCH /m14/res9/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:14:23 GET /m14/res10/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:15:24 POST /m14/res11/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:16:23 PUT /m14/res12/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:17:26 DELETE /m14/res13/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:18:25 PATCH /m14/res14/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:19:23 GET /m14/res15/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:20:24 POST /m14/res16/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:21:23 PUT /m14/res17/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:22:26 DELETE /m14/res18/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:23:25 PATCH /m14/res19/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:24:23 GET /m14/res20/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:25:24 POST /m14/res21/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:26:23 PUT /m14/res22/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:27:26 DELETE /m14/res23/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:28:25 PATCH /m14/res24/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:29:23 GET /m14/res25/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:30:24 POST /m14/res26/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:31:23 PUT /m14/res27/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:32:26 DELETE /m14/res28/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:33:25 PATCH /m14/res29/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:34:23 GET /m14/res30/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:35:24 POST /m14/res31/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:36:23 PUT /m14/res32/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:37:26 DELETE /m14/res33/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:38:25 PATCH /m14/res34/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:39:23 GET /m14/res35/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:40:24 POST /m14/res36/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:41:23 PUT /m14/res37/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:42:26 DELETE /m14/res38/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:43:25 PATCH /m14/res39/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:44:23 GET /m14/res40/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:45:24 POST /m14/res41/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:46:23 PUT /m14/res42/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:47:26 DELETE /m14/res43/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:48:25 PATCH /m14/res44/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:49:23 GET /m14/res45/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:4:22 GET /m14/res0/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:50:24 POST /m14/res46/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:51:23 PUT /m14/res47/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:52:26 DELETE /m14/res48/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:53:25 PATCH /m14/res49/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:54:23 GET /m14/res50/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:55:24 POST /m14/res51/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:56:23 PUT /m14/res52/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:57:26 DELETE /m14/res53/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:58:25 PATCH /m14/res54/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:59:23 GET /m14/res55/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:5:23 POST /m14/res1/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:60:24 POST /m14/res56/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:61:23 PUT /m14/res57/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:62:26 DELETE /m14/res58/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:63:25 PATCH /m14/res59/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:64:23 GET /m14/res60/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:65:24 POST /m14/res61/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:66:23 PUT /m14/res62/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:67:26 DELETE /m14/res63/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:68:25 PATCH /m14/res64/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:69:23 GET /m14/res65/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:6:22 PUT /m14/res2/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:70:24 POST /m14/res66/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:71:23 PUT /m14/res67/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:72:26 DELETE /m14/res68/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:73:25 PATCH /m14/res69/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:74:23 GET /m14/res70/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:75:24 POST /m14/res71/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:76:23 PUT /m14/res72/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:77:26 DELETE /m14/res73/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:78:25 PATCH /m14/res74/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:79:23 GET /m14/res75/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:7:25 DELETE /m14/res3/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:80:24 POST /m14/res76/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:81:23 PUT /m14/res77/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:82:26 DELETE /m14/res78/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:83:25 PATCH /m14/res79/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:84:23 GET /m14/res80/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:85:24 POST /m14/res81/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:86:23 PUT /m14/res82/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:87:26 DELETE /m14/res83/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:88:25 PATCH /m14/res84/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:89:23 GET /m14/res85/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:8:24 PATCH /m14/res4/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:90:24 POST /m14/res86/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:91:23 PUT /m14/res87/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:92:26 DELETE /m14/res88/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:93:25 PATCH /m14/res89/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:94:23 GET /m14/res90/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:95:24 POST /m14/res91/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:96:23 PUT /m14/res92/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:97:26 DELETE /m14/res93/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:98:25 PATCH /m14/res94/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:99:23 GET /m14/res95/:id index.ts:24:13 +regby mod14.ts:1:1 mod14.ts:9:22 GET /m14/res5/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:100:24 POST /m15/res96/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:101:23 PUT /m15/res97/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:102:26 DELETE /m15/res98/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:103:25 PATCH /m15/res99/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:10:23 POST /m15/res6/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:11:22 PUT /m15/res7/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:12:25 DELETE /m15/res8/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:13:24 PATCH /m15/res9/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:14:23 GET /m15/res10/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:15:24 POST /m15/res11/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:16:23 PUT /m15/res12/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:17:26 DELETE /m15/res13/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:18:25 PATCH /m15/res14/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:19:23 GET /m15/res15/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:20:24 POST /m15/res16/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:21:23 PUT /m15/res17/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:22:26 DELETE /m15/res18/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:23:25 PATCH /m15/res19/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:24:23 GET /m15/res20/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:25:24 POST /m15/res21/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:26:23 PUT /m15/res22/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:27:26 DELETE /m15/res23/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:28:25 PATCH /m15/res24/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:29:23 GET /m15/res25/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:30:24 POST /m15/res26/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:31:23 PUT /m15/res27/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:32:26 DELETE /m15/res28/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:33:25 PATCH /m15/res29/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:34:23 GET /m15/res30/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:35:24 POST /m15/res31/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:36:23 PUT /m15/res32/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:37:26 DELETE /m15/res33/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:38:25 PATCH /m15/res34/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:39:23 GET /m15/res35/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:40:24 POST /m15/res36/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:41:23 PUT /m15/res37/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:42:26 DELETE /m15/res38/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:43:25 PATCH /m15/res39/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:44:23 GET /m15/res40/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:45:24 POST /m15/res41/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:46:23 PUT /m15/res42/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:47:26 DELETE /m15/res43/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:48:25 PATCH /m15/res44/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:49:23 GET /m15/res45/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:4:22 GET /m15/res0/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:50:24 POST /m15/res46/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:51:23 PUT /m15/res47/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:52:26 DELETE /m15/res48/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:53:25 PATCH /m15/res49/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:54:23 GET /m15/res50/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:55:24 POST /m15/res51/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:56:23 PUT /m15/res52/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:57:26 DELETE /m15/res53/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:58:25 PATCH /m15/res54/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:59:23 GET /m15/res55/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:5:23 POST /m15/res1/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:60:24 POST /m15/res56/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:61:23 PUT /m15/res57/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:62:26 DELETE /m15/res58/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:63:25 PATCH /m15/res59/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:64:23 GET /m15/res60/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:65:24 POST /m15/res61/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:66:23 PUT /m15/res62/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:67:26 DELETE /m15/res63/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:68:25 PATCH /m15/res64/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:69:23 GET /m15/res65/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:6:22 PUT /m15/res2/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:70:24 POST /m15/res66/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:71:23 PUT /m15/res67/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:72:26 DELETE /m15/res68/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:73:25 PATCH /m15/res69/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:74:23 GET /m15/res70/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:75:24 POST /m15/res71/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:76:23 PUT /m15/res72/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:77:26 DELETE /m15/res73/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:78:25 PATCH /m15/res74/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:79:23 GET /m15/res75/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:7:25 DELETE /m15/res3/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:80:24 POST /m15/res76/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:81:23 PUT /m15/res77/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:82:26 DELETE /m15/res78/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:83:25 PATCH /m15/res79/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:84:23 GET /m15/res80/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:85:24 POST /m15/res81/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:86:23 PUT /m15/res82/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:87:26 DELETE /m15/res83/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:88:25 PATCH /m15/res84/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:89:23 GET /m15/res85/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:8:24 PATCH /m15/res4/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:90:24 POST /m15/res86/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:91:23 PUT /m15/res87/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:92:26 DELETE /m15/res88/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:93:25 PATCH /m15/res89/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:94:23 GET /m15/res90/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:95:24 POST /m15/res91/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:96:23 PUT /m15/res92/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:97:26 DELETE /m15/res93/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:98:25 PATCH /m15/res94/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:99:23 GET /m15/res95/:id index.ts:24:13 +regby mod15.ts:1:1 mod15.ts:9:22 GET /m15/res5/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:100:24 POST /m16/res96/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:101:23 PUT /m16/res97/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:102:26 DELETE /m16/res98/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:103:25 PATCH /m16/res99/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:10:23 POST /m16/res6/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:11:22 PUT /m16/res7/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:12:25 DELETE /m16/res8/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:13:24 PATCH /m16/res9/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:14:23 GET /m16/res10/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:15:24 POST /m16/res11/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:16:23 PUT /m16/res12/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:17:26 DELETE /m16/res13/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:18:25 PATCH /m16/res14/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:19:23 GET /m16/res15/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:20:24 POST /m16/res16/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:21:23 PUT /m16/res17/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:22:26 DELETE /m16/res18/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:23:25 PATCH /m16/res19/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:24:23 GET /m16/res20/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:25:24 POST /m16/res21/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:26:23 PUT /m16/res22/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:27:26 DELETE /m16/res23/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:28:25 PATCH /m16/res24/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:29:23 GET /m16/res25/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:30:24 POST /m16/res26/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:31:23 PUT /m16/res27/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:32:26 DELETE /m16/res28/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:33:25 PATCH /m16/res29/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:34:23 GET /m16/res30/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:35:24 POST /m16/res31/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:36:23 PUT /m16/res32/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:37:26 DELETE /m16/res33/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:38:25 PATCH /m16/res34/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:39:23 GET /m16/res35/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:40:24 POST /m16/res36/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:41:23 PUT /m16/res37/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:42:26 DELETE /m16/res38/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:43:25 PATCH /m16/res39/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:44:23 GET /m16/res40/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:45:24 POST /m16/res41/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:46:23 PUT /m16/res42/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:47:26 DELETE /m16/res43/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:48:25 PATCH /m16/res44/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:49:23 GET /m16/res45/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:4:22 GET /m16/res0/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:50:24 POST /m16/res46/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:51:23 PUT /m16/res47/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:52:26 DELETE /m16/res48/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:53:25 PATCH /m16/res49/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:54:23 GET /m16/res50/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:55:24 POST /m16/res51/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:56:23 PUT /m16/res52/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:57:26 DELETE /m16/res53/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:58:25 PATCH /m16/res54/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:59:23 GET /m16/res55/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:5:23 POST /m16/res1/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:60:24 POST /m16/res56/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:61:23 PUT /m16/res57/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:62:26 DELETE /m16/res58/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:63:25 PATCH /m16/res59/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:64:23 GET /m16/res60/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:65:24 POST /m16/res61/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:66:23 PUT /m16/res62/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:67:26 DELETE /m16/res63/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:68:25 PATCH /m16/res64/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:69:23 GET /m16/res65/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:6:22 PUT /m16/res2/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:70:24 POST /m16/res66/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:71:23 PUT /m16/res67/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:72:26 DELETE /m16/res68/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:73:25 PATCH /m16/res69/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:74:23 GET /m16/res70/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:75:24 POST /m16/res71/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:76:23 PUT /m16/res72/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:77:26 DELETE /m16/res73/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:78:25 PATCH /m16/res74/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:79:23 GET /m16/res75/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:7:25 DELETE /m16/res3/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:80:24 POST /m16/res76/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:81:23 PUT /m16/res77/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:82:26 DELETE /m16/res78/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:83:25 PATCH /m16/res79/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:84:23 GET /m16/res80/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:85:24 POST /m16/res81/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:86:23 PUT /m16/res82/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:87:26 DELETE /m16/res83/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:88:25 PATCH /m16/res84/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:89:23 GET /m16/res85/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:8:24 PATCH /m16/res4/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:90:24 POST /m16/res86/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:91:23 PUT /m16/res87/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:92:26 DELETE /m16/res88/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:93:25 PATCH /m16/res89/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:94:23 GET /m16/res90/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:95:24 POST /m16/res91/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:96:23 PUT /m16/res92/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:97:26 DELETE /m16/res93/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:98:25 PATCH /m16/res94/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:99:23 GET /m16/res95/:id index.ts:24:13 +regby mod16.ts:1:1 mod16.ts:9:22 GET /m16/res5/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:100:24 POST /m17/res96/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:101:23 PUT /m17/res97/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:102:26 DELETE /m17/res98/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:103:25 PATCH /m17/res99/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:10:23 POST /m17/res6/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:11:22 PUT /m17/res7/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:12:25 DELETE /m17/res8/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:13:24 PATCH /m17/res9/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:14:23 GET /m17/res10/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:15:24 POST /m17/res11/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:16:23 PUT /m17/res12/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:17:26 DELETE /m17/res13/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:18:25 PATCH /m17/res14/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:19:23 GET /m17/res15/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:20:24 POST /m17/res16/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:21:23 PUT /m17/res17/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:22:26 DELETE /m17/res18/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:23:25 PATCH /m17/res19/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:24:23 GET /m17/res20/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:25:24 POST /m17/res21/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:26:23 PUT /m17/res22/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:27:26 DELETE /m17/res23/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:28:25 PATCH /m17/res24/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:29:23 GET /m17/res25/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:30:24 POST /m17/res26/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:31:23 PUT /m17/res27/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:32:26 DELETE /m17/res28/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:33:25 PATCH /m17/res29/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:34:23 GET /m17/res30/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:35:24 POST /m17/res31/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:36:23 PUT /m17/res32/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:37:26 DELETE /m17/res33/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:38:25 PATCH /m17/res34/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:39:23 GET /m17/res35/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:40:24 POST /m17/res36/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:41:23 PUT /m17/res37/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:42:26 DELETE /m17/res38/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:43:25 PATCH /m17/res39/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:44:23 GET /m17/res40/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:45:24 POST /m17/res41/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:46:23 PUT /m17/res42/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:47:26 DELETE /m17/res43/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:48:25 PATCH /m17/res44/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:49:23 GET /m17/res45/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:4:22 GET /m17/res0/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:50:24 POST /m17/res46/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:51:23 PUT /m17/res47/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:52:26 DELETE /m17/res48/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:53:25 PATCH /m17/res49/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:54:23 GET /m17/res50/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:55:24 POST /m17/res51/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:56:23 PUT /m17/res52/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:57:26 DELETE /m17/res53/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:58:25 PATCH /m17/res54/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:59:23 GET /m17/res55/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:5:23 POST /m17/res1/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:60:24 POST /m17/res56/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:61:23 PUT /m17/res57/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:62:26 DELETE /m17/res58/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:63:25 PATCH /m17/res59/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:64:23 GET /m17/res60/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:65:24 POST /m17/res61/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:66:23 PUT /m17/res62/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:67:26 DELETE /m17/res63/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:68:25 PATCH /m17/res64/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:69:23 GET /m17/res65/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:6:22 PUT /m17/res2/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:70:24 POST /m17/res66/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:71:23 PUT /m17/res67/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:72:26 DELETE /m17/res68/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:73:25 PATCH /m17/res69/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:74:23 GET /m17/res70/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:75:24 POST /m17/res71/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:76:23 PUT /m17/res72/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:77:26 DELETE /m17/res73/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:78:25 PATCH /m17/res74/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:79:23 GET /m17/res75/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:7:25 DELETE /m17/res3/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:80:24 POST /m17/res76/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:81:23 PUT /m17/res77/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:82:26 DELETE /m17/res78/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:83:25 PATCH /m17/res79/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:84:23 GET /m17/res80/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:85:24 POST /m17/res81/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:86:23 PUT /m17/res82/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:87:26 DELETE /m17/res83/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:88:25 PATCH /m17/res84/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:89:23 GET /m17/res85/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:8:24 PATCH /m17/res4/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:90:24 POST /m17/res86/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:91:23 PUT /m17/res87/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:92:26 DELETE /m17/res88/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:93:25 PATCH /m17/res89/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:94:23 GET /m17/res90/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:95:24 POST /m17/res91/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:96:23 PUT /m17/res92/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:97:26 DELETE /m17/res93/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:98:25 PATCH /m17/res94/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:99:23 GET /m17/res95/:id index.ts:24:13 +regby mod17.ts:1:1 mod17.ts:9:22 GET /m17/res5/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:100:24 POST /m18/res96/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:101:23 PUT /m18/res97/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:102:26 DELETE /m18/res98/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:103:25 PATCH /m18/res99/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:10:23 POST /m18/res6/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:11:22 PUT /m18/res7/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:12:25 DELETE /m18/res8/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:13:24 PATCH /m18/res9/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:14:23 GET /m18/res10/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:15:24 POST /m18/res11/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:16:23 PUT /m18/res12/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:17:26 DELETE /m18/res13/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:18:25 PATCH /m18/res14/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:19:23 GET /m18/res15/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:20:24 POST /m18/res16/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:21:23 PUT /m18/res17/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:22:26 DELETE /m18/res18/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:23:25 PATCH /m18/res19/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:24:23 GET /m18/res20/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:25:24 POST /m18/res21/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:26:23 PUT /m18/res22/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:27:26 DELETE /m18/res23/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:28:25 PATCH /m18/res24/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:29:23 GET /m18/res25/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:30:24 POST /m18/res26/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:31:23 PUT /m18/res27/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:32:26 DELETE /m18/res28/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:33:25 PATCH /m18/res29/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:34:23 GET /m18/res30/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:35:24 POST /m18/res31/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:36:23 PUT /m18/res32/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:37:26 DELETE /m18/res33/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:38:25 PATCH /m18/res34/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:39:23 GET /m18/res35/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:40:24 POST /m18/res36/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:41:23 PUT /m18/res37/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:42:26 DELETE /m18/res38/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:43:25 PATCH /m18/res39/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:44:23 GET /m18/res40/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:45:24 POST /m18/res41/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:46:23 PUT /m18/res42/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:47:26 DELETE /m18/res43/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:48:25 PATCH /m18/res44/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:49:23 GET /m18/res45/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:4:22 GET /m18/res0/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:50:24 POST /m18/res46/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:51:23 PUT /m18/res47/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:52:26 DELETE /m18/res48/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:53:25 PATCH /m18/res49/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:54:23 GET /m18/res50/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:55:24 POST /m18/res51/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:56:23 PUT /m18/res52/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:57:26 DELETE /m18/res53/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:58:25 PATCH /m18/res54/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:59:23 GET /m18/res55/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:5:23 POST /m18/res1/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:60:24 POST /m18/res56/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:61:23 PUT /m18/res57/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:62:26 DELETE /m18/res58/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:63:25 PATCH /m18/res59/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:64:23 GET /m18/res60/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:65:24 POST /m18/res61/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:66:23 PUT /m18/res62/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:67:26 DELETE /m18/res63/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:68:25 PATCH /m18/res64/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:69:23 GET /m18/res65/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:6:22 PUT /m18/res2/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:70:24 POST /m18/res66/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:71:23 PUT /m18/res67/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:72:26 DELETE /m18/res68/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:73:25 PATCH /m18/res69/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:74:23 GET /m18/res70/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:75:24 POST /m18/res71/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:76:23 PUT /m18/res72/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:77:26 DELETE /m18/res73/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:78:25 PATCH /m18/res74/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:79:23 GET /m18/res75/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:7:25 DELETE /m18/res3/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:80:24 POST /m18/res76/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:81:23 PUT /m18/res77/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:82:26 DELETE /m18/res78/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:83:25 PATCH /m18/res79/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:84:23 GET /m18/res80/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:85:24 POST /m18/res81/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:86:23 PUT /m18/res82/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:87:26 DELETE /m18/res83/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:88:25 PATCH /m18/res84/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:89:23 GET /m18/res85/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:8:24 PATCH /m18/res4/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:90:24 POST /m18/res86/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:91:23 PUT /m18/res87/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:92:26 DELETE /m18/res88/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:93:25 PATCH /m18/res89/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:94:23 GET /m18/res90/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:95:24 POST /m18/res91/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:96:23 PUT /m18/res92/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:97:26 DELETE /m18/res93/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:98:25 PATCH /m18/res94/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:99:23 GET /m18/res95/:id index.ts:24:13 +regby mod18.ts:1:1 mod18.ts:9:22 GET /m18/res5/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:100:24 POST /m19/res96/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:101:23 PUT /m19/res97/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:102:26 DELETE /m19/res98/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:103:25 PATCH /m19/res99/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:10:23 POST /m19/res6/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:11:22 PUT /m19/res7/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:12:25 DELETE /m19/res8/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:13:24 PATCH /m19/res9/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:14:23 GET /m19/res10/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:15:24 POST /m19/res11/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:16:23 PUT /m19/res12/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:17:26 DELETE /m19/res13/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:18:25 PATCH /m19/res14/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:19:23 GET /m19/res15/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:20:24 POST /m19/res16/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:21:23 PUT /m19/res17/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:22:26 DELETE /m19/res18/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:23:25 PATCH /m19/res19/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:24:23 GET /m19/res20/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:25:24 POST /m19/res21/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:26:23 PUT /m19/res22/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:27:26 DELETE /m19/res23/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:28:25 PATCH /m19/res24/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:29:23 GET /m19/res25/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:30:24 POST /m19/res26/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:31:23 PUT /m19/res27/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:32:26 DELETE /m19/res28/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:33:25 PATCH /m19/res29/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:34:23 GET /m19/res30/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:35:24 POST /m19/res31/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:36:23 PUT /m19/res32/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:37:26 DELETE /m19/res33/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:38:25 PATCH /m19/res34/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:39:23 GET /m19/res35/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:40:24 POST /m19/res36/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:41:23 PUT /m19/res37/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:42:26 DELETE /m19/res38/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:43:25 PATCH /m19/res39/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:44:23 GET /m19/res40/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:45:24 POST /m19/res41/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:46:23 PUT /m19/res42/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:47:26 DELETE /m19/res43/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:48:25 PATCH /m19/res44/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:49:23 GET /m19/res45/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:4:22 GET /m19/res0/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:50:24 POST /m19/res46/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:51:23 PUT /m19/res47/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:52:26 DELETE /m19/res48/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:53:25 PATCH /m19/res49/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:54:23 GET /m19/res50/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:55:24 POST /m19/res51/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:56:23 PUT /m19/res52/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:57:26 DELETE /m19/res53/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:58:25 PATCH /m19/res54/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:59:23 GET /m19/res55/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:5:23 POST /m19/res1/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:60:24 POST /m19/res56/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:61:23 PUT /m19/res57/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:62:26 DELETE /m19/res58/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:63:25 PATCH /m19/res59/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:64:23 GET /m19/res60/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:65:24 POST /m19/res61/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:66:23 PUT /m19/res62/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:67:26 DELETE /m19/res63/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:68:25 PATCH /m19/res64/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:69:23 GET /m19/res65/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:6:22 PUT /m19/res2/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:70:24 POST /m19/res66/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:71:23 PUT /m19/res67/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:72:26 DELETE /m19/res68/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:73:25 PATCH /m19/res69/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:74:23 GET /m19/res70/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:75:24 POST /m19/res71/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:76:23 PUT /m19/res72/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:77:26 DELETE /m19/res73/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:78:25 PATCH /m19/res74/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:79:23 GET /m19/res75/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:7:25 DELETE /m19/res3/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:80:24 POST /m19/res76/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:81:23 PUT /m19/res77/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:82:26 DELETE /m19/res78/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:83:25 PATCH /m19/res79/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:84:23 GET /m19/res80/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:85:24 POST /m19/res81/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:86:23 PUT /m19/res82/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:87:26 DELETE /m19/res83/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:88:25 PATCH /m19/res84/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:89:23 GET /m19/res85/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:8:24 PATCH /m19/res4/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:90:24 POST /m19/res86/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:91:23 PUT /m19/res87/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:92:26 DELETE /m19/res88/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:93:25 PATCH /m19/res89/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:94:23 GET /m19/res90/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:95:24 POST /m19/res91/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:96:23 PUT /m19/res92/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:97:26 DELETE /m19/res93/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:98:25 PATCH /m19/res94/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:99:23 GET /m19/res95/:id index.ts:24:13 +regby mod19.ts:1:1 mod19.ts:9:22 GET /m19/res5/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:100:23 POST /m2/res96/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:101:22 PUT /m2/res97/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:102:25 DELETE /m2/res98/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:103:24 PATCH /m2/res99/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:10:22 POST /m2/res6/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:11:21 PUT /m2/res7/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:12:24 DELETE /m2/res8/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:13:23 PATCH /m2/res9/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:14:22 GET /m2/res10/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:15:23 POST /m2/res11/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:16:22 PUT /m2/res12/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:17:25 DELETE /m2/res13/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:18:24 PATCH /m2/res14/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:19:22 GET /m2/res15/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:20:23 POST /m2/res16/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:21:22 PUT /m2/res17/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:22:25 DELETE /m2/res18/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:23:24 PATCH /m2/res19/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:24:22 GET /m2/res20/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:25:23 POST /m2/res21/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:26:22 PUT /m2/res22/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:27:25 DELETE /m2/res23/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:28:24 PATCH /m2/res24/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:29:22 GET /m2/res25/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:30:23 POST /m2/res26/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:31:22 PUT /m2/res27/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:32:25 DELETE /m2/res28/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:33:24 PATCH /m2/res29/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:34:22 GET /m2/res30/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:35:23 POST /m2/res31/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:36:22 PUT /m2/res32/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:37:25 DELETE /m2/res33/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:38:24 PATCH /m2/res34/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:39:22 GET /m2/res35/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:40:23 POST /m2/res36/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:41:22 PUT /m2/res37/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:42:25 DELETE /m2/res38/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:43:24 PATCH /m2/res39/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:44:22 GET /m2/res40/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:45:23 POST /m2/res41/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:46:22 PUT /m2/res42/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:47:25 DELETE /m2/res43/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:48:24 PATCH /m2/res44/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:49:22 GET /m2/res45/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:4:21 GET /m2/res0/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:50:23 POST /m2/res46/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:51:22 PUT /m2/res47/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:52:25 DELETE /m2/res48/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:53:24 PATCH /m2/res49/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:54:22 GET /m2/res50/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:55:23 POST /m2/res51/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:56:22 PUT /m2/res52/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:57:25 DELETE /m2/res53/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:58:24 PATCH /m2/res54/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:59:22 GET /m2/res55/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:5:22 POST /m2/res1/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:60:23 POST /m2/res56/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:61:22 PUT /m2/res57/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:62:25 DELETE /m2/res58/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:63:24 PATCH /m2/res59/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:64:22 GET /m2/res60/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:65:23 POST /m2/res61/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:66:22 PUT /m2/res62/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:67:25 DELETE /m2/res63/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:68:24 PATCH /m2/res64/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:69:22 GET /m2/res65/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:6:21 PUT /m2/res2/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:70:23 POST /m2/res66/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:71:22 PUT /m2/res67/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:72:25 DELETE /m2/res68/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:73:24 PATCH /m2/res69/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:74:22 GET /m2/res70/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:75:23 POST /m2/res71/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:76:22 PUT /m2/res72/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:77:25 DELETE /m2/res73/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:78:24 PATCH /m2/res74/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:79:22 GET /m2/res75/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:7:24 DELETE /m2/res3/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:80:23 POST /m2/res76/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:81:22 PUT /m2/res77/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:82:25 DELETE /m2/res78/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:83:24 PATCH /m2/res79/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:84:22 GET /m2/res80/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:85:23 POST /m2/res81/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:86:22 PUT /m2/res82/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:87:25 DELETE /m2/res83/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:88:24 PATCH /m2/res84/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:89:22 GET /m2/res85/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:8:23 PATCH /m2/res4/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:90:23 POST /m2/res86/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:91:22 PUT /m2/res87/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:92:25 DELETE /m2/res88/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:93:24 PATCH /m2/res89/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:94:22 GET /m2/res90/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:95:23 POST /m2/res91/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:96:22 PUT /m2/res92/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:97:25 DELETE /m2/res93/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:98:24 PATCH /m2/res94/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:99:22 GET /m2/res95/:id index.ts:24:13 +regby mod2.ts:1:1 mod2.ts:9:21 GET /m2/res5/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:100:23 POST /m3/res96/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:101:22 PUT /m3/res97/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:102:25 DELETE /m3/res98/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:103:24 PATCH /m3/res99/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:10:22 POST /m3/res6/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:11:21 PUT /m3/res7/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:12:24 DELETE /m3/res8/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:13:23 PATCH /m3/res9/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:14:22 GET /m3/res10/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:15:23 POST /m3/res11/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:16:22 PUT /m3/res12/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:17:25 DELETE /m3/res13/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:18:24 PATCH /m3/res14/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:19:22 GET /m3/res15/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:20:23 POST /m3/res16/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:21:22 PUT /m3/res17/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:22:25 DELETE /m3/res18/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:23:24 PATCH /m3/res19/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:24:22 GET /m3/res20/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:25:23 POST /m3/res21/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:26:22 PUT /m3/res22/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:27:25 DELETE /m3/res23/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:28:24 PATCH /m3/res24/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:29:22 GET /m3/res25/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:30:23 POST /m3/res26/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:31:22 PUT /m3/res27/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:32:25 DELETE /m3/res28/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:33:24 PATCH /m3/res29/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:34:22 GET /m3/res30/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:35:23 POST /m3/res31/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:36:22 PUT /m3/res32/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:37:25 DELETE /m3/res33/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:38:24 PATCH /m3/res34/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:39:22 GET /m3/res35/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:40:23 POST /m3/res36/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:41:22 PUT /m3/res37/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:42:25 DELETE /m3/res38/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:43:24 PATCH /m3/res39/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:44:22 GET /m3/res40/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:45:23 POST /m3/res41/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:46:22 PUT /m3/res42/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:47:25 DELETE /m3/res43/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:48:24 PATCH /m3/res44/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:49:22 GET /m3/res45/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:4:21 GET /m3/res0/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:50:23 POST /m3/res46/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:51:22 PUT /m3/res47/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:52:25 DELETE /m3/res48/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:53:24 PATCH /m3/res49/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:54:22 GET /m3/res50/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:55:23 POST /m3/res51/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:56:22 PUT /m3/res52/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:57:25 DELETE /m3/res53/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:58:24 PATCH /m3/res54/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:59:22 GET /m3/res55/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:5:22 POST /m3/res1/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:60:23 POST /m3/res56/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:61:22 PUT /m3/res57/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:62:25 DELETE /m3/res58/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:63:24 PATCH /m3/res59/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:64:22 GET /m3/res60/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:65:23 POST /m3/res61/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:66:22 PUT /m3/res62/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:67:25 DELETE /m3/res63/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:68:24 PATCH /m3/res64/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:69:22 GET /m3/res65/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:6:21 PUT /m3/res2/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:70:23 POST /m3/res66/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:71:22 PUT /m3/res67/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:72:25 DELETE /m3/res68/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:73:24 PATCH /m3/res69/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:74:22 GET /m3/res70/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:75:23 POST /m3/res71/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:76:22 PUT /m3/res72/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:77:25 DELETE /m3/res73/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:78:24 PATCH /m3/res74/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:79:22 GET /m3/res75/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:7:24 DELETE /m3/res3/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:80:23 POST /m3/res76/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:81:22 PUT /m3/res77/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:82:25 DELETE /m3/res78/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:83:24 PATCH /m3/res79/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:84:22 GET /m3/res80/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:85:23 POST /m3/res81/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:86:22 PUT /m3/res82/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:87:25 DELETE /m3/res83/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:88:24 PATCH /m3/res84/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:89:22 GET /m3/res85/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:8:23 PATCH /m3/res4/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:90:23 POST /m3/res86/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:91:22 PUT /m3/res87/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:92:25 DELETE /m3/res88/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:93:24 PATCH /m3/res89/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:94:22 GET /m3/res90/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:95:23 POST /m3/res91/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:96:22 PUT /m3/res92/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:97:25 DELETE /m3/res93/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:98:24 PATCH /m3/res94/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:99:22 GET /m3/res95/:id index.ts:24:13 +regby mod3.ts:1:1 mod3.ts:9:21 GET /m3/res5/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:100:23 POST /m4/res96/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:101:22 PUT /m4/res97/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:102:25 DELETE /m4/res98/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:103:24 PATCH /m4/res99/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:10:22 POST /m4/res6/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:11:21 PUT /m4/res7/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:12:24 DELETE /m4/res8/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:13:23 PATCH /m4/res9/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:14:22 GET /m4/res10/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:15:23 POST /m4/res11/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:16:22 PUT /m4/res12/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:17:25 DELETE /m4/res13/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:18:24 PATCH /m4/res14/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:19:22 GET /m4/res15/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:20:23 POST /m4/res16/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:21:22 PUT /m4/res17/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:22:25 DELETE /m4/res18/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:23:24 PATCH /m4/res19/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:24:22 GET /m4/res20/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:25:23 POST /m4/res21/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:26:22 PUT /m4/res22/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:27:25 DELETE /m4/res23/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:28:24 PATCH /m4/res24/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:29:22 GET /m4/res25/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:30:23 POST /m4/res26/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:31:22 PUT /m4/res27/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:32:25 DELETE /m4/res28/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:33:24 PATCH /m4/res29/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:34:22 GET /m4/res30/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:35:23 POST /m4/res31/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:36:22 PUT /m4/res32/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:37:25 DELETE /m4/res33/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:38:24 PATCH /m4/res34/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:39:22 GET /m4/res35/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:40:23 POST /m4/res36/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:41:22 PUT /m4/res37/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:42:25 DELETE /m4/res38/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:43:24 PATCH /m4/res39/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:44:22 GET /m4/res40/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:45:23 POST /m4/res41/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:46:22 PUT /m4/res42/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:47:25 DELETE /m4/res43/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:48:24 PATCH /m4/res44/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:49:22 GET /m4/res45/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:4:21 GET /m4/res0/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:50:23 POST /m4/res46/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:51:22 PUT /m4/res47/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:52:25 DELETE /m4/res48/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:53:24 PATCH /m4/res49/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:54:22 GET /m4/res50/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:55:23 POST /m4/res51/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:56:22 PUT /m4/res52/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:57:25 DELETE /m4/res53/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:58:24 PATCH /m4/res54/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:59:22 GET /m4/res55/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:5:22 POST /m4/res1/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:60:23 POST /m4/res56/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:61:22 PUT /m4/res57/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:62:25 DELETE /m4/res58/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:63:24 PATCH /m4/res59/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:64:22 GET /m4/res60/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:65:23 POST /m4/res61/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:66:22 PUT /m4/res62/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:67:25 DELETE /m4/res63/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:68:24 PATCH /m4/res64/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:69:22 GET /m4/res65/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:6:21 PUT /m4/res2/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:70:23 POST /m4/res66/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:71:22 PUT /m4/res67/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:72:25 DELETE /m4/res68/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:73:24 PATCH /m4/res69/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:74:22 GET /m4/res70/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:75:23 POST /m4/res71/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:76:22 PUT /m4/res72/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:77:25 DELETE /m4/res73/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:78:24 PATCH /m4/res74/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:79:22 GET /m4/res75/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:7:24 DELETE /m4/res3/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:80:23 POST /m4/res76/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:81:22 PUT /m4/res77/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:82:25 DELETE /m4/res78/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:83:24 PATCH /m4/res79/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:84:22 GET /m4/res80/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:85:23 POST /m4/res81/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:86:22 PUT /m4/res82/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:87:25 DELETE /m4/res83/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:88:24 PATCH /m4/res84/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:89:22 GET /m4/res85/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:8:23 PATCH /m4/res4/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:90:23 POST /m4/res86/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:91:22 PUT /m4/res87/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:92:25 DELETE /m4/res88/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:93:24 PATCH /m4/res89/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:94:22 GET /m4/res90/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:95:23 POST /m4/res91/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:96:22 PUT /m4/res92/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:97:25 DELETE /m4/res93/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:98:24 PATCH /m4/res94/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:99:22 GET /m4/res95/:id index.ts:24:13 +regby mod4.ts:1:1 mod4.ts:9:21 GET /m4/res5/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:100:23 POST /m5/res96/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:101:22 PUT /m5/res97/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:102:25 DELETE /m5/res98/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:103:24 PATCH /m5/res99/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:10:22 POST /m5/res6/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:11:21 PUT /m5/res7/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:12:24 DELETE /m5/res8/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:13:23 PATCH /m5/res9/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:14:22 GET /m5/res10/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:15:23 POST /m5/res11/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:16:22 PUT /m5/res12/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:17:25 DELETE /m5/res13/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:18:24 PATCH /m5/res14/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:19:22 GET /m5/res15/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:20:23 POST /m5/res16/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:21:22 PUT /m5/res17/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:22:25 DELETE /m5/res18/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:23:24 PATCH /m5/res19/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:24:22 GET /m5/res20/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:25:23 POST /m5/res21/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:26:22 PUT /m5/res22/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:27:25 DELETE /m5/res23/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:28:24 PATCH /m5/res24/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:29:22 GET /m5/res25/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:30:23 POST /m5/res26/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:31:22 PUT /m5/res27/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:32:25 DELETE /m5/res28/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:33:24 PATCH /m5/res29/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:34:22 GET /m5/res30/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:35:23 POST /m5/res31/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:36:22 PUT /m5/res32/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:37:25 DELETE /m5/res33/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:38:24 PATCH /m5/res34/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:39:22 GET /m5/res35/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:40:23 POST /m5/res36/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:41:22 PUT /m5/res37/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:42:25 DELETE /m5/res38/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:43:24 PATCH /m5/res39/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:44:22 GET /m5/res40/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:45:23 POST /m5/res41/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:46:22 PUT /m5/res42/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:47:25 DELETE /m5/res43/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:48:24 PATCH /m5/res44/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:49:22 GET /m5/res45/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:4:21 GET /m5/res0/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:50:23 POST /m5/res46/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:51:22 PUT /m5/res47/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:52:25 DELETE /m5/res48/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:53:24 PATCH /m5/res49/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:54:22 GET /m5/res50/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:55:23 POST /m5/res51/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:56:22 PUT /m5/res52/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:57:25 DELETE /m5/res53/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:58:24 PATCH /m5/res54/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:59:22 GET /m5/res55/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:5:22 POST /m5/res1/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:60:23 POST /m5/res56/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:61:22 PUT /m5/res57/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:62:25 DELETE /m5/res58/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:63:24 PATCH /m5/res59/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:64:22 GET /m5/res60/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:65:23 POST /m5/res61/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:66:22 PUT /m5/res62/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:67:25 DELETE /m5/res63/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:68:24 PATCH /m5/res64/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:69:22 GET /m5/res65/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:6:21 PUT /m5/res2/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:70:23 POST /m5/res66/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:71:22 PUT /m5/res67/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:72:25 DELETE /m5/res68/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:73:24 PATCH /m5/res69/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:74:22 GET /m5/res70/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:75:23 POST /m5/res71/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:76:22 PUT /m5/res72/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:77:25 DELETE /m5/res73/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:78:24 PATCH /m5/res74/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:79:22 GET /m5/res75/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:7:24 DELETE /m5/res3/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:80:23 POST /m5/res76/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:81:22 PUT /m5/res77/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:82:25 DELETE /m5/res78/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:83:24 PATCH /m5/res79/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:84:22 GET /m5/res80/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:85:23 POST /m5/res81/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:86:22 PUT /m5/res82/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:87:25 DELETE /m5/res83/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:88:24 PATCH /m5/res84/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:89:22 GET /m5/res85/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:8:23 PATCH /m5/res4/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:90:23 POST /m5/res86/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:91:22 PUT /m5/res87/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:92:25 DELETE /m5/res88/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:93:24 PATCH /m5/res89/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:94:22 GET /m5/res90/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:95:23 POST /m5/res91/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:96:22 PUT /m5/res92/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:97:25 DELETE /m5/res93/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:98:24 PATCH /m5/res94/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:99:22 GET /m5/res95/:id index.ts:24:13 +regby mod5.ts:1:1 mod5.ts:9:21 GET /m5/res5/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:100:23 POST /m6/res96/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:101:22 PUT /m6/res97/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:102:25 DELETE /m6/res98/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:103:24 PATCH /m6/res99/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:10:22 POST /m6/res6/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:11:21 PUT /m6/res7/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:12:24 DELETE /m6/res8/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:13:23 PATCH /m6/res9/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:14:22 GET /m6/res10/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:15:23 POST /m6/res11/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:16:22 PUT /m6/res12/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:17:25 DELETE /m6/res13/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:18:24 PATCH /m6/res14/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:19:22 GET /m6/res15/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:20:23 POST /m6/res16/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:21:22 PUT /m6/res17/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:22:25 DELETE /m6/res18/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:23:24 PATCH /m6/res19/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:24:22 GET /m6/res20/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:25:23 POST /m6/res21/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:26:22 PUT /m6/res22/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:27:25 DELETE /m6/res23/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:28:24 PATCH /m6/res24/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:29:22 GET /m6/res25/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:30:23 POST /m6/res26/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:31:22 PUT /m6/res27/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:32:25 DELETE /m6/res28/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:33:24 PATCH /m6/res29/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:34:22 GET /m6/res30/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:35:23 POST /m6/res31/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:36:22 PUT /m6/res32/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:37:25 DELETE /m6/res33/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:38:24 PATCH /m6/res34/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:39:22 GET /m6/res35/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:40:23 POST /m6/res36/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:41:22 PUT /m6/res37/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:42:25 DELETE /m6/res38/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:43:24 PATCH /m6/res39/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:44:22 GET /m6/res40/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:45:23 POST /m6/res41/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:46:22 PUT /m6/res42/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:47:25 DELETE /m6/res43/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:48:24 PATCH /m6/res44/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:49:22 GET /m6/res45/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:4:21 GET /m6/res0/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:50:23 POST /m6/res46/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:51:22 PUT /m6/res47/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:52:25 DELETE /m6/res48/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:53:24 PATCH /m6/res49/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:54:22 GET /m6/res50/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:55:23 POST /m6/res51/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:56:22 PUT /m6/res52/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:57:25 DELETE /m6/res53/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:58:24 PATCH /m6/res54/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:59:22 GET /m6/res55/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:5:22 POST /m6/res1/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:60:23 POST /m6/res56/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:61:22 PUT /m6/res57/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:62:25 DELETE /m6/res58/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:63:24 PATCH /m6/res59/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:64:22 GET /m6/res60/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:65:23 POST /m6/res61/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:66:22 PUT /m6/res62/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:67:25 DELETE /m6/res63/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:68:24 PATCH /m6/res64/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:69:22 GET /m6/res65/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:6:21 PUT /m6/res2/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:70:23 POST /m6/res66/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:71:22 PUT /m6/res67/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:72:25 DELETE /m6/res68/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:73:24 PATCH /m6/res69/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:74:22 GET /m6/res70/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:75:23 POST /m6/res71/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:76:22 PUT /m6/res72/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:77:25 DELETE /m6/res73/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:78:24 PATCH /m6/res74/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:79:22 GET /m6/res75/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:7:24 DELETE /m6/res3/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:80:23 POST /m6/res76/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:81:22 PUT /m6/res77/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:82:25 DELETE /m6/res78/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:83:24 PATCH /m6/res79/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:84:22 GET /m6/res80/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:85:23 POST /m6/res81/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:86:22 PUT /m6/res82/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:87:25 DELETE /m6/res83/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:88:24 PATCH /m6/res84/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:89:22 GET /m6/res85/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:8:23 PATCH /m6/res4/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:90:23 POST /m6/res86/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:91:22 PUT /m6/res87/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:92:25 DELETE /m6/res88/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:93:24 PATCH /m6/res89/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:94:22 GET /m6/res90/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:95:23 POST /m6/res91/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:96:22 PUT /m6/res92/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:97:25 DELETE /m6/res93/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:98:24 PATCH /m6/res94/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:99:22 GET /m6/res95/:id index.ts:24:13 +regby mod6.ts:1:1 mod6.ts:9:21 GET /m6/res5/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:100:23 POST /m7/res96/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:101:22 PUT /m7/res97/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:102:25 DELETE /m7/res98/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:103:24 PATCH /m7/res99/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:10:22 POST /m7/res6/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:11:21 PUT /m7/res7/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:12:24 DELETE /m7/res8/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:13:23 PATCH /m7/res9/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:14:22 GET /m7/res10/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:15:23 POST /m7/res11/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:16:22 PUT /m7/res12/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:17:25 DELETE /m7/res13/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:18:24 PATCH /m7/res14/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:19:22 GET /m7/res15/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:20:23 POST /m7/res16/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:21:22 PUT /m7/res17/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:22:25 DELETE /m7/res18/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:23:24 PATCH /m7/res19/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:24:22 GET /m7/res20/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:25:23 POST /m7/res21/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:26:22 PUT /m7/res22/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:27:25 DELETE /m7/res23/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:28:24 PATCH /m7/res24/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:29:22 GET /m7/res25/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:30:23 POST /m7/res26/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:31:22 PUT /m7/res27/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:32:25 DELETE /m7/res28/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:33:24 PATCH /m7/res29/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:34:22 GET /m7/res30/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:35:23 POST /m7/res31/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:36:22 PUT /m7/res32/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:37:25 DELETE /m7/res33/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:38:24 PATCH /m7/res34/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:39:22 GET /m7/res35/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:40:23 POST /m7/res36/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:41:22 PUT /m7/res37/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:42:25 DELETE /m7/res38/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:43:24 PATCH /m7/res39/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:44:22 GET /m7/res40/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:45:23 POST /m7/res41/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:46:22 PUT /m7/res42/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:47:25 DELETE /m7/res43/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:48:24 PATCH /m7/res44/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:49:22 GET /m7/res45/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:4:21 GET /m7/res0/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:50:23 POST /m7/res46/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:51:22 PUT /m7/res47/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:52:25 DELETE /m7/res48/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:53:24 PATCH /m7/res49/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:54:22 GET /m7/res50/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:55:23 POST /m7/res51/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:56:22 PUT /m7/res52/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:57:25 DELETE /m7/res53/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:58:24 PATCH /m7/res54/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:59:22 GET /m7/res55/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:5:22 POST /m7/res1/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:60:23 POST /m7/res56/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:61:22 PUT /m7/res57/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:62:25 DELETE /m7/res58/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:63:24 PATCH /m7/res59/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:64:22 GET /m7/res60/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:65:23 POST /m7/res61/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:66:22 PUT /m7/res62/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:67:25 DELETE /m7/res63/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:68:24 PATCH /m7/res64/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:69:22 GET /m7/res65/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:6:21 PUT /m7/res2/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:70:23 POST /m7/res66/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:71:22 PUT /m7/res67/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:72:25 DELETE /m7/res68/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:73:24 PATCH /m7/res69/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:74:22 GET /m7/res70/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:75:23 POST /m7/res71/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:76:22 PUT /m7/res72/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:77:25 DELETE /m7/res73/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:78:24 PATCH /m7/res74/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:79:22 GET /m7/res75/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:7:24 DELETE /m7/res3/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:80:23 POST /m7/res76/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:81:22 PUT /m7/res77/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:82:25 DELETE /m7/res78/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:83:24 PATCH /m7/res79/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:84:22 GET /m7/res80/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:85:23 POST /m7/res81/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:86:22 PUT /m7/res82/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:87:25 DELETE /m7/res83/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:88:24 PATCH /m7/res84/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:89:22 GET /m7/res85/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:8:23 PATCH /m7/res4/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:90:23 POST /m7/res86/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:91:22 PUT /m7/res87/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:92:25 DELETE /m7/res88/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:93:24 PATCH /m7/res89/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:94:22 GET /m7/res90/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:95:23 POST /m7/res91/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:96:22 PUT /m7/res92/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:97:25 DELETE /m7/res93/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:98:24 PATCH /m7/res94/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:99:22 GET /m7/res95/:id index.ts:24:13 +regby mod7.ts:1:1 mod7.ts:9:21 GET /m7/res5/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:100:23 POST /m8/res96/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:101:22 PUT /m8/res97/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:102:25 DELETE /m8/res98/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:103:24 PATCH /m8/res99/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:10:22 POST /m8/res6/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:11:21 PUT /m8/res7/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:12:24 DELETE /m8/res8/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:13:23 PATCH /m8/res9/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:14:22 GET /m8/res10/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:15:23 POST /m8/res11/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:16:22 PUT /m8/res12/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:17:25 DELETE /m8/res13/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:18:24 PATCH /m8/res14/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:19:22 GET /m8/res15/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:20:23 POST /m8/res16/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:21:22 PUT /m8/res17/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:22:25 DELETE /m8/res18/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:23:24 PATCH /m8/res19/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:24:22 GET /m8/res20/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:25:23 POST /m8/res21/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:26:22 PUT /m8/res22/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:27:25 DELETE /m8/res23/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:28:24 PATCH /m8/res24/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:29:22 GET /m8/res25/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:30:23 POST /m8/res26/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:31:22 PUT /m8/res27/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:32:25 DELETE /m8/res28/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:33:24 PATCH /m8/res29/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:34:22 GET /m8/res30/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:35:23 POST /m8/res31/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:36:22 PUT /m8/res32/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:37:25 DELETE /m8/res33/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:38:24 PATCH /m8/res34/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:39:22 GET /m8/res35/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:40:23 POST /m8/res36/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:41:22 PUT /m8/res37/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:42:25 DELETE /m8/res38/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:43:24 PATCH /m8/res39/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:44:22 GET /m8/res40/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:45:23 POST /m8/res41/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:46:22 PUT /m8/res42/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:47:25 DELETE /m8/res43/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:48:24 PATCH /m8/res44/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:49:22 GET /m8/res45/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:4:21 GET /m8/res0/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:50:23 POST /m8/res46/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:51:22 PUT /m8/res47/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:52:25 DELETE /m8/res48/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:53:24 PATCH /m8/res49/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:54:22 GET /m8/res50/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:55:23 POST /m8/res51/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:56:22 PUT /m8/res52/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:57:25 DELETE /m8/res53/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:58:24 PATCH /m8/res54/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:59:22 GET /m8/res55/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:5:22 POST /m8/res1/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:60:23 POST /m8/res56/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:61:22 PUT /m8/res57/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:62:25 DELETE /m8/res58/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:63:24 PATCH /m8/res59/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:64:22 GET /m8/res60/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:65:23 POST /m8/res61/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:66:22 PUT /m8/res62/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:67:25 DELETE /m8/res63/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:68:24 PATCH /m8/res64/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:69:22 GET /m8/res65/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:6:21 PUT /m8/res2/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:70:23 POST /m8/res66/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:71:22 PUT /m8/res67/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:72:25 DELETE /m8/res68/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:73:24 PATCH /m8/res69/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:74:22 GET /m8/res70/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:75:23 POST /m8/res71/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:76:22 PUT /m8/res72/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:77:25 DELETE /m8/res73/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:78:24 PATCH /m8/res74/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:79:22 GET /m8/res75/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:7:24 DELETE /m8/res3/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:80:23 POST /m8/res76/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:81:22 PUT /m8/res77/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:82:25 DELETE /m8/res78/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:83:24 PATCH /m8/res79/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:84:22 GET /m8/res80/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:85:23 POST /m8/res81/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:86:22 PUT /m8/res82/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:87:25 DELETE /m8/res83/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:88:24 PATCH /m8/res84/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:89:22 GET /m8/res85/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:8:23 PATCH /m8/res4/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:90:23 POST /m8/res86/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:91:22 PUT /m8/res87/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:92:25 DELETE /m8/res88/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:93:24 PATCH /m8/res89/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:94:22 GET /m8/res90/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:95:23 POST /m8/res91/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:96:22 PUT /m8/res92/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:97:25 DELETE /m8/res93/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:98:24 PATCH /m8/res94/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:99:22 GET /m8/res95/:id index.ts:24:13 +regby mod8.ts:1:1 mod8.ts:9:21 GET /m8/res5/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:100:23 POST /m9/res96/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:101:22 PUT /m9/res97/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:102:25 DELETE /m9/res98/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:103:24 PATCH /m9/res99/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:10:22 POST /m9/res6/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:11:21 PUT /m9/res7/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:12:24 DELETE /m9/res8/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:13:23 PATCH /m9/res9/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:14:22 GET /m9/res10/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:15:23 POST /m9/res11/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:16:22 PUT /m9/res12/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:17:25 DELETE /m9/res13/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:18:24 PATCH /m9/res14/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:19:22 GET /m9/res15/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:20:23 POST /m9/res16/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:21:22 PUT /m9/res17/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:22:25 DELETE /m9/res18/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:23:24 PATCH /m9/res19/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:24:22 GET /m9/res20/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:25:23 POST /m9/res21/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:26:22 PUT /m9/res22/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:27:25 DELETE /m9/res23/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:28:24 PATCH /m9/res24/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:29:22 GET /m9/res25/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:30:23 POST /m9/res26/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:31:22 PUT /m9/res27/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:32:25 DELETE /m9/res28/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:33:24 PATCH /m9/res29/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:34:22 GET /m9/res30/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:35:23 POST /m9/res31/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:36:22 PUT /m9/res32/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:37:25 DELETE /m9/res33/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:38:24 PATCH /m9/res34/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:39:22 GET /m9/res35/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:40:23 POST /m9/res36/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:41:22 PUT /m9/res37/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:42:25 DELETE /m9/res38/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:43:24 PATCH /m9/res39/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:44:22 GET /m9/res40/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:45:23 POST /m9/res41/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:46:22 PUT /m9/res42/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:47:25 DELETE /m9/res43/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:48:24 PATCH /m9/res44/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:49:22 GET /m9/res45/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:4:21 GET /m9/res0/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:50:23 POST /m9/res46/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:51:22 PUT /m9/res47/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:52:25 DELETE /m9/res48/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:53:24 PATCH /m9/res49/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:54:22 GET /m9/res50/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:55:23 POST /m9/res51/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:56:22 PUT /m9/res52/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:57:25 DELETE /m9/res53/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:58:24 PATCH /m9/res54/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:59:22 GET /m9/res55/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:5:22 POST /m9/res1/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:60:23 POST /m9/res56/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:61:22 PUT /m9/res57/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:62:25 DELETE /m9/res58/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:63:24 PATCH /m9/res59/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:64:22 GET /m9/res60/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:65:23 POST /m9/res61/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:66:22 PUT /m9/res62/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:67:25 DELETE /m9/res63/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:68:24 PATCH /m9/res64/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:69:22 GET /m9/res65/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:6:21 PUT /m9/res2/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:70:23 POST /m9/res66/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:71:22 PUT /m9/res67/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:72:25 DELETE /m9/res68/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:73:24 PATCH /m9/res69/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:74:22 GET /m9/res70/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:75:23 POST /m9/res71/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:76:22 PUT /m9/res72/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:77:25 DELETE /m9/res73/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:78:24 PATCH /m9/res74/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:79:22 GET /m9/res75/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:7:24 DELETE /m9/res3/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:80:23 POST /m9/res76/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:81:22 PUT /m9/res77/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:82:25 DELETE /m9/res78/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:83:24 PATCH /m9/res79/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:84:22 GET /m9/res80/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:85:23 POST /m9/res81/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:86:22 PUT /m9/res82/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:87:25 DELETE /m9/res83/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:88:24 PATCH /m9/res84/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:89:22 GET /m9/res85/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:8:23 PATCH /m9/res4/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:90:23 POST /m9/res86/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:91:22 PUT /m9/res87/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:92:25 DELETE /m9/res88/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:93:24 PATCH /m9/res89/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:94:22 GET /m9/res90/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:95:23 POST /m9/res91/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:96:22 PUT /m9/res92/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:97:25 DELETE /m9/res93/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:98:24 PATCH /m9/res94/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:99:22 GET /m9/res95/:id index.ts:24:13 +regby mod9.ts:1:1 mod9.ts:9:21 GET /m9/res5/:id index.ts:24:13 diff --git a/graph/test/typescript/hono/expected/zod/composition.entries b/graph/test/typescript/hono/expected/zod/composition.entries new file mode 100644 index 000000000..a0aa82659 --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/composition.entries @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: each derived schema plus Base (and Extra for merge); .openapi('Person') keeps the same schema with Base as base +# census: zvalidator (SPEC 3.9 composition) +entry index.ts:19:53 http +entry index.ts:20:49 http +entry index.ts:21:49 http +entry index.ts:22:51 http +entry index.ts:23:52 http +entry index.ts:24:44 http +entry index.ts:25:47 http diff --git a/graph/test/typescript/hono/expected/zod/composition.tsv b/graph/test/typescript/hono/expected/zod/composition.tsv new file mode 100644 index 000000000..648a316d8 --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/composition.tsv @@ -0,0 +1,47 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: each derived schema plus Base (and Extra for merge); .openapi('Person') keeps the same schema with Base as base +# census: zvalidator (SPEC 3.9 composition) +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /extended index.ts:19:53 index.ts:18:13 1 +route POST /merged index.ts:20:49 index.ts:18:13 3 +route POST /picked index.ts:21:49 index.ts:18:13 5 +route POST /omitted index.ts:22:51 index.ts:18:13 7 +route PATCH /partial index.ts:23:52 index.ts:18:13 9 +route PUT /many index.ts:24:44 index.ts:18:13 11 +route POST /named index.ts:25:47 index.ts:18:13 13 +mw lib:@hono/zod-validator.zValidator@index.ts:19:23 index.ts:19:53 POST /extended 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:20:21 index.ts:20:49 POST /merged 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:21:21 index.ts:21:49 POST /picked 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:22:22 index.ts:22:51 POST /omitted 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:23:23 index.ts:23:52 PATCH /partial 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:24:18 index.ts:24:44 PUT /many 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:25:20 index.ts:25:47 POST /named 0 index.ts:18:13 +schema index.ts:19:53 json index.ts:10:7 +schema index.ts:19:53 json index.ts:8:7 +schema index.ts:20:49 json index.ts:11:7 +schema index.ts:20:49 json index.ts:8:7 +schema index.ts:20:49 json index.ts:9:7 +schema index.ts:21:49 json index.ts:12:7 +schema index.ts:21:49 json index.ts:8:7 +schema index.ts:22:51 json index.ts:13:7 +schema index.ts:22:51 json index.ts:8:7 +schema index.ts:23:52 json index.ts:14:7 +schema index.ts:23:52 json index.ts:8:7 +schema index.ts:24:44 json index.ts:15:7 +schema index.ts:24:44 json index.ts:8:7 +schema index.ts:25:47 json index.ts:16:7 +schema index.ts:25:47 json index.ts:8:7 +validread index.ts:19:67 index.ts:19:53 json index.ts:10:7 +validread index.ts:20:63 index.ts:20:49 json index.ts:11:7 +validread index.ts:21:63 index.ts:21:49 json index.ts:12:7 +validread index.ts:22:65 index.ts:22:51 json index.ts:13:7 +validread index.ts:23:66 index.ts:23:52 json index.ts:14:7 +validread index.ts:24:58 index.ts:24:44 json index.ts:15:7 +validread index.ts:25:61 index.ts:25:47 json index.ts:16:7 +regby index.ts:1:1 index.ts:19:53 POST /extended index.ts:18:13 +regby index.ts:1:1 index.ts:20:49 POST /merged index.ts:18:13 +regby index.ts:1:1 index.ts:21:49 POST /picked index.ts:18:13 +regby index.ts:1:1 index.ts:22:51 POST /omitted index.ts:18:13 +regby index.ts:1:1 index.ts:23:52 PATCH /partial index.ts:18:13 +regby index.ts:1:1 index.ts:24:44 PUT /many index.ts:18:13 +regby index.ts:1:1 index.ts:25:47 POST /named index.ts:18:13 diff --git a/graph/test/typescript/hono/expected/zod/decl-forms.entries b/graph/test/typescript/hono/expected/zod/decl-forms.entries new file mode 100644 index 000000000..aedb74df0 --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/decl-forms.entries @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: inline (expression), local, named, default, namespace member and barrel re-export each resolve to the declaration +# census: zvalidator, req_valid (SPEC 3.9 declaration forms) +entry index.ts:14:70 http +entry index.ts:15:47 http +entry index.ts:16:46 http +entry index.ts:17:52 http +entry index.ts:18:54 http +entry index.ts:19:53 http diff --git a/graph/test/typescript/hono/expected/zod/decl-forms.tsv b/graph/test/typescript/hono/expected/zod/decl-forms.tsv new file mode 100644 index 000000000..1af3dc2db --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/decl-forms.tsv @@ -0,0 +1,34 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: inline (expression), local, named, default, namespace member and barrel re-export each resolve to the declaration +# census: zvalidator, req_valid (SPEC 3.9 declaration forms) +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /inline index.ts:14:70 index.ts:13:13 1 +route POST /local index.ts:15:47 index.ts:13:13 3 +route POST /named index.ts:16:46 index.ts:13:13 5 +route POST /default index.ts:17:52 index.ts:13:13 7 +route GET /namespace index.ts:18:54 index.ts:13:13 9 +route POST /barrel index.ts:19:53 index.ts:13:13 11 +mw lib:@hono/zod-validator.zValidator@index.ts:14:21 index.ts:14:70 POST /inline 0 index.ts:13:13 +mw lib:@hono/zod-validator.zValidator@index.ts:15:20 index.ts:15:47 POST /local 0 index.ts:13:13 +mw lib:@hono/zod-validator.zValidator@index.ts:16:20 index.ts:16:46 POST /named 0 index.ts:13:13 +mw lib:@hono/zod-validator.zValidator@index.ts:17:22 index.ts:17:52 POST /default 0 index.ts:13:13 +mw lib:@hono/zod-validator.zValidator@index.ts:18:23 index.ts:18:54 GET /namespace 0 index.ts:13:13 +mw lib:@hono/zod-validator.zValidator@index.ts:19:21 index.ts:19:53 POST /barrel 0 index.ts:13:13 +schema index.ts:14:70 json expr@index.ts:14:40 +schema index.ts:15:47 json index.ts:11:7 +schema index.ts:16:46 json schemas/user.ts:3:14 +schema index.ts:17:52 json schemas/user.ts:5:7 +schema index.ts:18:54 query schemas/query.ts:3:14 +schema index.ts:19:53 json schemas/user.ts:3:14 +validread index.ts:14:84 index.ts:14:70 json expr@index.ts:14:40 +validread index.ts:15:61 index.ts:15:47 json index.ts:11:7 +validread index.ts:16:60 index.ts:16:46 json schemas/user.ts:3:14 +validread index.ts:17:66 index.ts:17:52 json schemas/user.ts:5:7 +validread index.ts:18:68 index.ts:18:54 query schemas/query.ts:3:14 +validread index.ts:19:67 index.ts:19:53 json schemas/user.ts:3:14 +regby index.ts:1:1 index.ts:14:70 POST /inline index.ts:13:13 +regby index.ts:1:1 index.ts:15:47 POST /local index.ts:13:13 +regby index.ts:1:1 index.ts:16:46 POST /named index.ts:13:13 +regby index.ts:1:1 index.ts:17:52 POST /default index.ts:13:13 +regby index.ts:1:1 index.ts:18:54 GET /namespace index.ts:13:13 +regby index.ts:1:1 index.ts:19:53 POST /barrel index.ts:13:13 diff --git a/graph/test/typescript/hono/expected/zod/no-route.entries b/graph/test/typescript/hono/expected/zod/no-route.entries new file mode 100644 index 000000000..4bd1b866e --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/no-route.entries @@ -0,0 +1,2 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: zod with no Hono app: zero rows; route table and route_schema must still be exported +# census: zvalidator (negative partition: zod with no Hono route, SPEC 3.9) diff --git a/graph/test/typescript/hono/expected/zod/no-route.tsv b/graph/test/typescript/hono/expected/zod/no-route.tsv new file mode 100644 index 000000000..a478d1df3 --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/no-route.tsv @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: zod with no Hono app: zero rows; route table and route_schema must still be exported +# census: zvalidator (negative partition: zod with no Hono route, SPEC 3.9) +absent forms.ts:7:1 +absent forms.ts:12:1 diff --git a/graph/test/typescript/hono/expected/zod/specifiers.entries b/graph/test/typescript/hono/expected/zod/specifiers.entries new file mode 100644 index 000000000..d322a7fc0 --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/specifiers.entries @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: z from zod, zod/v4, zod/v3, zod/v4-mini and @hono/zod-openapi: each route's json schema is its const; a validator(fn) route has no schema row +# census: zvalidator, req_valid (SPEC 3.9 import forms: zod, zod/v4, zod/v3, zod/v4-mini, @hono/zod-openapi's z) +entry index.ts:19:47 http +entry index.ts:20:45 http +entry index.ts:21:45 http +entry index.ts:22:49 http +entry index.ts:23:66 http +entry index.ts:24:57 http diff --git a/graph/test/typescript/hono/expected/zod/specifiers.tsv b/graph/test/typescript/hono/expected/zod/specifiers.tsv new file mode 100644 index 000000000..cde130fec --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/specifiers.tsv @@ -0,0 +1,32 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: z from zod, zod/v4, zod/v3, zod/v4-mini and @hono/zod-openapi: each route's json schema is its const; a validator(fn) route has no schema row +# census: zvalidator, req_valid (SPEC 3.9 import forms: zod, zod/v4, zod/v3, zod/v4-mini, @hono/zod-openapi's z) +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /zod index.ts:19:47 index.ts:18:13 1 +route POST /v4 index.ts:20:45 index.ts:18:13 3 +route POST /v3 index.ts:21:45 index.ts:18:13 5 +route POST /mini index.ts:22:49 index.ts:18:13 7 +route POST /fn index.ts:23:66 index.ts:18:13 9 +route POST /openapi-z index.ts:24:57 index.ts:18:13 11 +mw lib:@hono/zod-validator.zValidator@index.ts:19:18 index.ts:19:47 POST /zod 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:20:17 index.ts:20:45 POST /v4 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:21:17 index.ts:21:45 POST /v3 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:22:19 index.ts:22:49 POST /mini 0 index.ts:18:13 +mw lib:hono/validator.validator@index.ts:23:17 index.ts:23:66 POST /fn 0 index.ts:18:13 +mw lib:@hono/zod-validator.zValidator@index.ts:24:24 index.ts:24:57 POST /openapi-z 0 index.ts:18:13 +schema index.ts:19:47 json index.ts:12:7 +schema index.ts:20:45 json index.ts:13:7 +schema index.ts:21:45 json index.ts:14:7 +schema index.ts:22:49 json index.ts:15:7 +schema index.ts:24:57 json index.ts:16:7 +validread index.ts:19:61 index.ts:19:47 json index.ts:12:7 +validread index.ts:20:59 index.ts:20:45 json index.ts:13:7 +validread index.ts:21:59 index.ts:21:45 json index.ts:14:7 +validread index.ts:22:63 index.ts:22:49 json index.ts:15:7 +validread index.ts:24:71 index.ts:24:57 json index.ts:16:7 +regby index.ts:1:1 index.ts:19:47 POST /zod index.ts:18:13 +regby index.ts:1:1 index.ts:20:45 POST /v4 index.ts:18:13 +regby index.ts:1:1 index.ts:21:45 POST /v3 index.ts:18:13 +regby index.ts:1:1 index.ts:22:49 POST /mini index.ts:18:13 +regby index.ts:1:1 index.ts:23:66 POST /fn index.ts:18:13 +regby index.ts:1:1 index.ts:24:57 POST /openapi-z index.ts:18:13 diff --git a/graph/test/typescript/hono/expected/zod/valid-targets.entries b/graph/test/typescript/hono/expected/zod/valid-targets.entries new file mode 100644 index 000000000..2c7d29e32 --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/valid-targets.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: six targets each read and linked; valid('json') on /missing is valid_without_validator +# census: req_valid, zvalidator, validator_builtin (SPEC 3.9 targets) +entry index.ts:24:3 http +entry index.ts:29:45 http diff --git a/graph/test/typescript/hono/expected/zod/valid-targets.tsv b/graph/test/typescript/hono/expected/zod/valid-targets.tsv new file mode 100644 index 000000000..c5182b7c7 --- /dev/null +++ b/graph/test/typescript/hono/expected/zod/valid-targets.tsv @@ -0,0 +1,30 @@ +# hand-reviewed 2026-10-24 against the fixture and the oracle: six targets each read and linked; valid('json') on /missing is valid_without_validator +# census: req_valid, zvalidator, validator_builtin (SPEC 3.9 targets) +# iteration 2: mw rows carry the served root app (SPEC R3), re-derived; every other row unchanged +# iteration 2: regby rows (route_registered_by, SPEC 4) re-derived; every other row unchanged +route POST /all/:id index.ts:24:3 index.ts:15:13 6 +route GET /missing index.ts:29:45 index.ts:15:13 8 +mw lib:@hono/zod-validator.zValidator@index.ts:18:3 index.ts:24:3 POST /all/:id 0 index.ts:15:13 +mw lib:@hono/zod-validator.zValidator@index.ts:19:3 index.ts:24:3 POST /all/:id 1 index.ts:15:13 +mw lib:@hono/zod-validator.zValidator@index.ts:20:3 index.ts:24:3 POST /all/:id 2 index.ts:15:13 +mw lib:@hono/zod-validator.zValidator@index.ts:21:3 index.ts:24:3 POST /all/:id 3 index.ts:15:13 +mw lib:@hono/zod-validator.zValidator@index.ts:22:3 index.ts:24:3 POST /all/:id 4 index.ts:15:13 +mw lib:@hono/zod-validator.zValidator@index.ts:23:3 index.ts:24:3 POST /all/:id 5 index.ts:15:13 +mw lib:@hono/zod-validator.zValidator@index.ts:29:21 index.ts:29:45 GET /missing 0 index.ts:15:13 +schema index.ts:24:3 cookie index.ts:13:7 +schema index.ts:24:3 form index.ts:11:7 +schema index.ts:24:3 header index.ts:12:7 +schema index.ts:24:3 json index.ts:8:7 +schema index.ts:24:3 param index.ts:10:7 +schema index.ts:24:3 query index.ts:9:7 +schema index.ts:29:45 query index.ts:9:7 +validread index.ts:25:104 index.ts:24:3 header index.ts:12:7 +validread index.ts:25:127 index.ts:24:3 cookie index.ts:13:7 +validread index.ts:25:18 index.ts:24:3 json index.ts:8:7 +validread index.ts:25:39 index.ts:24:3 query index.ts:9:7 +validread index.ts:25:61 index.ts:24:3 param index.ts:10:7 +validread index.ts:25:83 index.ts:24:3 form index.ts:11:7 +validread index.ts:32:18 index.ts:29:45 query index.ts:9:7 +regby index.ts:1:1 index.ts:24:3 POST /all/:id index.ts:15:13 +regby index.ts:1:1 index.ts:29:45 GET /missing index.ts:15:13 +unknown index.ts:31:16 valid_without_validator index.ts:29:45 diff --git a/graph/test/typescript/hono/relations.tsv b/graph/test/typescript/hono/relations.tsv new file mode 100644 index 000000000..628b62c83 --- /dev/null +++ b/graph/test/typescript/hono/relations.tsv @@ -0,0 +1,22 @@ +# projection engine relation (export_manifest.tsv name) column=index ... +# The ONE place the suite names the engine's relations and their columns: SPEC.md section 4. +# A projection fed by several relations lists one line per relation (rows are unioned). +route http_route handler=0 verb=1 path=2 app=3 order=4 +partial http_route_partial handler=0 verb=1 path=2 app=3 reason=4 +submount http_mount parent=0 child=1 prefix=2 order=3 +mw middleware_applies mw=0 handler=1 verb=2 path=3 position=4 app=5 +ctx ctx_key_flow setter=0 reader=1 key=2 +remote remote_edge from=0 to=1 transport=2 dest=3 conf=4 +unserved remote_unserved fn=0 transport=1 dest=2 +entry entry_point fn=0 kind=1 +unknown remote_undetermined fn=0 transport=1 reason=2 +unknown framework_undetermined fn=0 what=1 reason=2 +schema route_schema handler=0 target=1 decl=2 +validread valid_read read=0 handler=1 target=2 decl=3 +regby route_registered_by regmethod=0 handler=1 verb=2 path=3 app=4 +# chains/ suite (SPEC R18-R21): call edges, the dispatch widening of a declared base, function-value calls, and the +# functions holding a call the engine declares unresolved (compare_chains in tools/hono_compare.py) +chainedge call_chain_edge from=1 to=3 prov=4 +dispatch method_dispatch_candidate base=0 impl=1 +fnvalue fn_value_call from=0 to=1 +ccunknown method_has_unknown_call fn=0 diff --git a/graph/test/typescript/hono/run-tests.sh b/graph/test/typescript/hono/run-tests.sh new file mode 100755 index 000000000..6c360a019 --- /dev/null +++ b/graph/test/typescript/hono/run-tests.sh @@ -0,0 +1,225 @@ +#!/usr/bin/env bash +# ───────────────────────────────────────────────────────────────────────────── +# axiom-code-graph: Hono torture suites (TypeScript; the JavaScript twin sources this runner) +# +# For every case cases///src: +# 1. the expected truth must exist and say it was REVIEWED: expected//.tsv and +# expected//.entries, each starting `# hand-reviewed`. A missing or unreviewed +# golden FAILS: it never skips. +# 2. with --oracle: the independent oracle (truth/, the TypeScript compiler API plus the real +# pinned `hono`) must reproduce the expected rows exactly, and its runtime half (the fixture +# run under the real Hono, `app.routes` read back) must agree with the static half wherever +# the fixture loads. A refusal or a disagreement FAILS. +# 3. the engine runs TWICE: `bare` (no library: a real checkout without node_modules) and `lib` +# (the pinned hono type declarations staged as a library). Each variant's Hono relations are +# normalised and compared row by row (tools/hono_compare.py). Until the engine exports the +# relations SPEC.md section 4 names, the case FAILS with "relation not exported". +# 4. at least ONE row is compared, or the case fails: an empty golden is not a pass. +# +# ./run-tests.sh [filter...] cases whose / contains a filter +# ./run-tests.sh --oracle also check expected/ against the oracle (needs truth/node_modules: npm install there) +# ./run-tests.sh --oracle-only only step 2 (no parser, no solver): validates the goldens +# ./run-tests.sh --draft write the oracle's rows to expected/*.draft for REVIEW (never to the golden) +# ./run-tests.sh --keep keep .work/ +# +# expected//.known-missing: `\t` per line (the row as the +# compare prints it after MISSING). A new miss fails; a listed miss that starts passing fails. +# +# Environment: AXIOM_PARSER (default parser/dist/index.js), HONO_ENGINE_ROOT (score another checkout's engine), +# HONO_LANG (typescript|javascript), +# HONO_SUITE_DIR (the suite dir holding cases/ and expected/; default: this dir) +# ───────────────────────────────────────────────────────────────────────────── +set -uo pipefail +TS_HONO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +HERE="${HONO_SUITE_DIR:-$TS_HONO}" +LANG_="${HONO_LANG:-typescript}" +ROOT="$TS_HONO"; while [ "$ROOT" != / ] && { [ ! -f "$ROOT/package.json" ] || [ ! -d "$ROOT/graph" ]; }; do ROOT="$(dirname "$ROOT")"; done +# HONO_ENGINE_ROOT: score ANOTHER checkout's parser and engine (e.g. the implementation branch's worktree) +# with this checkout's cases, goldens and oracle +ENGINE_ROOT="${HONO_ENGINE_ROOT:-$ROOT}" +PARSER="${AXIOM_PARSER:-$ENGINE_ROOT/parser/dist/index.js}" +# the baseline engine (pre-Hono): HONO_BASELINE_ROOT, else a wt-hono-baseline sibling of this checkout, else this tree +BASELINE_ROOT="${HONO_BASELINE_ROOT:-}" +if [ -z "$BASELINE_ROOT" ]; then + for cand in "$(cd "$ROOT/.." && pwd)/wt-hono-baseline"; do + [ -f "$cand/graph/pipeline/run-souffle.sh" ] && { BASELINE_ROOT="$cand"; break; } + done + [ -n "$BASELINE_ROOT" ] || BASELINE_ROOT="$ROOT" +fi +echo "baseline engine for pre-existing entry points: $BASELINE_ROOT" +ORACLE_DIR="$TS_HONO/truth" # NOT `oracle/`: the repository .gitignore ignores every dir of that name +WORK="$HERE/.work" +ORACLE=0; ORACLE_ONLY=0; DRAFT=0; KEEP=0; FILTERS=() +for a in "$@"; do case "$a" in + --oracle) ORACLE=1;; --oracle-only) ORACLE=1; ORACLE_ONLY=1;; --draft) ORACLE=1; ORACLE_ONLY=1; DRAFT=1;; + --keep) KEEP=1;; -h|--help) sed -n '2,30p' "$0"; exit 0;; *) FILTERS+=("$a");; esac; done + +if [ "$ORACLE" = 1 ] && [ ! -d "$ORACLE_DIR/node_modules/hono" ]; then + echo "FAIL: the oracle is not installed: (cd $ORACLE_DIR && npm install): refusing to score without it"; exit 1 +fi +[ "$ORACLE_ONLY" = 1 ] || [ -f "$PARSER" ] || { echo "FAIL: parser not found at $PARSER (build it: npm install)"; exit 1; } +MANIFEST="$ENGINE_ROOT/graph/$LANG_/souffle/export_manifest.tsv" +mkdir -p "$WORK" +EMPTY_LIB="$WORK/.empty-library"; mkdir -p "$EMPTY_LIB" + +# the `lib` variant's library: the pinned packages' TYPE declarations, parsed once per run +LIBIR="$WORK/.hono-lib-ir" +stage_lib() { + [ -s "$LIBIR/all-typescript-modules.csv" ] && return 0 + local src="$WORK/.hono-lib-src" p + rm -rf "$src" "$LIBIR"; mkdir -p "$src" "$LIBIR" + [ -d "$ORACLE_DIR/node_modules/hono" ] || { echo " (lib variant needs truth/node_modules: npm install there)"; return 1; } + for p in hono @hono/zod-openapi @hono/zod-validator @hono/node-server; do + local d="$ORACLE_DIR/node_modules/$p"; [ -d "$d" ] || continue + mkdir -p "$src/$p"; cp "$d/package.json" "$src/$p/" + if [ -d "$d/dist/types" ]; then cp -R "$d/dist/types/." "$src/$p/" + else (cd "$d/dist" && find . -name '*.d.ts' -o -name '*.d.mts' | while read -r f; do mkdir -p "$src/$p/$(dirname "$f")"; cp "$f" "$src/$p/$f"; done); fi + done + node "$PARSER" "$src" "hono-lib" false "$LIBIR" >"$WORK/.hono-lib.log" 2>&1 + [ -s "$LIBIR/all-typescript-modules.csv" ] +} + +solve() { # ir lib out (writes $3/rss_kb: the peak resident set of the solve, from /usr/bin/time -l) + local rc + if [ -x /usr/bin/time ]; then + /usr/bin/time -l bash "$ENGINE_ROOT/graph/pipeline/run-souffle.sh" --debug --language "$LANG_" \ + --client-ir "$1" --library "$2" --intermediate "$3/int" --output "$3/out" >"$3/solve.log" 2>"$3/solve.err"; rc=$? + cat "$3/solve.err" >>"$3/solve.log" + # macOS reports bytes, GNU time kilobytes: normalise to KB + awk -v div="$([ "$(uname)" = Darwin ] && echo 1024 || echo 1)" '/maximum resident set size/{b=$1} END{if(b!="") print int(b/div)}' "$3/solve.err" >"$3/rss_kb" + return $rc + fi + bash "$ENGINE_ROOT/graph/pipeline/run-souffle.sh" --debug --language "$LANG_" \ + --client-ir "$1" --library "$2" --intermediate "$3/int" --output "$3/out" >"$3/solve.log" 2>&1 +} + +rows_of() { grep -v '^#' "$1" | grep -v '^absent' | grep -v '^$' | sort; } + +pass=0; fail=0; failed=(); notexported=0 +for dir in "$HERE"/cases/*/*/; do + dir="${dir%/}"; suite="$(basename "$(dirname "$dir")")"; name="$(basename "$dir")"; id="$suite/$name" + if [ ${#FILTERS[@]} -gt 0 ]; then + m=0; for f in "${FILTERS[@]}"; do [[ "$id" == *"$f"* ]] && m=1; done; [ $m = 1 ] || continue + fi + printf '%-44s ' "$id" + exp="$HERE/expected/$suite/$name.tsv"; ent="$HERE/expected/$suite/$name.entries"; km="$HERE/expected/$suite/$name.known-missing" + # chains/ (SPEC R18-R21): a third golden of whole call chains, checked against the compiler oracle and the runtime trace + chf="$HERE/expected/$suite/$name.chains"; chargs=() + w="$WORK/$suite/$name"; rm -rf "$w"; mkdir -p "$w" + ok=1; notes="" + + if [ "$DRAFT" = 1 ]; then + mkdir -p "$(dirname "$exp")" + if node "$ORACLE_DIR/hono-oracle.mjs" "$dir/src" --stores >"$w/oracle.full" 2>"$w/oracle.log"; then + rt="$(node "$ORACLE_DIR/runtime-check.mjs" "$dir/src" "$w/oracle.full" 2>&1 | head -6)" + grep -v '^store' "$w/oracle.full" | grep -v '^entry' >"$exp.draft" + grep '^entry' "$w/oracle.full" >"$ent.draft" + if [ "$suite" = chains ]; then + node "$ORACLE_DIR/cc-oracle.mjs" "$dir/src" >"$w/cc.tsv" 2>"$w/cc.log" + node "$ORACLE_DIR/chain-runtime.mjs" "$dir/src" /dev/null --edges --routes "$w/oracle.full" --requests "$chf" >"$w/rt.tsv" 2>&1 + rt="$rt; $(python3 "$TS_HONO/tools/chain_check.py" --cc "$w/cc.tsv" --hono "$w/oracle.full" --rt "$w/rt.tsv" --draft "$chf.draft")" + fi + echo "drafted ($rt)"; pass=$((pass+1)) + else echo "FAIL (oracle refused)"; sed 's/^/ /' "$w/oracle.log" | head -4; fail=$((fail+1)); failed+=("$id"); fi + continue + fi + + golds=("$exp" "$ent"); [ "$suite" = chains ] && golds+=("$chf") + [ -f "$chf" ] && chargs=(--chains "$chf") + for f in "${golds[@]}"; do + if [ ! -f "$f" ]; then echo "FAIL (no golden: ${f#$HERE/}: draft with --draft, then REVIEW)"; ok=0; break; fi + if ! head -1 "$f" | grep -q '^# hand-reviewed'; then echo "FAIL (golden not reviewed: ${f#$HERE/} lacks the '# hand-reviewed' header)"; ok=0; break; fi + done + [ $ok = 1 ] || { fail=$((fail+1)); failed+=("$id"); continue; } + + if [ "$ORACLE" = 1 ]; then + if node "$ORACLE_DIR/hono-oracle.mjs" "$dir/src" --stores >"$w/oracle.full" 2>"$w/oracle.log"; then + grep -v '^store' "$w/oracle.full" | sort >"$w/oracle.rows" + cat "$exp" "$ent" | rows_of /dev/stdin >"$w/expected.rows" + if grep -q 'ORACLE_UNDECIDED' "$w/oracle.rows"; then echo "FAIL (the oracle could not decide a chain: extend it, do not hand-write)"; ok=0; fi + if ! diff -q "$w/expected.rows" "$w/oracle.rows" >/dev/null; then + echo "FAIL (expected differs from the oracle)"; diff "$w/expected.rows" "$w/oracle.rows" | sed 's/^/ /' | head -20; ok=0 + fi + # an `absent` row on a location that is no function can never fail: refuse it here, engine or not + node "$TS_HONO/tools/fn-locs.mjs" "$dir/src" | cut -f1 >"$w/fns" + for a in $(grep '^absent' "$exp" | cut -f2); do grep -qxF "$a" "$w/fns" || { echo "FAIL (absent row $a names no function in the fixture)"; ok=0; }; done + rt="$(node "$ORACLE_DIR/runtime-check.mjs" "$dir/src" "$w/oracle.full" 2>&1)"; rc=$? + if [ $rc -ne 0 ]; then echo "FAIL (runtime oracle disagrees with the static oracle)"; echo "$rt" | sed 's/^/ /' | head -8; ok=0; fi + notes="[$(echo "$rt" | head -1)]" + if [ -f "$chf" ]; then + if ! node "$ORACLE_DIR/cc-oracle.mjs" "$dir/src" >"$w/cc.tsv" 2>"$w/cc.log"; then echo "FAIL (call-chain oracle refused)"; head -3 "$w/cc.log" | sed 's/^/ /'; ok=0 + else + node "$ORACLE_DIR/chain-runtime.mjs" "$dir/src" /dev/null --edges --routes "$w/oracle.full" --requests "$chf" >"$w/rt.tsv" 2>&1 + if grep -q '^runtime skip' "$w/rt.tsv"; then echo "FAIL (chains need the runtime trace: $(grep '^runtime skip' "$w/rt.tsv"))"; ok=0; fi + if ! python3 "$TS_HONO/tools/chain_check.py" --cc "$w/cc.tsv" --hono "$w/oracle.full" --rt "$w/rt.tsv" --golden "$chf" >"$w/chaincheck.txt"; then + echo "FAIL (chains golden differs from the oracles)"; sed 's/^/ /' "$w/chaincheck.txt" | head -12; ok=0; fi + crt="$(node "$ORACLE_DIR/chain-runtime.mjs" "$dir/src" "$chf" 2>&1)" || { echo "FAIL (a chain hop was not observed at run time)"; echo "$crt" | sed 's/^/ /' | head -8; ok=0; } + notes="$notes [$(head -1 "$w/chaincheck.txt"); $(echo "$crt" | tail -1)]" + fi + fi + else + echo "FAIL (oracle refused: the case is unscored, not passing)"; sed 's/^/ /' "$w/oracle.log" | head -4; ok=0 + fi + if [ "$ORACLE_ONLY" = 1 ]; then + if [ $ok = 1 ]; then echo "oracle ok $notes"; pass=$((pass+1)); else fail=$((fail+1)); failed+=("$id"); fi + continue + fi + fi + + mkdir -p "$w/ir" + if ! node "$PARSER" "$dir/src" "$name" false "$w/ir" >"$w/parse.log" 2>&1; then + echo "FAIL (parse: see $w/parse.log)"; fail=$((fail+1)); failed+=("$id"); continue; fi + res="" + for variant in bare lib; do + v="$w/$variant"; mkdir -p "$v" + lib="$EMPTY_LIB" + if [ "$variant" = lib ]; then + if ! stage_lib; then res="$res lib:STAGING-FAILED"; ok=0; continue; fi + lib="$LIBIR" + fi + t0=$(date +%s) + if ! solve "$w/ir" "$lib" "$v"; then res="$res $variant:SOLVE-FAILED(see $v/solve.log)"; ok=0; continue; fi + dt=$(( $(date +%s) - t0 )) + # scale/ cases carry a wall-time budget (seconds) for the solve; exceeding it fails the case + # budget file: line 1 = seconds, optional `rss_mb N` = peak resident set of the solve + rss_mb=$(( $(cat "$v/rss_kb" 2>/dev/null || echo 0) / 1024 )) + if [ -f "$dir/budget" ]; then + tb="$(head -1 "$dir/budget")"; mb="$(sed -n 's/^rss_mb //p' "$dir/budget")" + if [ "$dt" -gt "$tb" ]; then res="$res $variant:OVER-BUDGET(${dt}s>${tb}s)"; ok=0; continue; fi + if [ -n "$mb" ] && [ ! -s "$v/rss_kb" ]; then res="$res $variant:RSS-UNMEASURED"; ok=0; continue; fi + if [ -n "$mb" ] && [ "$rss_mb" -gt "$mb" ]; then res="$res $variant:OVER-MEMORY(${rss_mb}MB>${mb}MB)"; ok=0; continue; fi + fi + python3 "$TS_HONO/tools/hono_compare.py" --lang "$LANG_" --ir "$w/ir" --raw "$v/out/raw" \ + --manifest "$MANIFEST" --relations "$TS_HONO/relations.tsv" --expected "$exp" --entries "$ent" \ + --known-missing "$km" --variant "$variant" ${chargs[@]+"${chargs[@]}"} >"$v/compare.txt" 2>&1; rc=$? + # an http entry_point row the engine adds that is not in the golden: if the BASELINE engine + # (HONO_BASELINE_ROOT, default the untouched baseline worktree) already emits it on the same IR, it is + # pre-existing receiver-blind behaviour SPEC keeps, not a Hono row (orchestrator ruling, iteration 1) + if [ $rc -eq 1 ] && grep -q '^EXTRA entry' "$v/compare.txt"; then + mkdir -p "$v/base" + if bash "$BASELINE_ROOT/graph/pipeline/run-souffle.sh" --debug --language "$LANG_" --client-ir "$w/ir" --library "$lib" \ + --intermediate "$v/base/int" --output "$v/base/out" >"$v/base/solve.log" 2>&1; then + python3 "$TS_HONO/tools/hono_compare.py" --lang "$LANG_" --ir "$w/ir" --raw "$v/out/raw" \ + --manifest "$MANIFEST" --relations "$TS_HONO/relations.tsv" --expected "$exp" --entries "$ent" \ + --known-missing "$km" --variant "$variant" ${chargs[@]+"${chargs[@]}"} --baseline-raw "$v/base/out/raw" >"$v/compare.txt" 2>&1; rc=$? + else res="$res $variant:BASELINE-SOLVE-FAILED"; ok=0; continue; fi + fi + n="$(sed -n 's/^compared=//p' "$v/compare.txt" | tail -1)" + if [ $rc -eq 3 ]; then res="$res $variant:$(head -1 "$v/compare.txt")"; ok=0; notexported=1; continue; fi + if [ $rc -eq 1 ]; then res="$res $variant:DIFF"; ok=0; continue; fi + if [ $rc -ne 0 ]; then res="$res $variant:COMPARE-CRASHED(see $v/compare.txt)"; ok=0; continue; fi + if [ "${n:-0}" -lt 1 ]; then res="$res $variant:COMPARED-0"; ok=0; continue; fi + res="$res $variant:ok($n,${dt}s,${rss_mb}MB)" + done + if [ $ok = 1 ]; then echo "ok$res $notes"; pass=$((pass+1)) + else + echo "FAIL$res" + for variant in bare lib; do [ -f "$w/$variant/compare.txt" ] && grep -E '^(MISSING|EXTRA|NOW-FIXED|PRESENT|CHAIN-BROKEN)' "$w/$variant/compare.txt" | head -12 | sed "s/^/ $variant /"; done + fail=$((fail+1)); failed+=("$id") + fi +done +[ "$KEEP" = 1 ] || rm -rf "$WORK"/*/ +echo +echo "passed $pass, failed $fail" +[ $((pass+fail)) -ge 1 ] || { echo "FAIL: no case ran (a filter matching nothing is not a pass)"; exit 1; } +[ $fail -eq 0 ] || { printf ' %s\n' "${failed[@]}"; exit 1; } diff --git a/graph/test/typescript/hono/tools/fn-locs.mjs b/graph/test/typescript/hono/tools/fn-locs.mjs new file mode 100644 index 000000000..9657178e7 --- /dev/null +++ b/graph/test/typescript/hono/tools/fn-locs.mjs @@ -0,0 +1,19 @@ +#!/usr/bin/env node +// List every function in a fixture with the location key the suite uses (`::`), +// for writing `absent` rows of a negative control by hand. usage: fn-locs.mjs +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +const { loadTypeScript } = await import(new URL('../../ground-truth/load-typescript.mjs', import.meta.url).href); +const ts = loadTypeScript(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'truth'), { toolName: 'fn-locs' }); +const src = path.resolve(process.argv[2]); +for (const f of fs.readdirSync(src, { recursive: true }).filter(f => /\.(m?[jt]sx?|cjs)$/.test(f)).sort()) { + const sf = ts.createSourceFile(f, fs.readFileSync(path.join(src, f), 'utf8'), ts.ScriptTarget.Latest, true); + (function v(n) { + if (ts.isFunctionLike(n) && n.body) { + const p = sf.getLineAndCharacterOfPosition(n.getStart(sf)); + console.log(`${f.split(path.sep).join('/')}:${p.line + 1}:${p.character + 1}\t${n.getText(sf).split('\n')[0].slice(0, 60)}`); + } + ts.forEachChild(n, v); + })(sf); +} diff --git a/graph/test/typescript/hono/tools/gen-scale.mjs b/graph/test/typescript/hono/tools/gen-scale.mjs new file mode 100644 index 000000000..a5c7022a0 --- /dev/null +++ b/graph/test/typescript/hono/tools/gen-scale.mjs @@ -0,0 +1,38 @@ +#!/usr/bin/env node +// Generate the scale/ fixtures (deterministic; re-run after editing, then --draft and review the counts). +// routes-2k : 20 modules x 100 routes, each module a sub-app mounted under its own prefix +// deep-mount: a chain of 10 sub-apps, each mounted inside the previous one +// mw-200 : 200 app-level middleware registered before 10 routes (2,000 applications) +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +const C = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'cases', 'scale'); +const w = (rel, txt) => { const f = path.join(C, rel); fs.mkdirSync(path.dirname(f), { recursive: true }); fs.writeFileSync(f, txt); }; +const VERBS = ['get', 'post', 'put', 'delete', 'patch']; + +// routes-2k +let idx = `// census: route_mount_literal, verb_literal_path (scale: 2,000 routes; generated by tools/gen-scale.mjs)\nimport { Hono } from 'hono'\n`; +for (let m = 0; m < 20; m++) { + let s = `import { Hono } from 'hono'\n\nexport const r${m} = new Hono()\n`; + for (let i = 0; i < 100; i++) s += `r${m}.${VERBS[i % 5]}('/res${i}/:id', (c) => c.text('m${m} r${i}'))\n`; + w(`routes-2k/src/mod${m}.ts`, s); + idx += `import { r${m} } from './mod${m}'\n`; +} +idx += `\nconst app = new Hono()\n`; +for (let m = 0; m < 20; m++) idx += `app.route('/m${m}', r${m})\n`; +w('routes-2k/src/index.ts', idx + `\nexport default app\n`); +w('routes-2k/budget', '300\nrss_mb 2048\n'); // seconds, then peak RSS of the solve in MB + +// deep-mount +let d = `// census: route_mount_literal (scale: 10-deep mounting; generated by tools/gen-scale.mjs)\nimport { Hono } from 'hono'\n\n`; +for (let i = 0; i < 10; i++) d += `const l${i} = new Hono()\nl${i}.get('/here', (c) => c.text('level ${i}'))\n`; +for (let i = 8; i >= 0; i--) d += `l${i}.route('/d${i + 1}', l${i + 1})\n`; +w('deep-mount/src/index.ts', d + `\nexport default l0\n`); +w('deep-mount/budget', '120\nrss_mb 2048\n'); // seconds, then peak RSS of the solve in MB + +// mw-200 +let mw = `// census: use_global, await_next (scale: 200 middleware x 10 routes; generated by tools/gen-scale.mjs)\nimport { Hono } from 'hono'\nimport type { MiddlewareHandler } from 'hono'\n\nconst app = new Hono()\n`; +for (let i = 0; i < 200; i++) mw += `const m${i}: MiddlewareHandler = async (c, next) => { await next() }\napp.use(m${i})\n`; +for (let i = 0; i < 10; i++) mw += `app.get('/r${i}', (c) => c.text('${i}'))\n`; +w('mw-200/src/index.ts', mw + `\nexport default app\n`); +w('mw-200/budget', '300\nrss_mb 2048\n'); // seconds, then peak RSS of the solve in MB diff --git a/graph/test/typescript/hono/tools/hono_compare.py b/graph/test/typescript/hono/tools/hono_compare.py new file mode 100644 index 000000000..5d3ad6cbe --- /dev/null +++ b/graph/test/typescript/hono/tools/hono_compare.py @@ -0,0 +1,398 @@ +#!/usr/bin/env python3 +"""Compare the engine's Hono relations against a hand-reviewed expected TSV. + +usage: hono_compare.py --lang typescript|javascript --ir IR --raw OUT/raw --manifest export_manifest.tsv + --relations relations.tsv --expected CASE.tsv --entries CASE.entries + [--known-missing CASE.known-missing] --variant bare|lib + +Every engine row is normalised to the expected file's vocabulary: a function is +`::` of its declaration (the IR's own filePath, +startLine, startColumn); a function that is not in the client IR (a library declaration) is `lib`. +A `lib:@` key in the expected file compares as `lib` too. + +Compared projections (relations.tsv says which engine file and columns feed each one, so a +column rename in SPEC.md is a one-line edit there, never a change here): + route VERB FULLPATH HANDLER @APP <- route(Handler, Verb, FullPath, App, Order); App is compared (SPEC R16a) + mw MW HANDLER ORDER <- middleware_applies(Mw, Handler, Order) + ctx SETTER READER KEY <- ctx_key_flow(Setter, Reader, Key) + mwp MW HANDLER VERB ORDER <- middleware_applies rows of a partial-only handler (path, app dropped) + rpc / request FROM HANDLER [CONF] <- remote_edge(From, To, .., Confidence): class base_unknown | method_unknown | known + entry FN <- entry_point(Fn, "http") + unknown FN <- remote_undetermined(Fn, ...) (expected col 4) + mount PATH <- a route row ALL PATH (any handler) + absent FN <- FN must appear in NO row above (negative controls) + +Verdicts: MISSING (expected, not produced), EXTRA (produced, not expected), NOW-FIXED (a +known-missing row that is produced). Any of them fails. Exits 3 when a relation the expected +file needs is not exported at all, so the runner can say so instead of reporting a diff. +Prints `compared=`; n is the number of expected rows checked, and the runner requires n >= 1. +""" +import argparse +import re +import csv +import os +import sys + +csv.field_size_limit(10**9) + + +def read_tsv(p): + if not os.path.exists(p) or os.path.getsize(p) == 0: + return [] + with open(p, newline='') as fh: + return [r for r in csv.reader(fh, delimiter='\t')] + + +def ir_methods(ir, lang): + f = 'all-typescript-methods.csv' if lang == 'typescript' else 'all-javascript-methods.csv' + hcol = 'tsMethodUniqueHash' if lang == 'typescript' else 'jsMethodUniqueHash' + rows = read_tsv(os.path.join(ir, f)) + if not rows: + return {} + hdr = rows[0] + ix = {c: i for i, c in enumerate(hdr)} + out = {} + for r in rows[1:]: + r = r + [''] * (len(hdr) - len(r)) + out[r[ix[hcol]]] = f"{r[ix['filePath']].replace(os.sep, '/')}:{r[ix['startLine']]}:{r[ix['startColumn']]}" + return out + + +def ir_vars(ir, lang): + """variable hash -> `::` of its name (a schema declaration, SPEC 3.9).""" + t = 'typescript' if lang == 'typescript' else 'javascript' + pre = 'ts' if lang == 'typescript' else 'js' + rows = read_tsv(os.path.join(ir, f'all-{t}-variables.csv')) + if not rows: + return {} + ix = {c: i for i, c in enumerate(rows[0])} + hk = f'{pre}VariableUniqueHash' + if hk not in ix or not {'startColumn', 'startLine', 'filePath'} <= set(ix): + return {} # the JavaScript IR's variables carry no filePath: schemas are a TypeScript row for now + out = {} + # TypeScript variables carry filePath; JavaScript ones only their module, joined like expressions + mk = next((c for c in rows[0] if c.endswith('ModuleLinkHash')), None) + mpath = module_paths(ir, t) if 'filePath' not in ix else {} + for r in rows[1:]: + r = r + [''] * (len(rows[0]) - len(r)) + fp = r[ix['filePath']] if 'filePath' in ix else mpath.get(r[ix[mk]], '?') if mk else '?' + out[r[ix[hk]]] = f"{fp.replace(os.sep, '/')}:{r[ix['startLine']]}:{r[ix['startColumn']]}" + return out + + +def module_paths(ir, t): + mods = read_tsv(os.path.join(ir, f'all-{t}-modules.csv')) + out = {} + if mods: + ix = {c: i for i, c in enumerate(mods[0])} + hk = next((c for c in mods[0] if c.endswith('ModuleUniqueHash')), None) + for r in mods[1:]: + if hk and 'filePath' in ix and len(r) > max(ix[hk], ix['filePath']): + out[r[ix[hk]]] = r[ix['filePath']].replace(os.sep, '/') + return out + + +def ir_exprs(ir, lang): + """expression hash -> `::` (the module's filePath joined through tsModuleLinkHash).""" + t = 'typescript' if lang == 'typescript' else 'javascript' + pre = 'ts' if lang == 'typescript' else 'js' + mods = read_tsv(os.path.join(ir, f'all-{t}-modules.csv')) + mpath = {} + if mods: + ix = {c: i for i, c in enumerate(mods[0])} + hk = next((c for c in mods[0] if c.endswith('ModuleUniqueHash')), None) + for r in mods[1:]: + if hk and 'filePath' in ix and len(r) > max(ix[hk], ix['filePath']): + mpath[r[ix[hk]]] = r[ix['filePath']].replace(os.sep, '/') + rows = read_tsv(os.path.join(ir, f'all-{t}-expressions.csv')) + out = {} + if not rows: + return out + ix = {c: i for i, c in enumerate(rows[0])} + hk = f'{pre}ExpressionUniqueHash' + mk = next((c for c in rows[0] if c.endswith('ModuleLinkHash')), None) + if hk not in ix or not mk: + return out + for r in rows[1:]: + r = r + [''] * (len(rows[0]) - len(r)) + out[r[ix[hk]]] = f"{mpath.get(r[ix[mk]], '?')}:{r[ix['startLine']]}:{r[ix['startColumn']]}" + return out + + +def main(): + a = argparse.ArgumentParser() + for k in ('lang', 'ir', 'raw', 'manifest', 'relations', 'expected', 'entries', 'variant'): + a.add_argument('--' + k, required=True) + a.add_argument('--known-missing', default='') + # the BASELINE engine's raw output on the same IR: an http entry_point row it already produced is + # pre-existing behaviour (the receiver-blind route-verb rule, which SPEC keeps), not a Hono row + a.add_argument('--baseline-raw', default='') + # chains/ suite (SPEC R18-R21): the case's `.chains` golden (fn / edge / chain / ccunknown rows) + a.add_argument('--chains', default='') + o = a.parse_args() + + names = ir_methods(o.ir, o.lang) + exprs = ir_exprs(o.ir, o.lang) + # a project function is its location; an expression (a library middleware's registering + # argument, SPEC 4) is `lib@`; anything else (a library declaration) is `lib` + names.update(ir_vars(o.ir, o.lang)) + fn = lambda h: names.get(h) or (('@' + exprs[h]) if h in exprs else 'lib') + manifest = {r[0]: r[1] for r in read_tsv(o.manifest) if len(r) >= 2} + rel = {} + for r in read_tsv(o.relations): + if not r or r[0].startswith('#'): + continue + rel.setdefault(r[0], []).append((r[1], dict(x.split('=') for x in r[2:]))) + + exp = [] + for path in (o.expected, o.entries): + for r in read_tsv(path): + if r and r[0] and not r[0].startswith('#'): + exp.append(r) + kinds = {r[0] for r in exp} + proj_of = {'route': {'route'}, 'mw': {'mw'}, 'ctx': {'ctx'}, 'rpc': {'remote'}, 'request': {'remote'}, + 'entry': {'entry'}, 'unknown': {'unknown'}, 'partial': {'partial'}, 'submount': {'submount'}, + 'unserved': {'unserved'}, 'schema': {'schema'}, 'validread': {'validread'}, 'regby': {'regby'}, + 'absent': {'route', 'mw'}, 'mwp': {'mw'}} + # an absence is checked against every Hono relation the engine exports; route and mw are required + # (above), the later ones (ctx, remote, schema) join the check as soon as they are exported + absent_optional = {'ctx', 'remote', 'schema'} + need = {'route'} # even a negative case asserts the route table EXISTS: absence of an + for k in kinds: # unexported relation is not absence of rows + need |= proj_of.get(k, set()) + missing_rel = sorted({f"{p} ({r[0]})" for p in need for r in rel.get(p, [('?', {})]) if r[0] not in manifest}) + if missing_rel: + print('relation not exported: ' + ', '.join(missing_rel)) + print('compared=0') + sys.exit(3) + + def engine(proj): + out = [] + for relname, cols in rel[proj]: + c = {k: int(v) for k, v in cols.items()} + rows = read_tsv(os.path.join(o.raw, manifest[relname])) + short = [k for k, i in c.items() if rows and any(len(r) <= i for r in rows)] + if short: + # a column SPEC names that the engine does not write is a missing relation shape, not a diff + print(f'relation {relname} lacks column(s): {", ".join(short)}') + print('compared=0') + sys.exit(3) + for r in rows: + g = lambda k: r[c[k]] if k in c and c[k] < len(r) else '' + if proj == 'route': + # SPEC R16a: the App column answers "which app serves this"; a wrong App is a false route (R16) + out.append(('route', g('verb').upper(), g('path'), fn(g('handler'))) + ((fn(g('app')),) if 'app' in c else ())) + elif proj == 'partial': + out.append(('partial', fn(g('handler')), g('reason'))) + elif proj == 'submount': + out.append(('submount', g('prefix'))) + elif proj == 'mw': + out.append(('mw', fn(g('mw')), fn(g('handler')), g('verb').upper(), g('path'), str(int(g('position') or -1))) + + ((fn(g('app')),) if 'app' in c else ())) + elif proj == 'ctx': + out.append(('ctx', fn(g('setter')), fn(g('reader')), g('key'))) + elif proj == 'remote': + # R14: precision is scored per confidence; base_unknown is its own class, the others (exact, + # route_shape) one class, as before + # R17: method_unknown (a request whose verb is not decidable) is a class of its own too + out.append(('remote', fn(g('from')), fn(g('to')), g('conf') if g('conf') in ('base_unknown', 'method_unknown') else 'known')) + elif proj == 'unserved': + out.append(('unserved', fn(g('fn')))) + elif proj == 'entry': + if g('kind') == 'http': + out.append(('entry', fn(g('fn')))) + elif proj == 'unknown': + out.append(('unknown', fn(g('fn')), g('reason'))) + elif proj == 'regby': + out.append(('regby', fn(g('regmethod')), fn(g('handler')), g('verb').upper(), g('path'), fn(g('app')))) + elif proj == 'schema': + out.append(('schema', fn(g('handler')), g('target'), fn(g('decl')))) + elif proj == 'validread': + out.append(('validread', fn(g('read')), fn(g('handler')), g('target'), fn(g('decl')))) + return out + + def lib(k): + # an expression is named by its location: `lib:.@` and `expr@` -> `@`; + # a bare `lib:...` (a library declaration) -> `lib` + if k.startswith('expr@'): + return '@' + k[5:] + # a library key is `lib` or `lib:...`; a project path may itself start with lib/ or libs/ + if not (k == 'lib' or k.startswith('lib:')): + return k + # the site follows the LAST '@': a scoped package (@hono/zod-validator) has one of its own + m = re.search(r'@([^@]+:\d+:\d+)$', k) + return '@' + m.group(1) if m else 'lib' + mw_app = any('app' in cols for _, cols in rel.get('mw', [])) + route_app = any('app' in cols for _, cols in rel.get('route', [])) + want = set() + absent = [] + for r in exp: + k = r[0] + if k == 'route': + want.add(('route', r[1], r[2], lib(r[3])) + (('@' + r[4],) if route_app else ())) + elif k == 'mw': + want.add(('mw', lib(r[1]), lib(r[2]), r[3], r[4], r[5]) + (('@' + r[6],) if mw_app else ())) + elif k == 'ctx': + want.add(('ctx', r[1], r[2], r[3])) + elif k in ('rpc', 'request'): + # dest spelling is SPEC-open: not compared; an optional 6th column is the confidence class (R14) + want.add(('remote', r[1], lib(r[4]), r[5] if len(r) > 5 and r[5] in ('base_unknown', 'method_unknown') else 'known')) + elif k == 'mwp': + want.add(('mwp', lib(r[1]), lib(r[2]), r[3], r[4])) + elif k == 'entry': + if not (r[1] == 'lib' or r[1].startswith('lib:')): + want.add(('entry', r[1])) + elif k == 'unknown': + want.add(('unknown', r[3], r[2])) + elif k == 'partial': + want.add(('partial', lib(r[1]), r[3])) + elif k == 'submount': + want.add(('submount', r[1])) + elif k == 'unserved': + want.add(('unserved', r[1])) + elif k == 'schema': + want.add(('schema', r[1], r[2], lib(r[3]))) + elif k == 'regby': + want.add(('regby', r[1], lib(r[2]), r[3], r[4], '@' + r[5])) + elif k == 'validread': + want.add(('validread', '@' + r[1], r[2], r[3], lib(r[4]))) + elif k == 'absent': + absent.append(r[1]) + else: + print(f'bad expected row kind: {k}') + sys.exit(2) + + if 'absent' in kinds: + for p in sorted(absent_optional): + if all(r[0] in manifest for r in rel.get(p, [('?', {})])): + need.add(p) + else: + print(f'note: absence not yet checked in {p} (not exported)') + have = set() + for p in sorted(need): + have |= set(engine(p)) + # SPEC 3.3: a partial route's chain is compared without its path and app (neither is decidable); a middleware + # row whose handler is partial-only in the expected truth is projected the same way + partial_only = {w[1] for w in want if w[0] == 'partial'} - {w[3] for w in want if w[0] == 'route'} + if partial_only: + have = {('mwp', h[1], h[2], h[3], h[5]) if h[0] == 'mw' and h[2] in partial_only else h for h in have} + + known = set() + if o.known_missing and os.path.exists(o.known_missing): + for r in read_tsv(o.known_missing): + if not r or r[0].startswith('#'): + continue + if r[0] in ('*', o.variant): + known.add(tuple(r[1:])) + + bad = 0 + for w in sorted(want): + if w not in have and w not in known: + print('MISSING ' + '\t'.join(w)); bad += 1 + elif w in have and w in known: + print('NOW-FIXED ' + '\t'.join(w)); bad += 1 + # extras: a row the engine produced that the reviewed truth does not have + pre = set() + if o.baseline_raw and 'entry_point' in manifest: + for r in read_tsv(os.path.join(o.baseline_raw, manifest['entry_point'])): + if len(r) >= 2 and r[1] == 'http': + pre.add(('entry', fn(r[0]))) + for h in sorted(have): + if h in want: + continue + if h in pre: + print('PRE-EXISTING ' + '\t'.join(h) + ' (the baseline engine already emits it)') + continue + print('EXTRA ' + '\t'.join(h)); bad += 1 + known_fns = set(names.values()) + for f in absent: + if f not in known_fns: + # an absence check on a location that is no function is vacuous: it can never fail + print(f'BAD-ABSENT {f} names no function in the IR'); bad += 1 + continue + hits = [h for h in have if f in h[1:] and h not in pre] # a pre-existing entry row is not a Hono row + for h in hits: + print('PRESENT ' + '\t'.join(h) + f' (absent {f})'); bad += 1 + nchain = 0 + if o.chains: + cbad, nchain = compare_chains(o, rel, manifest, fn, read_tsv) + bad += cbad + print(f'compared={len(want) + len(absent) + nchain}') + sys.exit(1 if bad else 0) + + +def compare_chains(o, rel, manifest, fn, read_tsv): + """chains/ (SPEC R18-R21). The golden `.chains` holds, from the compiler oracle and the runtime trace: + fn KEY every function with a body in the fixture (the universe edges are scored in) + edge FROM TO [rt] a call edge between two of them (`rt`: observed at run time, beyond the compiler) + chain NAME VERB PATH F0..Fn a WHOLE chain; each hop is a call edge or a middleware hop (A applies to handler H + and the next is H, or the next middleware of H's chain) + ccunknown FN FN holds a call whose target is not decidable: the engine must declare it (R21) + Engine call edges: call_chain_edge (FromMethod -> ToMethod), a declared base widened by + method_dispatch_candidate, and fn_value_call. Edges are compared both ways inside the fn universe (an edge to a + same-named method of an unrelated class is EXTRA); a chain is reported once, at its first break.""" + rows = [r for r in read_tsv(o.chains) if r and r[0] and not r[0].startswith('#')] + fns = {r[1] for r in rows if r[0] == 'fn'} + want_e = {(r[1], r[2]) for r in rows if r[0] == 'edge'} + chains = [r for r in rows if r[0] == 'chain'] + want_u = {r[1] for r in rows if r[0] == 'ccunknown'} + need = ['chainedge', 'dispatch', 'fnvalue', 'ccunknown', 'mw'] + miss = sorted({f'{p} ({r[0]})' for p in need for r in rel.get(p, [('?', {})]) if r[0] not in manifest}) + if miss: + print('relation not exported: ' + ', '.join(miss)) + # without call edges or middleware rows there is nothing to compare; a missing widening relation (dispatch, + # fnvalue, ccunknown) still FAILS the case (exit 3 below), but the chains are compared on what IS exported, so + # the report shows where they break (the JavaScript engine exports neither widening relation) + if any(m.split(' ')[0] in ('chainedge', 'mw') for m in miss): + print('compared=0'); sys.exit(3) + + def raw(p): + out = [] + for relname, cols in rel[p]: + if relname not in manifest: + continue + c = {k: int(v) for k, v in cols.items()} + for r in read_tsv(os.path.join(o.raw, manifest[relname])): + out.append({k: (r[i] if i < len(r) else '') for k, i in c.items()}) + return out + base = {} + for r in raw('dispatch'): + base.setdefault(r['base'], set()).add(r['impl']) + have_e = set() + for r in raw('chainedge'): + if r['to'] in ('', '-'): + continue + for t in {r['to']} | base.get(r['to'], set()): + have_e.add((fn(r['from']), fn(t))) + for r in raw('fnvalue'): + have_e.add((fn(r['from']), fn(r['to']))) + have_e = {e for e in have_e if e[0] in fns and e[1] in fns} + mwpos = {} + for r in raw('mw'): + mwpos.setdefault(fn(r['handler']), {})[fn(r['mw'])] = int(r['position'] or -1) + def mw_hop(a, b): + for h, ch in mwpos.items(): + if a in ch and (b == h or (b in ch and ch[b] == ch[a] + 1)): + return True + return False + have_u = {fn(r['fn']) for r in raw('ccunknown')} + bad = 0 + for e in sorted(want_e - have_e): + print('MISSING edge\t' + '\t'.join(e)); bad += 1 + for e in sorted(have_e - want_e): + print('EXTRA edge\t' + '\t'.join(e)); bad += 1 + for r in chains: + f = r[4:] + for i in range(len(f) - 1): + if (f[i], f[i + 1]) not in have_e and not mw_hop(f[i], f[i + 1]): + print(f'CHAIN-BROKEN {r[1]} at hop {i + 1}/{len(f) - 1}\t{f[i]} -> {f[i + 1]}'); bad += 1 + break + for u in sorted(want_u - have_u): + print('MISSING ccunknown\t' + u); bad += 1 + if miss: + print(f'compared={len(want_e) + len(chains) + len(want_u)}'); sys.exit(3) + return bad, len(want_e) + len(chains) + len(want_u) + + +if __name__ == '__main__': + main() diff --git a/graph/test/typescript/hono/tools/promote-draft.sh b/graph/test/typescript/hono/tools/promote-draft.sh new file mode 100755 index 000000000..62f545786 --- /dev/null +++ b/graph/test/typescript/hono/tools/promote-draft.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Promote a REVIEWED oracle draft to the golden. Run only after reading the draft against the fixture: +# the header it writes is a claim that a person checked every row. +# tools/promote-draft.sh / "" +set -euo pipefail +D="${HONO_SUITE_DIR:-$(cd "$(dirname "$0")/.." && pwd)}" +id="$1"; note="$2"; suite="${id%%/*}"; name="${id#*/}" +census="$(grep -rhoE '(//|#) census: .*' "$D/cases/$id/src" | head -1 | sed -E 's@^(//|#) census: @@')" +[ -n "$census" ] || { echo "the fixture has no '// census:' header: cite the shape it models first" >&2; exit 1; } +for ext in tsv entries; do + dr="$D/expected/$id.$ext.draft"; [ -f "$dr" ] || { echo "no draft $dr" >&2; exit 1; } + { echo "# hand-reviewed $(date +%F) against the fixture and the oracle: $note" + echo "# census: $census" + cat "$dr"; } > "$D/expected/$id.$ext" + rm -f "$dr" +done diff --git a/graph/test/typescript/hono/truth/.gitignore b/graph/test/typescript/hono/truth/.gitignore new file mode 100644 index 000000000..504afef81 --- /dev/null +++ b/graph/test/typescript/hono/truth/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +package-lock.json diff --git a/graph/test/typescript/hono/truth/deno-shims/deno.d.ts b/graph/test/typescript/hono/truth/deno-shims/deno.d.ts new file mode 100644 index 000000000..8181be2d6 --- /dev/null +++ b/graph/test/typescript/hono/truth/deno-shims/deno.d.ts @@ -0,0 +1,8 @@ +// The slice of the Deno runtime's global API a Hono-on-Deno fixture uses, so the oracle can typecheck it +// under the TypeScript compiler (no Deno toolchain is involved; the shapes follow Deno's own declarations). +declare namespace Deno { + type ServeHandler = (request: Request, info?: unknown) => Response | Promise + function serve(handler: ServeHandler): unknown + function serve(options: { port?: number; hostname?: string }, handler: ServeHandler): unknown + const env: { get(key: string): string | undefined } +} diff --git a/graph/test/typescript/hono/truth/deno-shims/middleware.d.ts b/graph/test/typescript/hono/truth/deno-shims/middleware.d.ts new file mode 100644 index 000000000..1f52edf05 --- /dev/null +++ b/graph/test/typescript/hono/truth/deno-shims/middleware.d.ts @@ -0,0 +1,7 @@ +// `https://deno.land/x/hono@v3.x/middleware.ts` re-exported every built-in middleware from one module. +export { cors } from 'hono/cors' +export { logger } from 'hono/logger' +export { etag } from 'hono/etag' +export { secureHeaders } from 'hono/secure-headers' +export { basicAuth } from 'hono/basic-auth' +export { jwt } from 'hono/jwt' diff --git a/graph/test/typescript/hono/truth/hono-oracle.mjs b/graph/test/typescript/hono/truth/hono-oracle.mjs new file mode 100644 index 000000000..031f706f2 --- /dev/null +++ b/graph/test/typescript/hono/truth/hono-oracle.mjs @@ -0,0 +1,1847 @@ +#!/usr/bin/env node +// ───────────────────────────────────────────────────────────────────────────── +// Hono torture oracle: the STATIC half of the ground truth. +// +// Independent of the engine by construction: it uses the TypeScript compiler API and the +// real `hono` package pinned in ./package.json (never the axiom parser, never the IR). It +// walks every registration in PROGRAM ORDER and replays Hono's own semantics, as read from +// hono-base.js of the pinned version: +// +// - `new Hono()` / `new OpenAPIHono()` (the class must be DECLARED in the pinned package; +// a local class that happens to be called Hono is not an app); +// - `basePath(p)` returns a clone that SHARES the route list, with base = mergePath(base, p); +// - a verb / `all` / `on` / `use` registers one entry per handler, `use` with method ALL and +// default path `*`, a path-less verb continues the instance's last path; +// - `route(p, sub)` COPIES sub's entries present AT THAT MOMENT under mergePath(base, p); +// - `mount(p, fetch)` registers ALL mergePath(base, p, '*') to a foreign handler. +// +// Which middleware applies to which handler is decided by Hono's OWN router (SmartRouter of +// the pinned package) matched against a concrete sample of the handler's path, in +// registration order: never by a re-implementation of the pattern language. +// +// usage: node hono-oracle.mjs [--json] +// Output (TSV on stdout, one relation per first column; locations are +// `::` of the function node, 1-based): +// route VERB FULLPATH HANDLER APP ORDER +// mw MW HANDLER ORDER (ORDER = position in the handler's middleware chain) +// entry FN http +// ctx SETTER_FN READER_FN KEY +// rpc FROM_FN VERB PATH HANDLER +// request FROM_FN VERB PATH HANDLER +// mount PATH TARGET +// unknown SITE REASON +// Exit 2 (refusal) when the program does not typecheck: an unscored case is not a pass. +// ───────────────────────────────────────────────────────────────────────────── +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const HERE = path.dirname(fileURLToPath(import.meta.url)); +// the compiler is the oracle's OWN pinned one, reached through the suite's shared loader (version-guarded) +const { loadTypeScript } = await import(new URL('../../ground-truth/load-typescript.mjs', import.meta.url).href); +const ts = loadTypeScript(HERE, { toolName: 'hono-oracle' }); +const NM = path.join(HERE, 'node_modules'); +const { SmartRouter } = await import(path.join(NM, 'hono/dist/router/smart-router/index.js')); +const { RegExpRouter } = await import(path.join(NM, 'hono/dist/router/reg-exp-router/index.js')); +const { TrieRouter } = await import(path.join(NM, 'hono/dist/router/trie-router/index.js')); + +const SRC = path.resolve(process.argv[2] || '.'); +const NO_TYPECHECK = process.argv.includes('--no-typecheck'); +const STORES = process.argv.includes('--stores'); +const DEBUG = !!process.env.HONO_ORACLE_DEBUG; +const T0 = Date.now(); +const phase = name => { if (DEBUG) process.stderr.write(`phase ${name} at ${((Date.now() - T0) / 1000).toFixed(1)}s\n`); }; + +// ── program ───────────────────────────────────────────────────────────────── +const EXT = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/; +function walkDir(d, out = []) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) walkDir(p, out); + else if (EXT.test(e.name) && !e.name.endsWith('.d.ts')) out.push(p); + } + return out; +} +const files = walkDir(SRC).sort(); +const options = { + target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.Bundler, strict: true, allowJs: true, checkJs: false, + noEmit: true, skipLibCheck: true, esModuleInterop: true, resolveJsonModule: true, + jsx: ts.JsxEmit.ReactJSX, jsxImportSource: 'hono/jsx', types: [], lib: ['lib.es2022.d.ts', 'lib.dom.d.ts'], + allowImportingTsExtensions: true, +}; +const host = ts.createCompilerHost(options, true); +const ANCHOR = path.join(HERE, 'anchor.ts'); // resolve bare packages from the oracle's own node_modules +// ── Deno: an import map (deno.json / deno.jsonc / import_map.json `imports`, exact and prefix keys) and +// jsr: / npm: / deno.land URL specifiers, each mapped to the PINNED package of the same name. This is a +// module-resolution host for the oracle only; it says nothing about how the engine should resolve them. +// every deno.json / deno.jsonc / import_map.json in the fixture (a Deno project need not sit at the root) +const denoCfgs = []; +(function scanDeno(d) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) scanDeno(p); else if (['deno.json', 'deno.jsonc', 'import_map.json'].includes(e.name)) denoCfgs.push(p); + } +})(SRC); +const DENO_CFG = denoCfgs.length > 0; +let importMap = {}; +for (const f of denoCfgs) { + try { + const j = JSON.parse(fs.readFileSync(f, 'utf8').replace(/^\s*\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '')); + importMap = { ...importMap, ...(j.imports || {}) }; + const im = j.importMap && path.join(path.dirname(f), j.importMap); + if (im && fs.existsSync(im)) importMap = { ...importMap, ...JSON.parse(fs.readFileSync(im, 'utf8')).imports }; + } catch { } +} +function canonicalSpec(spec) { + if (importMap[spec]) spec = importMap[spec]; + else { + const k = Object.keys(importMap).filter(k => k.endsWith('/') && spec.startsWith(k)).sort((a, b) => b.length - a.length)[0]; + if (k) spec = importMap[k] + spec.slice(k.length); + } + let m; + if ((m = spec.match(/^jsr:\/?@hono\/hono(?:@[^/]*)?(\/.*)?$/)) || (m = spec.match(/^@hono\/hono(\/.*)?$/))) return 'hono' + (m[1] || ''); + if ((m = spec.match(/^npm:\/?((?:@[^/@]+\/)?[^/@]+)(?:@[^/]*)?(\/.*)?$/))) return m[1] + (m[2] || ''); + if ((m = spec.match(/^jsr:\/?(@hono\/[^/@]+)(?:@[^/]*)?(\/.*)?$/))) return m[1] + (m[2] || ''); + if ((m = spec.match(/^https:\/\/deno\.land\/x\/hono(?:@[^/]*)?\/(.*)$/))) { + if (m[1] === 'mod.ts') return 'hono'; + if (m[1] === 'middleware.ts') return path.join(HERE, 'deno-shims', 'middleware.d.ts'); + return 'hono/' + m[1].replace(/^middleware\//, '').replace(/\/index\.ts$|\.ts$/, ''); + } + return spec; +} +const libLabel = ms => { const c = canonicalSpec(ms); return c.startsWith('/') ? 'hono/middleware' : c; }; +// tsconfig `paths` / `baseUrl` of the nearest tsconfig.json above the importing file (inside the fixture): +// an aliased project import (`@/lib/app`) resolves to the project file, as the project's compiler would +const _pathsCache = new Map(); +function pathsOpts(containing) { + let d = path.dirname(path.resolve(containing)); + const seen = []; + while (d.startsWith(SRC)) { + if (_pathsCache.has(d)) { const v = _pathsCache.get(d); seen.forEach(x => _pathsCache.set(x, v)); return v; } + seen.push(d); + const f = path.join(d, 'tsconfig.json'); + if (fs.existsSync(f)) { + let v = null; + try { + const cf = ts.readConfigFile(f, ts.sys.readFile); + const pc = ts.parseJsonConfigFileContent(cf.config || {}, ts.sys, d); + if (pc.options.paths || pc.options.baseUrl) v = { paths: pc.options.paths, baseUrl: pc.options.baseUrl, pathsBasePath: pc.options.pathsBasePath || d }; + } catch { v = null; } + seen.forEach(x => _pathsCache.set(x, v)); return v; + } + const up = path.dirname(d); if (up === d) break; d = up; + } + seen.forEach(x => _pathsCache.set(x, null)); return null; +} +// workspace packages: a package.json with a name anywhere in the fixture (not under node_modules) makes +// `` and `/` resolve to that package's files, as a workspace install links them +const workspace = new Map(); +(function scanWs(d) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) scanWs(p); + else if (e.name === 'package.json') { try { const n = JSON.parse(fs.readFileSync(p, 'utf8')).name; if (n) workspace.set(n, d); } catch { } } + } +})(SRC); +function exportsTarget(d, sub) { + // package.json `exports`: "./sub" -> a string, or a conditions object (types / import / default) + try { + const ex = JSON.parse(fs.readFileSync(path.join(d, 'package.json'), 'utf8')).exports; + if (!ex) return null; + const key = sub ? './' + sub : '.'; + let t = typeof ex === 'string' && key === '.' ? ex : ex[key]; + // a subpath pattern (`"./*"`, `"./adapters/*"`) lists every subpath it matches: substitute the matched part + if (t === undefined && typeof ex === 'object') for (const [k, v] of Object.entries(ex)) { + const i = k.indexOf('*'); if (i < 0 || !key.startsWith(k.slice(0, i)) || !key.endsWith(k.slice(i + 1)) || key.length < k.length - 1) continue; + const m = key.slice(i, key.length - (k.length - i - 1)); + let vv = v; if (vv && typeof vv === 'object') vv = vv.types || vv.import || vv.default || vv.require; + if (typeof vv === 'string') { t = vv.split('*').join(m); break; } + } + if (t && typeof t === 'object') t = t.types || t.import || t.default || t.require; + if (typeof t !== 'string') return null; + // O3b: an export that points at an unbuilt output file (`./dist/x.js`, `./dist/x.d.ts`) is served from the + // package's source in a checkout: the same path under src/, with a source extension + if (!fs.existsSync(path.join(d, t))) { + const stem = t.replace(/^\.?\//, '').replace(/^(dist|build|out|lib)\//, '').replace(/\.d\.(ts|mts|cts)$|\.(js|mjs|cjs|ts|mts|cts)$/, ''); + for (const base of ['src/', '']) for (const ext of ['.ts', '.tsx', '.mts', '.js', '.mjs', '/index.ts', '/index.js']) { + const c = path.join(d, base + stem + ext); + if (fs.existsSync(c)) return c; + } + } + return path.join(d, t); + } catch { return null; } +} +// the moduleResolution of the program a file belongs to: its nearest tsconfig.json inside the fixture, else the oracle's +const _mrCache = new Map(); +function moduleResolutionOf(containing) { + let d = path.dirname(path.resolve(containing)); + while (d.startsWith(SRC)) { + if (_mrCache.has(d)) return _mrCache.get(d); + const f = path.join(d, 'tsconfig.json'); + if (fs.existsSync(f)) { + let v = options.moduleResolution; + try { const pc = ts.parseJsonConfigFileContent(ts.readConfigFile(f, ts.sys.readFile).config || {}, ts.sys, d); v = ts.getEmitModuleResolutionKind(pc.options); } catch { } + _mrCache.set(d, v); return v; + } + const up = path.dirname(d); if (up === d) break; d = up; + } + return options.moduleResolution; +} +function workspaceOpts(spec, containing) { + for (const [n, d] of workspace) if (spec === n || spec.startsWith(n + '/')) { + const sub = spec === n ? '' : spec.slice(n.length + 1); + const t = exportsTarget(d, sub); + if (t) return { baseUrl: d, paths: { [spec]: [t] }, pathsBasePath: d }; + // SPEC R22a: a package whose `exports` does not list this subpath resolves only where the TypeScript compiler + // resolves it. node10 ignores `exports` and reads the package directory itself; bundler, node16 and nodenext + // enforce `exports`, so the import is unbound (no synthetic `src/` paths for it) + let hasExports = false; + try { hasExports = JSON.parse(fs.readFileSync(path.join(d, 'package.json'), 'utf8')).exports !== undefined; } catch { } + if (hasExports) { + const mr = containing ? moduleResolutionOf(containing) : options.moduleResolution; + if (mr !== ts.ModuleResolutionKind.Node10 && mr !== ts.ModuleResolutionKind.Classic) return null; + return { baseUrl: d, moduleResolution: ts.ModuleResolutionKind.Node10, paths: { [spec]: [sub ? path.join(d, sub) : d] }, pathsBasePath: d }; + } + return { baseUrl: d, paths: { [n]: [d, path.join(d, 'src')], [n + '/*']: [path.join(d, '*'), path.join(d, 'src', '*')] }, pathsBasePath: d }; + } + return null; +} +// ONE resolver for every project-side lookup (program building, module order, require), so a module the +// program contains is never evaluated out of order because a second lookup could not find it +function resolveSpec(rawSpec, containing, opts = options) { + const spec = canonicalSpec(rawSpec); + let r = ts.resolveModuleName(spec, containing, opts, host).resolvedModule; + if (!r && !spec.startsWith('.') && !spec.startsWith('/')) { + for (const po of [pathsOpts(containing), workspaceOpts(spec, containing)]) { + if (r || !po) continue; + r = ts.resolveModuleName(spec, containing, { ...opts, ...po }, host).resolvedModule; + if (r && r.isExternalLibraryImport) r = undefined; + } + if (!r) r = ts.resolveModuleName(spec, ANCHOR, opts, host).resolvedModule; + } + return r; +} +function isProjectSpec(spec, containing) { + if (spec.startsWith('.') || spec.startsWith('/')) return true; + const r = resolveSpec(spec, containing); + return !!(r && path.resolve(r.resolvedFileName).startsWith(SRC + path.sep) && !r.resolvedFileName.includes(`${path.sep}node_modules${path.sep}`)); +} +host.resolveModuleNameLiterals = (lits, containing, redirected, opts) => lits.map(lit => ({ resolvedModule: resolveSpec(lit.text, containing, opts) })); +const program = ts.createProgram(DENO_CFG ? [...files, path.join(HERE, 'deno-shims', 'deno.d.ts')] : files, options, host); +const checker = program.getTypeChecker();phase('program'); + +const srcFiles = program.getSourceFiles().filter(sf => path.resolve(sf.fileName).startsWith(SRC + path.sep)); + +if (!NO_TYPECHECK) { + const diags = ts.getPreEmitDiagnostics(program).filter(d => d.file && srcFiles.includes(d.file) + && !/\.(js|mjs|cjs|jsx)$/.test(d.file.fileName)); + if (diags.length) { + for (const d of diags.slice(0, 8)) { + const { line } = d.file.getLineAndCharacterOfPosition(d.start || 0); + process.stderr.write(`REFUSED (does not typecheck): ${path.relative(SRC, d.file.fileName)}:${line + 1} ${ts.flattenDiagnosticMessageText(d.messageText, ' ')}\n`); + } + process.exit(2); + } +} + +const rel = f => path.relative(SRC, path.resolve(f)).split(path.sep).join('/'); +const loc = n => { const sf = n.getSourceFile(); const p = sf.getLineAndCharacterOfPosition(n.getStart(sf)); return `${rel(sf.fileName)}:${p.line + 1}:${p.character + 1}`; }; +const inPkg = (decl, pkgs) => { const f = path.resolve(decl.getSourceFile().fileName); return pkgs.some(p => f.includes(`${path.sep}node_modules${path.sep}${p}${path.sep}`)); }; +const isSrcDecl = d => d && path.resolve(d.getSourceFile().fileName).startsWith(SRC + path.sep); + +// ── abstract values ───────────────────────────────────────────────────────── +// app: { k:'app', store, base, path, id } (a basePath clone shares store) +// str: { k:'str', v } +// fn: { k:'fn', node } user function +// libfn: { k:'libfn', name, site } a function a library call produced (cors(), zValidator()) +// list: { k:'list', items } createHandlers(...), arrays +// route: { k:'routedef', method, path } createRoute({...}) +// client: { k:'client', app, segs } hc() / testClient(app) and its property paths +// factory:{ k:'factory' } +// obj: { k:'obj', props: Map } +// unk: { k:'unk', why } +const UNK = why => ({ k: 'unk', why }); +// undef: { k:'undef' } a property an object literal the program shows does not have +// (`input.routes` when definePlugin was given `{ id, register }`): a loop over it runs no times, `x ?? y` is y. +// Only for a CLOSED literal (no unknown spread, no computed or accessor member) and a property name that no +// assignment `a. = ..` anywhere in the program writes; otherwise unknown, as before +const UNDEF = { k: 'undef' }; +let storeSeq = 0, entrySeq = 0, altSeq = 0, mountSeq = 0; +const stores = []; +function newApp(node) { + const store = { id: ++storeSeq, entries: [], mounted: false, origin: loc(node), notFound: null, onError: null }; + stores.push(store); + return { k: 'app', store, base: '/', path: '/', origin: store.origin }; +} +// mergePath, transcribed from hono/dist/utils/url.js of the pinned version +function mergePath(base, sub, ...rest) { + if (rest.length) sub = mergePath(sub, ...rest); + return `${base?.[0] === '/' ? '' : '/'}${base}${sub === '/' ? '' : `${base?.at(-1) === '/' ? '' : '/'}${sub?.[0] === '/' ? sub.slice(1) : sub}`}`; +} + +const out = { regby: new Set(), unserved: new Set(), partial: new Set(), submount: [], route: [], mw: [], entry: new Set(), ctx: new Set(), rpc: new Set(), request: new Set(), unknown: new Set() }; +// unknown rows: SITE REASON FN (REASON from SPEC section 4's closed list; FN = the method it is attributed to) +const siteFn = new Map(); +function unknownAt(node, reason, fnKey) { out.unknown.add(`${loc(node)}\t${reason}\t${fnKey || enclosingFn(node)}`); } +const handlerKey = v => v.k === 'fn' ? loc(v.node) : v.k === 'libfn' ? `lib:${v.name}@${v.site}` : null; + +function lexicalCond(node) { + for (let n = node.parent; n && !ts.isFunctionLike(n) && !ts.isSourceFile(n); n = n.parent) { + if (ts.isForOfStatement(n) || ts.isForStatement(n) || ts.isForInStatement(n) || ts.isWhileStatement(n) || ts.isDoStatement(n)) return 'loop_registration'; + if (ts.isIfStatement(n) || ts.isConditionalExpression(n)) return 'conditional_registration'; + } + return null; +} +function addEntry(app, method, p, hv, kind, site, node) { + if (app.dyn && kind === 'mw' && p.k === 'str') { + // middleware on a rebased holder: kept aside for the routes later mounted into it (partialMount) + const hs0 = hv.k === 'list' ? hv.items : [hv]; + app.store.dynMws = app.store.dynMws || []; + for (const h of hs0) { + const k = handlerKey(h); + if (k) app.store.dynMws.push({ seq: ++entrySeq, method, path: p.v, key: k, kind, val: h, anyBase: p.v === '*' || p.v === '/*', alt: hv.k === 'alt' ? hv.alt : undefined }); + // a middleware the program cannot name (a loop over a run-time list) is a marker in the order, as on a root + else app.store.dynMws.push({ seq: ++entrySeq, method, path: p.v, key: null, kind: 'dyn', val: h, anyBase: p.v === '*' || p.v === '/*' }); + } + return; + } + if (app.dyn) { + if (kind === 'handler') { + const k = handlerKey(hv), V = method === 'ALL' ? 'ANY' : method; + out.partial.add(`${k}\t${V}\t${app.dyn}`); unknownAt(node, app.dyn, k); + // SPEC 3.3: a route registered straight on a rebased holder keeps its decidable chain: the holder's own + // middleware registered before it whose scope matches inside the holder (all its paths share the one unknown + // prefix); a run-time middleware list among them makes the chain dynamic_middleware_list + if (k && p.k === 'str') { + const before = (app.store.dynMws || []).filter(m => m.method === 'ALL' || m.method === method); + const router = new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }); + before.forEach((m, j) => router.add(m.method, m.anyBase ? '*' : m.path, j)); + let matched; try { matched = new Set(router.match(method === 'ALL' ? 'GET' : method, samplePath(p.v))[0].map(x => x[0])); } catch { matched = new Set(); } + let at = -1, lastAlt, dynamic = false; + before.forEach((m, j) => { + if (!matched.has(j)) return; + if (m.kind === 'dyn') { dynamic = true; return; } + if (!(m.alt && m.alt === lastAlt)) at++; + lastAlt = m.alt; partialMw.add(`${m.key}\t${k}\t${V}\t${at}`); + }); + if (dynamic) out.unknown.add(`${loc(node)}\tdynamic_middleware_list\t${k}`); + } + } + return; + } + if (p.k !== 'str') { const k = handlerKey(hv); out.partial.add(`${k}\t${method === 'ALL' ? 'ANY' : method}\tdynamic_route_path`); unknownAt(node, 'dynamic_route_path', k); return; } + const full = mergePath(app.base, p.v); + const hs = hv.k === 'list' ? hv.items : hv.k === 'alt' ? hv.items : [hv]; + const alt = hv.k === 'alt' ? `a${++altSeq}` : undefined; + for (const h of hs) { + const key = handlerKey(h); + if (!key) { + // a middleware the program cannot name (a loop over a run-time list, a non-literal spread): a marker in the + // registration order, so every chain it may join is declared dynamic_middleware_list (SPEC 3.3, hono-1-06) + if (kind === 'mw') app.store.entries.push({ seq: ++entrySeq, method, path: full, key: null, kind: 'dyn', site, val: h }); + else unknownAt(node, 'unresolved_handler'); + continue; + } + app.store.entries.push({ seq: ++entrySeq, method, path: full, key, kind, site, val: h, regFn: node ? enclosingFn(node) : '?', alt, loopTag: loopTagNow() }); + if (h.k === 'fn') handlerFns.add(h.node); + // SPEC 3.1: a registration written inside a loop or behind a condition (lexically, within its own function) + const lc = condDepth && node ? lexicalCond(node) : null; + if (lc) unknownAt(node, lc, h.k === 'fn' ? key : undefined); + } +} + +// ── environment: values keyed by DECLARATION node ─────────────────────────── +const env = new Map(); +const evaluating = new Set(); +function declOf(id) { + let s = checker.getSymbolAtLocation(id); + if (!s) return null; + if (s.flags & ts.SymbolFlags.Alias) { try { s = checker.getAliasedSymbol(s); } catch { } } + return s.valueDeclaration || (s.declarations && s.declarations[0]) || null; +} +function valueOfDecl(d) { + if (!d) return UNK('no_declaration'); + if (env.has(d)) return env.get(d); + if (!isSrcDecl(d)) return { k: 'libdecl', decl: d }; + if (ts.isFunctionDeclaration(d) || ts.isFunctionExpression(d) || ts.isArrowFunction(d) || ts.isMethodDeclaration(d)) return { k: 'fn', node: d }; + if (ts.isVariableDeclaration(d) && d.initializer && !evaluating.has(d)) { + // lazily, for a const read before its module ran (memoised: no double registration) + evaluating.add(d); const v = evalExpr(d.initializer); evaluating.delete(d); env.set(d, v); return v; + } + if (ts.isBindingElement(d)) { + // `const { a } = ` / `const { a } = require('./m')`: the member of the evaluated initializer + const vd = d.parent && d.parent.parent; + if (vd && ts.isVariableDeclaration(vd) && vd.initializer && ts.isObjectBindingPattern(d.parent)) { + // O15: a destructured name whose initializer reads it back (through an object shorthand) is a cycle: + // the value is unknown, never a stack overflow + if (evaluating.has(d)) { noteCycle(d); return UNK('cycle'); } + evaluating.add(d); + let src; try { src = evalExpr(vd.initializer); } finally { evaluating.delete(d); } + const prop = (d.propertyName || d.name).getText(); + let v = null; + if (src.k === 'obj' && src.props.has(prop)) { const p = src.props.get(prop); if (!p.val) p.val = evalExpr(p.node); v = p.val; } + else if (src.k === 'module') { const ed = moduleExportDecl(src.sf, prop); if (ed) v = valueOfDecl(ed); } // O16: `const { x } = await import('./m')` + // O15: memoised like a const (a call frame's own bindings are reset per call by inline), so a chain of + // destructures is evaluated once, not once per read + env.set(d, v || UNK('destructured')); + return env.get(d); + } + return UNK('destructured'); + } + if (ts.isPropertyAccessExpression(d) && d.parent && ts.isBinaryExpression(d.parent) && d.parent.left === d) return env.get(d.parent) ?? evalExpr(d.parent.right); // `module.exports.a = fn` + if (ts.isExportAssignment(d)) return env.get(d) ?? evalExpr(d.expression); + if (ts.isShorthandPropertyAssignment(d)) { const vs = checker.getShorthandAssignmentValueSymbol(d); return vs ? valueOfDecl(vs.valueDeclaration) : UNK('shorthand'); } + if (ts.isPropertyAssignment(d)) return evalExpr(d.initializer); + if (ts.isBinaryExpression(d)) return env.get(d) ?? evalExpr(d.right); + if (ts.isSourceFile(d)) return { k: 'module', sf: d }; + return UNK('opaque_declaration'); +} + +// the leftmost identifier of `a.b.c(...)`/`a()` when it is bound by an import from a package: a library +// call even when the package cannot be resolved (it is still kept in the chain, never dropped) +function rootImport(callee) { + let n = callee; + while (ts.isPropertyAccessExpression(n) || ts.isCallExpression(n) || ts.isElementAccessExpression(n)) n = n.expression; + if (!ts.isIdentifier(n)) return null; + const s = checker.getSymbolAtLocation(n); + for (const d of (s && s.declarations) || []) { + const decl = ts.isImportSpecifier(d) ? d.parent.parent.parent : ts.isImportClause(d) ? d.parent : ts.isNamespaceImport(d) ? d.parent.parent : null; + if (!decl || !decl.moduleSpecifier) continue; + const ms = decl.moduleSpecifier.text; + if (isProjectSpec(ms, d.getSourceFile().fileName)) return null; + return `${libLabel(ms)}.${callee.getText().replace(/\s+/g, '').replace(/\(.*$/s, '')}`; + } + return null; +} +const reassignCache = new Map(); +function isReassigned(d) { + if (reassignCache.has(d)) return reassignCache.get(d); + const sym = checker.getSymbolAtLocation(d.name); let hit = false; + (function v(n) { + if (hit) return; + const target = ts.isBinaryExpression(n) && n.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && n.operatorToken.kind <= ts.SyntaxKind.LastAssignment ? n.left + : (ts.isPrefixUnaryExpression(n) || ts.isPostfixUnaryExpression(n)) && (n.operator === ts.SyntaxKind.PlusPlusToken || n.operator === ts.SyntaxKind.MinusMinusToken) ? n.operand : null; + if (target && ts.isIdentifier(target) && checker.getSymbolAtLocation(target) === sym) hit = true; + ts.forEachChild(n, v); + })(d.getSourceFile()); + reassignCache.set(d, hit); return hit; +} +function libName(callee) { + // `cors` imported from 'hono/cors' -> 'hono/cors.cors' + const id = ts.isPropertyAccessExpression(callee) ? callee.name : callee; + let s = checker.getSymbolAtLocation(id); + if (!s) return rootImport(callee); + const decls = s.declarations || []; + for (const d of decls) { + const ms = ts.isImportSpecifier(d) ? d.parent.parent.parent.moduleSpecifier.text : ts.isImportClause(d) ? d.parent.moduleSpecifier.text : null; + // a project module (relative, aliased, workspace): a library call only when that module re-exports a library + // function (O14 `export { cors } from 'hono/cors'`) or exports a member of a library call's result (O10) + if (ms && isProjectSpec(ms, d.getSourceFile().fileName)) return libViaProject(s); + if (ts.isImportSpecifier(d)) return `${libLabel(ms)}.${(d.propertyName || d.name).text}`; + if (ts.isImportClause(d)) return `${libLabel(ms)}.default`; + } + // O10: `const { upgradeWebSocket } = createBunWebSocket()`: a member of a library call's result is a library function + const lb = decls.find(x => ts.isBindingElement(x) && x.parent.parent.initializer && ts.isCallExpression(x.parent.parent.initializer) + && x.parent.parent.initializer.expression.getText() !== 'require'); + if (lb) { const inner = libName(lb.parent.parent.initializer.expression); if (inner) return `${inner}().${(lb.propertyName || lb.name).getText()}`; } + // `const { cors } = require('hono/cors')`: named by the required specifier, before alias resolution + const bd = decls.find(x => ts.isBindingElement(x) && x.parent.parent.initializer && ts.isCallExpression(x.parent.parent.initializer) + && x.parent.parent.initializer.expression.getText() === 'require' && ts.isStringLiteralLike(x.parent.parent.initializer.arguments[0])); + if (bd) { + const rs = bd.parent.parent.initializer.arguments[0].text; + if (isProjectSpec(rs, bd.getSourceFile().fileName)) return libViaRequire(bd.parent.parent.initializer, (bd.propertyName || bd.name).getText()); + return `${libLabel(rs)}.${(bd.propertyName || bd.name).getText()}`; + } + if (s.flags & ts.SymbolFlags.Alias) { try { s = checker.getAliasedSymbol(s); } catch { } } + const d = s.valueDeclaration || (s.declarations && s.declarations[0]); + if (d && !isSrcDecl(d)) { + const f = path.resolve(d.getSourceFile().fileName); const m = f.match(/node_modules\/((?:@[^/]+\/)?[^/]+)/); + return `${m ? m[1] : 'lib'}.${s.name}`; + } + // `const { cors } = require('hono/cors')` + if (d && ts.isBindingElement(d) && d.parent.parent.initializer && ts.isCallExpression(d.parent.parent.initializer) + && d.parent.parent.initializer.expression.getText() === 'require') { + return `${d.parent.parent.initializer.arguments[0].text}.${(d.propertyName || d.name).getText()}`; + } + return null; +} +// a binding that names a library function: `const { f } = libCall()` (O10) or `const { f } = require('pkg')` +function libOfBinding(d) { + if (!d || !ts.isBindingElement(d)) return null; + const init = d.parent && d.parent.parent && d.parent.parent.initializer; + if (!init || !ts.isCallExpression(init)) return null; + const nm = (d.propertyName || d.name).getText(); + if (init.expression.getText() === 'require') { + const a = init.arguments[0]; + if (!a || !ts.isStringLiteralLike(a)) return null; + return isProjectSpec(a.text, d.getSourceFile().fileName) ? libViaRequire(init, nm) : `${libLabel(a.text)}.${nm}`; + } + const inner = libName(init.expression); + return inner ? `${inner}().${nm}` : null; +} +// an import from a PROJECT module that ends at a library function: re-exported (`export { cors } from 'hono/cors'`, +// O14), or a project binding of one (`export const { upgradeWebSocket } = createBunWebSocket()`, O10) +function libViaProject(sym) { + let s = sym; + for (let i = 0; i < 12 && s && (s.flags & ts.SymbolFlags.Alias); i++) { + for (const d of s.declarations || []) { + const ed = ts.isExportSpecifier(d) ? d.parent.parent : null; + if (ed && ed.moduleSpecifier && !isProjectSpec(ed.moduleSpecifier.text, d.getSourceFile().fileName)) + return `${libLabel(ed.moduleSpecifier.text)}.${(d.propertyName || d.name).text}`; + } + let n; try { n = checker.getImmediateAliasedSymbol(s); } catch { n = null; } + if (!n || n === s) break; + s = n; + } + if (s && (s.flags & ts.SymbolFlags.Alias)) { try { s = checker.getAliasedSymbol(s); } catch { } } + const d = s && (s.valueDeclaration || (s.declarations && s.declarations[0])); + if (!d) return null; + if (!isSrcDecl(d)) { const m = path.resolve(d.getSourceFile().fileName).match(/node_modules\/((?:@[^/]+\/)?[^/]+)/); return `${m ? m[1] : 'lib'}.${s.name}`; } // `export * from 'pkg'` + return libOfBinding(d); +} +// a member of a PROJECT CommonJS module's exports that is a library function (`module.exports = { upgradeWebSocket }`) +function libViaRequire(reqCall, name) { + const v = requireValue(reqCall); + const p = v.k === 'obj' && v.props.get(name); + if (!p || !p.node) return null; + let id = p.node; + if (ts.isIdentifier(id) && id.parent && ts.isShorthandPropertyAssignment(id.parent)) { + const vs = checker.getShorthandAssignmentValueSymbol(id.parent); + return libOfBinding(vs && (vs.valueDeclaration || (vs.declarations && vs.declarations[0]))); + } + if (ts.isIdentifier(id)) return libOfBinding(declOf(id)); + return null; +} +// SPEC R2: a receiver whose DECLARED type is the app class (a property, field, getter, parameter) whose value +// the program does not show is an app of unknown prefix +function isTypedApp(expr) { + let t; + try { t = checker.getTypeAtLocation(expr); } catch { return false; } + const sym = t && (t.getSymbol() || t.aliasSymbol); + const d = sym && sym.declarations && sym.declarations[0]; + return !!(d && isHonoClassDecl(d)); +} +function unresolvedAppCall(name, e) { + const partial = (key, verb) => { out.partial.add(`${key}\t${verb}\tunresolved_app_value`); unknownAt(e, 'unresolved_app_value', key); }; + if (name === 'route') { + const sub = evalExpr(e.arguments[1]); + if (sub.k !== 'app') { unknownAt(e, 'unresolved_mount'); return UNK('app'); } + // O23: inside a plugin method that a host calls over a list (the O11 pass), the mount is a list mount: the engine's + // R13 reason, unresolved_mount; elsewhere a typed receiver of unknown value is unresolved_app_value (R2) + partialMount(sub, pluginPass ? 'unresolved_mount' : 'unresolved_app_value', e, []); + return UNK('app'); + } + if (VERBS.includes(name) || name === 'on' || name === 'openapi') { + const hs = argVals(e.arguments.slice(name === 'on' ? 2 : 1)); + const h = name === 'openapi' ? evalExpr(e.arguments[1]) : hs[hs.length - 1]; + const k = h && handlerKey(h); + if (k) { partial(k, name === 'all' ? 'ANY' : name === 'on' || name === 'openapi' ? '?' : name.toUpperCase()); if (h.k === 'fn') handlerFns.add(h.node); } + return UNK('app'); + } + return UNK('app'); +} +function isHonoClassDecl(d) { + return d && (ts.isClassDeclaration(d) || ts.isVariableDeclaration(d)) && d.name + && ['Hono', 'OpenAPIHono', 'HonoBase'].includes(d.name.getText()) && inPkg(d, ['hono', '@hono/zod-openapi']); +} +function classDeclOfNew(expr) { + let s = checker.getSymbolAtLocation(expr); + if (!s) return null; + if (s.flags & ts.SymbolFlags.Alias) { try { s = checker.getAliasedSymbol(s); } catch { } } + return s.valueDeclaration || (s.declarations && s.declarations[0]); +} + +// ── expressions ───────────────────────────────────────────────────────────── +let depth = 0; +// O15: a bound on nested evaluation. Every value is computed by recursion over declarations, so a cycle the +// memo sets do not catch would overflow the stack; past the bound the value is unknown (a declared gap) +let evalDepth = 0; +const EVAL_DEPTH_MAX = 1200; +const cycles = new Set(); +function noteCycle(n) { const l = loc(n); if (!cycles.has(l)) { cycles.add(l); if (DEBUG) process.stderr.write(`O15: evaluation cycle cut at ${l}\n`); } } +let evalCount = 0; +const inlineStack = [], hot = new Map(); +function evalExpr(e) { + if (DEBUG) { + if (e && evalCount < 3000000) { const l = loc(e); hot.set(l, (hot.get(l) || 0) + 1); } + if (++evalCount % 5000000 === 0) process.stderr.write(`O15 progress: ${evalCount} evaluations; inlined: ${inlineStack.join(' > ')}; hottest: ${[...hot].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([k, n]) => k + ' x' + n).join(', ')}\n`); + } + if (evalDepth >= EVAL_DEPTH_MAX) { if (e) noteCycle(e); return UNK('eval_depth'); } + evalDepth++; + try { return evalExpr0(e); } finally { evalDepth--; } +} +function evalExpr0(e) { + if (!e) return UNK('none'); + while (ts.isParenthesizedExpression(e) || ts.isAsExpression(e) || ts.isSatisfiesExpression?.(e) || ts.isNonNullExpression(e) || ts.isTypeAssertionExpression?.(e)) e = e.expression; + if (ts.isStringLiteral(e) || ts.isNoSubstitutionTemplateLiteral(e)) return { k: 'str', v: e.text }; + if (ts.isTemplateExpression(e)) { + let s = e.head.text; + for (const sp of e.templateSpans) { const v = evalExpr(sp.expression); if (v.k !== 'str') return UNK('template_part'); s += v.v + sp.literal.text; } + return { k: 'str', v: s }; + } + if (ts.isBinaryExpression(e) && e.operatorToken.kind === ts.SyntaxKind.PlusToken) { + const a = evalExpr(e.left), b = evalExpr(e.right); + return a.k === 'str' && b.k === 'str' ? { k: 'str', v: a.v + b.v } : UNK('concat_part'); + } + if (ts.isBinaryExpression(e) && (e.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken || e.operatorToken.kind === ts.SyntaxKind.BarBarToken)) { + // `x ?? []`: the left value when the program shows it, else unknown (a run-time value may be there) + const a = evalExpr(e.left); + if (a.k === 'undef') return evalExpr(e.right); + return a.k === 'unk' ? UNK('nullish') : a; + } + if (ts.isBinaryExpression(e) && e.operatorToken.kind === ts.SyntaxKind.EqualsToken) { + let v = evalExpr(e.right); env.set(e, v); + // SPEC R8a/R8b: an app holder re-pointed at a basePath clone of the SAME app under a condition is rebased only at + // run time: every route registered through it afterwards is partial (dynamic_mount_prefix), never exact + const rebase = (old) => (condDepth > 0 && v.k === 'app' && old && old.k === 'app' && old.store === v.store && old.base !== v.base && !v.dyn) + ? (v = { ...v, dyn: 'dynamic_mount_prefix' }) : v; + // SPEC 2 [iter2] flow through fields: `this.f = ` makes `this.f` that app in every method of the class + if (ts.isPropertyAccessExpression(e.left) && e.left.expression.kind === ts.SyntaxKind.ThisKeyword) { + const fd = declOf(e.left.name); if (fd && isSrcDecl(fd)) env.set(fd, rebase(env.get(fd))); + } + // R8b: the same for a `let` variable (`let app = new Hono(); if (base) app = app.basePath(base)`): the binding + // follows the assignment, so later registrations go through the (rebased) clone + if (ts.isIdentifier(e.left)) { + const vd = declOf(e.left); + if (vd && ts.isVariableDeclaration(vd) && isSrcDecl(vd) && !(vd.parent.flags & ts.NodeFlags.Const) && env.has(vd) && v.k === 'app') env.set(vd, rebase(env.get(vd))); + } + return v; + } + if (ts.isArrowFunction(e) || ts.isFunctionExpression(e)) return { k: 'fn', node: e }; + if (e.kind === ts.SyntaxKind.NullKeyword) return UNDEF; // `null` and `undefined` are both an absent value here + if (ts.isIdentifier(e)) { + if (e.text === 'undefined') return UNDEF; + const d = declOf(e); + if (d && ts.isParameter(d)) return env.has(d) ? env.get(d) : UNK('parameter'); + if (d && ts.isVariableDeclaration(d) && !(d.parent.flags & ts.NodeFlags.Const) && !env.has(d)) return UNK('mutable'); + const v = valueOfDecl(d); + if (v.k === 'str' && d && ts.isVariableDeclaration(d) && !(d.parent.flags & ts.NodeFlags.Const) && isReassigned(d)) return UNK('reassigned_let'); + return v; + } + if (ts.isArrayLiteralExpression(e)) { + const items = []; + for (const x of e.elements) { + if (ts.isSpreadElement(x)) { const v = evalExpr(x.expression); if (v.k === 'list') items.push(...v.items); else items.push(v); } + else items.push(evalExpr(x)); + } + return { k: 'list', items }; + } + if (ts.isObjectLiteralExpression(e)) { + const props = new Map(); + let open = false; + for (const p of e.properties) { + if (p.name && ts.isComputedPropertyName(p.name)) open = true; + if (ts.isGetAccessor(p) || ts.isSetAccessor(p)) open = true; + if (ts.isPropertyAssignment(p)) { + const pr = { node: p.initializer }; + // a nested object literal is evaluated now, while the parameters of an enclosing helper call are bound + // (`({ content: { 'application/json': { schema } } })`, hono-3-04); a parameter read keeps its argument + // inside a call being inlined every property is read NOW, as JavaScript does: read later, after the call's + // locals are reset, `{ handler: routes }` would evaluate the local's initializer again (a second, empty app) + if (ts.isObjectLiteralExpression(p.initializer) || depth > 0) pr.val = evalExpr(p.initializer); + else if (ts.isIdentifier(p.initializer)) { + const d = declOf(p.initializer); if (d && paramArgNode.has(d)) pr.argNode = paramArgNode.get(d); + // a local of the function being inlined (`{ handler: routes }` returned by a factory) is read NOW: read + // later, after the call's locals are reset, it would evaluate its initializer again (a second app) + } + props.set(p.name.getText().replace(/^['"]|['"]$/g, ''), pr); + } + else if (ts.isShorthandPropertyAssignment(p)) { + const vs = checker.getShorthandAssignmentValueSymbol(p); + const d = vs && (vs.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(vs) : vs); + const dd = d && (d.valueDeclaration || (d.declarations && d.declarations[0])); + const pr = { node: p.name, val: d ? valueOfDecl(dd) : UNK('shorthand') }; + if (dd && paramArgNode.has(dd)) pr.argNode = paramArgNode.get(dd); + props.set(p.name.text, pr); + } + else if (ts.isMethodDeclaration(p)) props.set(p.name.getText(), { val: { k: 'fn', node: p } }); + else if (ts.isSpreadAssignment(p)) { const v = evalExpr(p.expression); if (v.k === 'obj') { if (v.open) open = true; for (const [k, x] of v.props) props.set(k, x); } else if (v.k !== 'undef') open = true; } // `{ ...helper(schema), required: true }` + } + // O24: an app written into an entry of a route list (`[{ path, handler: app }]`) is served only if something + // mounts that entry; remember the entry so an app nothing mounts or serves is not reported as a root + if (e.parent && ts.isArrayLiteralExpression(e.parent)) for (const [, pr] of props) if (pr.val && pr.val.k === 'app' && !pr.val.store.listEntry) pr.val.store.listEntry = pr.node || e; + return { k: 'obj', props, node: e, open }; + } + if (ts.isPropertyAccessExpression(e) || ts.isElementAccessExpression(e)) { + const recv = calleeRecv.has(e.expression) ? calleeRecv.get(e.expression) : evalExpr(e.expression); + let name = ts.isPropertyAccessExpression(e) ? e.name.text : (ts.isStringLiteralLike(e.argumentExpression) ? e.argumentExpression.text : null); + if (name == null && ts.isElementAccessExpression(e) && recv.k === 'obj') { const kv = evalExpr(e.argumentExpression); if (kv.k === 'str') name = kv.v; } // `mods[path]` in a for..in + if (recv.k === 'client') return { ...recv, segs: [...recv.segs, name == null ? HOLE : name] }; // a dynamic key is one segment + if (recv.k === 'obj' && name != null && recv.props.has(name)) { const p = recv.props.get(name); if (!p.val) p.val = evalExpr(p.node); return p.val; } + if (recv.k === 'obj' && recv.open === false && name != null && ts.isObjectLiteralExpression(recv.node) && !assignedPropNames().has(name)) return UNDEF; + if (recv.k === 'undef') return UNDEF; // `x?.y` on an absent x (a plain read would throw: nothing runs either way) + if (recv.k === 'module' && name != null) { + const ed = moduleExportDecl(recv.sf, name); // through `export { x } from './x'` barrels + if (ed) return valueOfDecl(ed); + const s = checker.getSymbolAtLocation(e.name || e.argumentExpression); return s ? valueOfDecl(s.valueDeclaration) : UNK('module_member'); + } + if (recv.k === 'app' && name === 'fetch') { recv.store.fetchRead = true; return { k: 'fetch', app: recv }; } + // O17: `const createAdminMiddleware = adminFactory.createMiddleware` keeps the factory's method + if (recv.k === 'factory' && ['createMiddleware', 'createHandlers', 'createApp'].includes(name)) return { k: 'factoryMethod', factory: recv, name }; + if (ts.isPropertyAccessExpression(e)) { + const d = declOf(e.name); + if (d && isSrcDecl(d) && (ts.isPropertyDeclaration(d) || ts.isPropertySignature(d))) { + if (env.has(d)) return env.get(d); // a field assigned an app (`this.app = createApp()`) + if (ts.isPropertyDeclaration(d) && d.initializer && !evaluating.has(d)) { evaluating.add(d); const v = evalExpr(d.initializer); evaluating.delete(d); env.set(d, v); return v; } + } else if (d && isSrcDecl(d) && ts.isGetAccessorDeclaration(d) && d.body && depth < 8) return inline(d, []); // a getter is a call + else if (d && isSrcDecl(d)) return valueOfDecl(d); + } + return UNK('member'); + } + if (ts.isNewExpression(e)) { + const d = classDeclOfNew(e.expression); + if (isHonoClassDecl(d)) return newApp(e); + // a subclass of Hono declared in the project + if (d && ts.isClassDeclaration(d) && d.heritageClauses) { + for (const h of d.heritageClauses) for (const t of h.types) if (isHonoClassDecl(classDeclOfNew(t.expression))) return newApp(e); + } + if (d && ts.isClassDeclaration(d) && isSrcDecl(d) && depth < 8) { + for (const m of d.members) if (ts.isPropertyDeclaration(m) && m.initializer && !env.has(m)) { evaluating.add(m); env.set(m, evalExpr(m.initializer)); evaluating.delete(m); } + const ctor = d.members.find(m => ts.isConstructorDeclaration(m) && m.body); + if (ctor && !inlinedFns.has(ctor)) inline(ctor, e.arguments || []); + return { k: 'instance', cls: d }; + } + return UNK('new_other'); + } + if (ts.isCallExpression(e)) return evalCall(e); + if (ts.isAwaitExpression(e)) return evalExpr(e.expression); + if (ts.isConditionalExpression(e)) { + // `cond ? topoSort(list.filter(..)) : list.filter(..)`: two known lists are one list of their elements, a subset + // of which runs (R13b) + const a = evalExpr(e.whenTrue), b = evalExpr(e.whenFalse); + if (a.k === 'list' && b.k === 'list') { + const items = [...a.items]; for (const x of b.items) if (!items.includes(x)) items.push(x); + const same = a.items.length === b.items.length && a.items.every(x => b.items.includes(x)); + return { k: 'list', items, derived: same ? joinDerived(a.derived, b.derived) : 'filtered' }; + } + return UNK('conditional'); + } + return UNK('expr'); +} + +const VERBS = ['get', 'post', 'put', 'delete', 'options', 'patch', 'query', 'all']; +function argVals(args) { + const vs = []; + for (const a of args) { + if (ts.isSpreadElement(a)) { const v = evalExpr(a.expression); if (v.k === 'list') vs.push(...v.items); else vs.push(UNK('spread')); } + else vs.push(evalExpr(a)); + } + return vs; +} + +// O15: the receiver of `a.b(..)` as evalCall computed it. A user-function lookup evaluates the callee `a.b` again, +// which re-evaluated `a`: twice per link, 2^n for a long builder chain (`.option(..)` x 20) +const calleeRecv = new Map(); +// R12: module lists the program spells literally. `import.meta.glob()` is an object keyed by the +// matched files' paths (eager: the module; lazy: a function returning it), `require.context(dir, deep, /re/)` a +// function over its keys; Object.entries / values / keys / fromEntries over a known object follow the values +function globRegex(pat) { + let r = ''; + for (let i = 0; i < pat.length; i++) { + const ch = pat[i]; + if (ch === '*' && pat[i + 1] === '*') { r += pat[i + 2] === '/' ? '(?:.*/)?' : '.*'; i += pat[i + 2] === '/' ? 2 : 1; } + else if (ch === '*') r += '[^/]*'; + else if (ch === '?') r += '[^/]'; + else if (ch === '{') { const j = pat.indexOf('}', i); r += '(?:' + pat.slice(i + 1, j).split(',').map(x => x.replace(/[.+^$()|[\]\\]/g, '\\$&')).join('|') + ')'; i = j; } + else r += ch.replace(/[.+^$()|[\]\\]/g, '\\$&'); + } + return new RegExp('^' + r + '$'); +} +function filesMatching(fromFile, pats) { + const dir = path.dirname(path.resolve(fromFile)), hits = new Map(); + const pos = pats.filter(p => !p.startsWith('!')), neg = pats.filter(p => p.startsWith('!')).map(p => p.slice(1)); + for (const sf of srcFiles) { + let r = path.relative(dir, path.resolve(sf.fileName)).split(path.sep).join('/'); + if (!r.startsWith('.')) r = './' + r; + if (pos.some(p => globRegex(p).test(r)) && !neg.some(p => globRegex(p).test(r))) hits.set(r, sf); + } + return hits; +} +function moduleListCall(e) { + const c = e.expression; + if (!ts.isPropertyAccessExpression(c)) return null; + const obj = c.expression, nm = c.name.text; + if (ts.isMetaProperty(obj) && nm === 'glob') { + const a = e.arguments[0] && evalExpr(e.arguments[0]); + const pats = a && a.k === 'str' ? [a.v] : a && a.k === 'list' && a.items.every(x => x.k === 'str') ? a.items.map(x => x.v) : null; + if (!pats) return UNK('glob'); + const opt = e.arguments[1] && evalExpr(e.arguments[1]); + const eager = opt && opt.k === 'obj' && opt.props.has('eager') && opt.props.get('eager').node.kind === ts.SyntaxKind.TrueKeyword; + const props = new Map(); + for (const [k, sf] of filesMatching(e.getSourceFile().fileName, pats)) { + globbed.add(sf); evalModule(sf); + const mod = moduleExports(sf); + props.set(k, { val: eager ? mod : { k: 'thunk', val: mod } }); + } + return { k: 'obj', props, node: e }; + } + if (ts.isIdentifier(obj) && obj.text === 'require' && nm === 'context') { + const d = e.arguments[0] && evalExpr(e.arguments[0]), deep = e.arguments[1] && e.arguments[1].kind === ts.SyntaxKind.TrueKeyword; + const re = e.arguments[2] && ts.isRegularExpressionLiteral(e.arguments[2]) ? e.arguments[2].text : null; + if (!d || d.k !== 'str' || !re) return UNK('require_context'); + const m = re.match(/^\/(.*)\/([a-z]*)$/), rx = new RegExp(m[1], m[2]); + const base = path.resolve(path.dirname(e.getSourceFile().fileName), d.v), mods = new Map(); + for (const sf of srcFiles) { + const f = path.resolve(sf.fileName); if (!f.startsWith(base + path.sep)) continue; + const r = './' + path.relative(base, f).split(path.sep).join('/'); + if ((!deep && r.slice(2).includes('/')) || !rx.test(r)) continue; + globbed.add(sf); evalModule(sf); mods.set(r, moduleExports(sf)); + } + return { k: 'reqctx', mods }; + } + // `Object.assign(fn, { meta })` returns its first argument + if (ts.isIdentifier(obj) && obj.text === 'Object' && nm === 'assign' && e.arguments[0]) return evalExpr(e.arguments[0]); + if (ts.isIdentifier(obj) && obj.text === 'Object' && ['entries', 'values', 'keys', 'fromEntries'].includes(nm)) { + const v = e.arguments[0] && evalExpr(e.arguments[0]); + if (nm === 'fromEntries') { + if (!v || v.k !== 'list') return UNK('from_entries'); + const props = new Map(); + for (const it of v.items) if (it.k === 'list' && it.items[0] && it.items[0].k === 'str') props.set(it.items[0].v, { val: it.items[1] || UNK('entry') }); + return { k: 'obj', props, node: e, derived: v.derived }; + } + if (!v || v.k !== 'obj') return UNK('object_' + nm); + const ents = [...v.props].map(([k, p]) => [k, pv_(p)]); + const items = nm === 'entries' ? ents.map(([k, x]) => ({ k: 'list', items: [{ k: 'str', v: k }, x] })) : nm === 'values' ? ents.map(([, x]) => x) : ents.map(([k]) => ({ k: 'str', v: k })); + return { k: 'list', items, derived: v.derived }; + } + return null; +} +const globbed = new Set(), opaqueMounts = []; +let pluginPass = false; +function isModuleListArg(a) { + while (a && (ts.isParenthesizedExpression(a) || ts.isAsExpression(a) || ts.isNonNullExpression(a) || ts.isAwaitExpression(a))) a = a.expression; + return !!a && ts.isPropertyAccessExpression(a) && a.name.text === 'default'; +} +function methodOf(cls, name) { + for (let c = cls, i = 0; c && i < 10; i++) { + const m = c.members.find(x => ts.isMethodDeclaration(x) && x.body && x.name && x.name.getText() === name); + if (m) return m; + const h = c.heritageClauses && c.heritageClauses.find(x => x.token === ts.SyntaxKind.ExtendsKeyword); + const b = h && classDeclOfNew(h.types[0].expression); + c = b && ts.isClassDeclaration(b) && isSrcDecl(b) ? b : null; + } + return null; +} +function factoryCall(f, name, e) { + if (name === 'createApp') { const a = newApp(e); if (f.initApp && depth < 8) inline(f.initApp, [], [a]); return a; } + if (name === 'createMiddleware') return evalExpr(e.arguments[0]); + return { k: 'list', items: argVals(e.arguments) }; // createHandlers +} +// R13: array methods over a KNOWN list follow the values. map/flatMap run the callback per element; a filter, a +// sort, a slice (a subset or a new order) gives the same elements marked `derived`: which of them run, and in which +// order, is decided at run time, so a mount over it is partial (unresolved_mount), never exact +function joinDerived(...ds) { return ds.includes('filtered') ? 'filtered' : ds.includes('reordered') ? 'reordered' : undefined; } +function listMethod(recv, name, e) { + const cb = e.arguments[0] && evalExpr(e.arguments[0]); + const call = (it, i) => cb && cb.k === 'fn' && cb.node.body && depth < 8 ? inline(cb.node, [], [it, { k: 'str', v: String(i) }]) : UNK('callback'); + if (name === 'map') return { k: 'list', items: recv.items.map(call), derived: recv.derived }; + if (name === 'flatMap') return { k: 'list', items: recv.items.flatMap((it, i) => { const v = call(it, i); return v.k === 'list' ? v.items : [v]; }), derived: recv.derived }; + if (name === 'forEach') { recv.items.forEach((it, i) => call(it, i)); return UNK('void'); } + // R13b: a filter keeps a run-time SUBSET (each element's registrations are conditional); a sort / slice / reverse + // keeps the elements in a run-time ORDER + // a null element never survives the usual `filter((p) => !!p)`; and a body that reads it mounts nothing + if (name === 'filter') return { k: 'list', items: recv.items.filter(it => it.k !== 'undef'), derived: 'filtered' }; + if (['sort', 'toSorted', 'slice', 'reverse', 'toReversed'].includes(name)) return { k: 'list', items: recv.items, derived: recv.derived || 'reordered' }; + if (name === 'push' || name === 'unshift') { const vs = argVals(e.arguments); recv.items[name === 'push' ? 'push' : 'unshift'](...vs); recv.derived = joinDerived(recv.derived, 'reordered', ...loopCtx.map(c => c.kind)); return { k: 'str', v: String(recv.items.length) }; } + if (name === 'concat') { const more = argVals(e.arguments); return { k: 'list', items: [...recv.items, ...more.flatMap(v => v.k === 'list' ? v.items : [v])], derived: joinDerived(recv.derived, ...more.map(v => v.derived)) }; } + return null; +} +// property names some assignment `a. = ..` / `a[''] = ..` writes anywhere in the program: a read of such a +// name on an object literal that lacks it may see the written value, so it stays unknown +let assignedProps = null; +function assignedPropNames() { + if (assignedProps) return assignedProps; + assignedProps = new Set(); + for (const sf of srcFiles) (function v(n) { + if (ts.isBinaryExpression(n) && n.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && n.operatorToken.kind <= ts.SyntaxKind.LastAssignment) { + const l = n.left; + if (ts.isPropertyAccessExpression(l)) assignedProps.add(l.name.text); + else if (ts.isElementAccessExpression(l) && ts.isStringLiteralLike(l.argumentExpression)) assignedProps.add(l.argumentExpression.text); + } + if (ts.isCallExpression(n) && ts.isPropertyAccessExpression(n.expression) && n.expression.expression.getText() === 'Object' && ['assign', 'defineProperty', 'defineProperties'].includes(n.expression.name.text)) assignedProps.add('*'); + ts.forEachChild(n, v); + })(sf); + if (assignedProps.has('*')) assignedProps = { has: () => true }; // Object.assign(target, ..) can add any name + return assignedProps; +} +function evalCall(e) { + try { return evalCall0(e); } finally { if (ts.isPropertyAccessExpression(e.expression)) calleeRecv.delete(e.expression.expression); } +} +function evalCall0(e) { + const callee = e.expression; + const special = moduleListCall(e); + if (special) return special; + if (ts.isElementAccessExpression(callee)) { const f = evalExpr(callee); if (f.k === 'thunk') return f.val; } + if (ts.isIdentifier(callee) && callee.text === 'require') return requireValue(e); + if (callee.kind === ts.SyntaxKind.ImportKeyword) return requireValue(e); // O16: `await import('./m')` is that module + if (ts.isPropertyAccessExpression(callee)) { + const name = callee.name.text; + const recv = evalExpr(callee.expression); + calleeRecv.set(callee.expression, recv); // O15: read back below instead of evaluating the chain again + if (recv.k === 'app') return appMethod(recv, name, e); + if (recv.k !== 'app' && isTypedApp(callee.expression)) return unresolvedAppCall(name, e); + if (name === 'route' && e.arguments.length >= 2) { + // SPEC 2 [iter2]: a mount argument is never a root, even when the receiver is not recognised + const sub = evalExpr(e.arguments[1]); + if (sub.k === 'app') { partialMount(sub, 'unresolved_mount', e, []); return UNK('route_on_unknown'); } + } + if (recv.k === 'client') return clientCall(recv, name, e); + if (recv.k === 'factory' && ['createApp', 'createMiddleware', 'createHandlers'].includes(name)) return factoryCall(recv, name, e); + if (recv.k === 'list') { const r = listMethod(recv, name, e); if (r) return r; } + if (recv.k === 'undef') return UNDEF; // `routes.map(..)` on an absent list: nothing runs + if (recv.k === 'reqctx' && name === 'keys') return { k: 'list', items: [...recv.mods.keys()].map(v => ({ k: 'str', v })) }; + } + if (testCallbacks(e)) return UNK('test'); + const lname = libName(callee); + if (lname) { + const short = lname.split('.').pop(); + if (/^(hono\/factory|hono)\.createMiddleware$/.test(lname)) return evalExpr(e.arguments[0]); + if (/^hono\/factory\.createFactory$/.test(lname)) { + // SPEC 3.1: initApp's registrations happen first on every app the factory makes + const o = e.arguments[0] && evalExpr(e.arguments[0]); + const ia = o && o.k === 'obj' && o.props.get('initApp'); + const iv = ia && (ia.val || evalExpr(ia.node)); + return { k: 'factory', initApp: iv && iv.k === 'fn' ? iv.node : null }; + } + if (/^hono\/client\.hc$/.test(lname)) { + // JavaScript has no type argument: SPEC 3.5 matches the client against every served app (route_shape) + const js = /\.(js|mjs|cjs|jsx)$/.test(e.getSourceFile().fileName) && !e.typeArguments; + return { k: 'client', app: js ? 'ANY_APP' : clientApp(e), segs: [], via: 'hc' }; + } + if (/^hono\/testing\.testClient$/.test(lname)) { const a = evalExpr(e.arguments[0]); return { k: 'client', app: a.k === 'app' ? a : null, segs: [], via: 'testClient' }; } + if (/^@hono\/zod-openapi\.createRoute$/.test(lname)) return routeDef(e.arguments[0]); + if (/^@hono\/node-server\.serve$/.test(lname)) { serveSite(e); return UNK('server'); } + if (short === 'createRoute' || short === 'serve') return UNK('lib'); + const lf = { k: 'libfn', name: lname, site: loc(e) }; + if (/^(@hono\/zod-validator\.zValidator|hono\/validator\.validator)$/.test(lname) && e.arguments.length >= 2) { + const t = evalExpr(e.arguments[0]); + if (t.k === 'str') lf.validator = { target: t.v, schema: short === 'zValidator' ? e.arguments[1] : null }; + } + if (/^hono\/combine\.(every|some|except)$/.test(lname)) { + // the combinator is the middleware node; the project functions it runs are entry points + const as = argVals(short === 'except' ? e.arguments.slice(1) : e.arguments); + for (const v of as) if (v.k === 'fn') combinedFns.add(loc(v.node)); + if (short === 'except') { + // SPEC 3.1: applies to its scope minus the literal excluded paths + const ex = evalExpr(e.arguments[0]); + const xs = ex.k === 'list' ? ex.items : [ex]; + if (xs.every(x => x.k === 'str')) lf.except = xs.map(x => x.v); + else unknownAt(e, 'dynamic_except'); + } + } + return lf; + } + // a user function called here: inline it (factories, register(app) helpers, middleware factories) + if (ts.isIdentifier(callee) || ts.isPropertyAccessExpression(callee)) { + const f = evalExpr(callee); + if (f.k === 'factoryMethod') return factoryCall(f.factory, f.name, e); + if (f.k === 'thunk') return f.val; + if (f.k === 'reqctx') { const k = evalExpr(e.arguments[0]); return k.k === 'str' && f.mods.has(k.v) ? f.mods.get(k.v) : UNK('require_context_key'); } + // a method called on an instance the program built (`s.controller()` with `s` = new OAuth()): that class's own + // method (or an inherited one), never every implementation of the declared type's method + if (ts.isPropertyAccessExpression(callee) && calleeRecv.has(callee.expression) && calleeRecv.get(callee.expression).k === 'instance' && depth < 8) { + const m = methodOf(calleeRecv.get(callee.expression).cls, callee.name.text); + if (m) return inline(m, e.arguments); + } + if (f.k === 'fn' && f.node.body && depth < 8) return inline(f.node, e.arguments); + if (f.k === 'fn' && !f.node.body && ts.isPropertyAccessExpression(callee) && depth < 8) { + // an abstract / interface method on a receiver the program does not pin down: every project implementation + // may run (hono-2-01), so each is evaluated and their results are all candidates + const impls = implementationsOf(f.node); + const vals = impls.map(m => inline(m, e.arguments)); + return vals.length === 1 ? vals[0] : { k: 'list', items: vals }; + } + } + if (testCallbacks(e)) return UNK('test'); + // O26: the arguments of a call the oracle cannot follow (`expect(await app.request('/x'))`) still run, in place: + // left for a later pass, `app` would be read after the test's locals are reset, a second, empty app (unserved) + for (const a of e.arguments) if (!ts.isFunctionLike(a)) evalExpr(a); + return UNK('call'); +} + +// SPEC R1 (O1): an app built and exercised inside a test callback is a served root, so a test runner's +// callbacks (describe/it/test/hooks, incl. .only/.skip/.each(...)) are evaluated where they are registered +const TEST_FNS = new Set(['describe', 'it', 'test', 'beforeAll', 'beforeEach', 'afterAll', 'afterEach', 'bench', 'suite']); +function testCallbacks(e) { + let n = e.expression; + while (ts.isPropertyAccessExpression(n) || ts.isCallExpression(n)) n = n.expression; + if (!ts.isIdentifier(n) || !TEST_FNS.has(n.text)) return false; + const d = declOf(n); + if (d && isSrcDecl(d) && !ts.isImportSpecifier(d) && !(ts.isFunctionDeclaration(d) && !d.body)) return false; // a project function called `test` (an ambient `declare function it` is the runner's) + for (const a of e.arguments) if ((ts.isArrowFunction(a) || ts.isFunctionExpression(a)) && depth < 8) inline(a, []); + return true; +} +function implementationsOf(decl) { + const owner = decl.parent, name = decl.name && decl.name.getText(), out = []; + if (!owner || !name) return out; + const ownerSym = owner.name && checker.getSymbolAtLocation(owner.name); + for (const sf of srcFiles) (function v(n) { + if (ts.isClassDeclaration(n) && n !== owner) { + const t = checker.getTypeAtLocation(n); + const bases = [...(checker.getBaseTypes ? (t.isClassOrInterface() ? checker.getBaseTypes(t) : []) : []), ...((n.heritageClauses || []).flatMap(h => h.types.map(x => checker.getTypeAtLocation(x))))]; + if (bases.some(b => b.getSymbol() === ownerSym)) for (const m of n.members) if (ts.isMethodDeclaration(m) && m.body && m.name.getText() === name) out.push(m); + } + ts.forEachChild(n, v); + })(sf); + return out; +} +function requireValue(e) { + const s = e.arguments[0] && ts.isStringLiteralLike(e.arguments[0]) ? e.arguments[0].text : null; + if (!s || !isProjectSpec(s, e.getSourceFile().fileName)) return UNK('require_pkg'); + const r = resolveSpec(s, e.getSourceFile().fileName); + const sf = r && program.getSourceFile(r.resolvedFileName); + if (!sf) return UNK('require_unresolved'); + evalModule(sf); + return moduleExports(sf); +} +// the declaration an ES module exports under `name` (through re-exports) +function moduleExportDecl(sf, name) { + const ms = checker.getSymbolAtLocation(sf); + let ex = ms && checker.getExportsOfModule(ms).find(x => x.name === name); + if (!ex) return null; + if (ex.flags & ts.SymbolFlags.Alias) { try { ex = checker.getAliasedSymbol(ex); } catch { } } + return ex.valueDeclaration || (ex.declarations && ex.declarations[0]) || null; +} +function moduleExports(sf) { + for (const st of sf.statements) { + if (ts.isExpressionStatement(st) && ts.isBinaryExpression(st.expression) && st.expression.left.getText() === 'module.exports') return env.get(st.expression) ?? evalExpr(st.expression.right); + } + // O7: `module.exports.a = v` / `exports.a = v` make an exports object of those members + const props = new Map(); + for (const st of sf.statements) { + const be = ts.isExpressionStatement(st) && ts.isBinaryExpression(st.expression) && st.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken ? st.expression : null; + const m = be && be.left.getText().match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)$/); + if (m) props.set(m[1], { node: be.right, val: env.get(be) }); + } + return props.size ? { k: 'obj', props, node: sf } : { k: 'module', sf }; +} + +const inlinedFns = new Set(), handlerFns = new Set(); +const paramArgNode = new Map(); // a parameter of a function being inlined -> the argument expression it is bound to +function localDecls(fnNode) { + const out = []; + (function v(n) { if (n !== fnNode && ts.isFunctionLike(n)) return; if (ts.isVariableDeclaration(n) || ts.isBindingElement(n)) out.push(n); ts.forEachChild(n, v); })(fnNode.body || fnNode); + return out; +} +function inline(fnNode, args, vals) { + inlinedFns.add(fnNode); + depth++; + if (DEBUG) inlineStack.push(loc(fnNode)); + const saved = []; + for (const d of localDecls(fnNode)) { saved.push([d, env.get(d)]); env.delete(d); } // R6: a fresh app per call + const savedArgs = fnNode.parameters.map(p => [p, paramArgNode.get(p)]); + // a parameter passed straight on (`jsonBody(schema, ..)` inside another helper) keeps the ORIGINAL argument + const argNodes = fnNode.parameters.map((p, i) => { + const a = !vals && !p.dotDotDotToken ? args[i] : null; + if (!a) return null; + const d = ts.isIdentifier(a) ? declOf(a) : null; + return d && paramArgNode.has(d) ? paramArgNode.get(d) : a; + }); + fnNode.parameters.forEach((p, i) => { + if (argNodes[i]) paramArgNode.set(p, argNodes[i]); else paramArgNode.delete(p); + saved.push([p, env.get(p)]); + if (vals) env.set(p, vals[i] || UNK('missing_arg')); + else if (p.dotDotDotToken) env.set(p, { k: 'list', items: argVals(args.slice(i)) }); + // a parameter a real call leaves out is undefined (R17: `hit(path)` sends no init, so GET), not unknown + else env.set(p, args[i] ? evalExpr(args[i]) : (p.initializer ? evalExpr(p.initializer) : p.questionToken ? UNDEF : UNK('missing_arg'))); + }); + let ret = UNK('no_return'); + retAlts.push([]); + if (fnNode.body && !ts.isBlock(fnNode.body)) ret = evalExpr(fnNode.body); + else if (fnNode.body) ret = evalBlock(fnNode.body.statements) ?? UNK('no_return'); + // SPEC 3.1 (hono-3-03): a factory whose returns are several middleware (a pass-through arrow in a branch, a library + // call otherwise) yields ALL of them, as alternatives at one chain position + const alts = retAlts.pop(); + const mwLike = v => v && (v.k === 'fn' || v.k === 'libfn'); + if (alts.length && alts.every(mwLike) && (mwLike(ret) || (ret.k === 'unk' && ret.why === 'no_return'))) { + const items = mwLike(ret) ? [...alts, ret] : alts; + ret = items.length === 1 ? items[0] : { k: 'alt', items }; + } + for (const [p, a] of savedArgs) { if (a === undefined) paramArgNode.delete(p); else paramArgNode.set(p, a); } + for (const [p, v] of saved) { if (v === undefined) env.delete(p); else env.set(p, v); } + if (DEBUG) inlineStack.pop(); + depth--; + return ret; +} + +const pv_ = p => p.val || (p.val = evalExpr(p.node)); // an object property's value, memoised +function routeDef(arg) { + const o = evalExpr(arg); + if (o.k !== 'obj') return UNK('route_object'); + const m = o.props.get('method'), p = o.props.get('path'); + const mv = m ? evalExpr(m.node) : UNK('no_method'), pv = p ? evalExpr(p.node) : UNK('no_path'); + const mwp = o.props.get('middleware'); + // SPEC 3.9: the request and response schemas of a route object, by validation target + const schemas = []; + const rq = o.props.get('request'), rqv = rq && (rq.val || evalExpr(rq.node)); + if (rqv && rqv.k === 'obj') { + for (const [prop, target] of [['params', 'param'], ['query', 'query'], ['headers', 'header'], ['cookies', 'cookie']]) { + const x = rqv.props.get(prop); if (x && x.node) schemas.push([target, x.argNode || x.node]); + } + const body = rqv.props.get('body'), bv = body && pv_(body); + const content = bv && bv.k === 'obj' && bv.props.get('content'), cv = content && pv_(content); + if (cv && cv.k === 'obj') for (const [mt, x] of cv.props) { + const mv2 = pv_(x), sc = mv2.k === 'obj' && mv2.props.get('schema'); + if (sc) schemas.push([/json/.test(mt) ? 'json' : /form/.test(mt) ? 'form' : mt, sc.argNode || sc.node]); + } + } + // hono-3-04: a response written by a project helper (`200: jsonContent(User, 'me')`) names the helper's argument + const rs = o.props.get('responses'), rsv = rs && pv_(rs); + if (rsv && rsv.k === 'obj') for (const [, x] of rsv.props) { + const r1 = pv_(x), c1 = r1.k === 'obj' && r1.props.get('content'), cv1 = c1 && pv_(c1); + if (cv1 && cv1.k === 'obj') for (const [, y] of cv1.props) { const yv = pv_(y), sc = yv.k === 'obj' && yv.props.get('schema'); if (sc) schemas.push(['response', sc.argNode || sc.node]); } + } + return { k: 'routedef', method: mv, path: pv, middleware: mwp ? evalExpr(mwp.node) : null, node: arg, schemas }; +} + +function clientApp(e) { + const ta = e.typeArguments && e.typeArguments[0]; + if (!ta) return null; + let t = ta; + for (let i = 0; i < 4 && t; i++) { + if (ts.isTypeQueryNode(t)) { const v = evalExpr(t.exprName); return v.k === 'app' ? v : null; } + if (ts.isTypeReferenceNode(t)) { + let s = checker.getSymbolAtLocation(t.typeName); + if (s && s.flags & ts.SymbolFlags.Alias) s = checker.getAliasedSymbol(s); + const d = s && s.declarations && s.declarations[0]; + t = d && ts.isTypeAliasDeclaration(d) ? d.type : null; + } else break; + } + return null; +} + +function serveSite(e) { + const o = e.arguments[0] && evalExpr(e.arguments[0]); + if (o && o.k === 'obj' && o.props.has('fetch')) evalExpr(o.props.get('fetch').node); +} + +function appMethod(app, name, e) { + const args = e.arguments, site = loc(e); + if (VERBS.includes(name)) { + const vs = argVals(args); + let p, hs; + if (vs[0] && vs[0].k === 'str') { app.path = vs[0].v; p = vs[0]; hs = vs.slice(1); } + else if (vs[0] && vs[0].k === 'unk' && args[0] && isStringTyped(args[0])) { p = vs[0]; hs = vs.slice(1); } + else { p = { k: 'str', v: app.path }; hs = vs; } + hs.forEach((h, i) => addEntry(app, name.toUpperCase(), p, h, i === hs.length - 1 ? 'handler' : 'mw', site, e)); + return app; + } + if (name === 'on') { + const mv = evalExpr(args[0]), pv = evalExpr(args[1]); + const ms = mv.k === 'list' ? mv.items : [mv], ps = pv.k === 'list' ? pv.items : [pv]; + const hs = argVals(args.slice(2)); + if (ms.some(m => m.k !== 'str')) { const k = handlerKey(hs[hs.length - 1] || UNK('')); out.partial.add(`${k}\t?\tdynamic_verb`); unknownAt(e, 'dynamic_verb', k); return app; } + if (ps.some(x => x.k !== 'str')) { for (const m of ms) addEntry(app, m.v.toUpperCase(), UNK('p'), hs[hs.length - 1], 'handler', site, e); return app; } + for (const p of ps) { app.path = p.v; for (const m of ms) hs.forEach((h, i) => addEntry(app, m.v.toUpperCase(), p, h, i === hs.length - 1 ? 'handler' : 'mw', site, e)); } + return app; + } + if (name === 'use') { + const vs = argVals(args); + let p, hs; + if (vs[0] && vs[0].k === 'str') { p = vs[0]; hs = vs.slice(1); } + else if (vs[0] && vs[0].k === 'unk' && args[0] && isStringTyped(args[0])) { for (const h of vs.slice(1)) unknownAt(e, 'dynamic_route_path', handlerKey(h) || undefined); return app; } + else { p = { k: 'str', v: '*' }; hs = vs; } + app.path = p.v; + for (const h of hs) addEntry(app, 'ALL', p, h, 'mw', site, e); + return app; + } + if (name === 'basePath') { + const pv = evalExpr(args[0]); + if (pv.k !== 'str') return { k: 'app', store: app.store, base: app.base, path: '/', origin: app.origin, dyn: 'dynamic_mount_prefix' }; + return { k: 'app', store: app.store, base: mergePath(app.base, pv.v), path: '/', origin: app.origin }; + } + if (name === 'route') { + const pv = evalExpr(args[0]), subv = evalExpr(args[1]); + // a call dispatched over several implementations yields several apps: each is mounted the same way + const subs = subv.k === 'list' ? subv.items.filter(x => x.k === 'app') : [subv]; + if (!subs.length || subs[0].k !== 'app') { unknownAt(e, 'unresolved_mount'); if (isModuleListArg(args[1])) opaqueMounts.push(e); return app; } + for (const sub of subs) mountOne(app, pv, sub, e, site); + return app; + } + return appMethod2(app, name, e); +} +// SPEC 3.3 [iter2]: a partial route (prefix unknown) still has a decidable chain: the sub-app's OWN middleware (matched +// inside the sub-app, whose paths are all under the one unknown prefix) after the parent's `*` / `/*` middleware +// registered before the mount. `mwp` rows: (mw, handler, verb, position); path and app are not decidable +const partialMw = new Set(); +function partialMount(sub, reason, e, parentMws) { + sub.store.mounted = true; + const ents = [...sub.store.entries]; + const router = new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }); + ents.forEach((en, i) => router.add(en.method, en.path, i)); + ents.forEach((en, i) => { + if (en.kind !== 'handler') return; + const V = en.method === 'ALL' ? 'ANY' : en.method; + out.partial.add(`${en.key}\t${V}\t${reason}`); unknownAt(e, reason, en.key); + let matched; try { matched = router.match(en.method === 'ALL' ? 'GET' : en.method, samplePath(en.path))[0].map(x => x[0]); } catch { matched = null; } + if (!matched || !matched.includes(i)) return; + const seq = [...(parentMws || []), ...matched.filter(j => j < i).map(j => ents[j])]; + let at = -1, lastAlt; + // a run-time middleware list before the route makes its partial chain dynamic too + if (seq.some(x => x.kind === 'dyn')) out.unknown.add(`${en.site}\tdynamic_middleware_list\t${en.key}`); + for (const x of seq) { + if (x.kind === 'dyn' || !(x.kind === 'mw' || callsNext(x.val)) || (x.val.except && x.val.except.includes(en.path))) continue; + if (!(x.alt && x.alt === lastAlt)) at++; + lastAlt = x.alt; partialMw.add(`${x.key}\t${en.key}\t${V}\t${at}`); + } + }); +} +// the parent's middleware that applies to every path (`*`, `/*`), registered before now, and the scoped ones that +// cannot be decided against an unknown prefix +function parentStarMws(app, e) { + const all = [...app.store.entries, ...(app.store.dynMws || [])].sort((a, b) => a.seq - b.seq); + const star = []; + for (const m of all) if (m.kind === 'dyn' && m.method === 'ALL' && (m.path === '*' || m.path === '/*' || m.anyBase)) star.push(m); + else if (m.kind === 'mw' && m.method === 'ALL') { + if (m.path === '*' || m.path === '/*' || m.anyBase) star.push(m); + else if (m.val.k === 'fn') out.unknown.add(`${loc(e)}\tpartial_route_scope\t${m.key}`); + } + return star; +} +function mountOne(app, pv, sub, e, site) { + { + sub.store.mounted = true; + // R13b: a mount inside a loop over a filtered / reordered list of KNOWN elements is exact (conditional when + // filtered); only an undecidable path is partial + const why = app.dyn || (pv.k !== 'str' ? 'dynamic_mount_prefix' : null); + if (why) { + // every route of the sub-app is still emitted, partially, with the reason: never dropped; SPEC 3.3 [iter2]: a + // scoped `use` already on this app cannot be decided against routes whose prefix is unknown + const pm = sub.store.entries.some(r => r.kind === 'handler') ? parentStarMws(app, e) : []; + partialMount(sub, why, e, pm); + return app; + } + // the EFFECTIVE prefix: route() on a basePath'd app mounts under mergePath(base, prefix) (hono-base.js) + out.submount.push(`${mergePath(app.base, pv.v)}\t${loc(e.expression.name)}`); + const target = { ...app, base: mergePath(app.base, pv.v) }; + const ms = ++mountSeq, tag = loopTagNow(), filtered = loopCtx.some(c => c.kind === 'filtered'); + for (const r of [...sub.store.entries]) { + target.store.entries.push({ ...r, seq: ++entrySeq, path: mergePath(target.base, r.path), site: r.site, alt: r.alt && `${r.alt}m${ms}`, loopTag: tag ? [...(r.loopTag || []), ...tag] : r.loopTag }); + if (filtered && r.kind === 'handler') unknownAt(e, 'conditional_registration', r.key); + } + return app; + } +} +function appMethod2(app, name, e) { + const args = e.arguments, site = loc(e); + if (name === 'mount') { + const pv = evalExpr(args[0]); + if (pv.k !== 'str') { unknownAt(e, 'dynamic_mount_prefix'); return app; } + const fv = evalExpr(args[1]); + if (fv.k === 'fn') { + // O8: a mounted foreign handler is a route (ANY prefix/*) with a middleware chain like any other + app.store.foreign = app.store.foreign || []; app.store.foreign.push(loc(fv.node)); + app.store.entries.push({ seq: ++entrySeq, method: 'ALL', path: mergePath(app.base, pv.v, '*'), key: loc(fv.node), kind: 'handler', mount: true, site, val: fv, regFn: enclosingFn(e) }); + } + else unknownAt(e, 'foreign_mount'); + return app; + } + if (name === 'openapi') { + const rd = evalExpr(args[0]); + if (rd.k !== 'routedef' || rd.method.k !== 'str' || rd.path.k !== 'str') { unknownAt(e, rd.k === 'routedef' && rd.method.k !== 'str' ? 'dynamic_verb' : 'dynamic_route_path', handlerKey(evalExpr(args[1])) || undefined); return app; } + const h = evalExpr(args[1]); + const mws = rd.middleware ? (rd.middleware.k === 'list' ? rd.middleware.items : [rd.middleware]) : []; + const p = { k: 'str', v: rd.path.v.replaceAll(/\/{(.+?)}/g, '/:$1') }; + app.path = p.v; + for (const m of mws) addEntry(app, rd.method.v.toUpperCase(), p, m, 'mw', site, e); + addEntry(app, rd.method.v.toUpperCase(), p, h, 'handler', site, e); + if (app.store.entries.length) app.store.entries[app.store.entries.length - 1].routedef = rd; + return app; + } + if (name === 'notFound' || name === 'onError') { + const h = evalExpr(args[0]); + if (h.k === 'fn') app.store[name] = h; + return app; + } + if (name === 'request') { requestCall(app, e); return UNK('response'); } + if (name === 'fetch') { app.store.fetchRead = true; requestCall(app, e, true); return UNK('response'); } + if (name === 'fire') app.store.fetchRead = true; + if (name === 'doc' || name === 'doc31' || name === 'fire') return app; + return UNK('app_method'); +} + +function isStringTyped(n) { const t = checker.getTypeAtLocation(n); return !!(t.flags & (ts.TypeFlags.StringLike)); } + +function enclosingFn(n) { + for (let p = n.parent; p; p = p.parent) if (ts.isFunctionLike(p) && p.body) return loc(p); + return `${rel(n.getSourceFile().fileName)}:1:1`; // the module initializer, as the IR places it +} + +function requestCall(app, e, isFetch) { + // app.request(url, init) | app.request(new Request(url, init)) | app.fetch(new Request(url, init), env) + let urlArg = e.arguments[0], initArg = isFetch ? null : e.arguments[1]; + if (urlArg && ts.isNewExpression(urlArg) && urlArg.expression.getText() === 'Request') { initArg = urlArg.arguments && urlArg.arguments[1]; urlArg = urlArg.arguments && urlArg.arguments[0]; } + else if (isFetch) return; // `fetch(req, env, ctx) { return app.fetch(req, env, ctx) }` forwards a request (R4): no call of its own + let pv = evalExpr(urlArg); + // SPEC 3.5 [iter2]: a template's undecidable hole stands for exactly one segment + if (pv.k !== 'str' && urlArg && ts.isTemplateExpression(urlArg)) { + let t = urlArg.head.text; + for (const sp of urlArg.templateSpans) { const v = evalExpr(sp.expression); t += (v.k === 'str' ? v.v : HOLE) + sp.literal.text; } + pv = { k: 'str', v: t }; + } + if (pv.k !== 'str') { unknownAt(e, 'dynamic_rpc_path'); return; } + const reqPath = /^https?:\/\//.test(pv.v) ? pv.v.replace(/^https?:\/\/[^/]*/, '') || '/' : pv.v; + const method = httpMethodOf(initArg); + pendingRequests.push({ app, method, path: reqPath.split(/[?#]/)[0], from: enclosingFn(e), site: loc(e) }); // R9 +} +// SPEC R17 (O25): the HTTP method a request init gives. No init, an absent one, or a known object without `method` +// is GET (the default); a literal `method` is that verb; anything else (an init the program does not show, a +// non-literal method, a `method` some assignment may set later, hono-6-03) is '*': not decidable, so the request +// joins every route on its path, on any verb, with Confidence method_unknown (base_unknown wins when both are unknown) +function httpMethodOf(initArg) { + const init = initArg && evalExpr(initArg); + if (!init || init.k === 'undef') return 'GET'; + if (init.k !== 'obj') return '*'; + if (init.props.has('method')) { const m = evalExpr(init.props.get('method').node); return m.k === 'str' ? m.v.toUpperCase() : '*'; } + return init.open || assignedPropNames().has('method') ? '*' : 'GET'; +} +const ANY_VERBS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']; +const HOLE = '\u0000'; +const FILE_URL = 'file:'; +const LOCAL_URL = /^https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(?::[^/?#]*)?([/?#].*)?$/; +// the URL a fetch spells, with HOLE for every piece the program does not show (R14: a non-literal base is a hole) +function urlText(x) { + if (!x) return HOLE; + while (ts.isParenthesizedExpression(x) || ts.isAsExpression(x) || ts.isNonNullExpression(x)) x = x.expression; + if (ts.isStringLiteralLike(x)) return x.text; + if (ts.isTemplateExpression(x)) { let t = x.head.text; for (const sp of x.templateSpans) t += urlText(sp.expression) + sp.literal.text; return t; } + if (ts.isBinaryExpression(x) && x.operatorToken.kind === ts.SyntaxKind.PlusToken) return urlText(x.left) + urlText(x.right); + if (ts.isNewExpression(x) && x.expression.getText() === 'URL' && x.arguments && x.arguments.length >= 1) { + const p = urlText(x.arguments[0]); + if (x.arguments.length === 1 || /^https?:\/\//.test(p)) return p; + // O21: `new URL('../x.ttf', import.meta.url)` is a file next to the module, not a request + const ba = x.arguments[1]; + if (ts.isPropertyAccessExpression(ba) && ts.isMetaProperty(ba.expression) && ba.name.text === 'url') return FILE_URL; + const b = urlText(x.arguments[1]), o = b.match(/^https?:\/\/[^/?#]*/); + return (o ? o[0] : HOLE) + (p.startsWith('/') ? p : '/' + p); + } + const v = evalExpr(x); + return v.k === 'str' ? v.v : HOLE; +} +function localFetch(e) { + const a = e.arguments[0]; + if (!a) return; + const u = urlText(a); + let p = null, conf = ''; + const m = u.match(LOCAL_URL); + const sl = !m && u.match(/^https?:\/\/([A-Za-z0-9_-]+)(?::[^/?#]*)?([/?#].*)?$/); // R14a: a single-label host + if (m) p = m[1] || '/'; + else if (sl && !/^\d+$/.test(sl[1])) { p = sl[2] || '/'; conf = 'base_unknown'; } + else { + // R14: a base the program does not spell (a parameter, an env read, `env.X ?? 'http://localhost:3003'`) may reach + // this program's routes: joined on the path with confidence base_unknown. A literal external host never joins + const r = u.startsWith(HOLE) ? u.replace(/^\u0000+(?::[^/?#]*)?/, '') : /^https?:\/\/\u0000/.test(u) ? u.replace(/^https?:\/\/[^/?#]*/, '') : null; + if (r && r.startsWith('/')) { p = r; conf = 'base_unknown'; } + } + if (p == null) return; + // O21: a base_unknown path with no literal segment (`${base}/${key}`) names no route + if (conf && !p.split(/[?#]/)[0].split('/').some(x => x && x !== HOLE)) return; + const method = httpMethodOf(e.arguments[1]); + pendingRequests.push({ app: 'ANY_APP', method, path: (p.split(/[?#]/)[0]) || '/', from: enclosingFn(e), site: loc(e), local: true, conf }); +} +// a path with holes against a route pattern: a hole, a `:param` (any form) is one segment; a trailing `*` is the rest +function shapeMatch(route, p) { + const r = route.split('/').filter(Boolean), q = p.split('/').filter(Boolean); + for (let i = 0; i < r.length; i++) { + if (r[i] === '*' && i === r.length - 1) return true; + if (i >= q.length) return false; + // hono-5-07: `/api/skills${path}`: a segment of literal text then a hole that may span segments matches a route + // segment starting with that text, and anything after it + if (q[i] !== HOLE && q[i].includes(HOLE) && i === q.length - 1) return r[i].startsWith(q[i].split(HOLE)[0]) || (r[i].startsWith(':') && q[i].indexOf(HOLE) > 0); + if (q[i] === HOLE || r[i].startsWith(':') || r[i] === '*') continue; + if (r[i] !== q[i]) return false; + } + return r.length === q.length; +} +const CLIENT_VERBS = { $get: 'GET', $post: 'POST', $put: 'PUT', $delete: 'DELETE', $patch: 'PATCH', $options: 'OPTIONS' }; +function clientCall(c, name, e) { + if (name in CLIENT_VERBS) pendingRpc.push({ c, method: CLIENT_VERBS[name], from: enclosingFn(e), site: loc(e) }); + return UNK('client_response'); +} +const pendingRequests = [], pendingRpc = []; +const platformFns = new Set(); +const combinedFns = new Set(); + +// ── statements ────────────────────────────────────────────────────────────── +function evalBlock(stmts) { + for (const st of stmts) { const r = evalStmt(st); if (r) return r; } + return null; +} +function evalStmt(st) { + if (ts.isVariableStatement(st)) { + for (const d of st.declarationList.declarations) { + if (!d.initializer || env.has(d)) continue; + if (ts.isIdentifier(d.name)) env.set(d, evalExpr(d.initializer)); + else evalExpr(d.initializer); + } + return null; + } + if (ts.isExpressionStatement(st)) { evalExpr(st.expression); return null; } + if (ts.isExportAssignment(st)) { + const v = evalExpr(st.expression); env.set(st, v); + // `export default { fetch: app.fetch, scheduled, queue }`: the platform calls the others too (SPEC 3.6) + if (v.k === 'obj' && v.props.has('fetch')) for (const k of ['scheduled', 'queue']) { + const p = v.props.get(k); if (!p) continue; + const f = p.val || evalExpr(p.node); if (f.k === 'fn') platformFns.add(loc(f.node)); + } + return null; + } + if (ts.isReturnStatement(st)) return st.expression ? evalExpr(st.expression) : UNK('void'); + if (ts.isIfStatement(st)) { + const r0 = condReason; condReason = 'conditional_registration'; condDepth++; + const s0 = entrySeq; + const rt = evalStmt(st.thenStatement); + const s1 = entrySeq; + const re = st.elseStatement ? evalStmt(st.elseStatement) : null; + // if / else each registering ONE middleware on the same app: one of them runs, at one chain position + if (st.elseStatement && s1 - s0 === 1 && entrySeq - s1 === 1) { + const a = stores.flatMap(x => x.entries).find(x => x.seq === s0 + 1), b = stores.flatMap(x => x.entries).find(x => x.seq === s1 + 1); + if (a && b && a.kind === 'mw' && b.kind === 'mw' && a.method === b.method && a.path === b.path && !a.alt && !b.alt + && stores.some(x => x.entries.includes(a) && x.entries.includes(b))) a.alt = b.alt = `a${++altSeq}`; + } + // R15 clause 2: if / else registering DIFFERENT numbers of middleware on one app: each branch starts at the same + // position, and every later position on a chain holding them is decided at run time (dynamic_middleware_list) + if (st.elseStatement && s1 > s0 && entrySeq > s1 && !(s1 - s0 === 1 && entrySeq - s1 === 1)) { + for (const store of stores) { + const tm = store.entries.filter(x => x.seq > s0 && x.seq <= s1 && x.kind === 'mw'); + const em = store.entries.filter(x => x.seq > s1 && x.seq <= entrySeq && x.kind === 'mw'); + if (!tm.length || !em.length || tm.length === em.length) continue; + const g = `b${++altSeq}`; + for (const x of tm) x.br = { g, side: 't' }; + for (const x of em) x.br = { g, side: 'e' }; + } + } + condDepth--; condReason = r0; + // a return inside a branch is one of the function's results (kept as an alternative, see inline) + for (const r of [rt, re]) if (r && retAlts.length) retAlts[retAlts.length - 1].push(r); + return null; + } + if ((ts.isForOfStatement(st) || ts.isForInStatement(st)) && ts.isVariableDeclarationList(st.initializer) && st.initializer.declarations.length === 1) { + let it = evalExpr(st.expression); + // `for (const k in obj)`: the keys of a known object (an import.meta.glob result, R12) + if (ts.isForInStatement(st)) it = it.k === 'obj' ? { k: 'list', items: [...it.props.keys()].map(v => ({ k: 'str', v })), derived: it.derived } : UNK('for_in'); + if (it.k === 'undef') return null; // a loop over an absent property runs no times + if (it.k === 'list') { + // a loop over a known list: the body runs once per element with the variable bound (still a loop registration); + // array and object patterns bind the element's parts (`for (const [, mod] of Object.entries(m))`) + const vd = st.initializer.declarations[0], r0 = condReason, saved = []; + const decls = []; (function ds(n) { if (ts.isIdentifier(n)) return; for (const el of n.elements) if (ts.isBindingElement(el)) { decls.push(el); ds(el.name); } })(vd.name); + for (const d of [vd, ...decls]) saved.push([d, env.get(d)]); + condReason = 'loop_registration'; condDepth++; + const lid = ++loopSeq; + it.items.forEach((item, idx) => { + if (item.k === 'undef') return; // a null element: the body guards it or throws, it mounts nothing + if (it.derived) loopCtx.push({ id: lid, idx, kind: it.derived }); + bindPattern(vd, vd.name, item); evalStmt(st.statement); + if (it.derived) loopCtx.pop(); + }); + condDepth--; condReason = r0; + for (const [d, v] of saved) { if (v === undefined) env.delete(d); else env.set(d, v); } + return null; + } + } + if (ts.isForOfStatement(st) || ts.isForStatement(st) || ts.isForInStatement(st) || ts.isWhileStatement(st)) { const r0 = condReason; condReason = 'loop_registration'; condDepth++; evalStmt(st.statement); condDepth--; condReason = r0; return null; } + if (ts.isBlock(st)) return evalBlock(st.statements); + return null; +} + +let condDepth = 0, condReason = '', loopSeq = 0; +// R13b: the derived (filtered / reordered) loops the evaluation is inside, with the element index of each +const loopCtx = []; +const loopTagNow = () => loopCtx.length ? loopCtx.map(c => `${c.id}:${c.idx}`) : undefined; +function bindPattern(decl, name, v) { + if (ts.isIdentifier(name)) { env.set(decl, v); return; } + if (ts.isArrayBindingPattern(name)) name.elements.forEach((el, i) => { if (ts.isBindingElement(el)) bindPattern(el, el.name, v.k === 'list' && v.items[i] ? v.items[i] : UNK('pattern')); }); + else if (ts.isObjectBindingPattern(name)) for (const el of name.elements) { + const key = (el.propertyName || el.name).getText(), p = v.k === 'obj' && v.props.get(key); + bindPattern(el, el.name, p ? pv_(p) : v.k === 'module' && moduleExportDecl(v.sf, key) ? valueOfDecl(moduleExportDecl(v.sf, key)) : UNK('pattern')); + } +} +const retAlts = []; +const evaluated = new Set(); +function evalModule(sf) { + if (evaluated.has(sf)) return; + evaluated.add(sf); + // imports first, in source order: ES module evaluation order + for (const st of sf.statements) { + const spec = (ts.isImportDeclaration(st) || ts.isExportDeclaration(st)) && st.moduleSpecifier && st.moduleSpecifier.text; + if (!spec || (ts.isImportDeclaration(st) && st.importClause && st.importClause.isTypeOnly)) continue; + const r = resolveSpec(spec, sf.fileName); + const dep = r && program.getSourceFile(r.resolvedFileName); + if (dep && srcFiles.includes(dep)) evalModule(dep); + } + for (const st of sf.statements) evalStmt(st); +} +phase('typechecked'); +for (const sf of srcFiles) evalModule(sf); +phase('modules evaluated'); +// O11: a method of an object literal that nothing in the program calls but that mounts sub-apps (a plugin's +// `register(app) { app.route('/x', sub) }`, called by a host over a run-time plugin list) still runs: its parameters +// are unknown, so the sub-apps it mounts are mounted under an unknown receiver (partial), never roots. It runs before +// the uncalled-function pass below, so a builder it calls makes the mounted app, not a second root +const mountsInside = f => { let hit = false; (function v(n) { if (hit) return; if (n !== f && ts.isFunctionLike(n)) return; if (ts.isCallExpression(n) && ts.isPropertyAccessExpression(n.expression) && n.expression.name.text === 'route' && n.arguments.length >= 2) hit = true; ts.forEachChild(n, v); })(f.body); return hit; }; +for (const sf of srcFiles) (function v(n) { + if (ts.isObjectLiteralExpression(n)) for (const p of n.properties) { + const f = ts.isMethodDeclaration(p) ? p : ts.isPropertyAssignment(p) && (ts.isArrowFunction(p.initializer) || ts.isFunctionExpression(p.initializer)) ? p.initializer : null; + if (f && f.body && !inlinedFns.has(f) && !handlerFns.has(f) && mountsInside(f)) { pluginPass = true; inline(f, []); pluginPass = false; } + } + ts.forEachChild(n, v); +})(sf); +// O2: a module-level function nothing in the program calls (a server assembled inside `startServer(opts)`) +// still runs when its caller outside the program calls it: evaluate each such body once, parameters unknown +for (const sf of srcFiles) for (const st of sf.statements) { + const fns = []; + if (ts.isFunctionDeclaration(st) && st.body) fns.push(st); + if (ts.isVariableStatement(st)) for (const d of st.declarationList.declarations) if (d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer))) fns.push(d.initializer); + for (const f of fns) if (!inlinedFns.has(f) && !handlerFns.has(f)) inline(f, []); +} +phase('uncalled functions evaluated'); +// test and client code runs in functions nobody in the program calls (a test runner does): visit every +// `.request(...)` and `.$verb(...)` call not yet seen and evaluate its receiver: a read with no +// registration side effect, so it cannot add a route +const seenSites = new Set([...pendingRequests, ...pendingRpc].map(r => r.site)); +function sweep(n) { + // hono-3-02: the platform `fetch` to this machine (`http://localhost:${port}/x`) is a request to the served apps, + // matched on their composed full paths + if (ts.isCallExpression(n) && ts.isIdentifier(n.expression) && n.expression.text === 'fetch' && !seenSites.has(loc(n))) { + const d = declOf(n.expression); + if (!d || !isSrcDecl(d)) { seenSites.add(loc(n)); localFetch(n); } + } + if (ts.isCallExpression(n) && ts.isPropertyAccessExpression(n.expression)) { + const nm = n.expression.name.text; + if ((nm === 'request' || nm === 'fetch' || nm in CLIENT_VERBS) && !seenSites.has(loc(n))) { + seenSites.add(loc(n)); + const recv = evalExpr(n.expression.expression); + if (recv.k === 'app' && (nm === 'request' || nm === 'fetch')) requestCall(recv, n, nm === 'fetch'); + else if (recv.k === 'client') clientCall(recv, nm, n); + } + } + ts.forEachChild(n, sweep); +} +srcFiles.forEach(sweep); + +phase('requests swept'); +// R12.2: when a mount reads its app from a module list the program cannot spell (`mod.default` over a run-time list), +// a default-exported app that no module imports, nothing mounts, and nothing serves (no `fetch` read, no fire()) may be +// one of those modules: its routes are partial (unresolved_mount), never exact roots. The module that performs the +// mount, and every module that imports it, is the entry, never a candidate +if (opaqueMounts.length) { + const imported = new Set(globbed), importsOf = new Map(); + for (const sf of srcFiles) { + const deps = []; + (function v(n) { + let spec = null; + if ((ts.isImportDeclaration(n) || ts.isExportDeclaration(n)) && n.moduleSpecifier && ts.isStringLiteralLike(n.moduleSpecifier)) spec = n.moduleSpecifier.text; + else if (ts.isCallExpression(n) && (n.expression.kind === ts.SyntaxKind.ImportKeyword || (ts.isIdentifier(n.expression) && n.expression.text === 'require')) && n.arguments[0] && ts.isStringLiteralLike(n.arguments[0])) spec = n.arguments[0].text; + if (spec) { const r = resolveSpec(spec, sf.fileName); const d = r && program.getSourceFile(r.resolvedFileName); if (d && srcFiles.includes(d)) { deps.push(d); imported.add(d); } } + ts.forEachChild(n, v); + })(sf); + importsOf.set(sf, deps); + } + const entry = new Set(opaqueMounts.map(e => e.getSourceFile())); + for (let grew = true; grew;) { grew = false; for (const [sf, deps] of importsOf) if (!entry.has(sf) && deps.some(d => entry.has(d))) { entry.add(sf); grew = true; } } + for (const sf of srcFiles) { + if (imported.has(sf) || entry.has(sf)) continue; + for (const st of sf.statements) { + const v = ts.isExportAssignment(st) && !st.isExportEquals ? env.get(st) : null; + if (!v || v.k !== 'app' || v.store.mounted || v.store.fetchRead) continue; + partialMount(v, 'unresolved_mount', opaqueMounts[0], []); + } + } +} +// ── results ───────────────────────────────────────────────────────────────── +// SPEC 3.3 rule 4: a verb registration whose function declares `next` and calls it is middleware +// for the later routes it matches +function callsNext(v) { + if (v.k !== 'fn' || !v.node.parameters || v.node.parameters.length < 2) return false; + const nx = v.node.parameters[1].name; if (!ts.isIdentifier(nx)) return false; + let hit = false; + (function f(n) { if (ts.isCallExpression(n) && ts.isIdentifier(n.expression) && n.expression.text === nx.text) hit = true; ts.forEachChild(n, f); })(v.node.body); + return hit; +} +function samplePath(p) { + // a concrete request path the pattern matches (checked below against the router itself) + return p.split('/').map(seg => { + if (seg === '*') return 'w'; + return seg.replace(/:([A-Za-z0-9_]+)(\{([^}]*)\})?\??/g, (_, n, __, re) => { + if (!re) return 'v1'; + for (const c of ['1', 'a', 'v1', 'x.png', 'abc', '2024']) if (new RegExp(`^(?:${re})$`).test(c)) return c; + return 'v1'; + }); + }).join('/').replace(/\*/g, 'w') || '/'; +} +// O24 / R13: an app that only a route-list entry holds, that nothing mounts, requests, reads `.fetch` of or makes a +// client for, is served at no path: no exact root. Its handlers routed by no other app are partial unresolved_mount +// (a loader the program does not show may mount the entry); handlers another app serves need nothing more +{ + const reqd = new Set([...pendingRequests, ...pendingRpc.map(r => ({ app: r.c.app }))].map(r => r.app && r.app.store).filter(Boolean)); + for (const st of stores) if (st.listEntry && !st.mounted && !st.fetchRead && !reqd.has(st)) st.dropped = true; + const routed = new Set(stores.filter(st => !st.dropped).flatMap(st => st.entries.filter(en => en.kind === 'handler').map(en => en.key))); + for (const st of stores.filter(x => x.dropped)) for (const en of st.entries) { + if (en.kind !== 'handler' || routed.has(en.key)) continue; + const V = en.method === 'ALL' ? 'ANY' : en.method; + out.partial.add(`${en.key}\t${V}\tunresolved_mount`); unknownAt(st.listEntry, 'unresolved_mount', en.key); + } +} +const roots = stores.filter(s => !s.mounted && !s.dropped); +const allFns = new Set(); +const chains = []; // { handler, chain:[keys], store } +for (const st of stores) { + for (const en of st.entries) if (en.val.k === 'fn') allFns.add(en.key); + for (const k of ['notFound', 'onError']) if (st[k]) allFns.add(loc(st[k].node)); + for (const f of st.foreign || []) allFns.add(f); + for (const m of st.dynMws || []) if (m.val.k === 'fn') allFns.add(m.key); // middleware registered on a rebased holder + if (st.mounted || st.dropped) continue; + const router = new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }); + st.entries.forEach((en, i) => router.add(en.method, en.path, i)); + st.entries.forEach((en, i) => { + if (en.kind !== 'handler') return; + const V = en.method === 'ALL' ? 'ANY' : en.method; + out.route.push(`${V}\t${en.path}\t${en.key}\t${st.origin}\t${i}`); + // SPEC 4 [iter2] route_registered_by: the method enclosing the registering call + out.regby.add(`${en.regFn}\t${en.key}\t${V}\t${en.path}\t${st.origin}`); + const m = en.method === 'ALL' ? 'GET' : en.method; + const sp = samplePath(en.path); + let matched; + try { matched = router.match(m, sp)[0].map(x => x[0]); } catch (err) { matched = null; } + if (!matched || !matched.includes(i)) { out.unknown.add(`${en.site}\tORACLE_UNDECIDED_mw_chain\t${en.key}`); return; } + const chain = [], pos = []; + let dynamic = false, at = -1, lastAlt, brOpen = null; const brs = new Map(); + for (const j of matched) { + if (j === i) break; const x = st.entries[j]; if (x.kind === 'dyn') { dynamic = true; continue; } + // R15 clause 2: an unequal if / else pair: each branch counts from the same base; positions after it continue + // after the longer branch, and the chain is dynamic_middleware_list + if (x.br && (x.kind === 'mw' || callsNext(x.val))) { + const b = brs.get(x.br.g) || { base: at, t: 0, e: 0 }; brs.set(x.br.g, b); + const p = b.base + (++b[x.br.side]); at = Math.max(at, p); lastAlt = undefined; + chain.push(x.key); pos.push(p); brOpen = x.br.g; continue; + } + if (brOpen && (x.kind === 'mw' || callsNext(x.val))) dynamic = true; + // R13b: middleware another element of a filtered / reordered loop brought in may be absent or elsewhere + if (x.loopTag && x.loopTag.some(t => !(en.loopTag || []).includes(t)) && (x.kind === 'mw' || callsNext(x.val))) dynamic = true; + if ((x.kind === 'mw' || callsNext(x.val)) && !(x.val.except && x.val.except.includes(en.path))) { + if (!(x.alt && x.alt === lastAlt)) at++; // alternatives of one factory call share a position (hono-3-03) + lastAlt = x.alt; chain.push(x.key); pos.push(at); + } + } + if (dynamic) out.unknown.add(`${en.site}\tdynamic_middleware_list\t${en.key}`); + // R3: the served root app the chain belongs to is part of the row (one chain per app) + chain.forEach((mk, o) => out.mw.push(`${mk}\t${en.key}\t${V}\t${en.path}\t${pos[o]}\t${st.origin}`)); + chains.push({ handler: en.key, chain, store: st, entry: en }); + }); +} +for (const f of platformFns) allFns.add(f); +// a handler registered on a recognised app is an http entry point even when its path is only partly +// known (orchestrator ruling, iteration 1): the framework still calls it +for (const r of out.partial) { const k = r.split('\t')[0]; if (!k.startsWith('lib:')) allFns.add(k); } +for (const f of combinedFns) allFns.add(f); +for (const f of allFns) out.entry.add(`${f}\thttp`); + +// ctx: c.set(K) in a function of a chain -> c.get(K) / c.var.K in a later function of the same chain +const sets = new Map(), gets = new Map(); // fnloc -> Set(key) +function isContext(n) { + // untyped JavaScript: the first parameter of a function the router calls (a registered handler or middleware) + if (ts.isIdentifier(n)) { + const d = declOf(n); + if (d && ts.isParameter(d) && d.parent.parameters[0] === d && allFns.has(loc(d.parent))) return true; + } + const t = checker.getTypeAtLocation(n); + const s = t.getSymbol() || t.aliasSymbol; + const d = s && s.declarations && s.declarations[0]; + return !!(d && s.name === 'Context' && inPkg(d, ['hono'])); +} +// within ONE function a key flows to itself only when a read comes after a set (SPEC 3.4, j == i) +const firstSet = new Map(), lastGet = new Map(); +function notePos(f, k, isSet, pos) { + const key = f + '\t' + k; + if (isSet) { if (!firstSet.has(key) || pos < firstSet.get(key)) firstSet.set(key, pos); } + else if (!lastGet.has(key) || pos > lastGet.get(key)) lastGet.set(key, pos); +} +const selfFlows = (f, k) => firstSet.has(f + '\t' + k) && lastGet.has(f + '\t' + k) && firstSet.get(f + '\t' + k) < lastGet.get(f + '\t' + k); +function scanCtx(n) { + if (ts.isCallExpression(n) && ts.isPropertyAccessExpression(n.expression) && ['set', 'get'].includes(n.expression.name.text) + && n.arguments[0] && ts.isStringLiteralLike(n.arguments[0]) && isContext(n.expression.expression)) { + const isSet = n.expression.name.text === 'set'; + const m = isSet ? sets : gets; + const f = enclosingFn(n); if (!m.has(f)) m.set(f, new Set()); m.get(f).add(n.arguments[0].text); + notePos(f, n.arguments[0].text, isSet, n.getStart()); + } + if (ts.isPropertyAccessExpression(n) && ts.isPropertyAccessExpression(n.expression) && n.expression.name.text === 'var' + && isContext(n.expression.expression)) { + const f = enclosingFn(n); if (!gets.has(f)) gets.set(f, new Set()); gets.get(f).add(n.name.text); + notePos(f, n.name.text, false, n.getStart()); + } + ts.forEachChild(n, scanCtx); +} +srcFiles.forEach(scanCtx); +for (const { handler, chain } of chains) { + const seq = [...chain, handler]; + seq.forEach((s, i) => { + for (const k of sets.get(s) || []) for (let j = i; j < seq.length; j++) if ((gets.get(seq[j]) || new Set()).has(k) && (j > i || selfFlows(s, k))) out.ctx.add(`${s}\t${seq[j]}\t${k}`); + }); +} +// SPEC R23: a partial route's DECIDED chain (its `mwp` rows; a may-apply middleware is not in it) carries context keys +// exactly as an exact chain does: a setter at position p flows to a reader at a later position or to the handler +const pchains = new Map(); // handler -> [{ fn, at }] +{ const routed = new Set(out.route.map(r => r.split('\t')[2])); + for (const r of partialMw) { const [m, h, , at] = r.split('\t'); if (routed.has(h)) continue; + if (!pchains.has(h)) pchains.set(h, []); pchains.get(h).push({ fn: m, at: +at }); } } +for (const [h, ms] of pchains) { + const seq = [...ms, { fn: h, at: Infinity }]; + for (const a of seq) for (const k of sets.get(a.fn) || []) for (const b of seq) + if ((gets.get(b.fn) || new Set()).has(k) && (b.at > a.at || (b.fn === a.fn && selfFlows(a.fn, k)))) out.ctx.add(`${a.fn}\t${b.fn}\t${k}`); +} +// a reader whose key no function earlier on any of its chains sets: SPEC 3.4 no_setter_on_chain +for (const [r, keys] of gets) for (const k of keys) { + if ([...out.ctx].some(x => x.split('\t')[1] === r && x.split('\t')[2] === k)) continue; + if (!chains.some(c => c.handler === r || c.chain.includes(r)) && !pchains.has(r) && ![...pchains.values()].some(ms => ms.some(m => m.fn === r))) continue; + out.unknown.add(`${r}\tno_setter_on_chain\t${r}`); +} + +// rpc and app.request: matched by Hono's router on the target app's store +function routerFor(st) { + if (!st._router) { st._router = new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }); st.entries.forEach((en, i) => st._router.add(en.method, en.path, i)); } + return st._router; +} +const verbOk = (en, m) => m === '*' || en.method === m || en.method === 'ALL'; +// R17: the first handler Hono's router picks for this path, per verb (every verb when the method is '*') +function firstHandlers(st, m, p) { + const out_ = []; + for (const v of m === '*' ? ANY_VERBS : [m === 'HEAD' ? 'GET' : m]) { + let hs; try { hs = routerFor(st).match(v, p)[0].map(x => st.entries[x[0]]); } catch { hs = []; } + const h = hs.find(x => x.kind === 'handler'); if (h && !out_.includes(h)) out_.push(h); + } + return out_; +} +const holeRows = (r, entries, p) => { + const hs = entries.filter(en => en.kind === 'handler' && verbOk(en, r.method) && shapeMatch(en.path, p)); + const shown = p.split(HOLE).join('${}'); + if (!hs.length) { out.unknown.add(`${r.site}\tdynamic_rpc_path\t${r.from}`); return; } + for (const h of hs) (r.c ? out.rpc : out.request).add(`${r.from}\t${r.method}\t${shown}\t${h.key}${r.method === '*' ? '\tmethod_unknown' : ''}`); +}; +for (const r of pendingRequests) { + if (r.app === 'ANY_APP') { + // a request to this machine: every served root app may answer; no row when none does (it may be another process) + const isTest = f => /(\.test\.|\.spec\.|__tests__\/)/.test(f); + for (const st of stores.filter(x => !x.mounted && !x.dropped)) { + const cf = r.conf ? `\t${r.conf}` : r.method === '*' ? '\tmethod_unknown' : ''; + // O21: a request whose base is unknown is not joined to a test file's app from outside test code + if (r.conf && isTest(st.origin) && !isTest(r.site)) continue; + if (r.path.includes(HOLE)) { for (const h of st.entries.filter(en => en.kind === 'handler' && verbOk(en, r.method) && shapeMatch(en.path, r.path))) out.request.add(`${r.from}\t${r.method}\t${r.path.split(HOLE).join('${}')}\t${h.key}${cf}`); continue; } + // O21: an unknown-base request reaching only a catch-all handler is no route match + for (const h of firstHandlers(st, r.method, r.path)) if (!(r.conf && /(^|\/)\*$/.test(h.path))) out.request.add(`${r.from}\t${r.method}\t${r.path}\t${h.key}${cf}`); + } + continue; + } + if (r.path.includes(HOLE)) { holeRows(r, r.app.store.entries, r.path); continue; } + const hs = firstHandlers(r.app.store, r.method, r.path); + for (const h of hs) out.request.add(`${r.from}\t${r.method}\t${r.path}\t${h.key}${r.method === '*' ? '\tmethod_unknown' : ''}`); + if (!hs.length) out.unserved.add(`${r.from}\t${r.method} ${r.path}`); +} +for (const r of pendingRpc) { + if (!r.c.app) { out.unknown.add(`${r.site}\tunresolved_rpc_app_type\t${r.from}`); continue; } + const segs = r.c.segs.filter(s => s !== 'index'); + const p = '/' + segs.join('/'); + // the client spells the PATTERN; it is served by the handler registered under exactly that pattern + const cand = r.c.app === 'ANY_APP' ? stores.filter(st => !st.mounted && !st.dropped).flatMap(st => st.entries) : r.c.app.store.entries; + if (p.includes(HOLE)) { holeRows(r, cand, p); continue; } + const hs = cand.filter(en => en.kind === 'handler' && (en.method === r.method || en.method === 'ALL') && en.path === p); + const h = hs[0]; + for (const x of hs.slice(1)) if (r.c.app === 'ANY_APP') out.rpc.add(`${r.from}\t${r.method}\t${p}\t${x.key}`); + if (h) out.rpc.add(`${r.from}\t${r.method}\t${p}\t${h.key}`); + else out.unserved.add(`${r.from}\t${r.method} ${p}`); +} + +// ── SPEC 3.9: route_schema and valid_read ────────────────────────────────── +// A schema is named by its DECLARATION: a const's name (`::` of the identifier, as the +// IR places a variable), or `expr@` for an inline schema. Composition carries it: every project +// schema const a schema is built from (extend/merge/pick/omit/partial, z.array(B), fields, .openapi()) is a +// base, and the route is reached from each base too. +function schemaKey(n) { + if (!n) return { key: 'expr@?', init: undefined }; // a createRoute schema slot with no expression node (stress-2 guard) + if (n.parent && ts.isShorthandPropertyAssignment(n.parent) && n.parent.name === n) { + // `{ schema }` shorthand: the schema is the variable the shorthand names + const vs = checker.getShorthandAssignmentValueSymbol(n.parent); const d = vs && vs.valueDeclaration; + if (d && ts.isVariableDeclaration(d) && isSrcDecl(d) && d.initializer) return { key: loc(d.name), init: d.initializer }; + } + while (ts.isParenthesizedExpression(n) || ts.isAsExpression(n) || ts.isSatisfiesExpression?.(n)) n = n.expression; + if (ts.isIdentifier(n) || ts.isPropertyAccessExpression(n)) { + const d = declOf(ts.isPropertyAccessExpression(n) ? n.name : n); + if (d && ts.isVariableDeclaration(d) && isSrcDecl(d) && d.initializer) return { key: loc(d.name), init: d.initializer }; + } + return { key: `expr@${loc(n)}`, init: n }; +} +function schemaBases(init, seen = new Set()) { + const out = []; + if (!init) return out; // stress-2 guard + (function v(n) { + if (ts.isIdentifier(n) && !(ts.isPropertyAccessExpression(n.parent) && n.parent.name === n)) { + const d = declOf(n); + if (d && ts.isVariableDeclaration(d) && isSrcDecl(d) && d.initializer && !seen.has(d) + && !ts.isArrowFunction(d.initializer) && !ts.isFunctionExpression(d.initializer)) { // O19: a helper is no schema + seen.add(d); + if (/\bz\.|\.(extend|merge|pick|omit|partial|openapi)\(/.test(d.initializer.getText())) { out.push(loc(d.name)); out.push(...schemaBases(d.initializer, seen)); } + } + } + ts.forEachChild(n, v); + })(init); + return out; +} +const schemaRows = new Set(), validRows = new Set(); +for (const { handler, chain, store, entry } of chains) { + const byTarget = new Map(); + const add = (target, node) => { const sk = schemaKey(node); byTarget.set(target, sk.key); for (const k of [sk.key, ...schemaBases(sk.init)]) schemaRows.add(`${handler}\t${target}\t${k}`); }; + for (const mk of chain) { + const mv = store.entries.find(x => x.key === mk); + const vd = mv && mv.val.validator; + if (vd) { if (vd.schema) add(vd.target, vd.schema); else byTarget.set(vd.target, null); } + } + if (entry.routedef) for (const [t, n] of entry.routedef.schemas) if (t !== 'response') add(t, n); else { const sk = schemaKey(n); for (const k of [sk.key, ...schemaBases(sk.init)]) schemaRows.add(`${handler}\t${t}\t${k}`); } + entry.validTargets = byTarget; +} +function scanValid(n) { + if (ts.isCallExpression(n) && ts.isPropertyAccessExpression(n.expression) && n.expression.name.text === 'valid' + && ts.isPropertyAccessExpression(n.expression.expression) && n.expression.expression.name.text === 'req' + && isContext(n.expression.expression.expression) && n.arguments[0] && ts.isStringLiteralLike(n.arguments[0])) { + const f = enclosingFn(n), t = n.arguments[0].text; + for (const c of chains) if (c.handler === f) { + if (c.entry.validTargets.has(t)) { const sk = c.entry.validTargets.get(t); if (sk !== null) validRows.add(`${loc(n)}\t${f}\t${t}\t${sk === null ? '-' : sk}`); } + else out.unknown.add(`${loc(n)}\tvalid_without_validator\t${f}`); + } + } + ts.forEachChild(n, scanValid); +} +srcFiles.forEach(scanValid); + +phase('results'); +const lines = []; +for (const r of out.route) lines.push(`route\t${r}`); +for (const r of out.mw) lines.push(`mw\t${r}`); +for (const r of [...out.entry].sort()) lines.push(`entry\t${r}`); +for (const r of [...out.ctx].sort()) lines.push(`ctx\t${r}`); +for (const r of [...out.rpc].sort()) lines.push(`rpc\t${r}`); +for (const r of [...out.request].sort()) lines.push(`request\t${r}`); +for (const r of out.submount) lines.push(`submount\t${r}`); +for (const r of [...schemaRows].sort()) lines.push(`schema\t${r}`); +for (const r of [...validRows].sort()) lines.push(`validread\t${r}`); +// O12: a handler with a route row is not also partial (nor its partial reason rows) +{ + const routed = new Set(out.route.map(r => r.split('\t')[2])); + const PARTIAL_REASONS = new Set(['unresolved_app_value', 'unresolved_mount', 'dynamic_mount_prefix', 'dynamic_route_path', 'dynamic_verb']); + for (const r of [...out.partial]) if (routed.has(r.split('\t')[0])) out.partial.delete(r); + for (const r of [...out.unknown]) { const c = r.split('\t'); if (PARTIAL_REASONS.has(c[1]) && routed.has(c[2])) out.unknown.delete(r); } +} +for (const r of [...out.partial].sort()) lines.push(`partial\t${r}`); +{ const routed = new Set(out.route.map(r => r.split('\t')[2])); for (const r of [...partialMw].sort()) if (!routed.has(r.split('\t')[1])) lines.push(`mwp\t${r}`); } +for (const r of [...out.regby].sort()) lines.push(`regby\t${r}`); +for (const r of [...out.unserved].sort()) lines.push(`unserved\t${r}`); +for (const r of [...out.unknown].sort()) lines.push(`unknown\t${r}`); +// --stores: every store's full entry list in registration order, for the runtime cross-check +// (a mount's runtime handler is hono's wrapper: shown as a library entry; a dynamic marker is no runtime entry) +if (STORES) for (const st of stores) st.entries.filter(en => en.kind !== 'dyn').forEach((en, i) => lines.push(`store\t${st.origin}#${st.id}\t${st.mounted ? 'mounted' : st.dropped ? 'unserved' : 'root'}\t${i}\t${en.method}\t${en.path}\t${en.mount ? 'lib:mount' : en.key}\t${en.kind}\t${en.alt || ''}`)); +// streamed: one joined string exceeds V8's maximum string length on a 400-route project (stress-2 guard) +{ let buf = []; for (const l of lines) { buf.push(l); if (buf.length >= 20000) { fs.writeSync(1, buf.join('\n') + '\n'); buf = []; } } if (buf.length) fs.writeSync(1, buf.join('\n') + '\n'); } diff --git a/graph/test/typescript/hono/truth/package.json b/graph/test/typescript/hono/truth/package.json new file mode 100644 index 000000000..6e8380adb --- /dev/null +++ b/graph/test/typescript/hono/truth/package.json @@ -0,0 +1,14 @@ +{ + "name": "hono-torture-oracle", + "private": true, + "description": "Independent ground truth for the Hono torture suites: the TypeScript compiler API plus the real hono package. Never imports the axiom parser.", + "type": "module", + "dependencies": { + "typescript": "5.9.3", + "hono": "4.13.13", + "@hono/zod-openapi": "1.6.3", + "zod": "4.1.13", + "@hono/zod-validator": "0.9.1", + "@hono/node-server": "2.1.3" + } +} diff --git a/graph/test/typescript/hono/truth/runtime-check.mjs b/graph/test/typescript/hono/truth/runtime-check.mjs new file mode 100644 index 000000000..5194c9183 --- /dev/null +++ b/graph/test/typescript/hono/truth/runtime-check.mjs @@ -0,0 +1,154 @@ +#!/usr/bin/env node +// ───────────────────────────────────────────────────────────────────────────── +// Hono torture oracle: the RUNTIME half. +// +// Runs the fixture with the pinned `hono` and reads what Hono itself built: `app.routes` of every +// Hono instance a module exports, in registration order, each handler mapped back to its source +// function through V8's [[FunctionLocation]] (node strips TypeScript types in place, so positions +// are the source's own). The static oracle's store for that app must hold the SAME sequence of +// (method, full path, handler). This is the strongest truth for route / basePath / mount: it is +// not a model of Hono, it is Hono. +// +// usage: node runtime-check.mjs +// stdout: `runtime agree ` | `runtime skip `; exit 1 on disagreement. +// ───────────────────────────────────────────────────────────────────────────── +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { register } from 'node:module'; +import inspector from 'node:inspector'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +const HERE = path.dirname(fileURLToPath(import.meta.url)); +const { loadTypeScript } = await import(new URL('../../ground-truth/load-typescript.mjs', import.meta.url).href); +const ts = loadTypeScript(HERE, { toolName: 'hono-runtime-check' }); +const SRC = path.resolve(process.argv[2]); +const STATIC = fs.readFileSync(process.argv[3], 'utf8'); + +const files = []; +(function walk(d) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) walk(p); else if (/\.(ts|mts|js|mjs|cjs)$/.test(e.name) && !e.name.endsWith('.d.ts')) files.push(p); + } +})(SRC); +if (fs.readdirSync(SRC, { recursive: true }).some(f => /\.(tsx|jsx)$/.test(f))) { console.log('runtime skip jsx'); process.exit(0); } +// a fixture that only exists to be parsed (a test file, a client, a worker entry) can opt out by name +const SKIP = /(\.test\.|\.spec\.|client)/; + +// work copy: the fixture plus the oracle's node_modules, so `hono` is the pinned package +const W = fs.mkdtempSync(path.join(os.tmpdir(), 'hono-rt-')); +fs.cpSync(SRC, W, { recursive: true }); +fs.symlinkSync(path.join(HERE, 'node_modules'), path.join(W, 'node_modules')); +if (!fs.existsSync(path.join(W, 'package.json'))) fs.writeFileSync(path.join(W, 'package.json'), '{"type":"module"}'); +register(pathToFileURL(path.join(HERE, 'runtime-hooks.mjs')).href); + +const session = new inspector.Session(); +session.connect(); +const scripts = new Map(); +session.on('Debugger.scriptParsed', m => scripts.set(m.params.scriptId, m.params.url)); +const post = (m, p) => new Promise((res, rej) => session.post(m, p, (e, r) => e ? rej(e) : res(r))); +await post('Debugger.enable'); + +const { HonoBase } = await import(pathToFileURL(path.join(HERE, 'node_modules/hono/dist/hono-base.js')).href); +const COMPOSED = '__COMPOSED_HANDLER'; // hono/dist/utils/constants.js +const realW = fs.realpathSync(W); +const exported = new Set(); +let loadErr = null; +for (const f of files.map(f => path.join(realW, path.relative(SRC, f))).sort()) { + if (SKIP.test(path.basename(f))) continue; + try { + const ns = await import(pathToFileURL(f).href); + // a CommonJS fixture gets hono's cjs build, a different class object: recognise the instance by shape too + for (const v of Object.values(ns)) if (v instanceof HonoBase || (v && Array.isArray(v.routes) && v.router && typeof v.fetch === 'function')) exported.add(v); + } catch (e) { loadErr = `${path.relative(realW, f)}: ${String(e.message || e).split('\n')[0]}`; } +} +if (!exported.size) { console.log(`runtime skip ${loadErr ? 'not loadable (' + loadErr + ')' : 'no exported Hono instance'}`); cleanup(); process.exit(0); } + +// function -> static key `::` of the innermost function node containing V8's position +const sfCache = new Map(); +function keyAt(file, line0, col0) { + if (!sfCache.has(file)) sfCache.set(file, ts.createSourceFile(file, fs.readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, true)); + const sf = sfCache.get(file); + const pos = sf.getPositionOfLineAndCharacter(line0, col0); + let best = null; + (function visit(n) { + if (n.getStart(sf) <= pos && pos < n.end) { + if (ts.isFunctionLike(n) && n.body) best = n; + ts.forEachChild(n, visit); + } + })(sf); + if (!best) return null; + const p = sf.getLineAndCharacterOfPosition(best.getStart(sf)); + return `${path.relative(realW, file).split(path.sep).join('/')}:${p.line + 1}:${p.character + 1}`; +} +async function fnKey(fn) { + if (fn[COMPOSED]) fn = fn[COMPOSED]; + globalThis.__probe = fn; + const { result } = await post('Runtime.evaluate', { expression: 'globalThis.__probe' }); + const { internalProperties } = await post('Runtime.getProperties', { objectId: result.objectId, ownProperties: true }); + const fl = (internalProperties || []).find(p => p.name === '[[FunctionLocation]]'); + if (!fl) return 'lib'; + const url = scripts.get(fl.value.value.scriptId) || ''; + if (!url.startsWith('file:')) return 'lib'; + const file = fileURLToPath(url); + if (!file.startsWith(realW + path.sep) || file.includes(`${path.sep}node_modules${path.sep}`)) return 'lib'; + return keyAt(file, fl.value.value.lineNumber, fl.value.value.columnNumber) || 'lib'; +} + +// static stores: origin -> [{method,path,key}] +const stores = new Map(); +for (const l of STATIC.split('\n')) { + const c = l.split('\t'); + if (c[0] !== 'store') continue; + if (!stores.has(c[1])) stores.set(c[1], []); + const st = stores.get(c[1]), last = st[st.length - 1]; + // alternatives of one factory call (a branch returns one middleware, the rest another): one run registers ONE + // of them, so the group is a single static entry that any of its keys satisfies + if (c[8] && last && last.alt === c[8]) { last.keys.push(c[6]); continue; } + st.push({ method: c[4], path: c[5], key: c[6], keys: [c[6]], alt: c[8] || '' }); +} +// a registration the static half declared undetermined (a run-time path) is not in its store: the +// runtime value is one execution's answer, not the program's +const undetermined = new Set(STATIC.split('\n').filter(l => l.startsWith('partial\t')).map(l => l.split('\t')[1])); +// a registration behind a condition or in a loop: one run takes one path; compare only the rest +const conditional = new Set(STATIC.split('\n').filter(l => /^unknown\t.*\t(conditional|loop)_registration\t/.test(l)).map(l => l.split('\t')[3])); +for (const k of conditional) undetermined.add(k); +const staticRouteKeys = new Set(STATIC.split('\n').filter(l => l.startsWith('route\t')).map(l => l.split('\t')[3]).filter(k => !k.startsWith('lib'))); +for (const [, st] of stores) for (let i = st.length - 1; i >= 0; i--) if (st[i].keys.some(k => conditional.has(k))) st.splice(i, 1); +function aligns(rt, st) { + // user functions must align one for one; a runtime library entry the static model does not + // spell (a validator a library inserts on its own) may be skipped, never a user function + let i = 0, j = 0, n = 0; + while (i < rt.length && j < st.length) { + const a = rt[i], b = st[j]; + const same = a.method === b.method && a.path === b.path && b.keys.some(k => a.key === k || (a.key === 'lib' && k.startsWith('lib:'))); + if (same) { i++; j++; n++; continue; } + if (a.key === 'lib') { i++; continue; } + return { ok: false, at: `runtime #${i} ${a.method} ${a.path} ${a.key} vs static #${j} ${b.method} ${b.path} ${b.key}` }; + } + while (i < rt.length && rt[i].key === 'lib') i++; + return i === rt.length && j === st.length ? { ok: true, n } : { ok: false, at: `length runtime ${rt.length - i} left, static ${st.length - j} left` }; +} +let compared = 0, bad = []; +const seen = new Set(); +for (const app of exported) { + if (seen.has(app.routes)) continue; seen.add(app.routes); + const rt = []; + for (const r of app.routes) { const key = await fnKey(r.handler); r.handlerKey = key; if (!undetermined.has(key)) rt.push({ method: r.method, path: r.path, key }); } + if (!rt.length) continue; // every registration of this app is one the static half declared undetermined + // an app whose every ROUTE the static half declared undetermined (partial): its middleware alone is no route list + // to compare (the static half keeps a partial route's chain as `mwp` rows, without a store) + if (rt.every(r => !staticRouteKeys.has(r.key)) && app.routes.some(r => undetermined.has(r.handlerKey))) continue; + let best = null; + for (const [origin, st] of stores) { const a = aligns(rt, st); if (a.ok) { best = a; break; } best = best || { ...a, origin }; } + if (best && best.ok) compared += best.n; + else bad.push(`no static store matches an exported app: ${best ? best.at : 'no stores'}\n runtime: ${rt.map(r => `${r.method} ${r.path} ${r.key}`).join(' | ')}`); +} +cleanup(); +if (bad.length) { console.log(`runtime DISAGREE\n ${bad.join('\n ')}`); process.exit(1); } +if (compared < 1) { console.log('runtime skip every exported route is declared undetermined by the static half'); process.exit(0); } +console.log(`runtime agree ${compared} entries over ${seen.size} app(s)`); +process.exit(0); +function cleanup() { try { fs.rmSync(W, { recursive: true, force: true }); } catch { } } diff --git a/graph/test/typescript/hono/truth/runtime-hooks.mjs b/graph/test/typescript/hono/truth/runtime-hooks.mjs new file mode 100644 index 000000000..c3be5718e --- /dev/null +++ b/graph/test/typescript/hono/truth/runtime-hooks.mjs @@ -0,0 +1,20 @@ +// Module resolution hooks for the runtime cross-check: a fixture written for a bundler imports +// `./users` (no extension) or `./users.js` meaning `./users.ts`. Node's own resolver refuses both; +// this tries the candidates a bundler would, in its order, and nothing else. +import fs from 'node:fs'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +const CANDIDATES = ['', '.ts', '.mts', '.js', '.mjs', '/index.ts', '/index.js']; +export async function resolve(specifier, context, next) { + if ((specifier.startsWith('./') || specifier.startsWith('../')) && context.parentURL) { + const base = new URL(specifier, context.parentURL); + const p = fileURLToPath(base); + const stems = [p]; + if (/\.js$/.test(p)) stems.push(p.replace(/\.js$/, '.ts')); + for (const s of stems) for (const c of CANDIDATES) { + const f = s + c; + try { if (fs.statSync(f).isFile()) return next(pathToFileURL(f).href, context); } catch { } + } + } + return next(specifier, context); +} diff --git a/graph/typescript/engine/config-resolution/entry-points.dl b/graph/typescript/engine/config-resolution/entry-points.dl index 638f4121d..95058f93c 100644 --- a/graph/typescript/engine/config-resolution/entry-points.dl +++ b/graph/typescript/engine/config-resolution/entry-points.dl @@ -29,11 +29,16 @@ // parser's general name column and holds the identifier for an IDENTIFIER_REFERENCE, // so reading it unguarded turns variables into strings. cfg_path_arg(ce) :- expr_child("client", ce, "ARGUMENT", _, a), - expr_literal_type("client", "STRING", a), + cfg_string_literal_type(t), expr_literal_type("client", t, a), expr_literal_value("client", v, a), strlen(v) >= 1, substr(v, 0, 1) = "/". +// a backtick string with no `${}` is a string as much as a quoted one is +.decl cfg_string_literal_type(c0:symbol) +cfg_string_literal_type("STRING"). +cfg_string_literal_type("NO_SUBSTITUTION_TEMPLATE"). + // ── a NAMED function passed by reference as an argument ───────────────────── // `callback_arg` holds only for an ARROW_FUNCTION or FUNCTION_EXPRESSION written at // the site, because it reads `expr_anon_decl` -- the declaration an expression diff --git a/graph/typescript/engine/config-resolution/http-router-apps.dl b/graph/typescript/engine/config-resolution/http-router-apps.dl new file mode 100644 index 000000000..497cc9765 --- /dev/null +++ b/graph/typescript/engine/config-resolution/http-router-apps.dl @@ -0,0 +1,1109 @@ +// ============================================================================ +// CONFIG-RESOLUTION · HTTP ROUTER APPLICATIONS (which values are apps) +// +// An application object is built once (`new X()` of a class a knob names, imported +// from the module the knob names) and then carries a LIST of registrations: routes, +// middleware, mounted sub-applications. This file answers "which expressions hold an +// app, and which list do registrations on them land in"; http-router-routes.dl turns +// the lists into the route table. +// +// ── THE LIST, NOT THE OBJECT ──────────────────────────────────────────────── +// `basePath(p)` returns a NEW object that shares the original's route list, its own +// registrations prefixed by p. So an app value is (List, Base): the list a registration +// lands in, and the prefix it gets there. A list is identified by the expression that +// created it: +// * a construction `new X()`; +// * a CALL of a function that constructs and returns one (`createApp()`): every call +// is its own list, which INCLUDES the registrations the function made on the one it +// constructed (the template), at the call's position; +// * a PARAMETER an app is passed into (`registerUsers(app)`): its registrations are +// included into each passing caller's list, at the call's position, under the +// passed value's base. A parameter declared with the app class and passed nothing +// is a list of its own, whose paths cannot be completed. +// +// ── TWO STRATA ────────────────────────────────────────────────────────────── +// hr_app(Expr, List) knows no strings. It is what decides which path arguments are +// worth evaluating (rd_demand), so it may not depend on the evaluation itself, or the +// program would read a string to decide whether to read that string. hr_val(Expr, +// List, Base) repeats the same flow with the base attached, after rd_val. +// +// Framework names live in knobs.dl only (router_*). +// ============================================================================ + +// ── a specifier as the package it names ───────────────────────────────────── +// `npm:x@4/sub` and `jsr:@s/x@^4/sub` are `x/sub` and `@s/x/sub`; a URL under a knob +// prefix is the package (`/mod.ts`) or its subpath; anything else is itself. +.decl hr_spec_raw(c0:symbol) +hr_spec_raw(raw) :- rd_import_path(_, raw). +.decl hr_unscheme(c0:symbol, c1:symbol) +hr_unscheme(raw, x) :- hr_spec_raw(raw), router_spec_scheme(s), strlen(raw) > strlen(s), + substr(raw, 0, strlen(s)) = s, x = substr(raw, strlen(s), strlen(raw) - strlen(s)). +.decl hr_ver_at(c0:symbol, c1:number) +hr_ver_at(x, i) :- hr_unscheme(_, x), i = range(1, strlen(x)), substr(x, i, 1) = "@". +.decl hr_slash_after(c0:symbol, c1:number, c2:number) +hr_slash_after(x, i, j) :- hr_ver_at(x, i), j = range(i, strlen(x)), substr(x, j, 1) = "/". +.decl hr_ver_end(c0:symbol, c1:number, c2:number) +hr_ver_end(x, i, j) :- hr_ver_at(x, i), j = min y : { hr_slash_after(x, i, y) }. +hr_ver_end(x, i, strlen(x)) :- hr_ver_at(x, i), !hr_slash_after(x, i, _). +.decl hr_spec(c0:symbol, c1:symbol) +hr_spec(raw, raw) :- hr_spec_raw(raw). +hr_spec(raw, x) :- hr_unscheme(raw, x), !hr_ver_at(x, _). +hr_spec(raw, y) :- hr_unscheme(raw, x), hr_ver_at(x, i), hr_ver_end(x, i, j), + y = cat(substr(x, 0, i), substr(x, j, strlen(x) - j)). +.decl hr_url_rest(c0:symbol, c1:symbol, c2:symbol) +hr_url_rest(raw, pkg, rest) :- hr_spec_raw(raw), router_spec_url(pre, pkg), strlen(raw) > strlen(pre), + substr(raw, 0, strlen(pre)) = pre, rest = substr(raw, strlen(pre), strlen(raw) - strlen(pre)). +.decl hr_url_path(c0:symbol, c1:symbol, c2:symbol) +hr_url_path(raw, pkg, rest) :- hr_url_rest(raw, pkg, rest), substr(rest, 0, 1) = "/". +hr_url_path(raw, pkg, r2) :- hr_url_rest(raw, pkg, rest), substr(rest, 0, 1) = "@", + j = min y : { y = range(0, strlen(rest)), substr(rest, y, 1) = "/" }, r2 = substr(rest, j, strlen(rest) - j). +hr_spec(raw, pkg) :- hr_url_path(raw, pkg, "/mod.ts"). +.decl hr_import_spec(c0:symbol, c1:symbol) +hr_import_spec(ih, s) :- rd_import_path(ih, raw), hr_spec(raw, s). + +// ── does this program import the framework at all? ────────────────────────── +// Every app, client and validator above is recognised through an import of a module a knob +// names, so a program that imports none of them has no route table. hr_on() holds when one +// is imported; each rule below that would otherwise read the whole program (every call, every +// identifier, every return) is guarded by it, so on any other program they cost nothing. When +// it holds the rules are exactly the unguarded ones. +.decl hr_knob_spec(c0:symbol) +hr_knob_spec(s) :- router_app_class(s, _). +hr_knob_spec(s) :- router_factory(s, _). +hr_knob_spec(s) :- router_rpc_client(s, _). +hr_knob_spec(s) :- router_test_client(s, _). +hr_knob_spec(s) :- router_route_def(s, _). +hr_knob_spec(s) :- router_validator(s, _). +hr_knob_spec(s) :- router_validator_fn(s, _). +hr_knob_spec(s) :- router_mw_wrapper(s, _). +hr_knob_spec(s) :- router_mw_combinator(s, _). +hr_knob_spec(s) :- router_mw_except(s, _). +.decl hr_on() +hr_on() :- hr_import_spec(_, s), hr_knob_spec(s). + +// ── the class an app is built from ────────────────────────────────────────── +.decl hr_class_ref(c0:symbol) +hr_class_ref(f) :- hr_on(), expr_referenced("client", "IMPORT_BINDING", ih, f), + import_binding("client", _, _, orig, _, ih), orig != "", + hr_import_spec(ih, spec), router_app_class(spec, orig). +hr_class_ref(f) :- hr_on(), expr_referenced("client", "IMPORT_BINDING", ih, f), + import_binding("client", _, local, "", _, ih), + hr_import_spec(ih, spec), router_app_class(spec, local). +// `import * as h from '…'; new h.X()` +hr_class_ref(f) :- hr_on(), property_access_name(f, n), + expr_child("client", f, "RECEIVER", _, r), + expr_referenced("client", "IMPORT_BINDING", ih, r), + import_binding("client", "NAMESPACE", _, _, _, ih), + hr_import_spec(ih, spec), router_app_class(spec, n). + +.decl hr_new(c0:symbol) +hr_new(e) :- call_site("client", "CONSTRUCTOR_CALL", _, _, _, e, _), + expr_child("client", e, "METHOD_NAME", _, f), hr_class_ref(f). + +// the const an identifier denotes, here or through an import +.decl hr_denotes_var(c0:symbol, c1:symbol) +hr_denotes_var(e, v) :- hr_on(), expr_referenced("client", "VARIABLE", v, e). +hr_denotes_var(e, v) :- hr_on(), expr_referenced("client", "IMPORT_BINDING", ih, e), + import_binds(ih, "client", "VARIABLE", v). +// `import routes from './routes'; export { routes as apiRoutes }` in a barrel, imported as `apiRoutes`: the +// barrel re-exports its own DEFAULT import under an alias, which the generic export surface reads only for +// named imports +hr_denotes_var(e, v) :- hr_on(), expr_referenced("client", "IMPORT_BINDING", ih, e), import_binding("client", _, _, orig, _, ih), orig != "", + import_target_module(ih, _, m), export_decl(_, orig, ln, ek, m, h), export_kind_is_local_name(ek), export_source(_, "", _, h), + ln != "", import_binding("client", ik, ln, _, m, ih2), import_kind_binds_default(ik), import_binds(ih2, "client", "VARIABLE", v). +// `const { app } = await import('./server')`: the module's export, as a static import gives it +hr_denotes_var(e, v) :- hr_on(), expr_referenced("client", "VARIABLE", d, e), var_dynamic_import_binding(d, "VARIABLE", v). + +// a resolved call of a project function (not a handler handed to a registration) +.decl hr_call_of(c0:symbol, c1:symbol) +hr_call_of(ce, w) :- hr_on(), call_chain_edge(ce, _, "-", w, "client", st, _), st != "callback_registered". +// `const { auth } = middlewares` / `const { auth } = this.middlewares` where `middlewares` is a namespace +// import (`import * as middlewares from './middlewares'`, kept in a field): a call of `auth` calls the +// module's export, a function or a const holding one +.decl hr_ns_val(c0:symbol, c1:symbol) +hr_ns_val(x, m) :- hr_on(), expr_referenced("client", "IMPORT_BINDING", ih, x), import_binds_namespace(ih, "client", m). +hr_ns_val(x, m) :- ts_field_access_target(x, f), field_initializer("client", i, f), hr_ns_val(i, m). +hr_ns_val(x, m) :- expr_kind("client", k, _, x), expr_kind_is_transparent(k), expr_child("client", x, _, _, c), hr_ns_val(c, m). +.decl hr_ns_destr_fn(c0:symbol, c1:symbol) +hr_ns_destr_fn(d, w) :- var_binding("client", _, "PROPERTY", src, i, d), i != "", hr_ns_val(i, m), + module_export_any("client", m, src, "METHOD", w). +hr_ns_destr_fn(d, w) :- var_binding("client", _, "PROPERTY", src, i, d), i != "", hr_ns_val(i, m), + module_export_any("client", m, src, "VARIABLE", v), var_initializer("client", _, fx, v), expr_anon_decl("client", w, fx). +.decl hr_has_chain_call(c0:symbol) +hr_has_chain_call(ce) :- hr_on(), call_chain_edge(ce, _, "-", _, "client", _, _). +hr_call_of(ce, w) :- call_site("client", "FUNCTION_CALL", _, _, _, ce, _), !hr_has_chain_call(ce), + expr_child("client", ce, "METHOD_NAME", _, f), expr_referenced("client", "VARIABLE", d, f), hr_ns_destr_fn(d, w). + +// A MOUNT ARGUMENT whose call the call graph could not resolve (`s.controller()` on an element of +// a loop over objects of several classes, `g.build()` on a loop variable): the project methods of +// that name. Only for the second argument of a mount, where the alternative is worse: the app the +// method builds would otherwise be served at its own paths, as a root (SPEC 2, a mount argument is +// never a root). A call resolved to a declaration with overrides takes the overrides too. +.decl hr_mount_arg(c0:symbol) +hr_mount_arg(x) :- hr_on(), call_site("client", "METHOD_CALL", rn, _, _, ce, _), router_mount_app(rn), + expr_child("client", ce, "ARGUMENT", "1", x). +hr_mount_arg(x) :- hr_mount_arg(e), expr_kind("client", k, _, e), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"), + expr_child("client", e, _, _, x). +.decl hr_has_call(c0:symbol) +hr_has_call(x) :- hr_mount_arg(x), hr_call_of(x, _). +.decl hr_callee(c0:symbol, c1:symbol) +hr_callee(ce, w) :- hr_call_of(ce, w). +hr_callee(x, w) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", cn, _, _, x, _), !hr_has_call(x), + !hr_elem_typed(x), method_decl("client", cn, _, _, w), hr_arity_ok(x, w), hr_name_type_ok(x, cn, w). +// …of those, the ones the call can take: as many parameters as it passes arguments, at least, and visible on the +// receiver's declared class or overriding it there (`this.admin.getController()` on a field declared `AdminController` +// reaches no other controller's method; `strategy.getController(auth, opts)` reaches no zero-parameter one) +.decl hr_nargs(c0:symbol, c1:number) +hr_nargs(x, n) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", _, _, _, x, _), n = count : { expr_child("client", x, "ARGUMENT", _, _) }. +.decl hr_nparams(c0:symbol, c1:number) +hr_nparams(w, n) :- hr_on(), method_decl("client", _, _, _, w), n = count : { param_decl("client", _, _, _, w, _) }. +.decl hr_arity_ok(c0:symbol, c1:symbol) +hr_arity_ok(x, w) :- hr_nargs(x, a), call_site("client", "METHOD_CALL", cn, _, _, x, _), method_decl("client", cn, _, _, w), hr_nparams(w, n), n >= a. +.decl hr_name_type_ok(c0:symbol, c1:symbol, c2:symbol) +hr_name_type_ok(x, cn, w) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", cn, _, _, x, _), !hr_rtyped(x), method_decl("client", cn, _, _, w). +hr_name_type_ok(x, cn, w) :- hr_rtype(x, tt), call_site("client", "METHOD_CALL", cn, _, _, x, _), method_in_scope(tt, cn, _, w). +hr_name_type_ok(x, cn, w) :- hr_rtype(x, tt), call_site("client", "METHOD_CALL", cn, _, _, x, _), method_decl("client", cn, _, _, w), + method_owner("client", s, w), type_ancestor(s, tt). +// …the method of each element's class, when the receiver is a loop variable over a const array whose +// elements have a type (`[new PingRoutes()]`): a name fallback would also take a class the array never holds +.decl hr_elem_type(c0:symbol, c1:symbol) +hr_elem_type(x, tt) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", _, _, r, x, _), expr_referenced("client", "VARIABLE", v, r), + for_binding_iterable(v, it), hr_arr(it, arr), expr_child("client", arr, _, _, el), expr_type(el, _, tt). +.decl hr_elem_typed(c0:symbol) +hr_elem_typed(x) :- hr_elem_type(x, _). +hr_callee(x, w) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", cn, _, _, x, _), !hr_has_call(x), + hr_elem_type(x, tt), method_in_scope(tt, cn, _, w). +hr_callee(x, w) :- hr_mount_arg(x), hr_call_of(x, w0), method_dispatch_candidate(w0, w, _), method_prov(w, "client"), hr_disp_ok(x, w). +// …an override declared in the receiver's own static type or below it: `this.admin.getController()` on a field +// typed AdminController, which inherits the base method, reaches no other controller's override +.decl hr_rtype(c0:symbol, c1:symbol) +hr_rtype(x, tt) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", _, _, r, x, _), expr_type(r, _, tt). +// …or the class a field read is declared with (`adminController?: AdminController`) +hr_rtype(x, tt) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", _, _, r, x, _), ts_field_access_target(r, f), + field_type_ref("client", tr, f), type_ref_resolves("client", tr, tt), method_owner("client", tt, _). +// …or the class of what is written into the field (`this.adminController = new AdminController(..)`) +hr_rtype(x, tt) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", _, _, r, x, _), ts_field_access_target(r, f), + hr_field_write(f, v), call_site("client", "CONSTRUCTOR_CALL", _, _, _, v, _), expr_type(v, _, tt), method_owner("client", tt, _). +hr_rtype(x, tt) :- hr_mount_arg(x), call_site("client", "METHOD_CALL", _, _, r, x, _), ts_field_access_target(r, f), + hr_field_write(f, v), call_site("client", "CONSTRUCTOR_CALL", cn, _, _, v, _), type_decl("client", cn, _, _, _, _, tt), method_owner("client", tt, _). +.decl hr_rtyped(c0:symbol) +hr_rtyped(x) :- hr_rtype(x, _). +.decl hr_disp_ok(c0:symbol, c1:symbol) +hr_disp_ok(x, w) :- hr_rtype(x, tt), hr_call_of(x, w0), method_dispatch_candidate(w0, w, _), method_owner("client", s, w), + (s = tt ; type_ancestor(s, tt)). +hr_disp_ok(x, w) :- hr_mount_arg(x), !hr_rtyped(x), hr_call_of(x, w0), method_dispatch_candidate(w0, w, _). +// A mount argument whose call reaches SEVERAL methods that each build the app they return (`c.getController()` over +// a loop of controllers of several classes): each candidate's app is its own list (SPEC ruling R6), mounted on its +// own; merging them into the one list of the call would put one class's middleware before another class's routes +.decl hr_fresh_cand(c0:symbol, c1:symbol, c2:symbol) +hr_fresh_cand(x, w, t) :- hr_mount_arg(x), hr_callee(x, w), hr_ret_cand(w, t), expr_enclosing_method(t, w). +.decl hr_multi(c0:symbol) +hr_multi(x) :- hr_fresh_cand(x, w1, _), hr_fresh_cand(x, w2, _), w1 < w2. +// the element of the iterated array each candidate is the method of (`[new Reports(), new Exports()]`): the loop mounts +// them in the array's order, and a candidate no element holds is not mounted +.decl hr_cand_elem(c0:symbol, c1:symbol, c2:symbol) +hr_cand_elem(x, w, i) :- hr_fresh_cand(x, w, _), call_site("client", "METHOD_CALL", cn, _, r, x, _), expr_referenced("client", "VARIABLE", v, r), + for_binding_iterable(v, it), hr_arr(it, arr), expr_child("client", arr, _, i, el), expr_type(el, _, tt), method_in_scope(tt, cn, _, w), + strlen(i) <= 4. +.decl hr_x_has_elem(c0:symbol) +hr_x_has_elem(x) :- hr_cand_elem(x, _, _). +// (call, candidate's list, order among the candidates) +.decl hr_each_cand(c0:symbol, c1:symbol, c2:symbol) +hr_each_cand(x, t, o) :- hr_multi(x), hr_fresh_cand(x, w, t), hr_cand_elem(x, w, i), o = cat(substr("0000", 0, 4 - strlen(i)), i). +hr_each_cand(x, t, "9999") :- hr_multi(x), hr_fresh_cand(x, _, t), !hr_x_has_elem(x). + +// A returned list whose creating expression is evaluated inside the function itself +// (expr_enclosing_method) is fresh per call; anything else (a module-level app, a +// parameter) is the same list every time. +// what a function returns: its returned expression, never a part of it. method_returns_expr +// also lists the call sites INSIDE a returned expression (`return new X().basePath('/v1')` +// lists the `new` too), and reading those made the base-less inner value a second return. +.decl hr_ret_nested(c0:symbol) +hr_ret_nested(e) :- hr_on(), method_returns_expr(w, e), expr_child("client", p, _, _, e), method_returns_expr(w, p). +.decl hr_ret_expr(c0:symbol, c1:symbol) +hr_ret_expr(w, e) :- hr_on(), method_returns_expr(w, e), !hr_ret_nested(e). +.decl hr_ret_cand(c0:symbol, c1:symbol) +hr_ret_cand(w, t) :- hr_ret_expr(w, e), hr_app(e, t). + +// a parameter typed with the app class itself (`(app: X) => …`, `app: X`) +.decl hr_typed_slot(c0:symbol) +hr_typed_slot(p) :- hr_on(), param_type_ref("client", r, p), + type_ref("client", _, _, tn, _, _, _, r), type_ref_module("client", m, r), + import_binding("client", _, tn, orig, m, ih), orig != "", + hr_import_spec(ih, spec), router_app_class(spec, orig). +// …or one at the same position of a function that implements a signature declaring it so +// (`definePlugin({ register(app) { app.route(..) } })` where the input type says `register(app: Hono)`) +hr_typed_slot(p) :- hr_on(), param_decl("client", _, pos, _, m, p), method_dispatch_candidate(sig, m, _), + param_decl("client", _, pos, _, sig, ps), ps != p, param_type_ref("client", r, ps), + type_ref("client", _, _, tn, _, _, _, r), type_ref_module("client", mm, r), + import_binding("client", _, tn, orig, mm, ih), orig != "", + hr_import_spec(ih, spec), router_app_class(spec, orig). + +// ── a factory and the apps it makes ───────────────────────────────────────── +// (factory call): `createFactory(...)` imported from its module +.decl hr_factory_call(c0:symbol) +hr_factory_call(f) :- hr_on(), call_site("client", "FUNCTION_CALL", _, _, _, f, _), + expr_child("client", f, "METHOD_NAME", _, n), expr_referenced("client", "IMPORT_BINDING", ih, n), + import_binding("client", _, _, orig, _, ih), hr_import_spec(ih, spec), router_factory(spec, orig). +// an expression holding that factory: the call, or a const it initialises +.decl hr_factory_val(c0:symbol, c1:symbol) +hr_factory_val(f, f) :- hr_factory_call(f). +hr_factory_val(r, f) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), hr_factory_val(i, f). +// …returned by a project function (`function createTierFactory() { return createFactory() }`) +hr_factory_val(ce, f) :- hr_call_of(ce, w), hr_ret_expr(w, e), hr_factory_val(e, f). +// a factory METHOD read off into a const and called later (`const mk = factory.createMiddleware; mk(fn)`): +// (call, method name, factory) +.decl hr_detached(c0:symbol, c1:symbol, c2:symbol) +hr_detached(ce, n, f) :- call_site("client", "FUNCTION_CALL", _, _, _, ce, _), expr_child("client", ce, "METHOD_NAME", _, x), + hr_denotes_var(x, v), var_initializer("client", _, i, v), i != "", property_access_name(i, n), + expr_child("client", i, "RECEIVER", _, q), hr_factory_val(q, f). +// `.createApp()`: a new list per call +.decl hr_factory_app(c0:symbol, c1:symbol) +hr_factory_app(ce, f) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_factory_app(cn), hr_factory_val(r, f). +hr_factory_app(ce, f) :- hr_detached(ce, n, f), router_factory_app(n). +// the parameter of `initApp: (app) => {…}`: registered on every app the factory makes +.decl hr_factory_init_param(c0:symbol, c1:symbol) +hr_factory_init_param(f, p) :- hr_factory_call(f), expr_child("client", f, "ARGUMENT", "0", o), + rd_obj_prop(o, key, fn), router_factory_init(key), expr_anon_decl("client", m, fn), + param_decl("client", _, "0", _, m, p). + +// a property or field declared with the app class (`interface Ctx { server: X }`, +// `class C { app: X }`): reading it gives an app nothing here constructs, a list of its own +// whose paths cannot be completed (SPEC ruling R2) +.decl hr_typed_field(c0:symbol) +hr_typed_field(f) :- hr_on(), field_type_ref("client", r, f), + type_ref("client", _, _, tn, _, _, _, r), type_ref_module("client", m, r), + import_binding("client", _, tn, orig, m, ih), orig != "", + rd_import_path(ih, w), hr_spec(w, spec), router_app_class(spec, orig). +.decl hr_field_read(c0:symbol, c1:symbol) +hr_field_read(e, f) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, q), + expr_type(q, _, tt), field_in_scope(tt, n, _, f), hr_typed_field(f). + +// ── STRATUM 1: hr_app(Expr, List) ─────────────────────────────────────────── +.decl hr_app(c0:symbol, c1:symbol) +hr_app(e, e) :- hr_new(e). +hr_app(ce, ce) :- hr_factory_app(ce, _). +hr_app(e, a) :- hr_app(c, a), expr_child("client", e, _, _, c), + expr_kind("client", k, _, e), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +hr_app(r, a) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_app(i, a). +hr_app(ce, a) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_returns_app(cn), hr_app(r, a). +hr_app(ce, a) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_base_path(cn), hr_app(r, a). +// a function returning a list it created: each call is a new list +hr_app(ce, ce) :- hr_callee(ce, w), hr_ret_cand(w, t), expr_enclosing_method(t, w). +// …returning one it did not create: the same list +hr_app(ce, t) :- hr_callee(ce, w), hr_ret_cand(w, t), !expr_enclosing_method(t, w), !hv_isparam(t, w). +// …returning its own PARAMETER (`function withConfig(app) { app.use(mw); return app }`): the app this call passed, +// never one list shared by every caller (SPEC ruling R16: each test's app serves only its own routes) +hr_app(ce, a) :- hr_callee(ce, w), hr_ret_cand(w, t), hv_isparam(t, w), param_decl("client", _, pos, _, w, t), + expr_child("client", ce, "ARGUMENT", pos, x), hr_app(x, a). +// a parameter an app is passed into, or one declared with the app class +hr_app(e, p) :- expr_referenced("client", "PARAMETER", p, e), hr_slot(p). +hr_app(e, f) :- hr_field_read(e, f). +// a field an app is written into (`this.app = createApp()`, `private app = new X()`): every read of +// the field is that app (SPEC [iter2] flow through fields) +.decl hr_field_write(c0:symbol, c1:symbol) +hr_field_write(f, v) :- hr_on(), (expr_kind("client", "ASSIGNMENT_EXPRESSION", _, x) ; expr_kind("client", "BINARY_EXPRESSION", _, x)), + expr_operator("client", "=", x), + expr_child("client", x, "LEFT_OPERAND", _, l), ts_field_access_target(l, f), + expr_child("client", x, "RIGHT_OPERAND", _, v). +hr_field_write(f, i) :- hr_on(), field_initializer("client", i, f). +hr_app(e, a) :- ts_field_access_target(e, f), hr_field_write(f, v), hr_app(v, a), e != v. +// a property of an object literal holding an app (`return { dir, app: createApp(dir) }`), read +// back through a const, an await or a function's return (`(await setup()).app`, `fixture.app`) +.decl hr_app_obj(c0:symbol) +hr_app_obj(o) :- rd_obj_prop(o, _, v), hr_app(v, _). +.decl hr_objlit(c0:symbol, c1:symbol) +hr_objlit(o, o) :- hr_app_obj(o). +hr_objlit(r, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_objlit(i, o). +// …and the result of a call of a function returning one: (expression, call, function, object). The call is +// kept, because an app the function creates is a new one per call (SPEC ruling R6) +.decl hr_objcall(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hr_objcall(ce, ce, w, o) :- hr_call_of(ce, w), hr_ret_expr(w, e), hr_objlit(e, o). +hr_objcall(r, ce, w, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_objcall(i, ce, w, o). +hr_objcall(e, ce, w, o) :- hr_objcall(c, ce, w, o), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), + (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +// an element of a const array literal, read through a for..of binding over it +// (`for (const p of plugins) app.route(p.prefix, p.app)`) +.decl hr_arr(c0:symbol, c1:symbol) +hr_arr(x, x) :- hr_on(), expr_kind("client", "ARRAY_LITERAL", _, x). +hr_arr(r, a) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_arr(i, a). +hr_arr(e, a) :- hr_arr(c, a), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), expr_kind_is_transparent(k). +// an array method that returns some of the receiver's own elements (`[...].filter(f)`, `.sort(f)`, `.slice(1)`): a loop +// over the result iterates elements of that array literal, each still the value written there +.decl hr_arr_subset(c0:symbol) +hr_arr_subset("filter"). hr_arr_subset("sort"). hr_arr_subset("slice"). hr_arr_subset("reverse"). +hr_arr_subset("toSorted"). hr_arr_subset("toReversed"). +hr_arr(e, a) :- call_site("client", "METHOD_CALL", n, _, r, e, _), hr_arr_subset(n), r != "", hr_arr(r, a). +// …FILTERED: which elements survive is decided at run time, so what the loop registers for one is conditional (SPEC +// ruling R13b); sorting or slicing keeps every element's routes +.decl hr_arr_filtered(c0:symbol) +hr_arr_filtered(e) :- call_site("client", "METHOD_CALL", "filter", _, r, e, _), r != "", hr_arr(r, _). +hr_arr_filtered(e) :- call_site("client", "METHOD_CALL", n, _, r, e, _), hr_arr_subset(n), r != "", hr_arr_filtered(r). +hr_arr_filtered(r) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_arr_filtered(i). +hr_arr_filtered(e) :- hr_arr_filtered(c), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), expr_kind_is_transparent(k). +// a mount whose arguments read the variable of a loop over a filtered list: everything it mounts is registered when +// the element survives, exactly as a registration inside `if (...)` (SPEC ruling R13b) +.decl hr_marg_sub(c0:symbol, c1:symbol) +hr_marg_sub(ce, x) :- hr_on(), call_site("client", "METHOD_CALL", cn, _, _, ce, _), router_mount_app(cn), expr_child("client", ce, "ARGUMENT", _, x). +hr_marg_sub(ce, c) :- hr_marg_sub(ce, x), expr_child("client", x, _, _, c). +.decl hr_cond_mount(c0:symbol) +hr_cond_mount(ce) :- hr_marg_sub(ce, x), expr_referenced("client", "VARIABLE", v, x), for_binding_iterable(v, it), hr_arr_filtered(it). +hr_objlit(r, o) :- hr_denotes_var(r, v), for_binding_iterable(v, it), hr_arr(it, arr), expr_child("client", arr, _, _, el), + hr_objlit(el, o). +hr_objlit(e, o) :- hr_objlit(c, o), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), + (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +.decl hr_prop_of(c0:symbol, c1:symbol) +hr_prop_of(e, v) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, q), hr_objlit(q, o), + rd_obj_prop(o, n, v). +hr_app(e, a) :- hr_prop_of(e, v), hr_app(v, a). +// a name destructured from such an object (`const { app } = createTestApp()`) +.decl hr_destr_of(c0:symbol, c1:symbol) +hr_destr_of(e, v) :- expr_referenced("client", "VARIABLE", d, e), var_binding("client", _, "PROPERTY", src, i, d), i != "", + hr_objlit(i, o), rd_obj_prop(o, src, v). +hr_app(e, a) :- hr_destr_of(e, v), hr_app(v, a). +// a property of an object a call returned (`const { app } = createTestApp()`, `(await setup()).app`): an app +// the function created is this call's own list, any other is the same list every time +.decl hr_viacall(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hr_viacall(e, ce, w, v) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, q), hr_objcall(q, ce, w, o), + rd_obj_prop(o, n, v). +hr_viacall(e, ce, w, v) :- expr_referenced("client", "VARIABLE", d, e), var_binding("client", _, "PROPERTY", src, i, d), i != "", + hr_objcall(i, ce, w, o), rd_obj_prop(o, src, v). +.decl hr_objret_inst(c0:symbol, c1:symbol) +hr_objret_inst(ce, t) :- hr_viacall(_, ce, w, v), hr_app(v, t), expr_enclosing_method(t, w). +hr_app(e, ce) :- hr_viacall(e, ce, w, v), hr_app(v, t), expr_enclosing_method(t, w). +hr_app(e, t) :- hr_viacall(e, _, w, v), hr_app(v, t), !expr_enclosing_method(t, w). + +.decl hr_slot_arg(c0:symbol, c1:symbol, c2:symbol) +hr_slot_arg(oc, p, a) :- hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a), + param_decl("client", _, pos, _, w, p). +.decl hr_slot(c0:symbol) +.decl hr_param_mounted(c0:symbol) +hr_param_mounted(p) :- hr_reg(ce, _, cn), router_mount_app(cn), expr_child("client", ce, "ARGUMENT", "1", sx), hr_app(sx, p), + hr_slot_arg(_, p, _). +hr_slot(p) :- hr_slot_arg(_, p, a), hr_app(a, _). +hr_slot(p) :- hr_typed_slot(p). +hr_slot(f) :- hr_typed_field(f). +hr_slot(p) :- hr_factory_init_param(_, p). + +// ── a registration: a call on an app value ────────────────────────────────── +.decl hr_registrar(c0:symbol) +hr_registrar(n) :- hr_on(), router_verb(n, _). +hr_registrar(n) :- hr_on(), router_use(n). +hr_registrar(n) :- hr_on(), router_on(n). +hr_registrar(n) :- hr_on(), router_mount_app(n). +hr_registrar(n) :- hr_on(), router_mount_foreign(n). +hr_registrar(n) :- hr_on(), router_fallback(n). +hr_registrar(n) :- hr_on(), router_openapi(n). +.decl hr_reg(c0:symbol, c1:symbol, c2:symbol) +hr_reg(ce, a, cn) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), hr_registrar(cn), hr_app(r, a). + +// ── an OpenAPI route definition: the object `createRoute({...})` is called with ── +.decl hr_def_val(c0:symbol, c1:symbol) +hr_def_val(x, o) :- hr_on(), call_site("client", "FUNCTION_CALL", _, _, _, x, _), + expr_child("client", x, "METHOD_NAME", _, f), expr_referenced("client", "IMPORT_BINDING", ih, f), + import_binding("client", _, _, orig, _, ih), hr_import_spec(ih, spec), router_route_def(spec, orig), + expr_child("client", x, "ARGUMENT", "0", o), expr_kind("client", "OBJECT_LITERAL", _, o). +hr_def_val(r, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_def_val(i, o). +// `import * as routes from './routes'; app.openapi(routes.get, h)` +hr_def_val(e, o) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, r), + expr_referenced("client", "IMPORT_BINDING", ih, r), import_binds_namespace(ih, "client", m), + module_export_any("client", m, n, "VARIABLE", v), var_initializer("client", _, i, v), hr_def_val(i, o). +.decl hr_openapi_def(c0:symbol, c1:symbol) +hr_openapi_def(ce, o) :- hr_reg(ce, _, cn), router_openapi(cn), expr_child("client", ce, "ARGUMENT", "0", x), hr_def_val(x, o). +rd_demand(v) :- hr_openapi_def(_, o), rd_obj_prop(o, k, v), (router_def_key(k, "method") ; router_def_key(k, "path")). + +// the strings a registration may need +rd_demand(p) :- hr_reg(ce, _, _), expr_child("client", ce, "ARGUMENT", "0", p). +rd_demand(p) :- hr_reg(ce, _, cn), router_on(cn), expr_child("client", ce, "ARGUMENT", "1", p). +rd_demand(p) :- hr_reg(ce, _, cn), router_on(cn), expr_child("client", ce, "ARGUMENT", pos, arr), + (pos = "0" ; pos = "1"), expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, _, p). +rd_demand(p) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_base_path(cn), hr_app(r, _), + expr_child("client", ce, "ARGUMENT", "0", p). + +// ── an app VARIABLE re-pointed at a basePath result (SPEC rulings R8a, R8b) ── +// `let app = new X(); app.get(..); app = app.basePath('/api'); app.get(..)`: a read in the same function after the +// assignment (and not inside it) sees the assignment's value, a read before it the initializer's. Syntactic only, so +// the value rules may negate it. (assignment, variable, value) +.decl hr_rb_asg(c0:symbol, c1:symbol, c2:symbol) +hr_rb_asg(asg, v, val) :- hr_on(), expr_kind("client", "ASSIGNMENT_EXPRESSION", _, asg), expr_child("client", asg, "LEFT_OPERAND", _, tgt), + expr_referenced("client", "VARIABLE", v, tgt), expr_child("client", asg, "RIGHT_OPERAND", _, val), + call_site("client", "METHOD_CALL", cn, _, _, val, _), router_base_path(cn). +.decl hr_rb_read(c0:symbol, c1:symbol) +hr_rb_read(r, v) :- hr_rb_asg(_, v, _), expr_referenced("client", "VARIABLE", v, r). +.decl hr_rb_pos(c0:symbol, c1:symbol) +hr_rb_pos(x, k) :- (hr_rb_asg(x, _, _) ; hr_rb_read(x, _)), + ts_expression(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, l, c, _, _, _, _, _, _, _, _, _, _, _, x), + strlen(l) <= 7, strlen(c) <= 5, + k = cat(cat(substr("0000000", 0, 7 - strlen(l)), l), cat(":", cat(substr("00000", 0, 5 - strlen(c)), c))). +.decl hr_rb_sub(c0:symbol, c1:symbol) +hr_rb_sub(asg, asg) :- hr_rb_asg(asg, _, _). +hr_rb_sub(asg, c) :- hr_rb_sub(asg, x), expr_child("client", x, _, _, c). +.decl hr_rb_before(c0:symbol, c1:symbol, c2:symbol) +hr_rb_before(r, asg, ka) :- hr_rb_read(r, v), hr_rb_asg(asg, v, _), expr_enclosing_method(r, w), expr_enclosing_method(asg, w), + hr_rb_pos(asg, ka), hr_rb_pos(r, kr), ka < kr, !hr_rb_sub(asg, r). +.decl hr_rb_after(c0:symbol) +hr_rb_after(r) :- hr_rb_before(r, _, _). +.decl hr_rb_shadowed(c0:symbol, c1:symbol) +hr_rb_shadowed(r, asg) :- hr_rb_before(r, asg, k1), hr_rb_before(r, _, k2), k2 > k1. +.decl hr_rb_last(c0:symbol, c1:symbol) +hr_rb_last(r, asg) :- hr_rb_before(r, asg, _), !hr_rb_shadowed(r, asg). +// the blocks an assignment or a read sits in, and a conditional block holding the assignment but not the read +.decl hr_rb_up(c0:symbol, c1:symbol) +hr_rb_up(x, b) :- (hr_rb_asg(x, _, _) ; hr_rb_read(x, _)), expr_owner("client", "BLOCK", b, _, x). +hr_rb_up(x, p) :- hr_rb_up(x, b), block_parent("client", p, b). +.decl hr_rb_cond(c0:symbol, c1:symbol) +hr_rb_cond(r, asg) :- hr_rb_last(r, asg), hr_rb_up(asg, b), block_kind("client", k, b), hr_cond_kind(k), !hr_rb_up(r, b). + +// ── STRATUM 2: hr_val(Expr, List, Base) ───────────────────────────────────── +.decl hr_val(c0:symbol, c1:symbol, c2:symbol) +hr_val(e, e, "/") :- hr_new(e). +hr_val(ce, ce, "/") :- hr_factory_app(ce, _). +hr_val(e, a, b) :- hr_val(c, a, b), expr_child("client", e, _, _, c), + expr_kind("client", k, _, e), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +hr_val(r, a, b) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_val(i, a, b), !hr_rb_after(r). +// a read AFTER the variable was re-pointed at a basePath result takes that result (SPEC rulings R8a, R8b); when the +// re-pointing ran only under a condition the read does not enclose, the base is the run-time one (partial, never exact) +hr_val(r, a, b) :- hr_rb_last(r, asg), !hr_rb_cond(r, asg), hr_rb_asg(asg, _, val), hr_val(val, a, b). +hr_val(r, a, b) :- hr_rb_last(r, asg), hr_rb_cond(r, asg), hr_rb_asg(asg, _, val), hr_val(val, a, b), contains("{}", b). +hr_val(r, a, "/{}") :- hr_rb_last(r, asg), hr_rb_cond(r, asg), hr_rb_asg(asg, _, val), hr_val(val, a, b), !contains("{}", b). +hr_val(ce, a, b) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_returns_app(cn), hr_val(r, a, b). +hr_val(ce, a, nb) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_base_path(cn), hr_val(r, a, b), + expr_child("client", ce, "ARGUMENT", "0", p), rd_val(p, pv), hr_bmerge(b, pv, nb), strlen(nb) < 300. +hr_bmreq(b, pv) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_base_path(cn), hr_val(r, _, b), + expr_child("client", ce, "ARGUMENT", "0", p), rd_val(p, pv). +// basePath's own copy of mergePath (below): a base is decided before any mount is flattened, and a mount reads +// whether its receiver's base can be run-time (hr_dyn_base_call), so the two may not share one demand relation +.decl hr_bmreq(c0:symbol, c1:symbol) +.decl hr_bmerge(c0:symbol, c1:symbol, c2:symbol) +.decl hr_blead(c0:symbol, c1:symbol) +hr_blead(x, x) :- (hr_bmreq(x, _) ; hr_bmreq(_, x)), strlen(x) >= 1, substr(x, 0, 1) = "/". +hr_blead(x, y) :- (hr_bmreq(x, _) ; hr_bmreq(_, x)), strlen(x) >= 1, substr(x, 0, 1) != "/", y = cat("/", x). +hr_blead("", "/") :- (hr_bmreq("", _) ; hr_bmreq(_, "")). +hr_bmerge(a, b, a1) :- hr_bmreq(a, b), hr_blead(a, a1), hr_blead(b, "/"). +hr_bmerge(a, b, r) :- hr_bmreq(a, b), hr_blead(a, a1), hr_blead(b, b1), b1 != "/", + substr(a1, strlen(a1) - 1, 1) = "/", r = cat(substr(a1, 0, strlen(a1) - 1), b1). +hr_bmerge(a, b, r) :- hr_bmreq(a, b), hr_blead(a, a1), hr_blead(b, b1), b1 != "/", + substr(a1, strlen(a1) - 1, 1) != "/", r = cat(a1, b1). +hr_val(ce, ce, b) :- hr_callee(ce, w), hr_ret_expr(w, e), hr_val(e, t, b), expr_enclosing_method(t, w). +hr_val(ce, t, b) :- hr_callee(ce, w), hr_ret_expr(w, e), hr_val(e, t, b), hr_ret_cand(w, t), !expr_enclosing_method(t, w), !hv_isparam(t, w). +hr_val(ce, a, b) :- hr_callee(ce, w), hr_ret_expr(w, e), hr_val(e, t, "/"), hv_isparam(t, w), param_decl("client", _, pos, _, w, t), + expr_child("client", ce, "ARGUMENT", pos, x), hr_val(x, a, b). +hr_val(ce, a, nb) :- hr_callee(ce, w), hr_ret_expr(w, e), hr_val(e, t, rb), rb != "/", hv_isparam(t, w), param_decl("client", _, pos, _, w, t), + expr_child("client", ce, "ARGUMENT", pos, x), hr_val(x, a, b), hr_bmerge(b, rb, nb), strlen(nb) < 300. +hr_bmreq(b, rb) :- hr_callee(ce, w), hr_ret_expr(w, e), hr_val(e, t, rb), rb != "/", hv_isparam(t, w), param_decl("client", _, pos, _, w, t), + expr_child("client", ce, "ARGUMENT", pos, x), hr_val(x, _, b). +hr_val(e, p, "/") :- expr_referenced("client", "PARAMETER", p, e), hr_slot(p). +hr_val(e, f, "/") :- hr_field_read(e, f). +// A write that reads the field it writes (`this.server = this.server.basePath(b)`) is evaluated +// once: the reads inside it see the field's OTHER writes, never the write itself, so the update is +// not followed round as a cycle (SPEC ruling R8) +.decl hr_fw_sub(c0:symbol, c1:symbol) +hr_fw_sub(v, v) :- hr_field_write(_, v). +hr_fw_sub(v, c) :- hr_fw_sub(v, x), expr_child("client", x, _, _, c). +.decl hr_fw_self(c0:symbol, c1:symbol) +hr_fw_self(e, v) :- hr_field_write(f, v), hr_fw_sub(v, e), ts_field_access_target(e, f). +hr_val(e, a, b) :- ts_field_access_target(e, f), hr_field_write(f, v), hr_val(v, a, b), e != v, !hr_fw_self(e, v). +hr_val(e, a, b) :- hr_prop_of(e, v), hr_val(v, a, b). +hr_val(e, a, b) :- hr_destr_of(e, v), hr_val(v, a, b). +hr_val(e, ce, b) :- hr_viacall(e, ce, w, v), hr_val(v, t, b), expr_enclosing_method(t, w). +hr_val(e, t, b) :- hr_viacall(e, _, w, v), hr_val(v, t, b), !expr_enclosing_method(t, w). + +// ── INCLUSION: a list whose registrations are spliced into another ───────── +// hr_inc(Into, From, Site, Prefix, Kind). Kind: `mount` (route(prefix, sub)), +// `instance` (a call of a function that built the list), `param` (a function the app +// was passed into). +.decl hr_inc(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hr_inc(a, s, ce, pre, "mount") :- hr_reg(ce, a, cn), router_mount_app(cn), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), + expr_child("client", ce, "ARGUMENT", "1", sx), hr_app(sx, s), s != a, !hr_loop_paired(ce), !hr_multi(s), + !hr_elem_has_pre(ce, s), expr_child("client", ce, "ARGUMENT", "0", px), rd_val(px, pv), hr_merge(b, pv, pre), + (contains("{}", pre) ; !hr_dyn_base_call(ce)). +// …each candidate's own app, when the call reaches several (hr_multi) +hr_inc(a, t, ce, pre, "mount_each") :- hr_reg(ce, a, cn), router_mount_app(cn), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), + expr_child("client", ce, "ARGUMENT", "1", sx), hr_app(sx, s), s != a, !hr_loop_paired(ce), hr_multi(s), hr_each_cand(s, t, _), + !hr_elem_has_pre(ce, t), expr_child("client", ce, "ARGUMENT", "0", px), rd_val(px, pv), hr_merge(b, pv, pre), + (contains("{}", pre) ; !hr_dyn_base_call(ce)). +// …under the prefix THAT element's own method returns, when the prefix is a call on the same loop variable +// (`app.route(g.getBasePath(V), g.getRoutes())` over `[new A(), new B()]`): element i's class answers both calls, +// so A's routes are under A's prefix only (a call reaching several methods has no one value of its own) +.decl hr_elem_pre(c0:symbol, c1:symbol, c2:symbol) +hr_elem_pre(ce, s, pv) :- hr_reg(ce, _, cn), router_mount_app(cn), expr_child("client", ce, "ARGUMENT", "1", sx), + call_site("client", "METHOD_CALL", sn, _, r, sx, _), expr_referenced("client", "VARIABLE", v, r), + expr_child("client", ce, "ARGUMENT", "0", px), call_site("client", "METHOD_CALL", pn, _, pr, px, _), expr_referenced("client", "VARIABLE", v, pr), + for_binding_iterable(v, it), hr_arr(it, arr), expr_child("client", arr, _, _, el), expr_type(el, _, tt), + method_in_scope(tt, sn, _, w), hr_callee(sx, w), hr_ret_cand(w, s), method_in_scope(tt, pn, _, w2), rd_mval(w2, pv). +.decl hr_elem_has_pre(c0:symbol, c1:symbol) +hr_elem_has_pre(ce, s) :- hr_elem_pre(ce, s, _). +hr_inc(a, s, ce, pre, "mount") :- hr_reg(ce, a, cn), router_mount_app(cn), call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), + expr_child("client", ce, "ARGUMENT", "1", sx), hr_app(sx, s), s != a, !hr_loop_paired(ce), !hr_multi(s), + hr_elem_pre(ce, s, pv), hr_merge(b, pv, pre), (contains("{}", pre) ; !hr_dyn_base_call(ce)). +hr_inc(a, t, ce, pre, "mount_each") :- hr_reg(ce, a, cn), router_mount_app(cn), call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), + expr_child("client", ce, "ARGUMENT", "1", sx), hr_app(sx, s), s != a, !hr_loop_paired(ce), hr_multi(s), hr_each_cand(s, t, _), + hr_elem_pre(ce, t, pv), hr_merge(b, pv, pre), (contains("{}", pre) ; !hr_dyn_base_call(ce)). +hr_mreq(b, pv) :- hr_reg(ce, a, _), call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), hr_elem_pre(ce, _, pv). +// a module list read where it is mounted (R12.1): every module the literal glob matches, its default-exported app +hr_inc(a, s, ce, pre, "mount") :- hr_reg(ce, a, cn), router_mount_app(cn), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), hr_gtied(ce, g), hr_glob_app_of(g, s), s != a, + expr_child("client", ce, "ARGUMENT", "0", px), rd_val(px, pv), hr_merge(b, pv, pre), + (contains("{}", pre) ; !hr_dyn_base_call(ce)). +// …a derived one: mounted there under a prefix that also holds what the run-time choice adds (`/{}`); every route +// it brings is partial unresolved_mount (hr_dreach), never exact +hr_inc(a, s, ce, pre, "mount_derived") :- hr_reg(ce, a, cn), router_mount_app(cn), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), hr_gtied_d(ce, g), hr_glob_app_of(g, s), s != a, + expr_child("client", ce, "ARGUMENT", "0", px), rd_val(px, pv), hr_dpre(pv, dpv), hr_merge(b, dpv, pre). +.decl hr_dpre(c0:symbol, c1:symbol) +hr_dpre(pv, "/{}") :- hr_dpre_need(pv), (pv = "/" ; pv = ""). +hr_dpre(pv, cat("/{}", pv)) :- hr_dpre_need(pv), pv != "/", pv != "", substr(pv, 0, 1) = "/". +hr_dpre(pv, cat("/{}/", pv)) :- hr_dpre_need(pv), pv != "", substr(pv, 0, 1) != "/". +.decl hr_dpre_need(c0:symbol) +hr_dpre_need(pv) :- hr_gtied_d(ce, _), expr_child("client", ce, "ARGUMENT", "0", px), rd_val(px, pv). +hr_mreq(b, dpv) :- hr_reg(ce, a, cn), router_mount_app(cn), call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), + hr_gtied_d(ce, _), expr_child("client", ce, "ARGUMENT", "0", px), rd_val(px, pv), hr_dpre(pv, dpv). +// a loop over a const array of list entries: each element's app under that element's path (R13) +hr_inc(a, s, ce, pre, "mount") :- hr_reg(ce, a, cn), router_mount_app(cn), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), + hr_loop_sub(ce, o, s), s != a, hr_loop_pre_expr(ce, o, px), rd_val(px, pv), hr_merge(b, pv, pre), + (contains("{}", pre) ; !hr_dyn_base_call(ce)). +// a loop over the entries of a const object (`for (const [name, s] of Object.entries(strategies))`) mounting +// `s.method()` under a prefix made from `name`: each entry's own instance runs its own class's method, under its +// own key (the map is known, so this is value-following, not dispatch over every implementation) +hr_inc(a, t, ce, pre, "mount") :- hr_reg(ce, a, cn), router_mount_app(cn), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), hr_kv_sub(ce, key, t), t != a, hr_kv_pre(ce, key, pv), + hr_merge(b, pv, pre), (contains("{}", pre) ; !hr_dyn_base_call(ce)). +// a registration on a value that a run-time base may have reached (`if (b) this.server = +// this.server.basePath(b)`), a route or a mount alike, is declared once, partial, and never also +// served at its bare path (SPEC ruling R8a) +.decl hr_dyn_base_call(c0:symbol) +hr_dyn_base_call(ce) :- hr_reg(ce, _, _), call_site("client", _, _, _, r, ce, _), hr_val(r, _, b), contains("{}", b). +hr_mreq(b, pv) :- hr_reg(ce, a, cn), router_mount_app(cn), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), + expr_child("client", ce, "ARGUMENT", "0", px), rd_val(px, pv). +hr_mreq(b, pv) :- hr_reg(ce, a, cn), router_mount_app(cn), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), hr_loop_pre_expr(ce, _, px), rd_val(px, pv). +hr_inc(ce, t, ce, "/", "instance") :- hr_callee(ce, w), hr_ret_cand(w, t), expr_enclosing_method(t, w). +hr_inc(a, p, oc, b, "param") :- hr_slot_arg(oc, p, x), hr_val(x, a, b), a != p, !hr_param_mounted(p). +// a parameter the function MOUNTS (`function wrap(inner) { const w = new X(); w.route('/', inner); return w }`): the mount +// takes the app the call passes, so that app is included into the parameter's list at the call (kind `arg`), the +// other way round from a parameter registrations are made on +hr_inc(p, s, oc, "/", "arg") :- hr_param_mounted(p), hr_slot_arg(oc, p, x), hr_app(x, s), s != p. +hr_inc(ce, p, ce, "/", "instance") :- hr_factory_app(ce, f), hr_factory_init_param(f, p). +hr_inc(ce, t, ce, "/", "instance") :- hr_objret_inst(ce, t). + +// the same links without their prefixes: which lists are roots is decided before any +// string is read, because the strings a root's flattening needs depend on it +.decl hr_included(c0:symbol) +hr_included(s) :- hr_reg(ce, a, cn), router_mount_app(cn), expr_child("client", ce, "ARGUMENT", "1", sx), + hr_app(sx, s), s != a. +hr_included(t) :- hr_callee(ce, w), hr_ret_cand(w, t), expr_enclosing_method(t, w), ce = ce. +hr_included(p) :- hr_slot_arg(_, p, x), hr_app(x, a), a != p. +hr_included(s) :- hr_param_mounted(p), hr_slot_arg(_, p, x), hr_app(x, s), s != p. +hr_included(p) :- hr_factory_app(_, f), hr_factory_init_param(f, p). +hr_included(t) :- hr_objret_inst(_, t). +hr_included(t) :- hr_kv_sub(_, _, t). +hr_included(s) :- hr_reg(ce, a, cn), router_mount_app(cn), hr_gtied(ce, g), hr_glob_app_of(g, s), s != a. +hr_included(s) :- hr_reg(ce, a, cn), router_mount_app(cn), hr_gtied_d(ce, g), hr_glob_app_of(g, s), s != a. +.decl hr_list(c0:symbol) +hr_list(a) :- hr_app(_, a). +// a list nothing includes is what a server serves +.decl hr_root(c0:symbol) +hr_root(a) :- hr_list(a), !hr_included(a). +// an app mounted with route(p, it) on a receiver that is not a recognised app: it is served under a +// prefix nothing here knows, so it is never served at its own paths (SPEC [iter2], a mount argument +// is never a root) +.decl hr_unknown_mount(c0:symbol) +hr_unknown_mount(s) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_mount_app(cn), !hr_app(r, _), + expr_child("client", ce, "ARGUMENT", "1", sx), hr_app(sx, s). + +// ── a declarative route list (SPEC ruling R13) ────────────────────────────── +// `const routes = [{ path: '/x', handler: subApp }]; for (const r of routes) app.route(r.path, r.handler)`. +// Values only, never key names: the loop mounts what the ELEMENTS of the array it iterates hold, one +// element at a time, so each element's app is mounted at that element's own path. +// (mount call, loop variable, property the mount argument reads) +.decl hr_loop_mount(c0:symbol, c1:symbol, c2:symbol) +hr_loop_mount(ce, v, hk) :- call_site("client", "METHOD_CALL", cn, _, _, ce, _), router_mount_app(cn), + expr_child("client", ce, "ARGUMENT", "1", sx), hr_elem_read(sx, v, hk). +// `el.key`, also through `as X` / `!` +.decl hr_elem_read(c0:symbol, c1:symbol, c2:symbol) +hr_elem_read(x, v, k) :- hr_on(), property_access_name(x, k), expr_child("client", x, "RECEIVER", _, q), + expr_referenced("client", "VARIABLE", v, q), for_binding_iterable(v, _). +hr_elem_read(x, v, k) :- expr_kind("client", kd, _, x), expr_kind_is_transparent(kd), expr_child("client", x, _, _, y), + hr_elem_read(y, v, k). +// the element objects of a const array the loop iterates: each is mounted on its own +.decl hr_loop_elem(c0:symbol, c1:symbol) +hr_loop_elem(ce, o) :- hr_loop_mount(ce, v, _), for_binding_iterable(v, it), hr_arr(it, arr), + expr_child("client", arr, _, _, x), hr_objlit(x, o). +.decl hr_loop_paired(c0:symbol) +hr_loop_paired(ce) :- hr_loop_mount(ce, v, _), for_binding_iterable(v, it), hr_arr(it, _). +hr_loop_paired(ce) :- hr_kv_sub(ce, _, _). +// (key variable, value variable, object): `for (const [k, v] of Object.entries(o))`, o a const object literal +.decl hr_kv_loop(c0:symbol, c1:symbol, c2:symbol) +hr_kv_loop(vk, vv, o) :- ts_variable(_, _, _, _, _, _, _, _, "FOR_BINDING", _, _, _, _, _, _, b, _, _, _, _, _, _, _, _, _, "INDEX", "0", _, _, _, vk), + ts_variable(_, _, _, _, _, _, _, _, "FOR_BINDING", _, _, _, _, _, _, b, _, _, _, _, _, _, _, _, _, "INDEX", "1", _, _, _, vv), + for_binding_iterable(vk, it), call_site("client", "METHOD_CALL", "entries", _, _, it, _), + expr_child("client", it, "ARGUMENT", "0", x), hr_denotes_var(x, ov), rd_init_obj(ov, o). +.decl hr_kv_mount(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hr_kv_mount(ce, vk, vv, mn) :- call_site("client", "METHOD_CALL", cn, _, _, ce, _), router_mount_app(cn), + expr_child("client", ce, "ARGUMENT", "1", sx), call_site("client", "METHOD_CALL", mn, _, q, sx, _), + expr_referenced("client", "VARIABLE", vv, q), hr_kv_loop(vk, vv, _). +.decl hr_kv_sub(c0:symbol, c1:symbol, c2:symbol) +hr_kv_sub(ce, key, t) :- hr_kv_mount(ce, vk, vv, mn), hr_kv_loop(vk, vv, o), rd_obj_prop(o, key, e), expr_type(e, _, tt), + method_in_scope(tt, mn, _, w), hr_ret_cand(w, t), expr_enclosing_method(t, w). +// the prefix for one entry: the key itself, or a template whose one hole is the key +.decl hr_kv_pre(c0:symbol, c1:symbol, c2:symbol) +hr_kv_pre(ce, key, key) :- hr_kv_sub(ce, key, _), hr_kv_mount(ce, vk, _, _), expr_child("client", ce, "ARGUMENT", "0", px), + expr_referenced("client", "VARIABLE", vk, px). +hr_kv_pre(ce, key, pv) :- hr_kv_sub(ce, key, _), hr_kv_mount(ce, vk, _, _), expr_child("client", ce, "ARGUMENT", "0", px), + expr_literal_type("client", "TEMPLATE", px), expr_literal_value("client", raw, px), + expr_child("client", px, "TEMPLATE_SPAN", "0", sp), expr_referenced("client", "VARIABLE", vk, sp), + !expr_child("client", px, "TEMPLATE_SPAN", "1", _), + a = min i : { i = range(0, strlen(raw) - 1), substr(raw, i, 2) = "${" }, + z = min j : { j = range(0, strlen(raw)), substr(raw, j, 1) = "}" }, + pv = cat(substr(raw, 1, a - 1), cat(key, substr(raw, z + 1, strlen(raw) - z - 2))). +hr_mreq(b, pv) :- hr_reg(ce, a, cn), router_mount_app(cn), call_site("client", _, _, _, r, ce, _), hr_val(r, a, b), hr_kv_pre(ce, _, pv). +// the prefix expression for one element: the element's own property when the path argument reads +// the loop variable too, else the path argument itself +.decl hr_loop_pre_expr(c0:symbol, c1:symbol, c2:symbol) +hr_loop_pre_expr(ce, o, px) :- hr_loop_elem(ce, o), hr_loop_mount(ce, v, _), expr_child("client", ce, "ARGUMENT", "0", a), + hr_elem_read(a, v, pk), rd_obj_prop(o, pk, px). +hr_loop_pre_expr(ce, o, a) :- hr_loop_elem(ce, o), hr_loop_mount(ce, v, _), expr_child("client", ce, "ARGUMENT", "0", a), + !hr_elem_read(a, v, _). +rd_demand(px) :- hr_loop_pre_expr(_, _, px). +// the app one element holds under the property the mount reads +.decl hr_loop_sub(c0:symbol, c1:symbol, c2:symbol) +hr_loop_sub(ce, o, s) :- hr_loop_elem(ce, o), hr_loop_mount(ce, _, hk), rd_obj_prop(o, hk, hv), hr_app(hv, s). +// An app written into a list entry (the value of a property of an object literal that is an element of +// an array literal) is mounted by whatever reads that list. When no loop over a const array mounts it, +// the reader is not known here: its routes are served under a prefix nothing here knows, never at its +// own paths. A served shape (requested, its fetch handed out, the default export, a test client) +// always wins. +.decl hr_elem_obj(c0:symbol) +hr_elem_obj(o) :- hr_on(), expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, _, o), + expr_kind("client", "OBJECT_LITERAL", _, o). +hr_elem_obj(o) :- hr_on(), expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, _, x), + expr_kind("client", k, _, x), expr_kind_is_transparent(k), expr_child("client", x, _, _, o), + expr_kind("client", "OBJECT_LITERAL", _, o). +.decl hr_listed(c0:symbol) +hr_listed(a) :- hr_elem_obj(o), rd_obj_prop(o, _, v), hr_app(v, a). +.decl hr_served_list(c0:symbol) +hr_served_list(a) :- hr_served_use(a). +hr_served_list(a) :- hr_default_app(_, a). +.decl hr_default_app(c0:symbol, c1:symbol) +hr_default_app(m, a) :- export_decl(_, "default", _, _, m, h), export_expression("client", x, h), hr_app(x, a). +.decl hr_served_use(c0:symbol) +hr_served_use(a) :- hr_app(r, a), call_site("client", "METHOD_CALL", cn, _, r, _, _), + (router_app_request(cn) ; router_served_fetch(cn)). +// (written from the knob outward: started from hr_app, property_access_name shared no variable with it and the rule +// enumerated every app value against every property read) +hr_served_use(a) :- router_served_fetch(n), property_access_name(e, n), expr_child("client", e, "RECEIVER", _, r), hr_app(r, a). +hr_served_use(a) :- call_site("client", "FUNCTION_CALL", _, _, _, tc, _), expr_child("client", tc, "METHOD_NAME", _, f), + expr_referenced("client", "IMPORT_BINDING", ih, f), import_binding("client", _, _, orig, _, ih), + hr_import_spec(ih, spec), router_test_client(spec, orig), expr_child("client", tc, "ARGUMENT", "0", x), hr_app(x, a). +.decl hr_list_entry(c0:symbol) +hr_list_entry(a) :- hr_listed(a), !hr_served_list(a). +// An OPAQUE module list (SPEC ruling R12.2): `app.route(p, mod.default)` where `mod` is a module the program loads by +// a name it computes (`await import(file)` over a directory listing). Its candidates are the default-exported apps, +// in the mounting module's directory or below, that no module imports, nothing mounts and nothing serves; the +// mounting module and every module importing it are the entry, never a candidate. +.decl hr_opaque_mount(c0:symbol, c1:symbol) +hr_opaque_mount(ce, m) :- call_site("client", "METHOD_CALL", cn, _, _, ce, _), router_mount_app(cn), + expr_child("client", ce, "ARGUMENT", "1", sx), !hr_app(sx, _), hr_default_read(sx), expr_module("client", m, ce), + !hr_gtied(ce, _), !hr_gtied_d(ce, _). +.decl hr_default_read(c0:symbol) +hr_default_read(x) :- hr_on(), property_access_name(x, "default"). +hr_default_read(x) :- expr_kind("client", k, _, x), expr_kind_is_transparent(k), expr_child("client", x, _, _, y), hr_default_read(y). +.decl hr_entry_mod(c0:symbol) +hr_entry_mod(m) :- hr_opaque_mount(_, m). +hr_entry_mod(m) :- hr_entry_mod(t), import_binding("client", _, _, _, m, ih), import_target_module(ih, _, t). +.decl hr_imported_mod(c0:symbol) +hr_imported_mod(t) :- hr_on(), import_target_module(_, _, t). +hr_imported_mod(t) :- hr_on(), dynamic_import_module(_, t). +.decl hr_opaque_dir(c0:symbol) +hr_opaque_dir(d) :- hr_opaque_mount(_, m), module_decl("client", _, _, fp, _, m), hr_mdir(fp, d). +.decl hr_mdir_slash(c0:symbol, c1:number) +hr_mdir_slash(fp, i) :- hr_on(), module_decl("client", _, _, fp, _, _), i = range(0, strlen(fp)), substr(fp, i, 1) = "/". +.decl hr_mdir(c0:symbol, c1:symbol) +hr_mdir(fp, d) :- module_decl("client", _, _, fp, _, _), hr_mdir_slash(fp, _), j = max i : { hr_mdir_slash(fp, i) }, + d = substr(fp, 0, j + 1). +hr_mdir(fp, "") :- hr_on(), module_decl("client", _, _, fp, _, _), !hr_mdir_slash(fp, _). +.decl hr_opaque_cand(c0:symbol) +hr_opaque_cand(a) :- hr_opaque_dir(d), module_decl("client", _, _, fp, _, m), strlen(fp) > strlen(d), substr(fp, 0, strlen(d)) = d, + !hr_entry_mod(m), !hr_imported_mod(m), hr_default_app(m, a), !hr_included(a). +hr_list_entry(a) :- hr_opaque_cand(a), !hr_served_use(a). +// A module list (SPEC ruling R12): `import.meta.glob('../routes/**/*.index.ts')`, the pattern a literal, is resolved +// against the project's files; the default-exported app of each matched module is loaded to be mounted by +// whatever iterates the list. Its default export is how the list reads it, not how a server serves it, so only a +// served use wins here. Which mount takes it is not followed through the run-time filtering the list goes +// through: its routes are under a prefix not known here. +.decl hr_glob_pat(c0:symbol, c1:symbol, c2:symbol) +hr_glob_pat(ce, m, pat) :- call_site("client", "METHOD_CALL", cn, _, "", ce, _), router_module_glob(cn), + expr_child("client", ce, "METHOD_NAME", _, pa), !expr_child("client", pa, "RECEIVER", _, _), + expr_child("client", ce, "ARGUMENT", "0", x), hr_glob_lit(x, pat), expr_module("client", m, ce). +.decl hr_glob_lit(c0:symbol, c1:symbol) +hr_glob_lit(x, v) :- hr_on(), expr_kind("client", "LITERAL", _, x), expr_literal_value("client", v, x). +hr_glob_lit(x, v) :- hr_on(), expr_kind("client", "ARRAY_LITERAL", _, x), expr_child("client", x, _, _, y), + expr_kind("client", "LITERAL", _, y), expr_literal_value("client", v, y), substr(v, 0, 1) != "!". +// the pattern made relative to the source root: `./` dropped, each `../` takes a directory off the module's own +.decl hr_glob_res(c0:symbol, c1:symbol, c2:symbol) +hr_glob_res(ce, dir, pat) :- hr_glob_pat(ce, m, pat), module_decl("client", _, _, fp, _, m), hr_dir_of(fp, dir). +hr_glob_res(ce, dir, r) :- hr_glob_res(ce, dir, x), strlen(x) > 2, substr(x, 0, 2) = "./", r = substr(x, 2, strlen(x) - 2). +hr_glob_res(ce, up, r) :- hr_glob_res(ce, dir, x), strlen(x) > 3, substr(x, 0, 3) = "../", dir != "", + hr_dir_of(dir, up), r = substr(x, 3, strlen(x) - 3). +.decl hr_dir_need(c0:symbol) +hr_dir_need(fp) :- hr_glob_pat(_, m, _), module_decl("client", _, _, fp, _, m). +// …and every directory above it (decided from the path alone, so the walk up reads no result of itself) +hr_dir_need(d) :- hr_glob_pat(_, m, _), module_decl("client", _, _, fp, _, m), i = range(1, strlen(fp)), substr(fp, i, 1) = "/", + d = substr(fp, 0, i). +.decl hr_dir_slash(c0:symbol, c1:number) +hr_dir_slash(p, i) :- hr_dir_need(p), i = range(0, strlen(p)), substr(p, i, 1) = "/". +.decl hr_dir_of(c0:symbol, c1:symbol) +hr_dir_of(p, d) :- hr_dir_need(p), hr_dir_slash(p, _), j = max i : { hr_dir_slash(p, i) }, d = substr(p, 0, j). +hr_dir_of(p, "") :- hr_dir_need(p), !hr_dir_slash(p, _). +.decl hr_glob_full(c0:symbol, c1:symbol) +hr_glob_full(ce, f) :- hr_glob_res(ce, dir, x), substr(x, 0, 1) != ".", dir != "", f = cat(dir, cat("/", x)). +hr_glob_full(ce, x) :- hr_glob_res(ce, "", x), substr(x, 0, 1) != ".". +// the glob as a regular expression: `**/` any directories, `*` within one, `?` one character, `{a,b}` either +.decl hr_glob_re(c0:symbol, c1:number, c2:symbol, c3:number) +hr_glob_re(f, 0, "", 0) :- hr_glob_full(_, f). +hr_glob_re(f, i + 3, cat(acc, "(.*/)?"), b) :- hr_glob_re(f, i, acc, b), i + 3 <= strlen(f), substr(f, i, 3) = "**/". +hr_glob_re(f, i + 2, cat(acc, ".*"), b) :- hr_glob_re(f, i, acc, b), i + 2 <= strlen(f), substr(f, i, 2) = "**", + (i + 2 = strlen(f) ; substr(f, i + 2, 1) != "/"). +hr_glob_re(f, i + 1, cat(acc, "[^/]*"), b) :- hr_glob_re(f, i, acc, b), i < strlen(f), substr(f, i, 1) = "*", + (i + 1 = strlen(f) ; substr(f, i + 1, 1) != "*"). +hr_glob_re(f, i + 1, cat(acc, "[^/]"), b) :- hr_glob_re(f, i, acc, b), i < strlen(f), substr(f, i, 1) = "?". +hr_glob_re(f, i + 1, cat(acc, "("), 1) :- hr_glob_re(f, i, acc, 0), i < strlen(f), substr(f, i, 1) = "{". +hr_glob_re(f, i + 1, cat(acc, ")"), 0) :- hr_glob_re(f, i, acc, 1), i < strlen(f), substr(f, i, 1) = "}". +hr_glob_re(f, i + 1, cat(acc, "|"), 1) :- hr_glob_re(f, i, acc, 1), i < strlen(f), substr(f, i, 1) = ",". +hr_glob_re(f, i + 1, cat(acc, cat("\\", ch)), b) :- hr_glob_re(f, i, acc, b), i < strlen(f), ch = substr(f, i, 1), + (ch = "." ; ch = "(" ; ch = ")" ; ch = "+" ; ch = "$" ; ch = "^" ; ch = "[" ; ch = "]" ; ch = "|"). +hr_glob_re(f, i + 1, cat(acc, ch), b) :- hr_glob_re(f, i, acc, b), i < strlen(f), ch = substr(f, i, 1), + ch != "*", ch != "?", ch != "{", ch != "}", ch != ".", ch != "(", ch != ")", ch != "+", ch != "$", ch != "^", + ch != "[", ch != "]", ch != "|", (ch != "," ; b = 0). +hr_glob_re(f, i + 1, cat(acc, ","), 0) :- hr_glob_re(f, i, acc, 0), i < strlen(f), substr(f, i, 1) = ",". +.decl hr_glob_mod(c0:symbol, c1:symbol) +hr_glob_mod(ce, m2) :- hr_glob_full(ce, f), hr_glob_re(f, strlen(f), re, 0), module_decl("client", _, _, fp, _, m2), match(re, fp). +.decl hr_glob_app_of(c0:symbol, c1:symbol) +hr_glob_app_of(ce, a) :- hr_glob_mod(ce, m), hr_default_app(m, a). +.decl hr_glob_app(c0:symbol) +hr_glob_app(a) :- hr_glob_app_of(_, a). +// The list read where it is mounted: `for (const mod of Object.values(G)) app.route(p, mod.default)` (eager) or +// `const mod = await G[key](); app.route(p, mod.default)` (lazy), G the glob's value through consts, element reads, +// the loader's call, an await, and the language's own Object.values / Object.entries. A project function in +// between (a run-time filter) is not followed: then the modules stay candidates under an unknown prefix. +.decl hr_gflow(c0:symbol, c1:symbol) +hr_gflow(g, g) :- hr_glob_pat(g, _, _). +hr_gflow(r, g) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_gflow(i, g). +hr_gflow(r, g) :- expr_referenced("client", "VARIABLE", v, r), for_binding_iterable(v, it), hr_gflow(it, g). +hr_gflow(e, g) :- hr_gflow(c, g), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), + (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION" ; k = "ELEMENT_ACCESS"). +hr_gflow(e, g) :- hr_gflow(c, g), expr_child("client", e, "METHOD_NAME", _, c), call_site("client", _, _, _, _, e, _). +hr_gflow(e, g) :- hr_gflow(c, g), expr_child("client", e, "ARGUMENT", "0", c), call_site("client", "METHOD_CALL", n, _, r, e, _), + (n = "values" ; n = "entries"), r != "". +// …and the same list after a project function took it (a run-time filter, `pick(tier, all)`): DERIVED, its +// modules are mounted there, but which of them is decided at run time (SPEC R13 derived lists) +.decl hr_gflow_d(c0:symbol, c1:symbol) +hr_gflow_d(e, g) :- hr_gflow(c, g), expr_child("client", e, "ARGUMENT", _, c), hr_call_of(e, _). +hr_gflow_d(e, g) :- hr_gflow_d(c, g), expr_child("client", e, "ARGUMENT", _, c), hr_call_of(e, _). +hr_gflow_d(r, g) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_gflow_d(i, g). +hr_gflow_d(r, g) :- expr_referenced("client", "VARIABLE", v, r), for_binding_iterable(v, it), hr_gflow_d(it, g). +hr_gflow_d(e, g) :- hr_gflow_d(c, g), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), + (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION" ; k = "ELEMENT_ACCESS"). +hr_gflow_d(e, g) :- hr_gflow_d(c, g), expr_child("client", e, "METHOD_NAME", _, c), call_site("client", _, _, _, _, e, _). +hr_gflow_d(e, g) :- hr_gflow_d(c, g), expr_child("client", e, "ARGUMENT", "0", c), call_site("client", "METHOD_CALL", n, _, r, e, _), + (n = "values" ; n = "entries"), r != "". +.decl hr_gdefault_d(c0:symbol, c1:symbol) +hr_gdefault_d(x, g) :- property_access_name(x, "default"), expr_child("client", x, "RECEIVER", _, q), hr_gflow_d(q, g). +hr_gdefault_d(x, g) :- expr_kind("client", k, _, x), expr_kind_is_transparent(k), expr_child("client", x, _, _, y), hr_gdefault_d(y, g). +.decl hr_gtied_d(c0:symbol, c1:symbol) +hr_gtied_d(ce, g) :- call_site("client", "METHOD_CALL", cn, _, _, ce, _), router_mount_app(cn), + expr_child("client", ce, "ARGUMENT", "1", sx), hr_gdefault_d(sx, g), !hr_gtied(ce, _). +.decl hr_gtied(c0:symbol, c1:symbol) +hr_gtied(ce, g) :- call_site("client", "METHOD_CALL", cn, _, _, ce, _), router_mount_app(cn), + expr_child("client", ce, "ARGUMENT", "1", sx), hr_gdefault(sx, g). +.decl hr_gdefault(c0:symbol, c1:symbol) +hr_gdefault(x, g) :- property_access_name(x, "default"), expr_child("client", x, "RECEIVER", _, q), hr_gflow(q, g). +hr_gdefault(x, g) :- expr_kind("client", k, _, x), expr_kind_is_transparent(k), expr_child("client", x, _, _, y), hr_gdefault(y, g). +hr_list_entry(a) :- hr_glob_app(a), !hr_served_use(a). + + +// ── list elements followed through functions (SPEC ruling R13b) ───────────── +// A loop mounts the elements of a list a function was HANDED (`function mountPlugin(app, plugin) { for (const r of +// plugin.routes ?? []) app.route(r.path, r.handler) }`), or a list built by a helper (a push-built result, a sort, a +// `map` round trip). Values only: an expression's value is an object or array literal, reached by an ACCESS PATH +// (`|.routes|[]`: the property `routes`, then an element), or the same of a parameter. A parameter of a function that +// mounts (hv_ctx) is kept symbolic and bound per call, so each call mounts what it passes (/solo into the app of the +// call that passes it, never into every caller's); any other function's parameter is every value passed to it. +// The last column is 1 when the value went through a `filter` (its registrations are conditional). +.decl hv_ctx(c0:symbol) +hv_ctx(f) :- hr_loop_mount(ce, _, _), expr_enclosing_method(ce, f). +hv_ctx(f) :- hv_hsite(ce, _, _), expr_enclosing_method(ce, f). +hv_ctx(f) :- expr_enclosing_method(oc, f), hr_call_of(oc, g), hv_ctx(g). +// A function that RETURNS an object literal (`definePlugin(input) { return { routes: input.routes } }`) builds a fresh +// object per call: its result is the CALL (hv_objcall), and what the object holds is read with that call's own +// arguments, so its parameters are never the union of every caller's (one test's plugin is no other app's) +.decl hv_objret(c0:symbol, c1:symbol) +hv_objret(w, x) :- hr_ret_expr(w, x), expr_kind("client", "OBJECT_LITERAL", _, x). +.decl hv_nounion(c0:symbol) +hv_nounion(f) :- hv_ctx(f). +hv_nounion(w) :- hv_objret(w, _). +.decl hv_need(c0:symbol) +.decl hv(c0:symbol, c1:symbol, c2:symbol, c3:number) +hv_need(it) :- hr_loop_mount(ce, v, _), !hr_loop_paired(ce), for_binding_iterable(v, it). +hv_need(i) :- hv_need(e), hr_denotes_var(e, v), var_initializer("client", _, i, v), i != "". +hv_need(c) :- hv_need(e), expr_kind("client", k, _, e), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"), expr_child("client", e, _, _, c). +hv_need(q) :- hv_need(e), property_access_name(e, _), expr_child("client", e, "RECEIVER", _, q). +hv_need(it) :- hv_need(e), expr_referenced("client", "VARIABLE", v, e), for_binding_iterable(v, it). +hv_need(r) :- hv_need(e), call_site("client", "METHOD_CALL", n, _, r, e, _), hr_arr_subset(n), r != "". +hv_need(r) :- hv_need(e), call_site("client", "METHOD_CALL", n, _, r, e, _), hv_map(n), r != "". +hv_need(c) :- hv_need(e), expr_kind("client", "BINARY_EXPRESSION", _, e), (expr_operator("client", "??", e) ; expr_operator("client", "||", e)), + expr_child("client", e, _, _, c). +hv_need(c) :- hv_need(e), expr_kind("client", "TERNARY_EXPRESSION", _, e), (expr_child("client", e, "TERNARY_THEN", _, c) ; expr_child("client", e, "TERNARY_ELSE", _, c)). +hv_need(x) :- hv_need(e), hr_call_of(e, w), hr_ret_expr(w, x), !hv_objret(w, x). +hv_need(a) :- hv_need(e), expr_referenced("client", "PARAMETER", p, e), param_decl("client", _, pos, _, w, p), !hv_nounion(w), + hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a). +hv_need(r) :- hv_need(e), hv_cbparam(e, mc), call_site("client", "METHOD_CALL", _, _, r, mc, _). +// the methods whose result is the receiver's elements (hr_arr_subset), and those whose result is the callback's return +.decl hv_map(c0:symbol) +hv_map("map"). hv_map("flatMap"). +// a reference to the first parameter of a callback handed to a list method (`routes.map((r) => ..)`): an element +.decl hv_cbparam(c0:symbol, c1:symbol) +hv_cbparam(e, mc) :- expr_referenced("client", "PARAMETER", p, e), param_decl("client", _, "0", _, f, p), expr_anon_decl("client", f, fx), + expr_child("client", mc, "ARGUMENT", "0", fx), call_site("client", "METHOD_CALL", n, _, _, mc, _), (hv_map(n) ; hr_arr_subset(n) ; n = "forEach"). +hv(e, e, "", 0) :- hv_need(e), (expr_kind("client", "OBJECT_LITERAL", _, e) ; expr_kind("client", "ARRAY_LITERAL", _, e)). +hv(e, e, "", 0) :- hv_need(e), call_site("client", "METHOD_CALL", n, _, r, e, _), hv_map(n), r != "". +hv(e, b, p, c) :- hv_need(e), hr_denotes_var(e, v), var_initializer("client", _, i, v), i != "", hv(i, b, p, c). +hv(e, b, p, c) :- hv_need(e), expr_kind("client", k, _, e), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"), expr_child("client", e, _, _, x), + hv(x, b, p, c). +hv(e, b, cat(p, cat("|.", n)), c) :- hv_need(e), property_access_name(e, n), expr_child("client", e, "RECEIVER", _, q), hv(q, b, p, c), + strlen(p) < 120. +hv(e, b, cat(p, "|[]"), c) :- hv_need(e), expr_referenced("client", "VARIABLE", v, e), for_binding_iterable(v, it), hv(it, b, p, c), strlen(p) < 120. +hv(e, b, p, c) :- hv_need(e), call_site("client", "METHOD_CALL", n, _, r, e, _), hr_arr_subset(n), n != "filter", hv(r, b, p, c). +hv(e, b, p, 1) :- hv_need(e), call_site("client", "METHOD_CALL", "filter", _, r, e, _), hv(r, b, p, _). +hv(e, b, p, c) :- hv_need(e), expr_kind("client", "BINARY_EXPRESSION", _, e), (expr_operator("client", "??", e) ; expr_operator("client", "||", e)), + expr_child("client", e, _, _, x), hv(x, b, p, c). +hv(e, b, p, c) :- hv_need(e), expr_kind("client", "TERNARY_EXPRESSION", _, e), + (expr_child("client", e, "TERNARY_THEN", _, x) ; expr_child("client", e, "TERNARY_ELSE", _, x)), hv(x, b, p, c). +hv(e, e, "", 0) :- hv_need(e), hr_call_of(e, w), hv_objret(w, _). +hv(e, b, p, c) :- hv_need(e), hr_call_of(e, w), hr_ret_expr(w, x), !hv_objret(w, x), hv(x, b, p, c), !hv_isparam(b, w). +hv(e, b2, cat(p2, p), max(c, c2)) :- hv_need(e), hr_call_of(e, w), hr_ret_expr(w, x), !hv_objret(w, x), hv(x, b, p, c), hv_isparam(b, w), + param_decl("client", _, pos, _, w, b), expr_child("client", e, "ARGUMENT", pos, a), hv(a, b2, p2, c2), strlen(p2) + strlen(p) < 160. +hv(e, p, "", 0) :- hv_need(e), expr_referenced("client", "PARAMETER", p, e), !hv_cbparam(e, _). +hv(e, b, p, c) :- hv_need(e), expr_referenced("client", "PARAMETER", pp, e), param_decl("client", _, pos, _, w, pp), !hv_nounion(w), + !hv_cbparam(e, _), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a), hv(a, b, p, c). +hv(e, b, cat(p, "|[]"), c) :- hv_need(e), hv_cbparam(e, mc), call_site("client", "METHOD_CALL", _, _, r, mc, _), hv(r, b, p, c), strlen(p) < 120. +.decl hv_isparam(c0:symbol, c1:symbol) +hv_isparam(b, w) :- hr_on(), param_decl("client", _, _, _, w, b). +.decl hv_param(c0:symbol) +hv_param(b) :- hr_on(), param_decl("client", _, _, _, _, b). +// stepping a path from a literal: (base, path) -> (base', path') one step further, and whether a filter was crossed +.decl hv_step(c0:symbol, c1:symbol, c2:symbol) +hv_step(pth, tok, rest) :- hv_todo(_, pth), strlen(pth) > 1, substr(pth, 0, 1) = "|", j = range(2, strlen(pth) + 1), + (j = strlen(pth) ; substr(pth, j, 1) = "|"), tok = substr(pth, 1, j - 1), !contains("|", tok), rest = substr(pth, j, strlen(pth) - j). +// (a literal base, a path still to walk) +.decl hv_todo(c0:symbol, c1:symbol) +// the value one step reaches: a property of an object literal (also through `...spread`), an element of an array literal +// (also one `push`ed onto the variable it initialises), the callback's return for a `map` result +.decl hv_elem(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hv_elem(o, tok, v, "own") :- hv_todo(o, pth), hv_step(pth, tok, _), substr(tok, 0, 1) = ".", rd_obj_prop(o, substr(tok, 1, strlen(tok) - 1), v). +hv_elem(o, tok, v, "spread") :- hv_todo(o, pth), hv_step(pth, tok, _), substr(tok, 0, 1) = ".", expr_kind("client", "OBJECT_LITERAL", _, o), + expr_child("client", o, "SPREAD_OPERAND", _, x), hv_spread_src(x, v). +hv_elem(a, "[]", x, "own") :- hv_todo(a, pth), hv_step(pth, "[]", _), expr_kind("client", "ARRAY_LITERAL", _, a), expr_child("client", a, _, _, x), + !expr_kind("client", "SPREAD_ELEMENT", _, x). +hv_elem(a, "[]", x, "own") :- hv_todo(a, pth), hv_step(pth, "[]", _), expr_kind("client", "ARRAY_LITERAL", _, a), var_initializer("client", _, a, d), + call_site("client", "METHOD_CALL", "push", _, r, pc, _), expr_referenced("client", "VARIABLE", d, r), expr_child("client", pc, "ARGUMENT", _, x). +hv_elem(mc, "[]", x, "own") :- hv_todo(mc, pth), hv_step(pth, "[]", _), call_site("client", "METHOD_CALL", n, _, _, mc, _), hv_map(n), + expr_child("client", mc, "ARGUMENT", "0", fx), expr_anon_decl("client", f, fx), hr_ret_expr(f, x). +// …a property of the object a builder call returns (kind `call`: read with that call's arguments, hv_cv) +hv_elem(e, tok, v, "call") :- hv_todo(e, pth), hv_step(pth, tok, _), substr(tok, 0, 1) = ".", hr_call_of(e, w), hv_objret(w, x), + rd_obj_prop(x, substr(tok, 1, strlen(tok) - 1), v). +// …the elements of a list an array literal spreads (`[...routes].sort(..)`): looked up in the spread list +hv_elem(a, "[]", y, "spread") :- hv_todo(a, pth), hv_step(pth, "[]", _), expr_kind("client", "ARRAY_LITERAL", _, a), expr_child("client", a, _, _, x), + expr_kind("client", "SPREAD_ELEMENT", _, x), expr_child("client", x, _, _, y). +// …a method the object holds, as the LAST step (`plugin.register`, handed an app): the function itself +hv_elem(o, tok, f, "fn") :- hv_todo(o, pth), hv_step(pth, tok, ""), substr(tok, 0, 1) = ".", objlit_member_fn(o, substr(tok, 1, strlen(tok) - 1), f). +// a value read inside a builder, seen from one call of it: its parameters are that call's arguments +.decl hv_cv(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +// …a property the builder's object takes from an object it spreads (`return { ...input, defined: true }`): looked up +// in the spread value, as seen from the call (kind `callspread`) +hv_elem(e, tok, y, "callspread") :- hv_todo(e, pth), hv_step(pth, tok, _), substr(tok, 0, 1) = ".", hr_call_of(e, w), hv_objret(w, x), + expr_child("client", x, "SPREAD_OPERAND", _, sx), hv_spread_src(sx, y). +.decl hv_callk(c0:symbol) +hv_callk("call"). hv_callk("callspread"). +hv_cv(e, v, b2, p2, c) :- hv_elem(e, _, v, k), hv_callk(k), hv(v, b2, p2, c), hr_call_of(e, w), !hv_isparam(b2, w). +hv_cv(e, v, b4, cat(p4, p2), max(c, c4)) :- hv_elem(e, _, v, k), hv_callk(k), hv(v, b2, p2, c), hr_call_of(e, w), param_decl("client", _, pos, _, w, b2), + expr_child("client", e, "ARGUMENT", pos, a), hv(a, b4, p4, c4), strlen(p4) + strlen(p2) < 160. +hv_need(a) :- hv_elem(e, _, _, k), hv_callk(k), hr_call_of(e, w), param_decl("client", _, pos, _, w, _), expr_child("client", e, "ARGUMENT", pos, a). +// a spread source stands for the object it spreads: a property lookup continues in it (marked so the step repeats there) +.decl hv_spread_src(c0:symbol, c1:symbol) +hv_spread_src(x, y) :- hr_on(), expr_kind("client", "OBJECT_LITERAL", _, o), expr_child("client", o, "SPREAD_OPERAND", _, x), + expr_kind("client", "SPREAD_ELEMENT", _, x), expr_child("client", x, _, _, y). +hv_spread_src(x, x) :- hr_on(), expr_kind("client", "OBJECT_LITERAL", _, o), expr_child("client", o, "SPREAD_OPERAND", _, x), + !expr_kind("client", "SPREAD_ELEMENT", _, x). +hv_need(v) :- hv_elem(_, _, v, _). +// hv_res(base, path, base', path', filtered): the path walked from a literal until a parameter or the end +.decl hv_res(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +hv_res(b, p, b, p, 0) :- hv_todo(b, p), (p = "" ; hv_param(b)). +hv_res(b, p, b3, p3, max(c1, c2)) :- hv_todo(b, p), !hv_param(b), hv_step(p, tok, rest), hv_elem(b, tok, v, "own"), + hv(v, b2, p2, c1), hv_res(b2, cat(p2, rest), b3, p3, c2). +// …a property found in a spread object: look the same property up there +hv_res(b, p, b3, p3, max(c1, c2)) :- hv_todo(b, p), hv_step(p, tok, _), hv_elem(b, tok, v, "spread"), hv(v, b2, p2, c1), + hv_res(b2, cat(p2, p), b3, p3, c2). +hv_todo(b2, cat(p2, rest)) :- hv_todo(b, p), !hv_param(b), hv_step(p, tok, rest), hv_elem(b, tok, v, "own"), hv(v, b2, p2, _), + strlen(p2) + strlen(rest) < 160. +hv_res(b, p, b3, p3, max(c1, c2)) :- hv_todo(b, p), hv_step(p, tok, rest), hv_elem(b, tok, v, "call"), hv_cv(b, v, b2, p2, c1), + hv_res(b2, cat(p2, rest), b3, p3, c2). +hv_todo(b2, cat(p2, rest)) :- hv_todo(b, p), hv_step(p, tok, rest), hv_elem(b, tok, v, "call"), hv_cv(b, v, b2, p2, _), strlen(p2) + strlen(rest) < 160. +hv_res(b, p, b3, p3, max(c1, c2)) :- hv_todo(b, p), hv_step(p, _, _), hv_elem(b, tok, v, "callspread"), hv_step(p, tok, _), hv_cv(b, v, b2, p2, c1), + hv_res(b2, cat(p2, p), b3, p3, c2). +hv_todo(b2, cat(p2, p)) :- hv_todo(b, p), hv_step(p, tok, _), hv_elem(b, tok, v, "callspread"), hv_cv(b, v, b2, p2, _), strlen(p2) + strlen(p) < 160. +hv_res(b, p, f, "", 0) :- hv_todo(b, p), !hv_param(b), hv_step(p, tok, ""), hv_elem(b, tok, f, "fn"). +hv_todo(b2, cat(p2, p)) :- hv_todo(b, p), hv_step(p, tok, _), hv_elem(b, tok, v, "spread"), hv(v, b2, p2, _), strlen(p2) + strlen(p) < 160. + +// PENDING MOUNTS: (mount, inclusion site, receiver expression, element base, path, filtered, prefix key, app key). A mount +// in a loop over a list that is not a literal here: the element base is walked to an object literal, or, when it is a +// parameter of the function the pending sits in, bound at every call of that function (and so is a receiver that is a +// parameter of it, the call becoming the inclusion site) +.decl hv_pm(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:number, c6:symbol, c7:symbol) +hv_pm(ce, ce, r, b, cat(p, "|[]"), c, kp, kh) :- hr_loop_mount(ce, v, kh), !hr_loop_paired(ce), call_site("client", _, _, _, r, ce, _), + for_binding_iterable(v, it), hv(it, b, p, c), expr_child("client", ce, "ARGUMENT", "0", a0), hr_elem_read(a0, v, kp). +hv_todo(b, p) :- hv_pm(_, _, _, b, p, _, _, _). +hv_pm(om, s, r, b2, p2, max(c, c2), kp, kh) :- hv_pm(om, s, r, b, p, c, kp, kh), !hv_param(b), hv_res(b, p, b2, p2, c2), (b2 != b ; p2 != p). +// the receiver is a parameter of the function the element parameter belongs to: both bound at each call +.decl hv_recv_param(c0:symbol, c1:symbol, c2:symbol) +hv_recv_param(r, w, pos) :- hv_rx(r), hr_app(r, pr), param_decl("client", _, pos, _, w, pr). +.decl hv_rx(c0:symbol) +hv_rx(r) :- hr_on(), call_site("client", "METHOD_CALL", cn, _, r, _, _), router_mount_app(cn), r != "". +hv_rx(r) :- hr_on(), call_site("client", _, _, _, _, oc, _), expr_child("client", oc, "ARGUMENT", _, r). +hv_pm(om, oc, r2, b2, cat(p2, p), max(c, c2), kp, kh) :- hv_pm(om, _, r, b, p, c, kp, kh), param_decl("client", _, pos, _, w, b), + hv_recv_param(r, w, rpos), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a), hv_need_arg(a), hv(a, b2, p2, c2), + expr_child("client", oc, "ARGUMENT", rpos, r2), strlen(p2) + strlen(p) < 160. +hv_pm(om, s, r, b2, cat(p2, p), max(c, c2), kp, kh) :- hv_pm(om, s, r, b, p, c, kp, kh), param_decl("client", _, pos, _, w, b), + !hv_recv_param(r, w, _), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a), hv_need_arg(a), hv(a, b2, p2, c2), + strlen(p2) + strlen(p) < 160. +.decl hv_need_arg(c0:symbol) +hv_need_arg(a) :- hv_pm(_, _, _, b, _, _, _, _), param_decl("client", _, pos, _, w, b), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a). +hv_need(a) :- hv_need_arg(a). +// the element objects each pending mount takes, with the receiver it is mounted on +.decl hv_fin(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number, c5:symbol, c6:symbol) +hv_fin(om, s, r, o, c, kp, kh) :- hv_pm(om, s, r, o, "", c, kp, kh), expr_kind("client", "OBJECT_LITERAL", _, o). +.decl hv_fin_sub(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hv_fin_sub(s, r, o, t) :- hv_fin(_, s, r, o, _, _, kh), rd_obj_prop(o, kh, hx), hr_app(hx, t). +hr_included(t) :- hv_fin_sub(_, r, _, t), hr_app(r, a), a != t. +rd_demand(px) :- hv_fin(_, _, _, o, _, kp, _), rd_obj_prop(o, kp, px). +hr_inc(a, t, s, pre, "mount") :- hv_fin(_, s, r, o, _, kp, kh), rd_obj_prop(o, kh, hx), hr_app(hx, t), hr_val(r, a, b), a != t, + rd_obj_prop(o, kp, px), rd_val(px, pv), hr_merge(b, pv, pre), (contains("{}", pre) ; !hr_dyn_base_call_r(r)). +hr_mreq(b, pv) :- hv_fin(_, _, r, o, _, kp, _), hr_val(r, _, b), rd_obj_prop(o, kp, px), rd_val(px, pv). +.decl hr_dyn_base_call_r(c0:symbol) +hr_dyn_base_call_r(r) :- hv_fin(_, _, r, _, _, _, _), hr_val(r, _, b), contains("{}", b). +// filtered on the way: what it brings is conditional (SPEC R13b) +.decl hr_cond_inc(c0:symbol, c1:symbol) +hr_cond_inc(t, s) :- hv_fin(_, s, _, o, 1, _, kh), rd_obj_prop(o, kh, hx), hr_app(hx, t). +.decl hv_fin_mount(c0:symbol) +hv_fin_mount(om) :- hv_fin(om, _, _, _, _, _, _). + +// A method HANDED an app by a call through a list element (`for (const p of plugins) p.register?.(app)`): the element +// objects the loop iterates name the method; its app parameter is included into the passed app (kind `param`), each +// call where it is made (SPEC R13b; the element list filtered makes it conditional) +.decl hv_hcall(c0:symbol, c1:symbol) +hv_hcall(ce, q) :- call_site("client", k, n, _, q, ce, _), (k = "METHOD_CALL" ; k = "OPTIONAL_CALL"), q != "", expr_referenced("client", "VARIABLE", v, q), for_binding_iterable(v, _), + !hr_registrar(n), expr_child("client", ce, "ARGUMENT", _, x), hr_app(x, _). +hv_need(q) :- hv_hcall(_, q). +.decl hv_hobj(c0:symbol, c1:symbol, c2:number) +hv_hobj(ce, o, max(c, c2)) :- hv_hcall(ce, q), hv(q, b, p, c), hv_todo2(b, p), hv_res(b, p, o, "", c2), expr_kind("client", "OBJECT_LITERAL", _, o). +.decl hv_todo2(c0:symbol, c1:symbol) +hv_todo2(b, p) :- hv_hcall(_, q), hv(q, b, p, _). +hv_todo(b, p) :- hv_todo2(b, p). +// the method the element names: its own member, or one of an object it spreads +.decl hv_member(c0:symbol, c1:symbol, c2:symbol) +hv_member(o, n, f) :- hv_hobj(ce, o, _), call_site("client", _, n, _, _, ce, _), objlit_member_fn(o, n, f). +hv_member(o0, n, f) :- hv_hobj(ce, o0, _), call_site("client", _, n, _, _, ce, _), hv_spreads(o0, o), objlit_member_fn(o, n, f). +.decl hv_spreads(c0:symbol, c1:symbol) +hv_spreads(o, o2) :- hv_hobj(_, o, _), expr_child("client", o, "SPREAD_OPERAND", _, x), hv_spread_src(x, y), hv_need_sp(y), hv(y, b, p, _), hv_todo3(b, p), + hv_res(b, p, o2, "", _), expr_kind("client", "OBJECT_LITERAL", _, o2). +.decl hv_need_sp(c0:symbol) +hv_need_sp(y) :- hv_hobj(_, o, _), expr_child("client", o, "SPREAD_OPERAND", _, x), hv_spread_src(x, y). +hv_need(y) :- hv_need_sp(y). +.decl hv_todo3(c0:symbol, c1:symbol) +hv_todo3(b, p) :- hv_need_sp(y), hv(y, b, p, _). +hv_todo(b, p) :- hv_todo3(b, p). +.decl hv_handed(c0:symbol, c1:symbol, c2:symbol, c3:number) +hv_handed(ce, f, pf, c) :- hv_hobj(ce, o, c), hv_member(o, n, f), call_site("client", _, n, _, _, ce, _), + expr_child("client", ce, "ARGUMENT", pos, x), hr_app(x, _), param_decl("client", _, pos, _, f, pf). +hr_included(pf) :- hv_handed(_, _, pf, _). +hr_inc(a, pf, ce, b, "param") :- hv_handed(ce, f, pf, _), param_decl("client", _, pos, _, f, pf), expr_child("client", ce, "ARGUMENT", pos, x), + hr_val(x, a, b), a != pf. +hr_cond_inc(pf, ce) :- hv_handed(ce, _, pf, 1). + +// …the same through a PARAMETER (`function mountPlugin(app, plugin) { plugin.register?.(app) }`): the call names the +// member of whatever object each call of the function passes, so it is bound per call like a pending mount (hv_pm), +// rebinding the app argument too when it is a parameter of the same function; the outermost call is the inclusion +// site. (call, receiver parameter, position of the app argument) +.decl hv_hsite(c0:symbol, c1:symbol, c2:symbol) +hv_hsite(ce, q, xpos) :- call_site("client", k, n, _, q, ce, _), (k = "METHOD_CALL" ; k = "OPTIONAL_CALL"), q != "", + (expr_referenced("client", "PARAMETER", _, q) ; expr_referenced("client", "VARIABLE", v, q), for_binding_iterable(v, _)), + !hr_registrar(n), expr_child("client", ce, "ARGUMENT", xpos, x), hr_app(x, _). +hv_need(q) :- hv_hsite(_, q, _). +// (handed call, inclusion site, app expression, element base, path, filtered) +.decl hv_hpm(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:number) +hv_hpm(ce, ce, x, b, cat(p, cat("|.", n)), c) :- hv_hsite(ce, q, xpos), call_site("client", _, n, _, _, ce, _), expr_child("client", ce, "ARGUMENT", xpos, x), + hv(q, b, p, c), strlen(p) < 120. +hv_todo(b, p) :- hv_hpm(_, _, _, b, p, _). +hv_hpm(ce, s, x, b2, p2, max(c, c2)) :- hv_hpm(ce, s, x, b, p, c), !hv_param(b), hv_res(b, p, b2, p2, c2), (b2 != b ; p2 != p). +hv_hpm(ce, oc, x2, b2, cat(p2, p), max(c, c2)) :- hv_hpm(ce, _, x, b, p, c), param_decl("client", _, pos, _, w, b), + hv_recv_param(x, w, rpos), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a), hv_hneed_arg(a), hv(a, b2, p2, c2), + expr_child("client", oc, "ARGUMENT", rpos, x2), strlen(p2) + strlen(p) < 160. +hv_hpm(ce, s, x, b2, cat(p2, p), max(c, c2)) :- hv_hpm(ce, s, x, b, p, c), param_decl("client", _, pos, _, w, b), + !hv_recv_param(x, w, _), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a), hv_hneed_arg(a), hv(a, b2, p2, c2), + strlen(p2) + strlen(p) < 160. +.decl hv_hneed_arg(c0:symbol) +hv_hneed_arg(a) :- hv_hpm(_, _, _, b, _, _), param_decl("client", _, pos, _, w, b), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, a). +hv_need(a) :- hv_hneed_arg(a). +// the method each call reaches, and its app parameter included into the app the inclusion site passes +.decl hv_hfin(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +hv_hfin(ce, s, x, pf, c) :- hv_hpm(ce, s, x, f, "", c), hv_hsite(ce, _, xpos), param_decl("client", _, xpos, _, f, pf). +hr_included(pf) :- hv_hfin(_, _, _, pf, _). +hr_inc(a, pf, s, b, "param") :- hv_hfin(_, s, x, pf, _), hr_val(x, a, b), a != pf. +hr_cond_inc(pf, s) :- hv_hfin(_, s, _, pf, 1). + +// ── mergePath: how the router joins a prefix and a path ──────────────────── +// Each piece gets a leading `/`; a prefix's trailing `/` is dropped before a non-root +// path and kept before `/`; a `/` path adds nothing; the empty result is `/`. +.decl hr_mreq(c0:symbol, c1:symbol) +.decl hr_merge(c0:symbol, c1:symbol, c2:symbol) +.decl hr_lead(c0:symbol, c1:symbol) +hr_lead(x, x) :- (hr_mreq(x, _) ; hr_mreq(_, x)), strlen(x) >= 1, substr(x, 0, 1) = "/". +hr_lead(x, y) :- (hr_mreq(x, _) ; hr_mreq(_, x)), strlen(x) >= 1, substr(x, 0, 1) != "/", y = cat("/", x). +hr_lead("", "/") :- (hr_mreq("", _) ; hr_mreq(_, "")). +// prefix ends with `/` +hr_merge(a, b, a1) :- hr_mreq(a, b), hr_lead(a, a1), hr_lead(b, "/"), + substr(a1, strlen(a1) - 1, 1) = "/". +hr_merge(a, b, r) :- hr_mreq(a, b), hr_lead(a, a1), hr_lead(b, b1), b1 != "/", + substr(a1, strlen(a1) - 1, 1) = "/", r = cat(substr(a1, 0, strlen(a1) - 1), b1). +// prefix does not end with `/` +hr_merge(a, b, a1) :- hr_mreq(a, b), hr_lead(a, a1), hr_lead(b, "/"), + substr(a1, strlen(a1) - 1, 1) != "/". +hr_merge(a, b, r) :- hr_mreq(a, b), hr_lead(a, a1), hr_lead(b, b1), b1 != "/", + substr(a1, strlen(a1) - 1, 1) != "/", r = cat(a1, b1). + +// ── position keys: registration order as comparable symbols ──────────────── +// A key is the zero-padded line and column of the registering token (`.get` of +// `app.x().get()`, so a chain orders left to right), or of the call for a function +// call. A list included into another gets the include site's key as a dotted prefix, +// so plain symbol comparison is program order on the flattened list. +.decl hr_need_key(c0:symbol) +hr_need_key(ce) :- hr_reg(ce, _, _). +hr_need_key(oc) :- hr_slot_arg(oc, _, a), hr_app(a, _). +hr_need_key(ce) :- hr_app(ce, ce), !hr_new(ce). +hr_need_key(ce) :- hr_objret_inst(ce, _). +.decl hr_key_pn(c0:symbol, c1:symbol) +hr_key_pn(ce, pn) :- hr_need_key(ce), call_callee_expr(_, ce, ne), expr_child("client", ne, "PROPERTY_NAME", _, pn). +.decl hr_has_key_pn(c0:symbol) +hr_has_key_pn(ce) :- hr_key_pn(ce, _). +.decl hr_key_expr(c0:symbol, c1:symbol) +hr_key_expr(ce, pn) :- hr_key_pn(ce, pn). +hr_key_expr(ce, ce) :- hr_need_key(ce), !hr_has_key_pn(ce). +.decl hr_key(c0:symbol, c1:symbol) +hr_key(ce, k) :- hr_key_expr(ce, x), + ts_expression(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, l, c, _, _, _, _, _, _, _, _, _, _, _, x), + strlen(l) <= 7, strlen(c) <= 5, + k = cat(cat(substr("0000000", 0, 7 - strlen(l)), l), cat(":", cat(substr("00000", 0, 5 - strlen(c)), c))). diff --git a/graph/typescript/engine/config-resolution/http-router-routes.dl b/graph/typescript/engine/config-resolution/http-router-routes.dl new file mode 100644 index 000000000..35de0e2a8 --- /dev/null +++ b/graph/typescript/engine/config-resolution/http-router-routes.dl @@ -0,0 +1,827 @@ +// ============================================================================ +// CONFIG-RESOLUTION · HTTP ROUTER ROUTES (the route table, composed) +// +// From the app lists of http-router-apps.dl to what a served application answers: +// +// http_route(Handler, Verb, FullPath, App, Order) +// http_route_partial(Handler, Verb, KnownPath, App, Reason) +// http_mount(ParentApp, ChildApp, Prefix, Order) +// middleware_applies(Mw, Handler, Verb, FullPath, Position, App) +// middleware_site(Mw, Kind, Callee, Specifier) +// framework_undetermined(Node, What, Reason) +// +// Every function handed to a registration is an `http` entry point: the router calls +// it, nothing in the program does. +// +// ── THE MODEL ─────────────────────────────────────────────────────────────── +// A registering call puts one ENTRY per handler argument on its list, in argument +// order, under (verb, base + path). The last argument is the route's handler, earlier +// ones are middleware for that route only. `use` puts middleware entries for every verb +// (no path means every path). A list nothing includes is a ROOT; flattening a root +// splices every included list at its include site, under the include's prefix, so the +// flattened order key compares as program order. A mounted sub-app contributes only +// what was registered on it before the mount when both are in one module (the router +// copies the sub-app's routes at mount time); a sub-app from another module is fully +// registered by then, because a module's imports run before it. +// +// ── DECLINED, AND SAID SO ─────────────────────────────────────────────────── +// A path that is not a literal, a template of literals or a const comes out of rd_val +// with a `{}` hole: the route goes to http_route_partial with a reason, never into +// http_route. A middleware scope with a literal segment where the route has a +// parameter cannot be decided without a request: no row, framework_undetermined. +// ============================================================================ + +// ── where the handlers start in a registering call ────────────────────────── +// A first argument that is a function (written in place, named, or a value built by a +// call) is a handler, and the call continues the previous path of its chain. +.decl hr_arg0_fnlike(c0:symbol) +hr_arg0_fnlike(ce) :- hr_reg(ce, _, _), expr_child("client", ce, "ARGUMENT", "0", a), + expr_kind("client", k, _, a), (k = "ARROW_FUNCTION" ; k = "FUNCTION_EXPRESSION" ; k = "CALL_EXPRESSION" ; k = "SPREAD_ELEMENT"). +hr_arg0_fnlike(ce) :- hr_reg(ce, _, _), expr_child("client", ce, "ARGUMENT", "0", a), + expr_referenced("client", "METHOD", _, a). +hr_arg0_fnlike(ce) :- hr_reg(ce, _, _), expr_child("client", ce, "ARGUMENT", "0", a), + expr_referenced("client", "IMPORT_BINDING", ih, a), import_binds(ih, "client", "METHOD", _). +hr_arg0_fnlike(ce) :- hr_reg(ce, _, _), expr_child("client", ce, "ARGUMENT", "0", a), + hr_denotes_var(a, v), var_initializer("client", _, i, v), i != "", + expr_kind("client", k, _, i), (k = "ARROW_FUNCTION" ; k = "FUNCTION_EXPRESSION" ; k = "CALL_EXPRESSION"). + +.decl hr_pathed(c0:symbol) +hr_pathed(ce) :- hr_reg(ce, _, cn), (router_verb(cn, _) ; router_use(cn)), + expr_child("client", ce, "ARGUMENT", "0", _), !hr_arg0_fnlike(ce). +.decl hr_pathless(c0:symbol) +hr_pathless(ce) :- hr_reg(ce, _, cn), (router_verb(cn, _) ; router_use(cn)), hr_arg0_fnlike(ce). + +.decl hr_hstart(c0:symbol, c1:number) +hr_hstart(ce, 1) :- hr_pathed(ce). +hr_hstart(ce, 0) :- hr_pathless(ce). +hr_hstart(ce, 2) :- hr_reg(ce, _, cn), router_on(cn). +hr_hstart(ce, 0) :- hr_reg(ce, _, cn), router_fallback(cn). +// `openapi(routeDef, handler, hook?)`: the handler only (the hook validates, it is not a route) +hr_harg(ce, 100, a) :- hr_reg(ce, _, cn), router_openapi(cn), expr_child("client", ce, "ARGUMENT", "1", a). +// the definition's `middleware` (one, or a list) runs before it, in order +.decl hr_def_mw(c0:symbol, c1:symbol) +hr_def_mw(ce, v) :- hr_openapi_def(ce, o), rd_obj_prop(o, k, v), router_def_key(k, "middleware"). +hr_def_mw(ce, c) :- hr_def_mw(ce, v), expr_kind("client", k, _, v), expr_kind_is_transparent(k), expr_child("client", v, _, _, c). +hr_harg(ce, n, el) :- hr_def_mw(ce, arr), expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, epos, el), + to_number(epos) < 90, n = 10 + to_number(epos). +hr_harg(ce, 10, v) :- hr_def_mw(ce, v), !expr_kind("client", "ARRAY_LITERAL", _, v), + expr_kind("client", k, _, v), !expr_kind_is_transparent(k). + +// ── the handler arguments, in order: (call, ordinal, expression) ──────────── +// ordinal = argument position * 100 + element index, so a spread `...[a, b]` keeps +// its elements in place. +.decl hr_harg(c0:symbol, c1:number, c2:symbol) +hr_harg(ce, n, a) :- hr_hstart(ce, s), expr_child("client", ce, "ARGUMENT", pos, a), + n = to_number(pos) * 100, to_number(pos) >= s, + !expr_kind("client", "SPREAD_ELEMENT", _, a). +// `mount(prefix, fetch, options?)`: the one foreign handler +hr_harg(ce, 100, a) :- hr_reg(ce, _, cn), router_mount_foreign(cn), expr_child("client", ce, "ARGUMENT", "1", a). +// a spread of a const array literal, or of a call that hands its arguments back as +// handlers (router_handler_spread) +.decl hr_spread(c0:symbol, c1:number, c2:symbol) +hr_spread(ce, p, x) :- hr_hstart(ce, s), expr_child("client", ce, "ARGUMENT", pos, a), + p = to_number(pos), p >= s, expr_kind("client", "SPREAD_ELEMENT", _, a), + expr_child("client", a, _, _, x). +.decl hr_spread_arr(c0:symbol, c1:number, c2:symbol) +hr_spread_arr(ce, p, arr) :- hr_spread(ce, p, x), expr_kind("client", "ARRAY_LITERAL", _, x), arr = x. +hr_spread_arr(ce, p, arr) :- hr_spread(ce, p, x), hr_denotes_var(x, v), var_initializer("client", _, arr, v), + expr_kind("client", "ARRAY_LITERAL", _, arr). +.decl hr_spread_ret(c0:symbol, c1:number, c2:symbol) +hr_spread_ret(ce, p, x) :- hr_spread(ce, p, x), call_site("client", _, _, _, _, x, _). +hr_spread_ret(ce, p, i) :- hr_spread(ce, p, x), hr_denotes_var(x, v), var_initializer("client", _, i, v), + call_site("client", _, _, _, _, i, _). +.decl hr_spread_val(c0:symbol, c1:number, c2:symbol) +hr_spread_val(ce, p, e) :- hr_spread_ret(ce, p, x), hr_call_of(x, f), hr_ret_expr(f, e). +hr_spread_arr(ce, p, e) :- hr_spread_val(ce, p, e), expr_kind("client", "ARRAY_LITERAL", _, e). +hr_spread_arr(ce, p, arr) :- hr_spread_val(ce, p, e), hr_denotes_var(e, v), var_initializer("client", _, arr, v), + expr_kind("client", "ARRAY_LITERAL", _, arr). +hr_harg(ce, n, el) :- hr_spread_arr(ce, p, arr), expr_child("client", arr, _, epos, el), + n = p * 100 + to_number(epos), to_number(epos) < 100. +.decl hr_spread_call(c0:symbol, c1:number, c2:symbol) +hr_spread_call(ce, p, x) :- hr_spread(ce, p, x), call_site("client", _, sn, _, _, x, _), router_handler_spread(sn). +hr_spread_call(ce, p, i) :- hr_spread(ce, p, x), hr_denotes_var(x, v), var_initializer("client", _, i, v), + call_site("client", _, sn, _, _, i, _), router_handler_spread(sn). +// …through a factory method read off into a const (`const mkHandlers = factory.createHandlers`) +hr_spread_call(ce, p, x) :- hr_spread(ce, p, x), hr_detached(x, n, _), router_handler_spread(n). +hr_spread_call(ce, p, i) :- hr_spread(ce, p, x), hr_denotes_var(x, v), var_initializer("client", _, i, v), + hr_detached(i, n, _), router_handler_spread(n). +hr_harg(ce, n, el) :- hr_spread_call(ce, p, x), expr_child("client", x, "ARGUMENT", epos, el), + n = p * 100 + to_number(epos), to_number(epos) < 100. +.decl hr_spread_known(c0:symbol, c1:number) +hr_spread_known(ce, p) :- hr_spread_arr(ce, p, _). +hr_spread_known(ce, p) :- hr_spread_call(ce, p, _). +framework_undetermined(m, "middleware", "dynamic_middleware_list") :- hr_spread(ce, p, _), + !hr_spread_known(ce, p), expr_enclosing_method(ce, m). + +.decl hr_last(c0:symbol, c1:number) +hr_last(ce, n) :- hr_harg(ce, _, _), n = max x : { hr_harg(ce, x, _) }. + +// ── the function each handler argument is ─────────────────────────────────── +// Written in place, named, a const holding one, an import of one, what a project +// function returns when called (a middleware factory), or the argument of a knob +// wrapper (`createMiddleware(fn)`). +.decl hr_farg(c0:symbol) +hr_farg(a) :- hr_harg(_, _, a). +hr_farg(i) :- hr_farg(a), hr_denotes_var(a, v), var_initializer("client", _, i, v), i != "". +hr_farg(e) :- hr_farg(a), hr_call_of(a, f), hr_ret_expr(f, e). +hr_farg(x) :- hr_farg(a), hr_wrapper_call(a), expr_child("client", a, "ARGUMENT", "0", x). +hr_farg(c) :- hr_farg(a), expr_kind("client", k, _, a), expr_kind_is_transparent(k), expr_child("client", a, _, _, c). +// `Object.assign(fn, { scope })` returns its first argument, the function, with properties added +.decl hr_assign_call(c0:symbol) +hr_assign_call(a) :- hr_on(), call_site("client", "METHOD_CALL", "assign", _, r, a, _), r != "", !hr_call_of(a, _). +hr_farg(x) :- hr_farg(a), hr_assign_call(a), expr_child("client", a, "ARGUMENT", "0", x). +.decl hr_wrapper_call(c0:symbol) +hr_wrapper_call(a) :- call_site("client", "METHOD_CALL", cn, _, r, a, _), router_factory_mw(cn), hr_factory_val(r, _). +hr_wrapper_call(a) :- hr_detached(a, n, _), router_factory_mw(n). +hr_wrapper_call(a) :- hr_on(), call_site("client", "FUNCTION_CALL", _, _, _, a, _), + expr_child("client", a, "METHOD_NAME", _, f), expr_referenced("client", "IMPORT_BINDING", ih, f), + import_binding("client", _, _, orig, _, ih), hr_import_spec(ih, spec), router_mw_wrapper(spec, orig). +.decl hr_fn(c0:symbol, c1:symbol) +hr_fn(a, m) :- hr_farg(a), expr_anon_decl("client", m, a). +hr_fn(a, m) :- hr_farg(a), expr_referenced("client", "METHOD", m, a). +hr_fn(a, m) :- hr_farg(a), expr_referenced("client", "IMPORT_BINDING", ih, a), import_binds(ih, "client", "METHOD", m). +// a member of a namespace import (`import * as handlers …; handlers.login`): the module's export +.decl hr_ns_member(c0:symbol, c1:symbol, c2:symbol) +hr_ns_member(a, k, ent) :- hr_farg(a), property_access_name(a, n), expr_child("client", a, "RECEIVER", _, r), + expr_referenced("client", "IMPORT_BINDING", ih, r), import_binds_namespace(ih, "client", mod), + module_export_any("client", mod, n, k, ent). +hr_fn(a, m) :- hr_ns_member(a, "METHOD", m). +hr_farg(i) :- hr_ns_member(_, "VARIABLE", v), var_initializer("client", _, i, v), i != "". +hr_fn(a, m) :- hr_ns_member(a, "VARIABLE", v), var_initializer("client", _, i, v), i != "", hr_fn(i, m). +hr_fn(a, m) :- hr_farg(a), hr_denotes_var(a, v), var_initializer("client", _, i, v), hr_fn(i, m). +// the value the function RETURNS, never a function written inside it (the option callbacks of +// `return rateLimiter({ keyGenerator: (c) => … })` are not middleware, SPEC ruling R10) +hr_fn(a, m) :- hr_farg(a), hr_call_of(a, f), hr_ret_expr(f, e), hr_fn(e, m). +hr_fn(a, m) :- hr_farg(a), hr_wrapper_call(a), expr_child("client", a, "ARGUMENT", "0", x), hr_fn(x, m). +hr_fn(a, m) :- hr_farg(a), hr_assign_call(a), expr_child("client", a, "ARGUMENT", "0", x), hr_fn(x, m). +hr_fn(a, m) :- hr_farg(a), expr_kind("client", k, _, a), expr_kind_is_transparent(k), + expr_child("client", a, _, _, c), hr_fn(c, m). + +.decl hr_node_fn(c0:symbol, c1:number, c2:symbol) +hr_node_fn(ce, n, m) :- hr_harg(ce, n, a), hr_fn(a, m), method_prov(m, "client"). +// the node a ROUTE's handler is: the project function, or a library call's result keyed at the call +// (`serveStatic({...})`), never dropped (SPEC ruling R5) +.decl hr_route_node(c0:symbol, c1:number, c2:symbol) +hr_route_node(ce, n, m) :- hr_node_fn(ce, n, m). +hr_route_node(ce, n, a) :- hr_harg(ce, n, a), !hr_has_fn(ce, n), call_site("client", _, _, _, _, a, _), !hr_has_libret(a). +hr_route_node(ce, n, x) :- hr_harg(ce, n, a), !hr_has_fn(ce, n), hr_libret(a, x). +.decl hr_has_fn(c0:symbol, c1:number) +hr_has_fn(ce, n) :- hr_node_fn(ce, n, _). +// a project function that returns a library call's result (`limiter(5)` returning +// `rateLimiter({...})`): the middleware is that library call, keyed where it is written +.decl hr_libret(c0:symbol, c1:symbol) +hr_libret(a, x) :- hr_farg(a), hr_call_of(a, f), hr_ret_expr(f, x), call_site("client", _, _, _, _, x, _), + !hr_call_of(x, _), !hr_wrapper_call(x), !hr_assign_call(x). +// …also through a const holding that call (`const a = limiter(1); app.use(...[a, b])`) +hr_libret(a, x) :- hr_farg(a), hr_denotes_var(a, v), var_initializer("client", _, i, v), i != "", hr_libret(i, x). +// …or a const holding a library call itself (SPEC ruling R10; R11's `value` is for a non-call value only) +hr_libret(a, i) :- hr_farg(a), hr_denotes_var(a, v), var_initializer("client", _, i, v), i != "", + call_site("client", _, _, _, _, i, _), !hr_call_of(i, _), !hr_wrapper_call(i), !hr_any_fn(a). +.decl hr_any_fn(c0:symbol) +hr_any_fn(a) :- hr_fn(a, _). +// the node a handler argument stands for: the project function, else the library call it +// returns, else the argument +.decl hr_node(c0:symbol, c1:number, c2:symbol) +hr_node(ce, n, m) :- hr_node_fn(ce, n, m). +hr_node(ce, n, x) :- hr_harg(ce, n, a), hr_libret(a, x). +hr_node(ce, n, a) :- hr_harg(ce, n, a), !hr_has_fn(ce, n), !hr_has_libret(a). +.decl hr_has_libret(c0:symbol) +hr_has_libret(a) :- hr_libret(a, _). + +// a call of a knob function imported from its module: (call, which knob relation, name) +.decl hr_knob_call(c0:symbol, c1:symbol, c2:symbol) +hr_knob_call(a, spec, orig) :- hr_on(), call_site("client", "FUNCTION_CALL", _, _, _, a, _), + expr_child("client", a, "METHOD_NAME", _, f), expr_referenced("client", "IMPORT_BINDING", ih, f), + import_binding("client", _, _, orig, _, ih), hr_import_spec(ih, spec). +// the functions a combinator runs: `every(a, b)`, `some(a, b)`, `except(paths, mw)` +.decl hr_comb_arg(c0:symbol) +hr_comb_arg(x) :- hr_harg(_, _, a), hr_knob_call(a, spec, n), router_mw_combinator(spec, n), + expr_child("client", a, "ARGUMENT", _, x). +hr_comb_arg(x) :- hr_harg(_, _, a), hr_knob_call(a, spec, n), router_mw_except(spec, n), + expr_child("client", a, "ARGUMENT", "1", x). +hr_farg(x) :- hr_comb_arg(x). +entry_point(m, "http") :- hr_comb_arg(x), hr_fn(x, m), method_prov(m, "client"). +// `except(path | [paths], mw)`: not on a route whose full path is one of them +.decl hr_except_path(c0:symbol, c1:number, c2:symbol) +hr_except_path(ce, n, v) :- hr_harg(ce, n, a), hr_knob_call(a, spec, nm), router_mw_except(spec, nm), + expr_child("client", a, "ARGUMENT", "0", x), rd_val(x, v). +hr_except_path(ce, n, v) :- hr_harg(ce, n, a), hr_knob_call(a, spec, nm), router_mw_except(spec, nm), + expr_child("client", a, "ARGUMENT", "0", arr), expr_kind("client", "ARRAY_LITERAL", _, arr), + expr_child("client", arr, _, _, x), rd_val(x, v). +rd_demand(x) :- hr_harg(_, _, a), hr_knob_call(a, spec, nm), router_mw_except(spec, nm), + expr_child("client", a, "ARGUMENT", "0", x). +rd_demand(x) :- hr_harg(_, _, a), hr_knob_call(a, spec, nm), router_mw_except(spec, nm), + expr_child("client", a, "ARGUMENT", "0", arr), expr_kind("client", "ARRAY_LITERAL", _, arr), + expr_child("client", arr, _, _, x). +.decl hr_excepted(c0:symbol, c1:number, c2:symbol) +hr_excepted(ce, n, p) :- hr_except_path(ce, n, p). +// …an exclusion that is not written as literal paths (a property read, a predicate, a value chosen at run time): +// which routes it skips is not known here (SPEC 3.1 dynamic_except), declared on the middleware it wraps, or on the +// function that builds the call when that middleware is not a function the program names +.decl hr_except_call(c0:symbol, c1:symbol) +hr_except_call(x, a0) :- hr_knob_call(x, spec, nm), router_mw_except(spec, nm), expr_child("client", x, "ARGUMENT", "0", a0). +.decl hr_lit_paths(c0:symbol) +hr_lit_paths(a0) :- hr_except_call(_, a0), expr_kind("client", "LITERAL", _, a0). +hr_lit_paths(a0) :- hr_except_call(_, a0), expr_kind("client", "ARRAY_LITERAL", _, a0), + !hr_nonlit_elem(a0). +.decl hr_nonlit_elem(c0:symbol) +hr_nonlit_elem(a0) :- hr_except_call(_, a0), expr_kind("client", "ARRAY_LITERAL", _, a0), expr_child("client", a0, _, _, y), + !expr_kind("client", "LITERAL", _, y). +.decl hr_dyn_except(c0:symbol) +hr_dyn_except(x) :- hr_except_call(x, a0), !hr_lit_paths(a0). +.decl hr_dyn_except_named(c0:symbol) +hr_dyn_except_named(x) :- hr_dyn_except(x), expr_child("client", x, "ARGUMENT", "1", x1), hr_fn(x1, _). +framework_undetermined(m, "middleware_scope", "dynamic_except") :- hr_dyn_except(x), expr_child("client", x, "ARGUMENT", "1", x1), hr_fn(x1, m). +framework_undetermined(m, "middleware_scope", "dynamic_except") :- hr_dyn_except(x), !hr_dyn_except_named(x), expr_enclosing_method(x, m). + +// a served default export `{ fetch: app.fetch, scheduled, queue() {} }`: the platform +// calls the other members +.decl hr_served_obj(c0:symbol) +hr_served_obj(o) :- export_decl(_, "default", _, "DEFAULT_EXPRESSION", mod, h), export_expression("client", o, h), + expr_kind("client", "OBJECT_LITERAL", _, o), rd_obj_prop(o, key, v), router_served_fetch(key), + property_access_name(v, key), expr_child("client", v, "RECEIVER", _, r), hr_app(r, _), mod = mod. +// `{ fetch(req, env, ctx) { return app.fetch(req, env, ctx) } }`: a fetch member that forwards to an +// app's fetch is the same served shape (SPEC ruling R4); the fetch member is called by the platform too +hr_served_obj(o) :- export_decl(_, "default", _, "DEFAULT_EXPRESSION", _, h), export_expression("client", o, h), + expr_kind("client", "OBJECT_LITERAL", _, o), objlit_member_fn(o, key, fm), router_served_fetch(key), + call_site("client", "METHOD_CALL", key, _, r, ce, _), hr_app(r, _), expr_enclosing_method(ce, fm). + +// http_served_by(App, Site, How) — where a root app is handed to whatever serves it: the default export +// `{ fetch: app.fetch }` or one whose fetch forwards to the app (default_export_fetch), `export default app` +// (default_export), its fetch handed to a call such as `serve({ fetch: app.fetch })` (fetch_handed), or a request +// sent to it in place, a test (request). What "how does a request reach the app" is answered with. +http_served_by(ai, o, "default_export_fetch") :- hr_served_obj(o), rd_obj_prop(o, key, v), router_served_fetch(key), + property_access_name(v, key), expr_child("client", v, "RECEIVER", _, r), hr_app(r, a), hr_root(a), hr_app_id(a, ai). +http_served_by(ai, o, "default_export_fetch") :- hr_served_obj(o), objlit_member_fn(o, key, fm), router_served_fetch(key), + call_site("client", "METHOD_CALL", key, _, r, ce, _), expr_enclosing_method(ce, fm), hr_app(r, a), hr_root(a), hr_app_id(a, ai). +http_served_by(ai, x, "default_export") :- export_decl(_, "default", _, _, _, h), export_expression("client", x, h), hr_app(x, a), + hr_root(a), !hr_partial_root(a), hr_app_id(a, ai). +http_served_by(ai, ce, "fetch_handed") :- property_access_name(v, key), router_served_fetch(key), expr_child("client", v, "RECEIVER", _, r), + hr_app(r, a), hr_root(a), hr_app_id(a, ai), hr_handed_in(v, ce), !hr_served_obj_val(v). +.decl hr_handed_in(c0:symbol, c1:symbol) +hr_handed_in(v, ce) :- hr_on(), expr_child("client", ce, "ARGUMENT", _, v), call_site("client", _, _, _, _, ce, _). +hr_handed_in(v, ce) :- hr_on(), expr_child("client", o, "OBJECT_PROPERTY_VALUE", _, v), expr_child("client", ce, "ARGUMENT", _, o), + call_site("client", _, _, _, _, ce, _). +.decl hr_served_obj_val(c0:symbol) +hr_served_obj_val(v) :- hr_served_obj(o), rd_obj_prop(o, _, v). + +entry_point(m, "http") :- hr_served_obj(o), rd_obj_prop(o, key, v), !router_served_fetch(key), + expr_callable(v, m), method_prov(m, "client"). +entry_point(m, "http") :- hr_served_obj(o), objlit_member_fn(o, k, m), !router_served_fetch(k). + +// ── http: every function a router calls ───────────────────────────────────── +// Handlers, per-route and app-level middleware, notFound/onError, a mounted foreign +// handler. Receiver-checked, unlike entry-points.dl's verb-and-path rule, which stays. +entry_point(m, "http") :- hr_node_fn(_, _, m). + +// ── the local path of each registering call ───────────────────────────────── +.decl hr_local(c0:symbol, c1:symbol) +hr_local(ce, lp) :- hr_pathed(ce), expr_child("client", ce, "ARGUMENT", "0", p), rd_val(p, lp). +hr_local(ce, "*") :- hr_pathless(ce), hr_reg(ce, _, cn), router_use(cn). +// a path-less verb continues the path of the registration it is chained on; on a +// fresh app value that is `/`; on anything else it is not known here +.decl hr_cont_src(c0:symbol) +hr_cont_src(r) :- hr_reg(r, _, rn), (router_verb(rn, _) ; router_use(rn)). +hr_cont_src(r) :- hr_reg(r, _, rn), router_on(rn). +// after `on(verbs, path, h)` a path-less verb continues that path (each of a path list: the +// router keeps the last, which this does not single out) +hr_local(ce, lp) :- hr_pathless(ce), hr_reg(ce, _, cn), router_verb(cn, _), + call_site("client", _, _, _, r, ce, _), hr_reg(r, _, rn), router_on(rn), hr_on_piece(r, "1", lp). +hr_cont_src(r) :- hr_new(r). +hr_cont_src(r) :- hr_on(), call_site("client", "METHOD_CALL", cn, _, _, r, _), router_base_path(cn). +hr_local(ce, lp) :- hr_pathless(ce), hr_reg(ce, _, cn), router_verb(cn, _), + call_site("client", _, _, _, r, ce, _), hr_local(r, lp), hr_reg(r, _, rn), (router_verb(rn, _) ; router_use(rn)). +hr_local(ce, "/") :- hr_pathless(ce), hr_reg(ce, _, cn), router_verb(cn, _), + call_site("client", _, _, _, r, ce, _), (hr_new(r) ; call_site("client", "METHOD_CALL", bn, _, _, r, _), router_base_path(bn)). +// on a variable: the router keeps one current path per app object, so it is the path +// of the previous registration on that app in this module; with none, `/` +.decl hr_pathless_var(c0:symbol) +hr_pathless_var(ce) :- hr_pathless(ce), hr_reg(ce, _, cn), router_verb(cn, _), + call_site("client", _, _, _, r, ce, _), !hr_cont_src(r). +.decl hr_keyn(c0:symbol, c1:number) +hr_keyn(ce, n) :- hr_key_expr(ce, x), + ts_expression(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, l, c, _, _, _, _, _, _, _, _, _, _, _, x), + to_number(l) < 200000, to_number(c) < 10000, n = to_number(l) * 10000 + to_number(c). +.decl hr_prev_cand(c0:symbol, c1:symbol, c2:number) +hr_prev_cand(ce, r2, n2) :- hr_pathless_var(ce), hr_reg(ce, a, _), hr_keyn(ce, n), expr_module("client", m, ce), + hr_reg(r2, a, rn), (router_verb(rn, _) ; router_use(rn)), r2 != ce, expr_module("client", m, r2), + hr_keyn(r2, n2), n2 < n. +.decl hr_prev(c0:symbol, c1:symbol) +hr_prev(ce, r2) :- hr_prev_cand(ce, _, _), b = max x : { hr_prev_cand(ce, _, x) }, hr_prev_cand(ce, r2, b). +.decl hr_has_prev(c0:symbol) +hr_has_prev(ce) :- hr_prev(ce, _). +hr_local(ce, lp) :- hr_prev(ce, r2), hr_local(r2, lp). +hr_local(ce, "/") :- hr_pathless_var(ce), !hr_has_prev(ce). +// `mount(prefix, h)` serves prefix/* +hr_local(ce, lp) :- hr_reg(ce, _, cn), router_mount_foreign(cn), expr_child("client", ce, "ARGUMENT", "0", p), + rd_val(p, pv), hr_merge(pv, "*", lp). +hr_mreq(pv, "*") :- hr_reg(ce, _, cn), router_mount_foreign(cn), expr_child("client", ce, "ARGUMENT", "0", p), rd_val(p, pv). + +// `on(verb | [verbs], path | [paths], ...)`: a verb is written in either case +.decl hr_on_piece(c0:symbol, c1:symbol, c2:symbol) +hr_on_piece(ce, pos, v) :- hr_reg(ce, _, cn), router_on(cn), expr_child("client", ce, "ARGUMENT", pos, x), + (pos = "0" ; pos = "1"), !expr_kind("client", "ARRAY_LITERAL", _, x), rd_val(x, v). +hr_on_piece(ce, pos, v) :- hr_reg(ce, _, cn), router_on(cn), expr_child("client", ce, "ARGUMENT", pos, x), + (pos = "0" ; pos = "1"), expr_kind("client", "ARRAY_LITERAL", _, x), expr_child("client", x, _, _, el), rd_val(el, v). +.decl hr_on_verb(c0:symbol, c1:symbol) +hr_on_verb(ce, vv) :- hr_on_piece(ce, "0", w), ts_http_verb_name(w, vv). +hr_on_verb(ce, w) :- hr_on_piece(ce, "0", w), !ts_http_verb_name(w, _). + +// ── the entries of a list: (list, key, kind, verb, path, call, ordinal) ───── +.decl hr_entry(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol, c6:number) +.decl hr_entry_base(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol) +// (call, list, kind, verb, local path, base) +hr_entry_base(ce, a, "route", vv, lp, b) :- hr_reg(ce, a, cn), router_verb(cn, vv), hr_local(ce, lp), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b). +hr_entry_base(ce, a, "mw", "ANY", lp, b) :- hr_reg(ce, a, cn), router_use(cn), hr_local(ce, lp), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b). +hr_entry_base(ce, a, "route", "ANY", lp, b) :- hr_reg(ce, a, cn), router_mount_foreign(cn), hr_local(ce, lp), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b). +hr_entry_base(ce, a, "route", vv, lp, b) :- hr_reg(ce, a, _), hr_on_verb(ce, vv), hr_on_piece(ce, "1", lp), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b). +hr_entry_base(ce, a, "route", vv, lp, b) :- hr_openapi_def(ce, o), hr_reg(ce, a, _), + rd_obj_prop(o, mk, mv), router_def_key(mk, "method"), rd_val(mv, w), ts_http_verb_name(w, vv), + rd_obj_prop(o, pk, pv), router_def_key(pk, "path"), rd_val(pv, raw), hr_brace_path(raw, lp), + call_site("client", _, _, _, r, ce, _), hr_val(r, a, b). +// an OpenAPI path writes a parameter `{id}`; the router registers `:id` +.decl hr_brace_raw(c0:symbol) +hr_brace_raw(raw) :- hr_openapi_def(_, o), rd_obj_prop(o, pk, pv), router_def_key(pk, "path"), rd_val(pv, raw). +.decl hr_brace_step(c0:symbol, c1:number, c2:symbol) +hr_brace_step(raw, 0, "") :- hr_brace_raw(raw). +hr_brace_step(raw, i + 1, cat(acc, ":")) :- hr_brace_step(raw, i, acc), i < strlen(raw), substr(raw, i, 1) = "{". +hr_brace_step(raw, i + 1, acc) :- hr_brace_step(raw, i, acc), i < strlen(raw), substr(raw, i, 1) = "}". +hr_brace_step(raw, i + 1, cat(acc, ch)) :- hr_brace_step(raw, i, acc), i < strlen(raw), ch = substr(raw, i, 1), + ch != "{", ch != "}". +.decl hr_brace_path(c0:symbol, c1:symbol) +hr_brace_path(raw, p) :- hr_brace_step(raw, strlen(raw), p). +hr_mreq(b, lp) :- hr_entry_base(_, _, _, _, lp, b). +hr_entry(a, k2, kind, vv, p, ce, n) :- hr_entry_base(ce, a, kind, vv, lp, b), hr_merge(b, lp, p), + hr_harg(ce, n, _), hr_key(ce, k), s = to_string(n), + k2 = cat(k, cat(",", cat(substr("00000", 0, 5 - strlen(s)), s))). +// the path as the call itself wrote it, without any base (for the partial reason: a hole in the +// call's own path is a dynamic route path, one anywhere else a dynamic prefix) +.decl hr_entry_own(c0:symbol, c1:symbol) +hr_entry_own(ce, lp) :- hr_entry_base(ce, _, _, _, lp, _). + +// ── flattening a root ─────────────────────────────────────────────────────── +// (root, list, prefix, key prefix, cut key, cut module): a mount in the sub-app's own +// module copies only the entries registered before it. +.decl hr_flat(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol) +hr_flat(r, r, "/", "", "", "") :- hr_root(r). +.decl hr_cutoff(c0:symbol, c1:symbol, c2:symbol) +hr_cutoff(site, k, m) :- hr_inc(_, sub, site, _, "mount"), hr_key(site, k), expr_module("client", m, site), !hr_inline_sub(site, sub). +// an app built INSIDE the mount's argument (`app.route('/x', new X().get('/', h))`): its chain runs before the mount +// does, though it is written after the mount's name, so nothing it registers there is cut off +hr_cutoff(site, "", "") :- hr_inc(_, sub, site, _, "mount"), hr_inline_sub(site, sub). +.decl hr_inl_desc(c0:symbol, c1:symbol) +hr_inl_desc(site, sx) :- hr_on(), call_site("client", "METHOD_CALL", cn, _, _, site, _), router_mount_app(cn), expr_child("client", site, "ARGUMENT", "1", sx). +hr_inl_desc(site, y) :- hr_inl_desc(site, z), expr_child("client", z, k, _, y), (k = "RECEIVER" ; k = "METHOD_NAME" ; k = "ROOT"). +.decl hr_inline_sub(c0:symbol, c1:symbol) +hr_inline_sub(site, sub) :- hr_inl_desc(site, sub). +hr_cutoff(site, "", "") :- hr_inc(_, _, site, _, kind), kind != "mount". +hr_flat(r, s, np, nk, cut, cm) :- hr_flat(r, a, p, kp, _, _), hr_inc(a, s, site, pre, kd), kd != "mount_each", + hr_merge(p, pre, np), hr_key(site, k), nk = cat(kp, cat(k, ".")), strlen(nk) < 160, + hr_cutoff(site, cut, cm). +// the candidates one loop mount takes (hr_each_cand) sit at the mount, in the order of the elements: `~` +hr_flat(r, s, np, nk, "", "") :- hr_flat(r, a, p, kp, _, _), hr_inc(a, s, site, pre, "mount_each"), hr_each_ord(site, s, o), + hr_merge(p, pre, np), hr_key(site, k), nk = cat(kp, cat(k, cat("~", cat(o, ".")))), strlen(nk) < 160. +.decl hr_each_ord(c0:symbol, c1:symbol, c2:symbol) +hr_each_ord(site, t, o) :- hr_inc(_, t, site, _, "mount_each"), expr_child("client", site, "ARGUMENT", "1", sx), hr_app(sx, s), + hr_each_cand(s, t, o). +hr_mreq(p, pre) :- hr_flat(_, a, p, _, _, _), hr_inc(a, _, _, pre, _). + +// (root, flat key, kind, verb, full path, call, ordinal) +.decl hr_fentry(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol, c6:number) +.decl hr_fcand(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol, c6:number, c7:symbol) +// (root, key prefix, list, key, kind, verb, list path, call, ordinal, prefix): an entry of a list at one place +// the root's flattening reaches it +.decl hr_node_entry(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol, c6:symbol, c7:symbol, c8:number, c9:symbol) +hr_node_entry(r, kp, a, k, kind, vv, p, ce, n, pre) :- hr_flat(r, a, pre, kp, "", _), hr_entry(a, k, kind, vv, p, ce, n). +hr_node_entry(r, kp, a, k, kind, vv, p, ce, n, pre) :- hr_flat(r, a, pre, kp, cut, _), cut != "", + hr_entry(a, k, kind, vv, p, ce, n), k < cut. +hr_node_entry(r, kp, a, k, kind, vv, p, ce, n, pre) :- hr_flat(r, a, pre, kp, cut, cm), cut != "", + hr_entry(a, k, kind, vv, p, ce, n), expr_module("client", em, ce), em != cm. +hr_fcand(r, fk, kind, vv, p, ce, n, pre) :- hr_node_entry(r, kp, _, k, kind, vv, p, ce, n, pre), fk = cat(kp, k). +hr_mreq(pre, p) :- hr_fcand(_, _, _, _, p, _, _, pre). +hr_fentry(r, fk, kind, vv, fp, ce, n) :- hr_fcand(r, fk, kind, vv, p, ce, n, pre), hr_merge(pre, p, fp). + +// a root nothing passed an app into: a parameter typed as the app class +// the id an app is reported under: the expression that created its route list. A list made by a call of +// a factory (each call its own list here) is reported under the factory's `new`, which is what a reader opens. +.decl hr_is_instance(c0:symbol) +hr_is_instance(ce) :- hr_inc(ce, t, ce, _, "instance"), !hr_slot(t). +.decl hr_app_id(c0:symbol, c1:symbol) +hr_app_id(a, a) :- hr_list(a), !hr_is_instance(a). +hr_app_id(ce, i) :- hr_inc(ce, t, ce, _, "instance"), !hr_slot(t), hr_app_id(t, i). +.decl hr_partial_root(c0:symbol) +hr_partial_root(r) :- hr_root(r), hr_slot(r). +hr_partial_root(r) :- hr_root(r), hr_unknown_mount(r). +hr_partial_root(r) :- hr_root(r), hr_list_entry(r). + +// http_route(Handler, Verb, FullPath, App, Order) — a route a served app answers: +// the handler function, the verb (ANY for all), the full path composed through every +// basePath and mount, the root app (the expression that created it) and the order key +// of the registration on that app (dotted, zero-padded: compares as program order). +http_route(m, vv, fp, ai, fk) :- hr_fentry(r, fk, "route", vv, fp, ce, n), hr_last(ce, n), hr_app_id(r, ai), + hr_route_node(ce, n, m), !contains("{}", fp), !hr_partial_root(r), !hr_dyn_base_call(ce). + +// route_registered_by(RegMethod, Handler, Verb, FullPath, App) — the function whose body makes +// the registering call (a registration function, an app factory, a module body): what a change +// to it adds to or removes from the served route table +route_registered_by(rm, m, vv, fp, ai) :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), + hr_route_node(ce, n, m), !contains("{}", fp), !hr_partial_root(r), !hr_dyn_base_call(ce), expr_enclosing_method(ce, rm), hr_app_id(r, ai), + hr_reg(ce, _, _). + +// a route whose path or app cannot be completed: what is known, and why +.decl hr_partial(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hr_partial(m, vv, fp, r, "dynamic_route_path") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), + hr_route_node(ce, n, m), contains("{}", fp), !hr_partial_root(r), hr_entry_own(ce, op), contains("{}", op). +hr_partial(m, vv, fp, r, "dynamic_mount_prefix") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), + hr_route_node(ce, n, m), contains("{}", fp), !hr_partial_root(r), hr_entry_own(ce, op), !contains("{}", op). +// a route a derived module list brought in (hr_inc kind mount_derived), at any depth below it +.decl hr_dreach(c0:symbol, c1:symbol) +hr_dreach(r, s) :- hr_flat(r, a, _, _, _, _), hr_inc(a, s, _, _, "mount_derived"). +hr_dreach(r, s2) :- hr_dreach(r, s), hr_inc(s, s2, _, _, _). +hr_partial(m, vv, fp, r, "unresolved_mount") :- hr_node_entry(r, _, a, _, "route", vv, p, ce, n, pre), hr_last(ce, n), + hr_route_node(ce, n, m), hr_dreach(r, a), hr_merge(pre, p, fp). +hr_partial(m, vv, fp, r, "unresolved_app_value") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), + hr_route_node(ce, n, m), hr_root(r), hr_slot(r), !hr_slot_handed(r). +// a typed app slot of a method that the program DOES hand an app to, through a call the call graph cannot +// resolve (`for (const p of plugins.filter(..)) p.register?.(app)`): the app is known, which plugins receive +// it is decided at run time, so the routes are mounted under an unknown mount (SPEC R13, derived lists) +.decl hr_slot_handed(c0:symbol) +hr_slot_handed(p) :- hr_typed_slot(p), param_decl("client", _, pos, _, w, p), hr_handed_call(ce, w), + expr_child("client", ce, "ARGUMENT", pos, x), hr_app(x, _). +// a call that reaches the method through the signature it implements (`p.register?.(app)` typed by an +// interface), or by its name when nothing resolves it +.decl hr_handed_call(c0:symbol, c1:symbol) +hr_handed_call(ce, w) :- hr_call_of(ce, sig), method_dispatch_candidate(sig, w, _). +hr_handed_call(ce, w) :- method_decl("client", n, _, _, w), hr_typed_slot(p), param_decl("client", _, _, _, w, p), + call_site("client", "METHOD_CALL", n, _, _, ce, _), !hr_call_of(ce, _). +hr_partial(m, vv, fp, r, "unresolved_mount") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), + hr_route_node(ce, n, m), hr_root(r), hr_slot(r), hr_slot_handed(r). +hr_partial(m, vv, fp, r, "unresolved_mount") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), + hr_route_node(ce, n, m), hr_root(r), hr_unknown_mount(r), !hr_slot(r). +// an app in a list entry no loop over a const array mounts (SPEC ruling R13) +hr_partial(m, vv, fp, r, "unresolved_mount") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), + hr_route_node(ce, n, m), hr_root(r), hr_list_entry(r), !hr_slot(r). +// ONE row per route and app (SPEC R8): of the reasons, the one that says most (an app or mount nothing +// resolves, then the call's own path, then the prefix), and of the known paths the shortest (the fewest +// unknown pieces), never one row per depth a run-time prefix could be nested to +.decl hr_why_rank(c0:symbol, c1:number) +hr_why_rank("unresolved_app_value", 0). hr_why_rank("unresolved_mount", 1). +hr_why_rank("dynamic_route_path", 2). hr_why_rank("dynamic_mount_prefix", 3). +// (several root lists may be reported under one app id: every call of a helper that builds an app is its own +// list, R6, and is reported under the helper's `new`; the row is per app id, so per what the reader sees) +.decl hr_partial_ai(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hr_partial_ai(m, vv, fp, ai, w) :- hr_partial(m, vv, fp, r, w), hr_app_id(r, ai). +.decl hr_pbest(c0:symbol, c1:symbol, c2:symbol, c3:number) +hr_pbest(m, vv, ai, k) :- hr_partial_ai(m, vv, _, ai, _), k = min x : { hr_partial_ai(m, vv, _, ai, w), hr_why_rank(w, x) }. +.decl hr_plen(c0:symbol, c1:symbol, c2:symbol, c3:number) +hr_plen(m, vv, ai, n) :- hr_pbest(m, vv, ai, k), hr_why_rank(w, k), + n = min l : { hr_partial_ai(m, vv, fp, ai, w), l = strlen(fp) }. +// of equally short known paths, the first in symbol order: one row +.decl hr_pfirst(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hr_pfirst(m, vv, ai, fp) :- hr_pbest(m, vv, ai, k), hr_why_rank(w, k), hr_plen(m, vv, ai, n), + hr_partial_ai(m, vv, fp, ai, w), strlen(fp) = n, !hr_pshorter(m, vv, ai, fp). +.decl hr_pcand(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hr_pcand(m, vv, ai, fp) :- hr_pbest(m, vv, ai, k), hr_why_rank(w, k), hr_plen(m, vv, ai, n), + hr_partial_ai(m, vv, fp, ai, w), strlen(fp) = n. +.decl hr_pshorter(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hr_pshorter(m, vv, ai, fp) :- hr_pcand(m, vv, ai, fp), hr_pcand(m, vv, ai, f2), f2 < fp. +.decl hr_partial1(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hr_partial1(m, vv, fp, ai, w) :- hr_pfirst(m, vv, ai, fp), hr_pbest(m, vv, ai, k), hr_why_rank(w, k). +http_route_partial(m, vv, fp, ai, why) :- hr_partial1(m, vv, fp, ai, why). +remote_undetermined(m, "http", why) :- hr_partial1(m, _, _, _, why). + +// http_mount(ParentApp, ChildApp, Prefix, Order) — `route(prefix, child)`: the child +// app's registrations so far are served under prefix (merged with the parent's base). +http_mount(a, s, pre, k) :- hr_inc(a, s, site, pre, "mount"), hr_key(site, k), !contains("{}", pre). +http_mount(a, s, pre, k) :- hr_inc(a, s, site, pre, "mount_each"), hr_key(site, k), !contains("{}", pre). +framework_undetermined(m, "mount", "unresolved_mount") :- hr_reg(ce, _, cn), router_mount_app(cn), + expr_child("client", ce, "ARGUMENT", "1", sx), !hr_app(sx, _), expr_enclosing_method(ce, m), !hv_fin_mount(ce). +framework_undetermined(m, "mount", "foreign_mount") :- hr_reg(ce, _, cn), router_mount_foreign(cn), + !hr_has_fn(ce, 100), expr_enclosing_method(ce, m). + +// a registration inside a branch or a loop is still emitted, and said to be one +.decl hr_block_up(c0:symbol, c1:symbol) +hr_block_up(ce, b) :- hr_reg(ce, _, _), expr_owner("client", "BLOCK", b, _, ce). +hr_block_up(ce, p) :- hr_block_up(ce, b), block_parent("client", p, b). +.decl hr_cond_kind(c0:symbol) +hr_cond_kind("IF"). hr_cond_kind("ELSE"). hr_cond_kind("ELSE_IF"). hr_cond_kind("SWITCH"). +hr_cond_kind("CASE"). hr_cond_kind("DEFAULT"). hr_cond_kind("CATCH"). hr_cond_kind("CONDITIONAL"). +.decl hr_loop_kind(c0:symbol) +hr_loop_kind("FOR"). hr_loop_kind("FOR_OF"). hr_loop_kind("FOR_IN"). hr_loop_kind("WHILE"). +hr_loop_kind("DO"). hr_loop_kind("DO_WHILE"). +framework_undetermined(m, "registration", "conditional_registration") :- hr_block_up(ce, b), + block_kind("client", k, b), hr_cond_kind(k), hr_last(ce, n), hr_node_fn(ce, n, m). +framework_undetermined(m, "registration", "loop_registration") :- hr_block_up(ce, b), + block_kind("client", k, b), hr_loop_kind(k), hr_last(ce, n), hr_node_fn(ce, n, m). +// an app-level middleware registered inside a branch or a loop: it may not run before the routes it would match +framework_undetermined(x, "registration", "conditional_registration") :- hr_block_up(ce, b), hr_reg(ce, _, cn), router_use(cn), + block_kind("client", k, b), hr_cond_kind(k), hr_node_fn(ce, _, x), !hr_dyn_arg_any(ce). +framework_undetermined(x, "registration", "loop_registration") :- hr_block_up(ce, b), hr_reg(ce, _, cn), router_use(cn), + block_kind("client", k, b), hr_loop_kind(k), hr_node_fn(ce, _, x), !hr_dyn_arg_any(ce). +// …a library middleware or a value has no body to name: the function that registers it says so +framework_undetermined(m, "registration", "conditional_registration") :- hr_block_up(ce, b), hr_reg(ce, _, cn), router_use(cn), + block_kind("client", k, b), hr_cond_kind(k), hr_harg(ce, n, _), !hr_has_fn(ce, n), !hr_dyn_arg_any(ce), expr_enclosing_method(ce, m). +framework_undetermined(m, "registration", "loop_registration") :- hr_block_up(ce, b), hr_reg(ce, _, cn), router_use(cn), + block_kind("client", k, b), hr_loop_kind(k), hr_harg(ce, n, _), !hr_has_fn(ce, n), !hr_dyn_arg_any(ce), expr_enclosing_method(ce, m). +// registration_conditional(RegCall, Reason) — a registering call (`use(..)`, a verb, a mount) written inside a branch +// or a loop. The rows above name the function a registration runs; a library middleware has none, so this is how a +// chain line keyed at the library call finds that the `use(..)` putting it there may not run. +registration_conditional(ce, "conditional_registration") :- hr_block_up(ce, b), block_kind("client", k, b), hr_cond_kind(k). +registration_conditional(ce, "loop_registration") :- hr_block_up(ce, b), block_kind("client", k, b), hr_loop_kind(k). +// …or a `use(..)` whose middleware is a value read at run time (a loop variable, a ternary over a config list, a spread +// no rule reads): the call is where that list enters every chain it stands in, so a question can name the line +registration_conditional(ce, "dynamic_middleware_list") :- hr_dyn_arg(ce, _). +registration_conditional(ce, "dynamic_middleware_list") :- hr_spread(ce, p, _), !hr_spread_known(ce, p). +.decl hr_dyn_arg_any(c0:symbol) +hr_dyn_arg_any(ce) :- hr_dyn_arg(ce, _). + +// a route brought in by a mount in a loop over a filtered list (hr_cond_mount), at any depth below it: served at its +// exact path when its element survives the filter, so declared conditional (SPEC ruling R13b) +.decl hr_creach(c0:symbol, c1:symbol) +hr_creach(r, s) :- hr_flat(r, a, _, _, _, _), hr_inc(a, s, site, _, _), hr_cond_mount(site). +hr_creach(r, s) :- hr_flat(r, a, _, _, _, _), hr_inc(a, s, site, _, _), hr_cond_inc(s, site). +hr_creach(r, s2) :- hr_creach(r, s), hr_inc(s, s2, _, _, _). +framework_undetermined(m, "registration", "conditional_registration") :- hr_node_entry(r, _, a, _, "route", _, _, ce, n, _), + hr_last(ce, n), hr_route_node(ce, n, m), hr_creach(r, a). + +// ── middleware application ────────────────────────────────────────────────── +.decl hr_hent(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hr_hent(r, hk, ce, vv, fp) :- hr_fentry(r, hk, "route", vv, fp, ce, n), hr_last(ce, n), + !contains("{}", fp), !hr_partial_root(r), !hr_dyn_base_call(ce). +// (root, key, call, ordinal, scope, verb): `use` entries, and a verb registration whose +// function takes `next` and calls it (the router composes every matching entry, so it +// runs before a later route it matches) +.decl hr_ment(c0:symbol, c1:symbol, c2:symbol, c3:number, c4:symbol, c5:symbol) +hr_ment(r, mk, ce, n, q, vv) :- hr_fentry(r, mk, "mw", vv, q, ce, n), !contains("{}", q), !hr_dyn_arg(ce, n). +hr_ment(r, mk, ce, n, q, vv) :- hr_fentry(r, mk, "route", vv, q, ce, n), !contains("{}", q), + hr_node_fn(ce, n, m), hr_calls_next(m). +.decl hr_calls_next(c0:symbol) +hr_calls_next(m) :- hr_node_fn(_, _, m), param_decl("client", _, "1", _, m, p), + expr_referenced("client", "PARAMETER", p, f), expr_child("client", ce, "METHOD_NAME", _, f), + call_site("client", _, _, _, _, ce, _). +.decl hr_verb_ok(c0:symbol, c1:symbol) +hr_verb_ok("ANY", v) :- hr_hent(_, _, _, v, _). +hr_verb_ok(v, v) :- hr_hent(_, _, _, v, _). +.decl hr_spair(c0:symbol, c1:symbol) +hr_spair(q, p) :- hr_ment(r, mk, _, _, q, _), hr_hent(r, hk, _, _, p), mk < hk. + +// segments of a path, empty ones skipped +.decl hr_spath(c0:symbol) +hr_spath(q) :- hr_spair(q, _). +hr_spath(p) :- hr_spair(_, p). +.decl hr_slash(c0:symbol, c1:number) +hr_slash(p, i) :- hr_spath(p), i = range(0, strlen(p)), substr(p, i, 1) = "/". +.decl hr_next_slash(c0:symbol, c1:number, c2:number) +hr_next_slash(p, i, j) :- hr_slash(p, i), j = min x : { hr_slash(p, x), x > i }. +.decl hr_has_next(c0:symbol, c1:number) +hr_has_next(p, i) :- hr_next_slash(p, i, _). +.decl hr_seg_at(c0:symbol, c1:number, c2:symbol) +hr_seg_at(p, i, t) :- hr_next_slash(p, i, j), t = substr(p, i + 1, j - i - 1). +hr_seg_at(p, i, t) :- hr_slash(p, i), !hr_has_next(p, i), t = substr(p, i + 1, strlen(p) - i - 1). +.decl hr_seg_ne(c0:symbol, c1:number) +hr_seg_ne(p, i) :- hr_seg_at(p, i, t), t != "". +.decl hr_seg(c0:symbol, c1:number, c2:symbol) +hr_seg(p, k, t) :- hr_seg_at(p, i, t), t != "", k = count : { hr_seg_ne(p, x), x < i }. +.decl hr_nseg(c0:symbol, c1:number) +hr_nseg(p, n) :- hr_spath(p), n = count : { hr_seg(p, _, _) }. +.decl hr_lit(c0:symbol) +hr_lit(t) :- hr_seg(_, _, t), substr(t, 0, 1) != ":", t != "*". + +// a scope ending in `*` matches its fixed segments and anything (or nothing) after +.decl hr_qtail(c0:symbol) +hr_qtail(q) :- hr_nseg(q, n), n >= 1, hr_seg(q, n - 1, "*"). +.decl hr_qfix(c0:symbol, c1:number) +hr_qfix(q, n - 1) :- hr_qtail(q), hr_nseg(q, n). +hr_qfix(q, n) :- hr_nseg(q, n), !hr_qtail(q). +.decl hr_len_ok(c0:symbol, c1:symbol) +hr_len_ok(q, p) :- hr_spair(q, p), hr_qtail(q), hr_qfix(q, f), hr_nseg(p, np), np >= f. +hr_len_ok(q, p) :- hr_spair(q, p), !hr_qtail(q), hr_nseg(q, n), hr_nseg(p, n). +.decl hr_conflict(c0:symbol, c1:symbol) +hr_conflict(q, p) :- hr_spair(q, p), hr_qfix(q, f), hr_seg(q, i, x), i < f, hr_seg(p, i, y), + hr_lit(x), hr_lit(y), x != y. +.decl hr_undec(c0:symbol, c1:symbol) +hr_undec(q, p) :- hr_spair(q, p), hr_qfix(q, f), hr_seg(q, i, x), i < f, hr_seg(p, i, y), + hr_lit(x), !hr_lit(y). +.decl hr_qstar(c0:symbol, c1:symbol) +hr_qstar(q, pq) :- hr_spair(q, _), strlen(q) >= 2, substr(q, strlen(q) - 1, 1) = "*", substr(q, strlen(q) - 2, 1) != "/", + pq = substr(q, 0, strlen(q) - 1). +.decl hr_scope_ok(c0:symbol, c1:symbol) +hr_scope_ok(q, p) :- hr_len_ok(q, p), !hr_conflict(q, p), !hr_undec(q, p), !hr_qstar(q, _). +hr_scope_ok(q, p) :- hr_spair(q, p), hr_qstar(q, pq), strlen(p) >= strlen(pq), substr(p, 0, strlen(pq)) = pq. + +// (root, handler key, middleware key, middleware call, ordinal) +.decl hr_applies(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +hr_applies(r, hk, mk, mce, mn) :- hr_ment(r, mk, mce, mn, q, mv), hr_hent(r, hk, _, hv, p), mk < hk, + hr_verb_ok(mv, hv), hr_scope_ok(q, p), !hr_excepted(mce, mn, p). +// the earlier arguments of the route's own call +hr_applies(r, hk, mk, ce, mn) :- hr_hent(r, hk, ce, vv, fp), hr_fentry(r, mk, "route", vv, fp, ce, mn), + hr_last(ce, n), mn < n, !hr_dyn_arg(ce, mn). +framework_undetermined(mw, "middleware_scope", "param_vs_literal_scope") :- hr_ment(r, mk, mce, mn, q, mv), + hr_hent(r, hk, _, hv, p), mk < hk, hr_verb_ok(mv, hv), hr_len_ok(q, p), !hr_conflict(q, p), hr_undec(q, p), hr_node(mce, mn, mw). +// a scoped `use` against a route under an unknown prefix cannot be decided (SPEC 3.3 [iter2]): no row, +// and the middleware says so; a `*` scope on the same app is decidable and is not declared +framework_undetermined(mw, "middleware_scope", "partial_route_scope") :- hr_fentry(r, mk, "mw", _, q, mce, mn), + q != "/*", hr_mw_own(mce, lq), !hr_star(lq), hr_fentry(r, hk, "route", _, fp, ce, n), hr_last(ce, n), contains("{}", fp), mk < hk, + hr_node(mce, mn, mw). +// middleware_may_apply(Mw, Handler, Verb, KnownPath, Scope, App) — the same undecidable pairs, one row per route: +// a scoped `use` registered before a route whose prefix is not known may or may not run before it, so the +// question about that route can say `Mw may run (scope Scope vs unknown prefix)` instead of nothing +// A partial route's chain is decided where it can be (SPEC 3.3 [iter2]): a middleware registered before it on +// the SAME list, whose scope as written there matches the route's path as written there (both sit under the +// one unknown prefix that list is mounted at), and a whole-app `*` / `/*` of the root before it; the +// route's own earlier arguments. Positions are ordinals among those. +// (root, flat key, call, verb, list path, flat full path, key prefix, list) +.decl hr_pr(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol, c6:symbol, c7:symbol) +hr_pr(r, hk, ce, vv, p, fp, kp, a) :- hr_node_entry(r, kp, a, k, "route", vv, p, ce, n, pre), hr_last(ce, n), + hk = cat(kp, k), hr_merge(pre, p, fp), (contains("{}", fp) ; hr_partial_root(r) ; hr_dyn_base_call(ce)). +.decl hr_star(c0:symbol) +hr_star("*"). hr_star("/*"). +hr_spair(q, p) :- hr_pr(r, hk, _, _, p, _, kp, a), !contains("{}", p), hr_node_entry(r, kp, a, k, "mw", _, q, _, _, _), + cat(kp, k) < hk, !contains("{}", q), !hr_star(q). +.decl hr_papplies(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +hr_papplies(r, hk, mk, mce, mn) :- hr_pr(r, hk, _, hv, p, _, kp, a), hr_node_entry(r, kp, a, k, "mw", mv, q, mce, mn, _), + mk = cat(kp, k), mk < hk, hr_verb_ok(mv, hv), !hr_dyn_arg(mce, mn), + (hr_star(q) ; !contains("{}", p), !contains("{}", q), hr_scope_ok(q, p)), !hr_excepted(mce, mn, p). +hr_papplies(r, hk, mk, mce, mn) :- hr_pr(r, hk, _, hv, _, _, _, _), hr_fentry(r, mk, "mw", mv, q, mce, mn), hr_star(q), + mk < hk, hr_verb_ok(mv, hv), !hr_dyn_arg(mce, mn). +// a `*` use on the route's own list or on any list it is mounted under (the root included) runs before it, whatever +// the unknown prefix: that prefix is the one the route's path is under too +hr_papplies(r, hk, mk, mce, mn) :- hr_pr(r, hk, _, hv, _, _, kp, _), hr_node_entry(r, kpa, _, k, "mw", mv, _, mce, mn, _), + strlen(kpa) <= strlen(kp), substr(kp, 0, strlen(kpa)) = kpa, + hr_mw_own(mce, lq), hr_star(lq), mk = cat(kpa, k), mk < hk, hr_verb_ok(mv, hv), !hr_dyn_arg(mce, mn). +hr_papplies(r, hk, mk, ce, mn) :- hr_pr(r, hk, ce, vv, p, _, kp, a), hr_node_entry(r, kp, a, k, "route", vv, p, ce, mn, _), + hr_last(ce, n), mn < n, !hr_dyn_arg(ce, mn), mk = cat(kp, k). +hr_verb_ok("ANY", v) :- hr_pr(_, _, _, v, _, _, _, _). +hr_verb_ok(v, v) :- hr_pr(_, _, _, v, _, _, _, _). +.decl hr_papplies_k(c0:symbol, c1:symbol, c2:symbol) +hr_papplies_k(r, hk, mk) :- hr_papplies(r, hk, mk, _, _). +.decl hr_prank(c0:symbol, c1:symbol, c2:symbol, c3:number) +hr_prank(r, hk, mk, pos) :- hr_papplies_k(r, hk, mk), !hr_second(r, mk), pos = count : { hr_papplies_k(r, hk, k2), k2 < mk, !hr_second(r, k2) }. +hr_prank(r, hk, mk, pos) :- hr_papplies_k(r, hk, mk), hr_rep(r, mk, k1), pos = count : { hr_papplies_k(r, hk, k2), k2 < k1, !hr_second(r, k2) }. +// on the one known path the route's partial row names +middleware_applies(mw, h, vv, fp, to_string(pos), ai) :- hr_pr(r, hk, ce, vv, _, fp, _, _), hr_last(ce, n), + hr_route_node(ce, n, h), hr_app_id(r, ai), hr_partial1(h, vv, fp, ai, _), + hr_papplies(r, hk, mk, mce, mn), hr_node(mce, mn, mw), hr_prank(r, hk, mk, pos). +// middleware_may_apply: one row per (middleware, route, app), the scope as the `use` wrote it, the route's +// known path as its partial row names it; never a middleware the chain above already decided +.decl hr_mw_own(c0:symbol, c1:symbol) +hr_mw_own(mce, lp) :- hr_entry_base(mce, _, "mw", _, lp, _). +.decl hr_may(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number, c5:symbol) +hr_may(mw, h, vv, mce, mn, ai) :- hr_fentry(r, mk, "mw", _, q, mce, mn), q != "/*", + hr_pr(r, hk, ce, vv, _, _, _, _), hr_last(ce, n), mk < hk, !hr_papplies(r, hk, mk, mce, mn), + hr_node(mce, mn, mw), hr_route_node(ce, n, h), hr_app_id(r, ai). +.decl hr_may_decided(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hr_may_decided(mw, h, vv, ai) :- middleware_applies(mw, h, vv, fp, _, ai), contains("{}", fp). +middleware_may_apply(mw, h, vv, fp, q, ai) :- hr_may(mw, h, vv, mce, _, ai), hr_partial1(h, vv, fp, ai, _), + hr_mw_own(mce, q), !hr_may_decided(mw, h, vv, ai). +.decl hr_applies_k(c0:symbol, c1:symbol, c2:symbol) +hr_applies_k(r, hk, mk) :- hr_applies(r, hk, mk, _, _). +.decl hr_rank(c0:symbol, c1:symbol, c2:symbol, c3:number) +hr_rank(r, hk, mk, pos) :- hr_applies_k(r, hk, mk), !hr_second(r, mk), pos = count : { hr_applies_k(r, hk, k2), k2 < mk, !hr_second(r, k2) }. +hr_rank(r, hk, mk, pos) :- hr_applies_k(r, hk, mk), hr_rep(r, mk, k1), pos = count : { hr_applies_k(r, hk, k2), k2 < k1, !hr_second(r, k2) }. +// ALTERNATIVES (SPEC ruling R15): `if (..) app.use(s, a) else app.use(s, b)`, one middleware per branch on the same +// list and scope, are one position of the chain, held by whichever runs (each declared conditional_registration) +.decl hr_use_block(c0:symbol, c1:symbol, c2:symbol) +hr_use_block(b, a, ce) :- hr_reg(ce, a, cn), router_use(cn), expr_owner("client", "BLOCK", b, _, ce). +.decl hr_blk(c0:symbol, c1:symbol, c2:number, c3:symbol) +hr_blk(b, k, to_number(o), p) :- hr_use_block(b, _, _), ts_block(k, o, _, _, _, _, _, _, _, _, p, _, _, _, _, _, _, _, _, _, b). +// The j-th middleware of the else branch stands at the position of the j-th of the if branch (one of the two runs); +// when the branches register different numbers (R15 clause 2), the longer branch's extra ones follow on, so what +// comes after continues after the longer branch. (call, arg, ordinal in its branch's middleware on that list) +.decl hr_bkey(c0:symbol, c1:symbol, c2:symbol, c3:number, c4:symbol) +hr_bkey(b, a, c, n, kk) :- hr_use_block(b, a, c), hr_harg(c, n, _), hr_key(c, k), s = to_string(n), + kk = cat(k, cat(",", cat(substr("00000", 0, 5 - strlen(s)), s))). +.decl hr_bord(c0:symbol, c1:symbol, c2:number, c3:symbol, c4:number) +hr_bord(b, c, n, a, j) :- hr_bkey(b, a, c, n, kk), j = count : { hr_bkey(b, a, _, _, kk2), kk2 < kk }. +.decl hr_alt(c0:symbol, c1:number, c2:symbol, c3:number) +hr_alt(c1, n1, c2, n2) :- hr_use_block(b1, a, _), hr_blk(b1, "IF", o, p), hr_use_block(b2, a, _), hr_blk(b2, "ELSE", o + 1, p), + hr_bord(b1, c1, n1, a, j), hr_bord(b2, c2, n2, a, j), hr_mw_own(c1, q), hr_mw_own(c2, q). +// (root, the else-branch entry's flat key, the if-branch entry's flat key) +.decl hr_rep(c0:symbol, c1:symbol, c2:symbol) +hr_rep(r, k2, k1) :- hr_alt(c1, n1, c2, n2), hr_node_entry(r, kp, _, kk2, "mw", _, _, c2, n2, _), hr_node_entry(r, kp, _, kk1, "mw", _, _, c1, n1, _), + k2 = cat(kp, kk2), k1 = cat(kp, kk1). +.decl hr_second(c0:symbol, c1:symbol) +hr_second(r, k) :- hr_rep(r, k, _). +// …and an if/else whose two branches register DIFFERENT NUMBERS of middleware on one list (SPEC ruling R15, second +// clause): which branch ran decides how many positions there are, so every chain position after the first middleware +// either branch put there is not known statically (dynamic_middleware_list on each route whose chain holds a later one) +.decl hr_pair_blk(c0:symbol, c1:symbol, c2:symbol) +hr_pair_blk(b1, b2, a) :- hr_use_block(b1, a, _), hr_blk(b1, "IF", o, p), + ts_block("ELSE", o2, _, _, _, _, _, _, _, _, p, _, _, _, _, _, _, _, _, _, b2), to_number(o2) = o + 1. +hr_pair_blk(b1, b2, a) :- hr_use_block(b2, a, _), hr_blk(b2, "ELSE", o, p), + ts_block("IF", o1, _, _, _, _, _, _, _, _, p, _, _, _, _, _, _, _, _, _, b1), to_number(o1) + 1 = o. +.decl hr_uneq_use(c0:symbol) +hr_uneq_use(c) :- hr_pair_blk(b1, b2, a), n1 = count : { hr_use_block(b1, a, c1), hr_harg(c1, _, _) }, + n2 = count : { hr_use_block(b2, a, c2), hr_harg(c2, _, _) }, n1 != n2, (hr_use_block(b1, a, c) ; hr_use_block(b2, a, c)). +framework_undetermined(h, "middleware_chain", "dynamic_middleware_list") :- hr_uneq_use(c), hr_applies(r, hk, mk, c, _), + hr_applies(r, hk, mk2, c2, _), !hr_uneq_use(c2), mk2 > mk, hr_hent(r, hk, ce, _, _), hr_last(ce, n), hr_route_node(ce, n, h). +// the same on a PARTIAL route's chain (a list mounted under a prefix the program does not spell): its positions are +// the ordinals among the middleware decided there, so a later one is just as dependent on which branch ran +framework_undetermined(h, "middleware_chain", "dynamic_middleware_list") :- hr_uneq_use(c), hr_papplies(r, hk, mk, c, _), + hr_papplies(r, hk, mk2, c2, _), !hr_uneq_use(c2), mk2 > mk, hr_pr(r, hk, ce, _, _, _, _, _), hr_last(ce, n), hr_route_node(ce, n, h). +// a chain holding a middleware that ANOTHER element of a loop mount brought in (one candidate's `*` use reaches the +// routes of the candidates after it): which elements run, and in what order, is decided at run time (SPEC R13b) +.decl hr_each_pfx(c0:symbol, c1:symbol) +hr_each_pfx(mk, pfx) :- hr_applies_k(_, _, mk), i = range(0, strlen(mk)), substr(mk, i, 1) = "~", pfx = substr(mk, 0, i + 6). +framework_undetermined(h, "middleware_chain", "dynamic_middleware_list") :- hr_applies_k(r, hk, mk), hr_each_pfx(mk, pfx), + (strlen(hk) < strlen(pfx) ; substr(hk, 0, strlen(pfx)) != pfx), hr_hent(r, hk, ce, _, _), hr_last(ce, n), hr_route_node(ce, n, h). + +// a spread no rule could read (dynamic_middleware_list) stands somewhere in the chain: the +// positions are the ordinals among the KNOWN elements (relative order exact), and the handler's +// chain is declared incomplete (SPEC 3.3 [iter2]) +.decl hr_dyn_key(c0:symbol, c1:symbol) +hr_dyn_key(a, k) :- hr_spread(ce, p, _), !hr_spread_known(ce, p), hr_reg(ce, a, _), hr_key(ce, k0), + s = to_string(p * 100), k = cat(k0, cat(",", cat(substr("00000", 0, 5 - strlen(s)), s))). +// a middleware argument whose value is not known here: a loop variable +// (`for (const m of config.before) app.use('*', m)`) or a variable declared without a value. It +// takes no position; the chains it may stand in are declared incomplete instead (SPEC 3.3) +.decl hr_dyn_arg(c0:symbol, c1:number) +hr_dyn_arg(ce, n) :- hr_harg(ce, n, a), !hr_has_fn(ce, n), hr_denotes_var(a, v), var_initializer("client", _, "", v). +// …or chosen at run time (`use('/*', isX(mw) ? except(mw.paths, mw.handler) : mw)` over a config list) +hr_dyn_arg(ce, n) :- hr_harg(ce, n, a), !hr_has_fn(ce, n), expr_kind("client", "TERNARY_EXPRESSION", _, a). +framework_undetermined(h, "middleware_chain", "dynamic_middleware_list") :- hr_dyn_arg(ce, n), hr_last(ce, l), n < l, + hr_route_node(ce, l, h). +hr_dyn_key(a, k) :- hr_dyn_arg(ce, n), hr_reg(ce, a, cn), router_use(cn), hr_key(ce, k0), + s = to_string(n), k = cat(k0, cat(",", cat(substr("00000", 0, 5 - strlen(s)), s))). +.decl hr_fdyn(c0:symbol, c1:symbol) +hr_fdyn(r, fk) :- hr_flat(r, a, _, kp, _, _), hr_dyn_key(a, k), fk = cat(kp, k). +// …and of a partial route, from its own list or any list it is mounted under (SPEC 3.3: an `except(..)` chosen +// per element of a config list on the app a run-time tier list is mounted under) +.decl hr_fdyn_kp(c0:symbol, c1:symbol, c2:symbol) +hr_fdyn_kp(r, kpa, fk) :- hr_flat(r, a, _, kpa, _, _), hr_dyn_key(a, k), fk = cat(kpa, k). +framework_undetermined(h, "middleware_chain", "dynamic_middleware_list") :- hr_pr(r, hk, ce, _, _, _, kp, _), hr_fdyn_kp(r, kpa, sk), + strlen(kpa) <= strlen(kp), substr(kp, 0, strlen(kpa)) = kpa, sk < hk, hr_last(ce, n), hr_route_node(ce, n, h). +// positions stay the ordinals among the known elements; the chain says it is not complete +framework_undetermined(h, "middleware_chain", "dynamic_middleware_list") :- hr_hent(r, hk, ce, _, _), hr_fdyn(r, sk), + sk < hk, hr_last(ce, n), hr_route_node(ce, n, h). + +// middleware_applies(Mw, Handler, Verb, FullPath, Position, App) — Mw runs before Handler +// on a request for Verb FullPath, at Position (0 first) in the chain of the served app App +// (one chain per app a handler is mounted under). Mw is the project function, or the +// registering argument expression for library middleware. +middleware_applies(mw, h, vv, fp, to_string(pos), ai) :- hr_hent(r, hk, ce, vv, fp), hr_last(ce, n), hr_app_id(r, ai), + hr_route_node(ce, n, h), hr_applies(r, hk, mk, mce, mn), hr_node(mce, mn, mw), hr_rank(r, hk, mk, pos). + +// middleware_registered_at(Mw, Handler, Verb, FullPath, App, RegCall) — the registering call that puts Mw in the +// chain of Handler on App: an app-level `use(..)` (or a verb registration that calls next), or the route's own +// call for its per-route middleware. What a change to that one line adds to or removes from the chain. +middleware_registered_at(mw, h, vv, fp, ai, mce) :- hr_hent(r, hk, ce, vv, fp), hr_last(ce, n), hr_app_id(r, ai), + hr_route_node(ce, n, h), hr_applies(r, hk, _, mce, mn), hr_node(mce, mn, mw). +middleware_registered_at(mw, h, vv, fp, ai, mce) :- hr_pr(r, hk, ce, vv, _, fp, _, _), hr_last(ce, n), + hr_route_node(ce, n, h), hr_app_id(r, ai), hr_partial1(h, vv, fp, ai, _), + hr_papplies(r, hk, _, mce, mn), hr_node(mce, mn, mw). + +// router_node_at(Id, File, Line, What) — where a node of the tables above is written, for the ones that are not +// call sites: an app held in a parameter or a field, a middleware value, a served default export. What a reader +// is shown in place of the id. +.decl hr_at_need(c0:symbol) +hr_at_need(x) :- http_route(_, _, _, x, _). +hr_at_need(x) :- http_route_partial(_, _, _, x, _). +hr_at_need(x) :- http_mount(x, _, _, _). +hr_at_need(x) :- http_mount(_, x, _, _). +hr_at_need(x) :- http_served_by(x, _, _). +hr_at_need(x) :- http_served_by(_, x, _). +hr_at_need(x) :- middleware_applies(x, _, _, _, _, _). +hr_at_need(x) :- middleware_registered_at(_, _, _, _, x, _). +hr_at_need(x) :- middleware_may_apply(x, _, _, _, _, _). +router_node_at(x, fp, l, "expression") :- hr_at_need(x), ts_expression(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, l, _, _, _, _, _, _, _, _, _, _, _, _, x), + expr_module("client", m, x), module_file("client", fp, m). +router_node_at(p, fp, l, cat("parameter ", n)) :- hr_at_need(p), ts_method_parameter(n, _, meth, _, _, _, _, _, _, _, l, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, p), + method_module("client", meth, mm), module_file("client", fp, mm). +router_node_at(f, fp, l, cat("field ", n)) :- hr_at_need(f), ts_field(n, _, _, _, _, _, l, _, _, _, _, _, _, _, mm, _, _, _, _, _, _, _, _, _, _, _, _, _, f), + module_file("client", fp, mm). + +// middleware_site(Mw, Kind, Callee, Specifier) — what a middleware node is: +// project_fn (a function of the project), factory_result (what a project function +// returns), library_call (the value a package's function returns; no project body). +.decl hr_mw_arg(c0:symbol, c1:number, c2:symbol) +hr_mw_arg(ce, n, a) :- hr_entry(_, _, "mw", _, _, ce, n), hr_harg(ce, n, a). +hr_mw_arg(ce, n, a) :- hr_harg(ce, n, a), hr_last(ce, l), n < l, hr_entry(_, _, "route", _, _, ce, n). +middleware_site(m, "project_fn", "", "") :- hr_mw_arg(ce, n, a), hr_node_fn(ce, n, m), + !expr_kind("client", "CALL_EXPRESSION", _, a). +middleware_site(m, "factory_result", cn, "") :- hr_mw_arg(ce, n, a), hr_node_fn(ce, n, m), + call_site("client", _, cn, _, _, a, _). +middleware_site(x, "library_call", orig, spec) :- hr_mw_arg(_, _, a), hr_libret(a, x), + expr_child("client", x, "METHOD_NAME", _, f), + expr_referenced("client", "IMPORT_BINDING", ih, f), import_binding("client", _, _, orig, _, ih), + rd_import_path(ih, spec). +// a middleware VALUE that is neither a function nor a call (`app.use(guard.middleware)`, `app.use(mw)` for a const +// holding something that is not a call): named only, keyed at the argument, never resolved (SPEC ruling R11) +.decl hr_mw_value(c0:symbol, c1:symbol, c2:symbol) +hr_mw_value(a, cat(cat(rn, "."), nm), spec) :- hr_mw_arg(ce, n, a), !hr_has_fn(ce, n), !hr_dyn_arg(ce, n), + property_access_name(a, nm), expr_child("client", a, "RECEIVER", _, r), hr_ref_name(r, rn, spec). +hr_mw_value(a, rn, spec) :- hr_mw_arg(ce, n, a), !hr_has_fn(ce, n), !hr_dyn_arg(ce, n), hr_ref_name(a, rn, spec), + !hr_has_libret(a). +.decl hr_ref_name(c0:symbol, c1:symbol, c2:symbol) +hr_ref_name(e, nm, "") :- hr_on(), expr_referenced("client", "VARIABLE", v, e), var_decl("client", nm, _, _, _, _, v). +hr_ref_name(e, local, spec) :- hr_on(), expr_referenced("client", "IMPORT_BINDING", ih, e), import_binding("client", _, local, _, _, ih), + rd_import_path(ih, spec). +middleware_site(a, "value", nm, spec) :- hr_mw_value(a, nm, spec), !call_site("client", _, _, _, _, a, _). +middleware_site(a, "library_call", orig, spec) :- hr_mw_arg(ce, n, a), !hr_has_fn(ce, n), !hr_has_libret(a), + call_site("client", _, _, _, _, a, _), expr_child("client", a, "METHOD_NAME", _, f), + expr_referenced("client", "IMPORT_BINDING", ih, f), import_binding("client", _, _, orig, _, ih), + rd_import_path(ih, spec). diff --git a/graph/typescript/engine/config-resolution/knobs.dl b/graph/typescript/engine/config-resolution/knobs.dl index c9064f726..65474bb0e 100644 --- a/graph/typescript/engine/config-resolution/knobs.dl +++ b/graph/typescript/engine/config-resolution/knobs.dl @@ -147,6 +147,14 @@ ts_remote_confidence_exact("exact"). // a placeholder on one end stood in for a literal on the other .decl ts_remote_confidence_route_shape(c0:symbol) ts_remote_confidence_route_shape("route_shape"). +// a request whose base address is not a literal (`${base}/x`, a computed host): it MAY reach the route +// (SPEC ruling R14) +.decl ts_remote_confidence_base_unknown(c0:symbol) +ts_remote_confidence_base_unknown("base_unknown"). +// a request whose HTTP method is not decidable (the init is a parameter, the method a computed value): it may reach a +// route of any verb its path fits (SPEC ruling R17); an unknown base wins over it (base_unknown) +.decl ts_remote_confidence_method_unknown(c0:symbol) +ts_remote_confidence_method_unknown("method_unknown"). // the verb that matches every other one: `app.all`, `@All`, a request whose method // is computed .decl ts_http_verb_any(c0:symbol) @@ -252,3 +260,162 @@ ts_msg_transport_member("Transport.TCP", "tcp"). // bound to one in module configuration): it joins an end of any transport .decl ts_remote_transport_message(c0:symbol) ts_remote_transport_message("message"). + +// ============================================================================ +// HTTP ROUTER APPLICATIONS (config-resolution/http-router-*.dl) +// +// An application object a program builds and registers routes, middleware and +// sub-applications on (`const app = new X(); app.get('/x', h); app.route('/p', sub)`). +// Recognised by the CLASS it is constructed from and the MODULE that class is imported +// from, never by a method name: `get`, `use`, `route`, `on` are everywhere. +// ============================================================================ + +// router_app_class(Specifier, ExportedName): `new ()` of this import is an app +.decl router_app_class(c0:symbol, c1:symbol) +router_app_class("hono", "Hono"). +router_app_class("hono/tiny", "Hono"). +router_app_class("hono/quick", "Hono"). +router_app_class("@hono/zod-openapi", "OpenAPIHono"). +// ── how a module may be named: Deno specifiers and URLs ───────────────────── +// router_spec_scheme(Prefix): `npm:hono@4`, `jsr:@hono/hono@^4/logger` name the package +// after the prefix, a version after `@` dropped; router_spec_url(Prefix, Package): a URL +// import of the package's published module (`@v3/mod.ts` is the package itself) +.decl router_spec_scheme(c0:symbol) +router_spec_scheme("npm:"). +router_spec_scheme("jsr:"). +.decl router_spec_url(c0:symbol, c1:symbol) +router_spec_url("https://deno.land/x/hono", "hono"). +router_spec_url("https://esm.sh/hono", "hono"). +// the JSR name of the package +router_app_class("@hono/hono", "Hono"). +// router_verb(Method, Verb): a route registration and the verb it serves +.decl router_verb(c0:symbol, c1:symbol) +router_verb("get", "GET"). router_verb("post", "POST"). +router_verb("put", "PUT"). router_verb("delete", "DELETE"). +router_verb("patch", "PATCH"). router_verb("options", "OPTIONS"). +router_verb("all", "ANY"). +// middleware for every verb, under a path or under all paths +.decl router_use(c0:symbol) +router_use("use"). +// `on(VERB | [VERBS], path | [paths], ...handlers)` +.decl router_on(c0:symbol) +router_on("on"). +// `route(prefix, subApp)`: the sub-app's registrations so far, under the prefix +.decl router_mount_app(c0:symbol) +router_mount_app("route"). +// `basePath(prefix)`: a new app value sharing the route list, its registrations prefixed +.decl router_base_path(c0:symbol) +router_base_path("basePath"). +// `mount(prefix, fetchHandler)`: a foreign handler for every verb under prefix/* +.decl router_mount_foreign(c0:symbol) +router_mount_foreign("mount"). +// a handler the router calls when nothing matched or something threw +.decl router_fallback(c0:symbol) +router_fallback("notFound"). +router_fallback("onError"). +// methods whose result is the app itself (so a chain stays an app value) +.decl router_returns_app(c0:symbol) +router_returns_app(n) :- router_verb(n, _). +router_returns_app(n) :- router_use(n). +router_returns_app(n) :- router_on(n). +router_returns_app(n) :- router_mount_app(n). +router_returns_app(n) :- router_mount_foreign(n). +router_returns_app(n) :- router_fallback(n). +router_returns_app("openapi"). +router_returns_app("doc"). +router_returns_app("doc31"). +// router_mw_wrapper(Specifier, Name): `name(fn)` is middleware whose body is `fn` +.decl router_mw_wrapper(c0:symbol, c1:symbol) +router_mw_wrapper("hono/factory", "createMiddleware"). +// router_mw_combinator(Specifier, Name): `name(mw, ...)` is one middleware running its +// function arguments (`every`, `some`); router_mw_except: `name(paths, mw)` runs mw +// everywhere but the listed paths +.decl router_mw_combinator(c0:symbol, c1:symbol) +router_mw_combinator("hono/combine", "every"). +router_mw_combinator("hono/combine", "some"). +.decl router_mw_except(c0:symbol, c1:symbol) +router_mw_except("hono/combine", "except"). +// router_served_fetch(Name): the property of an app that serves requests +// (`export default { fetch: app.fetch, scheduled }`: the other members are called by +// the platform) +.decl router_served_fetch(c0:symbol) +router_served_fetch("fetch"). +// router_module_glob(Name): `import.meta.(pattern | [patterns], opts)` loads every module the pattern +// matches (the bundler's module list, SPEC ruling R12) +.decl router_module_glob(c0:symbol) +router_module_glob("glob"). +// router_factory(Specifier, Name): `name({ initApp })` makes a factory; `.createApp()` +// (router_factory_app) is an app whose first registrations are initApp's (router_factory_init) +.decl router_factory(c0:symbol, c1:symbol) +router_factory("hono/factory", "createFactory"). +.decl router_factory_app(c0:symbol) +router_factory_app("createApp"). +.decl router_factory_init(c0:symbol) +router_factory_init("initApp"). +// `.createMiddleware(fn)`: the middleware is fn +.decl router_factory_mw(c0:symbol) +router_factory_mw("createMiddleware"). +// ── the clients that send to an app's routes (framework-behavior/http-rpc-client.dl) ── +// router_rpc_client: `name(base)`; router_test_client: `name(app)`; each +// property of the client is a path segment, router_rpc_index adds none, and a +// router_rpc_verb method sends; router_app_request: `app.(path, init)` sends +.decl router_rpc_client(c0:symbol, c1:symbol) +router_rpc_client("hono/client", "hc"). +.decl router_test_client(c0:symbol, c1:symbol) +router_test_client("hono/testing", "testClient"). +.decl router_rpc_index(c0:symbol) +router_rpc_index("index"). +.decl router_rpc_verb(c0:symbol, c1:symbol) +router_rpc_verb("$get", "GET"). router_rpc_verb("$post", "POST"). +router_rpc_verb("$put", "PUT"). router_rpc_verb("$delete", "DELETE"). +router_rpc_verb("$patch", "PATCH"). router_rpc_verb("$options", "OPTIONS"). +router_rpc_verb("$head", "HEAD"). +.decl router_app_request(c0:symbol) +router_app_request("request"). +// router_app_fetch: `app.(request, env)` serves a request object directly; +// router_request_class: the platform class a request object is built with +.decl router_app_fetch(c0:symbol) +router_app_fetch("fetch"). +.decl router_request_class(c0:symbol) +router_request_class("Request"). +// ── OpenAPI route definitions and schema validators ───────────────────────── +// router_openapi(Method): `app.(routeDef, handler, hook?)` registers routeDef's method +// and path; router_route_def(Specifier, Name): `name({ method, path, request, responses })` +.decl router_openapi(c0:symbol) +router_openapi("openapi"). +.decl router_route_def(c0:symbol, c1:symbol) +router_route_def("@hono/zod-openapi", "createRoute"). +// the keys of a route definition +.decl router_def_key(c0:symbol, c1:symbol) +router_def_key("method", "method"). router_def_key("path", "path"). +router_def_key("request", "request"). router_def_key("responses", "responses"). +router_def_key("body", "body"). router_def_key("content", "content"). +router_def_key("schema", "schema"). router_def_key("middleware", "middleware"). +// router_def_request_target(Key, Target): `request.` validates that target +.decl router_def_request_target(c0:symbol, c1:symbol) +router_def_request_target("params", "param"). router_def_request_target("query", "query"). +router_def_request_target("headers", "header"). router_def_request_target("cookies", "cookie"). +// router_def_body_target(ContentType, Target) +.decl router_def_body_target(c0:symbol, c1:symbol) +router_def_body_target("application/json", "json"). +router_def_body_target("multipart/form-data", "form"). +router_def_body_target("application/x-www-form-urlencoded", "form"). +// router_validator(Specifier, Name): `name(target, schema)` validates target with schema +// as route middleware; router_validator_fn: `name(target, fn)`, a function validator +.decl router_validator(c0:symbol, c1:symbol) +router_validator("@hono/zod-validator", "zValidator"). +.decl router_validator_fn(c0:symbol, c1:symbol) +router_validator_fn("hono/validator", "validator"). +// router_schema_module(Specifier): a schema library a validator's schemas are built with +.decl router_schema_module(c0:symbol) +router_schema_module("zod"). router_schema_module("zod/v4"). router_schema_module("zod/v3"). +router_schema_module("zod/mini"). router_schema_module("zod/v4-mini"). router_schema_module("zod/v4/core"). +router_schema_module("@hono/zod-openapi"). +// `c..(target)`: the validated value of target +.decl router_ctx_req(c0:symbol) +router_ctx_req("req"). +.decl router_valid_read(c0:symbol) +router_valid_read("valid"). +// router_handler_spread(Name): `...x.name(a, b)` spreads its arguments as handlers +.decl router_handler_spread(c0:symbol) +router_handler_spread("createHandlers"). diff --git a/graph/typescript/engine/framework-behavior/destinations.dl b/graph/typescript/engine/framework-behavior/destinations.dl index 35df1c87d..972f53ffc 100644 --- a/graph/typescript/engine/framework-behavior/destinations.dl +++ b/graph/typescript/engine/framework-behavior/destinations.dl @@ -81,7 +81,7 @@ rd_demand(c) :- rd_demand(e), expr_kind("client", "BINARY_EXPRESSION", _, e), ex expr_child("client", e, "RIGHT_OPERAND", _, c). rd_demand(c) :- rd_demand(e), expr_literal_type("client", "TEMPLATE", e), expr_child("client", e, "TEMPLATE_SPAN", _, c). -rd_demand(i) :- rd_demand(e), rd_denotes_var(e, v), var_initializer("client", _, i, v), i != "". +rd_demand(i) :- rd_demand(e), rd_denotes_var(e, v), var_initializer("client", _, i, v), i != "", !var_reassigned(v). rd_demand(val) :- rd_prop_value(_, val). rd_demand(c) :- rd_demand(e), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, c). @@ -105,12 +105,23 @@ rd_prop_value(e, val) :- rd_demand(e), expr_kind("client", "PROPERTY_ACCESS", _, .decl rd_is_prop(c0:symbol) .decl rd_is_transparent(c0:symbol) rd_is_str(e) :- rd_demand(e), expr_kind("client", "LITERAL", _, e), expr_literal_type("client", "STRING", e). +rd_is_str(e) :- rd_demand(e), expr_kind("client", "LITERAL", _, e), expr_literal_type("client", "NO_SUBSTITUTION_TEMPLATE", e). rd_is_tmpl(e) :- rd_demand(e), expr_literal_type("client", "TEMPLATE", e). rd_is_plus(e) :- rd_demand(e), expr_kind("client", "BINARY_EXPRESSION", _, e), expr_operator("client", "+", e). -rd_is_ref(e) :- rd_demand(e), rd_denotes_var(e, v), var_initializer("client", _, i, v), i != "". +// a `let` assigned again anywhere is not a constant: its use is a computed part `{}`, never its +// first value (`let id = ''` later set by a test step) +rd_is_ref(e) :- rd_demand(e), rd_denotes_var(e, v), var_initializer("client", _, i, v), i != "", !var_reassigned(v). rd_is_prop(e) :- rd_prop_value(e, _). rd_is_transparent(e) :- rd_demand(e), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, _). +// `this.baseUrl`, a field with an initializer that nothing assigns again +.decl rd_is_field(c0:symbol, c1:symbol) +.decl rd_field_rewritten(c0:symbol) +rd_field_rewritten(f) :- expr_kind("client", k, _, x), (k = "ASSIGNMENT_EXPRESSION" ; k = "COMPOUND_ASSIGNMENT"), + expr_child("client", x, "LEFT_OPERAND", _, l), ts_field_access_target(l, f). +rd_is_field(e, i) :- rd_demand(e), expr_kind("client", "PROPERTY_ACCESS", _, e), ts_field_access_target(e, f), + field_initializer("client", i, f), i != "", !rd_field_rewritten(f). +rd_demand(i) :- rd_is_field(_, i). .decl rd_val(c0:symbol, c1:symbol) // literalValue is the text between the quotes; an empty string has no row at all @@ -119,11 +130,47 @@ rd_val(e, "") :- rd_is_str(e), !expr_literal_value("client", _, e). rd_val(e, cat(l, r)) :- rd_is_plus(e), expr_child("client", e, "LEFT_OPERAND", _, lc), expr_child("client", e, "RIGHT_OPERAND", _, rc), rd_val(lc, l), rd_val(rc, r), strlen(l) + strlen(r) < 400. -rd_val(e, v) :- rd_demand(e), rd_denotes_var(e, x), var_initializer("client", _, i, x), i != "", rd_val(i, v). +rd_val(e, v) :- rd_demand(e), rd_denotes_var(e, x), var_initializer("client", _, i, x), i != "", !var_reassigned(x), rd_val(i, v). rd_val(e, v) :- rd_prop_value(e, val), rd_val(val, v). rd_val(e, v) :- rd_is_transparent(e), expr_child("client", e, _, _, c), rd_val(c, v). +rd_val(e, v) :- rd_is_field(e, i), rd_val(i, v). +// a call with no arguments of a function with exactly one return (`getBasePath() { return '/dashboard' }`): +// the value it returns (SPEC 3.2, G8) +.decl rd_call0(c0:symbol, c1:symbol) +rd_call0(ce, w) :- call_site("client", _, _, _, _, ce, _), + call_chain_edge(ce, _, "-", w, "client", st, _), st != "callback_registered". +rd_call0(ce, w) :- loop_elem_callee(ce, w). +.decl rd_ret_top(c0:symbol, c1:symbol) +rd_ret_top(w, e) :- rd_call0(_, w), method_returns_expr(w, e), !rd_ret_inner(w, e). +.decl rd_ret_inner(c0:symbol, c1:symbol) +rd_ret_inner(w, e) :- rd_call0(_, w), method_returns_expr(w, e), expr_child("client", p, _, _, e), method_returns_expr(w, p). +.decl rd_ret_one(c0:symbol, c1:symbol) +rd_ret_one(w, e) :- rd_ret_top(w, e), 1 = count : { rd_ret_top(w, _) }. +.decl rd_call0_one(c0:symbol, c1:symbol) +rd_call0_one(ce, e) :- rd_call0(ce, w), rd_ret_one(w, e), 1 = count : { rd_call0(ce, _) }. +rd_demand(e) :- rd_demand(ce), rd_call0_one(ce, e). +// the value ONE method returns, whatever calls it (a call reaching several methods, `g.prefix(v)` over a loop of +// classes, is then read per method by whoever knows which one runs) +rd_demand(e) :- rd_demand(ce), rd_call0(ce, w), rd_ret_one(w, e). +.decl rd_mval(c0:symbol, c1:symbol) +rd_mval(w, v) :- rd_ret_one(w, e), rd_demand(e), rd_val(e, v). +rd_val(ce, v) :- rd_call0_one(ce, e), rd_val(e, v). +.decl rd_is_call0(c0:symbol) +rd_is_call0(ce) :- rd_demand(ce), rd_call0_one(ce, _). +// a parameter of a function called from exactly one place: the value that place passes +.decl rd_param_calls(c0:symbol, c1:number) +rd_param_calls(w, n) :- param_decl("client", _, _, _, w, _), n = count : { rd_call0(_, w) }. +.decl rd_param_one(c0:symbol, c1:symbol) +rd_param_one(e, a) :- rd_demand(e), expr_referenced("client", "PARAMETER", p, e), param_decl("client", _, pos, _, w, p), + rd_param_calls(w, 1), rd_call0(ce, w), expr_child("client", ce, "ARGUMENT", pos, a). +rd_demand(a) :- rd_param_one(_, a). +rd_val(e, v) :- rd_param_one(e, a), rd_val(a, v). +.decl rd_is_param_one(c0:symbol) +rd_is_param_one(e) :- rd_param_one(e, _). +.decl rd_is_field_any(c0:symbol) +rd_is_field_any(e) :- rd_is_field(e, _). rd_val(e, "{}") :- rd_demand(e), !rd_is_str(e), !rd_is_tmpl(e), !rd_is_plus(e), !rd_is_ref(e), - !rd_is_prop(e), !rd_is_transparent(e). + !rd_is_prop(e), !rd_is_transparent(e), !rd_is_field_any(e), !rd_is_param_one(e), !rd_is_call0(e). // ── a template: its raw text, split at each `${…}` ────────────────────────── // The parser keeps the template as written, backticks included, and gives each hole @@ -176,7 +223,11 @@ rd_route_fn_arg(ce, p, m) :- rd_route_call(ce, _), cfg_named_fn_arg(ce, pos, m), .decl rd_route_last(c0:symbol, c1:number) rd_route_last(ce, p) :- rd_route_fn_arg(ce, _, _), p = max q : { rd_route_fn_arg(ce, q, _) }. rd_route_raw(m, p, verb) :- rd_route_call(ce, verb), rd_route_last(ce, k), rd_route_fn_arg(ce, k, m), - expr_child("client", ce, "ARGUMENT", "0", a), rd_val(a, p), strlen(p) >= 1, substr(p, 0, 1) = "/". + expr_child("client", ce, "ARGUMENT", "0", a), rd_val(a, p), strlen(p) >= 1, substr(p, 0, 1) = "/", !hr_reg(ce, _, _). +// a registration on an application whose route table the engine composes (http-router-routes.dl): joined on +// the FULL path a request reaches it by, never on the path written at the call (a `/health` registered on a +// `basePath('/api')` app is served at `/api/health` only) +rd_route_raw(m, p, verb) :- http_route(m, verb, p, _, _). // A handler registered under a path read from a constant is as much a root as one // under a literal: config-resolution/entry-points.dl reads the literal only. @@ -244,7 +295,22 @@ rd_fetch_verb(ce, v) :- rd_fetch_init(ce, o), !expr_kind("client", "OBJECT_LITER rd_demand(a) :- rd_fetch_call(ce), expr_child("client", ce, "ARGUMENT", "0", a). rd_send(ce, from, verb, u) :- rd_fetch_verb(ce, verb), expr_child("client", ce, "ARGUMENT", "0", a), rd_val(a, u), expr_enclosing_method(ce, from). -rd_send_site(ce, from) :- rd_fetch_call(ce), expr_enclosing_method(ce, from). +rd_send_site(ce, from) :- rd_fetch_call(ce), expr_enclosing_method(ce, from), !rd_file_fetch(ce). +// `fetch(new URL('./x.wasm', import.meta.url))` reads a file shipped beside the module: not a request at all +.decl rd_file_fetch(c0:symbol) +rd_file_fetch(ce) :- rd_fetch_call(ce), expr_child("client", ce, "ARGUMENT", "0", u), call_site("client", "CONSTRUCTOR_CALL", "URL", _, _, u, _), + expr_child("client", u, "ARGUMENT", "1", b), rd_module_url(b). +// `fetch(new URL('/x', base))`: the path under a base the program does not spell +.decl rd_fetch_url_obj(c0:symbol, c1:symbol) +rd_fetch_url_obj(ce, p) :- rd_fetch_call(ce), expr_child("client", ce, "ARGUMENT", "0", u), + call_site("client", "CONSTRUCTOR_CALL", "URL", _, _, u, _), expr_child("client", u, "ARGUMENT", "0", p), + expr_child("client", u, "ARGUMENT", "1", b), !rd_module_url(b). +// `new URL('../fonts/x.ttf', import.meta.url)` names a file next to the module, never a request to a route +.decl rd_module_url(c0:symbol) +rd_module_url(b) :- expr_kind("client", "PROPERTY_ACCESS", _, b), property_access_name(b, "url"), !expr_child("client", b, "RECEIVER", _, _). +rd_demand(p) :- rd_fetch_url_obj(_, p). +rd_send(ce, from, verb, cat("{}", pv)) :- rd_fetch_url_obj(ce, p), rd_fetch_verb(ce, verb), rd_val(p, pv), + substr(pv, 0, 1) = "/", expr_enclosing_method(ce, from). // ── .(url, …) ───────────────────────────────────────────────── // The receiver is the package's import itself, or a const its factory made: @@ -329,7 +395,13 @@ rd_pn_nonempty_at(n, i) :- rd_pn_seg_at(n, i, t), t != "". rd_pn_seg_raw(n, k, t) :- rd_pn_seg_at(n, i, t), t != "", k = count : { rd_pn_nonempty_at(n, x), x < i }. .decl rd_seg_ph(c0:symbol) rd_seg_ph(t) :- rd_pn_seg_raw(_, _, t), substr(t, 0, 1) = ":". -rd_seg_ph(t) :- rd_pn_seg_raw(_, _, t), contains("{", t). +rd_seg_ph(t) :- rd_pn_seg_raw(_, _, t), contains("{", t), !rd_mixed_lit(t, _). +// a client segment that starts with a literal and ends in a computed part (`skills${path}`): it keeps the literal, and +// fits only a segment starting with it; at the end of the path the computed part may hold more segments +.decl rd_mixed_lit(c0:symbol, c1:symbol) +rd_mixed_lit(t, l) :- rd_pn_seg_raw(n, _, t), rd_norm(s, n), rd_client_path(s), strlen(t) > 2, contains("{}", t), + c = substr(t, 0, 1), c != ":", c != "{", c != "*", i = min j : { j = range(0, strlen(t) - 1), substr(t, j, 2) = "{}" }, + l = substr(t, 0, i). rd_seg_ph("*") :- rd_pn_seg_raw(_, _, "*"). .decl rd_pn_seg(c0:symbol, c1:number, c2:symbol) rd_pn_seg(n, k, "{}") :- rd_pn_seg_raw(n, k, t), rd_seg_ph(t). @@ -373,12 +445,33 @@ rd_dest(s, d) :- rd_client_path(s), rd_ccount(s, c), c > 0, rd_dest_step(s, c, d rd_dest(p, d) :- rd_server_path(p), rd_scount(p, c), c > 0, rd_dest_step(p, c, d). rd_dest(p, "/") :- rd_server_path(p), rd_scount(p, 0). +// ── a send to a PUBLIC host is not a request to this program ─────────────── +// `https://api.example.com/health` names a host on the internet by its DNS name: another +// party's server, even when a route here happens to spell the same path. A single-label host +// (`http://billing:9000`, `localhost`), an address, or a computed host stays joinable: those +// are how services of one deployment, and tests, reach each other. +.decl rd_host(c0:symbol, c1:symbol) +rd_host(s, h) :- rd_after_scheme(s, r), rd_slash_in(r, _), m = min i : rd_slash_in(r, i), h = substr(r, 0, m). +rd_host(s, r) :- rd_after_scheme(s, r), !rd_slash_in(r, _). +.decl rd_foreign(c0:symbol) +rd_foreign(s) :- rd_client_path(s), rd_host(s, h), contains(".", h), !contains("{}", h), strlen(h) >= 1, + c = substr(h, 0, 1), c != "[", !rd_digit(c). +.decl rd_digit(c0:symbol) +rd_digit("0"). rd_digit("1"). rd_digit("2"). rd_digit("3"). rd_digit("4"). +rd_digit("5"). rd_digit("6"). rd_digit("7"). rd_digit("8"). rd_digit("9"). + // ── the join ──────────────────────────────────────────────────────────────── .decl rd_route_pair(c0:symbol, c1:symbol) -rd_route_pair(s, p) :- rd_ccount(s, c), rd_scount(p, c), rd_chas_literal(s). -rd_route_pair(s, p) :- rd_ccount(s, c), rd_scount(p, c + 1), rd_server_last_optional(p), rd_chas_literal(s). +rd_route_pair(s, p) :- rd_ccount(s, c), rd_scount(p, c), rd_chas_literal(s), !rd_foreign(s). +rd_route_pair(s, p) :- rd_ccount(s, c), rd_scount(p, c + 1), rd_server_last_optional(p), rd_chas_literal(s), !rd_foreign(s). +rd_route_pair(s, p) :- rd_ccount(s, c), rd_scount(p, c2), c2 > c, rd_ctail_mixed(s), rd_chas_literal(s), !rd_foreign(s). +.decl rd_ctail_mixed(c0:symbol) +rd_ctail_mixed(s) :- rd_ccount(s, c), rd_cseg(s, c - 1, a), rd_mixed_lit(a, _), substr(a, strlen(a) - 2, 2) = "{}". +.decl rd_mixed_fits(c0:symbol, c1:symbol) +rd_mixed_fits(a, b) :- rd_route_pair(s, p), rd_cseg(s, k, a), rd_sseg(p, k, b), rd_mixed_lit(a, l), strlen(b) >= strlen(l), + substr(b, 0, strlen(l)) = l. .decl rd_seg_conflict(c0:symbol, c1:symbol) -rd_seg_conflict(s, p) :- rd_route_pair(s, p), rd_cseg(s, k, a), rd_sseg(p, k, b), a != b, a != "{}", b != "{}". +rd_seg_conflict(s, p) :- rd_route_pair(s, p), rd_cseg(s, k, a), rd_sseg(p, k, b), a != b, a != "{}", b != "{}", !rd_mixed_fits(a, b). .decl rd_seg_literal_eq(c0:symbol, c1:symbol) rd_seg_literal_eq(s, p) :- rd_route_pair(s, p), rd_cseg(s, k, a), rd_sseg(p, k, a), a != "{}". .decl rd_seg_differs(c0:symbol, c1:symbol) @@ -401,7 +494,49 @@ rd_verb_ok(a, b) :- rd_send(_, _, a, _), rd_route_raw(_, _, b), ts_http_verb_any rd_seg_diff_n(s, p, n) :- rd_route_match(s, p, _), n = count : { rd_cseg(s, k, a), rd_sseg(p, k, b), a != b }. .decl rd_candidate(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:number) rd_candidate(e, from, to, p, conf, n) :- rd_send(e, from, sv, s), rd_route_match(s, p, conf), - rd_route_raw(to, p, rv), rd_verb_ok(sv, rv), rd_seg_diff_n(s, p, n), from != to. + rd_route_raw(to, p, rv), rd_verb_ok(sv, rv), rd_seg_diff_n(s, p, n), from != to, !rd_base_unknown_to(s, to, p), !rd_method_unknown(e). +// …whose method is not decidable: it MAY reach the route, on whatever verb (SPEC ruling R17) +rd_candidate(e, from, to, p, conf, n) :- rd_send(e, from, sv, s), rd_route_match(s, p, _), + rd_route_raw(to, p, rv), rd_verb_ok(sv, rv), rd_seg_diff_n(s, p, n), from != to, !rd_base_unknown_to(s, to, p), rd_method_unknown(e), + ts_remote_confidence_method_unknown(conf). +.decl rd_method_unknown(c0:symbol) +rd_method_unknown(ce) :- rd_fetch_has_method(ce), !rd_fetch_has_verb(ce). +rd_method_unknown(ce) :- rd_fetch_init(ce, o), !expr_kind("client", "OBJECT_LITERAL", _, o). +// a request whose base address is computed (`${base}/x`, a host from the environment) joined to a route of a +// composed route table: it MAY reach it, and says so (SPEC ruling R14) +rd_candidate(e, from, to, p, conf, n) :- rd_send(e, from, sv, s), rd_route_match(s, p, _), + rd_route_raw(to, p, rv), rd_verb_ok(sv, rv), rd_seg_diff_n(s, p, n), from != to, rd_base_unknown_to(s, to, p), + !contains("*", p), !rd_bu_test_only(e, to, p), ts_remote_confidence_base_unknown(conf). +// what a request with an unknown base may NOT be said to reach: a catch-all (`*`) route, which any path fits, and, +// from code outside the tests, a route only an app built in a test file serves (that app is never deployed) +.decl rd_test_path(c0:symbol) +rd_test_path(fp) :- module_decl("client", _, _, fp, _, _), + (contains(".test.", fp) ; contains(".spec.", fp) ; contains("__tests__/", fp) ; contains("/test/", fp) ; contains("/tests/", fp) ; + contains("/e2e/", fp) ; substr(fp, 0, 5) = "test/" ; substr(fp, 0, 6) = "tests/" ; substr(fp, 0, 4) = "e2e/"). +.decl rd_route_in_test(c0:symbol, c1:symbol) +rd_route_in_test(to, p) :- rd_base_unknown_to(_, to, p), http_route(to, _, p, a, _), expr_module("client", m, a), + module_decl("client", _, _, fp, _, m), rd_test_path(fp). +.decl rd_route_served_outside_tests(c0:symbol, c1:symbol) +rd_route_served_outside_tests(to, p) :- rd_base_unknown_to(_, to, p), http_route(to, _, p, a, _), expr_module("client", m, a), + module_decl("client", _, _, fp, _, m), !rd_test_path(fp). +.decl rd_bu_test_only(c0:symbol, c1:symbol, c2:symbol) +rd_bu_test_only(e, to, p) :- rd_send(e, _, _, s), rd_base_unknown_to(s, to, p), rd_route_in_test(to, p), + !rd_route_served_outside_tests(to, p), expr_module("client", m, e), module_decl("client", _, _, fp, _, m), !rd_test_path(fp). +.decl rd_base_unknown(c0:symbol) +rd_base_unknown(s) :- rd_client_drops_base(s). +// a computed host name (a computed port alone keeps the host: `http://localhost:${port}` is this machine) +rd_base_unknown(s) :- rd_host(s, h), contains("{}", h), !contains(":", h). +rd_base_unknown(s) :- rd_host(s, h), contains(":", h), i = min j : { j = range(0, strlen(h)), substr(h, j, 1) = ":" }, + contains("{}", substr(h, 0, i)). +// a literal SINGLE-LABEL host (`http://billing:9000`): a service of the same deployment, which app serves it is not +// known here (SPEC ruling R14a); localhost is this machine +rd_base_unknown(s) :- rd_host(s, h), rd_hostname(h, hn), hn != "", !contains("{}", hn), !contains(".", hn), hn != "localhost", + substr(hn, 0, 1) != "[". +.decl rd_hostname(c0:symbol, c1:symbol) +rd_hostname(h, substr(h, 0, i)) :- rd_host(_, h), contains(":", h), i = min j : { j = range(0, strlen(h)), substr(h, j, 1) = ":" }. +rd_hostname(h, h) :- rd_host(_, h), !contains(":", h). +.decl rd_base_unknown_to(c0:symbol, c1:symbol, c2:symbol) +rd_base_unknown_to(s, to, p) :- rd_base_unknown(s), rd_route_match(s, p, _), http_route(to, _, p, _, _). .decl rd_candidate_best(c0:symbol, c1:number) rd_candidate_best(e, b) :- rd_candidate(e, _, _, _, _, _), b = min n : rd_candidate(e, _, _, _, _, n). diff --git a/graph/typescript/engine/framework-behavior/http-router-chain.dl b/graph/typescript/engine/framework-behavior/http-router-chain.dl new file mode 100644 index 000000000..e61729d5e --- /dev/null +++ b/graph/typescript/engine/framework-behavior/http-router-chain.dl @@ -0,0 +1,74 @@ +// ============================================================================ +// FRAMEWORK-BEHAVIOR · HTTP ROUTER CHAIN (what the router does at request time) +// +// config-resolution/http-router-routes.dl says which functions a request runs and in +// what order (middleware_applies). This file adds what follows from running them: +// +// ctx_key_flow(Setter, Reader, Key) +// the request context is a keyed store: a function on the chain writes a key +// (`c.set('user', u)`) and a LATER function on the same chain reads it +// (`c.get('user')`, `c.var.user`). Neither calls the other; the key ties them. +// +// The context is the FIRST PARAMETER of a function the router calls (a handler or a +// middleware), so `c.get` on anything else (a Map, a cache) is never read here. +// A key that is not a literal or a const is declared, not guessed; a read whose key +// no earlier function on any of its chains sets is declared too: it reads nothing +// the program put there (or something a library middleware did). +// ============================================================================ + +// the context parameter of a function the router calls +.decl hb_ctx_param(c0:symbol, c1:symbol) +hb_ctx_param(m, p) :- hr_node_fn(_, _, m), param_decl("client", _, "0", _, m, p). +// an expression that is that context +.decl hb_ctx_ref(c0:symbol, c1:symbol) +hb_ctx_ref(e, m) :- hb_ctx_param(m, p), expr_referenced("client", "PARAMETER", p, e). + +// `c.set(K, v)` / `c.get(K)`: (function, call, key expression) +.decl hb_set_site(c0:symbol, c1:symbol, c2:symbol) +hb_set_site(m, ce, k) :- call_site("client", "METHOD_CALL", "set", _, r, ce, _), hb_ctx_ref(r, m), + expr_child("client", ce, "ARGUMENT", "0", k). +.decl hb_get_site(c0:symbol, c1:symbol, c2:symbol) +hb_get_site(m, ce, k) :- call_site("client", "METHOD_CALL", "get", _, r, ce, _), hb_ctx_ref(r, m), + expr_child("client", ce, "ARGUMENT", "0", k). +rd_demand(k) :- hb_set_site(_, _, k). +rd_demand(k) :- hb_get_site(_, _, k). + +// (function, key, site) +.decl hb_set_at(c0:symbol, c1:symbol, c2:symbol) +hb_set_at(m, key, ce) :- hb_set_site(m, ce, k), rd_val(k, key), !contains("{}", key). +.decl hb_get_at(c0:symbol, c1:symbol, c2:symbol) +hb_get_at(m, key, ce) :- hb_get_site(m, ce, k), rd_val(k, key), !contains("{}", key). +// `c.var.K` +hb_get_at(m, key, e) :- property_access_name(e, key), expr_child("client", e, "RECEIVER", _, v), + property_access_name(v, "var"), expr_child("client", v, "RECEIVER", _, c), hb_ctx_ref(c, m). +.decl hb_set(c0:symbol, c1:symbol) +hb_set(m, key) :- hb_set_at(m, key, _). +.decl hb_get(c0:symbol, c1:symbol) +hb_get(m, key) :- hb_get_at(m, key, _). +// source order of a site inside its function +.decl hb_site(c0:symbol) +hb_site(e) :- hb_set_at(_, _, e). +hb_site(e) :- hb_get_at(_, _, e). +.decl hb_pos(c0:symbol, c1:number, c2:number) +hb_pos(e, to_number(l), to_number(c)) :- hb_site(e), + ts_expression(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, l, c, _, _, _, _, _, _, _, _, _, _, _, e). +.decl hb_set_before_get(c0:symbol, c1:symbol) +hb_set_before_get(m, key) :- hb_set_at(m, key, s), hb_get_at(m, key, g), hb_pos(s, sl, sc), hb_pos(g, gl, gc), + (sl < gl ; sl = gl, sc < gc). +framework_undetermined(m, "ctx_key", "dynamic_ctx_key") :- hb_set_site(m, _, k), rd_val(k, key), contains("{}", key). +framework_undetermined(m, "ctx_key", "dynamic_ctx_key") :- hb_get_site(m, _, k), rd_val(k, key), contains("{}", key). + +// positions on one chain: (handler, verb, path, function, position); the handler last +.decl hb_chain(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +hb_chain(h, vv, p, mw, to_number(pos)) :- middleware_applies(mw, h, vv, p, pos, _). +hb_chain(h, vv, p, h, 100000) :- http_route(h, vv, p, _, _). + +// ctx_key_flow(Setter, Reader, Key) — on some route's chain, Setter runs before Reader +// (or is Reader) and writes the context key Reader reads. +ctx_key_flow(s, r, key) :- hb_set(s, key), hb_get(r, key), hb_chain(h, vv, p, s, i), hb_chain(h, vv, p, r, j), i < j. +ctx_key_flow(s, s, key) :- hb_set_before_get(s, key), hb_chain(_, _, _, s, _). + +framework_undetermined(r, "ctx_key", "no_setter_on_chain") :- hb_get(r, key), hb_chain(_, _, _, r, _), + !hb_read_served(r, key). +.decl hb_read_served(c0:symbol, c1:symbol) +hb_read_served(r, key) :- ctx_key_flow(_, r, key). diff --git a/graph/typescript/engine/framework-behavior/http-router-schema.dl b/graph/typescript/engine/framework-behavior/http-router-schema.dl new file mode 100644 index 000000000..e57b62dca --- /dev/null +++ b/graph/typescript/engine/framework-behavior/http-router-schema.dl @@ -0,0 +1,149 @@ +// ============================================================================ +// FRAMEWORK-BEHAVIOR · HTTP ROUTER SCHEMAS (what a route validates, and who reads it) +// +// route_schema(Handler, Target, SchemaDecl) +// the route's request part Target (json, query, param, form, header, cookie) or +// its `response` is validated by the schema declared at SchemaDecl: a variable, +// or the schema expression itself when it is written in place. A schema built from +// another (`Base.extend(..)`, `Base.merge(X)`, `z.array(Base)`, `Base.openapi('N')`) +// carries its bases too, so a change to Base reaches every route that validates +// with something derived from it. +// valid_read(ReadExpr, Handler, Target, SchemaDecl) +// `c.req.valid(Target)` in a route's handler reads what that route's validator for +// Target produced. A read with no validator for its target on the route is declared +// (framework_undetermined `valid_without_validator`). +// +// Two sources: a validator middleware argument (`zValidator('json', S)`, knob +// router_validator) and an OpenAPI route definition's `request` and `responses`. +// ============================================================================ + +// ── (route call, handler, target, schema expression) ──────────────────────── +.decl hs_validates(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hs_validates(ce, h, t, sx) :- hr_harg(ce, _, a), hr_knob_call(a, spec, n), router_validator(spec, n), + expr_child("client", a, "ARGUMENT", "0", tx), rd_val(tx, t), expr_child("client", a, "ARGUMENT", "1", sx), + hr_last(ce, l), hr_node_fn(ce, l, h). +// a function validator validates its target, with no schema +.decl hs_validates_fn(c0:symbol, c1:symbol, c2:symbol) +hs_validates_fn(ce, h, t) :- hr_harg(ce, _, a), hr_knob_call(a, spec, n), router_validator_fn(spec, n), + expr_child("client", a, "ARGUMENT", "0", tx), rd_val(tx, t), hr_last(ce, l), hr_node_fn(ce, l, h). +rd_demand(tx) :- hr_harg(_, _, a), hr_knob_call(a, spec, n), (router_validator(spec, n) ; router_validator_fn(spec, n)), + expr_child("client", a, "ARGUMENT", "0", tx). +// an OpenAPI definition: request.params / query / headers / cookies, request.body's +// content schemas, and every response's content schemas +hs_validates(ce, h, t, sx) :- hr_openapi_def(ce, o), hr_node_fn(ce, 100, h), + rd_obj_prop(o, rk, rq), router_def_key(rk, "request"), rd_obj_prop(rq, k, sx), router_def_request_target(k, t). +hs_validates(ce, h, t, sx) :- hr_openapi_def(ce, o), hr_node_fn(ce, 100, h), + rd_obj_prop(o, rk, rq), router_def_key(rk, "request"), rd_obj_prop(rq, bk, body), router_def_key(bk, "body"), + rd_obj_prop(body, ck, ct), router_def_key(ck, "content"), rd_obj_prop(ct, mime, mt), router_def_body_target(mime, t), + rd_obj_prop(mt, sk, sx), router_def_key(sk, "schema"). +// the definition's responses, one per status entry: under any key, a computed one too +// (`[Status.OK]: ..`), and the entries of a module-level object spread into it (`...errorResponses`) +.decl hs_resp_obj(c0:symbol, c1:symbol) +hs_resp_obj(o, rs) :- hr_openapi_def(_, o), rd_obj_prop(o, rk, rs), router_def_key(rk, "responses"), + expr_kind("client", "OBJECT_LITERAL", _, rs). +hs_resp_obj(o, ro) :- hr_openapi_def(_, o), rd_obj_prop(o, rk, rs), router_def_key(rk, "responses"), + hr_denotes_var(rs, v), rd_init_obj(v, ro). +hs_resp_obj(o, ro) :- hs_resp_obj(o, rs), expr_child("client", rs, _, _, sp), expr_kind("client", "SPREAD_ELEMENT", _, sp), + expr_child("client", sp, _, _, x), hr_denotes_var(x, v), rd_init_obj(v, ro). +hs_resp_obj(o, ro) :- hs_resp_obj(o, rs), expr_child("client", rs, _, _, sp), expr_kind("client", "SPREAD_ELEMENT", _, sp), + expr_child("client", sp, _, _, x), expr_kind("client", "OBJECT_LITERAL", _, x), ro = x. +.decl hs_resp(c0:symbol, c1:symbol) +hs_resp(o, r1) :- hs_resp_obj(o, rs), expr_child("client", rs, "OBJECT_PROPERTY_VALUE", _, r1). +hs_validates(ce, h, "response", sx) :- hr_openapi_def(ce, o), hr_node_fn(ce, 100, h), hs_resp(o, r1), + rd_obj_prop(r1, ck, ct), router_def_key(ck, "content"), rd_obj_prop(ct, _, mt), + rd_obj_prop(mt, sk, sx), router_def_key(sk, "schema"). +// a response or a request body built by a project helper (`200: jsonBody(User, 'ok')` returning `{ content: { +// 'application/json': { schema } } }`, a helper spreading another `{ ...jsonBody(s, d), required: true }`): the +// schema is the argument the outermost call passes for the parameter the innermost object names +.decl hs_hneed(c0:symbol) +hs_hneed(r1) :- hs_resp(_, r1). +hs_hneed(b) :- hr_openapi_def(_, o), rd_obj_prop(o, rk, rq), router_def_key(rk, "request"), rd_obj_prop(rq, bk, b), + router_def_key(bk, "body"). +hs_hneed(ro) :- hs_hneed(x), hr_call_of(x, w), hr_ret_expr(w, ro). +hs_hneed(c) :- hs_hneed(o), expr_kind("client", "OBJECT_LITERAL", _, o), expr_child("client", o, _, _, sp), + expr_kind("client", "SPREAD_ELEMENT", _, sp), expr_child("client", sp, _, _, c). +// (object or call, content type, schema expression, possibly a parameter of the helper) +.decl hs_cs(c0:symbol, c1:symbol, c2:symbol) +hs_cs(o, mime, sx) :- hs_hneed(o), rd_obj_prop(o, ck, ct), router_def_key(ck, "content"), rd_obj_prop(ct, mime, mt), + rd_obj_prop(mt, sk, sx), router_def_key(sk, "schema"). +hs_cs(o, mime, sx) :- hs_hneed(o), expr_kind("client", "OBJECT_LITERAL", _, o), expr_child("client", o, _, _, sp), + expr_kind("client", "SPREAD_ELEMENT", _, sp), expr_child("client", sp, _, _, c), hs_cs(c, mime, sx). +hs_cs(x, mime, sx) :- hs_hneed(x), hr_call_of(x, w), hr_ret_expr(w, ro), hs_cs(ro, mime, sx), !hs_param_of(sx, w, _). +hs_cs(x, mime, ax) :- hs_hneed(x), hr_call_of(x, w), hr_ret_expr(w, ro), hs_cs(ro, mime, px), hs_param_of(px, w, pos), + expr_child("client", x, "ARGUMENT", pos, ax). +.decl hs_param_of(c0:symbol, c1:symbol, c2:symbol) +hs_param_of(px, w, pos) :- hr_on(), expr_referenced("client", "PARAMETER", pp, px), param_decl("client", _, pos, _, w, pp). +.decl hs_is_param(c0:symbol) +hs_is_param(px) :- hr_on(), expr_referenced("client", "PARAMETER", _, px). +hs_validates(ce, h, "response", sx) :- hr_openapi_def(ce, o), hr_node_fn(ce, 100, h), + hs_resp(o, r1), call_site("client", _, _, _, _, r1, _), + hs_cs(r1, _, sx), !hs_is_param(sx). +hs_validates(ce, h, t, sx) :- hr_openapi_def(ce, o), hr_node_fn(ce, 100, h), + rd_obj_prop(o, rk, rq), router_def_key(rk, "request"), rd_obj_prop(rq, bk, b), router_def_key(bk, "body"), + call_site("client", _, _, _, _, b, _), hs_cs(b, mime, sx), router_def_body_target(mime, t), !hs_is_param(sx). + +// ── the declaration a schema expression names ─────────────────────────────── +// (expression, declaration): a const (here, imported, re-exported, a namespace member), +// else the expression itself +.decl hs_decl_var(c0:symbol, c1:symbol) +hs_decl_var(sx, v) :- hs_validates(_, _, _, sx), hr_denotes_var(sx, v). +hs_decl_var(sx, v) :- hs_validates(_, _, _, sx), property_access_name(sx, n), expr_child("client", sx, "RECEIVER", _, r), + expr_referenced("client", "IMPORT_BINDING", ih, r), import_binds_namespace(ih, "client", m), + module_export_any("client", m, n, "VARIABLE", v). +.decl hs_named(c0:symbol) +hs_named(sx) :- hs_decl_var(sx, _). +.decl hs_decl(c0:symbol, c1:symbol) +hs_decl(sx, v) :- hs_decl_var(sx, v). +hs_decl(sx, sx) :- hs_validates(_, _, _, sx), !hs_named(sx). + +// ── composition: a schema and every schema it is built from ───────────────── +// the expressions of a declaration's tree (a variable's initializer, or the expression) +.decl hs_root(c0:symbol, c1:symbol) +hs_root(d, i) :- hs_need(d), var_initializer("client", _, i, d), i != "". +hs_root(d, d) :- hs_need(d), expr_kind("client", _, _, d). +.decl hs_need(c0:symbol) +hs_need(d) :- hs_decl(_, d). +hs_need(b) :- hs_base(_, b). +.decl hs_desc(c0:symbol, c1:symbol) +hs_desc(d, i) :- hs_root(d, i). +hs_desc(d, c) :- hs_desc(d, e), expr_child("client", e, _, _, c). +// a reference to the schema library +.decl hs_lib_ref(c0:symbol) +hs_lib_ref(e) :- hr_on(), expr_referenced("client", "IMPORT_BINDING", ih, e), hr_import_spec(ih, spec), router_schema_module(spec). +// a variable referenced in the tree, which is itself a schema +.decl hs_base_cand(c0:symbol, c1:symbol) +hs_base_cand(d, v) :- hs_desc(d, e), hr_denotes_var(e, v), v != d. +.decl hs_base(c0:symbol, c1:symbol) +hs_base(d, v) :- hs_base_cand(d, v), hs_lib_tree(v). +// (decided without the recursion through hs_base: a candidate is a schema when its own +// tree names the library, or names a variable whose tree does, transitively) +.decl hs_lib_tree(c0:symbol) +hs_lib_tree(v) :- hs_base_cand(_, v), var_initializer("client", _, i, v), i != "", hs_tdesc(i, e), hs_lib_ref(e). +hs_lib_tree(v) :- hs_base_cand(_, v), var_initializer("client", _, i, v), i != "", hs_tdesc(i, e), + hr_denotes_var(e, w), w != v, hs_lib_tree(w). +.decl hs_tdesc(c0:symbol, c1:symbol) +hs_tdesc(i, i) :- hs_base_cand(_, v), var_initializer("client", _, i, v), i != "". +hs_tdesc(i, c) :- hs_tdesc(i, e), expr_child("client", e, _, _, c). +.decl hs_closure(c0:symbol, c1:symbol) +hs_closure(d, d) :- hs_need(d). +hs_closure(d, b) :- hs_closure(d, x), hs_base(x, b). + +// route_schema(Handler, Target, SchemaDecl) — the route's Target is validated by the +// schema declared at SchemaDecl, or by one built from it +route_schema(h, t, b) :- hs_validates(_, h, t, sx), hs_decl(sx, d), hs_closure(d, b). + +// ── `c.req.valid(target)` ─────────────────────────────────────────────────── +.decl hs_read(c0:symbol, c1:symbol, c2:symbol) +hs_read(ce, m, t) :- call_site("client", "METHOD_CALL", vn, _, q, ce, _), router_valid_read(vn), + property_access_name(q, rn), router_ctx_req(rn), expr_child("client", q, "RECEIVER", _, c), hb_ctx_ref(c, m), + expr_child("client", ce, "ARGUMENT", "0", tx), rd_val(tx, t). +rd_demand(tx) :- hr_on(), call_site("client", "METHOD_CALL", vn, _, q, ce, _), router_valid_read(vn), + property_access_name(q, rn), router_ctx_req(rn), expr_child("client", ce, "ARGUMENT", "0", tx). +// valid_read(ReadExpr, Handler, Target, SchemaDecl) — the read gets what the route's +// validator for Target produced +valid_read(ce, h, t, d) :- hs_read(ce, h, t), hs_validates(_, h, t, sx), hs_decl(sx, d). +.decl hs_read_ok(c0:symbol) +hs_read_ok(ce) :- hs_read(ce, h, t), hs_validates(_, h, t, _). +hs_read_ok(ce) :- hs_read(ce, h, t), hs_validates_fn(_, h, t). +framework_undetermined(h, "valid_read", "valid_without_validator") :- hs_read(ce, h, _), !hs_read_ok(ce), + hr_node_fn(_, _, h). diff --git a/graph/typescript/engine/framework-behavior/http-rpc-client.dl b/graph/typescript/engine/framework-behavior/http-rpc-client.dl new file mode 100644 index 000000000..bc6910cbb --- /dev/null +++ b/graph/typescript/engine/framework-behavior/http-rpc-client.dl @@ -0,0 +1,265 @@ +// ============================================================================ +// FRAMEWORK-BEHAVIOR · HTTP RPC CLIENT (a request that names a route of a known app) +// +// Three ways a program sends a request to an app whose route table this engine has +// (config-resolution/http-router-routes.dl), all in one language: +// * `app.request('/items/42', { method })` the app value itself, in a test; +// * `testClient(app).items[':id'].$get()` a client over an app value; +// * `hc(base).users[':id'].$get()` a typed client, where AppType is +// `typeof ` (directly or through a type alias, imported or not). +// Each property is a path segment (`index` adds none, `[':id']` adds the written +// segment) and `$` sends. The send is joined to the route of that app whose full +// path it fits: `exact` when every segment is equal, `route_shape` when a route +// parameter stood in for a written segment; of several, the fewest stand-ins win. +// +// remote_edge(Sender, Handler, "http", " ", Confidence) +// remote_unserved(Sender, "http", " ") a send no route of the app fits +// +// Names (the client functions, the `$verb` methods) live in knobs.dl. +// ============================================================================ + +// ── the app a client is over ──────────────────────────────────────────────── +// (client root expression, app list) +.decl hc_client_root(c0:symbol, c1:symbol) +hc_client_root(ce, a) :- hr_knob_call(ce, spec, n), router_test_client(spec, n), + expr_child("client", ce, "ARGUMENT", "0", x), hr_app(x, a). +// `hc(...)`: T is `typeof v`, or a type alias of it +.decl hc_typed_call(c0:symbol, c1:symbol) +hc_typed_call(ce, r) :- hr_knob_call(ce, spec, n), router_rpc_client(spec, n), + type_ref("client", _, "METHOD_TYPE_ARGUMENT", _, _, ce, _, r), hc_tref_pos0(r). +.decl hc_tref_pos0(c0:symbol) +hc_tref_pos0(r) :- hr_on(), ts_type_reference(_, "METHOD_TYPE_ARGUMENT", _, _, _, _, "0", _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, r). +.decl hc_type_query(c0:symbol, c1:symbol) +hc_type_query(ce, q) :- hc_typed_call(ce, q), type_ref("client", "TYPE_QUERY", _, _, _, _, _, q). +hc_type_query(ce, q) :- hc_typed_call(ce, r), type_ref_resolves("client", r, t), type_alias_target("client", q, t), + type_ref("client", "TYPE_QUERY", _, _, _, _, _, q). +// the variable a type query names: by its name in the module the query is written in +// (the IR does not link a TYPE_QUERY to its variable) +.decl hc_query_var(c0:symbol, c1:symbol) +hc_query_var(ce, v) :- hc_type_query(ce, q), type_ref("client", _, _, tn, _, _, _, q), type_ref_module("client", m, q), + var_decl("client", tn, _, _, _, m, v). +// …or imported into that module under the name (`import { todos } from './api'; hc`) +hc_query_var(ce, v) :- hc_type_query(ce, q), type_ref("client", _, _, tn, _, _, _, q), type_ref_module("client", m, q), + import_binding("client", _, tn, _, m, ih), import_binds(ih, "client", "VARIABLE", v). +hc_client_root(ce, a) :- hc_query_var(ce, v), var_initializer("client", _, i, v), i != "", hr_app(i, a). +rd_demand(p) :- hr_app(r, _), call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_app_request(cn), + expr_child("client", ce, "ARGUMENT", "0", p). + +// ── the path a client expression has accumulated ──────────────────────────── +.decl hc_cpath(c0:symbol, c1:symbol, c2:symbol) +hc_cpath(ce, a, "") :- hc_client_root(ce, a). +hc_cpath(r, a, p) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hc_cpath(i, a, p). +hc_cpath(e, a, p) :- property_access_name(e, n), router_rpc_index(n), expr_child("client", e, "RECEIVER", _, q), + hc_cpath(q, a, p). +hc_cpath(e, a, cat(p, cat("/", n))) :- property_access_name(e, n), !router_rpc_index(n), !router_rpc_verb(n, _), + expr_child("client", e, "RECEIVER", _, q), hc_cpath(q, a, p), strlen(p) < 300. +hc_cpath(e, a, cat(p, cat("/", k))) :- expr_kind("client", "ELEMENT_ACCESS", _, e), expr_child("client", e, "RECEIVER", _, q), + hc_cpath(q, a, p), expr_child("client", e, "INDEX_ARGUMENT", _, x), hc_lit_index(x), expr_literal_value("client", k, x), strlen(p) < 300. +hc_cpath(e, a, cat(p, "/{}")) :- expr_kind("client", "ELEMENT_ACCESS", _, e), expr_child("client", e, "RECEIVER", _, q), + hc_cpath(q, a, p), expr_child("client", e, "INDEX_ARGUMENT", _, x), !hc_lit_index(x), strlen(p) < 300. +.decl hc_lit_index(c0:symbol) +hc_lit_index(x) :- hr_on(), expr_child("client", _, "INDEX_ARGUMENT", _, x), (expr_literal_type("client", "STRING", x) ; expr_literal_type("client", "NO_SUBSTITUTION_TEMPLATE", x)). +hc_cpath(e, a, p) :- hc_cpath(c, a, p), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), + expr_kind_is_transparent(k). +// a function that returns a client: each call is that client +hc_cpath(ce, a, p) :- hr_call_of(ce, w), method_returns_expr(w, e), hc_cpath(e, a, p). +// A BUNDLE OF CLIENTS: an object literal whose members are clients, or functions returning one +// (`{ health: hc(..), config: (id) => hc(`../${id}/config`) }`), returned by a factory and +// kept in a variable; `bundle.health.$get()` and `bundle.config(id).identity.$put()` are sends +.decl hc_objval(c0:symbol) +hc_objval(v) :- hc_cpath(v, _, _). +hc_objval(v) :- expr_anon_decl("client", m, v), hr_ret_expr(m, r), hc_cpath(r, _, _). +.decl hc_obj(c0:symbol, c1:symbol) +hc_obj(o, o) :- rd_obj_prop(o, _, v), hc_objval(v). +hc_obj(r, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hc_obj(i, o). +hc_obj(r, o) :- hr_denotes_var(r, v), var_reassigned_value(v, i), hc_obj(i, o). +hc_obj(ce, o) :- hr_call_of(ce, w), hr_ret_expr(w, e), hc_obj(e, o). +hc_obj(e, o) :- hc_obj(c, o), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), + (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +hc_cpath(e, a, p) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, q), hc_obj(q, o), + rd_obj_prop(o, n, v), hc_cpath(v, a, p). +hc_cpath(ce, a, p) :- call_site("client", "METHOD_CALL", n, _, q, ce, _), hc_obj(q, o), rd_obj_prop(o, n, fv), + expr_anon_decl("client", m, fv), hr_ret_expr(m, r), hc_cpath(r, a, p). + +// ── the sends: (site, sender, app, verb, path) ────────────────────────────── +.decl hc_send(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hc_send(ce, from, a, vv, p) :- call_site("client", "METHOD_CALL", cn, _, q, ce, _), router_rpc_verb(cn, vv), + hc_cpath(q, a, p0), p0 != "", p = p0, expr_enclosing_method(ce, from). +hc_send(ce, from, a, vv, "/") :- call_site("client", "METHOD_CALL", cn, _, q, ce, _), router_rpc_verb(cn, vv), + hc_cpath(q, a, ""), expr_enclosing_method(ce, from). +// `app.request(path, init?)`: GET unless the init names a method +.decl hc_req(c0:symbol, c1:symbol, c2:symbol) +hc_req(ce, a, p) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_app_request(cn), hr_app(r, a), + expr_child("client", ce, "ARGUMENT", "0", x), rd_val(x, raw), strlen(raw) >= 1, substr(raw, 0, 1) = "/", + hc_strip(raw, p), !hc_preq_any(ce). +// an init the request does not write in place (a parameter, a parsed value), or one whose method is computed: the +// verb is not decidable (SPEC ruling R17), so the send stands for every verb (`*`) and joins as method_unknown +.decl hc_req_mu(c0:symbol) +hc_req_mu(ce) :- hc_req(ce, _, _), expr_child("client", ce, "ARGUMENT", "1", o), !expr_kind("client", "OBJECT_LITERAL", _, o). +hc_req_mu(ce) :- hc_req(ce, _, _), expr_child("client", ce, "ARGUMENT", "1", o), rd_obj_prop(o, key, _), ts_http_init_method_key(key), + !hc_req_has_method(ce). +// A TEST HELPER that requests the path it is handed (`async function hit(path, init?) { return app.request(path, init) }`): +// one send per call of the helper, its path and init that call's arguments. (request, helper, path position) +.decl hc_preq(c0:symbol, c1:symbol, c2:symbol) +hc_preq(ce, w, pos) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_app_request(cn), hr_app(r, _), + expr_child("client", ce, "ARGUMENT", "0", x), expr_referenced("client", "PARAMETER", pp, x), param_decl("client", _, pos, _, w, pp), + expr_enclosing_method(ce, w). +.decl hc_preq_any(c0:symbol) +hc_preq_any(ce) :- hc_preq(ce, _, _). +rd_demand(ax) :- hc_preq(_, w, pos), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, ax). +// (call of the helper, request, app, path) +.decl hc_preq_at(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hc_preq_at(oc, ce, a, p) :- hc_preq(ce, w, pos), call_site("client", _, _, _, r, ce, _), hr_app(r, a), hr_call_of(oc, w), + expr_child("client", oc, "ARGUMENT", pos, ax), rd_val(ax, raw), strlen(raw) >= 1, substr(raw, 0, 1) = "/", hc_strip(raw, p). +hc_strip_need(raw) :- hc_preq(_, w, pos), hr_call_of(oc, w), expr_child("client", oc, "ARGUMENT", pos, ax), rd_val(ax, raw), + strlen(raw) >= 1, substr(raw, 0, 1) = "/". +// the init that call hands it: none (GET), an object literal (its literal method, or GET), anything else (`*`) +.decl hc_preq_init(c0:symbol, c1:symbol, c2:symbol) +hc_preq_init(oc, ce, ia) :- hc_preq_at(oc, ce, _, _), expr_child("client", ce, "ARGUMENT", "1", x1), expr_referenced("client", "PARAMETER", ip, x1), + param_decl("client", _, ipos, _, _, ip), expr_child("client", oc, "ARGUMENT", ipos, ia). +.decl hc_preq_has_init(c0:symbol, c1:symbol) +hc_preq_has_init(oc, ce) :- hc_preq_init(oc, ce, _). +.decl hc_preq_verb(c0:symbol, c1:symbol, c2:symbol) +hc_preq_verb(oc, ce, "GET") :- hc_preq_at(oc, ce, _, _), !expr_child("client", ce, "ARGUMENT", "1", _). +hc_preq_verb(oc, ce, "GET") :- hc_preq_at(oc, ce, _, _), expr_child("client", ce, "ARGUMENT", "1", _), !hc_preq_has_init(oc, ce). +hc_preq_verb(oc, ce, vv) :- hc_preq_init(oc, ce, o), rd_obj_prop(o, key, val), ts_http_init_method_key(key), + expr_literal_value("client", w, val), ts_http_verb_name(w, vv). +hc_preq_verb(oc, ce, "GET") :- hc_preq_init(oc, ce, o), expr_kind("client", "OBJECT_LITERAL", _, o), !hc_obj_has_method_key(o). +hc_preq_verb(oc, ce, "*") :- hc_preq_init(oc, ce, o), !expr_kind("client", "OBJECT_LITERAL", _, o). +.decl hc_obj_has_method_key(c0:symbol) +hc_obj_has_method_key(o) :- hc_preq_init(_, _, o), rd_obj_prop(o, key, _), ts_http_init_method_key(key). +hc_send(oc, from, a, vv, p) :- hc_preq_at(oc, ce, a, p), hc_preq_verb(oc, ce, vv), expr_enclosing_method(ce, from). +.decl hc_req_method(c0:symbol, c1:symbol) +hc_req_method(ce, vv) :- hc_req(ce, _, _), expr_child("client", ce, "ARGUMENT", "1", o), rd_obj_prop(o, key, val), + ts_http_init_method_key(key), expr_literal_value("client", w, val), ts_http_verb_name(w, vv). +.decl hc_req_has_method(c0:symbol) +hc_req_has_method(ce) :- hc_req_method(ce, _). +hc_send(ce, from, a, vv, p) :- hc_req(ce, a, p), hc_req_method(ce, vv), expr_enclosing_method(ce, from). +hc_send(ce, from, a, "*", p) :- hc_req(ce, a, p), hc_req_mu(ce), expr_enclosing_method(ce, from). +// `app.request(new Request(url, init?))` / `app.fetch(new Request(url, init?), env)`: the +// URL's path (scheme and host dropped), the verb from the request's init +.decl hc_reqobj(c0:symbol, c1:symbol, c2:symbol) +hc_reqobj(ce, a, nq) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), (router_app_request(cn) ; router_app_fetch(cn)), + hr_app(r, a), expr_child("client", ce, "ARGUMENT", "0", nq), + call_site("client", "CONSTRUCTOR_CALL", rc, _, _, nq, _), router_request_class(rc). +rd_demand(u) :- hc_reqobj(_, _, nq), expr_child("client", nq, "ARGUMENT", "0", u). +.decl hc_reqobj_url(c0:symbol, c1:symbol) +hc_reqobj_url(ce, url) :- hc_reqobj(ce, _, nq), expr_child("client", nq, "ARGUMENT", "0", u), rd_val(u, url). +.decl hc_url_sch(c0:symbol, c1:number) +hc_url_sch(url, i) :- (hc_reqobj_url(_, url) ; hc_req_abs(_, _, url)), strlen(url) >= 3, i = range(0, strlen(url) - 2), substr(url, i, 3) = "://". +.decl hc_url_after(c0:symbol, c1:symbol) +hc_url_after(url, r) :- hc_url_sch(url, _), i = min x : { hc_url_sch(url, x) }, r = substr(url, i + 3, strlen(url) - i - 3). +.decl hc_url_slash(c0:symbol, c1:number) +hc_url_slash(r, i) :- hc_url_after(_, r), i = range(0, strlen(r)), substr(r, i, 1) = "/". +.decl hc_url_path(c0:symbol, c1:symbol) +hc_url_full(url, p) :- hc_url_after(url, r), m = min x : { hc_url_slash(r, x) }, p = substr(r, m, strlen(r) - m). +hc_url_full(url, "/") :- hc_url_after(url, r), !hc_url_slash(r, _). +hc_url_full(url, url) :- hc_reqobj_url(_, url), strlen(url) >= 1, substr(url, 0, 1) = "/". +hc_url_path(url, p) :- hc_url_full(url, raw), hc_strip(raw, p). +// a request's query string and fragment are not part of the path (SPEC ruling R9): `/x?path=…` +// and `/x#top` are requests for `/x` +.decl hc_url_full(c0:symbol, c1:symbol) +.decl hc_strip_need(c0:symbol) +hc_strip_need(raw) :- hc_url_full(_, raw). +hc_strip_need(raw) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_app_request(cn), hr_app(r, _), + expr_child("client", ce, "ARGUMENT", "0", x), rd_val(x, raw), strlen(raw) >= 1, substr(raw, 0, 1) = "/". +.decl hc_cut(c0:symbol, c1:number) +hc_cut(raw, i) :- hc_strip_need(raw), i = range(0, strlen(raw)), (substr(raw, i, 1) = "?" ; substr(raw, i, 1) = "#"). +.decl hc_strip(c0:symbol, c1:symbol) +hc_strip(raw, raw) :- hc_strip_need(raw), !hc_cut(raw, _). +hc_strip(raw, p) :- hc_strip_need(raw), m = min i : { hc_cut(raw, i) }, p0 = substr(raw, 0, m), + ((p0 = "", p = "/") ; (p0 != "", p = p0)). + +.decl hc_reqobj_method(c0:symbol, c1:symbol) +hc_reqobj_method(ce, vv) :- hc_reqobj(ce, _, nq), expr_child("client", nq, "ARGUMENT", "1", o), rd_obj_prop(o, key, val), + ts_http_init_method_key(key), expr_literal_value("client", w, val), ts_http_verb_name(w, vv). +.decl hc_reqobj_has_method(c0:symbol) +hc_reqobj_has_method(ce) :- hc_reqobj_method(ce, _). +hc_send(ce, from, a, vv, p) :- hc_reqobj(ce, a, _), hc_reqobj_url(ce, url), hc_url_path(url, p), !contains("{}", p), + hc_reqobj_method(ce, vv), expr_enclosing_method(ce, from). +hc_send(ce, from, a, "GET", p) :- hc_reqobj(ce, a, _), hc_reqobj_url(ce, url), hc_url_path(url, p), !contains("{}", p), + !hc_reqobj_has_method(ce), expr_enclosing_method(ce, from). +hc_send(ce, from, a, "GET", p) :- hc_req(ce, a, p), !hc_req_has_method(ce), !hc_req_mu(ce), expr_enclosing_method(ce, from). +// `app.request('http://localhost/x')`: the URL's path +.decl hc_req_abs(c0:symbol, c1:symbol, c2:symbol) +hc_req_abs(ce, a, url) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_app_request(cn), hr_app(r, a), + expr_child("client", ce, "ARGUMENT", "0", x), rd_val(x, url), contains("://", url). +hc_req(ce, a, p) :- hc_req_abs(ce, a, url), hc_url_path(url, p). + +// ── the routes a send may reach: those of the app's root, under its prefix ── +// (app, verb, full path relative to the app, handler) +.decl hc_app_route(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +// The app a send names is flattened AS IF it were served on its own: a request sent to a +// sub-app (`subApp.request('/posts')`, `hc`) is answered by its routes relative +// to it, not by the mounting app's full paths. Same flattening as http-router-routes.dl. +.decl hc_target(c0:symbol) +hc_target(a) :- hc_send(_, _, a, _, _). +.decl hc_flat(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:symbol) +hc_flat(a, a, "/", "", "", "") :- hc_target(a). +hc_flat(r, s, np, nk, cut, cm) :- hc_flat(r, a, p, kp, _, _), hr_inc(a, s, site, pre, kd), kd != "mount_each", + hr_merge(p, pre, np), hr_key(site, k), nk = cat(kp, cat(k, ".")), strlen(nk) < 160, hr_cutoff(site, cut, cm). +hc_flat(r, s, np, nk, "", "") :- hc_flat(r, a, p, kp, _, _), hr_inc(a, s, site, pre, "mount_each"), hr_each_ord(site, s, o), + hr_merge(p, pre, np), hr_key(site, k), nk = cat(kp, cat(k, cat("~", cat(o, ".")))), strlen(nk) < 160. +hr_mreq(p, pre) :- hc_flat(_, a, p, _, _, _), hr_inc(a, _, _, pre, _). +.decl hc_fcand(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number, c5:symbol) +hc_fcand(r, vv, p, ce, n, pre) :- hc_flat(r, a, pre, _, "", _), hr_entry(a, _, "route", vv, p, ce, n). +hc_fcand(r, vv, p, ce, n, pre) :- hc_flat(r, a, pre, _, cut, _), cut != "", hr_entry(a, k, "route", vv, p, ce, n), k < cut. +hc_fcand(r, vv, p, ce, n, pre) :- hc_flat(r, a, pre, _, cut, cm), cut != "", hr_entry(a, _, "route", vv, p, ce, n), + expr_module("client", em, ce), em != cm. +hr_mreq(pre, p) :- hc_fcand(_, _, p, _, _, pre). +hc_app_route(a, vv, fp, h) :- hc_fcand(a, vv, p, ce, n, pre), hr_last(ce, n), hr_route_node(ce, n, h), + hr_merge(pre, p, fp), !contains("{}", fp). + +// segments of both ends +.decl hc_path(c0:symbol) +hc_path(p) :- hc_send(_, _, _, _, p). +hc_path(p) :- hc_app_route(_, _, p, _). +.decl hc_slash(c0:symbol, c1:number) +hc_slash(p, i) :- hc_path(p), i = range(0, strlen(p)), substr(p, i, 1) = "/". +.decl hc_next(c0:symbol, c1:number, c2:number) +hc_next(p, i, j) :- hc_slash(p, i), j = min x : { hc_slash(p, x), x > i }. +.decl hc_has_next(c0:symbol, c1:number) +hc_has_next(p, i) :- hc_next(p, i, _). +.decl hc_seg_at(c0:symbol, c1:number, c2:symbol) +hc_seg_at(p, i, t) :- hc_next(p, i, j), t = substr(p, i + 1, j - i - 1). +hc_seg_at(p, i, t) :- hc_slash(p, i), !hc_has_next(p, i), t = substr(p, i + 1, strlen(p) - i - 1). +.decl hc_seg_ne(c0:symbol, c1:number) +hc_seg_ne(p, i) :- hc_seg_at(p, i, t), t != "". +.decl hc_seg(c0:symbol, c1:number, c2:symbol) +hc_seg(p, k, t) :- hc_seg_at(p, i, t), t != "", k = count : { hc_seg_ne(p, x), x < i }. +.decl hc_nseg(c0:symbol, c1:number) +hc_nseg(p, n) :- hc_path(p), n = count : { hc_seg(p, _, _) }. +.decl hc_param(c0:symbol) +hc_param(t) :- hc_seg(_, _, t), substr(t, 0, 1) = ":". +hc_param("*"). +// a computed segment of a send (`${id}`) is one segment of anything +.decl hc_hole(c0:symbol) +hc_hole(x) :- hc_seg(_, _, x), contains("{}", x). + +// a send fits a route of the same app: same segment count, each written segment equal or +// under a route parameter; the verb equal, or the route's ANY +.decl hc_pair(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hc_pair(ce, h, p, rp) :- hc_send(ce, _, a, sv, p), hc_app_route(a, rv, rp, h), (rv = sv ; rv = "ANY" ; sv = "*"), + hc_nseg(p, n), hc_nseg(rp, n). +.decl hc_conflict(c0:symbol, c1:symbol) +hc_conflict(p, rp) :- hc_pair(_, _, p, rp), hc_seg(p, k, x), hc_seg(rp, k, y), x != y, !hc_param(y), !hc_hole(x). +.decl hc_ndiff(c0:symbol, c1:symbol, c2:number) +hc_ndiff(p, rp, n) :- hc_pair(_, _, p, rp), !hc_conflict(p, rp), n = count : { hc_seg(p, k, x), hc_seg(rp, k, y), x != y }. +.decl hc_best(c0:symbol, c1:number) +hc_best(ce, b) :- hc_pair(ce, _, p, _), hc_ndiff(p, _, _), b = min n : { hc_pair(ce, _, p2, rp2), hc_ndiff(p2, rp2, n) }. + +remote_edge(from, h, "http", cat(sv, cat(" ", p)), "exact") :- hc_send(ce, from, _, sv, p), sv != "*", hc_pair(ce, h, p, rp), + hc_ndiff(p, rp, 0), hc_best(ce, 0), from != h. +remote_edge(from, h, "http", cat(sv, cat(" ", p)), "route_shape") :- hc_send(ce, from, _, sv, p), sv != "*", hc_pair(ce, h, p, rp), + hc_ndiff(p, rp, n), n > 0, hc_best(ce, n), from != h. +// a send with a computed segment can be any request of its shape: every route it fits +remote_edge(from, h, "http", cat(sv, cat(" ", p)), "route_shape") :- hc_send(ce, from, _, sv, p), sv != "*", contains("{}", p), + hc_pair(ce, h, p, rp), hc_ndiff(p, rp, n), n > 0, from != h. +// a send whose verb is not decidable: every route of any verb its path fits best (SPEC ruling R17) +remote_edge(from, h, "http", cat("* ", p), conf) :- hc_send(ce, from, _, "*", p), hc_pair(ce, h, p, rp), + hc_ndiff(p, rp, n), (hc_best(ce, n) ; contains("{}", p)), from != h, ts_remote_confidence_method_unknown(conf). +.decl hc_served(c0:symbol) +hc_served(ce) :- hc_best(ce, _). +remote_unserved(from, "http", cat(sv, cat(" ", p))) :- hc_send(ce, from, _, sv, p), !contains("{}", p), !hc_served(ce). +// a send with a computed segment that no route fits: where it goes is not known here +remote_undetermined(from, "http", "dynamic_rpc_path") :- hc_send(ce, from, _, _, p), contains("{}", p), !hc_served(ce). diff --git a/graph/typescript/engine/resolution/dynamic-import.dl b/graph/typescript/engine/resolution/dynamic-import.dl new file mode 100644 index 000000000..a8aaf30d2 --- /dev/null +++ b/graph/typescript/engine/resolution/dynamic-import.dl @@ -0,0 +1,61 @@ +// ============================================================================ +// RESOLUTION · DYNAMIC IMPORTS (what `await import('./m')` loads) +// +// `const { x } = await import('./m.ts')` binds x to module m's export x, exactly as +// `import { x } from './m.ts'` would. Test suites write it this way so a mock is in +// place before the module runs; nothing else in the engine read it, so every value the +// test took from the module (an app, a factory, a handler) had no origin. +// +// dynamic_import_module(Call, Module) the module an import() call loads +// var_dynamic_import_binding(Var, Kind, Entity) a destructured name and the export it takes +// +// The specifier must be a string literal. A relative one is joined to the importing +// file's directory (`./`, any number of `../`) and tried as written and with each source +// extension; a bare one resolves wherever a static import of the same specifier does. +// ============================================================================ + +.decl dyn_import_spec(c0:symbol, c1:symbol, c2:symbol) +dyn_import_spec(ce, spec, p) :- call_site("client", "DYNAMIC_IMPORT_CALL", _, _, _, ce, _), + expr_child("client", ce, "ARGUMENT", "0", a), expr_literal_value("client", spec, a), spec != "", + expr_module("client", mod, ce), module_file("client", p, mod). + +// the directory a path is in, with its trailing `/` ("" at the root) +.decl dyn_dir_of(c0:symbol, c1:symbol) +.decl dyn_dir_need(c0:symbol) +dyn_dir_need(p) :- dyn_import_spec(_, _, p). +.decl dyn_slash(c0:symbol, c1:number) +dyn_slash(p, i) :- dyn_dir_need(p), i = range(0, strlen(p)), substr(p, i, 1) = "/". +dyn_dir_of(p, d) :- dyn_dir_need(p), dyn_slash(p, _), i = max x : { dyn_slash(p, x) }, d = substr(p, 0, i + 1). +dyn_dir_of(p, "") :- dyn_dir_need(p), !dyn_slash(p, _). + +// (call, directory, rest of the specifier): `./` stays, each `../` climbs one directory +.decl dyn_rel(c0:symbol, c1:symbol, c2:symbol) +dyn_rel(ce, d, spec) :- dyn_import_spec(ce, spec, p), substr(spec, 0, 1) = ".", dyn_dir_of(p, d). +dyn_rel(ce, d, r) :- dyn_rel(ce, d, s), strlen(s) > 2, substr(s, 0, 2) = "./", r = substr(s, 2, strlen(s) - 2). +dyn_rel(ce, up, r) :- dyn_rel(ce, d, s), strlen(s) > 3, substr(s, 0, 3) = "../", dyn_parent(d, up), + r = substr(s, 3, strlen(s) - 3). +.decl dyn_parent_need(c0:symbol) +dyn_parent_need(d) :- dyn_rel(_, d, s), strlen(s) > 3, substr(s, 0, 3) = "../", d != "". +// (no aggregate here: the climb is recursive, so the parent is the prefix up to the slash that +// has no other slash after it) +.decl dyn_parent(c0:symbol, c1:symbol) +dyn_parent(d, up) :- dyn_parent_need(d), i = range(0, strlen(d) - 1), substr(d, i, 1) = "/", + !contains("/", substr(d, i + 1, strlen(d) - i - 2)), up = substr(d, 0, i + 1). +dyn_parent(d, "") :- dyn_parent_need(d), !contains("/", substr(d, 0, strlen(d) - 1)). + +.decl dynamic_import_module(c0:symbol, c1:symbol) +dynamic_import_module(ce, m) :- dyn_rel(ce, d, r), substr(r, 0, 1) != ".", module_file("client", cat(d, r), m). +dynamic_import_module(ce, m) :- dyn_rel(ce, d, r), substr(r, 0, 1) != ".", ts_source_extension(ext), + module_file("client", cat(cat(d, r), ext), m). +dynamic_import_module(ce, m) :- dyn_import_spec(ce, spec, _), substr(spec, 0, 1) != ".", + specifier_global_target(spec, "client", m). + +// the value an initializer takes from an import() call: the call itself, or its awaited result +.decl dyn_import_value(c0:symbol, c1:symbol) +dyn_import_value(ce, ce) :- dynamic_import_module(ce, _). +dyn_import_value(e, ce) :- dynamic_import_module(ce, _), expr_child("client", e, _, _, ce), + expr_kind("client", "AWAIT_EXPRESSION", _, e). + +.decl var_dynamic_import_binding(c0:symbol, c1:symbol, c2:symbol) +var_dynamic_import_binding(v, k, ent) :- var_binding("client", _, "PROPERTY", src, e, v), e != "", + dyn_import_value(e, ce), dynamic_import_module(ce, m), module_export_any("client", m, src, k, ent). diff --git a/graph/typescript/engine/resolution/value-flow.dl b/graph/typescript/engine/resolution/value-flow.dl index acc74483b..446df8bf2 100644 --- a/graph/typescript/engine/resolution/value-flow.dl +++ b/graph/typescript/engine/resolution/value-flow.dl @@ -839,3 +839,12 @@ array_method_element_param("reduceRight", "1"). // `h[i](x)`, `h.run(x)` call_runs(ce, m) :- call_callee_expr(_, ce, ne), elem_read_fn(ne, m). + +// loop_elem_callee(Call, Method) — `for (const g of groups) g.prefix(v)` where `groups` is a const array literal of typed +// elements (`[new UserRoutes()]`): the method of each element's class. A value fact the call graph does not draw +// (the loop variable's declared type is the elements' union, which it leaves ambiguous); read by the string +// evaluation only. +loop_elem_callee(ce, w) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), expr_referenced("client", "VARIABLE", v, r), + for_binding_iterable(v, it), expr_referenced("client", "VARIABLE", d, it), var_initializer("client", _, arr, d), + expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, _, el), expr_type(el, _, tt), method_in_scope(tt, cn, _, w), + !call_chain_edge(ce, _, "-", _, "client", _, _). diff --git a/graph/typescript/souffle/decls_all.dl b/graph/typescript/souffle/decls_all.dl index be835f2c9..cd8d54e95 100644 --- a/graph/typescript/souffle/decls_all.dl +++ b/graph/typescript/souffle/decls_all.dl @@ -371,6 +371,7 @@ .decl method_has_rest(c0:symbol) .decl method_has_unknown_call(c0:symbol,c1:symbol) .decl method_in_scope(c0:symbol,c1:symbol,c2:symbol,c3:symbol) +.decl loop_elem_callee(c0:symbol,c1:symbol) .decl method_is_client_body(c0:symbol) .decl method_kind(c0:symbol,c1:symbol,c2:symbol,c3:symbol) .decl method_kind_is_call_signature(c0:symbol) @@ -781,3 +782,18 @@ .decl sat_source_method(c0:symbol,c1:symbol,c2:symbol) .decl sat_target_method(c0:symbol,c1:symbol,c2:symbol) .decl sat_visible_source_method(c0:symbol,c1:symbol) +.decl http_route(c0:symbol,c1:symbol,c2:symbol,c3:symbol,c4:symbol) +.decl http_route_partial(c0:symbol,c1:symbol,c2:symbol,c3:symbol,c4:symbol) +.decl http_mount(c0:symbol,c1:symbol,c2:symbol,c3:symbol) +.decl middleware_applies(c0:symbol,c1:symbol,c2:symbol,c3:symbol,c4:symbol,c5:symbol) +.decl middleware_may_apply(c0:symbol,c1:symbol,c2:symbol,c3:symbol,c4:symbol,c5:symbol) +.decl middleware_site(c0:symbol,c1:symbol,c2:symbol,c3:symbol) +.decl middleware_registered_at(c0:symbol,c1:symbol,c2:symbol,c3:symbol,c4:symbol,c5:symbol) +.decl http_served_by(c0:symbol,c1:symbol,c2:symbol) +.decl router_node_at(c0:symbol,c1:symbol,c2:symbol,c3:symbol) +.decl registration_conditional(c0:symbol,c1:symbol) +.decl framework_undetermined(c0:symbol,c1:symbol,c2:symbol) +.decl ctx_key_flow(c0:symbol,c1:symbol,c2:symbol) +.decl route_registered_by(c0:symbol,c1:symbol,c2:symbol,c3:symbol,c4:symbol) +.decl route_schema(c0:symbol,c1:symbol,c2:symbol) +.decl valid_read(c0:symbol,c1:symbol,c2:symbol,c3:symbol) diff --git a/graph/typescript/souffle/export_manifest.tsv b/graph/typescript/souffle/export_manifest.tsv index f9e9ca385..0c951316c 100644 --- a/graph/typescript/souffle/export_manifest.tsv +++ b/graph/typescript/souffle/export_manifest.tsv @@ -56,3 +56,18 @@ remote_unserved remote-unserved.csv remote_unsent remote-unsent.csv remote_undetermined remote-undetermined.csv dispatch_assumes_closed_world assumption-dispatch-closed-world.csv +http_route http-route.csv +http_route_partial http-route-partial.csv +http_mount http-mount.csv +middleware_applies middleware-applies.csv +middleware_may_apply middleware-may-apply.csv +middleware_site middleware-site.csv +middleware_registered_at middleware-registered-at.csv +http_served_by http-served-by.csv +router_node_at router-node-at.csv +registration_conditional registration-conditional.csv +framework_undetermined framework-undetermined.csv +ctx_key_flow ctx-key-flow.csv +route_schema route-schema.csv +valid_read valid-read.csv +route_registered_by route-registered-by.csv diff --git a/plugins/axiomcode/skills/axiomcode/scripts/ax_blocks.py b/plugins/axiomcode/skills/axiomcode/scripts/ax_blocks.py index c20053e78..19427acab 100644 --- a/plugins/axiomcode/skills/axiomcode/scripts/ax_blocks.py +++ b/plugins/axiomcode/skills/axiomcode/scripts/ax_blocks.py @@ -192,7 +192,7 @@ def is_lead(p): f"(confirmed callers; no code shown): " + ', '.join(refs[:8]) + (f" +{len(refs) - 8}" if len(refs) > 8 else '')) other = grep_others(repo, names, {(p['f'], n) for p in plain for n in p['marks']}) if other: out.append(f" {other} other line(s) grep matches for that name are NOT this declaration (another symbol of the same name, or text)") - if not out: return None + if not out and not doc.get('router'): return None if len(places) > CAP: out.append(f"… {len(places) - CAP} more place(s) not shown — ask a narrower question to see them") # a bound: line is the answer saying where it stops being complete — an agent writing "only X reaches this" # from these places needs it as much as the verified: line, so it is never tidied away here. @@ -200,7 +200,10 @@ def is_lead(p): kept = [x for x in foot if x.startswith(('verified', 'bound:'))][:3] out += kept + unknown(doc, repo) + [x for x in foot if x.startswith('run:')][:1] # what the question resolved to goes first (ax_grep.head): the places are a page of the answer, not all of it - return ax_grep.head({'tests': 'test-impact'}.get(verb, verb), doc) + out + # an HTTP router's answer (impact's `router` lines: the routes a function registers, the apps it creates, the + # chain before a handler, the context keys a middleware sets): the route table speaks before the places + rt = [str(x) for x in (doc.get('router') or [])] + return ax_grep.head({'tests': 'test-impact'}.get(verb, verb), doc) + rt + out UNKNOWN_SHOWN = 5 # unresolved sites listed under an answer: the nearest; the verbs' --json carries up to 30 diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context index 160637dc7..8b8419150 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context @@ -358,6 +358,351 @@ def _route_eq(a, b): return len(a) == len(b) and all(x == y or '*' in (x, y) for x, y in zip(a, b)) +VERB_ROUTE = re.compile(r'(?i)\b(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD|ANY|ALL)\s+(/[^\s"\'`,;)]*|\*(?=[\s"\'`,;)]|$))') + + +def node_at(g, x): + """(file, line, what) of a router node that is not a call site (an app held in a parameter or a field, a served + default export), from ext_router_node_at; None when the engine wrote no row.""" + if not g.has('ext_router_node_at'): return None + try: r = g.q("SELECT c1, c2, c3 FROM ext_router_node_at WHERE c0 = ?", x) + except Exception: r = [] + if not r: return None + f, l, what = r[0] + try: p = g.q("SELECT rel FROM paths WHERE raw = ?", f) + except Exception: p = [] + return ((p[0][0] if p and p[0][0] else f), l, what) + + +def route_table_lines(g, text, cap=8): + """The ROUTE TABLE's answer to a quoted route, printed before any ranked code (SPEC 8.1 (a)/(b)): one line per + route the engine composed (ext_http_route) or could only partly compose (ext_http_route_partial), then the chain + the router runs for it on that app, in order (ext_middleware_applies). Nothing here is a text match: a line is + printed only for a row the engine wrote.""" + if not g.has('ext_http_route'): return [] + asked = [] + for m in VERB_ROUTE.finditer(text or ''): + asked.append((m.group(1).upper(), _route_parts(m.group(2)))) + if not asked: + for m in ROUTE.finditer(text or ''): + tok = m.group(1) + if tok.startswith('/') and not FILE_EXT.search(tok.rstrip('/')): asked.append((None, _route_parts(tok))) + if not asked: return [] + def vok(want, v): return want is None or v == want or v == 'ANY' or (want in ('ANY', 'ALL')) + def loc(x): return g.loc(x) if x in g.sym else None + def name(x): + if x in g.sym: return f"{g.disp(x)} {g.loc(x) or ''}".rstrip() + try: + cs = g.q("SELECT c2, c3 FROM ext_middleware_site WHERE c0 = ?", x) if g.has('ext_middleware_site') else [] + except Exception: + cs = [] + try: vk = g.q("SELECT c2 FROM ext_middleware_site WHERE c0 = ? AND c1 = 'value'", x) if g.has('ext_middleware_site') else [] + except Exception: vk = [] + if vk and vk[0][0]: return f"{vk[0][0]} (value)" + if cs and cs[0][0]: return f"{cs[0][0]}() from {cs[0][1]} (library middleware)" + try: st = g.q("SELECT callee_name, file, start_line FROM sites WHERE id = ?", x) + except Exception: st = [] + if st: return f"{st[0][0]}() at {st[0][1]}:{st[0][2]} (library middleware)" + n = node_at(g, x) + if n: return f"a middleware value at {n[0]}:{n[1]}" + return 'a middleware value the engine could not name' + # a catch-all asked as `GET *` is the route written `*` (or `/*`), not every one-segment route + def fits(w, a, path): + if a == ['*']: return path.strip() in ('*', '/*') + return _route_eq(_route_parts(path), a) + rows = [r for r in g.q("SELECT c0, c1, c2, c3, c4 FROM ext_http_route") + if any(vok(w, r[1]) and fits(w, a, r[2]) for w, a in asked)] + out = [] + roots = {r[3] for r in rows} + undet, cond = set(), set() + if g.has('ext_framework_undetermined'): + for r in g.q("SELECT c0, c1, c2 FROM ext_framework_undetermined"): + if r[1] == 'middleware_chain': undet.add(r[0]) + if r[2] in ('conditional_registration', 'loop_registration'): cond.add((r[0], r[2])) + def app_name(a): + try: r = g.q("SELECT file, start_line FROM sites WHERE id = ?", a) + except Exception: r = [] + if r: return f"{r[0][0]}:{r[0][1]}" + n = node_at(g, a) + return (f"{n[0]}:{n[1]}" + (f" ({n[2]})" if n[2] != 'expression' else '')) if n else a + # a library middleware has no function to carry the conditional row: the registering call that puts it in this + # chain (middleware_registered_at) says whether it sits in a branch or a loop (registration_conditional) + def regcond(mw, h, v, p, app): + if not (g.has('ext_middleware_registered_at') and g.has('ext_registration_conditional')): return [] + try: + rcs = {r[0] for r in g.q("SELECT c5 FROM ext_middleware_registered_at WHERE c0 = ? AND c1 = ? AND c2 = ? AND c3 = ? AND c4 = ?", mw, h, v, p, app)} + # only an app-level `use(..)`: a route's own middleware runs whenever the route is served + rcs = {rc for rc in rcs if any(r[0] == 'use' for r in g.q("SELECT callee_name FROM sites WHERE id = ?", rc))} + return sorted({r[1] for rc in rcs for r in g.q("SELECT c0, c1 FROM ext_registration_conditional WHERE c0 = ?", rc)} + & {'conditional_registration', 'loop_registration'}) + except Exception: return [] + def is_test(h): return bool((g.sym.get(h) or {}).get('is_test')) + # a handler with no name of its own (the function a wrapper returns, `traced('x', handleX)`): the registering + # call the engine attributes the route to (route_registered_by), as written, names what it wraps + def reg_line(h, v, p, app): + if not (g.has('ext_route_registered_by') and name(h).startswith('<')): return None + try: rms = {r[0] for r in g.q("SELECT c0 FROM ext_route_registered_by WHERE c1 = ? AND c2 = ? AND c3 = ? AND c4 = ?", h, v, p, app)} + except Exception: return None + files = set() + for rm in rms: + if rm in g.sym and g.loc(rm): files.add(g.loc(rm).rsplit(':', 1)[0]) + else: + n_ = node_at(g, rm) + if n_: files.add(n_[0]) + segs = [x for x in p.split('/') if x] + best = [] + for f in files: + # a chained call starts where the chain does: look through its whole span, the innermost call wins + try: st = g.q("SELECT start_line, end_line FROM sites WHERE file = ? AND callee_name IN (?, 'all', 'on')", f, v.lower()) + except Exception: st = [] + lines = source_lines(g.repo, f) + for l0, l1 in st: + l0 = int(l0); l1 = int(l1 or l0) + for ln in range(l0, min(l1, l0 + 40) + 1): + t = lines[ln - 1] if 0 < ln <= len(lines) else '' + k = max((j for j in range(1, len(segs) + 1) if any(f"{q}/{'/'.join(segs[-j:])}{q}" in t for q in '"\'`')), default=0) + if not k and p.strip() in ('/', '') and any(f"{q}/{q}" in t for q in '"\'`'): k = 1 + if k: best.append((-k, l1 - l0, f, ln, t.strip())) + best = sorted(set(best)) + if not best or (len(best) > 1 and best[0][:2] == best[1][:2] and best[0][2:4] != best[1][2:4]): return None + _k, _span, f, ln, t = best[0] + return f" registered at {f}:{ln}: {t[:160]}" + asked_lit = {'/'.join(a) for _w, a in asked} + # the order the router tries them: the served (non-test) apps first, then per app in registration order; a route + # spelled exactly as asked is never cut off by the cap + TESTISH = re.compile(r'(^|/)(tests?|__tests__|e2e)/|[._-](test|spec)\.[cm]?[jt]sx?$') + def app_test(a): return bool(TESTISH.search(app_name(a).rsplit(':', 1)[0])) + exact_apps = {r[3] for r in rows if '/'.join(_route_parts(r[2])) in {'/'.join(a) for _w, a in asked}} + ordered = sorted({tuple(r) for r in rows}, key=lambda r: (app_test(r[3]) or is_test(r[0]), r[3] not in exact_apps, app_name(r[3]), r[4])) + shown = [r for r in ordered[:cap]] + [r for r in ordered[cap:] if '/'.join(_route_parts(r[2])) in asked_lit] + # one block per distinct (route, chain): apps a helper builds per call share one creating expression, and the + # same block repeated once per call says nothing new (hono-3-06) + blocks, order_ = {}, [] + for h, v, p, app, _k in shown: + why = [w for (x, w) in cond if x == h] + head = (f"route: {v} {p} -> {name(h)}" + + ''.join(' (conditional: registered inside a branch, may not be served)' if w == 'conditional_registration' + else ' (registered inside a loop)' for w in sorted(why))) + body_ = [] + rl = reg_line(h, v, p, app) + if rl: body_.append(rl) + chain = [] + if g.has('ext_middleware_applies'): + try: + chain = g.q("SELECT c0, c4 FROM ext_middleware_applies WHERE c1 = ? AND c2 = ? AND c3 = ? AND c5 = ?", h, v, p, app) + except Exception: + chain = g.q("SELECT c0, c4 FROM ext_middleware_applies WHERE c1 = ? AND c2 = ? AND c3 = ?", h, v, p) + chain = sorted({(int(c[1]) if str(c[1]).isdigit() else 0, c[0]) for c in chain}) + if chain: + body_.append(f" runs, in order:") + # two middleware at ONE position are alternatives (an if/else registering one each): same number + nums = {q: k for k, q in enumerate(sorted({c[0] for c in chain}), 1)} + npos = collections.Counter(c[0] for c in chain) + for _i, (_pos, mw) in enumerate(chain, 1): + i = nums[_pos] + cw = sorted(w for (x, w) in cond if x == mw) or regcond(mw, h, v, p, app) + body_.append(f" {i}. {name(mw)}" + (' (one of the alternatives at this position)' if npos[_pos] > 1 else '') + ''.join(' (conditional: registered inside a branch, may not run)' if w == 'conditional_registration' + else ' (registered inside a loop)' for w in cw)) + body_.append(f" {len(nums) + 1}. {name(h)} (the handler)") + if h in undet: body_.append(" order among the known middleware; a list the engine could not read may add more at run time") + else: + body_.append(" no middleware" + ("; a list the engine could not read may add some at run time" if h in undet else '')) + key = (head, tuple(body_)) + if key not in blocks: blocks[key] = []; order_.append(key) + blocks[key].append(app_name(app)) + for key in order_: + apps_ = blocks[key] + uniq = sorted(set(apps_)) + if len(roots) > 1: + tag = (f" (app {uniq[0]})" if len(apps_) == 1 else + f" ({len(apps_)} apps built at {uniq[0]})" if len(uniq) == 1 else + f" (apps {', '.join(uniq[:3])}" + (f" +{len(uniq) - 3}" if len(uniq) > 3 else '') + ")") + else: tag = '' + out.append(key[0] + tag) + out.extend(key[1]) + if g.has('ext_http_route_partial'): + # SPEC 8.1(a): the partial whose KNOWN tail is the asked path comes first, then the ones that match it only + # through parameters and holes (`/{}/:action`), each with what is decided of its chain and what may run + hitp = [] + for h, v, p, app, why in sorted({tuple(r) for r in g.q("SELECT c0, c1, c2, c3, c4 FROM ext_http_route_partial")}): + kp0 = [x for x in _route_parts(p.replace('{}', ''))] + # an optional last parameter (`:module?`) may be left off by the request + kps = [kp0] + ([kp0[:-1]] if kp0 and p.rstrip('/').endswith('?') and len(kp0) > 1 else []) + best = None + for (w, a), kp in [(x, k) for x in asked for k in kps]: + if not (vok(w, v) and len(a) >= len(kp) and kp): continue + tail = a[-len(kp):] + # a parameter the question writes stands for a parameter of the route, never for one of its literal + # segments, and at least one literal segment must be the same: `/files` is no answer to `/x/:module` + if any(x != y and (x == '*' or y == '*') and not (x == '*' and y == '*') for x, y in zip(tail, kp)): continue + if any(x != y for x, y in zip(tail, kp) if x != '*' and y != '*'): continue + lit = sum(1 for x, y in zip(tail, kp) if x == y and x != '*') + if not lit: continue + rank = (0 if len(kp) == len(a) else 1, -lit, len(a) - len(kp)) + best = rank if best is None or rank < best else best + if best is not None: hitp.append((best, is_test(h), p, v, h, app, why)) + hitp.sort(key=lambda r: (r[0], r[1], r[2], r[3])) + # one block per (route, known path, chain): several apps (a holder's two creating expressions, an app value + # nothing resolves) answering it alike are one answer, their reasons listed together + pblocks, porder = {}, [] + for rk, _t, p, v, h, app, why in hitp: + chain = [] + if g.has('ext_middleware_applies'): + chain = sorted({(int(c[1]) if str(c[1]).isdigit() else 0, c[0]) for c in + g.q("SELECT c0, c4 FROM ext_middleware_applies WHERE c1 = ? AND c2 = ? AND c3 = ? AND c5 = ?", h, v, p, app)}) + may = sorted({(r[0], r[1]) for r in g.q("SELECT c0, c4 FROM ext_middleware_may_apply WHERE c1 = ? AND c3 = ? AND c5 = ?", h, p, app)}) \ + if g.has('ext_middleware_may_apply') else [] + key = (v, p, h, tuple(m for _p, m in chain), tuple(may)) + if key not in pblocks: pblocks[key] = (rk, set(), app); porder.append(key) + pblocks[key][1].add(why) + exact_n = sum(1 for k in porder if pblocks[k][0][0] == 0 and pblocks[k][0][1] == -len(_route_parts(k[1].replace('{}', '')))) + pcap = max(cap, exact_n) + for v, p, h, chain, may in porder[:pcap]: + whys = ', '.join(sorted(w.replace('_', ' ') for w in pblocks[(v, p, h, chain, may)][1])) + why_c = [w for (x, w) in cond if x == h] + out.append(f"route: {v} {p} -> {name(h)} (prefix not known: {whys})" + + ''.join(' (conditional: registered inside a branch, may not be served)' if w == 'conditional_registration' + else ' (registered inside a loop)' for w in sorted(why_c))) + if chain: + out.append(" runs, in order (what is decided on its own app):") + for i, mw in enumerate(chain, 1): + cw = sorted(w for (x, w) in cond if x == mw) or regcond(mw, h, v, p, pblocks[(v, p, h, chain, may)][2]) + out.append(f" {i}. {name(mw)}" + ''.join(' (conditional: registered inside a branch, may not run)' + if w == 'conditional_registration' else ' (registered inside a loop)' for w in cw)) + out.append(f" {len(chain) + 1}. {name(h)} (the handler)") + if h in undet: out.append(" order among the known middleware; a list the engine could not read may add more at run time") + elif h in undet: out.append(" a middleware list the engine could not read may run before it at run time") + for mw, q in may: + out.append(f" {name(mw)} may run (scope {q} vs unknown prefix)") + if len(porder) > pcap: out.append(f" ... +{len(porder) - pcap} more route(s) whose prefix is not known could match it") + return out + + +def mount_lines(g, text, cap=8): + """`what path does analyticsRouter serve`: an app named in the question by the variable that holds it, and the + prefix every mount of it composes (http_mount, the EFFECTIVE prefix through every basePath and mount), with the + routes it serves there. Only a variable whose initializer created a mounted app is answered.""" + if not (g.has('ext_http_mount') and g.has('symbols')): return [] + out = [] + try: + mounts = g.q("SELECT m.c0, m.c1, m.c2, s.file, s.start_line FROM ext_http_mount m JOIN sites s ON s.id = m.c1") + except Exception: + return [] + words = set(re.findall(r'[A-Za-z_$][\w$]*', text or '')) + for w in sorted(words): + if len(w) < 3: continue + try: vs = g.q("SELECT file, line, end_line FROM symbols WHERE name = ? AND kind IN ('const', 'let', 'var', 'field', 'property')", w) + except Exception: vs = [] + for f, l0, l1 in vs: + l1 = l1 or l0 + hit = sorted({(m[2], m[0]) for m in mounts if m[3] == f and l0 <= m[4] <= l1}) + for pre, parent in hit[:cap]: + try: pr = g.q("SELECT file, start_line FROM sites WHERE id = ?", parent) + except Exception: pr = [] + n_ = None if pr else node_at(g, parent) + pn = f"{pr[0][0]}:{pr[0][1]}" if pr else ((f"{n_[0]}:{n_[1]}" + (f" ({n_[2]})" if n_[2] != 'expression' else '')) if n_ else parent) + out.append(f"mount: {w} ({f}:{l0}) is served under {pre} (mounted on the app at {pn})") + try: + rs = g.q("""SELECT DISTINCT r.c1, r.c2, s.display, s.file, s.line FROM ext_http_route r JOIN symbols s ON s.method_id = r.c0 + WHERE s.file = ? AND (r.c2 = ? OR r.c2 LIKE ?) ORDER BY r.c4""", f, pre, pre.rstrip('/') + '/%') + except Exception: rs = [] + for v, p_, d, hf, hl in rs[:cap]: out.append(f" serves {v} {p_} -> {d} {hf}:{hl}") + if len(rs) > cap: out.append(f" ... +{len(rs) - cap} more") + return out + + +STOP = set('a an and are does do for from how is it of on or the this that to what when where which who whom why with route routes ' + 'serve serves served call calls reach reaches path paths run runs before after middleware handler request ' + 'app apps http get post put delete patch any all use'.split()) + + +def worded_route_lines(g, text, cap=6): + """A route question with no quoted path (`which route does apiClient.kb.admin general-knowledge $post call`, `who + serves the users list route`): the routes, composed or partial, whose literal path segments or handler file name + carry at least two of the question's words, a `$verb` narrowing the verb. Labelled as a word match, never as the + route table's answer to a quoted route.""" + if not g.has('ext_http_route') or VERB_ROUTE.search(text or ''): return [] + vm = re.search(r'\$(get|post|put|delete|patch|options|head)\b', text or '', re.I) + verb = vm.group(1).upper() if vm else None + words = {w.lower() for w in re.findall(r'[A-Za-z][\w-]*', (text or '').replace('$', ' ').replace('.', ' ').replace('[', ' ').replace(']', ' '))} + words = {w for w in words if w not in STOP and len(w) > 1} + if len(words) < 2: return [] + def segs(p): return {x.lower() for x in p.split('/') if x and not x.startswith(':') and '{' not in x and x != '*'} + rows = [(h, v, p, '') for h, v, p in g.q("SELECT DISTINCT c0, c1, c2 FROM ext_http_route")] + if g.has('ext_http_route_partial'): + rows += [(h, v, p, why) for h, v, p, why in g.q("SELECT DISTINCT c0, c1, c2, c4 FROM ext_http_route_partial")] + scored = [] + for h, v, p, why in rows: + if verb and v not in (verb, 'ANY'): continue + f = ((g.sym.get(h) or {}).get('file') or '').lower() + fw = set(re.findall(r'[a-z][a-z0-9-]*', f.rsplit('/', 1)[-1])) | set(re.findall(r'[a-z][a-z0-9-]*', f)) + # the handler's own name (`export const list = ...` serves the users LIST route) + fw |= {x.lower() for x in re.findall(r'[A-Za-z][a-z0-9]*', (g.sym.get(h) or {}).get('name') or '')} + hit = (segs(p) & words) | (fw & words if why else set()) + if len(hit) >= 2 or (len(hit) == len(segs(p) & words) == len(segs(p)) and len(hit) >= 2): + scored.append((-len(hit), bool((g.sym.get(h) or {}).get('is_test')), p, v, h, why)) + out = [] + for _n, _t, p, v, h, why in sorted(set(scored))[:cap]: + nm = f"{g.disp(h)} {g.loc(h) or ''}".rstrip() if h in g.sym else h + out.append(f"route (by the question's words): {v} {p} -> {nm}" + (f" (prefix not known: {why.replace('_', ' ')})" if why else '')) + return out + + +# a question about how a request gets IN (not "who serves GET /x", which the route table answers) +SERVE_WORDS = re.compile(r'(?i)\b(reach(es)?|entry|entries|fetch|request[s]?|start[s]?|listen[s]?|boot[s]?)\b') +SERVED_HOW = {'default_export_fetch': 'the default export { fetch }', 'default_export': 'the default export', 'fetch_handed': "its fetch handed to", + 'request': 'a request sent in place'} + + +def served_lines(g, text, cap=6): + """`how does a request reach the routers`: where each served app is handed to what serves it (http_served_by): the + default export `{ fetch: app.fetch }`, `export default app`, `serve({ fetch: app.fetch })`. Production apps first.""" + if not (g.has('ext_http_served_by') and SERVE_WORDS.search(text or '')): return [] + def site(x): + try: r = g.q("SELECT callee_name, file, start_line FROM sites WHERE id = ?", x) + except Exception: r = [] + if r: return r[0] + n = node_at(g, x) + return ('', n[0], n[1]) if n else None + TESTISH = re.compile(r'(^|/)(tests?|__tests__|e2e)/|[._-](test|spec)\.[cm]?[jt]sx?$') + rows = [] + for app, at, how in sorted({tuple(r) for r in g.q("SELECT c0, c1, c2 FROM ext_http_served_by")}): + a, s_ = site(app), site(at) + an = f"{a[1]}:{a[2]}" if a else app + sn = f"{s_[1]}:{s_[2]}" if s_ else at + call = f" {s_[0]}(..)" if (s_ and how == 'fetch_handed' and s_[0]) else '' + rows.append((bool(TESTISH.search(an.rsplit(':', 1)[0])), an, f"served: the app created at {an} is served through {SERVED_HOW.get(how, how)}{call} at {sn}")) + rows.sort() + out = [r[2] for r in rows if not r[0]][:cap] or [r[2] for r in rows][:cap] + n = sum(1 for r in rows if not r[0]) if any(not r[0] for r in rows) else len(rows) + return out + ([f" ... +{n - cap} more"] if n > cap else []) + + +def declared_unknown_lines(g, text, cap=6): + """A question whose words name a declared unknown (`except` -> dynamic_except, `list` -> dynamic_middleware_list): + the rows the engine wrote, so the answer says what cannot be decided instead of guessing an exclusion set.""" + if not g.has('ext_framework_undetermined'): return [] + words = {w.lower() for w in re.findall(r'[A-Za-z]+', text or '')} + out, head = [], [] + # where a run-time middleware list enters the chains (registration_conditional on the registering call): the line + # to read first, before the routes it stands in + regs = set() + if g.has('ext_registration_conditional'): + try: regs = {tuple(r) for r in g.q("SELECT c0, c1 FROM ext_registration_conditional WHERE c1 = 'dynamic_middleware_list'")} + except Exception: regs = set() + for rc, why in sorted(regs): + if not (set(why.split('_')) & words - {'route', 'path', 'registration'}): continue + try: st = g.q("SELECT callee_name, file, start_line FROM sites WHERE id = ?", rc) + except Exception: st = [] + if st: head.append(f"declared unknown: {why.replace('_', ' ')} registered at {st[0][1]}:{st[0][2]} ({st[0][0]}(..) with a middleware read at run time); the chains below may hold more") + for m, what, why in sorted({tuple(r) for r in g.q("SELECT c0, c1, c2 FROM ext_framework_undetermined")}): + toks = set(why.split('_')) + if not (toks & words - {'route', 'path', 'registration'}): continue + nm = f"{g.disp(m)} {g.loc(m) or ''}".rstrip() if m in g.sym else m + out.append(f"declared unknown: {why.replace('_', ' ')} ({what.replace('_', ' ')}) at {nm}") + return sorted(set(head))[:cap] + out[:cap] + ([f" ... +{len(out) - cap} more"] if len(out) > cap else []) + + def route_seeds(g, text): """[(symbol id, why)] for a route the question quotes (`GET /api/widgets`, `/orders/{id}`), at the declaration the graph records as serving it: a remote-edge row, a route attribute or annotation, or the innermost callable around @@ -390,6 +735,20 @@ def route_seeds(g, text): WHERE e.c2 = 'url_dispatch' AND e.c3 LIKE '/%'"""): tables.add(r['tf']) if any(_route_eq(_route_parts(r['path']), a) for a in asked): serve(r['sid'], r['path']) + # an HTTP router's route table: the full path the engine composed through every basePath and mount (http_route), + # and the middleware the router runs before the handler there, in chain order (middleware_applies). The chain is + # seeded first so the answer walks it in the order a request does: each middleware, then the handler. + if g.has('ext_http_route') and g.has('symbols'): + for r in g.q("""SELECT s.id AS sid, e.c0 AS h, e.c1 AS verb, e.c2 AS path FROM ext_http_route e + JOIN symbols s ON s.method_id = e.c0"""): + if not any(_route_eq(_route_parts(r['path']), a) for a in asked): continue + if g.has('ext_middleware_applies'): + mws = g.q("""SELECT s.id AS sid, m.c4 AS pos FROM ext_middleware_applies m JOIN symbols s ON s.method_id = m.c0 + WHERE m.c1 = ? AND m.c2 = ? AND m.c3 = ?""", r['h'], r['verb'], r['path']) + for mw in sorted(mws, key=lambda x: int(x['pos']) if str(x['pos']).isdigit() else 0): + if mw['sid'] in g.sym and (mw['sid'], f"\x00{r['path']}") not in hits: + hits.append((mw['sid'], f"\x00{r['path']}")) + serve(r['sid'], r['path']) # a route the engine answered is not searched again as text: the literal scan below is the fallback for a route # no edge carries, and on an answered one it only adds the table's module and every literal that spells a suffix asked = [a for a in asked if not any(_route_eq(_route_parts(r), a) for _s, r in hits)] @@ -438,7 +797,10 @@ def route_seeds(g, text): if inner and not g.sym[inner[0][1]].get('is_test') and not TESTY.search(f or ''): serve(inner[0][1], v) out, seen = [], set() for sid, route in hits: - if sid not in seen: seen.add(sid); out.append((sid, f"serves the route {route.strip()}")) + if sid in seen: continue + seen.add(sid) + # a middleware seeded from a route's chain runs BEFORE the handler: it does not serve the route + out.append((sid, f"runs before the handler of {route[1:].strip()}" if route.startswith('\x00') else f"serves the route {route.strip()}")) return out[:NAMED_FAN] @@ -1325,6 +1687,12 @@ def main(argv): + ("…" if n > 4 else "") + ")") print(" no --in was given, so NOTHING below is narrowed to it — " "pass --in to narrow.\n") + # a question the route table answers (a quoted route, a mounted app's name) needs no scope: the table is the + # answer, and refusing for want of --in returned nothing while holding it (QH18, a Deno project whose words + # landed in no directory) + if not flat and not scopes and not (have_from and not fixed) and (route_table_lines(g, body) or mount_lines(g, body)): + flat = True + print("scope: the repository root (the route table answers this; --in narrows the code below)\n") bad = None if flat or (have_from and not fixed) else require_scope(g, scopes, terms, rank=rank, flag=' --in-offered') if bad is not None: return bad @@ -1367,6 +1735,21 @@ def main(argv): RESULT['entry_points'] = [{'name': g.disp(sid), 'at': g.loc(sid), 'why': (term or 'best overall match')} for sid, term, sc in seeds] + _rt = route_table_lines(g, body) + mount_lines(g, body) + _served = served_lines(g, body) + if not _rt: + _rt = worded_route_lines(g, body) + # the best word match's own chain, as the route table prints a quoted route's + for l in _rt[:1]: + m_ = re.match(r"route \(by the question's words\): (\S+) (\S+) ->", l) + if m_ and 'prefix not known' not in l: + _rt = _rt + [x for x in route_table_lines(g, f"{m_.group(1)} {m_.group(2)}", cap=2) if not x.startswith('route:')] + _rt += declared_unknown_lines(g, body) + _rt = _served + _rt + if _rt: + print("the route table (composed by the engine through every basePath and mount; read these first):") + for line in _rt: print(' ' + line) + print() print(f"task terms: {' · '.join(terms[:10])}" + (f" (+{len(terms)-10})" if len(terms) > 10 else '')) if explain is None: explain = explain_intent(task) # explaining: the flow below starts at its own roots and IS the answer, so the lexical entry points (a word @@ -1406,7 +1789,10 @@ def main(argv): cands = [s_ for s_, _t, _sc in seeds if (g.sym.get(s_) or {}).get('method_id')] cands += [x for x in type_members(g, [s_ for s_, _t, _sc in seeds], scored) if x not in cands] asked = {s_ for s_, _why in named} - roots_ = sorted(cands, key=lambda x: x not in asked and bodiless(g, x))[:FLOW_ROOTS] + # a quoted route seeds its whole chain (each middleware, then the handler) and the flow starts at each of + # them, in that order: cutting it at FLOW_ROOTS dropped the handler, the one the question was about + n_route = sum(1 for _s, w in named if w.startswith(('serves the route', 'runs before the handler of'))) + roots_ = sorted(cands, key=lambda x: x not in asked and bodiless(g, x))[:max(FLOW_ROOTS, min(n_route, 8))] roots_ = producer_first(g, roots_, [s_ for s_, _t, _sc in seeds], task, terms) # a producer joined only by name is shown as one step: its code holds the call, and the budget goes to the # mechanism the task asked about rather than to everything else the producer does @@ -1550,7 +1936,9 @@ def main(argv): "destination both ends name):") for d, _a, _b, fr, to, tr, dest, conf, near_is_sender in rows[:REMOTE_SHOWN]: far = to if near_is_sender else fr - print(f" [remote] {tr} {dest} ({conf}) {g.disp(fr)} → {g.disp(to)} " + cw = {'base_unknown': 'may reach: the base address is not known', + 'method_unknown': 'may reach: the HTTP method is not known'}.get(conf, conf) + print(f" [remote] {tr} {dest} ({cw}) {g.disp(fr)} → {g.disp(to)} " f"{'handled in' if near_is_sender else 'sent from'} {g.loc(far)}") if len(rows) > REMOTE_SHOWN: print(f" … +{len(rows) - REMOTE_SHOWN} more") RESULT['remote'] = [{'from': g.disp(fr), 'from_at': g.loc(fr), 'to': g.disp(to), 'to_at': g.loc(to), diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-impact b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-impact index 83b0a0f53..4888ce911 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-impact +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-impact @@ -2460,6 +2460,34 @@ class Impact: g.q("SELECT c0, c1, c2, c3, c4 FROM ext_framework_edge") if r[0] and r[1] and r[0] != r[1]}) W('framework', fw) + # AN HTTP ROUTER'S CHAIN (config-resolution/http-router-routes.dl, framework-behavior/http-router-chain.dl): + # middleware_applies(Mw, Handler, Verb, Path, Position) and ctx_key_flow(Setter, Reader, Key). Neither is a + # call: the router runs the middleware before the handler, and a context key written by one function on the + # chain is read by a later one. A change to a middleware or to what a key holds reaches those functions. + mwa = [] + if g.has('ext_middleware_applies'): + mwa = sorted({(r[0], r[1], r[2], r[3], r[4]) for r in + g.q("SELECT c0, c1, c2, c3, c4 FROM ext_middleware_applies") if r[0] and r[1] and r[0] != r[1]}) + W('mw_applies', mwa) + ctxf = [] + if g.has('ext_ctx_key_flow'): + # one row per (setter, reader) naming EVERY key that flows between them: a reader of two keys a middleware sets + # was shown under one of them + _keys = collections.defaultdict(set) + for r in g.q("SELECT c0, c1, c2 FROM ext_ctx_key_flow"): + if r[0] and r[1]: _keys[(r[0], r[1])].add(r[2]) + ctxf = sorted((a, b, "', '".join(sorted(ks))) for (a, b), ks in _keys.items()) + W('ctx_flow', ctxf) + # a schema a route validates its input with (route_schema, TS framework-behavior/http-router-schema.dl), and the + # route each handler serves (http_route), so a change to the schema names the routes it reaches + rsch = [] + if g.has('ext_route_schema'): + rsch = sorted({(r[0], r[1], r[2]) for r in g.q("SELECT c0, c1, c2 FROM ext_route_schema") if r[0] and r[2]}) + W('route_schema', rsch) + hroute = [] + if g.has('ext_http_route'): + hroute = sorted({(r[0], r[1], r[2]) for r in g.q("SELECT c0, c1, c2 FROM ext_http_route") if r[0]}) + W('http_route', hroute) # A LIBRARY CALLING BACK INTO THE CLIENT (Python framework-behavior/library-callbacks.dl): walked by the closure as # its own hop, `library callback`, so a test that drives the library reaches the client member it hands back to. libcb = [] @@ -2934,6 +2962,221 @@ def target_ext(g, pay): return '' +def own_ids(g, targets): + """the declarations the question named (each target's payload), not the closure's expanded seeds: a type's seeds + reach every function that mentions it, and the router answer is about the named thing only""" + out = set() + def walk(x): + if isinstance(x, str): + if x in g.sym: out.add(x); mid = (g.sym[x] or {}).get('method_id') + else: mid = None + if mid: out.add(mid) + if x.startswith(('TS_', 'JS_')): out.add(x) + elif isinstance(x, dict): + for v in x.values(): walk(v) + elif isinstance(x, (list, tuple, set)): + for v in x: walk(v) + else: + try: vals = list(x) # a database row + except TypeError: return + for v in vals: walk(v) + for _k, _lab, p_ in targets: walk(p_) + return out + +def router_answer(g, seeds, limit=25): + """AN HTTP ROUTER'S ANSWERS (SPEC 8.1): what the route table (http_route, http_route_partial, + route_registered_by), the middleware chains (middleware_applies) and the context-key flow (ctx_key_flow) say about + a change to the seeds. Only rows the engine wrote are printed; every list is capped by `limit` with the count + behind it. Returned as lines so the text answer and the --json document (`router`) carry the same words.""" + out = [] + if not seeds: return out + def _rq(sql, *a): + try: return g.q(sql, *a) + except Exception: return [] + def has(t): + try: return g.has(t) + except Exception: return False + S = sorted(seeds) + sp = ','.join('?' * len(S)) + def site(x): + r = _rq("SELECT callee_name, file, start_line FROM sites WHERE id = ?", x) if has('sites') else [] + if r: return r[0] + # a node that is not a call site (an app held in a parameter or a field, a middleware value): where the engine + # says it is written (router_node_at) + n = _rq("SELECT c1, c2, c3 FROM ext_router_node_at WHERE c0 = ?", x) if has('ext_router_node_at') else [] + if not n: return None + f = n[0][0] + pr = _rq("SELECT rel FROM paths WHERE raw = ?", f) if has('paths') else [] + return ('' if n[0][2] == 'expression' else n[0][2], (pr[0][0] if pr and pr[0][0] else f), n[0][1]) + def where(x): + if x in g.sym: return g.loc(x) or '' + r = site(x) + return f"{r[1]}:{r[2]}" if r else '' + def name(x): + if x in g.sym: return f"{g.disp(x)} {g.loc(x) or ''}".rstrip() + vk = _rq("SELECT c2 FROM ext_middleware_site WHERE c0 = ? AND c1 = 'value'", x) if has('ext_middleware_site') else [] + if vk and vk[0][0]: return f"{vk[0][0]} (value)" + cs = _rq("SELECT c2, c3 FROM ext_middleware_site WHERE c0 = ? AND c1 != 'value'", x) if has('ext_middleware_site') else [] + r = site(x) + at = f" at {r[1]}:{r[2]}" if r else '' + if cs and cs[0][0]: return f"{cs[0][0]}() from {cs[0][1]}{at}" + if r and r[0] and not r[0].startswith(('parameter ', 'field ')): return f"{r[0]}(){at}" + if r: return f"a value{at}" + return 'a value the engine could not name' + def app_name(a): + r = site(a) + return (f"{r[1]}:{r[2]}" + (f" ({r[0]})" if r[0].startswith(('parameter ', 'field ')) else '')) if r else a + def cap(xs, f): + for x in xs[:limit]: out.append(f(x)) + if len(xs) > limit: out.append(f" ... +{len(xs) - limit} more (--limit N)") + def hits(h): + return sorted({(r[0], r[1]) for r in _rq("SELECT c0, c3 FROM ext_remote_edge WHERE c1 = ?", h)}) if has('ext_remote_edge') else [] + undet = collections.defaultdict(set) + if has('ext_framework_undetermined'): + for r in _rq("SELECT c0, c1, c2 FROM ext_framework_undetermined"): undet[r[0]].add((r[1], r[2])) + def honesty(h): + w = [] + for what, why in sorted(undet.get(h, ())): + if why == 'conditional_registration': w.append('conditional: registered inside a branch') + elif why == 'loop_registration': w.append('registered inside a loop') + elif why == 'dynamic_middleware_list': w.append('order among the known middleware; a list the engine could not read may add more at run time') + return w + # (c)/(d) a function that REGISTERS routes: a registration function, an app factory or builder, a module body + if has('ext_route_registered_by'): + rg_all = {tuple(r) for r in _rq(f"SELECT c0, c1, c2, c3, c4 FROM ext_route_registered_by WHERE c0 IN ({sp})", *S)} + # one line per route: the same registration served by several apps (a builder called from tests) is one change + apps = sorted({r[4] for r in rg_all}, key=lambda a: (bool(re.search(r'(^|/)(tests?|__tests__)/|\.(test|spec)\.', app_name(a))), app_name(a))) + rg = sorted({(r[0], r[1], r[2], r[3], '') for r in rg_all}, key=lambda r: (r[3], r[2])) + if rg: + out.append(f"registers: {len(rg)} route(s) a change here adds to or removes from the served app" + + (f" ({', '.join('app ' + app_name(a) for a in apps[:3])}" + (f" +{len(apps) - 3} more app(s)" if len(apps) > 3 else '') + ")" if apps else '') + ":") + def reg(r): + c, h, v, p, a = r + tail = ''.join(f"; hit by {g.disp(x)} {g.loc(x) or ''} ({d})" for x, d in hits(h)[:3] if x in g.sym) + hz = honesty(h) + return f" registers {v} {p} -> {name(h)}" + (f" ({'; '.join(hz)})" if hz else '') + tail + cap(rg, reg) + # an app factory / builder: the apps whose creating expression is inside the seed function, and what they serve + if has('ext_http_route') and has('sites'): + spans = [(r[0], r[1], r[2], x) for x in S for r in _rq("SELECT file, line, end_line FROM symbols WHERE id = ? OR method_id = ?", x, x)[:1]] + made = collections.defaultdict(set) + # distinct routes: every call of a helper that builds an app is its own app (R6) reported under the one creating + # expression, so the rows repeat each route once per call + for a, n in _rq("SELECT c3, COUNT(DISTINCT c0 || char(9) || c1 || char(9) || c2) FROM ext_http_route GROUP BY c3"): + r = site(a) + if not r: continue + for f, l0, l1, x in spans: + if f and r[1] == f and l0 and l1 and l0 <= r[2] <= l1: made[x].add((a, n)) + for x in sorted(made): + for a, n in sorted(made[x], key=lambda t: -t[1])[:limit]: + eg = _rq("SELECT c1, c2 FROM ext_http_route WHERE c3 = ? GROUP BY c1, c2 ORDER BY MIN(c4) LIMIT 3", a) + out.append(f"creates app {app_name(a)} serving {n} route(s)" + (f", e.g. {', '.join(v + ' ' + p for v, p in eg)}" if eg else '')) + # a route HANDLER: every route it serves (composed or partial), in table order, and the chain the router runs first + rt = sorted({tuple(r) for r in _rq(f"SELECT c0, c1, c2, c3, c4 FROM ext_http_route WHERE c0 IN ({sp})", *S)}) if has('ext_http_route') else [] + for h, v, p, a, k in sorted(rt, key=lambda r: (r[3], r[4]))[:limit]: + mw = _rq("SELECT c0, c4 FROM ext_middleware_applies WHERE c1 = ? AND c2 = ? AND c3 = ? AND c5 = ?", h, v, p, a) if has('ext_middleware_applies') else [] + before = sorted({(int(r[1]) if str(r[1]).isdigit() else 0, r[0]) for r in mw}) + hz = honesty(h) + out.append(f"route: {g.disp(h)} serves {v} {p} (app {app_name(a)})" + (f" ({'; '.join(x for x in hz if not x.startswith('order'))})" if [x for x in hz if not x.startswith('order')] else '')) + if before: + out.append(" the router runs first, in order: " + ', '.join(f"{i}. {name(m)}" for i, (_p, m) in enumerate(before, 1))) + else: + out.append(" no middleware the engine can see runs before it") + if any(x.startswith('order') for x in hz): out.append(" " + [x for x in hz if x.startswith('order')][0]) + if has('ext_http_route_partial'): + pr = sorted({(r[0], r[1], r[2], r[4]) for r in _rq(f"SELECT c0, c1, c2, c3, c4 FROM ext_http_route_partial WHERE c0 IN ({sp})", *S)}) + for h, v, p, why in pr[:limit]: + out.append(f"route: {g.disp(h)} serves {v} {p} -- prefix not known ({why.replace('_', ' ')}); the full path is decided at run time") + if has('ext_middleware_may_apply'): + for mw, q in sorted({(r[0], r[1]) for r in _rq("SELECT c0, c4 FROM ext_middleware_may_apply WHERE c1 = ? AND c3 = ?", h, p)}): + out.append(f" {name(mw)} may run before it (scope {q} vs unknown prefix)") + # (d)/(f) a MIDDLEWARE, or a middleware FACTORY (resolved to what it returns): the routes it runs before, and the + # context keys it sets grouped by key with their readers + mws = set(S); asked_mw = set(S) + # a builder or factory (`createApp(ctx)`, `createFactory({ initApp })`): the middleware written inside it is what a + # change to it changes on every app it makes + if has('ext_middleware_site'): + for x in S: + for f, l0, l1 in _rq("SELECT file, line, end_line FROM symbols WHERE id = ? OR method_id = ?", x, x)[:1]: + if not (f and l0 and l1 and l1 > l0): continue + for (m,) in _rq("""SELECT DISTINCT e.c0 FROM ext_middleware_site e JOIN symbols s ON s.method_id = e.c0 + WHERE e.c1 = 'project_fn' AND s.file = ? AND s.line BETWEEN ? AND ?""", f, l0, l1): + mws.add(m) + if has('ext_middleware_site'): + names = {g.sym[x]['name'] for x in S if x in g.sym and g.sym[x].get('name')} + for nm in names: + fr_ = {r[0] for r in _rq("SELECT c0 FROM ext_middleware_site WHERE c1 = 'factory_result' AND c2 = ?", nm)} + mws |= fr_; asked_mw |= fr_ + if has('ext_ctx_key_flow'): + mp = ','.join('?' * len(mws)) + keys = collections.defaultdict(set) + for s_, r_, k_ in _rq(f"SELECT c0, c1, c2 FROM ext_ctx_key_flow WHERE c0 IN ({mp})", *sorted(mws)): + keys[k_].add(r_) + for k_ in sorted(keys): + rs = sorted(keys[k_], key=lambda x: name(x)) + eg = ', '.join(name(x) for x in rs[:limit]) + out.append(f"context key: sets '{k_}': read by {len(rs)} function(s), e.g. {eg}" + (f" (+{len(rs) - limit} more)" if len(rs) > limit else '')) + if has('ext_middleware_may_apply'): + for m in sorted(x for x in mws if x in seeds): + mr = sorted({(r[0], r[1], r[2]) for r in _rq("SELECT c2, c3, c4 FROM ext_middleware_may_apply WHERE c0 = ?", m)}) + if mr: + out.append(f"middleware: {name(m)} may run before {len(mr)} route(s) whose prefix is not known:") + cap(mr, lambda r: f" may run before {r[0]} {r[1]} (scope {r[2]} vs unknown prefix)") + if has('ext_middleware_applies'): + for m in sorted(mws): + rows_ = {(r[0], r[1], r[2]) for r in _rq("SELECT c2, c3, c1 FROM ext_middleware_applies WHERE c0 = ?", m)} + # production handlers first: a test app's route is an example of nothing a user requests + ro = sorted({(v, p_, bool((g.sym.get(h) or {}).get('is_test'))) for v, p_, h in rows_}, key=lambda r: (r[2], r[1], r[0])) + hs = sorted({(bool((g.sym.get(h) or {}).get('is_test')), v, p_, h) for v, p_, h in rows_}) + if ro: + hz = [x for x in honesty(m) if not x.startswith('order')] + if m not in asked_mw: + # written inside the builder asked about: one line, the routes are the builder's + eg = ', '.join(f"{x[1]} {x[2]}" for x in ([x for x in hs if not x[0]] or hs)[:3]) + out.append(f"middleware: {name(m)} (registered inside it) runs before {len(ro)} route(s), e.g. {eg}" + (f" ({'; '.join(hz)}, so it may not run)" if hz else '')) + continue + out.append(f"middleware: {name(m)} runs before {len(ro)} route(s)" + (f" ({'; '.join(hz)}, so it may not run)" if hz else '') + ":") + # EVERY route, not a sample: "does a change to it reach GET /x" is answered by finding the line + # (QD04, QD29); one line per route, production apps first, capped only far above a real app's size + full = [x for x in hs if not x[0]] or hs + # FROM WHERE (QH25, QD29): the registering call that puts it in each route's chain, an app-level + # `use(..)` or the route's own call; a route reached by two registrations names both + via = collections.defaultdict(set) + if has('ext_middleware_registered_at'): + for h_, v_, p_, rc in _rq("SELECT c1, c2, c3, c5 FROM ext_middleware_registered_at WHERE c0 = ?", m): + via[(v_, p_, h_)].add(rc) + def via_txt(v_, p_, h_): + regs = [] + for rc in sorted(via.get((v_, p_, h_), ())): + r = site(rc) + if not r: continue + # `use(..)` attaches it to every matching route; any other registering call is the route's own + regs.append(f"{r[0]}(..) at {r[1]}:{r[2]}" if r[0] == 'use' else f"per-route, in {r[0]}(..) at {r[1]}:{r[2]}") + return f" (via {'; '.join(regs)})" if regs else '' + seen_ = set() + for x in full[:500]: + if (x[1], x[2], x[3]) in seen_: continue + seen_.add((x[1], x[2], x[3])); out.append(f" runs before {x[1]} {x[2]} -> {name(x[3])}{via_txt(x[1], x[2], x[3])}") + if len(full) > 500: out.append(f" ... +{len(full) - 500} more") + nt = sum(1 for x in hs if x[0]) + if nt and any(not x[0] for x in hs): out.append(f" (and before {nt} route(s) of apps built in tests)") + # (e) a request SCHEMA: every route validating with it (or a schema composed from it), and the validated reads + if has('ext_route_schema'): + rsch = sorted({tuple(r) for r in _rq(f"SELECT c0, c1 FROM ext_route_schema WHERE c2 IN ({sp})", *S)}) + rows = [] + for h, t in rsch: + got = sorted({(r[0], r[1]) for r in _rq("SELECT c1, c2 FROM ext_http_route WHERE c0 = ?", h)}) + got += sorted({(r[0], r[1] + ' (prefix not known)') for r in _rq("SELECT c1, c2 FROM ext_http_route_partial WHERE c0 = ?", h)}) if has('ext_http_route_partial') else [] + for v, p in got or [('?', '(route not composed)')]: rows.append((v, p, t, h)) + if rows: + out.append(f"schema: validates {len(rows)} route input(s):") + cap(sorted(rows), lambda r: f" validates {r[0]} {r[1]} (target {r[2]}) -> {name(r[3])}") + if has('ext_valid_read'): + vr = sorted({(r[0], r[1]) for r in _rq(f"SELECT c1, c2 FROM ext_valid_read WHERE c3 IN ({sp})", *S)}) + if vr: cap(vr, lambda r: f" read with c.req.valid('{r[1]}') in {name(r[0])}") + return out + + def main(argv): args = list(argv); depth = 40; limit = 25; IN = None; as_json = '--json' in args; kind = None # --tests lists the tests grouped by rung and file; --tests-only prints that section and nothing else; @@ -3040,6 +3283,11 @@ def main(argv): # accessor, a record component), so every such target came back as a JSONDecodeError rather than an answer print(f"note: {a} is a field and a method of that name — both are the change (--kind field | method for one)", file=sys.stderr if as_json else sys.stdout) + elif kinds in ({'type', 'var'}, {'type', 'field'}) and not kind: + # a value and a type of one name (`const User = z.object(..)` beside `type User = z.infer`, a + # const enum object beside its union type): one concept written twice, so both are the change, as above + print(f"note: {a} is a value and a type of that name: both are the change (--kind var | type for one)", + file=sys.stderr if as_json else sys.stdout) elif len(rs) > 1 and not kind: die(f"{a} is declared as more than one kind: " + ' · '.join(f"{k} ({lab})" for k, lab, _ in rs) + " — say which with --kind " + '|'.join(k for k, _, _ in rs)) # ONE NAME, UNRELATED DECLARATIONS IN DIFFERENT FILES. A bare name answers for every declaration of it — the @@ -3646,6 +3894,7 @@ def main(argv): **({'script': True, 'run': script_cmd(m)} if m in script_ids else {})} for m, (d, fx) in sorted(tests.items(), key=lambda kv: (kv[1][0], g.disp(kv[0]), g.loc(kv[0]), kv[0]))], # the tests whose route never loads the changed file, left out of `tests` (see `unloaded` above) **({'unloaded_tests': {'count': len(unloaded), 'files': sorted({g.sym[m]['file'] for m in unloaded if g.sym[m].get('file')})}} if unloaded else {}), + **({'router': _router} if (_router := router_answer(g, own_ids(g, targets), limit)) else {}), 'framework_entries': [{'id': m, 'display': g.disp(m), 'signal': sig, 'at': g.loc(m)} for m, sig in fw_ent], 'framework_grep': fw_grep, 'stub_tests': [{'id': m, 'display': g.disp(m), 'owner': g.sym[m]['owner'], 'name': g.sym[m]['name'], 'at': g.loc(m), 'certainty': 'type test' if m in type_tests else 'stubs it', @@ -3716,6 +3965,7 @@ def main(argv): # as the direct-dependent sections already do. Tests are counted apart and not explained: a test entry point is # not a place the outside world calls, and the tests section below is what names them. ep_rows = sorted({(r['method_id'], r['reason']) for r in eps}) + for _line in router_answer(g, own_ids(g, targets) & (set(seeds) | own_ids(g, targets)), limit): print(_line) _is_t = lambda m: m in g.sym and g.sym[m]['is_test'] ep_by_reason = collections.defaultdict(list) for m, why in ep_rows: diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path index 63adb2970..51961de48 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path @@ -1129,6 +1129,16 @@ class G: for a, b, how, where, conf in self.q(f"SELECT c0, c1, c2, c3, c4 FROM {table}"): if a == b or a not in self.sym or b not in self.sym: continue self.OUTSIDE.setdefault((a, b, tier), []).append((how, where, conf)) + # an HTTP router's chain: the router runs a middleware before each handler it applies to, and a request-context + # key one function on the chain sets is read by a later one (middleware_applies, ctx_key_flow) + if self.has('ext_middleware_applies'): + for a, b, verb, path, pos in self.q("SELECT c0, c1, c2, c3, c4 FROM ext_middleware_applies"): + if a == b or a not in self.sym or b not in self.sym: continue + self.OUTSIDE.setdefault((a, b, 'framework'), []).append(('middleware', f'{verb} {path}', f'position {pos}')) + if self.has('ext_ctx_key_flow'): + for a, b, key in self.q("SELECT c0, c1, c2 FROM ext_ctx_key_flow"): + if a == b or a not in self.sym or b not in self.sym: continue + self.OUTSIDE.setdefault((a, b, 'framework'), []).append(('context_key', key, 'same route chain')) # a library summarised (from its source) to call a client member back (Python framework-behavior/ # library-callbacks.dl): `impact` walks it as the `library callback` hop, so the chain search does too if self.has('ext_lib_callback_edge'): @@ -1236,6 +1246,8 @@ def outside_hop_holds(g, a, b, t): if t != 'framework': return False if g.has('ext_lib_callback_edge') and g.q("SELECT 1 FROM ext_lib_callback_edge WHERE c0 = ? AND c1 = ? LIMIT 1", a, b): return True + for tbl in ('ext_middleware_applies', 'ext_ctx_key_flow'): + if g.has(tbl) and g.q(f"SELECT 1 FROM {tbl} WHERE c0 = ? AND c1 = ? LIMIT 1", a, b): return True import ax_registration links = getattr(g, '_filter_links', None) if links is None: links = g._filter_links = {(x, y) for x, y, _h in ax_registration.filter_links(g.q)} diff --git a/plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl b/plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl index a7641525e..625c68189 100644 --- a/plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl +++ b/plugins/axiomcode/skills/axiomcode/scripts/dl/impact.dl @@ -337,6 +337,30 @@ direct(q, c, "uses", cat("serves what this sends across a process boundary (", c direct(q, c, "uses", cat("framework-mediated, not a call: ", cat(mech, cat(" via ", cat(detail, cat(" [", cat(conf, "]")))))), "framework", "", 0) :- target(q, "method", m, _), framework(c, m, mech, detail, conf), c != m. +// ── an HTTP router's chain ──────────────────────────────────────────────────────────────────────────── +// mw_applies(Mw, Handler, Verb, Path, Position): the router runs Mw before Handler for that route; a change to the +// middleware (what it rejects, what it puts on the request) reaches the handler with no call between them. +// ctx_flow(Setter, Reader, Key): a request-context key Setter writes and Reader, later on the same chain, reads. +// Certainty `framework`: the router joins them, not a resolved call. +.decl mw_applies(mw:symbol, h:symbol, verb:symbol, path:symbol, pos:symbol) .input mw_applies +.decl ctx_flow(s:symbol, r:symbol, key:symbol) .input ctx_flow +// route_schema(Handler, Target, SchemaDecl): the route validates its Target input (json, query, ...) with the schema +// declared at SchemaDecl or one built from it; http_route(Handler, Verb, Path): the route the handler serves +.decl route_schema(h:symbol, t:symbol, d:symbol) .input route_schema +.decl http_route(h:symbol, verb:symbol, path:symbol) .input http_route +.decl schema_route(d:symbol, h:symbol, t:symbol) +schema_route(d, h, t) :- route_schema(h, t, d). +.decl schema_routed(d:symbol, h:symbol) +schema_routed(d, h) :- route_schema(h, _, d), http_route(h, _, _). +direct(q, h, "uses", cat("validates the ", cat(t, cat(" input of ", cat(v, cat(" ", cat(p, " with this schema")))))), "framework", "", 0) + :- target(q, k, d, _), (k = "field" ; k = "var"), schema_route(d, h, t), http_route(h, v, p). +direct(q, h, "uses", cat("validates its ", cat(t, " input with this schema")), "framework", "", 0) + :- target(q, k, d, _), (k = "field" ; k = "var"), schema_route(d, h, t), !schema_routed(d, h). +direct(q, h, "uses", cat("runs after this as middleware on ", cat(v, cat(" ", cat(p, cat(" (chain position ", cat(pos, ")")))))), "framework", "", 0) + :- target(q, "method", m, _), mw_applies(m, h, v, p, pos), h != m. +direct(q, r, "reads", cat("reads the request-context key(s) '", cat(k, "' this sets, later on the same route chain")), "framework", "", 0) + :- target(q, "method", m, _), ctx_flow(m, r, k), r != m. + direct(q, c, "uses", cat("receives it by dependency injection (", kind, ") — the container hands it over, no call site"), "resolved", "", 0) :- target(q, "type", t, _), injected(t, c, kind), !inside_target(q, c). direct(q, c, "uses", cat("receives it by dependency injection (", kind, ") — the container hands it over, no call site"), "resolved", "", 0) :- target(q, "clinit", t, _), injected(t, c, kind). // a method that DEFINES a bean (@Bean): whoever is injected with the type it returns. Keyed on `bean_factory`, diff --git a/plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py b/plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py index 1ee917a50..a1b993fd3 100644 --- a/plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py +++ b/plugins/axiomcode/skills/axiomcode/scripts/graph_sql.py @@ -3615,6 +3615,9 @@ def _has_framework_hops(q, at=None, site_file=None): import ax_registration if at is not None and ax_registration.key_edges(q, at, site_file): return True if ax_registration.filter_links(q): return True # fw_edge "filter": a servlet filter a test's context holds + # an HTTP router's chain (dl/impact.dl mw_applies / ctx_flow): direct rows the rules have and this port does not + for t in ('ext_middleware_applies', 'ext_ctx_key_flow', 'ext_route_schema'): + if _has(q, t) and q(f"SELECT 1 FROM {t} LIMIT 1"): return True except Exception: return True # cannot tell: decline, because answering smaller is the failure try: From 63bb1dfe90ce1a6006c61e8ac60720e99c161585 Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:25:49 -0700 Subject: [PATCH 2/9] typescript, javascript: link destructured c.var reads; drop c.set made after next() A handler that reads a context key with `const { key } = c.var` is now a reader of the middleware that sets it. A middleware's `c.set` made after the `next()` that runs on every path is no longer linked to later functions on the chain: it happens once they have returned. A `next()` in a branch, loop or try (`if (skip) return next()`) does not count. The Hono oracle had the same gaps, and also credited a context access in a callback nested in a handler to the callback; it now credits the function that owns `c`. New cases cover each shape in TypeScript and JavaScript. Fixes #2003 --- .../framework-behavior/http-router-chain.dl | 29 ++++++++++- .../esm-context-closure-access/src/index.js | 19 +++++++ .../src/index.js | 18 +++++++ .../src/index.js | 23 +++++++++ .../js/esm-context-closure-access.entries | 4 ++ .../js/esm-context-closure-access.tsv | 7 +++ ...esm-context-destructure-after-next.entries | 4 ++ .../js/esm-context-destructure-after-next.tsv | 7 +++ .../js/esm-context-early-return-next.entries | 5 ++ .../js/esm-context-early-return-next.tsv | 8 +++ .../cases/context/closure-access/src/index.ts | 20 ++++++++ .../context/destructured-var/src/index.ts | 25 ++++++++++ .../context/early-return-next/src/index.ts | 24 +++++++++ .../cases/context/set-after-next/src/index.ts | 18 +++++++ .../expected/context/closure-access.entries | 4 ++ .../hono/expected/context/closure-access.tsv | 7 +++ .../expected/context/destructured-var.entries | 4 ++ .../expected/context/destructured-var.tsv | 10 ++++ .../context/early-return-next.entries | 5 ++ .../expected/context/early-return-next.tsv | 8 +++ .../expected/context/set-after-next.entries | 3 ++ .../hono/expected/context/set-after-next.tsv | 8 +++ .../typescript/hono/truth/hono-oracle.mjs | 49 ++++++++++++++++--- .../framework-behavior/http-router-chain.dl | 27 +++++++++- 24 files changed, 327 insertions(+), 9 deletions(-) create mode 100644 graph/test/javascript/hono/cases/js/esm-context-closure-access/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/esm-context-destructure-after-next/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/esm-context-early-return-next/src/index.js create mode 100644 graph/test/javascript/hono/expected/js/esm-context-closure-access.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-context-closure-access.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-context-destructure-after-next.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-context-destructure-after-next.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-context-early-return-next.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-context-early-return-next.tsv create mode 100644 graph/test/typescript/hono/cases/context/closure-access/src/index.ts create mode 100644 graph/test/typescript/hono/cases/context/destructured-var/src/index.ts create mode 100644 graph/test/typescript/hono/cases/context/early-return-next/src/index.ts create mode 100644 graph/test/typescript/hono/cases/context/set-after-next/src/index.ts create mode 100644 graph/test/typescript/hono/expected/context/closure-access.entries create mode 100644 graph/test/typescript/hono/expected/context/closure-access.tsv create mode 100644 graph/test/typescript/hono/expected/context/destructured-var.entries create mode 100644 graph/test/typescript/hono/expected/context/destructured-var.tsv create mode 100644 graph/test/typescript/hono/expected/context/early-return-next.entries create mode 100644 graph/test/typescript/hono/expected/context/early-return-next.tsv create mode 100644 graph/test/typescript/hono/expected/context/set-after-next.entries create mode 100644 graph/test/typescript/hono/expected/context/set-after-next.tsv diff --git a/graph/javascript/engine/framework-behavior/http-router-chain.dl b/graph/javascript/engine/framework-behavior/http-router-chain.dl index f7977dc0c..b66361b24 100644 --- a/graph/javascript/engine/framework-behavior/http-router-chain.dl +++ b/graph/javascript/engine/framework-behavior/http-router-chain.dl @@ -24,7 +24,15 @@ jb_set_at(m, key, ce) :- jb_set_site(m, ce, k), expr_value(k, "str", key). .decl jb_get_at(c0:symbol, c1:symbol, c2:symbol) jb_get_at(m, key, ce) :- jb_get_site(m, ce, k), expr_value(k, "str", key). jb_get_at(m, key, e) :- expr_kind(_, "PROPERTY_ACCESS", _, e), expr_name(_, key, e), expr_child(_, e, "ACCESS_TARGET", _, v), - expr_kind(_, "PROPERTY_ACCESS", _, v), expr_name(_, "var", v), expr_child(_, v, "ACCESS_TARGET", _, c), jb_ctx_ref(c, m). + jb_var_expr(v, m). +// `const { K } = c.var`: each destructured name is a read of that key, at the `c.var`. The IR keeps +// the name a property is bound to, not the property: a renamed one (`{ K: x }`) reads x, which no +// setter writes, so it is declared (no_setter_on_chain), never linked to the wrong key. +jb_get_at(m, key, v) :- var_binding_form("client", "OBJECT_PATTERN", h), var_init("client", _, v, h), jb_var_expr(v, m), + var_decl("client", key, _, _, _, _, h). +// `c.var`, on a context +.decl jb_var_expr(c0:symbol, c1:symbol) +jb_var_expr(v, m) :- expr_kind(_, "PROPERTY_ACCESS", _, v), expr_name(_, "var", v), expr_child(_, v, "ACCESS_TARGET", _, c), jb_ctx_ref(c, m). .decl jb_keyed(c0:symbol) jb_keyed(k) :- jb_set_site(_, _, k), expr_value(k, "str", _). jb_keyed(k) :- jb_get_site(_, _, k), expr_value(k, "str", _). @@ -46,7 +54,24 @@ jb_chain(h, vv, p, h, 100000) :- http_route(h, vv, p, _, _). // ctx_key_flow(Setter, Reader, Key) — on some route's chain, Setter runs before Reader // (or is Reader, setting before it reads) and writes the context key Reader reads. -ctx_key_flow(s, r, key) :- jb_set(s, key), jb_get(r, key), jb_chain(h, vv, p, s, i), jb_chain(h, vv, p, r, j), i < j. +ctx_key_flow(s, r, key) :- jb_set_live(s, key), jb_get(r, key), jb_chain(h, vv, p, s, i), jb_chain(h, vv, p, r, j), i < j. +// a set a middleware makes after calling its `next` runs once every later function on the chain has +// returned: only the middleware itself reads it back (jb_set_before_get) +.decl jb_set_live(c0:symbol, c1:symbol) +jb_set_live(m, key) :- jb_set_at(m, key, ce), !jb_after_next(m, ce). +.decl jb_next_call(c0:symbol, c1:symbol) +jb_next_call(m, nc) :- jb_set_at(m, _, _), param_decl("client", _, "1", m, p), expr_param(_, p, n), + call_site(_, "FUNCTION_CALL", _, _, _, _, nc, _, _), expr_child(_, nc, "CALLEE", _, n). +.decl jb_after_next(c0:symbol, c1:symbol) +jb_after_next(m, ce) :- jb_set_at(m, _, ce), jb_next_call(m, nc), !jb_next_guarded(m, nc), expr_span(_, nl, nc0, _, _, nc), jb_pos(ce, sl, sc), + (to_number(nl) < sl ; to_number(nl) = sl, to_number(nc0) < sc). +// a `next()` in a branch, a loop or a try (`if (skip) return next()`) of the function itself may not have run +// before a later set: a block of m whose span holds the call +.decl jb_next_guarded(c0:symbol, c1:symbol) +jb_next_guarded(m, nc) :- jb_next_call(m, nc), expr_span(_, l, c, _, _, nc), block_owner(k, m, b), block_span(_, _, l1, c1, l2, c2, b), + (jr_cond_kind(k) ; jr_loop_kind(k) ; k = "TRY"), + (to_number(l) > to_number(l1) ; to_number(l) = to_number(l1), to_number(c) >= to_number(c1)), + (to_number(l) < to_number(l2) ; to_number(l) = to_number(l2), to_number(c) <= to_number(c2)). ctx_key_flow(s, s, key) :- jb_set_before_get(s, key), jb_chain(_, _, _, s, _). .decl jb_read_served(c0:symbol, c1:symbol) jb_read_served(r, key) :- ctx_key_flow(_, r, key). diff --git a/graph/test/javascript/hono/cases/js/esm-context-closure-access/src/index.js b/graph/test/javascript/hono/cases/js/esm-context-closure-access/src/index.js new file mode 100644 index 000000000..dec047197 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-context-closure-access/src/index.js @@ -0,0 +1,19 @@ +// census: ctx_set_in_callback, ctx_get_in_callback +// A middleware writes a key, and a handler reads one, inside callbacks nested in them: the callbacks +// run during the request with the outer function's context, so the key still flows. +import { Hono } from 'hono' + +const app = new Hono() + +app.use(async (c, next) => { + await Promise.all([1].map(async () => c.set('user', 'u1'))) + c.set('role', 'admin') + await next() +}) +app.get('/', async (c) => { + const user = await Promise.resolve().then(() => c.get('user')) + const role = [0].map(() => c.var.role)[0] + return c.json({ user, role }) +}) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-context-destructure-after-next/src/index.js b/graph/test/javascript/hono/cases/js/esm-context-destructure-after-next/src/index.js new file mode 100644 index 000000000..b7917b867 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-context-destructure-after-next/src/index.js @@ -0,0 +1,18 @@ +// census: ctx_set, ctx_get_destructured, ctx_set_after_next +// Untyped JavaScript: a middleware sets a key before handing on and another after the chain has +// returned; the handler reads the first by destructuring and asks for the second by name. +import { Hono } from 'hono' + +const app = new Hono() + +app.use(async (c, next) => { + c.set('user', { id: 7 }) + await next() + c.set('done', true) +}) +app.get('/me', (c) => { + const { user } = c.var + return c.json({ id: user.id, done: c.get('done') }) +}) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-context-early-return-next/src/index.js b/graph/test/javascript/hono/cases/js/esm-context-early-return-next/src/index.js new file mode 100644 index 000000000..8b3b145a5 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-context-early-return-next/src/index.js @@ -0,0 +1,23 @@ +// census: ctx_set, ctx_get, ctx_next_in_branch +// A middleware that hands on early for some requests (`if (..) return next()`) and sets a key +// on the others: the set is before the `next()` that runs on its path, so the handler reads it. +import { Hono } from 'hono' + +const app = new Hono() + +app.use(async (c, next) => { + if (c.req.path === '/health') return next() + c.set('user', 'u1') + await next() +}) +app.use(async (c, next) => { + if (c.req.header('x-skip')) { + await next() + return + } + c.set('role', 'admin') + await next() +}) +app.get('/', (c) => c.json({ user: c.get('user'), role: c.var.role })) + +export default app diff --git a/graph/test/javascript/hono/expected/js/esm-context-closure-access.entries b/graph/test/javascript/hono/expected/js/esm-context-closure-access.entries new file mode 100644 index 000000000..39e105359 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-context-closure-access.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user is set, and both keys are read, in callbacks nested in the middleware and the handler; they run during the request, so both keys reach the handler +# census: ctx_set_in_callback, ctx_get_in_callback +entry index.js:13:14 http +entry index.js:8:9 http diff --git a/graph/test/javascript/hono/expected/js/esm-context-closure-access.tsv b/graph/test/javascript/hono/expected/js/esm-context-closure-access.tsv new file mode 100644 index 000000000..66916cb82 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-context-closure-access.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user is set, and both keys are read, in callbacks nested in the middleware and the handler; they run during the request, so both keys reach the handler +# census: ctx_set_in_callback, ctx_get_in_callback +route GET / index.js:13:14 index.js:6:13 1 +mw index.js:8:9 index.js:13:14 GET / 0 index.js:6:13 +ctx index.js:8:9 index.js:13:14 role +ctx index.js:8:9 index.js:13:14 user +regby index.js:1:1 index.js:13:14 GET / index.js:6:13 diff --git a/graph/test/javascript/hono/expected/js/esm-context-destructure-after-next.entries b/graph/test/javascript/hono/expected/js/esm-context-destructure-after-next.entries new file mode 100644 index 000000000..ab420b090 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-context-destructure-after-next.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user flows to the handler through a destructured c.var; done is set after next() and nothing downstream reads it +# census: ctx_set, ctx_get_destructured, ctx_set_after_next +entry index.js:13:16 http +entry index.js:8:9 http diff --git a/graph/test/javascript/hono/expected/js/esm-context-destructure-after-next.tsv b/graph/test/javascript/hono/expected/js/esm-context-destructure-after-next.tsv new file mode 100644 index 000000000..d07ba613c --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-context-destructure-after-next.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user flows to the handler through a destructured c.var; done is set after next() and nothing downstream reads it +# census: ctx_set, ctx_get_destructured, ctx_set_after_next +route GET /me index.js:13:16 index.js:6:13 1 +mw index.js:8:9 index.js:13:16 GET /me 0 index.js:6:13 +ctx index.js:8:9 index.js:13:16 user +regby index.js:1:1 index.js:13:16 GET /me index.js:6:13 +unknown index.js:13:16 no_setter_on_chain index.js:13:16 diff --git a/graph/test/javascript/hono/expected/js/esm-context-early-return-next.entries b/graph/test/javascript/hono/expected/js/esm-context-early-return-next.entries new file mode 100644 index 000000000..f82d4f463 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-context-early-return-next.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user and role are set before the next() that runs on their path (the early next() is in a branch), so both reach the handler +# census: ctx_set, ctx_get, ctx_next_in_branch +entry index.js:13:9 http +entry index.js:21:14 http +entry index.js:8:9 http diff --git a/graph/test/javascript/hono/expected/js/esm-context-early-return-next.tsv b/graph/test/javascript/hono/expected/js/esm-context-early-return-next.tsv new file mode 100644 index 000000000..1a8c61582 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-context-early-return-next.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user and role are set before the next() that runs on their path (the early next() is in a branch), so both reach the handler +# census: ctx_set, ctx_get, ctx_next_in_branch +route GET / index.js:21:14 index.js:6:13 2 +mw index.js:8:9 index.js:21:14 GET / 0 index.js:6:13 +mw index.js:13:9 index.js:21:14 GET / 1 index.js:6:13 +ctx index.js:13:9 index.js:21:14 role +ctx index.js:8:9 index.js:21:14 user +regby index.js:1:1 index.js:21:14 GET / index.js:6:13 diff --git a/graph/test/typescript/hono/cases/context/closure-access/src/index.ts b/graph/test/typescript/hono/cases/context/closure-access/src/index.ts new file mode 100644 index 000000000..c23adc7e2 --- /dev/null +++ b/graph/test/typescript/hono/cases/context/closure-access/src/index.ts @@ -0,0 +1,20 @@ +// census: ctx_set_in_callback, ctx_get_in_callback +// A middleware writes a key, and a handler reads one, inside callbacks nested in them: the callbacks +// run during the request with the outer function's context, so the key still flows. +import { Hono } from 'hono' + +type Env = { Variables: { user: string; role: string } } +const app = new Hono() + +app.use(async (c, next) => { + await Promise.all([1].map(async () => c.set('user', 'u1'))) + c.set('role', 'admin') + await next() +}) +app.get('/', async (c) => { + const user = await Promise.resolve().then(() => c.get('user')) + const role = [0].map(() => c.var.role)[0] + return c.json({ user, role }) +}) + +export default app diff --git a/graph/test/typescript/hono/cases/context/destructured-var/src/index.ts b/graph/test/typescript/hono/cases/context/destructured-var/src/index.ts new file mode 100644 index 000000000..bd978fcd4 --- /dev/null +++ b/graph/test/typescript/hono/cases/context/destructured-var/src/index.ts @@ -0,0 +1,25 @@ +// census: ctx_set, ctx_get_destructured, factory_init_app +// A middleware installed by the factory sets two keys; one handler reads them by destructuring +// the context's variables, another by name. +import { createFactory } from 'hono/factory' + +type Env = { Variables: { db: { url: string }; origin: string } } + +const factory = createFactory({ + initApp: (app) => { + app.use(async (c, next) => { + c.set('db', { url: 'pg://local' }) + c.set('origin', 'https://example.test') + await next() + }) + }, +}) + +const app = factory.createApp() +app.get('/both', (c) => { + const { db, origin: from } = c.var + return c.json({ url: db.url, from }) +}) +app.get('/one', (c) => c.text(c.var.origin)) + +export default app diff --git a/graph/test/typescript/hono/cases/context/early-return-next/src/index.ts b/graph/test/typescript/hono/cases/context/early-return-next/src/index.ts new file mode 100644 index 000000000..6484b04c8 --- /dev/null +++ b/graph/test/typescript/hono/cases/context/early-return-next/src/index.ts @@ -0,0 +1,24 @@ +// census: ctx_set, ctx_get, ctx_next_in_branch +// A middleware that hands on early for some requests (`if (..) return next()`) and sets a key +// on the others: the set is before the `next()` that runs on its path, so the handler reads it. +import { Hono } from 'hono' + +type Env = { Variables: { user: string; role: string } } +const app = new Hono() + +app.use(async (c, next) => { + if (c.req.path === '/health') return next() + c.set('user', 'u1') + await next() +}) +app.use(async (c, next) => { + if (c.req.header('x-skip')) { + await next() + return + } + c.set('role', 'admin') + await next() +}) +app.get('/', (c) => c.json({ user: c.get('user'), role: c.var.role })) + +export default app diff --git a/graph/test/typescript/hono/cases/context/set-after-next/src/index.ts b/graph/test/typescript/hono/cases/context/set-after-next/src/index.ts new file mode 100644 index 000000000..fae73db13 --- /dev/null +++ b/graph/test/typescript/hono/cases/context/set-after-next/src/index.ts @@ -0,0 +1,18 @@ +// census: ctx_set, ctx_get, ctx_set_after_next +// A middleware sets one key before handing on and another after the rest of the chain has +// returned: only the first is there when the handler reads it. The same middleware reads its +// late key back itself. +import { Hono } from 'hono' + +type Env = { Variables: { started: number; finished: number } } +const app = new Hono() + +app.use(async (c, next) => { + c.set('started', Date.now()) + await next() + c.set('finished', Date.now()) + c.header('x-elapsed', String(c.get('finished') - c.get('started'))) +}) +app.get('/', (c) => c.json({ started: c.var.started, finished: c.get('finished') })) + +export default app diff --git a/graph/test/typescript/hono/expected/context/closure-access.entries b/graph/test/typescript/hono/expected/context/closure-access.entries new file mode 100644 index 000000000..f64b92a4d --- /dev/null +++ b/graph/test/typescript/hono/expected/context/closure-access.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user is set, and both keys are read, in callbacks nested in the middleware and the handler; they run during the request, so both keys reach the handler +# census: ctx_set_in_callback, ctx_get_in_callback +entry index.ts:14:14 http +entry index.ts:9:9 http diff --git a/graph/test/typescript/hono/expected/context/closure-access.tsv b/graph/test/typescript/hono/expected/context/closure-access.tsv new file mode 100644 index 000000000..f9929bc42 --- /dev/null +++ b/graph/test/typescript/hono/expected/context/closure-access.tsv @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user is set, and both keys are read, in callbacks nested in the middleware and the handler; they run during the request, so both keys reach the handler +# census: ctx_set_in_callback, ctx_get_in_callback +route GET / index.ts:14:14 index.ts:7:13 1 +mw index.ts:9:9 index.ts:14:14 GET / 0 index.ts:7:13 +ctx index.ts:9:9 index.ts:14:14 role +ctx index.ts:9:9 index.ts:14:14 user +regby index.ts:1:1 index.ts:14:14 GET / index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/context/destructured-var.entries b/graph/test/typescript/hono/expected/context/destructured-var.entries new file mode 100644 index 000000000..32ba362af --- /dev/null +++ b/graph/test/typescript/hono/expected/context/destructured-var.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: db and origin flow to /both through a destructured c.var, origin to /one by name +entry index.ts:10:13 http +entry index.ts:19:18 http +entry index.ts:23:17 http diff --git a/graph/test/typescript/hono/expected/context/destructured-var.tsv b/graph/test/typescript/hono/expected/context/destructured-var.tsv new file mode 100644 index 000000000..4ef165c30 --- /dev/null +++ b/graph/test/typescript/hono/expected/context/destructured-var.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: db and origin flow to /both through a destructured c.var, origin to /one by name +route GET /both index.ts:19:18 index.ts:18:13 1 +route GET /one index.ts:23:17 index.ts:18:13 2 +mw index.ts:10:13 index.ts:19:18 GET /both 0 index.ts:18:13 +mw index.ts:10:13 index.ts:23:17 GET /one 0 index.ts:18:13 +ctx index.ts:10:13 index.ts:19:18 db +ctx index.ts:10:13 index.ts:19:18 origin +ctx index.ts:10:13 index.ts:23:17 origin +regby index.ts:1:1 index.ts:19:18 GET /both index.ts:18:13 +regby index.ts:1:1 index.ts:23:17 GET /one index.ts:18:13 diff --git a/graph/test/typescript/hono/expected/context/early-return-next.entries b/graph/test/typescript/hono/expected/context/early-return-next.entries new file mode 100644 index 000000000..5d9103391 --- /dev/null +++ b/graph/test/typescript/hono/expected/context/early-return-next.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user and role are set before the next() that runs on their path (the early next() is in a branch), so both reach the handler +# census: ctx_set, ctx_get, ctx_next_in_branch +entry index.ts:14:9 http +entry index.ts:22:14 http +entry index.ts:9:9 http diff --git a/graph/test/typescript/hono/expected/context/early-return-next.tsv b/graph/test/typescript/hono/expected/context/early-return-next.tsv new file mode 100644 index 000000000..88c18baf9 --- /dev/null +++ b/graph/test/typescript/hono/expected/context/early-return-next.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: user and role are set before the next() that runs on their path (the early next() is in a branch), so both reach the handler +# census: ctx_set, ctx_get, ctx_next_in_branch +route GET / index.ts:22:14 index.ts:7:13 2 +mw index.ts:9:9 index.ts:22:14 GET / 0 index.ts:7:13 +mw index.ts:14:9 index.ts:22:14 GET / 1 index.ts:7:13 +ctx index.ts:14:9 index.ts:22:14 role +ctx index.ts:9:9 index.ts:22:14 user +regby index.ts:1:1 index.ts:22:14 GET / index.ts:7:13 diff --git a/graph/test/typescript/hono/expected/context/set-after-next.entries b/graph/test/typescript/hono/expected/context/set-after-next.entries new file mode 100644 index 000000000..a95fce9ad --- /dev/null +++ b/graph/test/typescript/hono/expected/context/set-after-next.entries @@ -0,0 +1,3 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: started flows to the handler; finished is set after next() and is read back only by the middleware itself +entry index.ts:10:9 http +entry index.ts:16:14 http diff --git a/graph/test/typescript/hono/expected/context/set-after-next.tsv b/graph/test/typescript/hono/expected/context/set-after-next.tsv new file mode 100644 index 000000000..e38eca2eb --- /dev/null +++ b/graph/test/typescript/hono/expected/context/set-after-next.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-29 against the fixture, the oracle and a run of the pinned hono: started flows to the handler; finished is set after next() and is read back only by the middleware itself +route GET / index.ts:16:14 index.ts:8:13 1 +mw index.ts:10:9 index.ts:16:14 GET / 0 index.ts:8:13 +ctx index.ts:10:9 index.ts:10:9 finished +ctx index.ts:10:9 index.ts:10:9 started +ctx index.ts:10:9 index.ts:16:14 started +regby index.ts:1:1 index.ts:16:14 GET / index.ts:8:13 +unknown index.ts:16:14 no_setter_on_chain index.ts:16:14 diff --git a/graph/test/typescript/hono/truth/hono-oracle.mjs b/graph/test/typescript/hono/truth/hono-oracle.mjs index 031f706f2..64ecb3d15 100644 --- a/graph/test/typescript/hono/truth/hono-oracle.mjs +++ b/graph/test/typescript/hono/truth/hono-oracle.mjs @@ -1662,18 +1662,54 @@ function notePos(f, k, isSet, pos) { else if (!lastGet.has(key) || pos > lastGet.get(key)) lastGet.set(key, pos); } const selfFlows = (f, k) => firstSet.has(f + '\t' + k) && lastGet.has(f + '\t' + k) && firstSet.get(f + '\t' + k) < lastGet.get(f + '\t' + k); +// a set written after the middleware called `next` runs once every later function on the chain has +// returned: it is read back only in its own function (fnloc -> Set(key) of the sets made before `next`) +const setsBeforeNext = new Map(); +// the function whose context `c` is: a read or a write in a callback nested in it (`db.transaction(async +// (tx) => c.get(K))`) runs during the request, on that function's behalf +function ctxFnNode(c, n) { + const d = ts.isIdentifier(c) && declOf(c); + if (d && ts.isParameter(d) && ts.isFunctionLike(d.parent) && d.parent.body) return d.parent; + let fn = n.parent; + while (fn && !(ts.isFunctionLike(fn) && fn.body)) fn = fn.parent; + return fn; +} +const ctxFn = (c, n) => { const fn = ctxFnNode(c, n); return fn ? loc(fn) : enclosingFn(n); }; +function afterNext(n, c) { + const fn = ctxFnNode(c, n); + const p = fn && fn.parameters && fn.parameters[1]; + if (!p || !ts.isIdentifier(p.name)) return false; + const nextDecl = p; + let found = false; + // only a `next()` that runs on every path to the set: not in a branch, a loop, a try or a nested function + const guard = (x) => ts.isIfStatement(x) || ts.isConditionalExpression(x) || ts.isSwitchStatement(x) || ts.isIterationStatement(x, false) + || ts.isTryStatement(x) || ts.isFunctionLike(x) + || (ts.isBinaryExpression(x) && [ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken].includes(x.operatorToken.kind)); + (function walk(x) { + if (found || x.getStart() >= n.getStart() || guard(x)) return; + if (ts.isCallExpression(x) && ts.isIdentifier(x.expression) && declOf(x.expression) === nextDecl && x.getEnd() <= n.getStart()) { found = true; return; } + ts.forEachChild(x, walk); + })(fn.body); + return found; +} +function noteGet(f, k, pos) { if (!gets.has(f)) gets.set(f, new Set()); gets.get(f).add(k); notePos(f, k, false, pos); } +const isCtxVar = (e) => ts.isPropertyAccessExpression(e) && e.name.text === 'var' && isContext(e.expression); function scanCtx(n) { if (ts.isCallExpression(n) && ts.isPropertyAccessExpression(n.expression) && ['set', 'get'].includes(n.expression.name.text) && n.arguments[0] && ts.isStringLiteralLike(n.arguments[0]) && isContext(n.expression.expression)) { const isSet = n.expression.name.text === 'set'; const m = isSet ? sets : gets; - const f = enclosingFn(n); if (!m.has(f)) m.set(f, new Set()); m.get(f).add(n.arguments[0].text); + const f = ctxFn(n.expression.expression, n); if (!m.has(f)) m.set(f, new Set()); m.get(f).add(n.arguments[0].text); + if (isSet && !afterNext(n, n.expression.expression)) { if (!setsBeforeNext.has(f)) setsBeforeNext.set(f, new Set()); setsBeforeNext.get(f).add(n.arguments[0].text); } notePos(f, n.arguments[0].text, isSet, n.getStart()); } - if (ts.isPropertyAccessExpression(n) && ts.isPropertyAccessExpression(n.expression) && n.expression.name.text === 'var' - && isContext(n.expression.expression)) { - const f = enclosingFn(n); if (!gets.has(f)) gets.set(f, new Set()); gets.get(f).add(n.name.text); - notePos(f, n.name.text, false, n.getStart()); + if (ts.isPropertyAccessExpression(n) && isCtxVar(n.expression)) noteGet(ctxFn(n.expression.expression, n), n.name.text, n.getStart()); + // `const { K } = c.var`: each destructured property is a read of that key + if (ts.isVariableDeclaration(n) && n.initializer && ts.isObjectBindingPattern(n.name) && isCtxVar(n.initializer)) { + for (const el of n.name.elements) { + const k = el.propertyName ? el.propertyName.getText() : (ts.isIdentifier(el.name) ? el.name.text : null); + if (k && !el.dotDotDotToken) noteGet(ctxFn(n.initializer.expression, n), k, n.initializer.getStart()); + } } ts.forEachChild(n, scanCtx); } @@ -1681,7 +1717,8 @@ srcFiles.forEach(scanCtx); for (const { handler, chain } of chains) { const seq = [...chain, handler]; seq.forEach((s, i) => { - for (const k of sets.get(s) || []) for (let j = i; j < seq.length; j++) if ((gets.get(seq[j]) || new Set()).has(k) && (j > i || selfFlows(s, k))) out.ctx.add(`${s}\t${seq[j]}\t${k}`); + for (const k of sets.get(s) || []) for (let j = i; j < seq.length; j++) if ((gets.get(seq[j]) || new Set()).has(k) + && (j > i ? (setsBeforeNext.get(s) || new Set()).has(k) : selfFlows(s, k))) out.ctx.add(`${s}\t${seq[j]}\t${k}`); }); } // SPEC R23: a partial route's DECIDED chain (its `mwp` rows; a may-apply middleware is not in it) carries context keys diff --git a/graph/typescript/engine/framework-behavior/http-router-chain.dl b/graph/typescript/engine/framework-behavior/http-router-chain.dl index e61729d5e..7859918d5 100644 --- a/graph/typescript/engine/framework-behavior/http-router-chain.dl +++ b/graph/typescript/engine/framework-behavior/http-router-chain.dl @@ -41,6 +41,12 @@ hb_get_at(m, key, ce) :- hb_get_site(m, ce, k), rd_val(k, key), !contains("{}", // `c.var.K` hb_get_at(m, key, e) :- property_access_name(e, key), expr_child("client", e, "RECEIVER", _, v), property_access_name(v, "var"), expr_child("client", v, "RECEIVER", _, c), hb_ctx_ref(c, m). +// `const { K } = c.var` (and `{ K: x }`): each destructured property is a read of that key, at the `c.var` +hb_get_at(m, key, v) :- ts_variable(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, v, _, _, "true", "PROPERTY", key, _, _, _, _), + hb_var_expr(v, m). +// `c.var`, on a context +.decl hb_var_expr(c0:symbol, c1:symbol) +hb_var_expr(v, m) :- property_access_name(v, "var"), expr_child("client", v, "RECEIVER", _, c), hb_ctx_ref(c, m). .decl hb_set(c0:symbol, c1:symbol) hb_set(m, key) :- hb_set_at(m, key, _). .decl hb_get(c0:symbol, c1:symbol) @@ -65,7 +71,26 @@ hb_chain(h, vv, p, h, 100000) :- http_route(h, vv, p, _, _). // ctx_key_flow(Setter, Reader, Key) — on some route's chain, Setter runs before Reader // (or is Reader) and writes the context key Reader reads. -ctx_key_flow(s, r, key) :- hb_set(s, key), hb_get(r, key), hb_chain(h, vv, p, s, i), hb_chain(h, vv, p, r, j), i < j. +ctx_key_flow(s, r, key) :- hb_set_live(s, key), hb_get(r, key), hb_chain(h, vv, p, s, i), hb_chain(h, vv, p, r, j), i < j. +// a set a middleware makes after calling its `next` runs once every later function on the chain has +// returned: only the middleware itself reads it back (hb_set_before_get) +.decl hb_set_live(c0:symbol, c1:symbol) +hb_set_live(m, key) :- hb_set_at(m, key, ce), !hb_after_next(m, ce). +.decl hb_next_call(c0:symbol, c1:symbol) +hb_next_call(m, nc) :- hb_set_at(m, _, _), param_decl("client", _, "1", _, m, p), expr_referenced("client", "PARAMETER", p, n), + call_callee_expr(_, nc, n). +.decl hb_next_pos(c0:symbol, c1:number, c2:number) +hb_next_pos(nc, to_number(l), to_number(c)) :- hb_next_call(_, nc), + ts_expression(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, l, c, _, _, _, _, _, _, _, _, _, _, _, nc). +.decl hb_after_next(c0:symbol, c1:symbol) +hb_after_next(m, ce) :- hb_set_at(m, _, ce), hb_next_call(m, nc), !hb_next_guarded(nc), hb_next_pos(nc, nl, ncol), hb_pos(ce, sl, sc), + (nl < sl ; nl = sl, ncol < sc). +// a `next()` in a branch, a loop or a try (`if (skip) return next()`) may not have run before a later set +.decl hb_next_up(c0:symbol, c1:symbol, c2:symbol) +hb_next_up(m, nc, b) :- hb_next_call(m, nc), expr_owner("client", "BLOCK", b, _, nc), block_owner("client", m, _, b). +hb_next_up(m, nc, p) :- hb_next_up(m, nc, b), block_parent("client", p, b), block_owner("client", m, _, p). +.decl hb_next_guarded(c0:symbol) +hb_next_guarded(nc) :- hb_next_up(_, nc, b), block_kind("client", k, b), (hr_cond_kind(k) ; hr_loop_kind(k) ; k = "TRY"). ctx_key_flow(s, s, key) :- hb_set_before_get(s, key), hb_chain(_, _, _, s, _). framework_undetermined(r, "ctx_key", "no_setter_on_chain") :- hb_get(r, key), hb_chain(_, _, _, r, _), From e1b0ad7373602fa1b6bfde26a0bac9cdae8bee71 Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:26:54 -0700 Subject: [PATCH 3/9] typescript, javascript: Hono sub-apps read off a returned object, openapi routes built through a project wrapper, one route row per route, and partial routes with a hole in the middle of their path Route model fixes: - A function that returns an object holding several sub-apps: each property read of that object (rt.stock, or a destructured name) mounts only its own sub-app, not every app the function created. This also holds when the object is passed into another function and destructured there. It used to put every sub-app under every mount, which made false routes. - A test helper that writes the init of app.request(path, init) in place now joins on the method it writes. A method that is not a literal verb joins any route on that path. - Middleware read off an object a factory returns (guard.middleware, or a destructured name) is the function the factory put there. - openapi(def, handler) follows a project wrapper that returns createRoute({...config, middleware}). Method and path come from the call of the wrapper, also when the wrapper passes them on through its own parameters, and middleware comes from the wrapper, including a spread of the caller's list. A definition that cannot be read is an undetermined route with the reason dynamic_route_path, never dropped. A {} hole is no longer turned into a :param segment. - JavaScript gets the openapi model: openapi(def, handler) registers the route its createRoute definition describes (verb, path with {x} read as :x, middleware), directly or through a project wrapper. It lives in a new config-resolution/http-router-openapi.dl; the framework names are knobs. - http_route keeps one row per handler, verb, path and app, with the earliest registration key. An app factory called from many places used to give one row per call path, about a thousand rows per route on a large project. - An app that a request handler serves only by calling its fetch (a forwarding handler) is a partial route with the reason unresolved_mount, not a root served at its own paths. - context answers a full path with a partial route whose unknown piece sits in the middle of its path (/v1/{}/ping), with a tests/run.py case and a control that is not that route. Hono test oracle: a brace glob alternative that holds a * is read as a glob, each row is printed once (five goldens lose repeated rows, the row set is unchanged), a destructuring initializer is evaluated once, and a case can opt into rows that pin the known path of a partial route, which the runtime check also verifies. New cases: TypeScript compose/glob-brace-alternatives, compose/partial-path-mid-hole, compose/returned-object-destructure, compose/returned-object-member, middleware/factory-object-member, openapi/create-route-wrapper, rpc/helper-request-method; JavaScript js/cjs-factory-object-mw, js/cjs-returned-object-member, js/esm-create-route-wrapper, js/esm-helper-request-method, js/esm-openapi-create-route, js/esm-returned-object-destructure. --- .../config-resolution/http-router-apps.dl | 2 + .../config-resolution/http-router-openapi.dl | 104 ++++++++++++ .../engine/config-resolution/knobs.dl | 11 ++ .../js/cjs-factory-object-mw/src/checks.js | 16 ++ .../js/cjs-factory-object-mw/src/index.js | 15 ++ .../js/cjs-factory-object-mw/src/package.json | 1 + .../cjs-returned-object-member/src/index.js | 13 ++ .../src/package.json | 1 + .../src/routes/orders.js | 10 ++ .../src/routes/stock.js | 10 ++ .../cjs-returned-object-member/src/runtime.js | 10 ++ .../js/esm-create-route-wrapper/src/index.js | 17 ++ .../esm-create-route-wrapper/src/openapi.js | 13 ++ .../esm-create-route-wrapper/src/package.json | 1 + .../js/esm-helper-request-method/src/auth.js | 8 + .../src/package.json | 1 + .../src/test/auth.test.js | 24 +++ .../js/esm-openapi-create-route/src/index.js | 19 +++ .../esm-openapi-create-route/src/package.json | 1 + .../js/esm-openapi-create-route/src/routes.js | 4 + .../src/index.js | 13 ++ .../src/package.json | 1 + .../src/routes/orders.js | 8 + .../src/routes/stock.js | 8 + .../src/runtime.js | 8 + .../expected/js/cjs-factory-object-mw.entries | 6 + .../expected/js/cjs-factory-object-mw.tsv | 9 ++ .../js/cjs-returned-object-member.entries | 6 + .../js/cjs-returned-object-member.tsv | 12 ++ .../js/esm-create-route-wrapper.entries | 5 + .../expected/js/esm-create-route-wrapper.tsv | 8 + .../js/esm-helper-request-method.entries | 5 + .../expected/js/esm-helper-request-method.tsv | 19 +++ .../js/esm-openapi-create-route.entries | 4 + .../expected/js/esm-openapi-create-route.tsv | 6 + .../esm-returned-object-destructure.entries | 6 + .../js/esm-returned-object-destructure.tsv | 12 ++ .../glob-brace-alternatives/src/env.ts | 7 + .../glob-brace-alternatives/src/index.ts | 13 ++ .../src/stores/disk-store/routes.ts | 6 + .../src/stores/legacy/routes.ts | 6 + .../src/stores/memory/routes.ts | 6 + .../src/stores/remote-store/routes.ts | 6 + .../partial-path-mid-hole/partial-paths | 1 + .../compose/partial-path-mid-hole/src/main.ts | 25 +++ .../partial-path-mid-hole/src/system.ts | 9 ++ .../returned-object-destructure/src/index.ts | 17 ++ .../src/routes/audit.ts | 7 + .../src/routes/orders.ts | 8 + .../src/routes/stock.ts | 8 + .../src/runtime.ts | 11 ++ .../returned-object-member/src/index.ts | 17 ++ .../src/routes/audit.ts | 7 + .../src/routes/orders.ts | 8 + .../src/routes/stock.ts | 8 + .../returned-object-member/src/runtime.ts | 11 ++ .../factory-object-member/src/checks.ts | 16 ++ .../factory-object-member/src/index.ts | 18 +++ .../factory-object-member/src/limiter.ts | 18 +++ .../openapi/create-route-wrapper/src/index.ts | 11 ++ .../openapi/create-route-wrapper/src/items.ts | 21 +++ .../create-route-wrapper/src/openapi.ts | 16 ++ .../src/__tests__/auth.test.ts | 32 ++++ .../rpc/helper-request-method/src/auth.ts | 9 ++ .../compose/filtered-instance-loop.tsv | 2 +- .../compose/glob-brace-alternatives.entries | 6 + .../compose/glob-brace-alternatives.tsv | 11 ++ .../expected/compose/glob-module-list.tsv | 2 +- .../compose/partial-path-mid-hole.entries | 8 + .../compose/partial-path-mid-hole.tsv | 16 ++ .../compose/plugin-shared-test-and-prod.tsv | 3 +- .../returned-object-destructure.entries | 7 + .../compose/returned-object-destructure.tsv | 16 ++ .../compose/returned-object-member.entries | 7 + .../compose/returned-object-member.tsv | 16 ++ .../compose/shared-factory-filtered-loop.tsv | 2 +- .../middleware/factory-object-member.entries | 7 + .../middleware/factory-object-member.tsv | 11 ++ .../middleware/helper-app-mounted-thrice.tsv | 11 +- .../openapi/create-route-wrapper.entries | 6 + .../expected/openapi/create-route-wrapper.tsv | 11 ++ .../rpc/helper-request-method.entries | 6 + .../expected/rpc/helper-request-method.tsv | 24 +++ graph/test/typescript/hono/run-tests.sh | 9 +- .../typescript/hono/tools/hono_compare.py | 8 +- .../typescript/hono/truth/hono-oracle.mjs | 60 +++++-- .../typescript/hono/truth/runtime-check.mjs | 21 ++- .../config-resolution/http-router-apps.dl | 47 ++++-- .../config-resolution/http-router-routes.dl | 149 ++++++++++++++++-- .../framework-behavior/http-rpc-client.dl | 7 + .../axiomcode/scripts/axiomcode-context | 24 ++- .../route-hole-in-the-middle/case.json | 9 ++ .../route-hole-in-the-middle/src/main.ts | 15 ++ .../route-hole-in-the-middle/src/system.ts | 5 + 94 files changed, 1205 insertions(+), 60 deletions(-) create mode 100644 graph/javascript/engine/config-resolution/http-router-openapi.dl create mode 100644 graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/checks.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/routes/orders.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/routes/stock.js create mode 100644 graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/runtime.js create mode 100644 graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/openapi.js create mode 100644 graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/esm-helper-request-method/src/auth.js create mode 100644 graph/test/javascript/hono/cases/js/esm-helper-request-method/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/esm-helper-request-method/src/test/auth.test.js create mode 100644 graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/routes.js create mode 100644 graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/index.js create mode 100644 graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/package.json create mode 100644 graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/routes/orders.js create mode 100644 graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/routes/stock.js create mode 100644 graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/runtime.js create mode 100644 graph/test/javascript/hono/expected/js/cjs-factory-object-mw.entries create mode 100644 graph/test/javascript/hono/expected/js/cjs-factory-object-mw.tsv create mode 100644 graph/test/javascript/hono/expected/js/cjs-returned-object-member.entries create mode 100644 graph/test/javascript/hono/expected/js/cjs-returned-object-member.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-create-route-wrapper.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-create-route-wrapper.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-helper-request-method.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-helper-request-method.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-openapi-create-route.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-openapi-create-route.tsv create mode 100644 graph/test/javascript/hono/expected/js/esm-returned-object-destructure.entries create mode 100644 graph/test/javascript/hono/expected/js/esm-returned-object-destructure.tsv create mode 100644 graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/env.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/disk-store/routes.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/legacy/routes.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/memory/routes.ts create mode 100644 graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/remote-store/routes.ts create mode 100644 graph/test/typescript/hono/cases/compose/partial-path-mid-hole/partial-paths create mode 100644 graph/test/typescript/hono/cases/compose/partial-path-mid-hole/src/main.ts create mode 100644 graph/test/typescript/hono/cases/compose/partial-path-mid-hole/src/system.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-destructure/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/audit.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/orders.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/stock.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-destructure/src/runtime.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-member/src/index.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/audit.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/orders.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/stock.ts create mode 100644 graph/test/typescript/hono/cases/compose/returned-object-member/src/runtime.ts create mode 100644 graph/test/typescript/hono/cases/middleware/factory-object-member/src/checks.ts create mode 100644 graph/test/typescript/hono/cases/middleware/factory-object-member/src/index.ts create mode 100644 graph/test/typescript/hono/cases/middleware/factory-object-member/src/limiter.ts create mode 100644 graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/index.ts create mode 100644 graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/items.ts create mode 100644 graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/openapi.ts create mode 100644 graph/test/typescript/hono/cases/rpc/helper-request-method/src/__tests__/auth.test.ts create mode 100644 graph/test/typescript/hono/cases/rpc/helper-request-method/src/auth.ts create mode 100644 graph/test/typescript/hono/expected/compose/glob-brace-alternatives.entries create mode 100644 graph/test/typescript/hono/expected/compose/glob-brace-alternatives.tsv create mode 100644 graph/test/typescript/hono/expected/compose/partial-path-mid-hole.entries create mode 100644 graph/test/typescript/hono/expected/compose/partial-path-mid-hole.tsv create mode 100644 graph/test/typescript/hono/expected/compose/returned-object-destructure.entries create mode 100644 graph/test/typescript/hono/expected/compose/returned-object-destructure.tsv create mode 100644 graph/test/typescript/hono/expected/compose/returned-object-member.entries create mode 100644 graph/test/typescript/hono/expected/compose/returned-object-member.tsv create mode 100644 graph/test/typescript/hono/expected/middleware/factory-object-member.entries create mode 100644 graph/test/typescript/hono/expected/middleware/factory-object-member.tsv create mode 100644 graph/test/typescript/hono/expected/openapi/create-route-wrapper.entries create mode 100644 graph/test/typescript/hono/expected/openapi/create-route-wrapper.tsv create mode 100644 graph/test/typescript/hono/expected/rpc/helper-request-method.entries create mode 100644 graph/test/typescript/hono/expected/rpc/helper-request-method.tsv create mode 100644 tests/cases/typescript/route-hole-in-the-middle/case.json create mode 100644 tests/cases/typescript/route-hole-in-the-middle/src/main.ts create mode 100644 tests/cases/typescript/route-hole-in-the-middle/src/system.ts diff --git a/graph/javascript/engine/config-resolution/http-router-apps.dl b/graph/javascript/engine/config-resolution/http-router-apps.dl index ebaff0904..41dafd035 100644 --- a/graph/javascript/engine/config-resolution/http-router-apps.dl +++ b/graph/javascript/engine/config-resolution/http-router-apps.dl @@ -53,6 +53,7 @@ jr_module(spec) :- router_mw_combinator(spec, _). jr_module(spec) :- router_mw_except(spec, _). jr_module(spec) :- router_rpc_client(spec, _). jr_module(spec) :- router_test_client(spec, _). +jr_module(spec) :- router_route_def(spec, _). // jr_ref(Expr, Specifier, Name): Expr denotes export Name of a knob module ("" = the // module itself), through an import, a require, a destructure or a member read @@ -85,6 +86,7 @@ jr_registrar(n) :- router_on(n). jr_registrar(n) :- router_mount_app(n). jr_registrar(n) :- router_mount_foreign(n). jr_registrar(n) :- router_fallback(n). +jr_registrar(n) :- router_openapi(n). .decl jr_reg(c0:symbol, c1:symbol, c2:symbol) jr_reg(ce, a, cn) :- call_site(_, "METHOD_CALL", cn, _, r, _, ce, _, _), jr_registrar(cn), expr_value(r, "router_app", a). diff --git a/graph/javascript/engine/config-resolution/http-router-openapi.dl b/graph/javascript/engine/config-resolution/http-router-openapi.dl new file mode 100644 index 000000000..ad7eb77dd --- /dev/null +++ b/graph/javascript/engine/config-resolution/http-router-openapi.dl @@ -0,0 +1,104 @@ +// ============================================================================ +// CONFIG-RESOLUTION · HTTP ROUTER OPENAPI ROUTES (JavaScript) +// +// `app.openapi(def, handler, hook?)` registers the route `def` describes: verb = its `method`, path = its `path` with +// `{x}` written `:x`, the definition's `middleware` (one, or a list) running before the handler, in order. `def` is +// the object `createRoute({...})` was called with (a knob: router_route_def), reached through consts, imports and +// returns by the flow engine, or a call of a PROJECT function that returns such a definition +// (`scopedRoute(scope, { method, path })` returning `createRoute({ ...config, middleware: [..] })`). The flow engine +// has one value per object literal, so the wrapper's object would carry every caller's `method` and `path`; a +// wrapper call is read per call instead: its method and path are THAT call's argument object's, its middleware the +// wrapper's own. A registration whose definition gives no literal method and path is declared +// (dynamic_route_path), never dropped. Same contract as the TypeScript engine (SPEC 3.7). +// ============================================================================ + +// createRoute's result is its argument (the flow engine carries it through consts, imports, exports and returns) +.decl jo_root(c0:symbol, c1:symbol) +jo_root(x, o) :- jr_knob_call(x, spec, n), router_route_def(spec, n), call_arg(x, 0, a), expr_value(a, "obj", o). +expr_value(x, "obj", o) :- jo_root(x, o). +.decl jo_defobj(c0:symbol) +jo_defobj(o) :- jo_root(_, o). + +// a property written in the object itself (`key: v`, or the shorthand `key`), never one it spreads: of a definition, +// or of the object a wrapper call hands in (jo_callarg) +.decl jo_obj(c0:symbol) +jo_obj(o) :- jo_defobj(o). +jo_obj(o) :- jo_callarg(_, o). +.decl jo_own(c0:symbol, c1:symbol, c2:symbol) +jo_own(o, n, v) :- jo_obj(o), expr_child(_, o, "PROPERTY_KEY", idx, key), expr_name(_, n, key), expr_child(_, o, "PROPERTY_VALUE", idx, v). +jo_own(o, n, v) :- jo_obj(o), expr_child(_, o, "PROPERTY_VALUE", idx, v), !expr_child(_, o, "PROPERTY_KEY", idx, _), expr_name(_, n, v). + +// the registration's definition argument, followed to a call of a project wrapper: through consts in its module +.decl jo_reg(c0:symbol, c1:symbol) +jo_reg(ce, x) :- jr_reg(ce, _, cn), router_openapi(cn), call_arg(ce, 0, x). +.decl jo_at(c0:symbol, c1:symbol) +jo_at(ce, x) :- jo_reg(ce, x). +jo_at(ce, i) :- jo_at(ce, e), expr_kind(_, "IDENTIFIER", _, e), expr_binding(_, v, e), var_init(_, _, i, v), i != "". +// (registration, wrapper call, wrapper): a project function whose returned value is a createRoute call +.decl jo_wrap(c0:symbol, c1:symbol, c2:symbol) +jo_wrap(ce, x, w) :- jo_at(ce, x), expr_kind(_, "CALL", _, x), expr_resolves_to_method(x, w), + expr_root(_, "RETURN", e), expr_owner(_, w, _, e), jo_root(e, _). +.decl jo_wrapped(c0:symbol) +jo_wrapped(ce) :- jo_wrap(ce, _, _). +// the wrapper's own object, and the call's argument objects it spreads (`...config`, config a parameter) +.decl jo_inner(c0:symbol, c1:symbol, c2:symbol) +jo_inner(ce, x, o) :- jo_wrap(ce, x, w), expr_root(_, "RETURN", e), expr_owner(_, w, _, e), jo_root(e, o). +.decl jo_spread_ref(c0:symbol, c1:symbol) +jo_spread_ref(o, r) :- jo_defobj(o), expr_child(_, o, "SPREAD", _, s), expr_child(_, s, _, _, r). +jo_spread_ref(o, r) :- jo_defobj(o), expr_child(_, o, "SPREAD_OPERAND", _, r). +.decl jo_callarg(c0:symbol, c1:symbol) +jo_callarg(ce, o2) :- jo_wrap(ce, x, w), jo_inner(ce, x, o), jo_spread_ref(o, r), expr_param(_, p, r), + param_decl(_, _, pos, w, p), call_arg(x, to_number(pos), a), expr_value(a, "obj", o2). + +// (registration, object holding its method and path), (registration, object holding its middleware) +.decl jo_mp(c0:symbol, c1:symbol) +jo_mp(ce, o) :- jo_reg(ce, x), !jo_wrapped(ce), expr_value(x, "obj", o), jo_defobj(o). +jo_mp(ce, o2) :- jo_callarg(ce, o2). +.decl jo_mwobj(c0:symbol, c1:symbol) +jo_mwobj(ce, o) :- jo_reg(ce, x), !jo_wrapped(ce), expr_value(x, "obj", o), jo_defobj(o). +jo_mwobj(ce, o) :- jo_inner(ce, _, o). +.decl jo_inner_mw(c0:symbol) +jo_inner_mw(ce) :- jo_inner(ce, _, o), jo_own(o, k, _), router_def_key(k, "middleware"). +jo_mwobj(ce, o2) :- jo_callarg(ce, o2), !jo_inner_mw(ce). + +.decl jo_str(c0:symbol, c1:symbol, c2:symbol) +jo_str(ce, role, s) :- jo_mp(ce, o), jo_own(o, k, v), router_def_key(k, role), (role = "method" ; role = "path"), expr_value(v, "str", s). +.decl jo_verb(c0:symbol, c1:symbol) +jo_verb(ce, vv) :- jo_str(ce, "method", w), router_verb_name(w, vv). + +// an OpenAPI path writes a parameter `{id}`; the router registers `:id` +.decl jo_brace_step(c0:symbol, c1:number, c2:symbol) +jo_brace_step(raw, 0, "") :- jo_str(_, "path", raw). +jo_brace_step(raw, i + 1, cat(acc, ":")) :- jo_brace_step(raw, i, acc), i < strlen(raw), substr(raw, i, 1) = "{". +jo_brace_step(raw, i + 1, acc) :- jo_brace_step(raw, i, acc), i < strlen(raw), substr(raw, i, 1) = "}". +jo_brace_step(raw, i + 1, cat(acc, ch)) :- jo_brace_step(raw, i, acc), i < strlen(raw), ch = substr(raw, i, 1), + ch != "{", ch != "}". +.decl jo_path(c0:symbol, c1:symbol) +jo_path(ce, p) :- jo_str(ce, "path", raw), jo_brace_step(raw, strlen(raw), p). + +// the handler, and the definition's middleware before it (ordinals 10.. in list order) +jr_harg(ce, 100, a) :- jo_reg(ce, _), call_arg(ce, 1, a). +.decl jo_mw(c0:symbol, c1:symbol) +jo_mw(ce, v) :- jo_mwobj(ce, o), jo_own(o, k, v), router_def_key(k, "middleware"). +.decl jo_mw_arr(c0:symbol, c1:symbol) +jo_mw_arr(ce, v) :- jo_mw(ce, v), expr_kind(_, "ARRAY_LITERAL", _, v). +jo_mw_arr(ce, i) :- jo_mw(ce, v), expr_kind(_, "IDENTIFIER", _, v), expr_binding(_, d, v), var_init(_, _, i, d), i != "", + expr_kind(_, "ARRAY_LITERAL", _, i). +.decl jo_mw_listed(c0:symbol, c1:symbol) +jo_mw_listed(ce, v) :- jo_mw(ce, v), jo_mw_arr(ce, _). +jr_harg(ce, n, el) :- jo_mw_arr(ce, arr), expr_child(_, arr, _, epos, el), to_number(epos) < 90, n = 10 + to_number(epos), + !expr_kind(_, "SPREAD", _, el). +jr_harg(ce, 10, v) :- jo_mw(ce, v), !jo_mw_listed(ce, v). +// a spread in the list: decided at run time +framework_undetermined(m, "middleware", "dynamic_middleware_list") :- jo_mw_arr(ce, arr), expr_child(_, arr, _, _, el), + expr_kind(_, "SPREAD", _, el), call_site(_, _, _, _, _, m, ce, _, _). + +jr_entry_base(ce, a, "route", vv, lp, b) :- jr_reg(ce, a, cn), router_openapi(cn), jo_verb(ce, vv), jo_path(ce, lp), + jr_recv(ce, r), jr_val(r, a, b). + +// a definition this cannot read: the route exists, its verb and path are not known here +.decl jo_routed(c0:symbol) +jo_routed(ce) :- jo_verb(ce, _), jo_path(ce, _). +remote_undetermined(m, "http", "dynamic_route_path") :- jo_reg(ce, _), !jo_routed(ce), jr_node_fn(ce, 100, m). +remote_undetermined(m, "http", "dynamic_route_path") :- jo_reg(ce, _), !jo_routed(ce), !jr_has_fn(ce, 100), + call_site(_, _, _, _, _, m, ce, _, _). diff --git a/graph/javascript/engine/config-resolution/knobs.dl b/graph/javascript/engine/config-resolution/knobs.dl index ace8606f2..d4836da68 100644 --- a/graph/javascript/engine/config-resolution/knobs.dl +++ b/graph/javascript/engine/config-resolution/knobs.dl @@ -105,3 +105,14 @@ router_fetch_fn("fetch"). router_url_class("URL"). .decl router_local_host(c0:symbol) router_local_host("localhost"). router_local_host("127.0.0.1"). router_local_host("0.0.0.0"). router_local_host("[::1]"). + +// ── OpenAPI routes (config-resolution/http-router-openapi.dl) ─────────────── +// router_openapi: `app.(def, handler, hook?)` registers the route a definition describes; router_route_def: the +// function a definition is made with (`createRoute({ method, path, middleware })`), which returns its argument; +// router_def_key(Written, Role): the definition's keys +.decl router_openapi(c0:symbol) +router_openapi("openapi"). +.decl router_route_def(c0:symbol, c1:symbol) +router_route_def("@hono/zod-openapi", "createRoute"). +.decl router_def_key(c0:symbol, c1:symbol) +router_def_key("method", "method"). router_def_key("path", "path"). router_def_key("middleware", "middleware"). diff --git a/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/checks.js b/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/checks.js new file mode 100644 index 000000000..c351556f2 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/checks.js @@ -0,0 +1,16 @@ +// Two middleware in one returned object: each read must take its own property's function. +function makeChecks(realm) { + const auth = async (c, next) => { + if (!c.req.header('authorization')) return c.text(realm, 401) + await next() + } + return { + auth, + audit: async (c, next) => { + await next() + console.log(realm, c.req.path) + }, + } +} + +module.exports = { makeChecks } diff --git a/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/index.js b/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/index.js new file mode 100644 index 000000000..d39d3627e --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/index.js @@ -0,0 +1,15 @@ +// census: use_global, use_scoped, mw_factory_returning_fn, require_hono (violation hono-7-04; CommonJS) +// m.auth and m.audit are the two functions in checks.js, each on its own scope. Control: a cross-assignment +// would put audit on /admin/* only or auth on every route. +const { Hono } = require('hono') +const { makeChecks } = require('./checks') + +const m = makeChecks('ops') + +const app = new Hono() +app.use('/admin/*', m.auth) +app.use('*', m.audit) +app.get('/ping', (c) => c.text('pong')) +app.get('/admin/users', (c) => c.json([])) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/package.json b/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/package.json new file mode 100644 index 000000000..729ac4d93 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-factory-object-mw/src/package.json @@ -0,0 +1 @@ +{"type":"commonjs"} diff --git a/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/index.js b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/index.js new file mode 100644 index 000000000..c781b43da --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/index.js @@ -0,0 +1,13 @@ +// census: route_mount_literal, prop_receiver_route, require_hono (violation hono-7-01, P1 false_route; CommonJS) +// Sub-apps read as `rt.p` from the object a function returns. Control: distinct paths and handlers per sub-app, +// so a read that took the other property's value shows a route at the wrong prefix. +const { Hono } = require('hono') +const { createRuntime } = require('./runtime') + +const rt = createRuntime('eu') + +const app = new Hono() +app.route('/stock', rt.stockRoutes) +app.route('/orders', rt.orderRoutes) + +module.exports = app diff --git a/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/package.json b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/package.json new file mode 100644 index 000000000..729ac4d93 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/package.json @@ -0,0 +1 @@ +{"type":"commonjs"} diff --git a/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/routes/orders.js b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/routes/orders.js new file mode 100644 index 000000000..9bbd1aa4f --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/routes/orders.js @@ -0,0 +1,10 @@ +const { Hono } = require('hono') + +function buildOrderRoutes(label) { + const orders = new Hono() + orders.get('/recent', (c) => c.text(label())) + orders.put('/:orderId', (c) => c.text('saved')) + return orders +} + +module.exports = { buildOrderRoutes } diff --git a/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/routes/stock.js b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/routes/stock.js new file mode 100644 index 000000000..2e45afa00 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/routes/stock.js @@ -0,0 +1,10 @@ +const { Hono } = require('hono') + +function buildStockRoutes(label) { + const stock = new Hono() + stock.get('/', (c) => c.text(label())) + stock.delete('/:sku', (c) => c.text('removed')) + return stock +} + +module.exports = { buildStockRoutes } diff --git a/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/runtime.js b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/runtime.js new file mode 100644 index 000000000..e3f14a625 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/cjs-returned-object-member/src/runtime.js @@ -0,0 +1,10 @@ +const { buildStockRoutes } = require('./routes/stock') +const { buildOrderRoutes } = require('./routes/orders') + +// One object literal of sub-apps, shorthand and written-out properties: each read takes only its own property. +function createRuntime(region) { + const stockRoutes = buildStockRoutes(() => region) + return { region, stockRoutes, orderRoutes: buildOrderRoutes(() => region) } +} + +module.exports = { createRuntime } diff --git a/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/index.js b/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/index.js new file mode 100644 index 000000000..6147af0aa --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/index.js @@ -0,0 +1,17 @@ +// census: create_route, openapi_register, new_openapihono (violation hono-7-03; ESM JavaScript) +// A route built through a project wrapper around createRoute is a route (or partial/unknown with a reason), never +// dropped. Control: listRoute comes from createRoute directly. +import { OpenAPIHono, createRoute } from '@hono/zod-openapi' +import { scopedRoute } from './openapi.js' + +const listRoute = createRoute({ method: 'get', path: '/', responses: { 200: { description: 'ok' } } }) +const removeRoute = scopedRoute('items:delete', { method: 'delete', path: '/{id}', responses: { 204: { description: 'gone' } } }) + +const items = new OpenAPIHono() +items.openapi(listRoute, (c) => c.json([], 200)) +items.openapi(removeRoute, (c) => c.body(null, 204)) + +const app = new OpenAPIHono() +app.route('/api/items', items) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/openapi.js b/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/openapi.js new file mode 100644 index 000000000..534166670 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/openapi.js @@ -0,0 +1,13 @@ +import { createRoute } from '@hono/zod-openapi' + +export function authorize(scope) { + return async (c, next) => { + if (!scope) return c.text('no', 403) + await next() + } +} + +// A project wrapper around createRoute that adds a scope check to the route's middleware. +export function scopedRoute(scope, config) { + return createRoute({ ...config, middleware: [authorize(scope)] }) +} diff --git a/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/package.json b/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/package.json new file mode 100644 index 000000000..5ffd9800b --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-create-route-wrapper/src/package.json @@ -0,0 +1 @@ +{ "type": "module" } diff --git a/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/auth.js b/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/auth.js new file mode 100644 index 000000000..1fa2918fe --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/auth.js @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +const authRoutes = new Hono() +authRoutes.get('/login', (c) => c.text('form')) +authRoutes.post('/login', (c) => c.json({ ok: true })) +authRoutes.post('/login/form', (c) => c.text('ok')) + +export default authRoutes diff --git a/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/package.json b/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/package.json new file mode 100644 index 000000000..5ffd9800b --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/package.json @@ -0,0 +1 @@ +{ "type": "module" } diff --git a/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/test/auth.test.js b/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/test/auth.test.js new file mode 100644 index 000000000..5a57ae199 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-helper-request-method/src/test/auth.test.js @@ -0,0 +1,24 @@ +// census: app_request_test (violation hono-7-02; ESM JavaScript) +// `post` sends with `method: 'POST'`: each call joins the POST route on its path, never GET. Control: `get` +// passes no init, so its call joins GET on the same path. +import { Hono } from 'hono' +import authRoutes from '../auth.js' + +async function post(path, body) { + const app = new Hono() + app.route('/auth', authRoutes) + const headers = { 'Content-Type': 'application/json' } + return app.request(path, { method: 'POST', headers, body: JSON.stringify(body) }) +} + +async function get(path) { + const app = new Hono() + app.route('/auth', authRoutes) + return app.request(path) +} + +it('logs in and reads the form', async () => { + await post('/auth/login', { user: 'a' }) + await post('/auth/login/form', { user: 'a' }) + await get('/auth/login') +}) diff --git a/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/index.js b/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/index.js new file mode 100644 index 000000000..29d4de692 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/index.js @@ -0,0 +1,19 @@ +// census: create_route, openapi_register, new_openapihono (ESM JavaScript; found with violation hono-7-03: the +// JavaScript engine registers no `.openapi(route, handler)` route at all) +// The TypeScript openapi/create-route shape without types: route objects from another module, {id} registers as +// :id, the third argument is a validation hook, not the handler. +import { OpenAPIHono } from '@hono/zod-openapi' +import { listNotes, getNote } from './routes.js' + +const app = new OpenAPIHono() + +app.openapi(listNotes, (c) => c.json([], 200)) +app.openapi( + getNote, + (c) => c.json({ id: c.req.param('id') }, 200), + (result, c) => { + if (!result.success) return c.text('bad', 400) + } +) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/package.json b/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/package.json new file mode 100644 index 000000000..5ffd9800b --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/package.json @@ -0,0 +1 @@ +{ "type": "module" } diff --git a/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/routes.js b/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/routes.js new file mode 100644 index 000000000..840eaa44b --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-openapi-create-route/src/routes.js @@ -0,0 +1,4 @@ +import { createRoute } from '@hono/zod-openapi' + +export const listNotes = createRoute({ method: 'get', path: '/notes', responses: { 200: { description: 'notes' } } }) +export const getNote = createRoute({ method: 'get', path: '/notes/{id}', responses: { 200: { description: 'note' } } }) diff --git a/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/index.js b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/index.js new file mode 100644 index 000000000..f2573a95d --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/index.js @@ -0,0 +1,13 @@ +// census: route_mount_literal, app_factory_fn (violation hono-7-01, P1 false_route; ESM JavaScript) +// Sub-apps destructured from the object a function returns. Control: distinct paths and handlers per sub-app, +// so a name that took the other property's value shows a route at the wrong prefix. +import { Hono } from 'hono' +import { createRuntime } from './runtime.js' + +const { stockRoutes, orderRoutes } = createRuntime('eu') + +const app = new Hono() +app.route('/stock', stockRoutes) +app.route('/orders', orderRoutes) + +export default app diff --git a/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/package.json b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/package.json new file mode 100644 index 000000000..5ffd9800b --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/package.json @@ -0,0 +1 @@ +{ "type": "module" } diff --git a/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/routes/orders.js b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/routes/orders.js new file mode 100644 index 000000000..ae6f9d4c1 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/routes/orders.js @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function buildOrderRoutes(label) { + const orders = new Hono() + orders.get('/recent', (c) => c.text(label())) + orders.put('/:orderId', (c) => c.text('saved')) + return orders +} diff --git a/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/routes/stock.js b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/routes/stock.js new file mode 100644 index 000000000..755f1acbe --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/routes/stock.js @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function buildStockRoutes(label) { + const stock = new Hono() + stock.get('/', (c) => c.text(label())) + stock.delete('/:sku', (c) => c.text('removed')) + return stock +} diff --git a/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/runtime.js b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/runtime.js new file mode 100644 index 000000000..3e505ff93 --- /dev/null +++ b/graph/test/javascript/hono/cases/js/esm-returned-object-destructure/src/runtime.js @@ -0,0 +1,8 @@ +import { buildStockRoutes } from './routes/stock.js' +import { buildOrderRoutes } from './routes/orders.js' + +// One object literal of sub-apps, shorthand and written-out properties: each read takes only its own property. +export function createRuntime(region) { + const stockRoutes = buildStockRoutes(() => region) + return { region, stockRoutes, orderRoutes: buildOrderRoutes(() => region) } +} diff --git a/graph/test/javascript/hono/expected/js/cjs-factory-object-mw.entries b/graph/test/javascript/hono/expected/js/cjs-factory-object-mw.entries new file mode 100644 index 000000000..9b9778ea6 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-factory-object-mw.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-04 (CommonJS): m.auth checks.js:3 on /admin/* only, m.audit checks.js:9 on every route +# census: use_global, use_scoped, mw_factory_returning_fn, require_hono (violation hono-7-04; CommonJS) +entry checks.js:3:16 http +entry checks.js:9:12 http +entry index.js:12:18 http +entry index.js:13:25 http diff --git a/graph/test/javascript/hono/expected/js/cjs-factory-object-mw.tsv b/graph/test/javascript/hono/expected/js/cjs-factory-object-mw.tsv new file mode 100644 index 000000000..ca212801e --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-factory-object-mw.tsv @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-04 (CommonJS): m.auth checks.js:3 on /admin/* only, m.audit checks.js:9 on every route +# census: use_global, use_scoped, mw_factory_returning_fn, require_hono (violation hono-7-04; CommonJS) +route GET /ping index.js:12:18 index.js:9:13 2 +route GET /admin/users index.js:13:25 index.js:9:13 3 +mw checks.js:9:12 index.js:12:18 GET /ping 0 index.js:9:13 +mw checks.js:3:16 index.js:13:25 GET /admin/users 0 index.js:9:13 +mw checks.js:9:12 index.js:13:25 GET /admin/users 1 index.js:9:13 +regby index.js:1:1 index.js:12:18 GET /ping index.js:9:13 +regby index.js:1:1 index.js:13:25 GET /admin/users index.js:9:13 diff --git a/graph/test/javascript/hono/expected/js/cjs-returned-object-member.entries b/graph/test/javascript/hono/expected/js/cjs-returned-object-member.entries new file mode 100644 index 000000000..34b56d90d --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-returned-object-member.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-01 (CommonJS): each rt.p read takes only its own property: stock and orders with their own handlers +# census: route_mount_literal, prop_receiver_route, require_hono (violation hono-7-01, P1 false_route; CommonJS) +entry routes/orders.js:5:25 http +entry routes/orders.js:6:27 http +entry routes/stock.js:5:18 http +entry routes/stock.js:6:25 http diff --git a/graph/test/javascript/hono/expected/js/cjs-returned-object-member.tsv b/graph/test/javascript/hono/expected/js/cjs-returned-object-member.tsv new file mode 100644 index 000000000..fe8f83271 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/cjs-returned-object-member.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-01 (CommonJS): each rt.p read takes only its own property: stock and orders with their own handlers +# census: route_mount_literal, prop_receiver_route, require_hono (violation hono-7-01, P1 false_route; CommonJS) +route GET /stock routes/stock.js:5:18 index.js:9:13 0 +route DELETE /stock/:sku routes/stock.js:6:25 index.js:9:13 1 +route GET /orders/recent routes/orders.js:5:25 index.js:9:13 2 +route PUT /orders/:orderId routes/orders.js:6:27 index.js:9:13 3 +submount /stock index.js:10:5 +submount /orders index.js:11:5 +regby routes/orders.js:3:1 routes/orders.js:5:25 GET /orders/recent index.js:9:13 +regby routes/orders.js:3:1 routes/orders.js:6:27 PUT /orders/:orderId index.js:9:13 +regby routes/stock.js:3:1 routes/stock.js:5:18 GET /stock index.js:9:13 +regby routes/stock.js:3:1 routes/stock.js:6:25 DELETE /stock/:sku index.js:9:13 diff --git a/graph/test/javascript/hono/expected/js/esm-create-route-wrapper.entries b/graph/test/javascript/hono/expected/js/esm-create-route-wrapper.entries new file mode 100644 index 000000000..1b46f5903 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-create-route-wrapper.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-03 (ESM): the wrapped DELETE route is a route with authorize's inner arrow at position 0; GET / from createRoute directly +# census: create_route, openapi_register, new_openapihono (violation hono-7-03; ESM JavaScript) +entry index.js:11:26 http +entry index.js:12:28 http +entry openapi.js:4:10 http diff --git a/graph/test/javascript/hono/expected/js/esm-create-route-wrapper.tsv b/graph/test/javascript/hono/expected/js/esm-create-route-wrapper.tsv new file mode 100644 index 000000000..90353191b --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-create-route-wrapper.tsv @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-03 (ESM): the wrapped DELETE route is a route with authorize's inner arrow at position 0; GET / from createRoute directly +# census: create_route, openapi_register, new_openapihono (violation hono-7-03; ESM JavaScript) +route GET /api/items index.js:11:26 index.js:14:13 0 +route DELETE /api/items/:id index.js:12:28 index.js:14:13 2 +mw openapi.js:4:10 index.js:12:28 DELETE /api/items/:id 0 index.js:14:13 +submount /api/items index.js:15:5 +regby index.js:1:1 index.js:11:26 GET /api/items index.js:14:13 +regby index.js:1:1 index.js:12:28 DELETE /api/items/:id index.js:14:13 diff --git a/graph/test/javascript/hono/expected/js/esm-helper-request-method.entries b/graph/test/javascript/hono/expected/js/esm-helper-request-method.entries new file mode 100644 index 000000000..082ab99b4 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-helper-request-method.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-02 (ESM): the post helper's literal POST joins the two POST routes; the get helper (no init) joins GET /auth/login +# census: app_request_test (violation hono-7-02; ESM JavaScript) +entry auth.js:4:26 http +entry auth.js:5:27 http +entry auth.js:6:32 http diff --git a/graph/test/javascript/hono/expected/js/esm-helper-request-method.tsv b/graph/test/javascript/hono/expected/js/esm-helper-request-method.tsv new file mode 100644 index 000000000..28101f521 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-helper-request-method.tsv @@ -0,0 +1,19 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-02 (ESM): the post helper's literal POST joins the two POST routes; the get helper (no init) joins GET /auth/login +# census: app_request_test (violation hono-7-02; ESM JavaScript) +route GET /auth/login auth.js:4:26 test/auth.test.js:8:15 0 +route POST /auth/login auth.js:5:27 test/auth.test.js:8:15 1 +route POST /auth/login/form auth.js:6:32 test/auth.test.js:8:15 2 +route GET /auth/login auth.js:4:26 test/auth.test.js:15:15 0 +route POST /auth/login auth.js:5:27 test/auth.test.js:15:15 1 +route POST /auth/login/form auth.js:6:32 test/auth.test.js:15:15 2 +request test/auth.test.js:14:1 GET /auth/login auth.js:4:26 +request test/auth.test.js:7:1 POST /auth/login auth.js:5:27 +request test/auth.test.js:7:1 POST /auth/login/form auth.js:6:32 +submount /auth test/auth.test.js:9:7 +submount /auth test/auth.test.js:16:7 +regby auth.js:1:1 auth.js:4:26 GET /auth/login test/auth.test.js:15:15 +regby auth.js:1:1 auth.js:4:26 GET /auth/login test/auth.test.js:8:15 +regby auth.js:1:1 auth.js:5:27 POST /auth/login test/auth.test.js:15:15 +regby auth.js:1:1 auth.js:5:27 POST /auth/login test/auth.test.js:8:15 +regby auth.js:1:1 auth.js:6:32 POST /auth/login/form test/auth.test.js:15:15 +regby auth.js:1:1 auth.js:6:32 POST /auth/login/form test/auth.test.js:8:15 diff --git a/graph/test/javascript/hono/expected/js/esm-openapi-create-route.entries b/graph/test/javascript/hono/expected/js/esm-openapi-create-route.entries new file mode 100644 index 000000000..cadcafdd6 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-openapi-create-route.entries @@ -0,0 +1,4 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: createRoute + .openapi in ESM JavaScript: GET /notes and GET /notes/:id ({id} as :id), the handler is arg 1 not the hook +# census: create_route, openapi_register, new_openapihono (ESM JavaScript; found with violation hono-7-03: the +entry index.js:10:24 http +entry index.js:13:3 http diff --git a/graph/test/javascript/hono/expected/js/esm-openapi-create-route.tsv b/graph/test/javascript/hono/expected/js/esm-openapi-create-route.tsv new file mode 100644 index 000000000..e3a63b51a --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-openapi-create-route.tsv @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: createRoute + .openapi in ESM JavaScript: GET /notes and GET /notes/:id ({id} as :id), the handler is arg 1 not the hook +# census: create_route, openapi_register, new_openapihono (ESM JavaScript; found with violation hono-7-03: the +route GET /notes index.js:10:24 index.js:8:13 0 +route GET /notes/:id index.js:13:3 index.js:8:13 1 +regby index.js:1:1 index.js:10:24 GET /notes index.js:8:13 +regby index.js:1:1 index.js:13:3 GET /notes/:id index.js:8:13 diff --git a/graph/test/javascript/hono/expected/js/esm-returned-object-destructure.entries b/graph/test/javascript/hono/expected/js/esm-returned-object-destructure.entries new file mode 100644 index 000000000..233716936 --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-returned-object-destructure.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-01 (ESM): each destructured name takes only its own property: stock and orders with their own handlers +# census: route_mount_literal, app_factory_fn (violation hono-7-01, P1 false_route; ESM JavaScript) +entry routes/orders.js:5:25 http +entry routes/orders.js:6:27 http +entry routes/stock.js:5:18 http +entry routes/stock.js:6:25 http diff --git a/graph/test/javascript/hono/expected/js/esm-returned-object-destructure.tsv b/graph/test/javascript/hono/expected/js/esm-returned-object-destructure.tsv new file mode 100644 index 000000000..4e3a44a0d --- /dev/null +++ b/graph/test/javascript/hono/expected/js/esm-returned-object-destructure.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-01 (ESM): each destructured name takes only its own property: stock and orders with their own handlers +# census: route_mount_literal, app_factory_fn (violation hono-7-01, P1 false_route; ESM JavaScript) +route GET /stock routes/stock.js:5:18 index.js:9:13 0 +route DELETE /stock/:sku routes/stock.js:6:25 index.js:9:13 1 +route GET /orders/recent routes/orders.js:5:25 index.js:9:13 2 +route PUT /orders/:orderId routes/orders.js:6:27 index.js:9:13 3 +submount /stock index.js:10:5 +submount /orders index.js:11:5 +regby routes/orders.js:3:1 routes/orders.js:5:25 GET /orders/recent index.js:9:13 +regby routes/orders.js:3:1 routes/orders.js:6:27 PUT /orders/:orderId index.js:9:13 +regby routes/stock.js:3:1 routes/stock.js:5:18 GET /stock index.js:9:13 +regby routes/stock.js:3:1 routes/stock.js:6:25 DELETE /stock/:sku index.js:9:13 diff --git a/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/env.ts b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/env.ts new file mode 100644 index 000000000..c42a6ab20 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/env.ts @@ -0,0 +1,7 @@ +// the bundler's typing of import.meta.glob (a project declaration, as a vite-env file would add it) +declare global { + interface ImportMeta { + glob>(pattern: string | string[], options?: { eager?: boolean }): Record + } +} +export {} diff --git a/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/index.ts b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/index.ts new file mode 100644 index 000000000..52132f2a0 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/index.ts @@ -0,0 +1,13 @@ +// census: route_mount_root (SPEC R12.1; oracle note O28: a brace alternative holding a `*` crashed the oracle) +// The glob's brace alternatives are globs themselves: `memory` literally, `*-store` by wildcard. Control: the +// legacy store matches neither alternative: it stays its own exported app (GET /legacy), never under /stores. +import { Hono } from 'hono' + +const stores = import.meta.glob<{ default: Hono }>('./stores/{memory,*-store}/routes.ts', { eager: true }) + +const app = new Hono() +for (const mod of Object.values(stores)) { + app.route('/stores', mod.default) +} + +export default app diff --git a/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/disk-store/routes.ts b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/disk-store/routes.ts new file mode 100644 index 000000000..b680d8313 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/disk-store/routes.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const store = new Hono() +store.get('/disk', (c) => c.text('disk-store')) + +export default store diff --git a/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/legacy/routes.ts b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/legacy/routes.ts new file mode 100644 index 000000000..a105ff83e --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/legacy/routes.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const store = new Hono() +store.get('/legacy', (c) => c.text('legacy')) + +export default store diff --git a/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/memory/routes.ts b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/memory/routes.ts new file mode 100644 index 000000000..199f1cb23 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/memory/routes.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const store = new Hono() +store.get('/memory', (c) => c.text('memory')) + +export default store diff --git a/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/remote-store/routes.ts b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/remote-store/routes.ts new file mode 100644 index 000000000..578327247 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/glob-brace-alternatives/src/stores/remote-store/routes.ts @@ -0,0 +1,6 @@ +import { Hono } from 'hono' + +const store = new Hono() +store.get('/remote', (c) => c.text('remote-store')) + +export default store diff --git a/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/partial-paths b/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/partial-paths new file mode 100644 index 000000000..a2ca2b11d --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/partial-paths @@ -0,0 +1 @@ +opt-in: compare the known path of every partial route (ppath rows; oracle --partial-paths) diff --git a/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/src/main.ts b/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/src/main.ts new file mode 100644 index 000000000..19a634857 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/src/main.ts @@ -0,0 +1,25 @@ +// census: route_mount_nonliteral, use_scoped, use_global (violation hono-7-05: a partial route whose unknown piece +// is in the MIDDLE of the path) +// The mount prefix is a template: the version is a const, the group name is a run-time value. Every route of the +// sub-app is partial with the known path /v1/{}/; the global use runs on it, the /v1/* scoped use is a +// declared unknown (SPEC 3.3), and no exact /v1/... route is guessed. +import { Hono } from 'hono' +import { system } from './system' + +const VERSION = 'v1' + +declare const process: { env: Record } + +const app = new Hono() +app.use('*', async (c, next) => { + await next() +}) +app.use(`/${VERSION}/*`, async (c, next) => { + if (!c.req.header('authorization')) return c.text('no', 401) + await next() +}) +const group = process.env.GROUP ?? 'system' +app.route(`/${VERSION}/${group}`, system) +app.get('/health', (c) => c.text('ok')) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/src/system.ts b/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/src/system.ts new file mode 100644 index 000000000..82f2cce16 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/partial-path-mid-hole/src/system.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +export const system = new Hono() +system.use('*', async (c, next) => { + c.header('x-system', '1') + await next() +}) +system.get('/ping', (c) => c.text('pong')) +system.get('/status/:part', (c) => c.json({ part: c.req.param('part') })) diff --git a/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/index.ts b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/index.ts new file mode 100644 index 000000000..ed660901b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/index.ts @@ -0,0 +1,17 @@ +// census: route_mount_literal, app_factory_fn (violation hono-7-01, P1 false_route: sub-apps read out of an +// object a function returns) +// Destructured reads, one of them renamed. Control: the three sub-apps have distinct paths and handlers, so a +// name that took another property's value would show a route at the wrong prefix (e.g. DELETE /v1/orders/:sku). +import { Hono } from 'hono' +import { createRuntime } from './runtime' + +const { stockRoutes, orderRoutes, auditRoutes: audit } = createRuntime('eu') + +const app = new Hono() +const v1 = new Hono() +v1.route('/stock', stockRoutes) +v1.route('/orders', orderRoutes) +v1.route('/audit', audit) +app.route('/v1', v1) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/audit.ts b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/audit.ts new file mode 100644 index 000000000..eb0660691 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/audit.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +export function buildAuditRoutes(): Hono { + const audit = new Hono() + audit.post('/flush', (c) => c.text('flushed')) + return audit +} diff --git a/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/orders.ts b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/orders.ts new file mode 100644 index 000000000..e36d8b474 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/orders.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function buildOrderRoutes(label: () => string): Hono { + const orders = new Hono() + orders.get('/recent', (c) => c.text(label())) + orders.put('/:orderId', (c) => c.text('saved')) + return orders +} diff --git a/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/stock.ts b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/stock.ts new file mode 100644 index 000000000..5ad98860b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/routes/stock.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function buildStockRoutes(label: () => string): Hono { + const stock = new Hono() + stock.get('/', (c) => c.text(label())) + stock.delete('/:sku', (c) => c.text('removed')) + return stock +} diff --git a/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/runtime.ts b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/runtime.ts new file mode 100644 index 000000000..2a897328f --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-destructure/src/runtime.ts @@ -0,0 +1,11 @@ +import { buildStockRoutes } from './routes/stock' +import { buildOrderRoutes } from './routes/orders' +import { buildAuditRoutes } from './routes/audit' + +// The factory hands back several sub-apps in one object literal: shorthand properties, one written-out +// property, and a non-app property. Each read below must take only its own property's value. +export function createRuntime(region: string) { + const stockRoutes = buildStockRoutes(() => region) + const orderRoutes = buildOrderRoutes(() => region) + return { region, stockRoutes, orderRoutes, auditRoutes: buildAuditRoutes() } +} diff --git a/graph/test/typescript/hono/cases/compose/returned-object-member/src/index.ts b/graph/test/typescript/hono/cases/compose/returned-object-member/src/index.ts new file mode 100644 index 000000000..c193af77e --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-member/src/index.ts @@ -0,0 +1,17 @@ +// census: route_mount_literal, prop_receiver_route, app_factory_fn (violation hono-7-01, P1 false_route: sub-apps +// read out of an object a function returns) +// Member reads `rt.p` of the returned object. Control: the three sub-apps have distinct paths and handlers, so a +// read that took another property's value would show a route at the wrong prefix (e.g. GET /v1/stock/recent). +import { Hono } from 'hono' +import { createRuntime } from './runtime' + +const rt = createRuntime('eu') + +const app = new Hono() +const v1 = new Hono() +v1.route('/stock', rt.stockRoutes) +v1.route('/orders', rt.orderRoutes) +v1.route('/audit', rt.auditRoutes) +app.route('/v1', v1) + +export default app diff --git a/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/audit.ts b/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/audit.ts new file mode 100644 index 000000000..eb0660691 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/audit.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +export function buildAuditRoutes(): Hono { + const audit = new Hono() + audit.post('/flush', (c) => c.text('flushed')) + return audit +} diff --git a/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/orders.ts b/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/orders.ts new file mode 100644 index 000000000..e36d8b474 --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/orders.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function buildOrderRoutes(label: () => string): Hono { + const orders = new Hono() + orders.get('/recent', (c) => c.text(label())) + orders.put('/:orderId', (c) => c.text('saved')) + return orders +} diff --git a/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/stock.ts b/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/stock.ts new file mode 100644 index 000000000..5ad98860b --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-member/src/routes/stock.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function buildStockRoutes(label: () => string): Hono { + const stock = new Hono() + stock.get('/', (c) => c.text(label())) + stock.delete('/:sku', (c) => c.text('removed')) + return stock +} diff --git a/graph/test/typescript/hono/cases/compose/returned-object-member/src/runtime.ts b/graph/test/typescript/hono/cases/compose/returned-object-member/src/runtime.ts new file mode 100644 index 000000000..2a897328f --- /dev/null +++ b/graph/test/typescript/hono/cases/compose/returned-object-member/src/runtime.ts @@ -0,0 +1,11 @@ +import { buildStockRoutes } from './routes/stock' +import { buildOrderRoutes } from './routes/orders' +import { buildAuditRoutes } from './routes/audit' + +// The factory hands back several sub-apps in one object literal: shorthand properties, one written-out +// property, and a non-app property. Each read below must take only its own property's value. +export function createRuntime(region: string) { + const stockRoutes = buildStockRoutes(() => region) + const orderRoutes = buildOrderRoutes(() => region) + return { region, stockRoutes, orderRoutes, auditRoutes: buildAuditRoutes() } +} diff --git a/graph/test/typescript/hono/cases/middleware/factory-object-member/src/checks.ts b/graph/test/typescript/hono/cases/middleware/factory-object-member/src/checks.ts new file mode 100644 index 000000000..ff590e876 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/factory-object-member/src/checks.ts @@ -0,0 +1,16 @@ +import type { MiddlewareHandler } from 'hono' + +// Two middleware in one returned object, one written inline: each read must take its own property's function. +export function makeChecks(realm: string) { + const auth: MiddlewareHandler = async (c, next) => { + if (!c.req.header('authorization')) return c.text(realm, 401) + await next() + } + return { + auth, + audit: (async (c, next) => { + await next() + console.log(realm, c.req.path) + }) as MiddlewareHandler, + } +} diff --git a/graph/test/typescript/hono/cases/middleware/factory-object-member/src/index.ts b/graph/test/typescript/hono/cases/middleware/factory-object-member/src/index.ts new file mode 100644 index 000000000..5f214f6d6 --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/factory-object-member/src/index.ts @@ -0,0 +1,18 @@ +// census: use_global, use_scoped, mw_factory_returning_fn (violation hono-7-04: middleware read off the object a +// factory returns was keyed as the property-read expression, not as the function) +// limiter.middleware is the arrow in limiter.ts; m.auth and m.audit are the two functions in checks.ts, each on its +// own scope. Control: a cross-assignment would put audit on /admin/* or auth on every route. +import { Hono } from 'hono' +import { limiter } from './limiter' +import { makeChecks } from './checks' + +const m = makeChecks('ops') + +const app = new Hono() +app.use('*', limiter.middleware) +app.use('/admin/*', m.auth) +app.use('*', m.audit) +app.get('/ping', (c) => c.text('pong')) +app.get('/admin/users', (c) => c.json([])) + +export default app diff --git a/graph/test/typescript/hono/cases/middleware/factory-object-member/src/limiter.ts b/graph/test/typescript/hono/cases/middleware/factory-object-member/src/limiter.ts new file mode 100644 index 000000000..0089c2cfd --- /dev/null +++ b/graph/test/typescript/hono/cases/middleware/factory-object-member/src/limiter.ts @@ -0,0 +1,18 @@ +import type { MiddlewareHandler } from 'hono' + +// The factory returns an object: the middleware is one property, a counter reader the other. +export function createLimiter(opts: { limit: number }) { + let active = 0 + const middleware: MiddlewareHandler = async (c, next) => { + if (active >= opts.limit) return c.text('busy', 503) + active++ + try { + await next() + } finally { + active-- + } + } + return { middleware, active: () => active } +} + +export const limiter = createLimiter({ limit: 10 }) diff --git a/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/index.ts b/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/index.ts new file mode 100644 index 000000000..df0e5dac7 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/index.ts @@ -0,0 +1,11 @@ +// census: create_route, openapi_register, new_openapihono (violation hono-7-03: routes built through a project +// wrapper around createRoute were dropped with no partial and no unknown; SPEC 3.2 "never dropped") +// Control: listRoute comes from createRoute directly. The wrapped DELETE and PATCH routes must be routes, each with +// its own scope check as route middleware at position 0, or partial/unknown with a reason; never absent. +import { OpenAPIHono } from '@hono/zod-openapi' +import items from './items' + +const app = new OpenAPIHono() +app.route('/api/items', items) + +export default app diff --git a/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/items.ts b/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/items.ts new file mode 100644 index 000000000..97947685a --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/items.ts @@ -0,0 +1,21 @@ +import { OpenAPIHono, createRoute } from '@hono/zod-openapi' +import { scopedRoute } from './openapi' + +const listRoute = createRoute({ method: 'get', path: '/', responses: { 200: { description: 'ok' } } }) +const removeRoute = scopedRoute('items:delete', { + method: 'delete', + path: '/{id}', + responses: { 204: { description: 'gone' } }, +}) +const renameRoute = scopedRoute('items:write', { + method: 'patch', + path: '/{id}/name', + responses: { 200: { description: 'renamed' } }, +}) + +const items = new OpenAPIHono() + .openapi(listRoute, (c) => c.json([], 200)) + .openapi(removeRoute, (c) => c.body(null, 204)) + .openapi(renameRoute, (c) => c.json({ ok: true }, 200)) + +export default items diff --git a/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/openapi.ts b/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/openapi.ts new file mode 100644 index 000000000..5b81bcec5 --- /dev/null +++ b/graph/test/typescript/hono/cases/openapi/create-route-wrapper/src/openapi.ts @@ -0,0 +1,16 @@ +import { createRoute, type RouteConfig } from '@hono/zod-openapi' +import type { MiddlewareHandler } from 'hono' + +export function authorize(scope: string): MiddlewareHandler { + return async (c, next) => { + if (!scope) return c.text('no', 403) + await next() + } +} + +// A project wrapper around createRoute: it adds a scope check to the route's own middleware and returns +// createRoute's result. A route built through it is a route like any other. +export function scopedRoute

& { path: P }>(scope: string, config: T): T { + const middleware = [authorize(scope), ...((config.middleware ?? []) as [])] + return createRoute({ ...config, middleware } as T) as T +} diff --git a/graph/test/typescript/hono/cases/rpc/helper-request-method/src/__tests__/auth.test.ts b/graph/test/typescript/hono/cases/rpc/helper-request-method/src/__tests__/auth.test.ts new file mode 100644 index 000000000..3e480b228 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/helper-request-method/src/__tests__/auth.test.ts @@ -0,0 +1,32 @@ +// census: app_request_test (violation hono-7-02: a literal method in the init object of app.request inside a +// test helper that takes the path from its callers) +// `post` sends with `method: 'POST'`: each call joins the POST route on its path, never GET. Control: `get` +// passes no init, so its calls join GET; both helpers reach /auth/login, each on its own verb. +import { Hono } from 'hono' +import authRoutes from '../auth' + +declare function it(name: string, fn: () => unknown): void + +async function post(path: string, body: Record) { + const app = new Hono() + app.route('/auth', authRoutes) + const headers: Record = { 'Content-Type': 'application/json' } + const payload = JSON.stringify(body) + return app.request(path, { method: 'POST', headers, body: payload }, { DB: 1 }) +} + +async function get(path: string) { + const app = new Hono() + app.route('/auth', authRoutes) + return app.request(path) +} + +it('logs in', async () => { + await post('/auth/login', { user: 'a' }) + await post('/auth/login/form', { user: 'a' }) +}) + +it('reads pages', async () => { + await get('/auth/login') + await get('/auth/session') +}) diff --git a/graph/test/typescript/hono/cases/rpc/helper-request-method/src/auth.ts b/graph/test/typescript/hono/cases/rpc/helper-request-method/src/auth.ts new file mode 100644 index 000000000..cb93b8ea9 --- /dev/null +++ b/graph/test/typescript/hono/cases/rpc/helper-request-method/src/auth.ts @@ -0,0 +1,9 @@ +import { Hono } from 'hono' + +const authRoutes = new Hono() +authRoutes.get('/login', (c) => c.text('form')) +authRoutes.post('/login', (c) => c.json({ ok: true })) +authRoutes.post('/login/form', (c) => c.text('ok')) +authRoutes.get('/session', (c) => c.json({ user: null })) + +export default authRoutes diff --git a/graph/test/typescript/hono/expected/compose/filtered-instance-loop.tsv b/graph/test/typescript/hono/expected/compose/filtered-instance-loop.tsv index a210430ee..2fc142d87 100644 --- a/graph/test/typescript/hono/expected/compose/filtered-instance-loop.tsv +++ b/graph/test/typescript/hono/expected/compose/filtered-instance-loop.tsv @@ -1,10 +1,10 @@ # hand-reviewed 2026-10-28 against the fixture and the oracle: m36/hono-5-01 under R13b: exact /accounts and /invoices, each with conditional_registration # census: route_mount_root (stress-5 mutation m36 shape; violation hono-5-01, SPEC R13b) +# iteration 8: repeated rows removed (O29: the oracle prints each row once); the row set is unchanged route GET / index.ts:27:14 index.ts:26:13 0 route GET /accounts index.ts:11:24 index.ts:26:13 1 route GET /invoices index.ts:18:24 index.ts:26:13 2 submount / index.ts:29:7 -submount / index.ts:29:7 regby index.ts:16:3 index.ts:18:24 GET /invoices index.ts:26:13 regby index.ts:1:1 index.ts:27:14 GET / index.ts:26:13 regby index.ts:9:3 index.ts:11:24 GET /accounts index.ts:26:13 diff --git a/graph/test/typescript/hono/expected/compose/glob-brace-alternatives.entries b/graph/test/typescript/hono/expected/compose/glob-brace-alternatives.entries new file mode 100644 index 000000000..b30403949 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/glob-brace-alternatives.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: O28: {memory,*-store} matches memory, disk-store, remote-store (mounted under /stores); legacy stays its own exported app +# census: route_mount_root (SPEC R12.1; oracle note O28: a brace alternative holding a `*` crashed the oracle) +entry stores/disk-store/routes.ts:4:20 http +entry stores/legacy/routes.ts:4:22 http +entry stores/memory/routes.ts:4:22 http +entry stores/remote-store/routes.ts:4:22 http diff --git a/graph/test/typescript/hono/expected/compose/glob-brace-alternatives.tsv b/graph/test/typescript/hono/expected/compose/glob-brace-alternatives.tsv new file mode 100644 index 000000000..90232fbb6 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/glob-brace-alternatives.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: O28: {memory,*-store} matches memory, disk-store, remote-store (mounted under /stores); legacy stays its own exported app +# census: route_mount_root (SPEC R12.1; oracle note O28: a brace alternative holding a `*` crashed the oracle) +route GET /stores/disk stores/disk-store/routes.ts:4:20 index.ts:8:13 0 +route GET /stores/memory stores/memory/routes.ts:4:22 index.ts:8:13 1 +route GET /stores/remote stores/remote-store/routes.ts:4:22 index.ts:8:13 2 +route GET /legacy stores/legacy/routes.ts:4:22 stores/legacy/routes.ts:3:15 0 +submount /stores index.ts:10:7 +regby stores/disk-store/routes.ts:1:1 stores/disk-store/routes.ts:4:20 GET /stores/disk index.ts:8:13 +regby stores/legacy/routes.ts:1:1 stores/legacy/routes.ts:4:22 GET /legacy stores/legacy/routes.ts:3:15 +regby stores/memory/routes.ts:1:1 stores/memory/routes.ts:4:22 GET /stores/memory index.ts:8:13 +regby stores/remote-store/routes.ts:1:1 stores/remote-store/routes.ts:4:22 GET /stores/remote index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/compose/glob-module-list.tsv b/graph/test/typescript/hono/expected/compose/glob-module-list.tsv index 1a4cc9096..2f0b6b33f 100644 --- a/graph/test/typescript/hono/expected/compose/glob-module-list.tsv +++ b/graph/test/typescript/hono/expected/compose/glob-module-list.tsv @@ -1,12 +1,12 @@ # hand-reviewed 2026-10-28 against the fixture and the oracle: R12.1: literal import.meta.glob (eager and lazy) mounts each matched default-exported app; an unmatched module stays a root # census: route_mount_root (SPEC R12.1; QD16 shape: a tier app mounting `mod.default` over a literal glob) +# iteration 8: repeated rows removed (O29: the oracle prints each row once); the row set is unchanged route POST /api/orders routes/orders/orders.index.ts:4:24 index.ts:21:13 0 route GET /api/users routes/users/users.index.ts:4:21 index.ts:21:13 1 route GET /api/users/:id routes/users/users.index.ts:5:25 index.ts:21:13 2 route GET /pages/daily lazy/reports.page.ts:4:23 index.ts:21:13 3 route GET /scratch routes/orders/helpers.ts:5:25 routes/orders/helpers.ts:4:17 0 submount / index.ts:11:7 -submount / index.ts:11:7 submount /api index.ts:22:5 submount /pages index.ts:17:9 regby lazy/reports.page.ts:1:1 lazy/reports.page.ts:4:23 GET /pages/daily index.ts:21:13 diff --git a/graph/test/typescript/hono/expected/compose/partial-path-mid-hole.entries b/graph/test/typescript/hono/expected/compose/partial-path-mid-hole.entries new file mode 100644 index 000000000..5462e1fc4 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/partial-path-mid-hole.entries @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-05: the template prefix /v1/${group} keeps its known pieces: partial GET /v1/{}/ping and /v1/{}/status/:part (runtime /v1/system/... matches), global use 0 and the sub-app's use 1, the /v1/* use is partial_route_scope +# census: route_mount_nonliteral, use_scoped, use_global (violation hono-7-05: a partial route whose unknown piece +entry main.ts:14:14 http +entry main.ts:17:26 http +entry main.ts:23:20 http +entry system.ts:4:17 http +entry system.ts:8:21 http +entry system.ts:9:29 http diff --git a/graph/test/typescript/hono/expected/compose/partial-path-mid-hole.tsv b/graph/test/typescript/hono/expected/compose/partial-path-mid-hole.tsv new file mode 100644 index 000000000..c176925df --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/partial-path-mid-hole.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-05: the template prefix /v1/${group} keeps its known pieces: partial GET /v1/{}/ping and /v1/{}/status/:part (runtime /v1/system/... matches), global use 0 and the sub-app's use 1, the /v1/* use is partial_route_scope +# census: route_mount_nonliteral, use_scoped, use_global (violation hono-7-05: a partial route whose unknown piece +route GET /health main.ts:23:20 main.ts:13:13 2 +mw main.ts:14:14 main.ts:23:20 GET /health 0 main.ts:13:13 +partial system.ts:8:21 GET dynamic_mount_prefix +partial system.ts:9:29 GET dynamic_mount_prefix +ppath system.ts:8:21 GET /v1/{}/ping +ppath system.ts:9:29 GET /v1/{}/status/:part +mwp main.ts:14:14 system.ts:8:21 GET 0 +mwp main.ts:14:14 system.ts:9:29 GET 0 +mwp system.ts:4:17 system.ts:8:21 GET 1 +mwp system.ts:4:17 system.ts:9:29 GET 1 +regby main.ts:1:1 main.ts:23:20 GET /health main.ts:13:13 +unknown main.ts:22:1 dynamic_mount_prefix system.ts:8:21 +unknown main.ts:22:1 dynamic_mount_prefix system.ts:9:29 +unknown main.ts:22:1 partial_route_scope main.ts:17:26 diff --git a/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.tsv b/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.tsv index 3867ed3ff..220832422 100644 --- a/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.tsv +++ b/graph/test/typescript/hono/expected/compose/plugin-shared-test-and-prod.tsv @@ -1,5 +1,6 @@ # hand-reviewed 2026-10-28 against the fixture and the oracle: R16 control: a plugin mounted by production AND by a test app is a route of both; a test-only plugin stays on the test app; a test request for a production-only plugin path is unserved on the test app # census: app_request_test, plugin_list_mount (SPEC R16 control; R6, R13b) +# iteration 8: repeated rows removed (O29: the oracle prints each row once); the row set is unchanged route GET / server.ts:7:14 server.ts:6:13 0 route GET /api/search plugins/search.ts:5:17 server.ts:6:13 1 route POST /api/search/reindex plugins/search.ts:6:25 server.ts:6:13 2 @@ -14,8 +15,6 @@ request test/search.test.ts:7:46 GET /api/search plugins/search.ts:5:17 request test/search.test.ts:7:46 POST /api/search/reindex plugins/search.ts:6:25 submount /api/search plugins.ts:10:48 submount /api/billing plugins.ts:10:48 -submount /api/search plugins.ts:10:48 -submount /api/search plugins.ts:10:48 submount /__probe plugins.ts:10:48 regby plugins/billing.ts:1:1 plugins/billing.ts:5:26 GET /api/billing/invoices server.ts:6:13 regby plugins/search.ts:1:1 plugins/search.ts:5:17 GET /api/search server.ts:6:13 diff --git a/graph/test/typescript/hono/expected/compose/returned-object-destructure.entries b/graph/test/typescript/hono/expected/compose/returned-object-destructure.entries new file mode 100644 index 000000000..72565c301 --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/returned-object-destructure.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-01/O30: each destructured name (one renamed) takes only its own property: stock, orders, audit under /v1 with their own handlers; no unmounted copies +# census: route_mount_literal, app_factory_fn (violation hono-7-01, P1 false_route: sub-apps read out of an +entry routes/audit.ts:5:24 http +entry routes/orders.ts:5:25 http +entry routes/orders.ts:6:27 http +entry routes/stock.ts:5:18 http +entry routes/stock.ts:6:25 http diff --git a/graph/test/typescript/hono/expected/compose/returned-object-destructure.tsv b/graph/test/typescript/hono/expected/compose/returned-object-destructure.tsv new file mode 100644 index 000000000..e0def1d9f --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/returned-object-destructure.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-01/O30: each destructured name (one renamed) takes only its own property: stock, orders, audit under /v1 with their own handlers; no unmounted copies +# census: route_mount_literal, app_factory_fn (violation hono-7-01, P1 false_route: sub-apps read out of an +route GET /v1/stock routes/stock.ts:5:18 index.ts:10:13 0 +route DELETE /v1/stock/:sku routes/stock.ts:6:25 index.ts:10:13 1 +route GET /v1/orders/recent routes/orders.ts:5:25 index.ts:10:13 2 +route PUT /v1/orders/:orderId routes/orders.ts:6:27 index.ts:10:13 3 +route POST /v1/audit/flush routes/audit.ts:5:24 index.ts:10:13 4 +submount /stock index.ts:12:4 +submount /orders index.ts:13:4 +submount /audit index.ts:14:4 +submount /v1 index.ts:15:5 +regby routes/audit.ts:3:1 routes/audit.ts:5:24 POST /v1/audit/flush index.ts:10:13 +regby routes/orders.ts:3:1 routes/orders.ts:5:25 GET /v1/orders/recent index.ts:10:13 +regby routes/orders.ts:3:1 routes/orders.ts:6:27 PUT /v1/orders/:orderId index.ts:10:13 +regby routes/stock.ts:3:1 routes/stock.ts:5:18 GET /v1/stock index.ts:10:13 +regby routes/stock.ts:3:1 routes/stock.ts:6:25 DELETE /v1/stock/:sku index.ts:10:13 diff --git a/graph/test/typescript/hono/expected/compose/returned-object-member.entries b/graph/test/typescript/hono/expected/compose/returned-object-member.entries new file mode 100644 index 000000000..49066f03e --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/returned-object-member.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-01: each rt.p read takes only its own property: stock, orders, audit under /v1 with their own handlers +# census: route_mount_literal, prop_receiver_route, app_factory_fn (violation hono-7-01, P1 false_route: sub-apps +entry routes/audit.ts:5:24 http +entry routes/orders.ts:5:25 http +entry routes/orders.ts:6:27 http +entry routes/stock.ts:5:18 http +entry routes/stock.ts:6:25 http diff --git a/graph/test/typescript/hono/expected/compose/returned-object-member.tsv b/graph/test/typescript/hono/expected/compose/returned-object-member.tsv new file mode 100644 index 000000000..c7e9dab7f --- /dev/null +++ b/graph/test/typescript/hono/expected/compose/returned-object-member.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-01: each rt.p read takes only its own property: stock, orders, audit under /v1 with their own handlers +# census: route_mount_literal, prop_receiver_route, app_factory_fn (violation hono-7-01, P1 false_route: sub-apps +route GET /v1/stock routes/stock.ts:5:18 index.ts:10:13 0 +route DELETE /v1/stock/:sku routes/stock.ts:6:25 index.ts:10:13 1 +route GET /v1/orders/recent routes/orders.ts:5:25 index.ts:10:13 2 +route PUT /v1/orders/:orderId routes/orders.ts:6:27 index.ts:10:13 3 +route POST /v1/audit/flush routes/audit.ts:5:24 index.ts:10:13 4 +submount /stock index.ts:12:4 +submount /orders index.ts:13:4 +submount /audit index.ts:14:4 +submount /v1 index.ts:15:5 +regby routes/audit.ts:3:1 routes/audit.ts:5:24 POST /v1/audit/flush index.ts:10:13 +regby routes/orders.ts:3:1 routes/orders.ts:5:25 GET /v1/orders/recent index.ts:10:13 +regby routes/orders.ts:3:1 routes/orders.ts:6:27 PUT /v1/orders/:orderId index.ts:10:13 +regby routes/stock.ts:3:1 routes/stock.ts:5:18 GET /v1/stock index.ts:10:13 +regby routes/stock.ts:3:1 routes/stock.ts:6:25 DELETE /v1/stock/:sku index.ts:10:13 diff --git a/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.tsv b/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.tsv index d89579ebe..52a1da034 100644 --- a/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.tsv +++ b/graph/test/typescript/hono/expected/compose/shared-factory-filtered-loop.tsv @@ -1,5 +1,6 @@ # hand-reviewed 2026-10-28 against the fixture and the oracle: m35b/hono-5-02: per-call apps; /reports guard 0; /exports guard 0 (from reports), guard 1, quota 2, declared dynamic_middleware_list; exact plus conditional # census: route_mount_root (stress-5 mutation m35b shape; violation hono-5-02, SPEC R6 and R13b) +# iteration 8: repeated rows removed (O29: the oracle prints each row once); the row set is unchanged route GET /reports index.ts:30:23 index.ts:44:13 1 route GET /exports index.ts:37:23 index.ts:44:13 4 mw index.ts:9:21 index.ts:30:23 GET /reports 0 index.ts:44:13 @@ -7,7 +8,6 @@ mw index.ts:9:21 index.ts:37:23 GET /exports 0 index.ts:44:13 mw index.ts:9:21 index.ts:37:23 GET /exports 1 index.ts:44:13 mw index.ts:12:21 index.ts:37:23 GET /exports 2 index.ts:44:13 submount / index.ts:46:27 -submount / index.ts:46:27 regby index.ts:28:3 index.ts:30:23 GET /reports index.ts:44:13 regby index.ts:35:3 index.ts:37:23 GET /exports index.ts:44:13 unknown index.ts:37:5 dynamic_middleware_list index.ts:37:23 diff --git a/graph/test/typescript/hono/expected/middleware/factory-object-member.entries b/graph/test/typescript/hono/expected/middleware/factory-object-member.entries new file mode 100644 index 000000000..3ac48d371 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/factory-object-member.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-04: limiter.middleware is the arrow limiter.ts:6, m.auth checks.ts:5 on /admin/* only, m.audit checks.ts:11 on every route, in use order +# census: use_global, use_scoped, mw_factory_returning_fn (violation hono-7-04: middleware read off the object a +entry checks.ts:11:13 http +entry checks.ts:5:35 http +entry limiter.ts:6:41 http +entry index.ts:15:18 http +entry index.ts:16:25 http diff --git a/graph/test/typescript/hono/expected/middleware/factory-object-member.tsv b/graph/test/typescript/hono/expected/middleware/factory-object-member.tsv new file mode 100644 index 000000000..12cb6f4f7 --- /dev/null +++ b/graph/test/typescript/hono/expected/middleware/factory-object-member.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-04: limiter.middleware is the arrow limiter.ts:6, m.auth checks.ts:5 on /admin/* only, m.audit checks.ts:11 on every route, in use order +# census: use_global, use_scoped, mw_factory_returning_fn (violation hono-7-04: middleware read off the object a +route GET /ping index.ts:15:18 index.ts:11:13 3 +route GET /admin/users index.ts:16:25 index.ts:11:13 4 +mw limiter.ts:6:41 index.ts:15:18 GET /ping 0 index.ts:11:13 +mw checks.ts:11:13 index.ts:15:18 GET /ping 1 index.ts:11:13 +mw limiter.ts:6:41 index.ts:16:25 GET /admin/users 0 index.ts:11:13 +mw checks.ts:5:35 index.ts:16:25 GET /admin/users 1 index.ts:11:13 +mw checks.ts:11:13 index.ts:16:25 GET /admin/users 2 index.ts:11:13 +regby index.ts:1:1 index.ts:15:18 GET /ping index.ts:11:13 +regby index.ts:1:1 index.ts:16:25 GET /admin/users index.ts:11:13 diff --git a/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.tsv b/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.tsv index 6b753eb6a..22b4ccd2c 100644 --- a/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.tsv +++ b/graph/test/typescript/hono/expected/middleware/helper-app-mounted-thrice.tsv @@ -1,14 +1,7 @@ # hand-reviewed 2026-10-28 against the fixture and the oracle: m28/hono-4-02/R6: three per-call test apps, each chain use 0, param 1, json 2 # census: app_request_test (stress-4 mutation m28 shape; violation hono-4-02, SPEC R6) +# iteration 8: repeated rows removed (O29: the oracle prints each row once); the row set is unchanged route POST /:id/rename routes.ts:9:3 routes.test.ts:11:15 3 -route POST /:id/rename routes.ts:9:3 routes.test.ts:11:15 3 -route POST /:id/rename routes.ts:9:3 routes.test.ts:11:15 3 -mw routes.test.ts:12:16 routes.ts:9:3 POST /:id/rename 0 routes.test.ts:11:15 -mw lib:@hono/zod-validator.zValidator@routes.ts:7:3 routes.ts:9:3 POST /:id/rename 1 routes.test.ts:11:15 -mw lib:@hono/zod-validator.zValidator@routes.ts:8:3 routes.ts:9:3 POST /:id/rename 2 routes.test.ts:11:15 -mw routes.test.ts:12:16 routes.ts:9:3 POST /:id/rename 0 routes.test.ts:11:15 -mw lib:@hono/zod-validator.zValidator@routes.ts:7:3 routes.ts:9:3 POST /:id/rename 1 routes.test.ts:11:15 -mw lib:@hono/zod-validator.zValidator@routes.ts:8:3 routes.ts:9:3 POST /:id/rename 2 routes.test.ts:11:15 mw routes.test.ts:12:16 routes.ts:9:3 POST /:id/rename 0 routes.test.ts:11:15 mw lib:@hono/zod-validator.zValidator@routes.ts:7:3 routes.ts:9:3 POST /:id/rename 1 routes.test.ts:11:15 mw lib:@hono/zod-validator.zValidator@routes.ts:8:3 routes.ts:9:3 POST /:id/rename 2 routes.test.ts:11:15 @@ -16,8 +9,6 @@ request routes.test.ts:24:19 POST /1/rename routes.ts:9:3 request routes.test.ts:30:19 POST /2/rename routes.ts:9:3 request routes.test.ts:36:21 POST /3/rename routes.ts:9:3 submount / routes.test.ts:20:7 -submount / routes.test.ts:20:7 -submount / routes.test.ts:20:7 schema routes.ts:9:3 json expr@routes.ts:8:22 schema routes.ts:9:3 param expr@routes.ts:7:23 regby routes.ts:1:1 routes.ts:9:3 POST /:id/rename routes.test.ts:11:15 diff --git a/graph/test/typescript/hono/expected/openapi/create-route-wrapper.entries b/graph/test/typescript/hono/expected/openapi/create-route-wrapper.entries new file mode 100644 index 000000000..3a004eb48 --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/create-route-wrapper.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-03: routes built through the scopedRoute wrapper are routes (DELETE, PATCH) with the wrapper's authorize inner arrow at position 0; GET / from createRoute directly has none +# census: create_route, openapi_register, new_openapihono (violation hono-7-03: routes built through a project +entry items.ts:17:23 http +entry items.ts:18:25 http +entry items.ts:19:25 http +entry openapi.ts:5:10 http diff --git a/graph/test/typescript/hono/expected/openapi/create-route-wrapper.tsv b/graph/test/typescript/hono/expected/openapi/create-route-wrapper.tsv new file mode 100644 index 000000000..38cf4a808 --- /dev/null +++ b/graph/test/typescript/hono/expected/openapi/create-route-wrapper.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-03: routes built through the scopedRoute wrapper are routes (DELETE, PATCH) with the wrapper's authorize inner arrow at position 0; GET / from createRoute directly has none +# census: create_route, openapi_register, new_openapihono (violation hono-7-03: routes built through a project +route GET /api/items items.ts:17:23 index.ts:8:13 0 +route DELETE /api/items/:id items.ts:18:25 index.ts:8:13 2 +route PATCH /api/items/:id/name items.ts:19:25 index.ts:8:13 4 +mw openapi.ts:5:10 items.ts:18:25 DELETE /api/items/:id 0 index.ts:8:13 +mw openapi.ts:5:10 items.ts:19:25 PATCH /api/items/:id/name 0 index.ts:8:13 +submount /api/items index.ts:9:5 +regby items.ts:1:1 items.ts:17:23 GET /api/items index.ts:8:13 +regby items.ts:1:1 items.ts:18:25 DELETE /api/items/:id index.ts:8:13 +regby items.ts:1:1 items.ts:19:25 PATCH /api/items/:id/name index.ts:8:13 diff --git a/graph/test/typescript/hono/expected/rpc/helper-request-method.entries b/graph/test/typescript/hono/expected/rpc/helper-request-method.entries new file mode 100644 index 000000000..77c645e89 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/helper-request-method.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-02: the post helper's literal method POST joins POST /auth/login and POST /auth/login/form; the get helper (no init) joins GET /auth/login and GET /auth/session +# census: app_request_test (violation hono-7-02: a literal method in the init object of app.request inside a +entry auth.ts:4:26 http +entry auth.ts:5:27 http +entry auth.ts:6:32 http +entry auth.ts:7:28 http diff --git a/graph/test/typescript/hono/expected/rpc/helper-request-method.tsv b/graph/test/typescript/hono/expected/rpc/helper-request-method.tsv new file mode 100644 index 000000000..32e026c43 --- /dev/null +++ b/graph/test/typescript/hono/expected/rpc/helper-request-method.tsv @@ -0,0 +1,24 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: hono-7-02: the post helper's literal method POST joins POST /auth/login and POST /auth/login/form; the get helper (no init) joins GET /auth/login and GET /auth/session +# census: app_request_test (violation hono-7-02: a literal method in the init object of app.request inside a +route GET /auth/login auth.ts:4:26 __tests__/auth.test.ts:11:15 0 +route POST /auth/login auth.ts:5:27 __tests__/auth.test.ts:11:15 1 +route POST /auth/login/form auth.ts:6:32 __tests__/auth.test.ts:11:15 2 +route GET /auth/session auth.ts:7:28 __tests__/auth.test.ts:11:15 3 +route GET /auth/login auth.ts:4:26 __tests__/auth.test.ts:19:15 0 +route POST /auth/login auth.ts:5:27 __tests__/auth.test.ts:19:15 1 +route POST /auth/login/form auth.ts:6:32 __tests__/auth.test.ts:19:15 2 +route GET /auth/session auth.ts:7:28 __tests__/auth.test.ts:19:15 3 +request __tests__/auth.test.ts:10:1 POST /auth/login auth.ts:5:27 +request __tests__/auth.test.ts:10:1 POST /auth/login/form auth.ts:6:32 +request __tests__/auth.test.ts:18:1 GET /auth/login auth.ts:4:26 +request __tests__/auth.test.ts:18:1 GET /auth/session auth.ts:7:28 +submount /auth __tests__/auth.test.ts:12:7 +submount /auth __tests__/auth.test.ts:20:7 +regby auth.ts:1:1 auth.ts:4:26 GET /auth/login __tests__/auth.test.ts:11:15 +regby auth.ts:1:1 auth.ts:4:26 GET /auth/login __tests__/auth.test.ts:19:15 +regby auth.ts:1:1 auth.ts:5:27 POST /auth/login __tests__/auth.test.ts:11:15 +regby auth.ts:1:1 auth.ts:5:27 POST /auth/login __tests__/auth.test.ts:19:15 +regby auth.ts:1:1 auth.ts:6:32 POST /auth/login/form __tests__/auth.test.ts:11:15 +regby auth.ts:1:1 auth.ts:6:32 POST /auth/login/form __tests__/auth.test.ts:19:15 +regby auth.ts:1:1 auth.ts:7:28 GET /auth/session __tests__/auth.test.ts:11:15 +regby auth.ts:1:1 auth.ts:7:28 GET /auth/session __tests__/auth.test.ts:19:15 diff --git a/graph/test/typescript/hono/run-tests.sh b/graph/test/typescript/hono/run-tests.sh index 6c360a019..32cdb2aa8 100755 --- a/graph/test/typescript/hono/run-tests.sh +++ b/graph/test/typescript/hono/run-tests.sh @@ -22,6 +22,9 @@ # ./run-tests.sh --draft write the oracle's rows to expected/*.draft for REVIEW (never to the golden) # ./run-tests.sh --keep keep .work/ # +# cases///partial-paths (opt-in): the oracle also prints, and the compare also checks, `ppath HANDLER +# VERB KNOWNPATH` rows: a partial route's composed path with `{}` for the run-time piece (http_route_partial KnownPath). +# # expected//.known-missing: `\t` per line (the row as the # compare prints it after MISSING). A new miss fails; a listed miss that starts passing fails. # @@ -106,11 +109,13 @@ for dir in "$HERE"/cases/*/*/; do # chains/ (SPEC R18-R21): a third golden of whole call chains, checked against the compiler oracle and the runtime trace chf="$HERE/expected/$suite/$name.chains"; chargs=() w="$WORK/$suite/$name"; rm -rf "$w"; mkdir -p "$w" + # a `partial-paths` file opts the case into `ppath` rows: the known path of each partial route (hono-7-05) + oflags=(); [ -f "$dir/partial-paths" ] && oflags=(--partial-paths) ok=1; notes="" if [ "$DRAFT" = 1 ]; then mkdir -p "$(dirname "$exp")" - if node "$ORACLE_DIR/hono-oracle.mjs" "$dir/src" --stores >"$w/oracle.full" 2>"$w/oracle.log"; then + if node "$ORACLE_DIR/hono-oracle.mjs" "$dir/src" --stores ${oflags[@]+"${oflags[@]}"} >"$w/oracle.full" 2>"$w/oracle.log"; then rt="$(node "$ORACLE_DIR/runtime-check.mjs" "$dir/src" "$w/oracle.full" 2>&1 | head -6)" grep -v '^store' "$w/oracle.full" | grep -v '^entry' >"$exp.draft" grep '^entry' "$w/oracle.full" >"$ent.draft" @@ -133,7 +138,7 @@ for dir in "$HERE"/cases/*/*/; do [ $ok = 1 ] || { fail=$((fail+1)); failed+=("$id"); continue; } if [ "$ORACLE" = 1 ]; then - if node "$ORACLE_DIR/hono-oracle.mjs" "$dir/src" --stores >"$w/oracle.full" 2>"$w/oracle.log"; then + if node "$ORACLE_DIR/hono-oracle.mjs" "$dir/src" --stores ${oflags[@]+"${oflags[@]}"} >"$w/oracle.full" 2>"$w/oracle.log"; then grep -v '^store' "$w/oracle.full" | sort >"$w/oracle.rows" cat "$exp" "$ent" | rows_of /dev/stdin >"$w/expected.rows" if grep -q 'ORACLE_UNDECIDED' "$w/oracle.rows"; then echo "FAIL (the oracle could not decide a chain: extend it, do not hand-write)"; ok=0; fi diff --git a/graph/test/typescript/hono/tools/hono_compare.py b/graph/test/typescript/hono/tools/hono_compare.py index 5d3ad6cbe..4b70bfadc 100644 --- a/graph/test/typescript/hono/tools/hono_compare.py +++ b/graph/test/typescript/hono/tools/hono_compare.py @@ -16,6 +16,7 @@ mw MW HANDLER ORDER <- middleware_applies(Mw, Handler, Order) ctx SETTER READER KEY <- ctx_key_flow(Setter, Reader, Key) mwp MW HANDLER VERB ORDER <- middleware_applies rows of a partial-only handler (path, app dropped) + ppath HANDLER VERB KNOWNPATH <- http_route_partial(Handler, Verb, KnownPath); only when the golden has ppath rows rpc / request FROM HANDLER [CONF] <- remote_edge(From, To, .., Confidence): class base_unknown | method_unknown | known entry FN <- entry_point(Fn, "http") unknown FN <- remote_undetermined(Fn, ...) (expected col 4) @@ -153,7 +154,7 @@ def main(): proj_of = {'route': {'route'}, 'mw': {'mw'}, 'ctx': {'ctx'}, 'rpc': {'remote'}, 'request': {'remote'}, 'entry': {'entry'}, 'unknown': {'unknown'}, 'partial': {'partial'}, 'submount': {'submount'}, 'unserved': {'unserved'}, 'schema': {'schema'}, 'validread': {'validread'}, 'regby': {'regby'}, - 'absent': {'route', 'mw'}, 'mwp': {'mw'}} + 'absent': {'route', 'mw'}, 'mwp': {'mw'}, 'ppath': {'partial'}} # an absence is checked against every Hono relation the engine exports; route and mw are required # (above), the later ones (ctx, remote, schema) join the check as soon as they are exported absent_optional = {'ctx', 'remote', 'schema'} @@ -184,6 +185,9 @@ def engine(proj): out.append(('route', g('verb').upper(), g('path'), fn(g('handler'))) + ((fn(g('app')),) if 'app' in c else ())) elif proj == 'partial': out.append(('partial', fn(g('handler')), g('reason'))) + # hono-7-05 (opt-in: a case whose golden has ppath rows): the KnownPath of the partial route + if 'ppath' in kinds: + out.append(('ppath', fn(g('handler')), (g('verb') or '').upper(), g('path'))) elif proj == 'submount': out.append(('submount', g('prefix'))) elif proj == 'mw': @@ -246,6 +250,8 @@ def lib(k): want.add(('unknown', r[3], r[2])) elif k == 'partial': want.add(('partial', lib(r[1]), r[3])) + elif k == 'ppath': + want.add(('ppath', lib(r[1]), r[2], r[3])) elif k == 'submount': want.add(('submount', r[1])) elif k == 'unserved': diff --git a/graph/test/typescript/hono/truth/hono-oracle.mjs b/graph/test/typescript/hono/truth/hono-oracle.mjs index 64ecb3d15..a2715ceb7 100644 --- a/graph/test/typescript/hono/truth/hono-oracle.mjs +++ b/graph/test/typescript/hono/truth/hono-oracle.mjs @@ -48,6 +48,10 @@ const { TrieRouter } = await import(path.join(NM, 'hono/dist/router/trie-router/ const SRC = path.resolve(process.argv[2] || '.'); const NO_TYPECHECK = process.argv.includes('--no-typecheck'); const STORES = process.argv.includes('--stores'); +// --partial-paths (hono-7-05): also print `ppath HANDLER VERB KNOWNPATH` for a route under a dynamic mount prefix, +// the composed path with `{}` for each piece the program does not show (SPEC 3.2 KnownPath). Opt-in per case (a +// `partial-paths` file in the case dir), so the partial rows of every other golden are unchanged +const PARTIAL_PATHS = process.argv.includes('--partial-paths'); const DEBUG = !!process.env.HONO_ORACLE_DEBUG; const T0 = Date.now(); const phase = name => { if (DEBUG) process.stderr.write(`phase ${name} at ${((Date.now() - T0) / 1000).toFixed(1)}s\n`); }; @@ -283,7 +287,7 @@ function mergePath(base, sub, ...rest) { return `${base?.[0] === '/' ? '' : '/'}${base}${sub === '/' ? '' : `${base?.at(-1) === '/' ? '' : '/'}${sub?.[0] === '/' ? sub.slice(1) : sub}`}`; } -const out = { regby: new Set(), unserved: new Set(), partial: new Set(), submount: [], route: [], mw: [], entry: new Set(), ctx: new Set(), rpc: new Set(), request: new Set(), unknown: new Set() }; +const out = { regby: new Set(), unserved: new Set(), partial: new Set(), ppath: new Set(), submount: [], route: [], mw: [], entry: new Set(), ctx: new Set(), rpc: new Set(), request: new Set(), unknown: new Set() }; // unknown rows: SITE REASON FN (REASON from SPEC section 4's closed list; FN = the method it is attributed to) const siteFn = new Map(); function unknownAt(node, reason, fnKey) { out.unknown.add(`${loc(node)}\t${reason}\t${fnKey || enclosingFn(node)}`); } @@ -380,7 +384,8 @@ function valueOfDecl(d) { // the value is unknown, never a stack overflow if (evaluating.has(d)) { noteCycle(d); return UNK('cycle'); } evaluating.add(d); - let src; try { src = evalExpr(vd.initializer); } finally { evaluating.delete(d); } + let src; try { src = env.has(vd) ? env.get(vd) : evalExpr(vd.initializer); } finally { evaluating.delete(d); } + env.set(vd, src); // O30: the initializer is evaluated once per declaration, not once per destructured name const prop = (d.propertyName || d.name).getText(); let v = null; if (src.k === 'obj' && src.props.has(prop)) { const p = src.props.get(prop); if (!p.val) p.val = evalExpr(p.node); v = p.val; } @@ -739,18 +744,31 @@ const calleeRecv = new Map(); // R12: module lists the program spells literally. `import.meta.glob()` is an object keyed by the // matched files' paths (eager: the module; lazy: a function returning it), `require.context(dir, deep, /re/)` a // function over its keys; Object.entries / values / keys / fromEntries over a known object follow the values -function globRegex(pat) { +// O28: a brace alternative is a glob of its own (`{a,*-b}`, `{x/**,y}`): translate it with the same rules, never +// as escaped literal text (a bare `*` there made an invalid regex: "Nothing to repeat"). Braces may nest. +function globBody(pat) { let r = ''; for (let i = 0; i < pat.length; i++) { const ch = pat[i]; if (ch === '*' && pat[i + 1] === '*') { r += pat[i + 2] === '/' ? '(?:.*/)?' : '.*'; i += pat[i + 2] === '/' ? 2 : 1; } else if (ch === '*') r += '[^/]*'; else if (ch === '?') r += '[^/]'; - else if (ch === '{') { const j = pat.indexOf('}', i); r += '(?:' + pat.slice(i + 1, j).split(',').map(x => x.replace(/[.+^$()|[\]\\]/g, '\\$&')).join('|') + ')'; i = j; } - else r += ch.replace(/[.+^$()|[\]\\]/g, '\\$&'); + else if (ch === '{') { + let depth = 0, j = i, start = i + 1; const alts = []; + for (; j < pat.length; j++) { + if (pat[j] === '{') depth++; + else if (pat[j] === '}') { if (--depth === 0) break; } + else if (pat[j] === ',' && depth === 1) { alts.push(pat.slice(start, j)); start = j + 1; } + } + if (j >= pat.length) { r += '\\{'; continue; } // an unclosed brace is a literal + alts.push(pat.slice(start, j)); + r += '(?:' + alts.map(globBody).join('|') + ')'; i = j; + } + else r += ch.replace(/[.+^$()|[\]\\{}]/g, '\\$&'); } - return new RegExp('^' + r + '$'); + return r; } +function globRegex(pat) { return new RegExp('^' + globBody(pat) + '$'); } function filesMatching(fromFile, pats) { const dir = path.dirname(path.resolve(fromFile)), hits = new Map(); const pos = pats.filter(p => !p.startsWith('!')), neg = pats.filter(p => p.startsWith('!')).map(p => p.slice(1)); @@ -1168,7 +1186,13 @@ function appMethod(app, name, e) { // inside the sub-app, whose paths are all under the one unknown prefix) after the parent's `*` / `/*` middleware // registered before the mount. `mwp` rows: (mw, handler, verb, position); path and app are not decidable const partialMw = new Set(); -function partialMount(sub, reason, e, parentMws) { +function prefixText(x) { + if (!x) return HOLE; + while (ts.isParenthesizedExpression(x) || ts.isAsExpression(x)) x = x.expression; + if (ts.isTemplateExpression(x)) { let t = x.head.text; for (const sp of x.templateSpans) { const v = evalExpr(sp.expression); t += (v.k === 'str' ? v.v : HOLE) + sp.literal.text; } return t; } + const v = evalExpr(x); return v.k === 'str' ? v.v : HOLE; +} +function partialMount(sub, reason, e, parentMws, knownPrefix) { sub.store.mounted = true; const ents = [...sub.store.entries]; const router = new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }); @@ -1177,6 +1201,7 @@ function partialMount(sub, reason, e, parentMws) { if (en.kind !== 'handler') return; const V = en.method === 'ALL' ? 'ANY' : en.method; out.partial.add(`${en.key}\t${V}\t${reason}`); unknownAt(e, reason, en.key); + if (knownPrefix != null && en.key) out.ppath.add(`${en.key}\t${V}\t${mergePath(knownPrefix, en.path).split(HOLE).join('{}')}`); let matched; try { matched = router.match(en.method === 'ALL' ? 'GET' : en.method, samplePath(en.path))[0].map(x => x[0]); } catch { matched = null; } if (!matched || !matched.includes(i)) return; const seq = [...(parentMws || []), ...matched.filter(j => j < i).map(j => ents[j])]; @@ -1212,7 +1237,9 @@ function mountOne(app, pv, sub, e, site) { // every route of the sub-app is still emitted, partially, with the reason: never dropped; SPEC 3.3 [iter2]: a // scoped `use` already on this app cannot be decided against routes whose prefix is unknown const pm = sub.store.entries.some(r => r.kind === 'handler') ? parentStarMws(app, e) : []; - partialMount(sub, why, e, pm); + // the known pieces of a dynamic prefix: a template's decidable spans stay, every other piece is a HOLE + const known = app.dyn ? null : mergePath(app.base, prefixText(e.arguments[0])); + partialMount(sub, why, e, pm, known); return app; } // the EFFECTIVE prefix: route() on a basePath'd app mounts under mergePath(base, prefix) (hono-base.js) @@ -1376,7 +1403,9 @@ function evalStmt(st) { for (const d of st.declarationList.declarations) { if (!d.initializer || env.has(d)) continue; if (ts.isIdentifier(d.name)) env.set(d, evalExpr(d.initializer)); - else evalExpr(d.initializer); + // O30 (hono-7-01): a destructuring declaration evaluates its initializer ONCE; every name it binds reads that + // one value (valueOfDecl), so `const { a, b } = makeApps()` makes one set of apps, not one per name + else env.set(d, evalExpr(d.initializer)); } return null; } @@ -1856,13 +1885,13 @@ srcFiles.forEach(scanValid); phase('results'); const lines = []; -for (const r of out.route) lines.push(`route\t${r}`); -for (const r of out.mw) lines.push(`mw\t${r}`); +for (const r of new Set(out.route)) lines.push(`route\t${r}`); // O29: first occurrence only +for (const r of new Set(out.mw)) lines.push(`mw\t${r}`); for (const r of [...out.entry].sort()) lines.push(`entry\t${r}`); for (const r of [...out.ctx].sort()) lines.push(`ctx\t${r}`); for (const r of [...out.rpc].sort()) lines.push(`rpc\t${r}`); for (const r of [...out.request].sort()) lines.push(`request\t${r}`); -for (const r of out.submount) lines.push(`submount\t${r}`); +for (const r of new Set(out.submount)) lines.push(`submount\t${r}`); for (const r of [...schemaRows].sort()) lines.push(`schema\t${r}`); for (const r of [...validRows].sort()) lines.push(`validread\t${r}`); // O12: a handler with a route row is not also partial (nor its partial reason rows) @@ -1873,6 +1902,8 @@ for (const r of [...validRows].sort()) lines.push(`validread\t${r}`); for (const r of [...out.unknown]) { const c = r.split('\t'); if (PARTIAL_REASONS.has(c[1]) && routed.has(c[2])) out.unknown.delete(r); } } for (const r of [...out.partial].sort()) lines.push(`partial\t${r}`); +// a handler with a route row has no partial row (O12 above), so no partial path either +if (PARTIAL_PATHS) { const routed = new Set(out.route.map(r => r.split('\t')[2])); for (const r of [...out.ppath].sort()) if (!routed.has(r.split('\t')[0])) lines.push(`ppath\t${r}`); } { const routed = new Set(out.route.map(r => r.split('\t')[2])); for (const r of [...partialMw].sort()) if (!routed.has(r.split('\t')[1])) lines.push(`mwp\t${r}`); } for (const r of [...out.regby].sort()) lines.push(`regby\t${r}`); for (const r of [...out.unserved].sort()) lines.push(`unserved\t${r}`); @@ -1881,4 +1912,7 @@ for (const r of [...out.unknown].sort()) lines.push(`unknown\t${r}`); // (a mount's runtime handler is hono's wrapper: shown as a library entry; a dynamic marker is no runtime entry) if (STORES) for (const st of stores) st.entries.filter(en => en.kind !== 'dyn').forEach((en, i) => lines.push(`store\t${st.origin}#${st.id}\t${st.mounted ? 'mounted' : st.dropped ? 'unserved' : 'root'}\t${i}\t${en.method}\t${en.path}\t${en.mount ? 'lib:mount' : en.key}\t${en.kind}\t${en.alt || ''}`)); // streamed: one joined string exceeds V8's maximum string length on a 400-route project (stress-2 guard) -{ let buf = []; for (const l of lines) { buf.push(l); if (buf.length >= 20000) { fs.writeSync(1, buf.join('\n') + '\n'); buf = []; } } if (buf.length) fs.writeSync(1, buf.join('\n') + '\n'); } +// O29: every row is printed ONCE. The same (route|mw|submount) row is reached again for each store that shares a +// registration (a sub-app mounted under several parents, a builder called per test); goldens are sets, so this changes +// no committed expectation, but a row repeated 1000x inflated count-based scores and wrote a 1.1 GB file +{ const seen = new Set(); let buf = []; for (const l of lines) { if (seen.has(l)) continue; seen.add(l); buf.push(l); if (buf.length >= 20000) { fs.writeSync(1, buf.join('\n') + '\n'); buf = []; } } if (buf.length) fs.writeSync(1, buf.join('\n') + '\n'); } diff --git a/graph/test/typescript/hono/truth/runtime-check.mjs b/graph/test/typescript/hono/truth/runtime-check.mjs index 5194c9183..7a866b13d 100644 --- a/graph/test/typescript/hono/truth/runtime-check.mjs +++ b/graph/test/typescript/hono/truth/runtime-check.mjs @@ -115,6 +115,12 @@ const undetermined = new Set(STATIC.split('\n').filter(l => l.startsWith('partia // a registration behind a condition or in a loop: one run takes one path; compare only the rest const conditional = new Set(STATIC.split('\n').filter(l => /^unknown\t.*\t(conditional|loop)_registration\t/.test(l)).map(l => l.split('\t')[3])); for (const k of conditional) undetermined.add(k); +// hono-7-05: a middleware the static half places ONLY on partial routes (`mwp` rows, never an `mw` row) was registered on +// a sub-app mounted under a run-time prefix: its runtime path is that one execution's prefix, so it is not compared +{ + const placed = new Set(STATIC.split('\n').filter(l => l.startsWith('mw\t')).map(l => l.split('\t')[1])); + for (const l of STATIC.split('\n')) if (l.startsWith('mwp\t') && !placed.has(l.split('\t')[1])) undetermined.add(l.split('\t')[1]); +} const staticRouteKeys = new Set(STATIC.split('\n').filter(l => l.startsWith('route\t')).map(l => l.split('\t')[3]).filter(k => !k.startsWith('lib'))); for (const [, st] of stores) for (let i = st.length - 1; i >= 0; i--) if (st[i].keys.some(k => conditional.has(k))) st.splice(i, 1); function aligns(rt, st) { @@ -132,11 +138,22 @@ function aligns(rt, st) { return i === rt.length && j === st.length ? { ok: true, n } : { ok: false, at: `length runtime ${rt.length - i} left, static ${st.length - j} left` }; } let compared = 0, bad = []; +const ppaths = new Map(STATIC.split('\n').filter(l => l.startsWith('ppath\t')).map(l => { const c = l.split('\t'); return [`${c[1]}\t${c[2]}`, c[3]]; })); +const ppathSeen = new Map(), ppathOk = new Set(); const seen = new Set(); for (const app of exported) { if (seen.has(app.routes)) continue; seen.add(app.routes); const rt = []; for (const r of app.routes) { const key = await fnKey(r.handler); r.handlerKey = key; if (!undetermined.has(key)) rt.push({ method: r.method, path: r.path, key }); } + // hono-7-05: a partial route's known path (`ppath`, --partial-paths) must match a path this run registered for it, a + // `{}` standing for the run-time piece (one or more segments); the sub-app's own unprefixed copy is not that path + for (const r of app.routes) { + const k = `${r.handlerKey}\t${r.method === 'ALL' ? 'ANY' : r.method}`, pp = ppaths.get(k); + if (!pp) continue; + const re = new RegExp('^' + pp.split('{}').map(x => x.replace(/[.*+?^$()|[\]\\]/g, '\\$&')).join('[^/]+(?:/[^/]+)*') + '$'); + if (!ppathSeen.has(k)) ppathSeen.set(k, []); + if (re.test(r.path)) ppathOk.add(k); else ppathSeen.get(k).push(r.path); + } if (!rt.length) continue; // every registration of this app is one the static half declared undetermined // an app whose every ROUTE the static half declared undetermined (partial): its middleware alone is no route list // to compare (the static half keeps a partial route's chain as `mwp` rows, without a store) @@ -146,9 +163,11 @@ for (const app of exported) { if (best && best.ok) compared += best.n; else bad.push(`no static store matches an exported app: ${best ? best.at : 'no stores'}\n runtime: ${rt.map(r => `${r.method} ${r.path} ${r.key}`).join(' | ')}`); } +for (const [k, paths] of ppathSeen) if (!ppathOk.has(k)) bad.push(`partial path ${ppaths.get(k)} of ${k.split('\t')[0]} matches no runtime path (${paths.join(', ')})`); +const ppathChecked = ppathOk.size; cleanup(); if (bad.length) { console.log(`runtime DISAGREE\n ${bad.join('\n ')}`); process.exit(1); } if (compared < 1) { console.log('runtime skip every exported route is declared undetermined by the static half'); process.exit(0); } -console.log(`runtime agree ${compared} entries over ${seen.size} app(s)`); +console.log(`runtime agree ${compared} entries over ${seen.size} app(s)${ppaths.size ? `, ${ppathChecked}/${ppaths.size} partial paths matched` : ''}`); process.exit(0); function cleanup() { try { fs.rmSync(W, { recursive: true, force: true }); } catch { } } diff --git a/graph/typescript/engine/config-resolution/http-router-apps.dl b/graph/typescript/engine/config-resolution/http-router-apps.dl index 497cc9765..1e71a21b6 100644 --- a/graph/typescript/engine/config-resolution/http-router-apps.dl +++ b/graph/typescript/engine/config-resolution/http-router-apps.dl @@ -320,6 +320,10 @@ hr_objlit(r, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i ! .decl hr_objcall(c0:symbol, c1:symbol, c2:symbol, c3:symbol) hr_objcall(ce, ce, w, o) :- hr_call_of(ce, w), hr_ret_expr(w, e), hr_objlit(e, o). hr_objcall(r, ce, w, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_objcall(i, ce, w, o). +// …or a PARAMETER such a result is passed into (`mountHttp(runtime)` where `runtime = await createRuntime()`, then +// `const { usersRoutes } = runtime` inside): what each call passes there +hr_objcall(r, ce, w, o) :- hr_objcall(a, ce, w, o), expr_child("client", oc, "ARGUMENT", pos, a), hr_call_of(oc, f), + param_decl("client", _, pos, _, f, p), expr_referenced("client", "PARAMETER", p, r). hr_objcall(e, ce, w, o) :- hr_objcall(c, ce, w, o), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). // an element of a const array literal, read through a for..of binding over it @@ -362,15 +366,19 @@ hr_destr_of(e, v) :- expr_referenced("client", "VARIABLE", d, e), var_binding("c hr_objlit(i, o), rd_obj_prop(o, src, v). hr_app(e, a) :- hr_destr_of(e, v), hr_app(v, a). // a property of an object a call returned (`const { app } = createTestApp()`, `(await setup()).app`): an app -// the function created is this call's own list, any other is the same list every time +// the function created is this call's own list, any other is the same list every time. The call's own list is +// one per (call, created app), never the call alone: `return { envRoutes, fileRoutes }` holds two apps, and a read +// of `envRoutes` is the first only (one list for the call made every property read every app). Its id is the call +// and the created app joined by `#`; it is reported under the created app (hr_app_id) .decl hr_viacall(c0:symbol, c1:symbol, c2:symbol, c3:symbol) hr_viacall(e, ce, w, v) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, q), hr_objcall(q, ce, w, o), rd_obj_prop(o, n, v). hr_viacall(e, ce, w, v) :- expr_referenced("client", "VARIABLE", d, e), var_binding("client", _, "PROPERTY", src, i, d), i != "", hr_objcall(i, ce, w, o), rd_obj_prop(o, src, v). -.decl hr_objret_inst(c0:symbol, c1:symbol) -hr_objret_inst(ce, t) :- hr_viacall(_, ce, w, v), hr_app(v, t), expr_enclosing_method(t, w). -hr_app(e, ce) :- hr_viacall(e, ce, w, v), hr_app(v, t), expr_enclosing_method(t, w). +// (list, call, created app) +.decl hr_objret_inst(c0:symbol, c1:symbol, c2:symbol) +hr_objret_inst(l, ce, t) :- hr_viacall(_, ce, w, v), hr_app(v, t), expr_enclosing_method(t, w), l = cat(ce, cat("#", t)). +hr_app(e, l) :- hr_viacall(e, ce, w, v), hr_app(v, t), expr_enclosing_method(t, w), l = cat(ce, cat("#", t)). hr_app(e, t) :- hr_viacall(e, _, w, v), hr_app(v, t), !expr_enclosing_method(t, w). .decl hr_slot_arg(c0:symbol, c1:symbol, c2:symbol) @@ -402,8 +410,29 @@ hr_reg(ce, a, cn) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), hr_reg hr_def_val(x, o) :- hr_on(), call_site("client", "FUNCTION_CALL", _, _, _, x, _), expr_child("client", x, "METHOD_NAME", _, f), expr_referenced("client", "IMPORT_BINDING", ih, f), import_binding("client", _, _, orig, _, ih), hr_import_spec(ih, spec), router_route_def(spec, orig), - expr_child("client", x, "ARGUMENT", "0", o), expr_kind("client", "OBJECT_LITERAL", _, o). + expr_child("client", x, "ARGUMENT", "0", a), hr_def_obj(a, o). hr_def_val(r, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_def_val(i, o). +hr_def_val(e, o) :- hr_on(), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, c), hr_def_val(c, o). +// an object literal written in place or held by a const, through `as T` / `satisfies T` / parentheses +.decl hr_def_obj(c0:symbol, c1:symbol) +hr_def_obj(o, o) :- hr_on(), expr_kind("client", "OBJECT_LITERAL", _, o). +hr_def_obj(e, o) :- hr_on(), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, c), hr_def_obj(c, o). +hr_def_obj(r, o) :- hr_on(), hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_def_obj(i, o). +// …a project function that RETURNS a definition (`authRoute(scope, { method, path, .. })` returning +// `createRoute({ ...config, middleware })`): the call is a definition, the object the function builds. Its keys spread +// from a parameter (`...config`) are that call's argument's own (hr_def_spread), so each call is its own route +.decl hr_def_call(c0:symbol, c1:symbol) +hr_def_call(x, w) :- hr_call_of(x, w), hr_ret_expr(w, e), hr_def_val(e, _). +hr_def_val(x, o) :- hr_def_call(x, w), hr_ret_expr(w, e), hr_def_val(e, o). +// (call, the object the function builds, the call's argument object spread into it) +.decl hr_def_spread(c0:symbol, c1:symbol, c2:symbol) +hr_def_spread(x, o, o2) :- hr_def_call(x, w), hr_ret_expr(w, e), hr_def_val(e, o), expr_child("client", o, "SPREAD_OPERAND", _, sx), + hv_spread_src(sx, y), hr_def_param(y, p), param_decl("client", _, pos, _, w, p), expr_child("client", x, "ARGUMENT", pos, a), hr_def_obj(a, o2). +hr_def_val(x, o2) :- hr_def_spread(x, _, o2). +// a reference to a parameter, through `as T` and parentheses +.decl hr_def_param(c0:symbol, c1:symbol) +hr_def_param(y, p) :- hr_on(), expr_referenced("client", "PARAMETER", p, y). +hr_def_param(y, p) :- hr_on(), expr_kind("client", k, _, y), expr_kind_is_transparent(k), expr_child("client", y, _, _, c), hr_def_param(c, p). // `import * as routes from './routes'; app.openapi(routes.get, h)` hr_def_val(e, o) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, r), expr_referenced("client", "IMPORT_BINDING", ih, r), import_binds_namespace(ih, "client", m), @@ -505,7 +534,7 @@ hr_fw_self(e, v) :- hr_field_write(f, v), hr_fw_sub(v, e), ts_field_access_targe hr_val(e, a, b) :- ts_field_access_target(e, f), hr_field_write(f, v), hr_val(v, a, b), e != v, !hr_fw_self(e, v). hr_val(e, a, b) :- hr_prop_of(e, v), hr_val(v, a, b). hr_val(e, a, b) :- hr_destr_of(e, v), hr_val(v, a, b). -hr_val(e, ce, b) :- hr_viacall(e, ce, w, v), hr_val(v, t, b), expr_enclosing_method(t, w). +hr_val(e, l, b) :- hr_viacall(e, ce, w, v), hr_val(v, t, b), expr_enclosing_method(t, w), l = cat(ce, cat("#", t)). hr_val(e, t, b) :- hr_viacall(e, _, w, v), hr_val(v, t, b), !expr_enclosing_method(t, w). // ── INCLUSION: a list whose registrations are spliced into another ───────── @@ -588,7 +617,7 @@ hr_inc(a, p, oc, b, "param") :- hr_slot_arg(oc, p, x), hr_val(x, a, b), a != p, // other way round from a parameter registrations are made on hr_inc(p, s, oc, "/", "arg") :- hr_param_mounted(p), hr_slot_arg(oc, p, x), hr_app(x, s), s != p. hr_inc(ce, p, ce, "/", "instance") :- hr_factory_app(ce, f), hr_factory_init_param(f, p). -hr_inc(ce, t, ce, "/", "instance") :- hr_objret_inst(ce, t). +hr_inc(l, t, ce, "/", "instance") :- hr_objret_inst(l, ce, t). // the same links without their prefixes: which lists are roots is decided before any // string is read, because the strings a root's flattening needs depend on it @@ -599,7 +628,7 @@ hr_included(t) :- hr_callee(ce, w), hr_ret_cand(w, t), expr_enclosing_method(t, hr_included(p) :- hr_slot_arg(_, p, x), hr_app(x, a), a != p. hr_included(s) :- hr_param_mounted(p), hr_slot_arg(_, p, x), hr_app(x, s), s != p. hr_included(p) :- hr_factory_app(_, f), hr_factory_init_param(f, p). -hr_included(t) :- hr_objret_inst(_, t). +hr_included(t) :- hr_objret_inst(_, _, t). hr_included(t) :- hr_kv_sub(_, _, t). hr_included(s) :- hr_reg(ce, a, cn), router_mount_app(cn), hr_gtied(ce, g), hr_glob_app_of(g, s), s != a. hr_included(s) :- hr_reg(ce, a, cn), router_mount_app(cn), hr_gtied_d(ce, g), hr_glob_app_of(g, s), s != a. @@ -1094,7 +1123,7 @@ hr_merge(a, b, r) :- hr_mreq(a, b), hr_lead(a, a1), hr_lead(b, b1), b1 != "/", hr_need_key(ce) :- hr_reg(ce, _, _). hr_need_key(oc) :- hr_slot_arg(oc, _, a), hr_app(a, _). hr_need_key(ce) :- hr_app(ce, ce), !hr_new(ce). -hr_need_key(ce) :- hr_objret_inst(ce, _). +hr_need_key(ce) :- hr_objret_inst(_, ce, _). .decl hr_key_pn(c0:symbol, c1:symbol) hr_key_pn(ce, pn) :- hr_need_key(ce), call_callee_expr(_, ce, ne), expr_child("client", ne, "PROPERTY_NAME", _, pn). .decl hr_has_key_pn(c0:symbol) diff --git a/graph/typescript/engine/config-resolution/http-router-routes.dl b/graph/typescript/engine/config-resolution/http-router-routes.dl index 35de0e2a8..05f58e456 100644 --- a/graph/typescript/engine/config-resolution/http-router-routes.dl +++ b/graph/typescript/engine/config-resolution/http-router-routes.dl @@ -60,12 +60,48 @@ hr_hstart(ce, 0) :- hr_reg(ce, _, cn), router_fallback(cn). hr_harg(ce, 100, a) :- hr_reg(ce, _, cn), router_openapi(cn), expr_child("client", ce, "ARGUMENT", "1", a). // the definition's `middleware` (one, or a list) runs before it, in order .decl hr_def_mw(c0:symbol, c1:symbol) -hr_def_mw(ce, v) :- hr_openapi_def(ce, o), rd_obj_prop(o, k, v), router_def_key(k, "middleware"). +hr_def_mw(ce, v) :- hr_openapi_def(ce, o), rd_obj_prop(o, k, v), router_def_key(k, "middleware"), !hr_def_mw_overridden(o). +// a caller's definition spread into a wrapper's object that writes its own `middleware` after it: the wrapper's wins +.decl hr_def_mw_overridden(c0:symbol) +hr_def_mw_overridden(o2) :- hr_def_spread(_, o, o2), rd_obj_prop(o, k, _), router_def_key(k, "middleware"). hr_def_mw(ce, c) :- hr_def_mw(ce, v), expr_kind("client", k, _, v), expr_kind_is_transparent(k), expr_child("client", v, _, _, c). +// …a const holding the list (`const middleware = [authorize(auth), ...rest]; return createRoute({ ...config, middleware })`) +hr_def_mw(ce, i) :- hr_def_mw(ce, v), hr_denotes_var(v, d), var_initializer("client", _, i, d), i != "", expr_kind("client", "ARRAY_LITERAL", _, i). +.decl hr_def_mw_var(c0:symbol) +hr_def_mw_var(v) :- hr_def_mw(_, v), hr_denotes_var(v, d), var_initializer("client", _, i, d), i != "", expr_kind("client", "ARRAY_LITERAL", _, i). hr_harg(ce, n, el) :- hr_def_mw(ce, arr), expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, epos, el), - to_number(epos) < 90, n = 10 + to_number(epos). + to_number(epos) < 90, n = 10 + to_number(epos), !expr_kind("client", "SPREAD_ELEMENT", _, el). hr_harg(ce, 10, v) :- hr_def_mw(ce, v), !expr_kind("client", "ARRAY_LITERAL", _, v), - expr_kind("client", k, _, v), !expr_kind_is_transparent(k). + expr_kind("client", k, _, v), !expr_kind_is_transparent(k), !hr_def_mw_var(v). +// …a spread in that list of the CALLER's own list (`...(config.middleware ?? [])`, `config` the definition the call +// was handed): that call's argument object's list, in place; a list the argument does not hold adds nothing. Any other +// spread is a list decided at run time (dynamic_middleware_list) +.decl hr_def_mwsp(c0:symbol, c1:number, c2:symbol) +hr_def_mwsp(ce, n, y) :- hr_def_mw(ce, arr), expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, epos, el), + to_number(epos) < 90, n = 10 + to_number(epos), expr_kind("client", "SPREAD_ELEMENT", _, el), expr_child("client", el, _, _, y). +hr_def_mwsp(ce, n, c) :- hr_def_mwsp(ce, n, y), expr_kind("client", k, _, y), expr_kind_is_transparent(k), expr_child("client", y, _, _, c). +hr_def_mwsp(ce, n, c) :- hr_def_mwsp(ce, n, y), expr_kind("client", "BINARY_EXPRESSION", _, y), (expr_operator("client", "??", y) ; expr_operator("client", "||", y)), + expr_child("client", y, "LEFT_OPERAND", _, c). +// (registration, ordinal, the caller's argument object, the key read) +.decl hr_def_mwsp_key(c0:symbol, c1:number, c2:symbol, c3:symbol) +hr_def_mwsp_key(ce, n, o2, key) :- hr_def_mwsp(ce, n, z), property_access_name(z, key), expr_child("client", z, "RECEIVER", _, q), + hr_def_param(q, p), param_decl("client", _, pos, _, w, p), expr_child("client", ce, "ARGUMENT", "0", x0), hr_def_callat(x0, x), + hr_def_call(x, w), expr_child("client", x, "ARGUMENT", pos, a), hr_def_obj(a, o2). +.decl hr_def_callat(c0:symbol, c1:symbol) +hr_def_callat(x, x) :- hr_def_call(x, _). +hr_def_callat(r, x) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_def_callat(i, x). +hr_def_callat(e, x) :- hr_on(), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, c), hr_def_callat(c, x). +.decl hr_def_mwsp_val(c0:symbol, c1:number, c2:symbol) +hr_def_mwsp_val(ce, n, v) :- hr_def_mwsp_key(ce, n, o2, key), rd_obj_prop(o2, key, v). +hr_def_mwsp_val(ce, n, c) :- hr_def_mwsp_val(ce, n, v), expr_kind("client", k, _, v), expr_kind_is_transparent(k), expr_child("client", v, _, _, c). +hr_def_mwsp_val(ce, n, i) :- hr_def_mwsp_val(ce, n, v), hr_denotes_var(v, d), var_initializer("client", _, i, d), i != "", + expr_kind("client", "ARRAY_LITERAL", _, i). +hr_harg(ce, n + to_number(j), el) :- hr_def_mwsp_val(ce, n, arr), expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, j, el), + !expr_kind("client", "SPREAD_ELEMENT", _, el), n + to_number(j) < 90. +.decl hr_def_mwsp_known(c0:symbol, c1:number) +hr_def_mwsp_known(ce, n) :- hr_def_mwsp_key(ce, n, _, _). +framework_undetermined(m, "middleware", "dynamic_middleware_list") :- hr_def_mwsp(ce, n, _), !hr_def_mwsp_known(ce, n), + expr_enclosing_method(ce, m). // ── the handler arguments, in order: (call, ordinal, expression) ──────────── // ordinal = argument position * 100 + element index, so a spread `...[a, b]` keeps @@ -130,6 +166,27 @@ hr_farg(c) :- hr_farg(a), expr_kind("client", k, _, a), expr_kind_is_transparent .decl hr_assign_call(c0:symbol) hr_assign_call(a) :- hr_on(), call_site("client", "METHOD_CALL", "assign", _, r, a, _), r != "", !hr_call_of(a, _). hr_farg(x) :- hr_farg(a), hr_assign_call(a), expr_child("client", a, "ARGUMENT", "0", x). +// a property of an object literal a handler argument reads (`guard.middleware` where `guard = createGuard(..)` returns +// `{ middleware, inFlight }`, or `const { middleware } = createGuard(..)`): that property's own value, never another's +.decl hr_fobj_need(c0:symbol) +hr_fobj_need(q) :- hr_farg(a), property_access_name(a, _), expr_child("client", a, "RECEIVER", _, q). +hr_fobj_need(i) :- hr_farg(a), expr_referenced("client", "VARIABLE", d, a), var_binding("client", _, "PROPERTY", _, i, d), i != "". +hr_fobj_need(i) :- hr_fobj_need(x), hr_denotes_var(x, v), var_initializer("client", _, i, v), i != "". +hr_fobj_need(e) :- hr_fobj_need(x), hr_call_of(x, f), hr_ret_expr(f, e). +hr_fobj_need(c) :- hr_fobj_need(x), expr_kind("client", k, _, x), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"), + expr_child("client", x, _, _, c). +// (expression, the object literal it evaluates to) +.decl hr_fobj(c0:symbol, c1:symbol) +hr_fobj(x, x) :- hr_fobj_need(x), expr_kind("client", "OBJECT_LITERAL", _, x). +hr_fobj(x, o) :- hr_fobj_need(x), hr_denotes_var(x, v), var_initializer("client", _, i, v), i != "", hr_fobj(i, o). +hr_fobj(x, o) :- hr_fobj_need(x), hr_call_of(x, f), hr_ret_expr(f, e), hr_fobj(e, o). +hr_fobj(x, o) :- hr_fobj_need(x), expr_kind("client", k, _, x), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"), + expr_child("client", x, _, _, c), hr_fobj(c, o). +.decl hr_fprop(c0:symbol, c1:symbol) +hr_fprop(a, v) :- hr_farg(a), property_access_name(a, n), expr_child("client", a, "RECEIVER", _, q), hr_fobj(q, o), rd_obj_prop(o, n, v). +hr_fprop(a, v) :- hr_farg(a), expr_referenced("client", "VARIABLE", d, a), var_binding("client", _, "PROPERTY", src, i, d), i != "", + hr_fobj(i, o), rd_obj_prop(o, src, v). +hr_farg(v) :- hr_fprop(_, v). .decl hr_wrapper_call(c0:symbol) hr_wrapper_call(a) :- call_site("client", "METHOD_CALL", cn, _, r, a, _), router_factory_mw(cn), hr_factory_val(r, _). hr_wrapper_call(a) :- hr_detached(a, n, _), router_factory_mw(n). @@ -154,6 +211,7 @@ hr_fn(a, m) :- hr_farg(a), hr_denotes_var(a, v), var_initializer("client", _, i, hr_fn(a, m) :- hr_farg(a), hr_call_of(a, f), hr_ret_expr(f, e), hr_fn(e, m). hr_fn(a, m) :- hr_farg(a), hr_wrapper_call(a), expr_child("client", a, "ARGUMENT", "0", x), hr_fn(x, m). hr_fn(a, m) :- hr_farg(a), hr_assign_call(a), expr_child("client", a, "ARGUMENT", "0", x), hr_fn(x, m). +hr_fn(a, m) :- hr_fprop(a, v), hr_fn(v, m). hr_fn(a, m) :- hr_farg(a), expr_kind("client", k, _, a), expr_kind_is_transparent(k), expr_child("client", a, _, _, c), hr_fn(c, m). @@ -340,16 +398,44 @@ hr_entry_base(ce, a, "route", "ANY", lp, b) :- hr_reg(ce, a, cn), router_mount_f hr_entry_base(ce, a, "route", vv, lp, b) :- hr_reg(ce, a, _), hr_on_verb(ce, vv), hr_on_piece(ce, "1", lp), call_site("client", _, _, _, r, ce, _), hr_val(r, a, b). hr_entry_base(ce, a, "route", vv, lp, b) :- hr_openapi_def(ce, o), hr_reg(ce, a, _), - rd_obj_prop(o, mk, mv), router_def_key(mk, "method"), rd_val(mv, w), ts_http_verb_name(w, vv), - rd_obj_prop(o, pk, pv), router_def_key(pk, "path"), rd_val(pv, raw), hr_brace_path(raw, lp), + rd_obj_prop(o, mk, mv), router_def_key(mk, "method"), hr_def_v(ce, mv, w), ts_http_verb_name(w, vv), + rd_obj_prop(o, pk, pv), router_def_key(pk, "path"), hr_def_v(ce, pv, raw), hr_brace_path(raw, lp), call_site("client", _, _, _, r, ce, _), hr_val(r, a, b). +// an `openapi(def, h)` whose definition gives no literal method and path (a definition built where it cannot be read): +// the route exists, its verb and path are not known here; declared, never dropped (SPEC 3.7, dynamic_route_path) +.decl hr_openapi_routed(c0:symbol) +hr_openapi_routed(ce) :- hr_openapi_def(ce, _), hr_entry_base(ce, _, "route", _, _, _). +.decl hr_openapi_unread(c0:symbol) +hr_openapi_unread(ce) :- hr_reg(ce, _, cn), router_openapi(cn), !hr_openapi_routed(ce). +remote_undetermined(h, "http", "dynamic_route_path") :- hr_openapi_unread(ce), hr_node_fn(ce, 100, h). +remote_undetermined(m, "http", "dynamic_route_path") :- hr_openapi_unread(ce), !hr_has_fn(ce, 100), expr_enclosing_method(ce, m). // an OpenAPI path writes a parameter `{id}`; the router registers `:id` .decl hr_brace_raw(c0:symbol) -hr_brace_raw(raw) :- hr_openapi_def(_, o), rd_obj_prop(o, pk, pv), router_def_key(pk, "path"), rd_val(pv, raw). +hr_brace_raw(raw) :- hr_openapi_def(ce, o), rd_obj_prop(o, pk, pv), router_def_key(pk, "path"), hr_def_v(ce, pv, raw). +// a definition's method or path written as a PARAMETER of a wrapper on the registration's call chain +// (`analyticsRoute(id, summary, '/overview', schema)` building `{ method: 'get', path }`): that call's argument, per +// registration (hr_def_chain: the wrapper calls from the registration's argument inward) +.decl hr_def_chain(c0:symbol, c1:symbol) +hr_def_chain(ce, x) :- hr_reg(ce, _, cn), router_openapi(cn), expr_child("client", ce, "ARGUMENT", "0", x0), hr_def_callat(x0, x). +hr_def_chain(ce, x2) :- hr_def_chain(ce, x), hr_def_call(x, w), hr_ret_expr(w, e), hr_def_callat(e, x2). +.decl hr_def_parg(c0:symbol, c1:symbol, c2:symbol) +hr_def_parg(ce, pv, a) :- hr_openapi_def(ce, o), rd_obj_prop(o, k, pv), (router_def_key(k, "method") ; router_def_key(k, "path")), + hr_def_param(pv, p), param_decl("client", _, pos, _, w, p), hr_def_chain(ce, x), hr_def_call(x, w), + expr_child("client", x, "ARGUMENT", pos, a). +rd_demand(a) :- hr_def_parg(_, _, a). +.decl hr_def_pargd(c0:symbol, c1:symbol) +hr_def_pargd(ce, pv) :- hr_def_parg(ce, pv, _). +.decl hr_def_v(c0:symbol, c1:symbol, c2:symbol) +hr_def_v(ce, pv, raw) :- hr_openapi_def(ce, o), rd_obj_prop(o, k, pv), (router_def_key(k, "method") ; router_def_key(k, "path")), + !hr_def_pargd(ce, pv), rd_val(pv, raw). +hr_def_v(ce, pv, raw) :- hr_def_parg(ce, pv, a), rd_val(a, raw). .decl hr_brace_step(c0:symbol, c1:number, c2:symbol) hr_brace_step(raw, 0, "") :- hr_brace_raw(raw). -hr_brace_step(raw, i + 1, cat(acc, ":")) :- hr_brace_step(raw, i, acc), i < strlen(raw), substr(raw, i, 1) = "{". +// …but a `{}` is a piece decided at run time (rd_val's hole), never a parameter named nothing: kept, so the route is partial +hr_brace_step(raw, i + 2, cat(acc, "{}")) :- hr_brace_step(raw, i, acc), i + 1 < strlen(raw), substr(raw, i, 1) = "{", substr(raw, i + 1, 1) = "}". +hr_brace_step(raw, i + 1, cat(acc, ":")) :- hr_brace_step(raw, i, acc), i + 1 < strlen(raw), substr(raw, i, 1) = "{", substr(raw, i + 1, 1) != "}". hr_brace_step(raw, i + 1, acc) :- hr_brace_step(raw, i, acc), i < strlen(raw), substr(raw, i, 1) = "}". +hr_brace_step(raw, i + 1, cat(acc, "{")) :- hr_brace_step(raw, i, acc), i + 1 = strlen(raw), substr(raw, i, 1) = "{". hr_brace_step(raw, i + 1, cat(acc, ch)) :- hr_brace_step(raw, i, acc), i < strlen(raw), ch = substr(raw, i, 1), ch != "{", ch != "}". .decl hr_brace_path(c0:symbol, c1:symbol) @@ -409,21 +495,51 @@ hr_fentry(r, fk, kind, vv, fp, ce, n) :- hr_fcand(r, fk, kind, vv, p, ce, n, pre // the id an app is reported under: the expression that created its route list. A list made by a call of // a factory (each call its own list here) is reported under the factory's `new`, which is what a reader opens. .decl hr_is_instance(c0:symbol) -hr_is_instance(ce) :- hr_inc(ce, t, ce, _, "instance"), !hr_slot(t). +hr_is_instance(l) :- hr_inc(l, t, _, _, "instance"), !hr_slot(t). .decl hr_app_id(c0:symbol, c1:symbol) hr_app_id(a, a) :- hr_list(a), !hr_is_instance(a). -hr_app_id(ce, i) :- hr_inc(ce, t, ce, _, "instance"), !hr_slot(t), hr_app_id(t, i). +hr_app_id(l, i) :- hr_inc(l, t, _, _, "instance"), !hr_slot(t), hr_app_id(t, i). .decl hr_partial_root(c0:symbol) hr_partial_root(r) :- hr_root(r), hr_slot(r). hr_partial_root(r) :- hr_root(r), hr_unknown_mount(r). +hr_partial_root(r) :- hr_root(r), hr_fetch_in_handler(r). +// an app a REQUEST HANDLER serves itself, by calling its fetch (`wrapped.fetch(new Request(url, ..))` in a function an +// `all('*', (c) => invokePackage(c, app))` handler calls): it answers under whatever prefix that handler's route has, +// a run-time forwarding, so its routes are an unresolved mount, never exact roots at their own paths +.decl hr_fetch_site(c0:symbol, c1:symbol) +hr_fetch_site(r, m) :- call_site("client", "METHOD_CALL", cn, _, x, ce, _), router_app_fetch(cn), hr_app(x, r), hr_root(r), + expr_enclosing_method(ce, m). +// (fetching function, a function calling it): the site's function and its callers, two calls up +.decl hr_fetch_up(c0:symbol, c1:symbol, c2:number) +hr_fetch_up(r, m, 0) :- hr_fetch_site(r, m). +hr_fetch_up(r, g, d + 1) :- hr_fetch_up(r, f, d), d < 2, hr_call_of(oc, f), expr_enclosing_method(oc, g). +.decl hr_is_handler(c0:symbol) +hr_is_handler(m) :- hr_node_fn(_, _, m). +.decl hr_fetch_in_handler(c0:symbol) +hr_fetch_in_handler(r) :- hr_fetch_up(r, m, _), hr_is_handler(m). hr_partial_root(r) :- hr_root(r), hr_list_entry(r). // http_route(Handler, Verb, FullPath, App, Order) — a route a served app answers: // the handler function, the verb (ANY for all), the full path composed through every // basePath and mount, the root app (the expression that created it) and the order key // of the registration on that app (dotted, zero-padded: compares as program order). -http_route(m, vv, fp, ai, fk) :- hr_fentry(r, fk, "route", vv, fp, ce, n), hr_last(ce, n), hr_app_id(r, ai), +// One row per (handler, verb, path, app): an app built by a factory that many calls make (each test's +// `createApp()`) is many lists reported under one App, each with the route under its own include key; the row keeps the +// earliest key (lowest line, then column, of its first segment, then the lowest key), never one per list (hono-7-06) +.decl hr_route_row(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hr_route_row(m, vv, fp, ai, fk) :- hr_fentry(r, fk, "route", vv, fp, ce, n), hr_last(ce, n), hr_app_id(r, ai), hr_route_node(ce, n, m), !contains("{}", fp), !hr_partial_root(r), !hr_dyn_base_call(ce). +.decl hr_route_rl(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:number, c6:number) +hr_route_rl(m, vv, fp, ai, fk, to_number(substr(fk, 0, 7)), to_number(substr(fk, 8, 5))) :- hr_route_row(m, vv, fp, ai, fk), strlen(fk) >= 13. +.decl hr_route_ml(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) +hr_route_ml(m, vv, fp, ai, l) :- hr_route_rl(m, vv, fp, ai, _, _, _), l = min x : { hr_route_rl(m, vv, fp, ai, _, x, _) }. +.decl hr_route_mc(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number, c5:number) +hr_route_mc(m, vv, fp, ai, l, c) :- hr_route_ml(m, vv, fp, ai, l), c = min y : { hr_route_rl(m, vv, fp, ai, _, l, y) }. +.decl hr_route_cand(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hr_route_cand(m, vv, fp, ai, fk) :- hr_route_mc(m, vv, fp, ai, l, c), hr_route_rl(m, vv, fp, ai, fk, l, c). +.decl hr_route_later(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +hr_route_later(m, vv, fp, ai, fk) :- hr_route_cand(m, vv, fp, ai, fk), hr_route_cand(m, vv, fp, ai, k2), k2 < fk. +http_route(m, vv, fp, ai, fk) :- hr_route_cand(m, vv, fp, ai, fk), !hr_route_later(m, vv, fp, ai, fk). // route_registered_by(RegMethod, Handler, Verb, FullPath, App) — the function whose body makes // the registering call (a registration function, an app factory, a module body): what a change @@ -462,6 +578,8 @@ hr_partial(m, vv, fp, r, "unresolved_mount") :- hr_fentry(r, _, "route", vv, fp, hr_route_node(ce, n, m), hr_root(r), hr_slot(r), hr_slot_handed(r). hr_partial(m, vv, fp, r, "unresolved_mount") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), hr_route_node(ce, n, m), hr_root(r), hr_unknown_mount(r), !hr_slot(r). +hr_partial(m, vv, fp, r, "unresolved_mount") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), + hr_route_node(ce, n, m), hr_root(r), hr_fetch_in_handler(r), !hr_slot(r), !hr_unknown_mount(r). // an app in a list entry no loop over a const array mounts (SPEC ruling R13) hr_partial(m, vv, fp, r, "unresolved_mount") :- hr_fentry(r, _, "route", vv, fp, ce, n), hr_last(ce, n), hr_route_node(ce, n, m), hr_root(r), hr_list_entry(r), !hr_slot(r). @@ -496,8 +614,15 @@ remote_undetermined(m, "http", why) :- hr_partial1(m, _, _, _, why). // http_mount(ParentApp, ChildApp, Prefix, Order) — `route(prefix, child)`: the child // app's registrations so far are served under prefix (merged with the parent's base). -http_mount(a, s, pre, k) :- hr_inc(a, s, site, pre, "mount"), hr_key(site, k), !contains("{}", pre). -http_mount(a, s, pre, k) :- hr_inc(a, s, site, pre, "mount_each"), hr_key(site, k), !contains("{}", pre). +// An app is exported under an expression: a list a returned object's property gives (`#`, hr_objret_inst) +// is exported under the app it was made from. +http_mount(ao, so, pre, k) :- hr_inc(a, s, site, pre, kd), (kd = "mount" ; kd = "mount_each"), hr_key(site, k), !contains("{}", pre), + hr_mount_id(a, ao), hr_mount_id(s, so). +.decl hr_mount_id_need(c0:symbol) +hr_mount_id_need(x) :- hr_inc(a, s, _, _, kd), (kd = "mount" ; kd = "mount_each"), (x = a ; x = s). +.decl hr_mount_id(c0:symbol, c1:symbol) +hr_mount_id(x, x) :- hr_mount_id_need(x), !contains("#", x). +hr_mount_id(x, i) :- hr_mount_id_need(x), contains("#", x), hr_app_id(x, i). framework_undetermined(m, "mount", "unresolved_mount") :- hr_reg(ce, _, cn), router_mount_app(cn), expr_child("client", ce, "ARGUMENT", "1", sx), !hr_app(sx, _), expr_enclosing_method(ce, m), !hv_fin_mount(ce). framework_undetermined(m, "mount", "foreign_mount") :- hr_reg(ce, _, cn), router_mount_foreign(cn), diff --git a/graph/typescript/engine/framework-behavior/http-rpc-client.dl b/graph/typescript/engine/framework-behavior/http-rpc-client.dl index bc6910cbb..bb51f59fa 100644 --- a/graph/typescript/engine/framework-behavior/http-rpc-client.dl +++ b/graph/typescript/engine/framework-behavior/http-rpc-client.dl @@ -117,6 +117,8 @@ hc_strip_need(raw) :- hc_preq(_, w, pos), hr_call_of(oc, w), expr_child("client" .decl hc_preq_init(c0:symbol, c1:symbol, c2:symbol) hc_preq_init(oc, ce, ia) :- hc_preq_at(oc, ce, _, _), expr_child("client", ce, "ARGUMENT", "1", x1), expr_referenced("client", "PARAMETER", ip, x1), param_decl("client", _, ipos, _, _, ip), expr_child("client", oc, "ARGUMENT", ipos, ia). +// …or the init the helper writes in place (`app.request(path, { method: 'POST', body })`): the same for every call +hc_preq_init(oc, ce, x1) :- hc_preq_at(oc, ce, _, _), expr_child("client", ce, "ARGUMENT", "1", x1), !expr_referenced("client", "PARAMETER", _, x1). .decl hc_preq_has_init(c0:symbol, c1:symbol) hc_preq_has_init(oc, ce) :- hc_preq_init(oc, ce, _). .decl hc_preq_verb(c0:symbol, c1:symbol, c2:symbol) @@ -126,6 +128,11 @@ hc_preq_verb(oc, ce, vv) :- hc_preq_init(oc, ce, o), rd_obj_prop(o, key, val), t expr_literal_value("client", w, val), ts_http_verb_name(w, vv). hc_preq_verb(oc, ce, "GET") :- hc_preq_init(oc, ce, o), expr_kind("client", "OBJECT_LITERAL", _, o), !hc_obj_has_method_key(o). hc_preq_verb(oc, ce, "*") :- hc_preq_init(oc, ce, o), !expr_kind("client", "OBJECT_LITERAL", _, o). +// …a method key whose value is not a literal verb: not decidable (SPEC ruling R17) +hc_preq_verb(oc, ce, "*") :- hc_preq_init(oc, ce, o), hc_obj_has_method_key(o), !hc_obj_lit_method(o). +.decl hc_obj_lit_method(c0:symbol) +hc_obj_lit_method(o) :- hc_preq_init(_, _, o), rd_obj_prop(o, key, val), ts_http_init_method_key(key), expr_literal_value("client", w, val), + ts_http_verb_name(w, _). .decl hc_obj_has_method_key(c0:symbol) hc_obj_has_method_key(o) :- hc_preq_init(_, _, o), rd_obj_prop(o, key, _), ts_http_init_method_key(key). hc_send(oc, from, a, vv, p) :- hc_preq_at(oc, ce, a, p), hc_preq_verb(oc, ce, vv), expr_enclosing_method(ce, from). diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context index 8b8419150..df420db32 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context @@ -354,6 +354,14 @@ def _route_parts(r): return [('*' if (p.startswith('{') or p.startswith(':') or p.startswith('<') or p == '*') else p) for p in r.strip('/').split('/') if p] +def _holes_match(a, hp): + """Whether the asked segments `a` are the route parts `hp`, where None is a hole of any number of segments and + every other part is the same segment (a parameter `*` only for a parameter).""" + if not hp: return not a + if hp[0] is None: return any(_holes_match(a[i:], hp[1:]) for i in range(len(a) + 1)) + return bool(a) and a[0] == hp[0] and _holes_match(a[1:], hp[1:]) + + def _route_eq(a, b): return len(a) == len(b) and all(x == y or '*' in (x, y) for x, y in zip(a, b)) @@ -525,18 +533,20 @@ def route_table_lines(g, text, cap=8): # through parameters and holes (`/{}/:action`), each with what is decided of its chain and what may run hitp = [] for h, v, p, app, why in sorted({tuple(r) for r in g.q("SELECT c0, c1, c2, c3, c4 FROM ext_http_route_partial")}): - kp0 = [x for x in _route_parts(p.replace('{}', ''))] + # a hole (`{}`) is a part of the path decided at run time: any number of segments, before the known tail + # or between two known parts (`/v1/{}/ping`) + hp0 = [None if x == '\x00' else x for x in _route_parts(p.replace('{}', '\x00'))] + while hp0 and hp0[0] is None: hp0 = hp0[1:] # an optional last parameter (`:module?`) may be left off by the request - kps = [kp0] + ([kp0[:-1]] if kp0 and p.rstrip('/').endswith('?') and len(kp0) > 1 else []) + hps = [hp0] + ([hp0[:-1]] if hp0 and hp0[-1] is not None and p.rstrip('/').endswith('?') and len(hp0) > 1 else []) best = None - for (w, a), kp in [(x, k) for x in asked for k in kps]: + for (w, a), hp in [(x, k) for x in asked for k in hps]: + kp = [x for x in hp if x is not None] if not (vok(w, v) and len(a) >= len(kp) and kp): continue - tail = a[-len(kp):] # a parameter the question writes stands for a parameter of the route, never for one of its literal # segments, and at least one literal segment must be the same: `/files` is no answer to `/x/:module` - if any(x != y and (x == '*' or y == '*') and not (x == '*' and y == '*') for x, y in zip(tail, kp)): continue - if any(x != y for x, y in zip(tail, kp) if x != '*' and y != '*'): continue - lit = sum(1 for x, y in zip(tail, kp) if x == y and x != '*') + if not _holes_match(a, [None] + hp): continue + lit = sum(1 for y in kp if y != '*') if not lit: continue rank = (0 if len(kp) == len(a) else 1, -lit, len(a) - len(kp)) best = rank if best is None or rank < best else best diff --git a/tests/cases/typescript/route-hole-in-the-middle/case.json b/tests/cases/typescript/route-hole-in-the-middle/case.json new file mode 100644 index 000000000..f4af80d72 --- /dev/null +++ b/tests/cases/typescript/route-hole-in-the-middle/case.json @@ -0,0 +1,9 @@ +{"lang": "typescript", "src": "src", + "checks": [ + {"why": "a route whose mount prefix has a run-time piece in the MIDDLE (`/v1/{}/ping`) answers a question about a full path it can be (`/v1/system/ping`), with the scoped middleware that may run on it; the lookup dropped the hole and compared `/v1/ping` against the asked path's tail", + "run": ["context", "what runs before GET /v1/system/ping"], + "want": ["route: GET /v1/{}/ping", "may run (scope /v1/* vs unknown prefix)"]}, + {"why": "CONTROL: a path whose known first segment differs (`/v2/system/ping`) is not that route", + "run": ["context", "what runs before GET /v2/system/ping"], + "avoid": ["route: GET /v1/{}/ping"]} + ]} diff --git a/tests/cases/typescript/route-hole-in-the-middle/src/main.ts b/tests/cases/typescript/route-hole-in-the-middle/src/main.ts new file mode 100644 index 000000000..66e28cbeb --- /dev/null +++ b/tests/cases/typescript/route-hole-in-the-middle/src/main.ts @@ -0,0 +1,15 @@ +import { Hono } from 'hono' +import { system } from './system' + +const VERSION = 'v1' +declare const process: { env: Record } + +const app = new Hono() +app.use(`/${VERSION}/*`, async (c, next) => { + if (!c.req.header('authorization')) return c.text('no', 401) + await next() +}) +const group = process.env.GROUP ?? 'system' +app.route(`/${VERSION}/${group}`, system) + +export default app diff --git a/tests/cases/typescript/route-hole-in-the-middle/src/system.ts b/tests/cases/typescript/route-hole-in-the-middle/src/system.ts new file mode 100644 index 000000000..7f73223c2 --- /dev/null +++ b/tests/cases/typescript/route-hole-in-the-middle/src/system.ts @@ -0,0 +1,5 @@ +import { Hono } from 'hono' + +export const system = new Hono() +system.get('/ping', (c) => c.text('pong')) +system.get('/status/:part', (c) => c.json({ part: c.req.param('part') })) From af5738aa09d0ab7474ec31feb52b852534cc2d6f Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Fri, 30 Oct 2026 02:40:15 -0700 Subject: [PATCH 4/9] typescript: the Hono route model's middleware scope checks, loop-mount prefix scan and openapi definition lookups do the same work in a fraction of the time - The loop-mount prefix scan read every middleware key of every app character by character; it now scans only the keys that hold a loop-mount mark. - Scope and path pairs were built through every (middleware key, handler key) pair; they are now built from each root's distinct scopes and distinct paths. A pair is only consulted beside such a key pair and a segment's kind depends on the segment alone, so the decisions are the same. - The param_vs_literal_scope declaration starts from the few undecidable (scope, path) pairs instead of every key pair. - The earliest registration key of a route is taken once per (handler, verb, path, app) instead of once per row of the group. - Two recursive rules (a route definition read through a namespace import, and a call through a destructured name) start from their recursive atom instead of rescanning every property or variable read each round. - Object literals and parameters are read only for the expressions a route definition is taken from, never for every object literal and parameter read in the program. Every output relation is identical, as a sorted multiset, on six projects. On the largest of them the solve CPU goes from 34.6 s to 24.8 s. --- .../config-resolution/http-router-apps.dl | 49 ++++++++++++++----- .../config-resolution/http-router-routes.dl | 29 +++++++++-- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/graph/typescript/engine/config-resolution/http-router-apps.dl b/graph/typescript/engine/config-resolution/http-router-apps.dl index 1e71a21b6..eb289808c 100644 --- a/graph/typescript/engine/config-resolution/http-router-apps.dl +++ b/graph/typescript/engine/config-resolution/http-router-apps.dl @@ -373,8 +373,9 @@ hr_app(e, a) :- hr_destr_of(e, v), hr_app(v, a). .decl hr_viacall(c0:symbol, c1:symbol, c2:symbol, c3:symbol) hr_viacall(e, ce, w, v) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, q), hr_objcall(q, ce, w, o), rd_obj_prop(o, n, v). -hr_viacall(e, ce, w, v) :- expr_referenced("client", "VARIABLE", d, e), var_binding("client", _, "PROPERTY", src, i, d), i != "", - hr_objcall(i, ce, w, o), rd_obj_prop(o, src, v). +// (from the call's object outward: this is recursive, and starting at every variable read rescanned them all each round) +hr_viacall(e, ce, w, v) :- hr_objcall(i, ce, w, o), var_binding("client", _, "PROPERTY", src, i, d), i != "", + rd_obj_prop(o, src, v), expr_referenced("client", "VARIABLE", d, e). // (list, call, created app) .decl hr_objret_inst(c0:symbol, c1:symbol, c2:symbol) hr_objret_inst(l, ce, t) :- hr_viacall(_, ce, w, v), hr_app(v, t), expr_enclosing_method(t, w), l = cat(ce, cat("#", t)). @@ -412,12 +413,22 @@ hr_def_val(x, o) :- hr_on(), call_site("client", "FUNCTION_CALL", _, _, _, x, _) import_binding("client", _, _, orig, _, ih), hr_import_spec(ih, spec), router_route_def(spec, orig), expr_child("client", x, "ARGUMENT", "0", a), hr_def_obj(a, o). hr_def_val(r, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_def_val(i, o). -hr_def_val(e, o) :- hr_on(), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, c), hr_def_val(c, o). -// an object literal written in place or held by a const, through `as T` / `satisfies T` / parentheses +hr_def_val(e, o) :- hr_def_val(c, o), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), expr_kind_is_transparent(k). +// an object literal written in place or held by a const, through `as T` / `satisfies T` / parentheses: read only for +// the expressions a definition is taken from (createRoute's argument, a wrapper call's arguments), never for every +// object literal and every read of one in the program +.decl hr_def_obj_need(c0:symbol) +hr_def_obj_need(a) :- hr_on(), call_site("client", "FUNCTION_CALL", _, _, _, x, _), + expr_child("client", x, "METHOD_NAME", _, f), expr_referenced("client", "IMPORT_BINDING", ih, f), + import_binding("client", _, _, orig, _, ih), hr_import_spec(ih, spec), router_route_def(spec, orig), + expr_child("client", x, "ARGUMENT", "0", a). +hr_def_obj_need(a) :- hr_def_call(x, _), expr_child("client", x, "ARGUMENT", _, a). +hr_def_obj_need(c) :- hr_def_obj_need(e), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, c). +hr_def_obj_need(i) :- hr_def_obj_need(r), hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "". .decl hr_def_obj(c0:symbol, c1:symbol) -hr_def_obj(o, o) :- hr_on(), expr_kind("client", "OBJECT_LITERAL", _, o). -hr_def_obj(e, o) :- hr_on(), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, c), hr_def_obj(c, o). -hr_def_obj(r, o) :- hr_on(), hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_def_obj(i, o). +hr_def_obj(o, o) :- hr_def_obj_need(o), expr_kind("client", "OBJECT_LITERAL", _, o). +hr_def_obj(e, o) :- hr_def_obj_need(e), expr_kind("client", k, _, e), expr_kind_is_transparent(k), expr_child("client", e, _, _, c), hr_def_obj(c, o). +hr_def_obj(r, o) :- hr_def_obj_need(r), hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_def_obj(i, o). // …a project function that RETURNS a definition (`authRoute(scope, { method, path, .. })` returning // `createRoute({ ...config, middleware })`): the call is a definition, the object the function builds. Its keys spread // from a parameter (`...config`) are that call's argument's own (hr_def_spread), so each call is its own route @@ -429,14 +440,26 @@ hr_def_val(x, o) :- hr_def_call(x, w), hr_ret_expr(w, e), hr_def_val(e, o). hr_def_spread(x, o, o2) :- hr_def_call(x, w), hr_ret_expr(w, e), hr_def_val(e, o), expr_child("client", o, "SPREAD_OPERAND", _, sx), hv_spread_src(sx, y), hr_def_param(y, p), param_decl("client", _, pos, _, w, p), expr_child("client", x, "ARGUMENT", pos, a), hr_def_obj(a, o2). hr_def_val(x, o2) :- hr_def_spread(x, _, o2). -// a reference to a parameter, through `as T` and parentheses +// a reference to a parameter, through `as T` and parentheses; read only where a definition can name one: a spread +// into an object, a definition's method or path value, the receiver of a property read inside an array spread +.decl hr_def_param_need(c0:symbol) +hr_def_param_need(y) :- hv_spread_src(_, y). +hr_def_param_need(pv) :- rd_obj_prop(_, k, pv), (router_def_key(k, "method") ; router_def_key(k, "path")). +hr_def_param_need(q) :- hr_sp_el(z), property_access_name(z, _), expr_child("client", z, "RECEIVER", _, q). +hr_def_param_need(c) :- hr_def_param_need(y), expr_kind("client", k, _, y), expr_kind_is_transparent(k), expr_child("client", y, _, _, c). +.decl hr_sp_el(c0:symbol) +hr_sp_el(y) :- hr_on(), expr_kind("client", "SPREAD_ELEMENT", _, el), expr_child("client", el, _, _, y). +hr_sp_el(c) :- hr_sp_el(y), expr_kind("client", k, _, y), expr_kind_is_transparent(k), expr_child("client", y, _, _, c). +hr_sp_el(c) :- hr_sp_el(y), expr_kind("client", "BINARY_EXPRESSION", _, y), (expr_operator("client", "??", y) ; expr_operator("client", "||", y)), + expr_child("client", y, "LEFT_OPERAND", _, c). .decl hr_def_param(c0:symbol, c1:symbol) -hr_def_param(y, p) :- hr_on(), expr_referenced("client", "PARAMETER", p, y). -hr_def_param(y, p) :- hr_on(), expr_kind("client", k, _, y), expr_kind_is_transparent(k), expr_child("client", y, _, _, c), hr_def_param(c, p). +hr_def_param(y, p) :- hr_def_param_need(y), expr_referenced("client", "PARAMETER", p, y). +hr_def_param(y, p) :- hr_def_param_need(y), expr_kind("client", k, _, y), expr_kind_is_transparent(k), expr_child("client", y, _, _, c), hr_def_param(c, p). // `import * as routes from './routes'; app.openapi(routes.get, h)` -hr_def_val(e, o) :- property_access_name(e, n), expr_child("client", e, "RECEIVER", _, r), - expr_referenced("client", "IMPORT_BINDING", ih, r), import_binds_namespace(ih, "client", m), - module_export_any("client", m, n, "VARIABLE", v), var_initializer("client", _, i, v), hr_def_val(i, o). +// (from the definition outward: it is recursive, and starting at every property read rescanned them all each round) +hr_def_val(e, o) :- hr_def_val(i, o), var_initializer("client", _, i, v), module_export_any("client", m, n, "VARIABLE", v), + import_binds_namespace(ih, "client", m), expr_referenced("client", "IMPORT_BINDING", ih, r), + expr_child("client", e, "RECEIVER", _, r), property_access_name(e, n). .decl hr_openapi_def(c0:symbol, c1:symbol) hr_openapi_def(ce, o) :- hr_reg(ce, _, cn), router_openapi(cn), expr_child("client", ce, "ARGUMENT", "0", x), hr_def_val(x, o). rd_demand(v) :- hr_openapi_def(_, o), rd_obj_prop(o, k, v), (router_def_key(k, "method") ; router_def_key(k, "path")). diff --git a/graph/typescript/engine/config-resolution/http-router-routes.dl b/graph/typescript/engine/config-resolution/http-router-routes.dl index 05f58e456..e638361e2 100644 --- a/graph/typescript/engine/config-resolution/http-router-routes.dl +++ b/graph/typescript/engine/config-resolution/http-router-routes.dl @@ -532,7 +532,10 @@ hr_route_row(m, vv, fp, ai, fk) :- hr_fentry(r, fk, "route", vv, fp, ce, n), hr_ .decl hr_route_rl(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol, c5:number, c6:number) hr_route_rl(m, vv, fp, ai, fk, to_number(substr(fk, 0, 7)), to_number(substr(fk, 8, 5))) :- hr_route_row(m, vv, fp, ai, fk), strlen(fk) >= 13. .decl hr_route_ml(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) -hr_route_ml(m, vv, fp, ai, l) :- hr_route_rl(m, vv, fp, ai, _, _, _), l = min x : { hr_route_rl(m, vv, fp, ai, _, x, _) }. +// (the minimum once per (handler, verb, path, app), not once per row of the group) +.decl hr_route_grp(c0:symbol, c1:symbol, c2:symbol, c3:symbol) +hr_route_grp(m, vv, fp, ai) :- hr_route_rl(m, vv, fp, ai, _, _, _). +hr_route_ml(m, vv, fp, ai, l) :- hr_route_grp(m, vv, fp, ai), l = min x : { hr_route_rl(m, vv, fp, ai, _, x, _) }. .decl hr_route_mc(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number, c5:number) hr_route_mc(m, vv, fp, ai, l, c) :- hr_route_ml(m, vv, fp, ai, l), c = min y : { hr_route_rl(m, vv, fp, ai, _, l, y) }. .decl hr_route_cand(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) @@ -691,8 +694,16 @@ hr_calls_next(m) :- hr_node_fn(_, _, m), param_decl("client", _, "1", _, m, p), .decl hr_verb_ok(c0:symbol, c1:symbol) hr_verb_ok("ANY", v) :- hr_hent(_, _, _, v, _). hr_verb_ok(v, v) :- hr_hent(_, _, _, v, _). +// (scope, path) pairs whose match is decided: every scope a root's middleware writes against every path its handlers have, +// distinct values first. The join through each (middleware key, handler key) pair with mk < hk gave the same decisions +// (a pair is only consulted beside such a key pair, and a segment's kind depends on the segment alone) at the size of +// hr_applies (2.1 s for 9k pairs on a large project) .decl hr_spair(c0:symbol, c1:symbol) -hr_spair(q, p) :- hr_ment(r, mk, _, _, q, _), hr_hent(r, hk, _, _, p), mk < hk. +.decl hr_rscope(c0:symbol, c1:symbol) +hr_rscope(r, q) :- hr_ment(r, _, _, _, q, _). +.decl hr_rpath(c0:symbol, c1:symbol) +hr_rpath(r, p) :- hr_hent(r, _, _, _, p). +hr_spair(q, p) :- hr_rscope(r, q), hr_rpath(r, p). // segments of a path, empty ones skipped .decl hr_spath(c0:symbol) @@ -745,8 +756,12 @@ hr_applies(r, hk, mk, mce, mn) :- hr_ment(r, mk, mce, mn, q, mv), hr_hent(r, hk, // the earlier arguments of the route's own call hr_applies(r, hk, mk, ce, mn) :- hr_hent(r, hk, ce, vv, fp), hr_fentry(r, mk, "route", vv, fp, ce, mn), hr_last(ce, n), mn < n, !hr_dyn_arg(ce, mn). -framework_undetermined(mw, "middleware_scope", "param_vs_literal_scope") :- hr_ment(r, mk, mce, mn, q, mv), - hr_hent(r, hk, _, hv, p), mk < hk, hr_verb_ok(mv, hv), hr_len_ok(q, p), !hr_conflict(q, p), hr_undec(q, p), hr_node(mce, mn, mw). +// (the few undecidable (scope, path) pairs first, then the entries holding them: joined the other way round it walked +// every middleware/handler pair of every root for 8 rows, 1.7 s on a large project) +.decl hr_undec_pair(c0:symbol, c1:symbol) +hr_undec_pair(q, p) :- hr_undec(q, p), hr_len_ok(q, p), !hr_conflict(q, p). +framework_undetermined(mw, "middleware_scope", "param_vs_literal_scope") :- hr_undec_pair(q, p), hr_ment(r, mk, mce, mn, q, mv), + hr_hent(r, hk, _, hv, p), mk < hk, hr_verb_ok(mv, hv), hr_node(mce, mn, mw). // a scoped `use` against a route under an unknown prefix cannot be decided (SPEC 3.3 [iter2]): no row, // and the middleware says so; a `*` scope on the same app is decidable and is not declared framework_undetermined(mw, "middleware_scope", "partial_route_scope") :- hr_fentry(r, mk, "mw", _, q, mce, mn), @@ -851,7 +866,11 @@ framework_undetermined(h, "middleware_chain", "dynamic_middleware_list") :- hr_u // a chain holding a middleware that ANOTHER element of a loop mount brought in (one candidate's `*` use reaches the // routes of the candidates after it): which elements run, and in what order, is decided at run time (SPEC R13b) .decl hr_each_pfx(c0:symbol, c1:symbol) -hr_each_pfx(mk, pfx) :- hr_applies_k(_, _, mk), i = range(0, strlen(mk)), substr(mk, i, 1) = "~", pfx = substr(mk, 0, i + 6). +// (a key is scanned once, and only one that holds a loop-mount mark at all: the scan per applies row, of every key, cost +// 3 s for no row on a large project) +.decl hr_each_key(c0:symbol) +hr_each_key(mk) :- hr_applies_k(_, _, mk), contains("~", mk). +hr_each_pfx(mk, pfx) :- hr_each_key(mk), i = range(0, strlen(mk)), substr(mk, i, 1) = "~", pfx = substr(mk, 0, i + 6). framework_undetermined(h, "middleware_chain", "dynamic_middleware_list") :- hr_applies_k(r, hk, mk), hr_each_pfx(mk, pfx), (strlen(hk) < strlen(pfx) ; substr(hk, 0, strlen(pfx)) != pfx), hr_hent(r, hk, ce, _, _), hr_last(ce, n), hr_route_node(ce, n, h). From 3f362209380c9ea53541bbe6fe0b008791dbde59 Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Fri, 30 Oct 2026 02:40:25 -0700 Subject: [PATCH 5/9] typescript: a Hono helper that mounts a sub-app read from its parameter no longer serves one call's sub-app under another call's app The returned-object-through-a-parameter rule took every call's object at once, so with attach(host, ordersModule()) and attach(host, invoicesModule()) each host served both modules' routes. When objects returned by two different functions reach a parameter that a mount call in its function reads its sub-app from, that parameter no longer carries them, and the sub-apps they hold are declared unresolved_mount instead. One function's results reaching it (the same factory called at several sites) keep their routes. Which functions reach a parameter is decided from calls and bindings alone, before any app is known. A tests/run.py case pins both shapes: the two false routes the previous rules printed are gone, and the same-factory control keeps its routes. --- .../config-resolution/http-router-apps.dl | 46 ++++++++++++++++++- .../case.json | 12 +++++ .../src/main.ts | 17 +++++++ .../src/modules.ts | 9 ++++ .../src/regions.ts | 17 +++++++ .../src/routes/invoices.ts | 8 ++++ .../src/routes/orders.ts | 8 ++++ 7 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/case.json create mode 100644 tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/main.ts create mode 100644 tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/modules.ts create mode 100644 tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/regions.ts create mode 100644 tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/routes/invoices.ts create mode 100644 tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/routes/orders.ts diff --git a/graph/typescript/engine/config-resolution/http-router-apps.dl b/graph/typescript/engine/config-resolution/http-router-apps.dl index eb289808c..2e29dcd35 100644 --- a/graph/typescript/engine/config-resolution/http-router-apps.dl +++ b/graph/typescript/engine/config-resolution/http-router-apps.dl @@ -322,10 +322,51 @@ hr_objcall(ce, ce, w, o) :- hr_call_of(ce, w), hr_ret_expr(w, e), hr_objlit(e, o hr_objcall(r, ce, w, o) :- hr_denotes_var(r, v), var_initializer("client", _, i, v), i != "", hr_objcall(i, ce, w, o). // …or a PARAMETER such a result is passed into (`mountHttp(runtime)` where `runtime = await createRuntime()`, then // `const { usersRoutes } = runtime` inside): what each call passes there +// One source only: when the results of two DIFFERENT functions reach the parameter (`attach(host, ordersModule())`, +// `attach(host, invoicesModule())`), a read of it has no call context to tell which call's object it is, and every +// mount the function makes would take both objects' sub-apps (hono-8-01); those sub-apps are declared instead +// (hr_unknown_mount, unresolved_mount) hr_objcall(r, ce, w, o) :- hr_objcall(a, ce, w, o), expr_child("client", oc, "ARGUMENT", pos, a), hr_call_of(oc, f), - param_decl("client", _, pos, _, f, p), expr_referenced("client", "PARAMETER", p, r). + param_decl("client", _, pos, _, f, p), !hr_objpar_amb(p), expr_referenced("client", "PARAMETER", p, r). hr_objcall(e, ce, w, o) :- hr_objcall(c, ce, w, o), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +// ── a returned object handed to a PARAMETER: one source or several ───────────── +// hr_objcall follows an object a function returns into a parameter it is passed to (`mountHttp(runtime)`), with no +// call context: every call's object reaches every read of the parameter. That is right while every call passes the +// result of the SAME function (each call's apps are copies of one template), and wrong when two calls pass objects of +// two different functions: each mount the function makes would take both objects' sub-apps (hono-8-01). Which +// functions' results reach a parameter is decided here from calls and bindings only, before any app is known, so the +// app flow can test it. +// a function that returns an object literal (written in place, or a const holding one) +.decl hr_objfn(c0:symbol) +hr_objfn(w) :- hr_on(), hr_ret_expr(w, e), expr_kind("client", "OBJECT_LITERAL", _, e). +hr_objfn(w) :- hr_on(), hr_ret_expr(w, e), hr_denotes_var(e, v), var_initializer("client", _, i, v), i != "", + expr_kind("client", "OBJECT_LITERAL", _, i). +// (expression, function whose returned object it holds) +.decl hr_psrc(c0:symbol, c1:symbol) +hr_psrc(x, w) :- hr_objfn(w), hr_call_of(x, w). +hr_psrc(e, w) :- hr_psrc(c, w), expr_child("client", e, _, _, c), expr_kind("client", k, _, e), + (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +hr_psrc(r, w) :- hr_psrc(i, w), var_initializer("client", _, i, v), hr_denotes_var(r, v). +hr_psrc(r, w) :- hr_psrc_p(p, w), expr_referenced("client", "PARAMETER", p, r). +// (parameter, function whose returned object a call passes there) +.decl hr_psrc_p(c0:symbol, c1:symbol) +hr_psrc_p(p, w) :- hr_psrc(a, w), expr_child("client", oc, "ARGUMENT", pos, a), hr_call_of(oc, f), + param_decl("client", _, pos, _, f, p). +// a parameter a mount in its function reads its sub-app from (`const { http } = mod; host.route('/', http)`, +// `host.route('/', mod.http)`): the only place where taking every call's object at once makes a route false +.decl hr_marg_need(c0:symbol) +hr_marg_need(sx) :- hr_on(), call_site("client", "METHOD_CALL", cn, _, _, ce, _), router_mount_app(cn), + expr_child("client", ce, "ARGUMENT", "1", sx). +hr_marg_need(c) :- hr_marg_need(x), expr_kind("client", k, _, x), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"), + expr_child("client", x, _, _, c). +hr_marg_need(q) :- hr_marg_need(x), property_access_name(x, _), expr_child("client", x, "RECEIVER", _, q). +hr_marg_need(i) :- hr_marg_need(x), expr_referenced("client", "VARIABLE", d, x), var_binding("client", _, "PROPERTY", _, i, d), i != "". +.decl hr_pmount(c0:symbol) +hr_pmount(p) :- hr_marg_need(x), expr_referenced("client", "PARAMETER", p, x). +// such a parameter two different functions' objects reach +.decl hr_objpar_amb(c0:symbol) +hr_objpar_amb(p) :- hr_pmount(p), hr_psrc_p(p, w1), hr_psrc_p(p, w2), w1 < w2. // an element of a const array literal, read through a for..of binding over it // (`for (const p of plugins) app.route(p.prefix, p.app)`) .decl hr_arr(c0:symbol, c1:symbol) @@ -666,6 +707,9 @@ hr_root(a) :- hr_list(a), !hr_included(a). .decl hr_unknown_mount(c0:symbol) hr_unknown_mount(s) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), router_mount_app(cn), !hr_app(r, _), expr_child("client", ce, "ARGUMENT", "1", sx), hr_app(sx, s). +// …or an app held by an object that reaches a parameter together with another function's object (hono-8-01): the +// function mounts one of them per call, and which one is not followed here +hr_unknown_mount(s) :- hr_objpar_amb(p), hr_psrc_p(p, w), hr_objcall(_, _, w, o), rd_obj_prop(o, _, v), hr_app(v, s). // ── a declarative route list (SPEC ruling R13) ────────────────────────────── // `const routes = [{ path: '/x', handler: subApp }]; for (const r of routes) app.route(r.path, r.handler)`. diff --git a/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/case.json b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/case.json new file mode 100644 index 000000000..9d91dcaac --- /dev/null +++ b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/case.json @@ -0,0 +1,12 @@ +{"lang": "typescript", "src": "src", + "checks": [ + {"why": "two calls of one helper pass the objects of two DIFFERENT functions into its parameter; the helper mounts the sub-app it destructures, so each call's app serves its own module's routes only, never the other module's handler under its prefix", + "run": ["context", "who serves DELETE /orders/:invoiceId"], + "avoid": ["route: DELETE /orders/:invoiceId"]}, + {"why": "the same, the other way round", + "run": ["context", "who serves DELETE /invoices/:orderId"], + "avoid": ["route: DELETE /invoices/:orderId"]}, + {"why": "CONTROL: a helper whose parameter only ever receives the object of ONE function (called twice, mounted at /east and /west) still mounts its sub-app at each prefix", + "run": ["context", "who serves DELETE /east/:orderId"], + "want": ["route: DELETE /east/:orderId -> src/routes/orders.ts:6"]} + ]} diff --git a/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/main.ts b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/main.ts new file mode 100644 index 000000000..9c4e38248 --- /dev/null +++ b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/main.ts @@ -0,0 +1,17 @@ +import { Hono } from 'hono' +import { orderModule, invoiceModule } from './modules' + +function attach(host: Hono, mod: { http: Hono; name: string }) { + const { http } = mod + host.route('/', http) + return host +} + +const orders = attach(new Hono(), orderModule()) +const invoices = attach(new Hono(), invoiceModule()) + +const app = new Hono() +app.route('/orders', orders) +app.route('/invoices', invoices) + +export default app diff --git a/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/modules.ts b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/modules.ts new file mode 100644 index 000000000..a3ec170c6 --- /dev/null +++ b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/modules.ts @@ -0,0 +1,9 @@ +import { orderRoutes } from './routes/orders' +import { invoiceRoutes } from './routes/invoices' + +export function orderModule() { + return { http: orderRoutes(), name: 'orders' } +} +export function invoiceModule() { + return { http: invoiceRoutes(), name: 'invoices' } +} diff --git a/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/regions.ts b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/regions.ts new file mode 100644 index 000000000..3807d4c46 --- /dev/null +++ b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/regions.ts @@ -0,0 +1,17 @@ +import { Hono } from 'hono' +import { orderModule } from './modules' + +function attachRegion(host: Hono, mod: { http: Hono; name: string }) { + const { http } = mod + host.route('/', http) + return host +} + +const east = attachRegion(new Hono(), orderModule()) +const west = attachRegion(new Hono(), orderModule()) + +const regions = new Hono() +regions.route('/east', east) +regions.route('/west', west) + +export default regions diff --git a/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/routes/invoices.ts b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/routes/invoices.ts new file mode 100644 index 000000000..77feca983 --- /dev/null +++ b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/routes/invoices.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function invoiceRoutes(): Hono { + const r = new Hono() + r.get('/', (c) => c.json([])) + r.delete('/:invoiceId', (c) => c.text('voided ' + c.req.param('invoiceId'))) + return r +} diff --git a/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/routes/orders.ts b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/routes/orders.ts new file mode 100644 index 000000000..cda6967df --- /dev/null +++ b/tests/cases/typescript/returned-objects-of-two-functions-through-one-parameter/src/routes/orders.ts @@ -0,0 +1,8 @@ +import { Hono } from 'hono' + +export function orderRoutes(): Hono { + const r = new Hono() + r.get('/', (c) => c.json([])) + r.delete('/:orderId', (c) => c.text('cancelled ' + c.req.param('orderId'))) + return r +} From 6f9985d1cd8fa536bae8be3905de03cb14c2ba63 Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Fri, 30 Oct 2026 02:41:54 -0700 Subject: [PATCH 6/9] typescript, javascript: call chains follow element types, this inside arrows, decided table keys, functions handed to project functions, and context values to the services they hold Rules that close breaks in request call chains, each framework-free unless named: - Element types: a value read as an element of an array or tuple, or of what a function returns without a written return type, has the element's type; tuple destructuring gives each name its own position's type, and a member of an object literal shape keeps the type of what was written there. - this inside an arrow function is the this of the method or function that encloses it, through any depth of nested arrows. - A table of functions read under a decided key (a literal, a const, or a const-object member) runs only that key's entry. A key known only at run time stays an unknown call instead of reaching every entry; a typed table's entries under such a key are value candidates of its index signature. - A function handed to a project function (withRetry(2, () => load(id)), apply(upper, s)) is a hop from the hand-over site, under the new tier callback_handed, where the callee's body can run it. It is not emitted for a getter read, for a parameter the body never reads or only compares, or for a keyed registry's slot, which the publish on the same key runs. - Hono: a value read from the request context (c.get, c.var, destructured c.var) has the type the app's environment declares for its key, or, when it declares none, the type of what the app's own middleware writes under that key. A context key also flows along a partial route's decided chain. JavaScript gets the same context-value link, and exports its dispatch and function-value envelope under the TypeScript file names. - The string evaluation reads a called function's returned root instead of joining every returned sub-expression with its parents, which keeps the index cost flat. The bundle schema documents callback_handed as a hand-over tier, and the chain reader ranks it beside callback_registered. The TypeScript edge normalizer skips it in compiler pairs, as it skips callback_registered. Fourteen TypeScript regression goldens change, each row checked against its fixture. Each added callback_handed row is a function handed to a project function whose body calls or passes on that parameter: apply calls t (08), fromArgument calls pick (09), withSink calls run (34), $constructor calls init (59), makeThing and useMemoLike call f (75), each calls fn (78-hof-callback-at-library-boundary), Set.forEach, Promise.then, Lazy.then and Lazy.map call cb (81), and Pipeline.map hands project to Array.map (82). In 80-generic-return-receiver the elements of a typed table read under a computed key move from value_call to value candidates of the index signature. In 78-callable-collections the two value_call rows of an untyped table read under a computed key are dropped; the call stays ambiguous_unknown. --- graph/bundle/SCHEMA.md | 3 +- graph/bundle/schema.ts | 3 +- .../call-edge-generation/dispatch-envelope.dl | 14 ++ .../engine/config-resolution/knobs.dl | 8 + .../http-router-ctx-value.dl | 30 +++ graph/javascript/souffle/decls_all.dl | 2 + graph/javascript/souffle/export_manifest.tsv | 2 + .../78-callable-collections/src/pipeline.ts | 2 +- .../08-function-values-and-callbacks.edges | 2 + ...08-function-values-and-callbacks.lib.edges | 2 + .../expected/09-generics-substitution.edges | 1 + .../09-generics-substitution.lib.edges | 1 + .../expected/34-callback-of-callback.edges | 1 + .../59-merged-export-through-namespace.edges | 3 + .../expected/75-jsx-wrapper-guards.edges | 2 + .../expected/78-callable-collections.envelope | 2 - .../78-hof-callback-at-library-boundary.edges | 1 + .../80-generic-return-receiver.envelope | 6 +- .../80-generic-return-receiver.lib.envelope | 6 +- .../81-contextual-lambda-param-no-lib.edges | 4 + ...structural-conformer-across-programs.edges | 1 + .../test/typescript/tools/normalize_edges.py | 6 +- .../engine/call-edge-generation/call_chain.dl | 42 ++++ .../engine/config-resolution/knobs.dl | 24 +++ .../engine/framework-behavior/destinations.dl | 7 +- .../framework-behavior/http-router-chain.dl | 3 + .../http-router-ctx-type.dl | 180 ++++++++++++++++++ .../engine/resolution/element-values.dl | 42 ++++ .../engine/resolution/lexical-this.dl | 33 ++++ .../engine/resolution/reference-types.dl | 1 + .../engine/resolution/shape-members.dl | 40 ++++ .../engine/resolution/value-flow.dl | 66 ++++++- .../skills/axiomcode/scripts/ax_edges.py | 4 +- 33 files changed, 525 insertions(+), 19 deletions(-) create mode 100644 graph/javascript/engine/call-edge-generation/dispatch-envelope.dl create mode 100644 graph/javascript/engine/framework-behavior/http-router-ctx-value.dl create mode 100644 graph/typescript/engine/framework-behavior/http-router-ctx-type.dl create mode 100644 graph/typescript/engine/resolution/element-values.dl create mode 100644 graph/typescript/engine/resolution/lexical-this.dl create mode 100644 graph/typescript/engine/resolution/shape-members.dl diff --git a/graph/bundle/SCHEMA.md b/graph/bundle/SCHEMA.md index 6cd2c4bd2..3b54dcc71 100644 --- a/graph/bundle/SCHEMA.md +++ b/graph/bundle/SCHEMA.md @@ -566,6 +566,7 @@ THE GRAPH. One row per (site, resolved target). A site with N possible targets h | `fan_capped` | javascript, java, csharp | More targets than --dispatch-cap: the set was refused rather than emitted. JavaScript: callee is NULL. Java and C#: callee is the declared base method the fan would have started from; dispatch-capped-sites.csv carries the refused count. | | `callback_registered` | javascript, typescript | The site HANDS the callee this function (`xs.forEach(f)`, `p.then(f)`, `emitter.on('x', h)`, `setTimeout(f)`), which may invoke it. Not the site's own callee; a reachability edge, labelled so it is never read as a resolved call. | | `callback_registered` | python | A method or function named as a VALUE and handed over, stored or returned (`pool.submit(self.work)`, `Thread(target=svc.run)`, `sorted(xs, key=self.rank)`, `[h.on_event]`), always kind METHOD_REFERENCE. Its target is resolved the way a call on the same receiver would be; whoever receives the value calls it. A reachability edge, labelled so it is never read as a resolved call. | +| `callback_handed` | typescript | The site hands a function to a PROJECT function whose body can run it (`withRetry(2, () => load(id))`, `apply(upper, s)`). Not the site's own callee (that is the row for the called function); a reachability edge from the hand-over site, like callback_registered. Not emitted for a getter read, a parameter the body never reads or only compares, or a keyed registry's slot. | | `event_dispatch` | javascript | `x.emit('name')` reaching a handler registered by `x.on('name', h)` on a value x may hold — name-sensitive for literal names, every handler on that value for a computed one. | | `event_dispatch` | typescript | A publish on a project class's keyed registry reaching each function filed under the same key: `bus.publish('x', p)` where `publish` (or a method it passes the key to) calls what `this.handlers.get(name)` holds, to the function a `bus.subscribe('x', f)` stored there, or an entry `{ ['x']: f }` of a table a subscribe-all method files by key. A literal, a const or a const-object member key; a key only known at run time matches every key, and a constant the dispatch also looks up (a wildcard) matches every publish. The site is the publish call (call-edge-generation/keyed_registry.dl). | | `event_dispatch` | java | A Spring application event: `publishEvent(e)` reaching each listener (`@EventListener`, `@TransactionalEventListener`, `ApplicationListener.onApplicationEvent`) whose declared event type e's static type is, or is a subtype of. Added beside the publishEvent boundary row, never in place of it (call-edge-generation/event_dispatch.dl). Also a JPA entity write (`save`, `persist`, `merge`, `delete` on a repository or EntityManager) reaching the `@PrePersist`/`@PreUpdate`/`@PreRemove`-style callbacks of the written entity's type and of the listeners `@EntityListeners` names on it or a superclass (kind entity_callback; call-edge-generation/entity_lifecycle.dl). | @@ -588,7 +589,7 @@ THE GRAPH. One row per (site, resolved target). A site with N possible targets h - **python** — A `boundary_lib` edge may point at a builtin (callee_provenance builtin, callee_label `builtin:NAME`) or at an unstaged import path (callee_provenance external) — neither has a methods row. - **java** — A `boundary_lib` edge with callee_provenance external names a method of an ancestor type no staged IR declares (callee_label `external:.`, no methods row). A site whose receiver is declared as such a type is multi_inferred even with one client override: the platform method itself, and the platform's own subclasses, are the other possible targets. Stage the library to replace the label with the real method. - **python** — The reason a site is ambiguous_unknown is exported per site in ext_call_site_unresolved (site, caller, reason, detail). -- **all** — THE TRUST LINE, and it is not the same set of tiers in every language. RESOLVED (callee_method_id is set): known_edge and multi_inferred in every language, and boundary_lib where the library is staged (--library) — without it boundary_lib names the target in callee_label and leaves callee_method_id NULL; ALSO ambient_terminal in TypeScript, fan_capped in Java and C# (the declared base, the fan refused), and runtime_observed in C#. HANDED OVER (callee set, but the site passes the function rather than calling it): callback_registered and event_dispatch in JavaScript and TypeScript, callback_registered (kind METHOD_REFERENCE) in Python; event_dispatch in C# too, where a mediator Send or Publish runs the handler for the request type, beside the row for the site itself. CORRECT END (callee NULL, and nothing is missing): intrinsic_terminal in TypeScript; ambient_terminal, implicit_constructor and dynamic_terminal in JavaScript; known_implicit_ctor, known_builtin_operator and boundary_generated (callee_label set) in C#. BLIND SPOT (callee NULL; exactly the tiers named `ambiguous_*`, which are what unresolved_sites holds): ambiguous_unknown everywhere, ALSO ambiguous_anon in Java and ambiguous_dynamic in C#. CAPPED (callee NULL, not in unresolved_sites): fan_capped in JavaScript. Python emits the four shared tiers and callback_registered. A filter written as `tier IN (known_edge, multi_inferred)` therefore drops resolved edges in every language but Python — derive the set from this note or from unresolved_sites, never from a hardcoded list. +- **all** — THE TRUST LINE, and it is not the same set of tiers in every language. RESOLVED (callee_method_id is set): known_edge and multi_inferred in every language, and boundary_lib where the library is staged (--library) — without it boundary_lib names the target in callee_label and leaves callee_method_id NULL; ALSO ambient_terminal in TypeScript, fan_capped in Java and C# (the declared base, the fan refused), and runtime_observed in C#. HANDED OVER (callee set, but the site passes the function rather than calling it): callback_registered and event_dispatch in JavaScript and TypeScript, ALSO callback_handed in TypeScript, callback_registered (kind METHOD_REFERENCE) in Python; event_dispatch in C# too, where a mediator Send or Publish runs the handler for the request type, beside the row for the site itself. CORRECT END (callee NULL, and nothing is missing): intrinsic_terminal in TypeScript; ambient_terminal, implicit_constructor and dynamic_terminal in JavaScript; known_implicit_ctor, known_builtin_operator and boundary_generated (callee_label set) in C#. BLIND SPOT (callee NULL; exactly the tiers named `ambiguous_*`, which are what unresolved_sites holds): ambiguous_unknown everywhere, ALSO ambiguous_anon in Java and ambiguous_dynamic in C#. CAPPED (callee NULL, not in unresolved_sites): fan_capped in JavaScript. Python emits the four shared tiers and callback_registered. A filter written as `tier IN (known_edge, multi_inferred)` therefore drops resolved edges in every language but Python — derive the set from this note or from unresolved_sites, never from a hardcoded list. - **java** — A multi_inferred fan is CHA-wide: it is every override the hierarchy admits, bounded only by the dispatch cap. type_instantiated is computed and exported but NOT read by any rule, so the fan is not narrowed to types the program constructs. Narrow it yourself by joining dispatch_candidates to type_instantiated — see the dispatch_envelope_of query. A receiver is ALSO typed by what flows into it (a local's initializer, the arguments callers pass to a parameter, the receivers callers invoke a method on for its `this`), and each flow-in type resolves its member directly, outside the fan: that is why a `fan_capped` site still carries edges, and why they are the types the program was seen to hand over, not the whole hierarchy. - **typescript** — A multi_inferred fan is CHA-wide, as in Java: type_instantiated is computed and exported but NOT read by any rule. The fan also has sources that are not virtual dispatch at all — an overload set or a union-typed receiver produces one too. - **python** — A multi_inferred fan IS narrowed by the instantiation set: type_instantiated_reachable (the constructed classes and their bases) bounds dispatch in resolution/dispatch.dl. Python is the only front end where that narrowing is applied, so a fan here is tighter than the same shape would be in Java or TypeScript. diff --git a/graph/bundle/schema.ts b/graph/bundle/schema.ts index 9d71c3906..906ca4eda 100644 --- a/graph/bundle/schema.ts +++ b/graph/bundle/schema.ts @@ -525,6 +525,7 @@ export const VOCAB: readonly VocabSpec[] = [ { table: 'call_edges', column: 'tier', value: 'fan_capped', languages: ['javascript', 'java', 'csharp'], meaning: 'More targets than --dispatch-cap: the set was refused rather than emitted. JavaScript: callee is NULL. Java and C#: callee is the declared base method the fan would have started from; dispatch-capped-sites.csv carries the refused count.' }, { table: 'call_edges', column: 'tier', value: 'callback_registered', languages: ['javascript', 'typescript'], meaning: 'The site HANDS the callee this function (`xs.forEach(f)`, `p.then(f)`, `emitter.on(\'x\', h)`, `setTimeout(f)`), which may invoke it. Not the site\'s own callee; a reachability edge, labelled so it is never read as a resolved call.' }, { table: 'call_edges', column: 'tier', value: 'callback_registered', languages: P, meaning: 'A method or function named as a VALUE and handed over, stored or returned (`pool.submit(self.work)`, `Thread(target=svc.run)`, `sorted(xs, key=self.rank)`, `[h.on_event]`), always kind METHOD_REFERENCE. Its target is resolved the way a call on the same receiver would be; whoever receives the value calls it. A reachability edge, labelled so it is never read as a resolved call.' }, + { table: 'call_edges', column: 'tier', value: 'callback_handed', languages: T, meaning: 'The site hands a function to a PROJECT function whose body can run it (`withRetry(2, () => load(id))`, `apply(upper, s)`). Not the site\'s own callee (that is the row for the called function); a reachability edge from the hand-over site, like callback_registered. Not emitted for a getter read, a parameter the body never reads or only compares, or a keyed registry\'s slot.' }, { table: 'call_edges', column: 'tier', value: 'event_dispatch', languages: S, meaning: '`x.emit(\'name\')` reaching a handler registered by `x.on(\'name\', h)` on a value x may hold — name-sensitive for literal names, every handler on that value for a computed one.' }, { table: 'call_edges', column: 'tier', value: 'event_dispatch', languages: T, meaning: 'A publish on a project class\'s keyed registry reaching each function filed under the same key: `bus.publish(\'x\', p)` where `publish` (or a method it passes the key to) calls what `this.handlers.get(name)` holds, to the function a `bus.subscribe(\'x\', f)` stored there, or an entry `{ [\'x\']: f }` of a table a subscribe-all method files by key. A literal, a const or a const-object member key; a key only known at run time matches every key, and a constant the dispatch also looks up (a wildcard) matches every publish. The site is the publish call (call-edge-generation/keyed_registry.dl).' }, { table: 'call_edges', column: 'tier', value: 'event_dispatch', languages: J, meaning: 'A Spring application event: `publishEvent(e)` reaching each listener (`@EventListener`, `@TransactionalEventListener`, `ApplicationListener.onApplicationEvent`) whose declared event type e\'s static type is, or is a subtype of. Added beside the publishEvent boundary row, never in place of it (call-edge-generation/event_dispatch.dl). Also a JPA entity write (`save`, `persist`, `merge`, `delete` on a repository or EntityManager) reaching the `@PrePersist`/`@PreUpdate`/`@PreRemove`-style callbacks of the written entity\'s type and of the listeners `@EntityListeners` names on it or a superclass (kind entity_callback; call-edge-generation/entity_lifecycle.dl).' }, @@ -773,7 +774,7 @@ export const NOTES: readonly NoteSpec[] = [ { language: 'python', table: 'entry_points', note: 'Framework entry points only: url, http, orm_hook, task, signal_receiver, fixture, di_provider and grpc_service. There is no test and no main reason: a pytest test is recognised by the query layer from its file and name, not here.' }, { language: 'python', table: 'overrides', note: 'EMPTY — this table is Java-shaped. The Python dispatch envelope is in dispatch_candidates with basis `mro`, and `value` for a function assigned onto an instance\'s member; the raw linearisation is in ext_mro_position.' }, { language: 'python', table: 'type_instantiated', note: 'Every row has how = `new`: the rule set records that some client call constructs the class, not which form.' }, - { language: 'all', table: 'call_edges', note: 'THE TRUST LINE, and it is not the same set of tiers in every language. RESOLVED (callee_method_id is set): known_edge and multi_inferred in every language, and boundary_lib where the library is staged (--library) — without it boundary_lib names the target in callee_label and leaves callee_method_id NULL; ALSO ambient_terminal in TypeScript, fan_capped in Java and C# (the declared base, the fan refused), and runtime_observed in C#. HANDED OVER (callee set, but the site passes the function rather than calling it): callback_registered and event_dispatch in JavaScript and TypeScript, callback_registered (kind METHOD_REFERENCE) in Python; event_dispatch in C# too, where a mediator Send or Publish runs the handler for the request type, beside the row for the site itself. CORRECT END (callee NULL, and nothing is missing): intrinsic_terminal in TypeScript; ambient_terminal, implicit_constructor and dynamic_terminal in JavaScript; known_implicit_ctor, known_builtin_operator and boundary_generated (callee_label set) in C#. BLIND SPOT (callee NULL; exactly the tiers named `ambiguous_*`, which are what unresolved_sites holds): ambiguous_unknown everywhere, ALSO ambiguous_anon in Java and ambiguous_dynamic in C#. CAPPED (callee NULL, not in unresolved_sites): fan_capped in JavaScript. Python emits the four shared tiers and callback_registered. A filter written as `tier IN (known_edge, multi_inferred)` therefore drops resolved edges in every language but Python — derive the set from this note or from unresolved_sites, never from a hardcoded list.' }, + { language: 'all', table: 'call_edges', note: 'THE TRUST LINE, and it is not the same set of tiers in every language. RESOLVED (callee_method_id is set): known_edge and multi_inferred in every language, and boundary_lib where the library is staged (--library) — without it boundary_lib names the target in callee_label and leaves callee_method_id NULL; ALSO ambient_terminal in TypeScript, fan_capped in Java and C# (the declared base, the fan refused), and runtime_observed in C#. HANDED OVER (callee set, but the site passes the function rather than calling it): callback_registered and event_dispatch in JavaScript and TypeScript, ALSO callback_handed in TypeScript, callback_registered (kind METHOD_REFERENCE) in Python; event_dispatch in C# too, where a mediator Send or Publish runs the handler for the request type, beside the row for the site itself. CORRECT END (callee NULL, and nothing is missing): intrinsic_terminal in TypeScript; ambient_terminal, implicit_constructor and dynamic_terminal in JavaScript; known_implicit_ctor, known_builtin_operator and boundary_generated (callee_label set) in C#. BLIND SPOT (callee NULL; exactly the tiers named `ambiguous_*`, which are what unresolved_sites holds): ambiguous_unknown everywhere, ALSO ambiguous_anon in Java and ambiguous_dynamic in C#. CAPPED (callee NULL, not in unresolved_sites): fan_capped in JavaScript. Python emits the four shared tiers and callback_registered. A filter written as `tier IN (known_edge, multi_inferred)` therefore drops resolved edges in every language but Python — derive the set from this note or from unresolved_sites, never from a hardcoded list.' }, { language: 'java', table: 'call_edges', note: 'A multi_inferred fan is CHA-wide: it is every override the hierarchy admits, bounded only by the dispatch cap. type_instantiated is computed and exported but NOT read by any rule, so the fan is not narrowed to types the program constructs. Narrow it yourself by joining dispatch_candidates to type_instantiated — see the dispatch_envelope_of query. A receiver is ALSO typed by what flows into it (a local\'s initializer, the arguments callers pass to a parameter, the receivers callers invoke a method on for its `this`), and each flow-in type resolves its member directly, outside the fan: that is why a `fan_capped` site still carries edges, and why they are the types the program was seen to hand over, not the whole hierarchy.' }, { language: 'typescript', table: 'call_edges', note: 'A multi_inferred fan is CHA-wide, as in Java: type_instantiated is computed and exported but NOT read by any rule. The fan also has sources that are not virtual dispatch at all — an overload set or a union-typed receiver produces one too.' }, { language: 'python', table: 'call_edges', note: 'A multi_inferred fan IS narrowed by the instantiation set: type_instantiated_reachable (the constructed classes and their bases) bounds dispatch in resolution/dispatch.dl. Python is the only front end where that narrowing is applied, so a fan here is tighter than the same shape would be in Java or TypeScript.' }, diff --git a/graph/javascript/engine/call-edge-generation/dispatch-envelope.dl b/graph/javascript/engine/call-edge-generation/dispatch-envelope.dl new file mode 100644 index 000000000..d5e7c9067 --- /dev/null +++ b/graph/javascript/engine/call-edge-generation/dispatch-envelope.dl @@ -0,0 +1,14 @@ +// ============================================================================ +// Call-edge generation · THE DISPATCH AND FUNCTION-VALUE ENVELOPE (exported, as TypeScript exports it) +// +// The flow engine resolves a call to the functions its receiver's VALUE may run, so a +// JavaScript call edge already names the override that runs. The two envelope relations +// TypeScript exports are kept here under the same names and columns, so a reader that widens +// a call to what may run (impact, the chain checks) reads one schema for both languages: +// +// method_dispatch_candidate(Base, Impl, Kind) Impl, in a subclass, overrides Base +// fn_value_call(Caller, Fn) Caller hands Fn over at a call that runs it +// ============================================================================ +method_dispatch_candidate(base, impl, "override") :- type_own_member(t, n, impl), type_ancestor(t, s), + type_own_member(s, n, base), base != impl. +fn_value_call(caller, m) :- call_chain_edge(_, caller, "-", m, "client", "callback_registered", _), caller != m. diff --git a/graph/javascript/engine/config-resolution/knobs.dl b/graph/javascript/engine/config-resolution/knobs.dl index d4836da68..8c9f20a34 100644 --- a/graph/javascript/engine/config-resolution/knobs.dl +++ b/graph/javascript/engine/config-resolution/knobs.dl @@ -116,3 +116,11 @@ router_openapi("openapi"). router_route_def("@hono/zod-openapi", "createRoute"). .decl router_def_key(c0:symbol, c1:symbol) router_def_key("method", "method"). router_def_key("path", "path"). router_def_key("middleware", "middleware"). + +// the context's keyed write and read, and its whole-store view (framework-behavior/http-router-ctx-value.dl) +.decl router_ctx_set(c0:symbol) +router_ctx_set("set"). +.decl router_ctx_get(c0:symbol) +router_ctx_get("get"). +.decl router_ctx_var(c0:symbol) +router_ctx_var("var"). diff --git a/graph/javascript/engine/framework-behavior/http-router-ctx-value.dl b/graph/javascript/engine/framework-behavior/http-router-ctx-value.dl new file mode 100644 index 000000000..50f54d103 --- /dev/null +++ b/graph/javascript/engine/framework-behavior/http-router-ctx-value.dl @@ -0,0 +1,30 @@ +// ============================================================================ +// FRAMEWORK-BEHAVIOR · HTTP ROUTER CONTEXT VALUES (what `c.get(k)` holds, JavaScript) +// +// JavaScript has no Env type, so what a context read holds is what a function registered on +// the SAME app writes under that key: `c.set('services', { orders: new OrderService(db) })` +// in a middleware, `const { orders } = c.get('services')` / `c.var.services.orders` in a +// handler. The read takes the written value, so the flow engine follows it on like any other +// value (destructuring, members, calls). Keyed by the app the writer and the reader are +// registered on (jr_reg), never by the key alone. Positive, inside the expr_value fixpoint. +// Framework names live in knobs.dl. +// ============================================================================ +.decl jcx_fn_root(c0:symbol, c1:symbol) // (function, app) the function is registered on the app +// any argument of a registration that is a function value, or a knob wrapper's function (`use(p, createMiddleware(fn))`, +// or a const holding one); read off call_arg rather than the route table's handler arguments, which use negation +jcx_fn_root(m, a) :- jr_reg(ce, a, _), call_arg(ce, _, x), expr_value(x, "func", m). +jcx_fn_root(m, a) :- jr_reg(ce, a, _), call_arg(ce, _, x), jr_wrapper_call(x), call_arg(x, 0, y), expr_value(y, "func", m). +jcx_fn_root(m, a) :- jr_reg(ce, a, _), call_arg(ce, _, x), expr_binding(_, v, x), var_init(_, _, i, v), jr_wrapper_call(i), + call_arg(i, 0, y), expr_value(y, "func", m). +.decl jcx_ctx(c0:symbol, c1:symbol) // (context expression, app) +jcx_ctx(e, a) :- jcx_fn_root(m, a), param_decl("client", _, "0", m, p), expr_param(_, p, e). +.decl jcx_set(c0:symbol, c1:symbol, c2:symbol) // (app, key, written value) +jcx_set(a, key, v) :- call_site(_, "METHOD_CALL", sn, _, r, _, ce, _, _), router_ctx_set(sn), jcx_ctx(r, a), + call_arg(ce, 0, k), expr_value(k, "str", key), call_arg(ce, 1, v). +// `c.get(k)` +expr_value(g, k2, i2) :- call_site(_, "METHOD_CALL", gn, _, r, _, g, _, _), router_ctx_get(gn), jcx_ctx(r, a), + call_arg(g, 0, k), expr_value(k, "str", key), jcx_set(a, key, v), expr_value(v, k2, i2). +// `c.var.k` +expr_value(e, k2, i2) :- expr_kind(_, "PROPERTY_ACCESS", _, e), expr_name(_, key, e), expr_child(_, e, "ACCESS_TARGET", _, vx), + expr_kind(_, "PROPERTY_ACCESS", _, vx), expr_name(_, vn, vx), router_ctx_var(vn), expr_child(_, vx, "ACCESS_TARGET", _, r), + jcx_ctx(r, a), jcx_set(a, key, v), expr_value(v, k2, i2). diff --git a/graph/javascript/souffle/decls_all.dl b/graph/javascript/souffle/decls_all.dl index 50b48f0c2..8ca24fb07 100644 --- a/graph/javascript/souffle/decls_all.dl +++ b/graph/javascript/souffle/decls_all.dl @@ -634,3 +634,5 @@ .decl remote_edge(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) .decl remote_unserved(c0:symbol, c1:symbol, c2:symbol) .decl route_registered_by(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:symbol) +.decl method_dispatch_candidate(c0:symbol, c1:symbol, c2:symbol) +.decl fn_value_call(c0:symbol, c1:symbol) diff --git a/graph/javascript/souffle/export_manifest.tsv b/graph/javascript/souffle/export_manifest.tsv index 46b0df9ae..a6e22cb03 100644 --- a/graph/javascript/souffle/export_manifest.tsv +++ b/graph/javascript/souffle/export_manifest.tsv @@ -61,3 +61,5 @@ ctx_key_flow ctx-key-flow.csv remote_edge remote-edge.csv remote_unserved remote-unserved.csv route_registered_by route-registered-by.csv +method_dispatch_candidate dispatch-candidates.csv +fn_value_call function-value-call.csv diff --git a/graph/test/typescript/cases/78-callable-collections/src/pipeline.ts b/graph/test/typescript/cases/78-callable-collections/src/pipeline.ts index 729b63fef..0a5fc05ee 100644 --- a/graph/test/typescript/cases/78-callable-collections/src/pipeline.ts +++ b/graph/test/typescript/cases/78-callable-collections/src/pipeline.ts @@ -30,7 +30,7 @@ export function runIndexed(node: Node, i: number): Node { return pick(steps[0](node)) } -// a table read under a computed key reaches every entry; under a written key, only that entry +// a table read under a computed key is a declared unknown (SPEC R21a); under a written key, only that entry export function runByKey(node: Node, k: 'grow' | 'shrink'): Node { return table[k](node) } diff --git a/graph/test/typescript/expected/08-function-values-and-callbacks.edges b/graph/test/typescript/expected/08-function-values-and-callbacks.edges index c0aeef314..aab049882 100644 --- a/graph/test/typescript/expected/08-function-values-and-callbacks.edges +++ b/graph/test/typescript/expected/08-function-values-and-callbacks.edges @@ -1,5 +1,7 @@ ambiguous_unknown FUNCTION_CALL values#useLibrary() @L40 -> - ambiguous_unknown METHOD_CALL values#drive() @L29 -> - +callback_handed FUNCTION_CALL values#drive() @L30 -> values#lower(?) +callback_handed FUNCTION_CALL values#drive() @L30 -> values#upper(?) callback_registered FUNCTION_CALL values#useLibrary() @L40 -> values#local(?) known_edge CONSTRUCTOR_CALL values#drive() @L27 -> Pipeline#() known_edge FUNCTION_CALL values#apply(Transform,string) @L12 -> values#(string) diff --git a/graph/test/typescript/expected/08-function-values-and-callbacks.lib.edges b/graph/test/typescript/expected/08-function-values-and-callbacks.lib.edges index 20caf154d..51c52f545 100644 --- a/graph/test/typescript/expected/08-function-values-and-callbacks.lib.edges +++ b/graph/test/typescript/expected/08-function-values-and-callbacks.lib.edges @@ -2,6 +2,8 @@ ambiguous_unknown METHOD_CALL values#drive() @L29 -> - boundary_lib FUNCTION_CALL values#useLibrary() @L40 -> hooks#(string) boundary_lib FUNCTION_CALL values#useLibrary() @L40 -> hooks#identity(?) boundary_lib FUNCTION_CALL values#useLibrary() @L40 -> hooks#withHook(Hook,string) +callback_handed FUNCTION_CALL values#drive() @L30 -> values#lower(?) +callback_handed FUNCTION_CALL values#drive() @L30 -> values#upper(?) callback_registered FUNCTION_CALL values#useLibrary() @L40 -> values#local(?) known_edge CONSTRUCTOR_CALL values#drive() @L27 -> Pipeline#() known_edge FUNCTION_CALL values#apply(Transform,string) @L12 -> values#(string) diff --git a/graph/test/typescript/expected/09-generics-substitution.edges b/graph/test/typescript/expected/09-generics-substitution.edges index 944a266cf..60c46ee6d 100644 --- a/graph/test/typescript/expected/09-generics-substitution.edges +++ b/graph/test/typescript/expected/09-generics-substitution.edges @@ -2,6 +2,7 @@ ambiguous_unknown CONSTRUCTOR_CALL generic#useLibrary() @L42 -> - ambiguous_unknown FUNCTION_CALL generic#useLibrary() @L45 -> - ambiguous_unknown METHOD_CALL generic#inferredFromArgument() @L35 -> - ambiguous_unknown METHOD_CALL generic#useLibrary() @L45 -> - +callback_handed FUNCTION_CALL generic#inferredFromArgument() @L35 -> generic#(?) known_edge CONSTRUCTOR_CALL Box#map((t: T) =) @L16 -> Box#(T) known_edge CONSTRUCTOR_CALL generic#fromReceiver() @L21 -> Box#(T) known_edge CONSTRUCTOR_CALL generic#fromReceiver() @L21 -> Item#() diff --git a/graph/test/typescript/expected/09-generics-substitution.lib.edges b/graph/test/typescript/expected/09-generics-substitution.lib.edges index b1235c884..ce3aa6b96 100644 --- a/graph/test/typescript/expected/09-generics-substitution.lib.edges +++ b/graph/test/typescript/expected/09-generics-substitution.lib.edges @@ -3,6 +3,7 @@ ambiguous_unknown METHOD_CALL generic#useLibrary() @L45 -> - boundary_lib CONSTRUCTOR_CALL generic#useLibrary() @L42 -> Cell#(T) boundary_lib FUNCTION_CALL generic#useLibrary() @L45 -> container#unwrap(Cell) boundary_lib METHOD_CALL generic#useLibrary() @L45 -> Cell#value() +callback_handed FUNCTION_CALL generic#inferredFromArgument() @L35 -> generic#(?) known_edge CONSTRUCTOR_CALL Box#map((t: T) =) @L16 -> Box#(T) known_edge CONSTRUCTOR_CALL generic#fromReceiver() @L21 -> Box#(T) known_edge CONSTRUCTOR_CALL generic#fromReceiver() @L21 -> Item#() diff --git a/graph/test/typescript/expected/34-callback-of-callback.edges b/graph/test/typescript/expected/34-callback-of-callback.edges index d2ff99be3..c8f6aff26 100644 --- a/graph/test/typescript/expected/34-callback-of-callback.edges +++ b/graph/test/typescript/expected/34-callback-of-callback.edges @@ -1,3 +1,4 @@ +callback_handed FUNCTION_CALL exec#viaClientCallbackOfCallback() @L32 -> exec#(?) callback_registered CONSTRUCTOR_CALL exec#viaExecutor() @L12 -> exec#(?,?) callback_registered CONSTRUCTOR_CALL exec#viaRenamedExecutor() @L20 -> exec#(?,?) callback_registered FUNCTION_CALL exec#withSink((emit: Sink) =) @L29 -> exec#() diff --git a/graph/test/typescript/expected/59-merged-export-through-namespace.edges b/graph/test/typescript/expected/59-merged-export-through-namespace.edges index ec737c759..f2f171898 100644 --- a/graph/test/typescript/expected/59-merged-export-through-namespace.edges +++ b/graph/test/typescript/expected/59-merged-export-through-namespace.edges @@ -1,3 +1,6 @@ +callback_handed FUNCTION_CALL classic/schemas#() @L5 -> classic/schemas#() +callback_handed METHOD_CALL classic/schemas#() @L4 -> classic/schemas#(?) +callback_handed METHOD_CALL core/schemas#() @L3 -> core/schemas#(?) known_edge FUNCTION_CALL classic/schemas#() @L5 -> core/core#$constructor(string,(inst: object) =) known_edge FUNCTION_CALL core/core#$constructor(string,(inst: object) =) @L3 -> core/core#(object) known_edge METHOD_CALL classic/schemas#(?) @L4 -> core/core#helper() diff --git a/graph/test/typescript/expected/75-jsx-wrapper-guards.edges b/graph/test/typescript/expected/75-jsx-wrapper-guards.edges index 78ebdfe74..684930deb 100644 --- a/graph/test/typescript/expected/75-jsx-wrapper-guards.edges +++ b/graph/test/typescript/expected/75-jsx-wrapper-guards.edges @@ -19,6 +19,8 @@ ambiguous_unknown METHOD_CALL app#() @L15 -> - ambiguous_unknown METHOD_CALL app#() @L16 -> - ambiguous_unknown METHOD_CALL app#() @L18 -> - ambiguous_unknown METHOD_CALL app#ArrowRenameTag({ icon: Fc }[]) @L57 -> - +callback_handed FUNCTION_CALL app#() @L25 -> app#() +callback_handed FUNCTION_CALL app#() @L26 -> app#() callback_registered FUNCTION_CALL app#() @L15 -> app#() callback_registered FUNCTION_CALL app#() @L16 -> app#() callback_registered FUNCTION_CALL app#() @L18 -> app#() diff --git a/graph/test/typescript/expected/78-callable-collections.envelope b/graph/test/typescript/expected/78-callable-collections.envelope index 90d7c9ee5..a0cc7f8d0 100644 --- a/graph/test/typescript/expected/78-callable-collections.envelope +++ b/graph/test/typescript/expected/78-callable-collections.envelope @@ -2,8 +2,6 @@ value pipeline#@10:23 -> steps#pushed value_call pipeline#@43 -> steps#narrow value_call pipeline#@43 -> steps#widen value_call pipeline#@46 -> steps#square -value_call pipeline#runByKey -> steps#narrow -value_call pipeline#runByKey -> steps#widen value_call pipeline#runFrozen -> steps#square value_call pipeline#runImported -> steps#halve value_call pipeline#runIndexed -> steps#narrow diff --git a/graph/test/typescript/expected/78-hof-callback-at-library-boundary.edges b/graph/test/typescript/expected/78-hof-callback-at-library-boundary.edges index 7159e17d1..9e0d19af6 100644 --- a/graph/test/typescript/expected/78-hof-callback-at-library-boundary.edges +++ b/graph/test/typescript/expected/78-hof-callback-at-library-boundary.edges @@ -7,6 +7,7 @@ ambiguous_unknown CONSTRUCTOR_CALL store#() @L9 -> - ambiguous_unknown METHOD_CALL store#known() @L17 -> - ambiguous_unknown METHOD_CALL store#store() @L12 -> - ambiguous_unknown METHOD_CALL store#store() @L13 -> - +callback_handed FUNCTION_CALL app#useEach(number[]) @L48 -> app#(?) callback_registered FUNCTION_CALL app#atStartup() @L38 -> app#() callback_registered FUNCTION_CALL plugin#() @L7 -> plugin#() callback_registered FUNCTION_CALL store#deferred() @L24 -> store#onUpdated() diff --git a/graph/test/typescript/expected/80-generic-return-receiver.envelope b/graph/test/typescript/expected/80-generic-return-receiver.envelope index aee022b92..9a7c461a1 100644 --- a/graph/test/typescript/expected/80-generic-return-receiver.envelope +++ b/graph/test/typescript/expected/80-generic-return-receiver.envelope @@ -1,6 +1,6 @@ nominal use#Shape.area -> use#Circ.area nominal use#Shape.area -> use#Sq.area nominal use#Warrior.fight -> use#Ninja.fight -value_call use#recordArea -> use#@38 -value_call use#totalArea -> use#@30 -value_call use#totalArea -> use#@31 +value use#@29:33 -> use#@30 +value use#@29:33 -> use#@31 +value use#@38:30 -> use#@38 diff --git a/graph/test/typescript/expected/80-generic-return-receiver.lib.envelope b/graph/test/typescript/expected/80-generic-return-receiver.lib.envelope index aee022b92..9a7c461a1 100644 --- a/graph/test/typescript/expected/80-generic-return-receiver.lib.envelope +++ b/graph/test/typescript/expected/80-generic-return-receiver.lib.envelope @@ -1,6 +1,6 @@ nominal use#Shape.area -> use#Circ.area nominal use#Shape.area -> use#Sq.area nominal use#Warrior.fight -> use#Ninja.fight -value_call use#recordArea -> use#@38 -value_call use#totalArea -> use#@30 -value_call use#totalArea -> use#@31 +value use#@29:33 -> use#@30 +value use#@29:33 -> use#@31 +value use#@38:30 -> use#@38 diff --git a/graph/test/typescript/expected/81-contextual-lambda-param-no-lib.edges b/graph/test/typescript/expected/81-contextual-lambda-param-no-lib.edges index f055468cc..747f9d6f7 100644 --- a/graph/test/typescript/expected/81-contextual-lambda-param-no-lib.edges +++ b/graph/test/typescript/expected/81-contextual-lambda-param-no-lib.edges @@ -14,6 +14,10 @@ ambiguous_unknown METHOD_CALL use#reduceElement(User[]) @L58 -> - ambiguous_unknown METHOD_CALL use#thenBare(Repo) @L61 -> - ambiguous_unknown METHOD_CALL use#thenParam(Promise) @L64 -> - ambiguous_unknown METHOD_CALL use#thenThenMap(Promise) @L82 -> - +callback_handed METHOD_CALL shadow#ctlOwnPromise(Promise) @L29 -> shadow#(?) +callback_handed METHOD_CALL shadow#ctlOwnSet(Set) @L26 -> shadow#(?) +callback_handed METHOD_CALL use#ctlOwnMap(Lazy) @L109 -> use#(?) +callback_handed METHOD_CALL use#ctlOwnThen(Lazy) @L106 -> use#(?) callback_registered METHOD_CALL use#(?) @L78 -> use#(?) callback_registered METHOD_CALL use#(?) @L83 -> use#(?) callback_registered METHOD_CALL use#aliasGeneric(Many) @L73 -> use#(?) diff --git a/graph/test/typescript/expected/82-structural-conformer-across-programs.edges b/graph/test/typescript/expected/82-structural-conformer-across-programs.edges index 5d1aad278..143ff87d0 100644 --- a/graph/test/typescript/expected/82-structural-conformer-across-programs.edges +++ b/graph/test/typescript/expected/82-structural-conformer-across-programs.edges @@ -1,3 +1,4 @@ +callback_handed METHOD_CALL app/pipeline#build(string[]) @L15 -> app/pipeline#(?) known_edge CONSTRUCTOR_CALL Pipeline#map((value: T) =) @L10 -> Pipeline#(T[]) known_edge CONSTRUCTOR_CALL app/pipeline#build(string[]) @L15 -> Pipeline#(T[]) known_edge CONSTRUCTOR_CALL fixture/use#() @L16 -> Bag#() diff --git a/graph/test/typescript/tools/normalize_edges.py b/graph/test/typescript/tools/normalize_edges.py index 9807d7574..33528f3b0 100644 --- a/graph/test/typescript/tools/normalize_edges.py +++ b/graph/test/typescript/tools/normalize_edges.py @@ -284,8 +284,10 @@ def main(): continue # a function the site HANDS OVER (`xs.map(cb)`) is not a call the compiler lists at that # site, nor is the handler a publish reaches through a keyed registry (`event_dispatch`): the - # pairs are scored against the compiler, the .edges golden still carries both - if f[5] in ('callback_registered', 'event_dispatch'): + # pairs are scored against the compiler, the .edges golden still carries both. A function handed + # to a PROJECT function (`apply(upper, s)`, `callback_handed`) is the same hand-over: the compiler + # resolves that site to `apply`, which the pairs already score, never to `upper` + if f[5] in ('callback_registered', 'callback_handed', 'event_dispatch'): continue seen.add(f"{n.label(f[1])} -> {n.label(f[3])}") else: diff --git a/graph/typescript/engine/call-edge-generation/call_chain.dl b/graph/typescript/engine/call-edge-generation/call_chain.dl index 890a08ad5..e51eeffd4 100644 --- a/graph/typescript/engine/call-edge-generation/call_chain.dl +++ b/graph/typescript/engine/call-edge-generation/call_chain.dl @@ -134,6 +134,48 @@ call_chain_edge(ce, caller, "-", m, "client", "callback_registered", kind) :- caller != m, invocation_site(ce, kind). +// …and a function handed to a PROJECT function (`withRetry(2, () => load(id))`, `tryit(purge)`): the +// site hands it over exactly as it does to a library one, and the compiler names the hop. The callee's +// body, which runs it, keeps its own edge (fn_value_call); this one is the site's, so it carries the +// hand-over and nothing from another caller. +// +// Only where the hand-over can run it. A getter read (`new Context(Request.empty)`) hands over what the +// getter RETURNS, not the getter. A body that never reads the parameter (`on(_e, _listener) {}`), or +// only compares it (`c === undefined`), cannot run what it was given. And a keyed registry's slot +// (keyed_registry.dl) is run by the publish on the same key, not by the subscribe that files it. +call_chain_edge(ce, caller, "-", m, "client", "callback_handed", kind) :- + cb_handed_arg(ce, pos, m), + cb_handed_live(ce, pos), + call_from(ce, caller), + caller != m, + invocation_site(ce, kind). + +.decl cb_handed_arg(c0:symbol, c1:symbol, c2:symbol) // (call, argument position, function handed there) +.decl cb_handed_live(c0:symbol, c1:symbol) // (call, position): a body that runs the call may run it +.decl cb_param_runs(c0:symbol) // a parameter whose value the body can call or pass on +.decl cb_ref_inert(c0:symbol) // a reference that only tests the value +.decl cb_inert_op(c0:symbol) +cb_handed_arg(ce, pos, m) :- call_runs_client_body(ce), + expr_child("client", ce, "ARGUMENT", pos, a), + !expr_kind("client", "CALL_EXPRESSION", _, a), + expr_callable(a, m), + !method_is_accessor(m), + method_prov(m, "client"). +// a position with no parameter of its own: a rest parameter's tail, or `arguments` +cb_handed_live(ce, pos) :- cb_handed_arg(ce, pos, _), call_runs_method(ce, b), method_prov(b, "client"), + !param_decl("client", _, pos, _, b, _). +cb_handed_live(ce, pos) :- cb_handed_arg(ce, pos, _), call_runs_method(ce, b), method_prov(b, "client"), + param_decl("client", _, pos, _, b, p), !kr_registry_param(p), cb_param_runs(p). +cb_param_runs(p) :- expr_referenced("client", "PARAMETER", p, e), !cb_ref_inert(e). +cb_ref_inert(e) :- expr_referenced("client", "PARAMETER", _, e), expr_child("client", x, _, _, e), + expr_operator("client", op, x), cb_inert_op(op). +cb_inert_op("==="). +cb_inert_op("!=="). +cb_inert_op("=="). +cb_inert_op("!="). +cb_inert_op("typeof"). +cb_inert_op("!"). + // …and a function written inside a JSX `{…}` (resolution/value-flow.dl, jsx_handed_value). The `{…}` is // the site: an intrinsic tag has no call site of its own, and the expression positions the edge. call_chain_edge(r, caller, "-", m, "client", "callback_registered", "JSX_COMPONENT_CALL") :- diff --git a/graph/typescript/engine/config-resolution/knobs.dl b/graph/typescript/engine/config-resolution/knobs.dl index 65474bb0e..87c876ff6 100644 --- a/graph/typescript/engine/config-resolution/knobs.dl +++ b/graph/typescript/engine/config-resolution/knobs.dl @@ -419,3 +419,27 @@ router_valid_read("valid"). // router_handler_spread(Name): `...x.name(a, b)` spreads its arguments as handlers .decl router_handler_spread(c0:symbol) router_handler_spread("createHandlers"). + +// ── the request context's environment and store (framework-behavior/http-router-ctx-type.dl) ── +// the member of an environment type that lists the context keys and their types +.decl router_env_vars(c0:symbol) +router_env_vars("Variables"). +// a context's keyed read and write, and its whole-store view +.decl router_ctx_get(c0:symbol) +router_ctx_get("get"). +.decl router_ctx_set(c0:symbol) +router_ctx_set("set"). +.decl router_ctx_var(c0:symbol) +router_ctx_var("var"). +// a helper whose first type argument is the environment of the function it is handed +.decl router_env_call(c0:symbol) +router_env_call("createMiddleware"). +// a type operator that takes a function type and gives what it returns (TypeScript's own) +.decl ts_type_return_operator(c0:symbol) +ts_type_return_operator("ReturnType"). +// validators from other packages, same (target, schema) arguments (gap census K2) +router_validator("hono-openapi", "validator"). +router_validator("@hono/valibot-validator", "vValidator"). +router_validator("@hono/typebox-validator", "tbValidator"). +router_validator("@hono/arktype-validator", "arktypeValidator"). +router_validator("@hono/standard-validator", "sValidator"). diff --git a/graph/typescript/engine/framework-behavior/destinations.dl b/graph/typescript/engine/framework-behavior/destinations.dl index 972f53ffc..dbe121ad3 100644 --- a/graph/typescript/engine/framework-behavior/destinations.dl +++ b/graph/typescript/engine/framework-behavior/destinations.dl @@ -141,9 +141,10 @@ rd_call0(ce, w) :- call_site("client", _, _, _, _, ce, _), call_chain_edge(ce, _, "-", w, "client", st, _), st != "callback_registered". rd_call0(ce, w) :- loop_elem_callee(ce, w). .decl rd_ret_top(c0:symbol, c1:symbol) -rd_ret_top(w, e) :- rd_call0(_, w), method_returns_expr(w, e), !rd_ret_inner(w, e). -.decl rd_ret_inner(c0:symbol, c1:symbol) -rd_ret_inner(w, e) :- rd_call0(_, w), method_returns_expr(w, e), expr_child("client", p, _, _, e), method_returns_expr(w, p). +// the returned expression itself: a ROOT (method_returns_expr also holds every sub-expression under a +// `return`). Read off the expression's own root flag rather than by joining each returned expression with +// its parents, which ran over every returned sub-expression of every called function in the program. +rd_ret_top(w, e) :- rd_call0(_, w), method_returns_expr(w, e), expr_kind("client", _, "ROOT", e). .decl rd_ret_one(c0:symbol, c1:symbol) rd_ret_one(w, e) :- rd_ret_top(w, e), 1 = count : { rd_ret_top(w, _) }. .decl rd_call0_one(c0:symbol, c1:symbol) diff --git a/graph/typescript/engine/framework-behavior/http-router-chain.dl b/graph/typescript/engine/framework-behavior/http-router-chain.dl index 7859918d5..b348c63ec 100644 --- a/graph/typescript/engine/framework-behavior/http-router-chain.dl +++ b/graph/typescript/engine/framework-behavior/http-router-chain.dl @@ -68,6 +68,9 @@ framework_undetermined(m, "ctx_key", "dynamic_ctx_key") :- hb_get_site(m, _, k), .decl hb_chain(c0:symbol, c1:symbol, c2:symbol, c3:symbol, c4:number) hb_chain(h, vv, p, mw, to_number(pos)) :- middleware_applies(mw, h, vv, p, pos, _). hb_chain(h, vv, p, h, 100000) :- http_route(h, vv, p, _, _). +// …and on a PARTIAL route (SPEC R23): the key flows along its decided chain (middleware_applies under the partial +// path) whatever the path is. A middleware that only MAY apply (middleware_may_apply) is no position on it. +hb_chain(h, vv, p, h, 100000) :- http_route_partial(h, vv, p, _, _). // ctx_key_flow(Setter, Reader, Key) — on some route's chain, Setter runs before Reader // (or is Reader) and writes the context key Reader reads. diff --git a/graph/typescript/engine/framework-behavior/http-router-ctx-type.dl b/graph/typescript/engine/framework-behavior/http-router-ctx-type.dl new file mode 100644 index 000000000..da1526616 --- /dev/null +++ b/graph/typescript/engine/framework-behavior/http-router-ctx-type.dl @@ -0,0 +1,180 @@ +// ============================================================================ +// FRAMEWORK-BEHAVIOR · HTTP ROUTER CONTEXT VALUE TYPES (what a context read holds) +// +// http-router-chain.dl ties a context write to the reads that see it (ctx_key_flow). +// This file gives the READ a type, so a call on the value it returns resolves like a +// call on any typed receiver: +// +// const { keys } = c.get('services') // the type declared for key `services` +// await keys.verify(id) // ... so this reaches KeyService.verify +// c.var.db.query(...) // the same through the `var` view +// +// The type of key k is declared once, in the app's environment type: the first type +// argument of the context type, of the app class, of the factory or of the middleware +// helper (`Context`, `new App()`, `createFactory()`, +// `createMiddleware(fn)`, `MiddlewareHandler`), an object whose +// variables member (knob router_env_vars) lists the keys. The environment is found +// from the context PARAMETER of the function that reads it: +// (a) the parameter is annotated with a type carrying an environment; +// (b) the function is the initializer of a variable annotated with one; +// (c) the function is handed to a call whose first type argument is one +// (knob router_env_call); +// (d) the function is handed to a method call on an app value whose environment is +// known: built with one, declared with one (directly, through an alias, or as +// `ReturnType` of a function returning such an app), or reached through +// the calls that return the app (knob router_returns_app, router_base_path). +// +// Typing only: the rules add expr_type / expr_shape rows, never an edge, so a call on +// the value still goes through the ordinary member lookup and dispatch. Everything here +// is POSITIVE (no negation): it sits in the expr_type fixpoint, and it must not read the +// route table (http_route reads the call graph under negation), which is why app values +// are followed here by their syntax and declarations, not by hr_app. +// ============================================================================ + +// ── an environment type: an object type with a variables member ───────────── +// (env reference, the variables field) +.decl hcx_env_vars(c0:symbol, c1:symbol) +hcx_env_vars(e, fv) :- hr_on(), type_ref("client", _, _, _, _, _, _, e), ref_shape_target(e, o), + declared_field(o, vn, _, fv), router_env_vars(vn). +hcx_env_vars(e, fv) :- hr_on(), type_ref("client", _, _, _, _, _, _, e), ref_type_target(e, _, o), + declared_field(o, vn, _, fv), router_env_vars(vn). + +// ── a type reference that carries an environment as its first argument ────── +// (reference, env reference) +.decl hcx_envref(c0:symbol, c1:symbol) +hcx_envref(r, e) :- type_ref("client", "TYPE_REFERENCE", _, _, _, _, _, r), + type_ref_parent("client", r, "0", e), hcx_env_vars(e, _). +// `type AppContext = Context`, `type App = OpenAPIApp` +hcx_envref(r, e) :- alias_rhs(r, "client", rhs), hcx_envref(rhs, e). +// `type App = ReturnType`: the app the function returns +hcx_envref(r, e) :- hcx_return_of_query(r, f), method_returns_expr(f, x), hcx_app_env(x, e). + +// `ReturnType` (the reference, the function the query names in its module) +.decl hcx_return_of_query(c0:symbol, c1:symbol) +hcx_return_of_query(r, f) :- type_ref("client", "TYPE_REFERENCE", _, rt, _, _, _, r), ts_type_return_operator(rt), + type_ref_parent("client", r, "0", q), type_ref("client", "TYPE_QUERY", _, tn, _, _, _, q), + type_ref_module("client", m, q), scope_name_binds("client", m, tn, _, "METHOD", f). +hcx_return_of_query(r, f) :- type_ref("client", "TYPE_REFERENCE", _, rt, _, _, _, r), ts_type_return_operator(rt), + type_ref_parent("client", r, "0", q), type_ref("client", "TYPE_QUERY", _, tn, _, _, _, q), + type_ref_module("client", m, q), scope_name_binds("client", m, tn, _, "VARIABLE", v), var_is_function(v, f). + +// ── an app value whose environment is known ───────────────────────────────── +// (expression, env reference) +.decl hcx_app_env(c0:symbol, c1:symbol) +// built with it: `new App()`, `createFactory()` +hcx_app_env(x, e) :- hr_new(x), call_type_argument(x, "0", e), hcx_env_vars(e, _). +hcx_app_env(x, e) :- hr_factory_call(x), call_type_argument(x, "0", e), hcx_env_vars(e, _). +// `factory.createApp()`, and every call that returns the app it was called on +hcx_app_env(x, e) :- call_site("client", "METHOD_CALL", cn, _, r, x, _), router_factory_app(cn), hcx_app_env(r, e). +hcx_app_env(x, e) :- call_site("client", "METHOD_CALL", cn, _, r, x, _), router_returns_app(cn), hcx_app_env(r, e). +hcx_app_env(x, e) :- call_site("client", "METHOD_CALL", cn, _, r, x, _), router_base_path(cn), hcx_app_env(r, e). +// parentheses, `await`, casts +hcx_app_env(x, e) :- hcx_app_env(c, e), expr_child("client", x, _, _, c), + expr_kind("client", k, _, x), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +// a variable holding one (here or imported), or declared as one +hcx_app_env(x, e) :- hr_denotes_var(x, v), var_initializer("client", _, i, v), i != "", hcx_app_env(i, e). +hcx_app_env(x, e) :- hr_denotes_var(x, v), var_type_ref("client", r, v), hcx_envref(r, e). +// a parameter declared as one (`app: App`, `app: OpenAPIApp`) +hcx_app_env(x, e) :- expr_referenced("client", "PARAMETER", p, x), param_type_ref("client", r, p), hcx_envref(r, e). +// a call of a project function that returns one (`const app = newApp()`) +hcx_app_env(x, e) :- expr_call_candidate(x, w), method_returns_expr(w, y), hcx_app_env(y, e). + +// ── the environment a function's context parameter has ────────────────────── +// (function, env reference) +.decl hcx_fn_env(c0:symbol, c1:symbol) +// (a) annotated: `(c: Context) => …`, `(c: AppContext) => …` +hcx_fn_env(m, e) :- param_decl("client", _, "0", _, m, p), param_type_ref("client", r, p), hcx_envref(r, e). +// (b) the initializer of a variable declared `MiddlewareHandler` +hcx_fn_env(m, e) :- var_is_function(v, m), var_type_ref("client", r, v), hcx_envref(r, e). +// (b2) returned from a function declared to return one (`function auth(): MiddlewareHandler { return async (c, next) => .. }`) +hcx_fn_env(m, e) :- method_return_ref("client", r, f), hcx_envref(r, e), method_returns_expr(f, x), expr_anon_decl("client", m, x). +// (c) handed to `createMiddleware(fn)` +hcx_fn_env(m, e) :- callback_arg(ce, _, m), call_type_argument(ce, "0", e), hcx_env_vars(e, _), + call_site("client", _, cn, _, _, ce, _), router_env_call(cn). +// (d) handed to a method of an app value with a known environment (a route, a middleware) +hcx_fn_env(m, e) :- callback_arg(ce, _, m), call_site("client", "METHOD_CALL", _, _, r, ce, _), hcx_app_env(r, e). +// …or to a factory's handler helper (`factory.createHandlers(fn)`, `factory.createMiddleware(fn)`) +hcx_fn_env(m, e) :- callback_arg(ce, _, m), call_site("client", "METHOD_CALL", cn, _, r, ce, _), + (router_handler_spread(cn) ; router_factory_mw(cn)), hcx_app_env(r, e). + +// ── the context expression, and its environment ───────────────────────────── +.decl hcx_ctx(c0:symbol, c1:symbol) +hcx_ctx(x, e) :- expr_referenced("client", "PARAMETER", p, x), param_decl("client", _, "0", _, m, p), hcx_fn_env(m, e). + +// the variables object (env reference, the variables field) +// the field of key k (env reference, key, field) +.decl hcx_key_field(c0:symbol, c1:symbol, c2:symbol) +hcx_key_field(e, k, fk) :- hcx_env_vars(e, fv), field_type_ref("client", vr, fv), ref_shape_target(vr, vo), + declared_field(vo, k, _, fk). +hcx_key_field(e, k, fk) :- hcx_env_vars(e, fv), field_type_ref("client", vr, fv), ref_type_target(vr, _, vo), + declared_field(vo, k, _, fk). + +// ── `c.get(k)`: the type declared for k ───────────────────────────────────── +.decl hcx_get(c0:symbol, c1:symbol, c2:symbol) +hcx_get(ce, e, key) :- call_site("client", "METHOD_CALL", gn, _, r, ce, _), router_ctx_get(gn), hcx_ctx(r, e), + expr_child("client", ce, "ARGUMENT", "0", k), expr_literal_value("client", key, k). +expr_type(ce, prov, t) :- hcx_get(ce, e, key), hcx_key_field(e, key, fk), field_type(fk, prov, t). +expr_shape(ce, s) :- hcx_get(ce, e, key), hcx_key_field(e, key, fk), field_shape(fk, s). + +// ── `c.var`: the variables object, so `c.var.k` and `const { k } = c.var` are its members ─ +.decl hcx_var(c0:symbol, c1:symbol) +hcx_var(x, e) :- property_access_recv(x, vn, r), router_ctx_var(vn), hcx_ctx(r, e). +expr_type(x, prov, t) :- hcx_var(x, e), hcx_env_vars(e, fv), field_type(fv, prov, t). +expr_shape(x, s) :- hcx_var(x, e), hcx_env_vars(e, fv), field_shape(fv, s). + +// ── NO ENVIRONMENT DECLARED: the type of what the app's own middleware writes ─ +// `new App()` (or `App`, or a context parameter written `c: any`) declares no key +// types, and the compiler types every read `any`. What the read holds is then what a +// function handed to the SAME app writes under that key (`c.set('orders', new +// OrderService(..))`): the value's type, wherever it is known. Keyed by the app the +// writer and the reader are registered on, never by the key alone, so two apps that use +// one key for different things do not mix. (A write in a parent app read in a sub-app it +// mounts is not followed here: the mount is in the route table, which this stratum may +// not read.) +// (expression, the expression that created the app's route list) +.decl hcx_app_root(c0:symbol, c1:symbol) +hcx_app_root(x, x) :- hr_new(x). +hcx_app_root(x, x) :- hr_factory_call(x). +hcx_app_root(x, a) :- call_site("client", "METHOD_CALL", cn, _, r, x, _), router_factory_app(cn), hcx_app_root(r, a). +hcx_app_root(x, a) :- call_site("client", "METHOD_CALL", cn, _, r, x, _), router_returns_app(cn), hcx_app_root(r, a). +hcx_app_root(x, a) :- call_site("client", "METHOD_CALL", cn, _, r, x, _), router_base_path(cn), hcx_app_root(r, a). +hcx_app_root(x, a) :- hcx_app_root(c, a), expr_child("client", x, _, _, c), + expr_kind("client", k, _, x), (expr_kind_is_transparent(k) ; k = "AWAIT_EXPRESSION"). +hcx_app_root(x, a) :- hr_denotes_var(x, v), var_initializer("client", _, i, v), i != "", hcx_app_root(i, a). +hcx_app_root(x, a) :- expr_call_candidate(x, w), method_returns_expr(w, y), hcx_app_root(y, a). +// (function, app root): a function handed to a method of the app (a route, a middleware) +.decl hcx_fn_root(c0:symbol, c1:symbol) +hcx_fn_root(m, a) :- callback_arg(ce, _, m), call_site("client", "METHOD_CALL", _, _, r, ce, _), hcx_app_root(r, a). +// the context parameter of such a function, typed or not +.decl hcx_ctx_root(c0:symbol, c1:symbol) +hcx_ctx_root(x, a) :- expr_referenced("client", "PARAMETER", p, x), param_decl("client", _, "0", _, m, p), hcx_fn_root(m, a). +// (app root, key, written value) +.decl hcx_set_root(c0:symbol, c1:symbol, c2:symbol) +hcx_set_root(a, key, v) :- call_site("client", "METHOD_CALL", sn, _, r, ce, _), router_ctx_set(sn), hcx_ctx_root(r, a), + expr_child("client", ce, "ARGUMENT", "0", k), expr_literal_value("client", key, k), + expr_child("client", ce, "ARGUMENT", "1", v). +// `c.get(k)` and `c.var.k` on the same app take the written value's type +.decl hcx_read_root(c0:symbol, c1:symbol, c2:symbol) +hcx_read_root(ce, a, key) :- call_site("client", "METHOD_CALL", gn, _, r, ce, _), router_ctx_get(gn), hcx_ctx_root(r, a), + expr_child("client", ce, "ARGUMENT", "0", k), expr_literal_value("client", key, k). +hcx_read_root(x, a, key) :- property_access_recv(x, key, v), property_access_recv(v, vn, r), router_ctx_var(vn), hcx_ctx_root(r, a). +expr_type(x, prov, t) :- hcx_read_root(x, a, key), hcx_set_root(a, key, v), expr_type(v, prov, t). +expr_shape(x, s) :- hcx_read_root(x, a, key), hcx_set_root(a, key, v), expr_shape(v, s). + +// ── `c.req.valid(target)` after a function validator: what the validator returns ── +// `app.post(p, validator('json', (value) => new Role(..)), (c) => c.req.valid('json').toJSON())`: the +// validated value IS the validator function's result. Read off the registration's own arguments (the +// validator before the handler in one call), not off the route table, for the stratum reason above. +.decl hcx_valid_fn(c0:symbol, c1:symbol, c2:symbol) // (registration call, target, validator function) +hcx_valid_fn(rce, t, f) :- expr_child("client", rce, "ARGUMENT", vp, vce), hr_knob_call(vce, spec, n), router_validator_fn(spec, n), + expr_child("client", vce, "ARGUMENT", "0", tx), expr_literal_value("client", t, tx), callback_arg(vce, "1", f), + expr_child("client", rce, "ARGUMENT", hp, _), to_number(vp) < to_number(hp). +.decl hcx_valid_read(c0:symbol, c1:symbol) // (read, validator function) +hcx_valid_read(ce, f) :- hcx_valid_fn(rce, t, f), callback_arg(rce, hp, m), expr_child("client", rce, "ARGUMENT", vp, vce), + hr_knob_call(vce, _, _), to_number(vp) < to_number(hp), + expr_referenced("client", "PARAMETER", p, cx), param_decl("client", _, "0", _, m, p), + property_access_recv(rq, rn, cx), router_ctx_req(rn), + call_site("client", "METHOD_CALL", vn, _, rq, ce, _), router_valid_read(vn), + expr_child("client", ce, "ARGUMENT", "0", tx), expr_literal_value("client", t, tx). +expr_type(ce, prov, t) :- hcx_valid_read(ce, f), method_returns_expr(f, x), expr_kind("client", _, "ROOT", x), expr_type(x, prov, t). +expr_shape(ce, s) :- hcx_valid_read(ce, f), method_returns_expr(f, x), expr_kind("client", _, "ROOT", x), expr_shape(x, s). diff --git a/graph/typescript/engine/resolution/element-values.dl b/graph/typescript/engine/resolution/element-values.dl new file mode 100644 index 000000000..ada71b4e3 --- /dev/null +++ b/graph/typescript/engine/resolution/element-values.dl @@ -0,0 +1,42 @@ +// ============================================================================ +// Resolution · TYPES THAT ARRIVE AS AN ELEMENT (SPEC R19, framework-free) +// +// The no-library clauses in contextual-params.dl type a callback's element parameter from +// the RECEIVER'S DECLARED REFERENCE (`xs: Role[]`, `getItems(): Role[]`). Three readings of +// the same reference were missing, and each left a parameter or a binding untyped: +// +// * a call whose function declares no return type gives back what it returns: +// `roles() { return this.entries }` then `ledger.roles().map((r) => r.toJSON())`; +// * the element-taking members of a built-in container hand back one element: +// `pairs.shift()!`, `pairs.pop()`, `pairs.at(0)`, `pairs.find(..)`; +// * an array pattern over a value whose reference is a tuple binds the member at its +// position: `const [, first] = pairs.shift()!` binds `first` to the tuple's member 1. +// +// References only, positive, inside the expr_type fixpoint like the rules they extend. +// ============================================================================ +.decl elem_taking_member(c0:symbol) +elem_taking_member("shift"). elem_taking_member("pop"). elem_taking_member("at"). +elem_taking_member("find"). elem_taking_member("findLast"). + +// a call of a function with no declared return: the reference of what it returns +expr_decl_ref(e, r) :- expr_call_candidate(e, callee), + !method_declares_return(callee), + method_returns_expr(callee, x), + expr_kind("client", _, "ROOT", x), + expr_decl_ref(x, r). +// `xs.shift()`: one element of a built-in container +expr_decl_ref(e, c) :- call_site("client", "METHOD_CALL", n, _, recv, e, _), + elem_taking_member(n), + expr_decl_ref(recv, rr), + builtin_elem_ref(rr, c). +// `x!`, `(x)`: the same reference +expr_decl_ref(e, r) :- expr_kind("client", k, _, e), (k = "NON_NULL_EXPRESSION" ; k = "PARENTHESIZED_EXPRESSION"), + expr_child("client", e, _, _, x), + expr_decl_ref(x, r). +// `const [, first] = `: the member at the bound position +var_type(v, prov, t) :- var_binding("client", _, "INDEX", i, e, v), e != "", + !var_has_declared_type(v), + expr_decl_ref(e, tr), + type_ref(_, "TUPLE", _, _, _, _, _, tr), + type_ref_parent(_, tr, i, mr), + ref_type_target(mr, prov, t). diff --git a/graph/typescript/engine/resolution/lexical-this.dl b/graph/typescript/engine/resolution/lexical-this.dl new file mode 100644 index 000000000..2843cdb3c --- /dev/null +++ b/graph/typescript/engine/resolution/lexical-this.dl @@ -0,0 +1,33 @@ +// ============================================================================ +// Resolution · LEXICAL `this` (an arrow function has no `this` of its own) +// +// `this` inside an arrow is the `this` of the nearest enclosing function that is NOT an +// arrow, at any depth: `static fail(e) { return e.issues.map((i) => this.norm(i)) }` calls +// the class's static `norm`. expr-type.dl decides "this is the class, search the statics" +// from the flags of the function the expression sits in, and for a `this` written inside +// the arrow that function is the arrow, which is never static, so the call was searched in +// the instance table and found nothing. Same for a declared `this` parameter of the outer +// function. The instance type of `this` is unaffected: expr_enclosing_type already names +// the class for an expression at any depth inside it. +// ============================================================================ + +.decl lx_arrow(c0:symbol, c1:symbol) +// (arrow declaration, the expression that writes it) +lx_arrow(lm, x) :- expr_anon_decl("client", lm, x), expr_kind("client", "ARROW_FUNCTION", _, x). +// the function `this` is bound by, for a function: itself unless it is an arrow +.decl lx_home(c0:symbol, c1:symbol) +lx_home(m, m) :- method_decl("client", _, _, _, m), !lx_arrow(m, _). +lx_home(lm, h) :- lx_arrow(lm, x), expr_enclosing_method(x, m), m != lm, lx_home(m, h). + +// `this` in an arrow nested in a STATIC method is the class +expr_static_type(e, "client", t) :- this_expr(e), + expr_enclosing_type(e, t), + expr_enclosing_method(e, em), + lx_arrow(em, _), + lx_home(em, h), + method_flags(_, "true", _, _, h). +// …and in an arrow nested in a function that declares its `this` parameter +expr_type(e, prov, t) :- this_expr(e), expr_enclosing_method(e, em), lx_arrow(em, _), lx_home(em, h), + method_declared_this(h, p), param_type(p, prov, t). +expr_shape(e, s) :- this_expr(e), expr_enclosing_method(e, em), lx_arrow(em, _), lx_home(em, h), + method_declared_this(h, p), param_shape_target(p, s). diff --git a/graph/typescript/engine/resolution/reference-types.dl b/graph/typescript/engine/resolution/reference-types.dl index 04b5345bb..013f53eda 100644 --- a/graph/typescript/engine/resolution/reference-types.dl +++ b/graph/typescript/engine/resolution/reference-types.dl @@ -202,6 +202,7 @@ var_type(v, prov, t) :- var_initializer("client", _, e, v), var_shape(v, s) :- var_initializer("client", _, e, v), e != "", !var_has_declared_type(v), + !var_is_destructured(v), expr_shape(e, s). // `const s = "text"` and friends — a literal initializer types the variable by its diff --git a/graph/typescript/engine/resolution/shape-members.dl b/graph/typescript/engine/resolution/shape-members.dl new file mode 100644 index 000000000..cfca70ec8 --- /dev/null +++ b/graph/typescript/engine/resolution/shape-members.dl @@ -0,0 +1,40 @@ +// ============================================================================ +// Resolution · MEMBERS OF AN ANONYMOUS SHAPE (a shape's field is a value too) +// +// An object type written in place (`{ keys: KeyService; db: { primary: Db } }`) owns +// its members through the type-reference row (ref_shape_target). A property access on +// a shape-typed receiver already gives the member's NAMED type (expr-type.dl), but two +// readings of the same member had no rule: +// +// * the member is itself a shape: `services.db.primary` -- `services.db` had no shape, +// so `.primary` and everything below it was untyped; +// * the member is DESTRUCTURED out of a shape-typed value: `const { keys } = services` +// -- var_binding_target reads expr_type only, so a name bound out of a shape had no +// type at all, while `services.keys` one line up resolved. +// +// Both are the shape mirror of rules that exist for a named owner (expr-type.dl's +// field_shape clause, reference-types.dl's var_binding_target), and read the same +// member tables. Positive and inside the expr_type fixpoint, like their mirrors. +// ============================================================================ + +// `services.db` where `services` is a shape and `db` is declared with an object type +expr_shape(e, s) :- property_access_recv(e, n, q), + expr_shape(q, qs), + field_in_scope(qs, n, _, f), + field_shape(f, s). +expr_decl_ref(e, r) :- property_access_recv(e, n, q), + expr_shape(q, qs), + field_in_scope(qs, n, _, f), + field_type_ref(_, r, f). + +// `const { keys, db } = services` where `services` is a shape +var_type(v, prov, ft) :- var_binding("client", _, "PROPERTY", src, e, v), + e != "", + expr_shape(e, s), + field_in_scope(s, src, _, f), + field_type(f, prov, ft). +var_shape(v, sh) :- var_binding("client", _, "PROPERTY", src, e, v), + e != "", + expr_shape(e, s), + field_in_scope(s, src, _, f), + field_shape(f, sh). diff --git a/graph/typescript/engine/resolution/value-flow.dl b/graph/typescript/engine/resolution/value-flow.dl index 446df8bf2..e05dffcc6 100644 --- a/graph/typescript/engine/resolution/value-flow.dl +++ b/graph/typescript/engine/resolution/value-flow.dl @@ -782,6 +782,7 @@ holder_elem_fn(h, m) :- holder_value(h, e), // ── the reads ─────────────────────────────────────────────────────────────── // `h[i]`, `h[kind]`: any element (an index or a computed key picks one we cannot) elem_read_fn(x, m) :- expr_kind("client", "ELEMENT_ACCESS", _, x), + !elem_key_is_decided(x), expr_child("client", x, "RECEIVER", _, r), expr_holder(r, h), holder_elem_fn(h, m). @@ -837,8 +838,20 @@ array_method_element_param("every", "0"). array_method_element_param("reduce", "1"). array_method_element_param("reduceRight", "1"). -// `h[i](x)`, `h.run(x)` -call_runs(ce, m) :- call_callee_expr(_, ce, ne), elem_read_fn(ne, m). +// `h[i](x)`, `h.run(x)` -- and, for an element access, only when its key is decided (SPEC R21a): a table +// read with a key the program computes (`handlers[op](x)`) can run any element, so the call stays unknown +// and the elements stay reachable from it only as its candidates (elem_call_candidate), never as calls it makes +call_runs(ce, m) :- call_callee_expr(_, ce, ne), elem_read_fn(ne, m), !elem_key_undecided(ne). +elem_key_undecided(x) :- expr_kind("client", "ELEMENT_ACCESS", _, x), !elem_key_is_decided(x). +elem_call_candidate(ce, m) :- call_callee_expr(_, ce, ne), elem_key_undecided(ne), elem_read_fn(ne, m). +// …and where the table is TYPED (`const factories: { [k: string]: (n: number) => Shape }`), the call resolves to the +// call signature of its element type: each element is then a `value` candidate of that signature, the tier that says +// "may run" (SPEC R21a), not a call the site makes +method_dispatch_candidate(sig, m, "value") :- elem_call_candidate(ce, m), + expr_resolves_to_method(ce, sig), + method_kind(_, k, _, sig), + method_kind_is_call_signature(k), + m != sig. // loop_elem_callee(Call, Method) — `for (const g of groups) g.prefix(v)` where `groups` is a const array literal of typed // elements (`[new UserRoutes()]`): the method of each element's class. A value fact the call graph does not draw @@ -848,3 +861,52 @@ loop_elem_callee(ce, w) :- call_site("client", "METHOD_CALL", cn, _, r, ce, _), for_binding_iterable(v, it), expr_referenced("client", "VARIABLE", d, it), var_initializer("client", _, arr, d), expr_kind("client", "ARRAY_LITERAL", _, arr), expr_child("client", arr, _, _, el), expr_type(el, _, tt), method_in_scope(tt, cn, _, w), !call_chain_edge(ce, _, "-", _, "client", _, _). + +// ── a DECIDED key picks its own element (SPEC R21a) ───────────────────────── +.decl elem_key_decided(c0:symbol, c1:symbol) +.decl elem_key_is_decided(c0:symbol) +.decl elem_key_undecided(c0:symbol) +.decl elem_value_fn(c0:symbol, c1:symbol) +.decl elem_call_candidate(c0:symbol, c1:symbol) // (call, function): a table read with an undecided key may run it +// `handlers['archive'](id)`, `handlers[ACTION](id)` with `const ACTION = 'restore'`, and +// `pipeline[1](id)` name ONE element of a literal the holder holds. Joining them to every +// element ran `purge` from a call that can only run `archive`. A key written as a literal, +// or a const initialised with one, is decided; it selects the property of that name of an +// object literal, or the element at that position of an array literal, and nothing else. +// A key that is not decided still reaches every element (the rule above), as a dispatch +// candidate of a call that stays unknown. +// (element access, the key) +elem_key_decided(x, k) :- expr_kind("client", "ELEMENT_ACCESS", _, x), + expr_child("client", x, "INDEX_ARGUMENT", _, i), + expr_kind("client", "LITERAL", _, i), + expr_literal_value("client", k, i). +elem_key_decided(x, k) :- expr_kind("client", "ELEMENT_ACCESS", _, x), + expr_child("client", x, "INDEX_ARGUMENT", _, i), + expr_literal_type("client", "NO_SUBSTITUTION_TEMPLATE", i), + expr_literal_value("client", k, i). +elem_key_decided(x, k) :- expr_kind("client", "ELEMENT_ACCESS", _, x), + expr_child("client", x, "INDEX_ARGUMENT", _, i), + expr_referenced("client", "VARIABLE", v, i), + !var_reassigned(v), + var_initializer("client", _, li, v), + expr_kind("client", "LITERAL", _, li), + expr_literal_value("client", k, li). +elem_key_is_decided(x) :- elem_key_decided(x, _). +// the property of that name / the element at that position +elem_read_fn(x, m) :- elem_key_decided(x, k), + expr_child("client", x, "RECEIVER", _, r), + expr_holder(r, h), + holder_value(h, e), + value_branch(e, l), + objlit_prop(l, k, val), + elem_value_fn(val, m). +elem_read_fn(x, m) :- elem_key_decided(x, k), + expr_child("client", x, "RECEIVER", _, r), + expr_holder(r, h), + holder_value(h, e), + value_branch(e, l), + expr_kind("client", "ARRAY_LITERAL", _, l), + expr_child("client", l, "ARRAY_ELEMENT", k, val), + elem_value_fn(val, m). +elem_value_fn(val, m) :- !expr_kind("client", "CALL_EXPRESSION", _, val), expr_callable(val, m). +elem_value_fn(val, m) :- expr_holder(val, h2), holder_holds_function(h2, m). diff --git a/plugins/axiomcode/skills/axiomcode/scripts/ax_edges.py b/plugins/axiomcode/skills/axiomcode/scripts/ax_edges.py index 98764b89a..dce8841a8 100644 --- a/plugins/axiomcode/skills/axiomcode/scripts/ax_edges.py +++ b/plugins/axiomcode/skills/axiomcode/scripts/ax_edges.py @@ -47,6 +47,7 @@ 'dispatch': 2, # a base method to an override that is actually instantiated 'overload': 0, # the overload signature a call selected runs its set's implementation: one body, certain 'callback_registered': 3, # handed over as a value and invoked by whoever holds it + 'callback_handed': 3, # handed to a project function whose body can run it 'event_dispatch': 3, # emitted here, handled there 'asserted': 3, # a link someone recorded (axiomcode link) where the engine resolved nothing: read, not derived 'remote': 5, # a request crosses a process to its handler (remote_edge): no call site names it. @@ -71,6 +72,7 @@ 'dispatch': 'a base method to an override the project instantiates', 'overload': 'the call selected an overload signature; this is the implementation that runs', 'callback_registered': 'handed over as a value and invoked by whoever holds it', + 'callback_handed': 'handed to a project function whose body can run it', 'event_dispatch': 'emitted here, handled there', 'asserted': 'ASSERTED by a link (axiomcode-links.tsv): someone read the call and recorded its target; the engine did not resolve it', 'remote': 'NOT a call site: a request crosses a process to the handler that serves it (transport and destination on the hop)', @@ -179,7 +181,7 @@ def legend(tiers): 'implicit_constructor': 'resolved', 'written': 'resolved', 'known_implicit_ctor': 'resolved', 'known_builtin_operator': 'resolved', 'runtime_observed': 'resolved', 'multi_inferred': 'one of a set', - 'callback_registered': 'registered', 'event_dispatch': 'registered', + 'callback_registered': 'registered', 'callback_handed': 'registered', 'event_dispatch': 'registered', 'ambient_terminal': 'registered', 'dynamic_terminal': 'registered', 'intrinsic_terminal': 'registered', 'fan_capped': 'capped set', 'asserted': 'asserted', # a link someone recorded (ax_links.py): an edge, never `resolved` From fa7558856a8b437c74bb778836c384d714771900 Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Fri, 30 Oct 2026 02:43:41 -0700 Subject: [PATCH 7/9] tests: Hono call-chain cases, each checked against the compiler, the Hono oracle and a runtime trace A chains/ case pins, per request, the chain of functions it runs: middleware in order, the handler, and the services and helpers the handler reaches. Its .chains golden is checked against three independent halves of the truth: a compiler call-chain oracle (truth/cc-oracle.mjs, the TypeScript compiler API over the fixture), the Hono oracle's middleware order, and a runtime trace (truth/chain-runtime.mjs: the fixture instrumented by the repository's runtime oracle and each chain's request sent to the real pinned hono). tools/chain_check.py compares them and lists every difference. TypeScript cases: ctx-partial-route, ctx-partial-untyped, ctx-services-typed, ctx-set-untyped, function-values, library-generic-params, post-validator-chain, same-name-unrelated, this-in-nested-arrows. JavaScript: esm-services-chain. --- .../chains/esm-services-chain/src/index.js | 22 + .../esm-services-chain/src/package.json | 1 + .../chains/esm-services-chain/src/services.js | 37 ++ .../expected/chains/esm-services-chain.chains | 23 + .../chains/esm-services-chain.entries | 5 + .../expected/chains/esm-services-chain.tsv | 10 + .../cases/chains/ctx-partial-route/src/app.ts | 42 ++ .../chains/ctx-partial-route/src/users.ts | 39 ++ .../chains/ctx-partial-untyped/src/app.ts | 28 + .../chains/ctx-partial-untyped/src/orders.ts | 32 ++ .../chains/ctx-services-typed/src/app.ts | 33 ++ .../cases/chains/ctx-services-typed/src/db.ts | 10 + .../chains/ctx-services-typed/src/env.ts | 9 + .../chains/ctx-services-typed/src/handlers.ts | 14 + .../chains/ctx-services-typed/src/repos.ts | 21 + .../chains/ctx-services-typed/src/services.ts | 27 + .../chains/ctx-services-typed/src/wire.ts | 22 + .../cases/chains/ctx-set-untyped/src/app.ts | 24 + .../chains/ctx-set-untyped/src/orders.ts | 30 + .../cases/chains/function-values/src/app.ts | 28 + .../cases/chains/function-values/src/ops.ts | 46 ++ .../chains/library-generic-params/src/app.ts | 25 + .../library-generic-params/src/model.ts | 39 ++ .../library-generic-params/src/report.ts | 36 ++ .../chains/post-validator-chain/src/app.ts | 40 ++ .../chains/post-validator-chain/src/orders.ts | 36 ++ .../chains/same-name-unrelated/src/app.ts | 32 ++ .../chains/same-name-unrelated/src/stores.ts | 23 + .../chains/this-in-nested-arrows/src/app.ts | 12 + .../chains/this-in-nested-arrows/src/resp.ts | 47 ++ .../expected/chains/ctx-partial-route.chains | 25 + .../expected/chains/ctx-partial-route.entries | 5 + .../expected/chains/ctx-partial-route.tsv | 11 + .../chains/ctx-partial-untyped.chains | 20 + .../chains/ctx-partial-untyped.entries | 6 + .../expected/chains/ctx-partial-untyped.tsv | 12 + .../expected/chains/ctx-services-typed.chains | 38 ++ .../chains/ctx-services-typed.entries | 9 + .../expected/chains/ctx-services-typed.tsv | 24 + .../expected/chains/ctx-set-untyped.chains | 22 + .../expected/chains/ctx-set-untyped.entries | 6 + .../hono/expected/chains/ctx-set-untyped.tsv | 11 + .../expected/chains/function-values.chains | 42 ++ .../expected/chains/function-values.entries | 9 + .../hono/expected/chains/function-values.tsv | 16 + .../chains/library-generic-params.chains | 58 ++ .../chains/library-generic-params.entries | 8 + .../chains/library-generic-params.tsv | 15 + .../chains/post-validator-chain.chains | 25 + .../chains/post-validator-chain.entries | 5 + .../expected/chains/post-validator-chain.tsv | 14 + .../chains/same-name-unrelated.chains | 23 + .../chains/same-name-unrelated.entries | 8 + .../expected/chains/same-name-unrelated.tsv | 16 + .../chains/this-in-nested-arrows.chains | 53 ++ .../chains/this-in-nested-arrows.entries | 7 + .../expected/chains/this-in-nested-arrows.tsv | 12 + .../test/typescript/hono/tools/chain_check.py | 106 ++++ .../test/typescript/hono/truth/cc-oracle.mjs | 511 ++++++++++++++++++ .../typescript/hono/truth/chain-runtime.mjs | 161 ++++++ 60 files changed, 2071 insertions(+) create mode 100644 graph/test/javascript/hono/cases/chains/esm-services-chain/src/index.js create mode 100644 graph/test/javascript/hono/cases/chains/esm-services-chain/src/package.json create mode 100644 graph/test/javascript/hono/cases/chains/esm-services-chain/src/services.js create mode 100644 graph/test/javascript/hono/expected/chains/esm-services-chain.chains create mode 100644 graph/test/javascript/hono/expected/chains/esm-services-chain.entries create mode 100644 graph/test/javascript/hono/expected/chains/esm-services-chain.tsv create mode 100644 graph/test/typescript/hono/cases/chains/ctx-partial-route/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-partial-route/src/users.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-partial-untyped/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-partial-untyped/src/orders.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-services-typed/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-services-typed/src/db.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-services-typed/src/env.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-services-typed/src/handlers.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-services-typed/src/repos.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-services-typed/src/services.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-services-typed/src/wire.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-set-untyped/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/ctx-set-untyped/src/orders.ts create mode 100644 graph/test/typescript/hono/cases/chains/function-values/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/function-values/src/ops.ts create mode 100644 graph/test/typescript/hono/cases/chains/library-generic-params/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/library-generic-params/src/model.ts create mode 100644 graph/test/typescript/hono/cases/chains/library-generic-params/src/report.ts create mode 100644 graph/test/typescript/hono/cases/chains/post-validator-chain/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/post-validator-chain/src/orders.ts create mode 100644 graph/test/typescript/hono/cases/chains/same-name-unrelated/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/same-name-unrelated/src/stores.ts create mode 100644 graph/test/typescript/hono/cases/chains/this-in-nested-arrows/src/app.ts create mode 100644 graph/test/typescript/hono/cases/chains/this-in-nested-arrows/src/resp.ts create mode 100644 graph/test/typescript/hono/expected/chains/ctx-partial-route.chains create mode 100644 graph/test/typescript/hono/expected/chains/ctx-partial-route.entries create mode 100644 graph/test/typescript/hono/expected/chains/ctx-partial-route.tsv create mode 100644 graph/test/typescript/hono/expected/chains/ctx-partial-untyped.chains create mode 100644 graph/test/typescript/hono/expected/chains/ctx-partial-untyped.entries create mode 100644 graph/test/typescript/hono/expected/chains/ctx-partial-untyped.tsv create mode 100644 graph/test/typescript/hono/expected/chains/ctx-services-typed.chains create mode 100644 graph/test/typescript/hono/expected/chains/ctx-services-typed.entries create mode 100644 graph/test/typescript/hono/expected/chains/ctx-services-typed.tsv create mode 100644 graph/test/typescript/hono/expected/chains/ctx-set-untyped.chains create mode 100644 graph/test/typescript/hono/expected/chains/ctx-set-untyped.entries create mode 100644 graph/test/typescript/hono/expected/chains/ctx-set-untyped.tsv create mode 100644 graph/test/typescript/hono/expected/chains/function-values.chains create mode 100644 graph/test/typescript/hono/expected/chains/function-values.entries create mode 100644 graph/test/typescript/hono/expected/chains/function-values.tsv create mode 100644 graph/test/typescript/hono/expected/chains/library-generic-params.chains create mode 100644 graph/test/typescript/hono/expected/chains/library-generic-params.entries create mode 100644 graph/test/typescript/hono/expected/chains/library-generic-params.tsv create mode 100644 graph/test/typescript/hono/expected/chains/post-validator-chain.chains create mode 100644 graph/test/typescript/hono/expected/chains/post-validator-chain.entries create mode 100644 graph/test/typescript/hono/expected/chains/post-validator-chain.tsv create mode 100644 graph/test/typescript/hono/expected/chains/same-name-unrelated.chains create mode 100644 graph/test/typescript/hono/expected/chains/same-name-unrelated.entries create mode 100644 graph/test/typescript/hono/expected/chains/same-name-unrelated.tsv create mode 100644 graph/test/typescript/hono/expected/chains/this-in-nested-arrows.chains create mode 100644 graph/test/typescript/hono/expected/chains/this-in-nested-arrows.entries create mode 100644 graph/test/typescript/hono/expected/chains/this-in-nested-arrows.tsv create mode 100644 graph/test/typescript/hono/tools/chain_check.py create mode 100644 graph/test/typescript/hono/truth/cc-oracle.mjs create mode 100644 graph/test/typescript/hono/truth/chain-runtime.mjs diff --git a/graph/test/javascript/hono/cases/chains/esm-services-chain/src/index.js b/graph/test/javascript/hono/cases/chains/esm-services-chain/src/index.js new file mode 100644 index 000000000..2e69dc61b --- /dev/null +++ b/graph/test/javascript/hono/cases/chains/esm-services-chain/src/index.js @@ -0,0 +1,22 @@ +import { Hono } from 'hono' +import { createMiddleware } from 'hono/factory' +import { Db, OrderService } from './services.js' + +const provide = createMiddleware(async (c, next) => { + c.set('services', { orders: new OrderService(new Db()) }) + await next() +}) + +const app = new Hono() +app.use('/orders/*', provide) + +// destructured c.get(k), then a call on the member +app.get('/orders/:id', (c) => { + const { orders } = c.get('services') + return c.json(orders.find(c.req.param('id'))) +}) + +// c.var.k read inline, two members deep +app.get('/orders/:id/total', (c) => c.json({ total: c.var.services.orders.total(c.req.param('id')) })) + +export default app diff --git a/graph/test/javascript/hono/cases/chains/esm-services-chain/src/package.json b/graph/test/javascript/hono/cases/chains/esm-services-chain/src/package.json new file mode 100644 index 000000000..089153bcb --- /dev/null +++ b/graph/test/javascript/hono/cases/chains/esm-services-chain/src/package.json @@ -0,0 +1 @@ +{"type":"module"} diff --git a/graph/test/javascript/hono/cases/chains/esm-services-chain/src/services.js b/graph/test/javascript/hono/cases/chains/esm-services-chain/src/services.js new file mode 100644 index 000000000..7a42969fe --- /dev/null +++ b/graph/test/javascript/hono/cases/chains/esm-services-chain/src/services.js @@ -0,0 +1,37 @@ +// census: ctx_di (SPEC R18, the JavaScript form; call-chain break class 1) +// The JavaScript twin of the typed TypeScript chains: no types anywhere, so what `c.get('services')` holds is only +// the provider middleware's `c.set('services', ..)` on the chain (ctx_key_flow). The repository builds its own db +// handle from a constructor argument the compiler cannot type, so service -> repository resolves statically and +// repository -> db is observed at run time. +export class Db { + get(key) { + return [key] + } + sum(key) { + return key.length + } +} + +export class OrderRepo { + constructor(db) { + this.db = db + } + byId(id) { + return this.db.get(`order:${id}`) + } + total(id) { + return this.db.sum(`order:${id}`) + } +} + +export class OrderService { + constructor(db) { + this.repo = new OrderRepo(db) + } + find(id) { + return this.repo.byId(id) + } + total(id) { + return this.repo.total(id) + } +} diff --git a/graph/test/javascript/hono/expected/chains/esm-services-chain.chains b/graph/test/javascript/hono/expected/chains/esm-services-chain.chains new file mode 100644 index 000000000..ca94beeb8 --- /dev/null +++ b/graph/test/javascript/hono/expected/chains/esm-services-chain.chains @@ -0,0 +1,23 @@ +# hand-reviewed 2026-10-28 against the fixture, the compiler call-chain oracle and the runtime trace: the JavaScript twin of the R18 chains: the compiler types c.get(k) / c.var.k as any and the repository db as any, so handler -> service and repository -> db are run-time edges (rt); service -> repository resolves statically through the field the constructor builds +# census: ctx_di (SPEC R18, the JavaScript form; call-chain break class 1) +fn index.js:14:24 +fn index.js:20:30 +fn index.js:5:34 +fn services.js:10:3 +fn services.js:16:3 +fn services.js:19:3 +fn services.js:22:3 +fn services.js:28:3 +fn services.js:31:3 +fn services.js:34:3 +fn services.js:7:3 +edge index.js:5:34 services.js:28:3 +edge services.js:28:3 services.js:16:3 +edge services.js:31:3 services.js:19:3 +edge services.js:34:3 services.js:22:3 +edge index.js:14:24 services.js:31:3 rt +edge index.js:20:30 services.js:34:3 rt +edge services.js:19:3 services.js:7:3 rt +edge services.js:22:3 services.js:10:3 rt +chain order-find GET /orders/x1 index.js:5:34 index.js:14:24 services.js:31:3 services.js:19:3 services.js:7:3 +chain order-total GET /orders/x1/total index.js:5:34 index.js:20:30 services.js:34:3 services.js:22:3 services.js:10:3 diff --git a/graph/test/javascript/hono/expected/chains/esm-services-chain.entries b/graph/test/javascript/hono/expected/chains/esm-services-chain.entries new file mode 100644 index 000000000..a0efade66 --- /dev/null +++ b/graph/test/javascript/hono/expected/chains/esm-services-chain.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: chains fixture, ESM JavaScript (createMiddleware provider, c.get destructured, c.var two members deep): routes, middleware chains, ctx flows +# census: ctx_di (SPEC R18, the JavaScript form; call-chain break class 1) +entry index.js:14:24 http +entry index.js:20:30 http +entry index.js:5:34 http diff --git a/graph/test/javascript/hono/expected/chains/esm-services-chain.tsv b/graph/test/javascript/hono/expected/chains/esm-services-chain.tsv new file mode 100644 index 000000000..0d27a7130 --- /dev/null +++ b/graph/test/javascript/hono/expected/chains/esm-services-chain.tsv @@ -0,0 +1,10 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: chains fixture, ESM JavaScript (createMiddleware provider, c.get destructured, c.var two members deep): routes, middleware chains, ctx flows +# census: ctx_di (SPEC R18, the JavaScript form; call-chain break class 1) +route GET /orders/:id index.js:14:24 index.js:10:13 1 +route GET /orders/:id/total index.js:20:30 index.js:10:13 2 +mw index.js:5:34 index.js:14:24 GET /orders/:id 0 index.js:10:13 +mw index.js:5:34 index.js:20:30 GET /orders/:id/total 0 index.js:10:13 +ctx index.js:5:34 index.js:14:24 services +ctx index.js:5:34 index.js:20:30 services +regby index.js:1:1 index.js:14:24 GET /orders/:id index.js:10:13 +regby index.js:1:1 index.js:20:30 GET /orders/:id/total index.js:10:13 diff --git a/graph/test/typescript/hono/cases/chains/ctx-partial-route/src/app.ts b/graph/test/typescript/hono/cases/chains/ctx-partial-route/src/app.ts new file mode 100644 index 000000000..20436a109 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-partial-route/src/app.ts @@ -0,0 +1,42 @@ +import { Hono } from 'hono' +import { Db, UserRepo, UserService } from './users' + +declare const process: { env: Record } + +type Env = { Variables: { services: { users: UserService } } } + +// a feature sub-app with its own Env and its own provider middleware +const users = new Hono() +users.use('*', async (c, next) => { + c.set('services', { users: new UserService(new UserRepo(new Db())) }) + await next() +}) +users.get('/:id', (c) => { + const { users } = c.get('services') + return c.json(users.find(c.req.param('id'))) +}) +users.delete('/:id', async (c) => { + const svc = c.var.services + return c.json({ ok: svc.users.remove(c.req.param('id')) }) +}) + +// the served root lives in a server holder that a run-time prefix rebases (the corpus's server-class shape) +class Server { + app = new Hono() + private prefix?: string + constructor(prefix?: string) { + this.prefix = prefix + } + build() { + this.app = new Hono() + if (this.prefix) { + this.app = this.app.basePath(this.prefix) + } + return this + } +} + +const server = new Server(process.env.API_BASE).build() +server.app.route('/users', users) + +export default server.app diff --git a/graph/test/typescript/hono/cases/chains/ctx-partial-route/src/users.ts b/graph/test/typescript/hono/cases/chains/ctx-partial-route/src/users.ts new file mode 100644 index 000000000..6835ba7f1 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-partial-route/src/users.ts @@ -0,0 +1,39 @@ +// census: ctx_di on a rebased root (SPEC R18 on a PARTIAL route: http_route_partial, dynamic_mount_prefix) +// The served app takes its base path from the environment, so every route under it is partial (its full path is +// not decidable). What R18 asks does not depend on the path: the handler still reads the service out of the +// context, typed by the sub-app's Env, and its calls must still resolve. At run time the variable is unset, so the +// chains are requested at the un-rebased path. +export class Db { + get(key: string): string[] { + return [key] + } + del(key: string): boolean { + return key.length > 0 + } +} + +export class UserRepo { + private db: Db + constructor(db: Db) { + this.db = db + } + byId(id: string) { + return this.db.get(`user:${id}`) + } + drop(id: string) { + return this.db.del(`user:${id}`) + } +} + +export class UserService { + private repo: UserRepo + constructor(repo: UserRepo) { + this.repo = repo + } + find(id: string) { + return this.repo.byId(id) + } + remove(id: string) { + return this.repo.drop(id) + } +} diff --git a/graph/test/typescript/hono/cases/chains/ctx-partial-untyped/src/app.ts b/graph/test/typescript/hono/cases/chains/ctx-partial-untyped/src/app.ts new file mode 100644 index 000000000..52176c566 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-partial-untyped/src/app.ts @@ -0,0 +1,28 @@ +import { Hono } from 'hono' +import { OrderRepo, OrderService } from './orders' + +declare const process: { env: Record } + +// the feature sub-app: no Env, its own provider middleware +const orders = new Hono() +orders.use('*', async (c: any, next) => { + c.set('orders', new OrderService(new OrderRepo())) + await next() +}) +orders.get('/list', (c: any) => { + const svc = c.get('orders') + return c.json({ rows: svc.list(), tenant: String(c.get('tenant') ?? '') }) +}) +orders.get('/total', (c: any) => c.json({ total: c.var.orders.total() })) + +// the served root mounts the feature at a prefix read from the environment (the default is only what an unset +// variable gives at run time); a scoped middleware on the root MAY apply, depending on that prefix +const app = new Hono() +app.use('/api/*', async (c: any, next) => { + c.set('tenant', 't1') + await next() +}) +const prefix = process.env.ORDERS_PREFIX || '/api/orders' +app.route(prefix, orders) + +export default app diff --git a/graph/test/typescript/hono/cases/chains/ctx-partial-untyped/src/orders.ts b/graph/test/typescript/hono/cases/chains/ctx-partial-untyped/src/orders.ts new file mode 100644 index 000000000..dbe30d540 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-partial-untyped/src/orders.ts @@ -0,0 +1,32 @@ +// census: ctx_di on a rebased root, untyped (SPEC R23 with R18's setter-type form, on PARTIAL routes) +// No Env type: the only thing that says what `c.get('orders')` holds is the sub-app middleware's +// `c.set('orders', new OrderService(..))`. That middleware is in the partial routes' DECIDED chain (mwp), so the +// key flows to the handlers exactly as on an exact route (R23) and the calls on the value resolve to OrderService. +// The root's scoped `use('/api/*', ..)` only MAY apply under the unknown mount prefix: its `c.set('tenant', ..)` gives no +// flow row, and the reader of 'tenant' is no_setter_on_chain. The compiler types the reads as any; the run-time +// trace observes the calls. +export class OrderRepo { + rows: string[] + constructor() { + this.rows = [] + } + all() { + return this.rows + } + count() { + return this.rows.length + } +} + +export class OrderService { + repo: OrderRepo + constructor(repo: OrderRepo) { + this.repo = repo + } + list() { + return this.repo.all() + } + total() { + return this.repo.count() + } +} diff --git a/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/app.ts b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/app.ts new file mode 100644 index 000000000..22af181f5 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/app.ts @@ -0,0 +1,33 @@ +import { Hono } from 'hono' +import type { IEnv } from './env' +import type { Env } from './env' +import { provide, auditTrail, factory } from './wire' +import { verifyKey, showUser } from './handlers' +import { Db } from './db' +import { UserRepo } from './repos' +import { UserService } from './services' + +const app = new Hono() +app.use('*', provide) +app.use('/keys/*', auditTrail) +app.get('/keys/:key', verifyKey) + +// inline Env generic, c.get(k).m() on the call result +app.get('/users/:id', (c) => c.json(c.get('services').users.find(c.req.param('id') ?? ''))) + +// a factory app with the same Env: handlers from createHandlers read c.var.services +const fapp = factory.createApp() +fapp.use('*', provide) +fapp.get('/whoami/:id', ...factory.createHandlers((c) => c.json(c.var.services.users.find(c.req.param('id') ?? '')))) +app.route('/f', fapp) + +// an interface Env on a sub-app +const iapp = new Hono() +iapp.use('*', async (c, next) => { + c.set('users', new UserService(new UserRepo(new Db()))) + await next() +}) +iapp.get('/u/:id', showUser) +app.route('/i', iapp) + +export default app diff --git a/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/db.ts b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/db.ts new file mode 100644 index 000000000..9fe53b751 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/db.ts @@ -0,0 +1,10 @@ +// census: ctx_di (SPEC R18; call-chain break class 1, "a service read out of the framework context") +// A request chain handler -> service -> repository -> db, where the handler reaches the service ONLY through the +// framework context: `c.get(k)`, `c.var.k`, a destructured `c.get(k)`, a `const x = c.var.k`, with the key's type +// declared by each Env form (inline generic, type alias, interface, createFactory, MiddlewareHandler, +// Context parameter). +export class Db { + query(sql: string): string[] { + return [sql] + } +} diff --git a/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/env.ts b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/env.ts new file mode 100644 index 000000000..0468b8f9a --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/env.ts @@ -0,0 +1,9 @@ +import type { UserService, KeyService, AuditService } from './services' + +// the Env as a type alias +export type Env = { Variables: { services: { users: UserService; keys: KeyService }; audit: AuditService } } + +// the same shape as an interface +export interface IEnv { + Variables: { users: UserService } +} diff --git a/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/handlers.ts b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/handlers.ts new file mode 100644 index 000000000..ee076d25e --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/handlers.ts @@ -0,0 +1,14 @@ +import type { Context } from 'hono' +import type { Env, IEnv } from './env' + +// Context parameter, destructured c.get +export async function verifyKey(c: Context) { + const { keys } = c.get('services') + return c.json({ ok: keys.verify(c.req.param('key') ?? '') }) +} + +// Context (an interface Env), `const x = c.var.k` +export async function showUser(c: Context) { + const users = c.var.users + return c.json(users.find(c.req.param('id') ?? '')) +} diff --git a/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/repos.ts b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/repos.ts new file mode 100644 index 000000000..bb0af0c87 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/repos.ts @@ -0,0 +1,21 @@ +import { Db } from './db' + +export class UserRepo { + private db: Db + constructor(db: Db) { + this.db = db + } + byId(id: string) { + return this.db.query(`user ${id}`) + } +} + +export class KeyRepo { + private db: Db + constructor(db: Db) { + this.db = db + } + lookup(key: string) { + return this.db.query(`key ${key}`) + } +} diff --git a/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/services.ts b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/services.ts new file mode 100644 index 000000000..1358d3a3e --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/services.ts @@ -0,0 +1,27 @@ +import { UserRepo, KeyRepo } from './repos' + +export class UserService { + private repo: UserRepo + constructor(repo: UserRepo) { + this.repo = repo + } + find(id: string) { + return this.repo.byId(id) + } +} + +export class KeyService { + private repo: KeyRepo + constructor(repo: KeyRepo) { + this.repo = repo + } + verify(key: string) { + return this.repo.lookup(key).length > 0 + } +} + +export class AuditService { + record(what: string) { + return what.length + } +} diff --git a/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/wire.ts b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/wire.ts new file mode 100644 index 000000000..bd7b6ed21 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-services-typed/src/wire.ts @@ -0,0 +1,22 @@ +import { createFactory } from 'hono/factory' +import type { MiddlewareHandler } from 'hono' +import type { Env } from './env' +import { Db } from './db' +import { UserRepo, KeyRepo } from './repos' +import { UserService, KeyService, AuditService } from './services' + +const db = new Db() +export const factory = createFactory() + +// MiddlewareHandler: the middleware that puts the services into the context +export const provide: MiddlewareHandler = async (c, next) => { + c.set('services', { users: new UserService(new UserRepo(db)), keys: new KeyService(new KeyRepo(db)) }) + c.set('audit', new AuditService()) + await next() +} + +// createFactory().createMiddleware: a middleware that reads a service back with c.var +export const auditTrail = factory.createMiddleware(async (c, next) => { + c.var.audit.record(c.req.path) + await next() +}) diff --git a/graph/test/typescript/hono/cases/chains/ctx-set-untyped/src/app.ts b/graph/test/typescript/hono/cases/chains/ctx-set-untyped/src/app.ts new file mode 100644 index 000000000..3a1804bf8 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-set-untyped/src/app.ts @@ -0,0 +1,24 @@ +import { Hono } from 'hono' +import { OrderRepo, OrderService } from './orders' + +// handlers whose context parameter is `any` (the commonest untyped shape) +const app = new Hono() +app.use('/orders/*', async (c: any, next) => { + c.set('orders', new OrderService(new OrderRepo())) + await next() +}) +app.get('/orders/list', (c: any) => { + const orders = c.get('orders') + return c.json(orders.list()) +}) + +// an app whose Env is `any`: c.var.k is any too +const reports = new Hono() +reports.use('*', async (c, next) => { + c.set('orders', new OrderService(new OrderRepo())) + await next() +}) +reports.get('/total', (c) => c.json({ total: c.var.orders.total() })) +app.route('/reports', reports) + +export default app diff --git a/graph/test/typescript/hono/cases/chains/ctx-set-untyped/src/orders.ts b/graph/test/typescript/hono/cases/chains/ctx-set-untyped/src/orders.ts new file mode 100644 index 000000000..e9f1c146f --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/ctx-set-untyped/src/orders.ts @@ -0,0 +1,30 @@ +// census: ctx_di (SPEC R18, the untyped form; call-chain break class 1) +// No Env type anywhere: the app is `new Hono()`. The only thing that says what `c.get('orders')` holds is the +// middleware's `c.set('orders', new OrderService(..))` on the chain that reaches the handler (ctx_key_flow), so +// calls on the value read back resolve to OrderService. The compiler types the read as any; the run-time trace +// observes the calls. +export class OrderRepo { + rows: string[] + constructor() { + this.rows = [] + } + all() { + return this.rows + } + count() { + return this.rows.length + } +} + +export class OrderService { + repo: OrderRepo + constructor(repo: OrderRepo) { + this.repo = repo + } + list() { + return this.repo.all() + } + total() { + return this.repo.count() + } +} diff --git a/graph/test/typescript/hono/cases/chains/function-values/src/app.ts b/graph/test/typescript/hono/cases/chains/function-values/src/app.ts new file mode 100644 index 000000000..7ef58978b --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/function-values/src/app.ts @@ -0,0 +1,28 @@ +import { Hono } from 'hono' +import { handlers, withRetry, tryit, run, readInput, pipeline, Mailer, purge } from './ops' + +const app = new Hono() +const ACTION = 'restore' + +// a callback passed to a project higher-order function +app.get('/retry/:id', (c) => c.text(withRetry(2, () => handlers.archive(c.req.param('id'))))) +// a handler table read with a literal bracket key and with a const key +app.get('/table/:id', (c) => c.text(handlers['archive'](c.req.param('id')) + handlers[ACTION](c.req.param('id')))) +// a returned function, then called +app.get('/tryit/:id', (c) => c.text(tryit(purge)(c.req.param('id')))) +// a function stored in an options object +app.get('/options', (c) => c.text(run({ getInput: readInput }))) +// an array element +app.get('/pipeline/:id', (c) => c.text(pipeline[1](c.req.param('id')))) +// a bound method +app.get('/mail/:to', (c) => { + const send = new Mailer('ops').send.bind(new Mailer('noreply')) + return c.text(send(c.req.param('to'))) +}) +// a key the program does not decide: a declared unknown +app.get('/act/:op/:id', (c) => { + const op = c.req.param('op') as keyof typeof handlers + return c.text(handlers[op](c.req.param('id'))) +}) + +export default app diff --git a/graph/test/typescript/hono/cases/chains/function-values/src/ops.ts b/graph/test/typescript/hono/cases/chains/function-values/src/ops.ts new file mode 100644 index 000000000..1a150d374 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/function-values/src/ops.ts @@ -0,0 +1,46 @@ +// census: fn_value (SPEC R21; call-chain break class 2, "callback / higher-order: function values") +// A function value passed, stored or returned reaches its target when the value is decidable: a callback +// parameter, a handler table read with a literal key (dot, bracket, and a const key), an options-object function, +// an array element, a returned function, and a bound function. A table read with a key the program does not decide +// is a declared unknown, never silently dropped. +export function archive(id: string) { + return `archived ${id}` +} +export function restore(id: string) { + return `restored ${id}` +} +export function purge(id: string) { + return `purged ${id}` +} + +export const handlers = { archive, restore, purge } + +export function withRetry(times: number, fn: () => T): T { + let last: T = fn() + for (let i = 1; i < times; i++) last = fn() + return last +} + +export function tryit(fn: (...a: A) => R) { + return (...a: A) => fn(...a) +} + +export function run(opts: { getInput: () => string }) { + return opts.getInput() +} + +export function readInput() { + return 'input' +} + +export const pipeline = [archive, restore] + +export class Mailer { + from: string + constructor(from: string) { + this.from = from + } + send(to: string) { + return `${this.from} -> ${to}` + } +} diff --git a/graph/test/typescript/hono/cases/chains/library-generic-params/src/app.ts b/graph/test/typescript/hono/cases/chains/library-generic-params/src/app.ts new file mode 100644 index 000000000..6b4981278 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/library-generic-params/src/app.ts @@ -0,0 +1,25 @@ +import { Hono } from 'hono' +import { validator } from 'hono/validator' +import { Ledger, Role } from './model' +import { describeRoles, topRank, latestName, staticProviders, byName, makePairs, makeIndex } from './report' + +const app = new Hono() +const ledger = new Ledger() + +app.get('/roles', (c) => c.json(describeRoles(ledger))) +app.get('/rank', (c) => c.json({ top: topRank(ledger) })) +app.get('/latest', async (c) => c.json(await latestName(ledger))) +app.get('/providers', (c) => c.json(staticProviders(makePairs()))) +app.get('/names', (c) => c.json(byName(makeIndex()))) + +// a validator hook: the value is typed by the library's generic +app.post( + '/roles', + validator('json', (value) => new Role(String(value))), + (c) => { + const role = c.req.valid('json') + return c.json(role.toJSON()) + }, +) + +export default app diff --git a/graph/test/typescript/hono/cases/chains/library-generic-params/src/model.ts b/graph/test/typescript/hono/cases/chains/library-generic-params/src/model.ts new file mode 100644 index 000000000..c5f38fd48 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/library-generic-params/src/model.ts @@ -0,0 +1,39 @@ +// census: callback_generic (SPEC R19; call-chain break class 3, "type through a library generic") +// A callback parameter or a destructured element whose type comes ONLY through a library or builtin generic: +// Array.map / forEach / filter / reduce callbacks, Promise.then, Map.forEach, a tuple taken from Array.shift() +// and Object.entries, and a Hono validator hook. Calls on them resolve to the project methods. +export class Role { + name: string + constructor(name: string) { + this.name = name + } + toJSON() { + return { role: this.name } + } + rank() { + return this.name.length + } +} + +export class Wrapper { + label: string + constructor(label: string) { + this.label = label + } + isStatic() { + return this.label.startsWith('static') + } +} + +export class Ledger { + entries: Role[] + constructor() { + this.entries = [new Role('admin'), new Role('viewer')] + } + roles() { + return this.entries + } + async latest(): Promise { + return this.entries[0] + } +} diff --git a/graph/test/typescript/hono/cases/chains/library-generic-params/src/report.ts b/graph/test/typescript/hono/cases/chains/library-generic-params/src/report.ts new file mode 100644 index 000000000..953060335 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/library-generic-params/src/report.ts @@ -0,0 +1,36 @@ +import { Role, Wrapper, Ledger } from './model' + +export function describeRoles(ledger: Ledger) { + return ledger.roles().map((r) => r.toJSON()) +} + +export function topRank(ledger: Ledger) { + return ledger.roles().reduce((acc, r) => Math.max(acc, r.rank()), 0) +} + +export async function latestName(ledger: Ledger) { + return ledger.latest().then((role) => role.toJSON()) +} + +export function staticProviders(pairs: Array<[string, Wrapper]>) { + const [, first] = pairs.shift()! + const out = [first.isStatic()] + for (const [, w] of Object.entries(Object.fromEntries(pairs))) out.push(w.isStatic()) + return out +} + +export function byName(index: Map) { + const names: number[] = [] + index.forEach((role) => { + names.push(role.rank()) + }) + return names +} + +export function makePairs(): Array<[string, Wrapper]> { + return [['a', new Wrapper('static-a')], ['b', new Wrapper('dyn-b')]] +} + +export function makeIndex() { + return new Map([['admin', new Role('admin')]]) +} diff --git a/graph/test/typescript/hono/cases/chains/post-validator-chain/src/app.ts b/graph/test/typescript/hono/cases/chains/post-validator-chain/src/app.ts new file mode 100644 index 000000000..fd87e0885 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/post-validator-chain/src/app.ts @@ -0,0 +1,40 @@ +import { Hono } from 'hono' +import type { MiddlewareHandler } from 'hono' +import { validator } from 'hono/validator' +import { zValidator } from '@hono/zod-validator' +import { z } from 'zod' +import { Db, OrderRepo, OrderService } from './orders' + +type Env = { Variables: { services: { orders: OrderService } } } + +const createOrder = z.object({ sku: z.string(), qty: z.number() }) + +const provide: MiddlewareHandler = async (c, next) => { + c.set('services', { orders: new OrderService(new OrderRepo(new Db())) }) + await next() +} + +const app = new Hono() +app.use('/orders/*', provide) + +// zValidator('json', schema) before the handler; destructured c.get('services') +app.post('/orders', zValidator('json', createOrder), async (c) => { + const input = c.req.valid('json') + const { orders } = c.get('services') + return c.json({ id: orders.create(input) }, 201) +}) + +// the built-in validator('json', fn) with a hand-written check; c.var.services read inline +app.post( + '/orders/:id/items', + validator('json', (value, c) => { + if (typeof value?.sku !== 'string') return c.text('sku required', 400) + return { sku: value.sku as string } + }), + (c) => { + const { sku } = c.req.valid('json') + return c.json({ n: c.var.services.orders.addItem(c.req.param('id'), sku) }) + } +) + +export default app diff --git a/graph/test/typescript/hono/cases/chains/post-validator-chain/src/orders.ts b/graph/test/typescript/hono/cases/chains/post-validator-chain/src/orders.ts new file mode 100644 index 000000000..621a64d6c --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/post-validator-chain/src/orders.ts @@ -0,0 +1,36 @@ +// census: ctx_di + zvalidator + req_valid (SPEC R18 with SPEC 3.9; call-chain break class 1 on a POST route) +// The commonest write route in the corpus: a json validator runs before the handler, the handler reads the +// validated body with c.req.valid('json') and hands it to a service it reads out of the context. The chain is +// provider middleware -> (library validator) -> handler -> service -> repository -> db. At run time the handler +// is only entered when the request carries a JSON body the schema accepts (the golden's `body` rows). +export class Db { + insert(table: string, row: object): number { + return Object.keys(row).length + table.length + } +} + +export class OrderRepo { + private db: Db + constructor(db: Db) { + this.db = db + } + save(sku: string, qty: number) { + return this.db.insert('orders', { sku, qty }) + } + saveItem(orderId: string, sku: string) { + return this.db.insert('order_items', { orderId, sku }) + } +} + +export class OrderService { + private repo: OrderRepo + constructor(repo: OrderRepo) { + this.repo = repo + } + create(input: { sku: string; qty: number }) { + return this.repo.save(input.sku, input.qty) + } + addItem(orderId: string, sku: string) { + return this.repo.saveItem(orderId, sku) + } +} diff --git a/graph/test/typescript/hono/cases/chains/same-name-unrelated/src/app.ts b/graph/test/typescript/hono/cases/chains/same-name-unrelated/src/app.ts new file mode 100644 index 000000000..2fbf1b4c9 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/same-name-unrelated/src/app.ts @@ -0,0 +1,32 @@ +import { Hono } from 'hono' +import { UserStore, AuditLog, saveAll } from './stores' + +type Env = { Variables: { users: UserStore } } + +const users = new UserStore() +const audit = new AuditLog() + +const app = new Hono() +app.use('/typed/*', async (c, next) => { + c.set('users', users) + await next() +}) +// a typed receiver: only UserStore.save +app.post('/typed/save', (c) => c.text(users.save('x'))) +// through the typed context: only UserStore.save +app.post('/typed/ctx', (c) => c.text(c.var.users.save('x'))) + +// untyped: the c.set value decides (UserStore under 'store'; the AuditLog under another key is not it) +const loose = new Hono() +loose.use('*', async (c: any, next) => { + c.set('store', new UserStore()) + c.set('log', new AuditLog()) + await next() +}) +loose.post('/save', (c: any) => c.text(c.get('store').save('y'))) +app.route('/loose', loose) + +// the positive control: an interface call reaches both implementations +app.post('/all', (c) => c.json(saveAll([users, audit], 'z'))) + +export default app diff --git a/graph/test/typescript/hono/cases/chains/same-name-unrelated/src/stores.ts b/graph/test/typescript/hono/cases/chains/same-name-unrelated/src/stores.ts new file mode 100644 index 000000000..b3a883fb5 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/same-name-unrelated/src/stores.ts @@ -0,0 +1,23 @@ +// census: ctx_di, negative (SPEC R18-R21 negative controls) +// Two unrelated classes each declare `save()`. A call on a receiver whose type (or, untyped, whose c.set value) is +// one of them reaches ONLY that class's method: an edge to the other same-named method is a false edge. The +// positive control: a call through an interface both implement reaches both. +export interface Saver { + save(v: string): string +} + +export class UserStore implements Saver { + save(v: string) { + return `user ${v}` + } +} + +export class AuditLog implements Saver { + save(v: string) { + return `audit ${v}` + } +} + +export function saveAll(savers: Saver[], v: string) { + return savers.map((s) => s.save(v)) +} diff --git a/graph/test/typescript/hono/cases/chains/this-in-nested-arrows/src/app.ts b/graph/test/typescript/hono/cases/chains/this-in-nested-arrows/src/app.ts new file mode 100644 index 000000000..117084280 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/this-in-nested-arrows/src/app.ts @@ -0,0 +1,12 @@ +import { Hono } from 'hono' +import { Catalog, Resp } from './resp' + +const app = new Hono() +const catalog = new Catalog() +app.get('/prices', (c) => c.json(catalog.prices())) +app.get('/taxes', (c) => c.json(catalog.taxes())) +app.get('/rounded', (c) => c.json(catalog.rounded())) +app.get('/later', async (c) => c.json(await catalog.later())) +app.get('/fail', (c) => c.json(Resp.fail([{ path: ['a', 'b'] }]))) + +export default app diff --git a/graph/test/typescript/hono/cases/chains/this-in-nested-arrows/src/resp.ts b/graph/test/typescript/hono/cases/chains/this-in-nested-arrows/src/resp.ts new file mode 100644 index 000000000..e0b8e5838 --- /dev/null +++ b/graph/test/typescript/hono/cases/chains/this-in-nested-arrows/src/resp.ts @@ -0,0 +1,47 @@ +// census: this_arrow (SPEC R20; call-chain break class 4, "this inside a nested arrow") +// `this` inside an arrow function nested in a method, at depth 1, 2 and 3, and in an arrow inside a STATIC method +// (where `this` is the class), is the method's `this`: the calls reach the class's own members. +export class Resp { + private static norm(p: string[]) { + return p.join('.') + } + static fail(issues: Array<{ path: string[] }>) { + return issues.map((i) => ({ path: this.norm(i.path) })) + } +} + +export class Catalog { + items: string[] + constructor() { + this.items = ['a', 'b'] + } + price(item: string) { + return item.length + } + tax(n: number) { + return n / 10 + } + round(n: number) { + return Math.round(n) + } + // depth 1 + prices() { + return this.items.map((i) => this.price(i)) + } + // depth 2 + taxes() { + return this.items.map((i) => [i].map((j) => this.tax(this.price(j)))) + } + // depth 3 + rounded() { + return this.items.map((i) => [i].map((j) => [j].map((k) => this.round(this.tax(this.price(k)))))) + } + // an arrow stored in a local, then called + async later() { + const run = async () => { + const inner = () => this.price('late') + return inner() + } + return run() + } +} diff --git a/graph/test/typescript/hono/expected/chains/ctx-partial-route.chains b/graph/test/typescript/hono/expected/chains/ctx-partial-route.chains new file mode 100644 index 000000000..68e2c3327 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-partial-route.chains @@ -0,0 +1,25 @@ +# hand-reviewed 2026-10-28 against the fixture, the compiler call-chain oracle and the runtime trace: R18 on partial routes: the handlers read the service out of the context typed by the sub-app Env (destructured c.get, const x = c.var.k then a member call); service -> repository -> db. The provider -> handler hop is the partial route decidable chain (mwp). At run time API_BASE is unset, so the chains are requested at the un-rebased path +# census: ctx_di on a rebased root (SPEC R18 on a PARTIAL route: http_route_partial, dynamic_mount_prefix) +fn app.ts:10:16 +fn app.ts:14:19 +fn app.ts:18:22 +fn app.ts:27:3 +fn app.ts:30:3 +fn users.ts:10:3 +fn users.ts:17:3 +fn users.ts:20:3 +fn users.ts:23:3 +fn users.ts:30:3 +fn users.ts:33:3 +fn users.ts:36:3 +fn users.ts:7:3 +edge app.ts:10:16 users.ts:17:3 +edge app.ts:10:16 users.ts:30:3 +edge app.ts:14:19 users.ts:33:3 +edge app.ts:18:22 users.ts:36:3 +edge users.ts:20:3 users.ts:7:3 +edge users.ts:23:3 users.ts:10:3 +edge users.ts:33:3 users.ts:20:3 +edge users.ts:36:3 users.ts:23:3 +chain user-find GET /users/x1 app.ts:10:16 app.ts:14:19 users.ts:33:3 users.ts:20:3 users.ts:7:3 +chain user-remove DELETE /users/x1 app.ts:10:16 app.ts:18:22 users.ts:36:3 users.ts:23:3 users.ts:10:3 diff --git a/graph/test/typescript/hono/expected/chains/ctx-partial-route.entries b/graph/test/typescript/hono/expected/chains/ctx-partial-route.entries new file mode 100644 index 000000000..f36caac83 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-partial-route.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: chains fixture (R18 on PARTIAL routes): the served root is rebased by a run-time prefix in a server holder, so both sub-app routes are partial dynamic_mount_prefix with their decidable chain (the provider at 0) +# census: ctx_di on a rebased root (SPEC R18 on a PARTIAL route: http_route_partial, dynamic_mount_prefix) +entry app.ts:10:16 http +entry app.ts:14:19 http +entry app.ts:18:22 http diff --git a/graph/test/typescript/hono/expected/chains/ctx-partial-route.tsv b/graph/test/typescript/hono/expected/chains/ctx-partial-route.tsv new file mode 100644 index 000000000..eaad55342 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-partial-route.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: chains fixture (R18 on PARTIAL routes): the served root is rebased by a run-time prefix in a server holder, so both sub-app routes are partial dynamic_mount_prefix with their decidable chain (the provider at 0) +# census: ctx_di on a rebased root (SPEC R18 on a PARTIAL route: http_route_partial, dynamic_mount_prefix) +# R23 (2026-10-29): re-derived; the provider is in both decided chains, so its services key flows to both handlers (2 ctx rows added); every other row unchanged +ctx app.ts:10:16 app.ts:14:19 services +ctx app.ts:10:16 app.ts:18:22 services +partial app.ts:14:19 GET dynamic_mount_prefix +partial app.ts:18:22 DELETE dynamic_mount_prefix +mwp app.ts:10:16 app.ts:14:19 GET 0 +mwp app.ts:10:16 app.ts:18:22 DELETE 0 +unknown app.ts:40:1 dynamic_mount_prefix app.ts:14:19 +unknown app.ts:40:1 dynamic_mount_prefix app.ts:18:22 diff --git a/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.chains b/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.chains new file mode 100644 index 000000000..82a87e138 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.chains @@ -0,0 +1,20 @@ +# hand-reviewed 2026-10-29 against the fixture, the compiler call-chain oracle and the runtime trace: R23 + R18 setter-type form on partial routes: with no Env, c.get(k) / c.var.k take the type of the decided-chain setter value (OrderService); the compiler types them any and the run-time trace observes handler -> service (rt); service -> repository is static. The root scoped middleware ran at run time (the unset variable gives /api/orders) but is no chain hop: statically it only may apply. At run time ORDERS_PREFIX is unset, so the chains are requested under the default +# census: ctx_di on a rebased root, untyped (SPEC R23 with R18 setter-type form, on PARTIAL routes) +fn app.ts:12:21 +fn app.ts:16:22 +fn app.ts:21:19 +fn app.ts:8:17 +fn orders.ts:10:3 +fn orders.ts:13:3 +fn orders.ts:16:3 +fn orders.ts:23:3 +fn orders.ts:26:3 +fn orders.ts:29:3 +edge app.ts:8:17 orders.ts:10:3 +edge app.ts:8:17 orders.ts:23:3 +edge orders.ts:26:3 orders.ts:13:3 +edge orders.ts:29:3 orders.ts:16:3 +edge app.ts:12:21 orders.ts:26:3 rt +edge app.ts:16:22 orders.ts:29:3 rt +chain orders-list GET /api/orders/list app.ts:8:17 app.ts:12:21 orders.ts:26:3 orders.ts:13:3 +chain orders-total GET /api/orders/total app.ts:8:17 app.ts:16:22 orders.ts:29:3 orders.ts:16:3 diff --git a/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.entries b/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.entries new file mode 100644 index 000000000..83f95fd0b --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: R23: the sub-app provider is in both partial routes decided chain (mwp 0) and its orders key flows to both handlers; the root scoped use only may apply (partial_route_scope), so its tenant key gives no flow and the /list reader is no_setter_on_chain +# census: ctx_di on a rebased root, untyped (SPEC R23 with R18 setter-type form, on PARTIAL routes) +entry app.ts:12:21 http +entry app.ts:16:22 http +entry app.ts:21:19 http +entry app.ts:8:17 http diff --git a/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.tsv b/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.tsv new file mode 100644 index 000000000..1198f4cd2 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-partial-untyped.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: R23: the sub-app provider is in both partial routes decided chain (mwp 0) and its orders key flows to both handlers; the root scoped use only may apply (partial_route_scope), so its tenant key gives no flow and the /list reader is no_setter_on_chain +# census: ctx_di on a rebased root, untyped (SPEC R23 with R18 setter-type form, on PARTIAL routes) +ctx app.ts:8:17 app.ts:12:21 orders +ctx app.ts:8:17 app.ts:16:22 orders +partial app.ts:12:21 GET dynamic_mount_prefix +partial app.ts:16:22 GET dynamic_mount_prefix +mwp app.ts:8:17 app.ts:12:21 GET 0 +mwp app.ts:8:17 app.ts:16:22 GET 0 +unknown app.ts:12:21 no_setter_on_chain app.ts:12:21 +unknown app.ts:26:1 dynamic_mount_prefix app.ts:12:21 +unknown app.ts:26:1 dynamic_mount_prefix app.ts:16:22 +unknown app.ts:26:1 partial_route_scope app.ts:21:19 diff --git a/graph/test/typescript/hono/expected/chains/ctx-services-typed.chains b/graph/test/typescript/hono/expected/chains/ctx-services-typed.chains new file mode 100644 index 000000000..a12ef825d --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-services-typed.chains @@ -0,0 +1,38 @@ +# hand-reviewed 2026-10-29 against the fixture, the compiler call-chain oracle and the runtime trace: R18: services read from the context (c.get destructured, c.var.k, const x = c.var.k, c.get(k).m()) through every Env form reach service -> repository -> db +# census: ctx_di (SPEC R18; call-chain break class 1) +fn app.ts:16:23 +fn app.ts:21:51 +fn app.ts:26:15 +fn db.ts:7:3 +fn handlers.ts:11:1 +fn handlers.ts:5:1 +fn repos.ts:15:3 +fn repos.ts:18:3 +fn repos.ts:5:3 +fn repos.ts:8:3 +fn services.ts:15:3 +fn services.ts:18:3 +fn services.ts:24:3 +fn services.ts:5:3 +fn services.ts:8:3 +fn wire.ts:12:48 +fn wire.ts:19:52 +edge app.ts:16:23 services.ts:8:3 +edge app.ts:21:51 services.ts:8:3 +edge app.ts:26:15 repos.ts:5:3 +edge app.ts:26:15 services.ts:5:3 +edge handlers.ts:11:1 services.ts:8:3 +edge handlers.ts:5:1 services.ts:18:3 +edge repos.ts:18:3 db.ts:7:3 +edge repos.ts:8:3 db.ts:7:3 +edge services.ts:18:3 repos.ts:18:3 +edge services.ts:8:3 repos.ts:8:3 +edge wire.ts:12:48 repos.ts:15:3 +edge wire.ts:12:48 repos.ts:5:3 +edge wire.ts:12:48 services.ts:15:3 +edge wire.ts:12:48 services.ts:5:3 +edge wire.ts:19:52 services.ts:24:3 +chain keys-verify GET /keys/abc wire.ts:12:48 wire.ts:19:52 handlers.ts:5:1 services.ts:18:3 repos.ts:18:3 db.ts:7:3 +chain users-inline-get GET /users/u1 wire.ts:12:48 app.ts:16:23 services.ts:8:3 repos.ts:8:3 db.ts:7:3 +chain factory-handlers-var GET /f/whoami/u1 wire.ts:12:48 app.ts:21:51 services.ts:8:3 repos.ts:8:3 db.ts:7:3 +chain interface-env-const-var GET /i/u/u1 wire.ts:12:48 app.ts:26:15 handlers.ts:11:1 services.ts:8:3 repos.ts:8:3 db.ts:7:3 diff --git a/graph/test/typescript/hono/expected/chains/ctx-services-typed.entries b/graph/test/typescript/hono/expected/chains/ctx-services-typed.entries new file mode 100644 index 000000000..fe25753a8 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-services-typed.entries @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: R18 chains fixture: routes, middleware chains, ctx flows +# census: ctx_di (SPEC R18) +entry app.ts:16:23 http +entry app.ts:21:51 http +entry app.ts:26:15 http +entry handlers.ts:11:1 http +entry handlers.ts:5:1 http +entry wire.ts:12:48 http +entry wire.ts:19:52 http diff --git a/graph/test/typescript/hono/expected/chains/ctx-services-typed.tsv b/graph/test/typescript/hono/expected/chains/ctx-services-typed.tsv new file mode 100644 index 000000000..ee92c4051 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-services-typed.tsv @@ -0,0 +1,24 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: R18 chains fixture: routes, middleware chains, ctx flows +# census: ctx_di (SPEC R18) +route GET /keys/:key handlers.ts:5:1 app.ts:10:13 2 +route GET /users/:id app.ts:16:23 app.ts:10:13 3 +route GET /f/whoami/:id app.ts:21:51 app.ts:10:13 5 +route GET /i/u/:id handlers.ts:11:1 app.ts:10:13 7 +mw wire.ts:12:48 handlers.ts:5:1 GET /keys/:key 0 app.ts:10:13 +mw wire.ts:19:52 handlers.ts:5:1 GET /keys/:key 1 app.ts:10:13 +mw wire.ts:12:48 app.ts:16:23 GET /users/:id 0 app.ts:10:13 +mw wire.ts:12:48 app.ts:21:51 GET /f/whoami/:id 0 app.ts:10:13 +mw wire.ts:12:48 app.ts:21:51 GET /f/whoami/:id 1 app.ts:10:13 +mw wire.ts:12:48 handlers.ts:11:1 GET /i/u/:id 0 app.ts:10:13 +mw app.ts:26:15 handlers.ts:11:1 GET /i/u/:id 1 app.ts:10:13 +ctx app.ts:26:15 handlers.ts:11:1 users +ctx wire.ts:12:48 app.ts:16:23 services +ctx wire.ts:12:48 app.ts:21:51 services +ctx wire.ts:12:48 handlers.ts:5:1 services +ctx wire.ts:12:48 wire.ts:19:52 audit +submount /f app.ts:22:5 +submount /i app.ts:31:5 +regby app.ts:1:1 app.ts:16:23 GET /users/:id app.ts:10:13 +regby app.ts:1:1 app.ts:21:51 GET /f/whoami/:id app.ts:10:13 +regby app.ts:1:1 handlers.ts:11:1 GET /i/u/:id app.ts:10:13 +regby app.ts:1:1 handlers.ts:5:1 GET /keys/:key app.ts:10:13 diff --git a/graph/test/typescript/hono/expected/chains/ctx-set-untyped.chains b/graph/test/typescript/hono/expected/chains/ctx-set-untyped.chains new file mode 100644 index 000000000..93e87e877 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-set-untyped.chains @@ -0,0 +1,22 @@ +# hand-reviewed 2026-10-29 against the fixture, the compiler call-chain oracle and the runtime trace: R18 untyped form: with no Env type (c: any, Hono), c.get(k) / c.var.k take the c.set value's type; the calls are observed at run time (rt), the compiler types them any +# census: ctx_di (SPEC R18 untyped; call-chain break class 1) +fn app.ts:10:25 +fn app.ts:17:18 +fn app.ts:21:23 +fn app.ts:6:22 +fn orders.ts:11:3 +fn orders.ts:14:3 +fn orders.ts:21:3 +fn orders.ts:24:3 +fn orders.ts:27:3 +fn orders.ts:8:3 +edge app.ts:17:18 orders.ts:21:3 +edge app.ts:17:18 orders.ts:8:3 +edge app.ts:6:22 orders.ts:21:3 +edge app.ts:6:22 orders.ts:8:3 +edge orders.ts:24:3 orders.ts:11:3 +edge orders.ts:27:3 orders.ts:14:3 +edge app.ts:10:25 orders.ts:24:3 rt +edge app.ts:21:23 orders.ts:27:3 rt +chain orders-list GET /orders/list app.ts:6:22 app.ts:10:25 orders.ts:24:3 orders.ts:11:3 +chain reports-total GET /reports/total app.ts:17:18 app.ts:21:23 orders.ts:27:3 orders.ts:14:3 diff --git a/graph/test/typescript/hono/expected/chains/ctx-set-untyped.entries b/graph/test/typescript/hono/expected/chains/ctx-set-untyped.entries new file mode 100644 index 000000000..02eb7f7d7 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-set-untyped.entries @@ -0,0 +1,6 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (ctx_di (SPEC R18 untyped; call-chain break class 1)): routes, middleware chains, ctx flows +# census: ctx_di (SPEC R18 untyped; call-chain break class 1) +entry app.ts:10:25 http +entry app.ts:17:18 http +entry app.ts:21:23 http +entry app.ts:6:22 http diff --git a/graph/test/typescript/hono/expected/chains/ctx-set-untyped.tsv b/graph/test/typescript/hono/expected/chains/ctx-set-untyped.tsv new file mode 100644 index 000000000..73fcc9308 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/ctx-set-untyped.tsv @@ -0,0 +1,11 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (ctx_di (SPEC R18 untyped; call-chain break class 1)): routes, middleware chains, ctx flows +# census: ctx_di (SPEC R18 untyped; call-chain break class 1) +route GET /orders/list app.ts:10:25 app.ts:5:13 1 +route GET /reports/total app.ts:21:23 app.ts:5:13 3 +mw app.ts:6:22 app.ts:10:25 GET /orders/list 0 app.ts:5:13 +mw app.ts:17:18 app.ts:21:23 GET /reports/total 0 app.ts:5:13 +ctx app.ts:17:18 app.ts:21:23 orders +ctx app.ts:6:22 app.ts:10:25 orders +submount /reports app.ts:22:5 +regby app.ts:1:1 app.ts:10:25 GET /orders/list app.ts:5:13 +regby app.ts:1:1 app.ts:21:23 GET /reports/total app.ts:5:13 diff --git a/graph/test/typescript/hono/expected/chains/function-values.chains b/graph/test/typescript/hono/expected/chains/function-values.chains new file mode 100644 index 000000000..bc80bbbce --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/function-values.chains @@ -0,0 +1,42 @@ +# hand-reviewed 2026-10-29 against the fixture, the compiler call-chain oracle and the runtime trace: R21: callbacks, handler tables (literal and const keys), returned functions, options-object functions, array elements and bound methods reach their targets (rt where the compiler sees only a signature); a table read with an undecided key is a declared unknown +# census: fn_value (SPEC R21; call-chain break class 2) +fn app.ts:10:23 +fn app.ts:12:23 +fn app.ts:14:21 +fn app.ts:16:26 +fn app.ts:18:22 +fn app.ts:23:25 +fn app.ts:8:23 +fn app.ts:8:50 +fn ops.ts:12:1 +fn ops.ts:18:1 +fn ops.ts:24:1 +fn ops.ts:25:10 +fn ops.ts:28:1 +fn ops.ts:32:1 +fn ops.ts:40:3 +fn ops.ts:43:3 +fn ops.ts:6:1 +fn ops.ts:9:1 +edge app.ts:10:23 ops.ts:6:1 +edge app.ts:10:23 ops.ts:9:1 +edge app.ts:12:23 ops.ts:12:1 +edge app.ts:12:23 ops.ts:24:1 +edge app.ts:12:23 ops.ts:25:10 +edge app.ts:14:21 ops.ts:28:1 +edge app.ts:18:22 ops.ts:40:3 +edge app.ts:18:22 ops.ts:43:3 +edge app.ts:8:23 app.ts:8:50 +edge app.ts:8:23 ops.ts:18:1 +edge app.ts:8:50 ops.ts:6:1 +edge app.ts:16:26 ops.ts:9:1 rt +edge ops.ts:18:1 app.ts:8:50 rt +edge ops.ts:25:10 ops.ts:12:1 rt +edge ops.ts:28:1 ops.ts:32:1 rt +chain retry-callback GET /retry/x1 app.ts:8:23 ops.ts:18:1 app.ts:8:50 ops.ts:6:1 +chain table-const-key GET /table/x1 app.ts:10:23 ops.ts:9:1 +chain tryit-returned GET /tryit/x1 app.ts:12:23 ops.ts:25:10 ops.ts:12:1 +chain options-fn GET /options app.ts:14:21 ops.ts:28:1 ops.ts:32:1 +chain array-element GET /pipeline/x1 app.ts:16:26 ops.ts:9:1 +chain bound-method GET /mail/x1 app.ts:18:22 ops.ts:43:3 +ccunknown app.ts:23:25 diff --git a/graph/test/typescript/hono/expected/chains/function-values.entries b/graph/test/typescript/hono/expected/chains/function-values.entries new file mode 100644 index 000000000..b3ed9145c --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/function-values.entries @@ -0,0 +1,9 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (fn_value (SPEC R21; call-chain break class 2)): routes, middleware chains, ctx flows +# census: fn_value (SPEC R21; call-chain break class 2) +entry app.ts:10:23 http +entry app.ts:12:23 http +entry app.ts:14:21 http +entry app.ts:16:26 http +entry app.ts:18:22 http +entry app.ts:23:25 http +entry app.ts:8:23 http diff --git a/graph/test/typescript/hono/expected/chains/function-values.tsv b/graph/test/typescript/hono/expected/chains/function-values.tsv new file mode 100644 index 000000000..5d28c8011 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/function-values.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (fn_value (SPEC R21; call-chain break class 2)): routes, middleware chains, ctx flows +# census: fn_value (SPEC R21; call-chain break class 2) +route GET /retry/:id app.ts:8:23 app.ts:4:13 0 +route GET /table/:id app.ts:10:23 app.ts:4:13 1 +route GET /tryit/:id app.ts:12:23 app.ts:4:13 2 +route GET /options app.ts:14:21 app.ts:4:13 3 +route GET /pipeline/:id app.ts:16:26 app.ts:4:13 4 +route GET /mail/:to app.ts:18:22 app.ts:4:13 5 +route GET /act/:op/:id app.ts:23:25 app.ts:4:13 6 +regby app.ts:1:1 app.ts:10:23 GET /table/:id app.ts:4:13 +regby app.ts:1:1 app.ts:12:23 GET /tryit/:id app.ts:4:13 +regby app.ts:1:1 app.ts:14:21 GET /options app.ts:4:13 +regby app.ts:1:1 app.ts:16:26 GET /pipeline/:id app.ts:4:13 +regby app.ts:1:1 app.ts:18:22 GET /mail/:to app.ts:4:13 +regby app.ts:1:1 app.ts:23:25 GET /act/:op/:id app.ts:4:13 +regby app.ts:1:1 app.ts:8:23 GET /retry/:id app.ts:4:13 diff --git a/graph/test/typescript/hono/expected/chains/library-generic-params.chains b/graph/test/typescript/hono/expected/chains/library-generic-params.chains new file mode 100644 index 000000000..6538e0e68 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/library-generic-params.chains @@ -0,0 +1,58 @@ +# hand-reviewed 2026-10-29 against the fixture, the compiler call-chain oracle and the runtime trace: R19: callback parameters typed only by Array.map/reduce, Promise.then, Map.forEach, tuple elements from shift()/Object.entries, and a validator's output reach the project methods +# census: callback_generic (SPEC R19; call-chain break class 3) +fn app.ts:10:18 +fn app.ts:11:20 +fn app.ts:12:23 +fn app.ts:13:19 +fn app.ts:18:21 +fn app.ts:19:3 +fn app.ts:9:19 +fn model.ts:10:3 +fn model.ts:13:3 +fn model.ts:20:3 +fn model.ts:23:3 +fn model.ts:30:3 +fn model.ts:33:3 +fn model.ts:36:3 +fn model.ts:7:3 +fn report.ts:11:1 +fn report.ts:12:31 +fn report.ts:15:1 +fn report.ts:22:1 +fn report.ts:24:17 +fn report.ts:30:1 +fn report.ts:34:1 +fn report.ts:3:1 +fn report.ts:4:29 +fn report.ts:7:1 +fn report.ts:8:32 +edge app.ts:10:18 report.ts:7:1 +edge app.ts:11:20 report.ts:11:1 +edge app.ts:12:23 report.ts:15:1 +edge app.ts:12:23 report.ts:30:1 +edge app.ts:13:19 report.ts:22:1 +edge app.ts:13:19 report.ts:34:1 +edge app.ts:18:21 model.ts:7:3 +edge app.ts:19:3 model.ts:10:3 +edge app.ts:9:19 report.ts:3:1 +edge model.ts:30:3 model.ts:7:3 +edge report.ts:11:1 model.ts:36:3 +edge report.ts:11:1 report.ts:12:31 +edge report.ts:12:31 model.ts:10:3 +edge report.ts:15:1 model.ts:23:3 +edge report.ts:22:1 report.ts:24:17 +edge report.ts:24:17 model.ts:13:3 +edge report.ts:30:1 model.ts:20:3 +edge report.ts:34:1 model.ts:7:3 +edge report.ts:3:1 model.ts:33:3 +edge report.ts:3:1 report.ts:4:29 +edge report.ts:4:29 model.ts:10:3 +edge report.ts:7:1 model.ts:33:3 +edge report.ts:7:1 report.ts:8:32 +edge report.ts:8:32 model.ts:13:3 +chain roles-map GET /roles app.ts:9:19 report.ts:3:1 report.ts:4:29 model.ts:10:3 +chain rank-reduce GET /rank app.ts:10:18 report.ts:7:1 report.ts:8:32 model.ts:13:3 +chain latest-then GET /latest app.ts:11:20 report.ts:11:1 report.ts:12:31 model.ts:10:3 +chain providers-tuple GET /providers app.ts:12:23 report.ts:15:1 model.ts:23:3 +chain names-map-foreach GET /names app.ts:13:19 report.ts:22:1 report.ts:24:17 model.ts:13:3 +chain validator-output - - app.ts:19:3 model.ts:10:3 diff --git a/graph/test/typescript/hono/expected/chains/library-generic-params.entries b/graph/test/typescript/hono/expected/chains/library-generic-params.entries new file mode 100644 index 000000000..eed3f910e --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/library-generic-params.entries @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (callback_generic (SPEC R19; call-chain break class 3)): routes, middleware chains, ctx flows +# census: callback_generic (SPEC R19; call-chain break class 3) +entry app.ts:10:18 http +entry app.ts:11:20 http +entry app.ts:12:23 http +entry app.ts:13:19 http +entry app.ts:19:3 http +entry app.ts:9:19 http diff --git a/graph/test/typescript/hono/expected/chains/library-generic-params.tsv b/graph/test/typescript/hono/expected/chains/library-generic-params.tsv new file mode 100644 index 000000000..3058b6d78 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/library-generic-params.tsv @@ -0,0 +1,15 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (callback_generic (SPEC R19; call-chain break class 3)): routes, middleware chains, ctx flows +# census: callback_generic (SPEC R19; call-chain break class 3) +route GET /roles app.ts:9:19 app.ts:6:13 0 +route GET /rank app.ts:10:18 app.ts:6:13 1 +route GET /latest app.ts:11:20 app.ts:6:13 2 +route GET /providers app.ts:12:23 app.ts:6:13 3 +route GET /names app.ts:13:19 app.ts:6:13 4 +route POST /roles app.ts:19:3 app.ts:6:13 6 +mw lib:hono/validator.validator@app.ts:18:3 app.ts:19:3 POST /roles 0 app.ts:6:13 +regby app.ts:1:1 app.ts:10:18 GET /rank app.ts:6:13 +regby app.ts:1:1 app.ts:11:20 GET /latest app.ts:6:13 +regby app.ts:1:1 app.ts:12:23 GET /providers app.ts:6:13 +regby app.ts:1:1 app.ts:13:19 GET /names app.ts:6:13 +regby app.ts:1:1 app.ts:19:3 POST /roles app.ts:6:13 +regby app.ts:1:1 app.ts:9:19 GET /roles app.ts:6:13 diff --git a/graph/test/typescript/hono/expected/chains/post-validator-chain.chains b/graph/test/typescript/hono/expected/chains/post-validator-chain.chains new file mode 100644 index 000000000..cc6b1094f --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/post-validator-chain.chains @@ -0,0 +1,25 @@ +# hand-reviewed 2026-10-28 against the fixture, the compiler call-chain oracle and the runtime trace: R18 on POST routes: the provider middleware, then the library json validator (zValidator; the built-in validator with a hand-written check), then the handler, which reads the validated body and calls the service it reads out of the context; service -> repository -> db. The body rows are the JSON each request carries: without them the validator answers 400 and no handler runs (checked). The middleware -> handler hop runs through the validator, which awaits the body before it calls next, so it is observed as both functions running in a one-request run, the handler entered from library code +# census: ctx_di + zvalidator + req_valid (SPEC R18 with SPEC 3.9; call-chain break class 1 on a POST route) +fn app.ts:12:41 +fn app.ts:21:54 +fn app.ts:30:21 +fn app.ts:34:3 +fn orders.ts:14:3 +fn orders.ts:17:3 +fn orders.ts:20:3 +fn orders.ts:27:3 +fn orders.ts:30:3 +fn orders.ts:33:3 +fn orders.ts:7:3 +edge app.ts:12:41 orders.ts:14:3 +edge app.ts:12:41 orders.ts:27:3 +edge app.ts:21:54 orders.ts:30:3 +edge app.ts:34:3 orders.ts:33:3 +edge orders.ts:17:3 orders.ts:7:3 +edge orders.ts:20:3 orders.ts:7:3 +edge orders.ts:30:3 orders.ts:17:3 +edge orders.ts:33:3 orders.ts:20:3 +body POST /orders {"sku":"a1","qty":2} +body POST /orders/x1/items {"sku":"a1"} +chain create-order POST /orders app.ts:12:41 app.ts:21:54 orders.ts:30:3 orders.ts:17:3 orders.ts:7:3 +chain add-item POST /orders/x1/items app.ts:12:41 app.ts:34:3 orders.ts:33:3 orders.ts:20:3 orders.ts:7:3 diff --git a/graph/test/typescript/hono/expected/chains/post-validator-chain.entries b/graph/test/typescript/hono/expected/chains/post-validator-chain.entries new file mode 100644 index 000000000..3756299be --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/post-validator-chain.entries @@ -0,0 +1,5 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: chains fixture (POST routes behind a json validator, zValidator and the built-in validator): routes, middleware chains with the library validator at position 1, ctx flows, schema and the validated read +# census: ctx_di + zvalidator + req_valid (SPEC R18 with SPEC 3.9; call-chain break class 1 on a POST route) +entry app.ts:12:41 http +entry app.ts:21:54 http +entry app.ts:34:3 http diff --git a/graph/test/typescript/hono/expected/chains/post-validator-chain.tsv b/graph/test/typescript/hono/expected/chains/post-validator-chain.tsv new file mode 100644 index 000000000..9703dff0d --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/post-validator-chain.tsv @@ -0,0 +1,14 @@ +# hand-reviewed 2026-10-28 against the fixture and the oracle: chains fixture (POST routes behind a json validator, zValidator and the built-in validator): routes, middleware chains with the library validator at position 1, ctx flows, schema and the validated read +# census: ctx_di + zvalidator + req_valid (SPEC R18 with SPEC 3.9; call-chain break class 1 on a POST route) +route POST /orders app.ts:21:54 app.ts:17:13 2 +route POST /orders/:id/items app.ts:34:3 app.ts:17:13 4 +mw app.ts:12:41 app.ts:21:54 POST /orders 0 app.ts:17:13 +mw lib:@hono/zod-validator.zValidator@app.ts:21:21 app.ts:21:54 POST /orders 1 app.ts:17:13 +mw app.ts:12:41 app.ts:34:3 POST /orders/:id/items 0 app.ts:17:13 +mw lib:hono/validator.validator@app.ts:30:3 app.ts:34:3 POST /orders/:id/items 1 app.ts:17:13 +ctx app.ts:12:41 app.ts:21:54 services +ctx app.ts:12:41 app.ts:34:3 services +schema app.ts:21:54 json app.ts:10:7 +validread app.ts:22:17 app.ts:21:54 json app.ts:10:7 +regby app.ts:1:1 app.ts:21:54 POST /orders app.ts:17:13 +regby app.ts:1:1 app.ts:34:3 POST /orders/:id/items app.ts:17:13 diff --git a/graph/test/typescript/hono/expected/chains/same-name-unrelated.chains b/graph/test/typescript/hono/expected/chains/same-name-unrelated.chains new file mode 100644 index 000000000..ecedebdc5 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/same-name-unrelated.chains @@ -0,0 +1,23 @@ +# hand-reviewed 2026-10-29 against the fixture, the compiler call-chain oracle and the runtime trace: negative controls: a typed receiver, a typed context read and an untyped c.set value reach UserStore.save only, never the unrelated AuditLog.save (an edge to it is EXTRA); an interface call reaches both +# census: ctx_di, negative (SPEC R18-R21 negative controls) +fn app.ts:10:21 +fn app.ts:15:25 +fn app.ts:17:24 +fn app.ts:21:16 +fn app.ts:26:21 +fn app.ts:30:18 +fn stores.ts:10:3 +fn stores.ts:16:3 +fn stores.ts:21:1 +fn stores.ts:22:21 +edge app.ts:15:25 stores.ts:10:3 +edge app.ts:17:24 stores.ts:10:3 +edge app.ts:30:18 stores.ts:21:1 +edge stores.ts:21:1 stores.ts:22:21 +edge stores.ts:22:21 stores.ts:10:3 +edge stores.ts:22:21 stores.ts:16:3 +edge app.ts:26:21 stores.ts:10:3 rt +chain typed-receiver POST /typed/save app.ts:10:21 app.ts:15:25 stores.ts:10:3 +chain typed-ctx POST /typed/ctx app.ts:10:21 app.ts:17:24 stores.ts:10:3 +chain untyped-set POST /loose/save app.ts:21:16 app.ts:26:21 stores.ts:10:3 +chain interface-both POST /all app.ts:30:18 stores.ts:21:1 stores.ts:22:21 stores.ts:16:3 diff --git a/graph/test/typescript/hono/expected/chains/same-name-unrelated.entries b/graph/test/typescript/hono/expected/chains/same-name-unrelated.entries new file mode 100644 index 000000000..525bfa509 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/same-name-unrelated.entries @@ -0,0 +1,8 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (ctx_di, negative (SPEC R18-R21 negative controls)): routes, middleware chains, ctx flows +# census: ctx_di, negative (SPEC R18-R21 negative controls) +entry app.ts:10:21 http +entry app.ts:15:25 http +entry app.ts:17:24 http +entry app.ts:21:16 http +entry app.ts:26:21 http +entry app.ts:30:18 http diff --git a/graph/test/typescript/hono/expected/chains/same-name-unrelated.tsv b/graph/test/typescript/hono/expected/chains/same-name-unrelated.tsv new file mode 100644 index 000000000..f76a74091 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/same-name-unrelated.tsv @@ -0,0 +1,16 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (ctx_di, negative (SPEC R18-R21 negative controls)): routes, middleware chains, ctx flows +# census: ctx_di, negative (SPEC R18-R21 negative controls) +route POST /typed/save app.ts:15:25 app.ts:9:13 1 +route POST /typed/ctx app.ts:17:24 app.ts:9:13 2 +route POST /loose/save app.ts:26:21 app.ts:9:13 4 +route POST /all app.ts:30:18 app.ts:9:13 5 +mw app.ts:10:21 app.ts:15:25 POST /typed/save 0 app.ts:9:13 +mw app.ts:10:21 app.ts:17:24 POST /typed/ctx 0 app.ts:9:13 +mw app.ts:21:16 app.ts:26:21 POST /loose/save 0 app.ts:9:13 +ctx app.ts:10:21 app.ts:17:24 users +ctx app.ts:21:16 app.ts:26:21 store +submount /loose app.ts:27:5 +regby app.ts:1:1 app.ts:15:25 POST /typed/save app.ts:9:13 +regby app.ts:1:1 app.ts:17:24 POST /typed/ctx app.ts:9:13 +regby app.ts:1:1 app.ts:26:21 POST /loose/save app.ts:9:13 +regby app.ts:1:1 app.ts:30:18 POST /all app.ts:9:13 diff --git a/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.chains b/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.chains new file mode 100644 index 000000000..8046ea89a --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.chains @@ -0,0 +1,53 @@ +# hand-reviewed 2026-10-29 against the fixture, the compiler call-chain oracle and the runtime trace: R20: this in arrows nested 1, 2 and 3 deep in a method, in a stored arrow, and in an arrow inside a static method reaches the class's own members +# census: this_arrow (SPEC R20; call-chain break class 4) +fn app.ts:10:18 +fn app.ts:6:20 +fn app.ts:7:19 +fn app.ts:8:21 +fn app.ts:9:19 +fn resp.ts:15:3 +fn resp.ts:18:3 +fn resp.ts:21:3 +fn resp.ts:24:3 +fn resp.ts:28:3 +fn resp.ts:29:27 +fn resp.ts:32:3 +fn resp.ts:33:27 +fn resp.ts:33:42 +fn resp.ts:36:3 +fn resp.ts:37:27 +fn resp.ts:37:42 +fn resp.ts:37:57 +fn resp.ts:40:3 +fn resp.ts:41:17 +fn resp.ts:42:21 +fn resp.ts:5:3 +fn resp.ts:8:3 +fn resp.ts:9:23 +edge app.ts:10:18 resp.ts:8:3 +edge app.ts:6:20 resp.ts:28:3 +edge app.ts:7:19 resp.ts:32:3 +edge app.ts:8:21 resp.ts:36:3 +edge app.ts:9:19 resp.ts:40:3 +edge resp.ts:28:3 resp.ts:29:27 +edge resp.ts:29:27 resp.ts:18:3 +edge resp.ts:32:3 resp.ts:33:27 +edge resp.ts:33:27 resp.ts:33:42 +edge resp.ts:33:42 resp.ts:18:3 +edge resp.ts:33:42 resp.ts:21:3 +edge resp.ts:36:3 resp.ts:37:27 +edge resp.ts:37:27 resp.ts:37:42 +edge resp.ts:37:42 resp.ts:37:57 +edge resp.ts:37:57 resp.ts:18:3 +edge resp.ts:37:57 resp.ts:21:3 +edge resp.ts:37:57 resp.ts:24:3 +edge resp.ts:40:3 resp.ts:41:17 +edge resp.ts:41:17 resp.ts:42:21 +edge resp.ts:42:21 resp.ts:18:3 +edge resp.ts:8:3 resp.ts:9:23 +edge resp.ts:9:23 resp.ts:5:3 +chain prices-depth1 GET /prices app.ts:6:20 resp.ts:28:3 resp.ts:29:27 resp.ts:18:3 +chain taxes-depth2 GET /taxes app.ts:7:19 resp.ts:32:3 resp.ts:33:27 resp.ts:33:42 resp.ts:21:3 +chain rounded-depth3 GET /rounded app.ts:8:21 resp.ts:36:3 resp.ts:37:27 resp.ts:37:42 resp.ts:37:57 resp.ts:24:3 +chain later-stored-arrow GET /later app.ts:9:19 resp.ts:40:3 resp.ts:41:17 resp.ts:42:21 resp.ts:18:3 +chain fail-static-this GET /fail app.ts:10:18 resp.ts:8:3 resp.ts:9:23 resp.ts:5:3 diff --git a/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.entries b/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.entries new file mode 100644 index 000000000..360d58824 --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.entries @@ -0,0 +1,7 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (this_arrow (SPEC R20; call-chain break class 4)): routes, middleware chains, ctx flows +# census: this_arrow (SPEC R20; call-chain break class 4) +entry app.ts:10:18 http +entry app.ts:6:20 http +entry app.ts:7:19 http +entry app.ts:8:21 http +entry app.ts:9:19 http diff --git a/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.tsv b/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.tsv new file mode 100644 index 000000000..22de5206b --- /dev/null +++ b/graph/test/typescript/hono/expected/chains/this-in-nested-arrows.tsv @@ -0,0 +1,12 @@ +# hand-reviewed 2026-10-29 against the fixture and the oracle: chains fixture (this_arrow (SPEC R20; call-chain break class 4)): routes, middleware chains, ctx flows +# census: this_arrow (SPEC R20; call-chain break class 4) +route GET /prices app.ts:6:20 app.ts:4:13 0 +route GET /taxes app.ts:7:19 app.ts:4:13 1 +route GET /rounded app.ts:8:21 app.ts:4:13 2 +route GET /later app.ts:9:19 app.ts:4:13 3 +route GET /fail app.ts:10:18 app.ts:4:13 4 +regby app.ts:1:1 app.ts:10:18 GET /fail app.ts:4:13 +regby app.ts:1:1 app.ts:6:20 GET /prices app.ts:4:13 +regby app.ts:1:1 app.ts:7:19 GET /taxes app.ts:4:13 +regby app.ts:1:1 app.ts:8:21 GET /rounded app.ts:4:13 +regby app.ts:1:1 app.ts:9:19 GET /later app.ts:4:13 diff --git a/graph/test/typescript/hono/tools/chain_check.py b/graph/test/typescript/hono/tools/chain_check.py new file mode 100644 index 000000000..b0022a0fb --- /dev/null +++ b/graph/test/typescript/hono/tools/chain_check.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""chains/ suite (SPEC R18-R21): check a case's `.chains` golden against BOTH halves of the truth, or draft one. + + --cc the compiler call-chain oracle's output (truth/cc-oracle.mjs: fn / edge / unres rows) + --hono the Hono oracle's output (truth/hono-oracle.mjs: its `mw` rows, and a partial route's `mwp` rows, + give the middleware hops) + --rt the runtime trace's observed edges (truth/chain-runtime.mjs --edges: `rtedge FROM TO`) + --golden the reviewed `.chains` file --draft OUT write the oracle rows (no chain rows) to OUT + +Truth, in the golden's vocabulary: + fn every function with a body (the compiler oracle's fn rows, modules excluded) + edge every compiler edge between two such functions, plus `rt` edges: observed at run time, not seen by the + compiler (a function value the compiler types only as a signature), and not a middleware hop + chain each hop is a truth edge or a middleware hop (the Hono oracle's chain order: middleware -> next middleware + of the same handler's chain -> the handler) + ccunknown the function holds a call the compiler cannot resolve (`unres` row) and that no run-time edge covers +Exit 1 with the differences listed; prints `chains ok ` otherwise. +""" +import argparse +import sys + + +def tsv(p): + with open(p) as fh: + return [l.rstrip('\n').split('\t') for l in fh if l.strip() and not l.startswith('#')] + + +def main(): + a = argparse.ArgumentParser() + a.add_argument('--cc', required=True) + a.add_argument('--hono', required=True) + a.add_argument('--rt', required=True) + a.add_argument('--golden', default='') + a.add_argument('--draft', default='') + o = a.parse_args() + cc = tsv(o.cc) + fns = {r[1] for r in cc if r[0] == 'fn' and r[2] != 'MODULE'} + static = {(r[1], r[2]) for r in cc if r[0] == 'edge' and r[1] in fns and r[2] in fns} + unres = {r[1] for r in cc if r[0] == 'unres'} + unres_sites = {r[2] for r in cc if r[0] == 'unres'} + chain_of = {} + for r in tsv(o.hono): + if r[0] == 'mw': + chain_of.setdefault((r[2], r[6] if len(r) > 6 else ''), []).append((int(r[5]), r[1])) + elif r[0] == 'mwp': + # a PARTIAL route's decidable chain (`mwp MW HANDLER VERB ORDER`): its hops are middleware hops too + chain_of.setdefault((r[2], 'partial ' + r[3]), []).append((int(r[4]), r[1])) + mwhops = set() + for (h, _), ms in chain_of.items(): + ms.sort() + for (p, m) in ms: + mwhops.add((m, h)) # a middleware reaches the handler through the rest of the chain + for (q, n) in ms: + if q == p + 1: + mwhops.add((m, n)) + # a run-time edge counts only at a call site the compiler could not resolve: a callback a library invokes is + # attributed by the tracer to whatever site is open (its documented limit), which is never such a site + rt = {(r[1], r[2]) for r in tsv(o.rt) if r[0] == 'rtedge' and r[1] in fns and r[2] in fns and len(r) > 3 and r[3] in unres_sites} + rt_only = rt - static - mwhops + if o.draft: + with open(o.draft, 'w') as fh: + for f in sorted(fns): + fh.write(f'fn\t{f}\n') + for e in sorted(static): + fh.write(f'edge\t{e[0]}\t{e[1]}\n') + for e in sorted(rt_only): + fh.write(f'edge\t{e[0]}\t{e[1]}\trt\n') + for u in sorted(unres & fns): + fh.write(f'# unres in {u} (write a ccunknown row when no rt edge covers it)\n') + print(f'drafted {len(fns)} fn, {len(static)} static + {len(rt_only)} rt edges') + return 0 + g = tsv(o.golden) + bad = [] + gf = {r[1] for r in g if r[0] == 'fn'} + gs = {(r[1], r[2]) for r in g if r[0] == 'edge' and len(r) < 4} + gr = {(r[1], r[2]) for r in g if r[0] == 'edge' and len(r) >= 4 and r[3] == 'rt'} + for x in sorted(gf ^ fns): + bad.append(f'fn {x}: ' + ('golden only' if x in gf else 'oracle only')) + for e in sorted(gs ^ static): + bad.append(f'edge {e[0]} -> {e[1]}: ' + ('golden only' if e in gs else 'compiler only')) + for e in sorted(gr ^ rt_only): + bad.append(f'rt edge {e[0]} -> {e[1]}: ' + ('golden only (not observed, or the compiler has it)' if e in gr else 'observed at run time, not in the golden')) + truth = static | rt_only | mwhops + chains = [r for r in g if r[0] == 'chain'] + for r in chains: + f = r[4:] + if len(f) < 2: + bad.append(f'chain {r[1]} has fewer than two functions') + for i in range(len(f) - 1): + if (f[i], f[i + 1]) not in truth: + bad.append(f'chain {r[1]} hop {i + 1}: {f[i]} -> {f[i + 1]} is no truth edge or middleware hop') + for r in g: + if r[0] == 'ccunknown' and r[1] not in unres: + bad.append(f'ccunknown {r[1]}: the compiler resolves every call in it') + if not chains: + bad.append('no chain row: a chains case must assert at least one whole chain') + for b in bad: + print('CHAIN-ORACLE ' + b) + if bad: + return 1 + print(f'chains ok {len(chains)} chains, {len(gs)} + {len(gr)} rt edges') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/graph/test/typescript/hono/truth/cc-oracle.mjs b/graph/test/typescript/hono/truth/cc-oracle.mjs new file mode 100644 index 000000000..e755b6565 --- /dev/null +++ b/graph/test/typescript/hono/truth/cc-oracle.mjs @@ -0,0 +1,511 @@ +#!/usr/bin/env node +// ───────────────────────────────────────────────────────────────────────────── +// Hono torture oracle: the STATIC half of the ground truth. +// +// Independent of the engine by construction: it uses the TypeScript compiler API and the +// real `hono` package pinned in ./package.json (never the axiom parser, never the IR). It +// walks every registration in PROGRAM ORDER and replays Hono's own semantics, as read from +// hono-base.js of the pinned version: +// +// - `new Hono()` / `new OpenAPIHono()` (the class must be DECLARED in the pinned package; +// a local class that happens to be called Hono is not an app); +// - `basePath(p)` returns a clone that SHARES the route list, with base = mergePath(base, p); +// - a verb / `all` / `on` / `use` registers one entry per handler, `use` with method ALL and +// default path `*`, a path-less verb continues the instance's last path; +// - `route(p, sub)` COPIES sub's entries present AT THAT MOMENT under mergePath(base, p); +// - `mount(p, fetch)` registers ALL mergePath(base, p, '*') to a foreign handler. +// +// Which middleware applies to which handler is decided by Hono's OWN router (SmartRouter of +// the pinned package) matched against a concrete sample of the handler's path, in +// registration order: never by a re-implementation of the pattern language. +// +// cc-oracle: program setup copied from hono-oracle.mjs (lines 1-219); call edges below. +// Output (TSV on stdout, one relation per first column; locations are +// `::` of the function node, 1-based): +// route VERB FULLPATH HANDLER APP ORDER +// mw MW HANDLER ORDER (ORDER = position in the handler's middleware chain) +// entry FN http +// ctx SETTER_FN READER_FN KEY +// rpc FROM_FN VERB PATH HANDLER +// request FROM_FN VERB PATH HANDLER +// mount PATH TARGET +// unknown SITE REASON +// Exit 2 (refusal) when the program does not typecheck: an unscored case is not a pass. +// ───────────────────────────────────────────────────────────────────────────── +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const HERE = path.dirname(fileURLToPath(import.meta.url)); +// the compiler is the oracle's OWN pinned one, reached through the suite's shared loader (version-guarded) +const { loadTypeScript } = await import(new URL('../../ground-truth/load-typescript.mjs', import.meta.url).href); +const ts = loadTypeScript(HERE, { toolName: 'hono-oracle' }); +const NM = path.join(HERE, 'node_modules'); +const { SmartRouter } = await import(path.join(NM, 'hono/dist/router/smart-router/index.js')); +const { RegExpRouter } = await import(path.join(NM, 'hono/dist/router/reg-exp-router/index.js')); +const { TrieRouter } = await import(path.join(NM, 'hono/dist/router/trie-router/index.js')); + +const SRC = path.resolve(process.argv[2] || '.'); +const NO_TYPECHECK = process.argv.includes('--no-typecheck'); +const STORES = process.argv.includes('--stores'); +const DEBUG = !!process.env.HONO_ORACLE_DEBUG; +const T0 = Date.now(); +const phase = name => { if (DEBUG) process.stderr.write(`phase ${name} at ${((Date.now() - T0) / 1000).toFixed(1)}s\n`); }; + +// ── program ───────────────────────────────────────────────────────────────── +const EXT = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/; +function walkDir(d, out = []) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) walkDir(p, out); + else if (EXT.test(e.name) && !e.name.endsWith('.d.ts')) out.push(p); + } + return out; +} +const files = walkDir(SRC).sort(); +const options = { + target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.Bundler, strict: true, allowJs: true, checkJs: false, + noEmit: true, skipLibCheck: true, esModuleInterop: true, resolveJsonModule: true, + jsx: ts.JsxEmit.ReactJSX, jsxImportSource: 'hono/jsx', types: [], lib: ['lib.es2022.d.ts', 'lib.dom.d.ts'], + allowImportingTsExtensions: true, +}; +const host = ts.createCompilerHost(options, true); +const ANCHOR = path.join(HERE, 'anchor.ts'); // resolve bare packages from the oracle's own node_modules +// ── Deno: an import map (deno.json / deno.jsonc / import_map.json `imports`, exact and prefix keys) and +// jsr: / npm: / deno.land URL specifiers, each mapped to the PINNED package of the same name. This is a +// module-resolution host for the oracle only; it says nothing about how the engine should resolve them. +// every deno.json / deno.jsonc / import_map.json in the fixture (a Deno project need not sit at the root) +const denoCfgs = []; +(function scanDeno(d) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) scanDeno(p); else if (['deno.json', 'deno.jsonc', 'import_map.json'].includes(e.name)) denoCfgs.push(p); + } +})(SRC); +const DENO_CFG = denoCfgs.length > 0; +let importMap = {}; +for (const f of denoCfgs) { + try { + const j = JSON.parse(fs.readFileSync(f, 'utf8').replace(/^\s*\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '')); + importMap = { ...importMap, ...(j.imports || {}) }; + const im = j.importMap && path.join(path.dirname(f), j.importMap); + if (im && fs.existsSync(im)) importMap = { ...importMap, ...JSON.parse(fs.readFileSync(im, 'utf8')).imports }; + } catch { } +} +function canonicalSpec(spec) { + if (importMap[spec]) spec = importMap[spec]; + else { + const k = Object.keys(importMap).filter(k => k.endsWith('/') && spec.startsWith(k)).sort((a, b) => b.length - a.length)[0]; + if (k) spec = importMap[k] + spec.slice(k.length); + } + let m; + if ((m = spec.match(/^jsr:\/?@hono\/hono(?:@[^/]*)?(\/.*)?$/)) || (m = spec.match(/^@hono\/hono(\/.*)?$/))) return 'hono' + (m[1] || ''); + if ((m = spec.match(/^npm:\/?((?:@[^/@]+\/)?[^/@]+)(?:@[^/]*)?(\/.*)?$/))) return m[1] + (m[2] || ''); + if ((m = spec.match(/^jsr:\/?(@hono\/[^/@]+)(?:@[^/]*)?(\/.*)?$/))) return m[1] + (m[2] || ''); + if ((m = spec.match(/^https:\/\/deno\.land\/x\/hono(?:@[^/]*)?\/(.*)$/))) { + if (m[1] === 'mod.ts') return 'hono'; + if (m[1] === 'middleware.ts') return path.join(HERE, 'deno-shims', 'middleware.d.ts'); + return 'hono/' + m[1].replace(/^middleware\//, '').replace(/\/index\.ts$|\.ts$/, ''); + } + return spec; +} +const libLabel = ms => { const c = canonicalSpec(ms); return c.startsWith('/') ? 'hono/middleware' : c; }; +// tsconfig `paths` / `baseUrl` of the nearest tsconfig.json above the importing file (inside the fixture): +// an aliased project import (`@/lib/app`) resolves to the project file, as the project's compiler would +const _pathsCache = new Map(); +function pathsOpts(containing) { + let d = path.dirname(path.resolve(containing)); + const seen = []; + while (d.startsWith(SRC)) { + if (_pathsCache.has(d)) { const v = _pathsCache.get(d); seen.forEach(x => _pathsCache.set(x, v)); return v; } + seen.push(d); + const f = path.join(d, 'tsconfig.json'); + if (fs.existsSync(f)) { + let v = null; + try { + const cf = ts.readConfigFile(f, ts.sys.readFile); + const pc = ts.parseJsonConfigFileContent(cf.config || {}, ts.sys, d); + if (pc.options.paths || pc.options.baseUrl) v = { paths: pc.options.paths, baseUrl: pc.options.baseUrl, pathsBasePath: pc.options.pathsBasePath || d }; + } catch { v = null; } + seen.forEach(x => _pathsCache.set(x, v)); return v; + } + const up = path.dirname(d); if (up === d) break; d = up; + } + seen.forEach(x => _pathsCache.set(x, null)); return null; +} +// workspace packages: a package.json with a name anywhere in the fixture (not under node_modules) makes +// `` and `/` resolve to that package's files, as a workspace install links them +const workspace = new Map(); +(function scanWs(d) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) scanWs(p); + else if (e.name === 'package.json') { try { const n = JSON.parse(fs.readFileSync(p, 'utf8')).name; if (n) workspace.set(n, d); } catch { } } + } +})(SRC); +function exportsTarget(d, sub) { + // package.json `exports`: "./sub" -> a string, or a conditions object (types / import / default) + try { + const ex = JSON.parse(fs.readFileSync(path.join(d, 'package.json'), 'utf8')).exports; + if (!ex) return null; + const key = sub ? './' + sub : '.'; + let t = typeof ex === 'string' && key === '.' ? ex : ex[key]; + // a subpath pattern (`"./*"`, `"./adapters/*"`) lists every subpath it matches: substitute the matched part + if (t === undefined && typeof ex === 'object') for (const [k, v] of Object.entries(ex)) { + const i = k.indexOf('*'); if (i < 0 || !key.startsWith(k.slice(0, i)) || !key.endsWith(k.slice(i + 1)) || key.length < k.length - 1) continue; + const m = key.slice(i, key.length - (k.length - i - 1)); + let vv = v; if (vv && typeof vv === 'object') vv = vv.types || vv.import || vv.default || vv.require; + if (typeof vv === 'string') { t = vv.split('*').join(m); break; } + } + if (t && typeof t === 'object') t = t.types || t.import || t.default || t.require; + if (typeof t !== 'string') return null; + // O3b: an export that points at an unbuilt output file (`./dist/x.js`, `./dist/x.d.ts`) is served from the + // package's source in a checkout: the same path under src/, with a source extension + if (!fs.existsSync(path.join(d, t))) { + const stem = t.replace(/^\.?\//, '').replace(/^(dist|build|out|lib)\//, '').replace(/\.d\.(ts|mts|cts)$|\.(js|mjs|cjs|ts|mts|cts)$/, ''); + for (const base of ['src/', '']) for (const ext of ['.ts', '.tsx', '.mts', '.js', '.mjs', '/index.ts', '/index.js']) { + const c = path.join(d, base + stem + ext); + if (fs.existsSync(c)) return c; + } + } + return path.join(d, t); + } catch { return null; } +} +// the moduleResolution of the program a file belongs to: its nearest tsconfig.json inside the fixture, else the oracle's +const _mrCache = new Map(); +function moduleResolutionOf(containing) { + let d = path.dirname(path.resolve(containing)); + while (d.startsWith(SRC)) { + if (_mrCache.has(d)) return _mrCache.get(d); + const f = path.join(d, 'tsconfig.json'); + if (fs.existsSync(f)) { + let v = options.moduleResolution; + try { const pc = ts.parseJsonConfigFileContent(ts.readConfigFile(f, ts.sys.readFile).config || {}, ts.sys, d); v = ts.getEmitModuleResolutionKind(pc.options); } catch { } + _mrCache.set(d, v); return v; + } + const up = path.dirname(d); if (up === d) break; d = up; + } + return options.moduleResolution; +} +function workspaceOpts(spec, containing) { + for (const [n, d] of workspace) if (spec === n || spec.startsWith(n + '/')) { + const sub = spec === n ? '' : spec.slice(n.length + 1); + const t = exportsTarget(d, sub); + if (t) return { baseUrl: d, paths: { [spec]: [t] }, pathsBasePath: d }; + // SPEC R22a: a package whose `exports` does not list this subpath resolves only where the TypeScript compiler + // resolves it. node10 ignores `exports` and reads the package directory itself; bundler, node16 and nodenext + // enforce `exports`, so the import is unbound (no synthetic `src/` paths for it) + let hasExports = false; + try { hasExports = JSON.parse(fs.readFileSync(path.join(d, 'package.json'), 'utf8')).exports !== undefined; } catch { } + if (hasExports) { + const mr = containing ? moduleResolutionOf(containing) : options.moduleResolution; + if (mr !== ts.ModuleResolutionKind.Node10 && mr !== ts.ModuleResolutionKind.Classic) return null; + return { baseUrl: d, moduleResolution: ts.ModuleResolutionKind.Node10, paths: { [spec]: [sub ? path.join(d, sub) : d] }, pathsBasePath: d }; + } + return { baseUrl: d, paths: { [n]: [d, path.join(d, 'src')], [n + '/*']: [path.join(d, '*'), path.join(d, 'src', '*')] }, pathsBasePath: d }; + } + return null; +} +// ONE resolver for every project-side lookup (program building, module order, require), so a module the +// program contains is never evaluated out of order because a second lookup could not find it +function resolveSpec(rawSpec, containing, opts = options) { + const spec = canonicalSpec(rawSpec); + let r = ts.resolveModuleName(spec, containing, opts, host).resolvedModule; + if (!r && !spec.startsWith('.') && !spec.startsWith('/')) { + for (const po of [pathsOpts(containing), workspaceOpts(spec, containing)]) { + if (r || !po) continue; + r = ts.resolveModuleName(spec, containing, { ...opts, ...po }, host).resolvedModule; + if (r && r.isExternalLibraryImport) r = undefined; + } + if (!r) r = ts.resolveModuleName(spec, ANCHOR, opts, host).resolvedModule; + } + return r; +} +function isProjectSpec(spec, containing) { + if (spec.startsWith('.') || spec.startsWith('/')) return true; + const r = resolveSpec(spec, containing); + return !!(r && path.resolve(r.resolvedFileName).startsWith(SRC + path.sep) && !r.resolvedFileName.includes(`${path.sep}node_modules${path.sep}`)); +} +host.resolveModuleNameLiterals = (lits, containing, redirected, opts) => lits.map(lit => ({ resolvedModule: resolveSpec(lit.text, containing, opts) })); +const program = ts.createProgram(DENO_CFG ? [...files, path.join(HERE, 'deno-shims', 'deno.d.ts')] : files, options, host); +const checker = program.getTypeChecker();phase('program'); + +const srcFiles = program.getSourceFiles().filter(sf => path.resolve(sf.fileName).startsWith(SRC + path.sep)); + +if (!NO_TYPECHECK) { + const diags = ts.getPreEmitDiagnostics(program).filter(d => d.file && srcFiles.includes(d.file) + && !/\.(js|mjs|cjs|jsx)$/.test(d.file.fileName)); + if (diags.length) { + for (const d of diags.slice(0, 8)) { + const { line } = d.file.getLineAndCharacterOfPosition(d.start || 0); + process.stderr.write(`REFUSED (does not typecheck): ${path.relative(SRC, d.file.fileName)}:${line + 1} ${ts.flattenDiagnosticMessageText(d.messageText, ' ')}\n`); + } + process.exit(2); + } +} + +const rel = f => path.relative(SRC, path.resolve(f)).split(path.sep).join('/'); +const loc = n => { const sf = n.getSourceFile(); const p = sf.getLineAndCharacterOfPosition(n.getStart(sf)); return `${rel(sf.fileName)}:${p.line + 1}:${p.character + 1}`; }; +const inPkg = (decl, pkgs) => { const f = path.resolve(decl.getSourceFile().fileName); return pkgs.some(p => f.includes(`${path.sep}node_modules${path.sep}${p}${path.sep}`)); }; +// ───────────────────────────────────────────────────────────────────────────── +// CALL-CHAIN ORACLE (callchain harness). Compiler-resolved caller->callee edges over the whole project. +// Rows (TSV): +// fn KEY KIND NAME ENDLINE every function-like with a body, plus each module (KEY rel:1:1) +// edge CALLER CALLEE KIND SITE FEATS KIND call|new|deco|tag|closure|fnref|impl|virt +// unres CALLER SITE REASON CALLEE_TEXT FEATS the compiler names no project declaration it can follow +// lib CALLER SITE LIB CALLEE_TEXT resolved into a library / lib.d.ts (chain boundary) +// ───────────────────────────────────────────────────────────────────────────── +const isSrcDecl = d => d && path.resolve(d.getSourceFile().fileName).startsWith(SRC + path.sep) && !d.getSourceFile().fileName.includes("/node_modules/") && !d.getSourceFile().isDeclarationFile; +const isFnNode = n => (ts.isFunctionDeclaration(n) || ts.isFunctionExpression(n) || ts.isArrowFunction(n) + || ts.isMethodDeclaration(n) || ts.isConstructorDeclaration(n) || ts.isGetAccessorDeclaration(n) + || ts.isSetAccessorDeclaration(n)) && !!n.body; +const isJsFile = f => /\.(js|mjs|cjs|jsx)$/.test(f); +const endLine = n => n.getSourceFile().getLineAndCharacterOfPosition(n.getEnd()).line + 1; +function fnName(n) { + if (n.name) return n.name.getText(); + if (ts.isConstructorDeclaration(n)) return 'constructor'; + const p = n.parent; + if (p && ts.isVariableDeclaration(p) && ts.isIdentifier(p.name)) return p.name.text; + if (p && (ts.isPropertyAssignment(p) || ts.isPropertyDeclaration(p))) return p.name.getText(); + return ts.isArrowFunction(n) ? '' : ''; +} +function encl(n) { + for (let p = n.parent; p; p = p.parent) if (isFnNode(p)) return p; + return null; +} +const keyOf = fnOrNull => fnOrNull ? loc(fnOrNull) : null; +const modKey = sf => `${rel(sf.fileName)}:1:1`; +const callerKey = n => { const f = encl(n); return f ? loc(f) : modKey(n.getSourceFile()); }; +const rows = []; +const emit = (...a) => rows.push(a.map(x => String(x ?? '').replace(/[\t\n]/g, ' ')).join('\t')); +const short = n => n.getText().replace(/\s+/g, ' ').slice(0, 80); + +// ── class hierarchy in the project: ancestor symbol -> implementing/extending class decls ── +const resolveSym = s => (s && (s.flags & ts.SymbolFlags.Alias)) ? checker.getAliasedSymbol(s) : s; +const descendants = new Map(); // ancestor decl node -> Set(class decl) +function heritageDecls(cls) { + const outD = []; + for (const h of cls.heritageClauses || []) for (const t of h.types) { + let s; try { s = resolveSym(checker.getSymbolAtLocation(t.expression)); } catch { } + for (const d of s?.declarations || []) if (ts.isClassLike(d) || ts.isInterfaceDeclaration(d)) outD.push(d); + } + return outD; +} +const allClasses = []; +for (const sf of srcFiles) (function v(n) { if (ts.isClassLike(n)) allClasses.push(n); ts.forEachChild(n, v); })(sf); +for (const c of allClasses) { + const seen = new Set(); const stack = heritageDecls(c); + while (stack.length) { + const a = stack.pop(); if (seen.has(a)) continue; seen.add(a); + if (!descendants.has(a)) descendants.set(a, new Set()); descendants.get(a).add(c); + stack.push(...heritageDecls(a)); + } +} +const memberName = d => d.name && (ts.isIdentifier(d.name) || ts.isStringLiteral(d.name) || ts.isPrivateIdentifier(d.name)) ? d.name.text : null; +function implsOf(decl) { + // decl: a member of an interface / class (abstract or concrete). Returns concrete overriding members in subclasses. + const owner = decl.parent; const nm = memberName(decl); const res = []; + if (!owner || !nm || !descendants.has(owner)) return res; + for (const c of descendants.get(owner)) for (const m of c.members || []) { + if (memberName(m) === nm && isFnNode(m) && !(ts.getCombinedModifierFlags(m) & ts.ModifierFlags.Static)) res.push(m); + if (memberName(m) === nm && ts.isPropertyDeclaration(m) && m.initializer && (ts.isArrowFunction(m.initializer) || ts.isFunctionExpression(m.initializer))) res.push(m.initializer); + } + return res; +} +// ── features of a call site, for break classification ── +function rootOf(e) { + while (true) { + if (ts.isPropertyAccessExpression(e) || ts.isElementAccessExpression(e)) e = e.expression; + else if (ts.isCallExpression(e)) e = e.expression; + else if (ts.isParenthesizedExpression(e) || ts.isAwaitExpression(e) || ts.isNonNullExpression(e) || ts.isAsExpression(e)) e = e.expression; + else return e; + } +} +function siteFeats(node, calleeExpr) { + const f = []; + const sf = node.getSourceFile(); + if (isJsFile(sf.fileName)) f.push('js'); + if (!calleeExpr) return f; + const txt = calleeExpr.getText(); + if (/(^|\W)(c|ctx|context)\.(get|var)\b/.test(txt) || /\bc\.env\b/.test(txt)) f.push('ctxdi'); + if (/\bawait import\(|\bimport\(/.test(txt)) f.push('dynimport'); + let ce = calleeExpr; + while (ts.isParenthesizedExpression(ce) || ts.isAwaitExpression(ce) || ts.isNonNullExpression(ce) || ts.isAsExpression(ce)) ce = ce.expression; + if (ts.isCallExpression(ce)) f.push('callresult'); + if (ts.isElementAccessExpression(ce)) f.push('elementaccess'); + if (node.questionDotToken || (ce.questionDotToken)) f.push('optional'); + const r = rootOf(calleeExpr); + if (r.kind === ts.SyntaxKind.ThisKeyword) { + for (let q = node.parent; q; q = q.parent) { + if (ts.isArrowFunction(q)) continue; + if (isFnNode(q) || ts.isClassStaticBlockDeclaration?.(q)) { + if ((ts.getCombinedModifierFlags(q) & ts.ModifierFlags.Static) || ts.isClassStaticBlockDeclaration?.(q)) f.push('staticthis'); + if (ts.isArrowFunction(node.parent) || true) { let a = node.parent; let viaArrow = false; while (a && a !== q) { if (ts.isArrowFunction(a)) viaArrow = true; a = a.parent; } if (viaArrow) f.push('arrowthis'); } + break; + } + } + // this.x.m(): x a constructor parameter property or assigned from a ctor parameter -> DI + if (ts.isPropertyAccessExpression(calleeExpr) && ts.isPropertyAccessExpression(calleeExpr.expression)) { + const fld = calleeExpr.expression.name.text; + let s; try { s = checker.getSymbolAtLocation(calleeExpr.expression.name); } catch { } + for (const d of s?.declarations || []) { + if (ts.isParameter(d) && ts.isConstructorDeclaration(d.parent)) f.push('ctordi'); + if (ts.isPropertyDeclaration(d) && (d.decorators || ts.getDecorators?.(d))?.length) f.push('ctordi'); + } + void fld; + } + f.push('this'); + } + if (ts.isIdentifier(r)) { + let s; try { s = checker.getSymbolAtLocation(r); } catch { } + if (s && (s.flags & ts.SymbolFlags.Alias)) { + // re-export: the import names a module that is not where the declaration lives + const imp = s.declarations?.[0]; + let decl = resolveSym(s)?.declarations?.[0]; + let im = imp; while (im && !ts.isImportDeclaration(im) && !ts.isImportEqualsDeclaration(im)) im = im.parent; + if (im && ts.isImportDeclaration(im) && !isProjectSpec(im.moduleSpecifier.text, sf.fileName)) f.push('librt'); + if (im && ts.isImportEqualsDeclaration(im)) f.push('librt?'); + if (im && ts.isImportDeclaration(im) && decl) { + const r2 = resolveSpec(im.moduleSpecifier.text, sf.fileName); + if (r2 && path.resolve(r2.resolvedFileName) !== path.resolve(decl.getSourceFile().fileName)) f.push('reexport'); + } + } + for (const d of (s && !(s.flags & ts.SymbolFlags.Alias) ? s.declarations : []) || []) { + if (ts.isVariableDeclaration(d) && d.initializer && /import\(/.test(d.initializer.getText())) f.push('dynimport'); + const direct = ce === r; // the callee IS the identifier: a function value is called + if (ts.isParameter(d)) f.push(direct ? 'param' : 'recv-param'); + if (ts.isParameter(d) && !d.type && (ts.isArrowFunction(d.parent) || ts.isFunctionExpression(d.parent)) + && d.parent.parent && (ts.isCallExpression(d.parent.parent) || ts.isNewExpression(d.parent.parent))) f.push('ctxparam'); + if (ts.isBindingElement(d)) f.push(direct ? 'destructured' : 'recv-destructured'); + let vd = d; while (vd && !ts.isVariableDeclaration(vd) && !ts.isParameter(vd)) vd = vd.parent; + if (vd && ts.isVariableDeclaration(vd) && vd.initializer && /(^|\W)(c|ctx|context)\.(get|var)\b|\bc\.env\b/.test(vd.initializer.getText().slice(0, 200))) f.push('ctxdi'); + if (ts.isVariableDeclaration(d) && d.initializer && /\brequire\(\s*['"][^.\/]/.test(d.initializer.getText())) f.push('librt'); + if (ts.isVariableDeclaration(d) && d.initializer && /\brequire\(/.test(d.initializer.getText())) f.push('require'); + } + } + return [...new Set(f)]; +} +const edgeSeen = new Map(); +function addEdge(caller, calleeNode, kind, site, feats) { + const k = `${caller}\t${loc(calleeNode)}\t${kind}\t${loc(site)}`; + if (edgeSeen.has(k)) return; + edgeSeen.set(k, 1); + emit('edge', caller, loc(calleeNode), kind, loc(site), feats.join(',')); +} +const libName = d => { + const f = path.resolve(d.getSourceFile().fileName); + const m = f.match(/node_modules\/((?:@[^/]+\/)?[^/]+)/); + return m ? m[1] : (f.includes('/typescript/lib/') ? 'ts-lib' : (f.endsWith('.d.ts') ? 'dts:' + rel(f) : 'other')); +}; +function classify(node, caller, calleeExpr, kind) { + // R21 (hono torture chains/): a function read out of a table or an array by ELEMENT ACCESS. The compiler names one + // declaration even when several are possible (functions of one signature collapse into one type), so its answer + // is kept only for a literal key on an object; a key the program does not decide is `dynkey`, and an element of a + // (non-tuple) array is `arrayelem`: both unresolved here, left to the run-time trace + let cx = calleeExpr; + while (cx && (ts.isParenthesizedExpression(cx) || ts.isNonNullExpression(cx) || ts.isAsExpression(cx))) cx = cx.expression; + if (cx && ts.isElementAccessExpression(cx)) { + let kt, ot; try { kt = checker.getTypeAtLocation(cx.argumentExpression); ot = checker.getTypeAtLocation(cx.expression); } catch { } + const lit = kt && (kt.flags & (ts.TypeFlags.StringLiteral | ts.TypeFlags.NumberLiteral)); + const arr = ot && checker.isArrayType && checker.isArrayType(ot); + if (!lit || arr) { emit('unres', caller, loc(node), lit ? 'arrayelem' : 'dynkey', short(calleeExpr), siteFeats(node, calleeExpr).join(',')); return; } + } + let sig; try { sig = checker.getResolvedSignature(node); } catch { sig = undefined; } + let d = sig?.declaration; + const feats = siteFeats(node, calleeExpr); + if (!d) { + if (ts.isNewExpression(node)) { + // implicit constructor: the class declares none; nothing in project code runs except field initialisers + let s; try { s = resolveSym(checker.getSymbolAtLocation(node.expression)); } catch { } + const cd = s?.declarations?.find(x => ts.isClassLike(x)); + if (cd && isSrcDecl(cd)) return; // no body to enter + if (cd) { emit('lib', caller, loc(node), libName(cd), short(node.expression)); return; } + } + let t; try { t = checker.getTypeAtLocation(calleeExpr || node); } catch { } + const reason = !t ? 'notype' : (t.flags & ts.TypeFlags.Any) ? 'any' : (t.flags & ts.TypeFlags.Unknown) ? 'unknown' : 'nosig'; + emit('unres', caller, loc(node), reason, short(calleeExpr || node), feats.join(',')); + return; + } + if (!isSrcDecl(d)) { emit('lib', caller, loc(node), libName(d), short(calleeExpr || node)); return; } + if (ts.isJSDocSignature?.(d) || ts.isFunctionTypeNode(d) || ts.isCallSignatureDeclaration(d) || ts.isConstructorTypeNode(d) || ts.isJSDocFunctionType?.(d)) { + emit('unres', caller, loc(node), 'fnvalue', short(calleeExpr || node), feats.join(',')); return; + } + const isAbstract = (ts.isMethodDeclaration(d) && !d.body) || ts.isMethodSignature(d) || ts.isPropertySignature(d) || ts.isConstructSignatureDeclaration?.(d); + if (isAbstract) { + const impls = implsOf(d); + if (!impls.length) { emit('unres', caller, loc(node), 'iface-noimpl', short(calleeExpr || node), [...feats, 'iface'].join(',')); return; } + for (const m of impls) addEdge(caller, m, 'impl', node, [...feats, 'iface']); + return; + } + if (isFnNode(d)) { + addEdge(caller, d, kind, node, feats); + if (ts.isMethodDeclaration(d)) for (const m of implsOf(d)) addEdge(caller, m, 'virt', node, [...feats, 'virt']); + return; + } + if (ts.isFunctionDeclaration(d) && !d.body) { // overload signature: the implementation is the body sibling + const impl = d.parent && (d.parent.statements || []).find(x => ts.isFunctionDeclaration(x) && x.body && x.name?.text === d.name?.text); + if (impl) { addEdge(caller, impl, kind, node, feats); return; } + } + emit('unres', caller, loc(node), 'decl:' + ts.SyntaxKind[d.kind], short(calleeExpr || node), feats.join(',')); +} +for (const sf of srcFiles) { + emit('fn', modKey(sf), 'MODULE', '', sf.getLineAndCharacterOfPosition(sf.getEnd()).line + 1); + const visit = (node) => { + if (isFnNode(node)) { + emit('fn', loc(node), ts.SyntaxKind[node.kind], fnName(node), endLine(node)); + // a function handed over as an argument: the enclosing function passes it (callback / higher-order hop) + const p = node.parent; + if ((ts.isCallExpression(p) || ts.isNewExpression(p)) && (p.arguments || []).includes(node)) + addEdge(callerKey(node), node, 'closure', p, siteFeats(p, p.expression)); + } + // a function REFERENCE handed over as an argument (app.get('/', handler), arr.map(this.f)) + if ((ts.isCallExpression(node) || ts.isNewExpression(node)) && node.arguments) { + for (const a of node.arguments) { + if (!(ts.isIdentifier(a) || ts.isPropertyAccessExpression(a))) continue; + let s; try { s = resolveSym(checker.getSymbolAtLocation(ts.isIdentifier(a) ? a : a.name)); } catch { } + for (const d of s?.declarations || []) { + let fnD = isFnNode(d) ? d : (ts.isVariableDeclaration(d) && d.initializer && isFnNode(d.initializer) ? d.initializer : null); + if (fnD && isSrcDecl(fnD)) addEdge(callerKey(node), fnD, 'fnref', node, siteFeats(node, node.expression)); + } + } + } + if (ts.isCallExpression(node)) { + if (node.expression.kind !== ts.SyntaxKind.ImportKeyword) + classify(node, callerKey(node), node.expression, 'call'); + } else if (ts.isNewExpression(node)) classify(node, callerKey(node), node.expression, 'new'); + else if (ts.isDecorator(node)) classify(node, callerKey(node), node.expression, 'deco'); + else if (ts.isTaggedTemplateExpression(node)) classify(node, callerKey(node), node.tag, 'tag'); + ts.forEachChild(node, visit); + }; + ts.forEachChild(sf, visit); +} + +// ── package entry exports (non-HTTP entries): each workspace package's entry module ── +for (const [pkgName, d] of workspace) { + let ent = exportsTarget(d, ''); + if (!ent) for (const c of ['src/index.ts', 'index.ts', 'src/index.js', 'index.js', 'lib/index.js']) if (fs.existsSync(path.join(d, c))) { ent = path.join(d, c); break; } + if (!ent) { try { const mn = JSON.parse(fs.readFileSync(path.join(d, 'package.json'), 'utf8')).main; if (mn && fs.existsSync(path.join(d, mn))) ent = path.join(d, mn); } catch { } } + if (!ent) continue; + const sf = program.getSourceFile(ent) || program.getSourceFiles().find(x => path.resolve(x.fileName) === path.resolve(ent)); + if (!sf || !srcFiles.includes(sf)) continue; + const ms = checker.getSymbolAtLocation(sf); if (!ms) continue; + for (const ex of checker.getExportsOfModule(ms)) { + const s = resolveSym(ex); + for (const dd of s?.declarations || []) { + if (!isSrcDecl(dd)) continue; + if (isFnNode(dd)) emit('pkgexport', loc(dd), pkgName, ex.name); + else if (ts.isVariableDeclaration(dd) && dd.initializer && isFnNode(dd.initializer)) emit('pkgexport', loc(dd.initializer), pkgName, ex.name); + else if (ts.isClassDeclaration(dd)) for (const m of dd.members) { + const fl = ts.getCombinedModifierFlags(m); + if (isFnNode(m) && !(fl & (ts.ModifierFlags.Private | ts.ModifierFlags.Protected)) && !(m.name && ts.isPrivateIdentifier(m.name))) + emit('pkgexport', loc(m), pkgName, ex.name + '.' + (memberName(m) || 'constructor')); + } + } + } +} +process.stdout.write(rows.join('\n') + '\n'); +process.stderr.write(`cc-oracle: ${srcFiles.length} files, ${rows.length} rows, ${((Date.now() - T0) / 1000).toFixed(1)}s\n`); diff --git a/graph/test/typescript/hono/truth/chain-runtime.mjs b/graph/test/typescript/hono/truth/chain-runtime.mjs new file mode 100644 index 000000000..cbe1c2963 --- /dev/null +++ b/graph/test/typescript/hono/truth/chain-runtime.mjs @@ -0,0 +1,161 @@ +#!/usr/bin/env node +// ───────────────────────────────────────────────────────────────────────────── +// Hono torture oracle: the RUNTIME half for call CHAINS (chains/ suite). +// +// The fixture is copied, every call site and function is instrumented by the repository's TypeScript runtime +// oracle (../../runtime-oracle/instrument.mjs + runtime.cjs: each call site records the declaration that actually +// ran), every module that exports a Hono app is loaded with the pinned `hono`, and each `chain` row's request is +// sent to every exported app with `app.request(path, { method })`. The observed edges are +// (function enclosing the site -> function entered); a framework hop such as middleware -> handler is observed too, +// because the handler is entered from the middleware's own `await next()` site. +// +// usage: node chain-runtime.mjs [--edges] [--routes ] [--requests ] +// A `body VERB PATH JSON` row of the chains file sends that JSON body (content-type application/json) with that request. +// stdout: `runtime chains over ` | `runtime skip `; one `rtedge FROM TO` line per +// observed project edge with --edges; exit 1 when a hop of a chain row was not observed, or an `edge .. rt` row of +// the golden was not observed. +// ───────────────────────────────────────────────────────────────────────────── +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +const HERE = path.dirname(fileURLToPath(import.meta.url)); +const RT = path.resolve(HERE, '../../runtime-oracle'); +const SRC = path.resolve(process.argv[2]); +const CHAINS = fs.readFileSync(process.argv[3], 'utf8').split('\n').filter(l => l && !l.startsWith('#')).map(l => l.split('\t')); +const EDGES = process.argv.includes('--edges'); +// --routes : also request every route the static oracle lists (a sample value per parameter), so +// the trace covers every handler, not only the chains the golden names +const RI = process.argv.indexOf('--routes'); +const ROUTES = RI > 0 ? fs.readFileSync(process.argv[RI + 1], 'utf8').split('\n').map(l => l.split('\t')).filter(r => r[0] === 'route') : []; +const sample = p => p.split('/').map(s => s.startsWith(':') ? 'x1' : s === '*' ? 'x' : s).join('/') || '/'; + +const chains = CHAINS.filter(r => r[0] === 'chain' && r[2] !== '-'); +const rtEdges = CHAINS.filter(r => r[0] === 'edge' && r[3] === 'rt'); +if (!chains.length && !rtEdges.length && !ROUTES.length && !(process.argv.includes('--requests') && fs.existsSync(process.argv[process.argv.indexOf('--requests') + 1]))) { console.log('runtime skip no chain with a request'); process.exit(0); } + +const W = fs.mkdtempSync(path.join(os.tmpdir(), 'hono-chain-')); +const cleanup = () => { try { fs.rmSync(W, { recursive: true, force: true }); } catch { } }; +fs.cpSync(SRC, W, { recursive: true }); +const realW = fs.realpathSync(W); +// a JavaScript fixture (the JS twin suite): the repository's runtime instrumenter rewrites TypeScript files only, so +// each `.js` is traced as a `.ts` of the same text (JavaScript is TypeScript syntax; node strips no types from it, +// and runtime-hooks.mjs resolves `./x.js` to `./x.ts`); every key is spelled back with `.js` below +const renamed = new Set(); +(function toTs(d) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) toTs(p); + else if (e.name.endsWith('.js')) { const t = p.replace(/\.js$/, '.ts'); fs.renameSync(p, t); renamed.add(path.relative(realW, t).split(path.sep).join('/')); } + } +})(realW); +const unTs = rel => renamed.has(rel) ? rel.replace(/\.ts$/, '.js') : rel; +const T = path.join(realW, '.ax-tables'); +fs.mkdirSync(T); +const ins = spawnSync(process.execPath, [path.join(RT, 'instrument.mjs'), '--src', realW, '--out', realW, '--tables', T], { encoding: 'utf8' }); +if (ins.status !== 0) { console.log(`runtime skip instrumenter failed: ${(ins.stderr || '').split('\n')[0]}`); cleanup(); process.exit(0); } +// the pinned packages are linked AFTER instrumenting: library code is never rewritten +fs.symlinkSync(path.join(HERE, 'node_modules'), path.join(realW, 'node_modules')); +if (!fs.existsSync(path.join(realW, 'package.json'))) fs.writeFileSync(path.join(realW, 'package.json'), '{"type":"module"}'); + +const files = []; +(function walk(d) { + for (const e of fs.readdirSync(d, { withFileTypes: true })) { + if (e.name === 'node_modules' || e.name.startsWith('.')) continue; + const p = path.join(d, e.name); + if (e.isDirectory()) walk(p); else if (/\.(ts|mts|js|mjs)$/.test(e.name) && !e.name.endsWith('.d.ts') && !/(\.test\.|\.spec\.|client)/.test(e.name)) files.push(p); + } +})(realW); +// `body VERB PATH JSON` rows: the request carries that JSON body (a route behind a json validator is only entered +// when the body passes it); every other request has none +// --requests : the --edges run (no chains file) also sends that file's chain requests, with its bodies; +// a partial route is in no `route` row, so its chain's request is the only one that reaches it +const QI = process.argv.indexOf('--requests'); +const QROWS = QI > 0 && fs.existsSync(process.argv[QI + 1]) ? fs.readFileSync(process.argv[QI + 1], 'utf8').split('\n').filter(l => l && !l.startsWith('#')).map(l => l.split('\t')) : []; +const bodies = new Map([...CHAINS, ...QROWS].filter(r => r[0] === 'body').map(r => [`${r[1]}\t${r[2]}`, r.slice(3).join('\t')])); +const requests = [...new Set([...[...chains, ...QROWS.filter(r => r[0] === 'chain' && r[2] !== '-')].map(r => `${r[2]}\t${r[3]}`), + ...ROUTES.filter(r => r[1] !== 'ANY').map(r => `${r[1]}\t${sample(r[2])}`)])]; +const driver = ` +import { register } from 'node:module'; +import { pathToFileURL } from 'node:url'; +register(${JSON.stringify(pathToFileURL(path.join(HERE, 'runtime-hooks.mjs')).href)}); +// the tracer runtime (CommonJS) is loaded by a dynamic import, which runs it exactly as a require would +await import(${JSON.stringify(pathToFileURL(path.join(RT, 'runtime.cjs')).href)}); +const { HonoBase } = await import(${JSON.stringify(pathToFileURL(path.join(HERE, 'node_modules/hono/dist/hono-base.js')).href)}); +const apps = new Set(); let err = null; +for (const f of ${JSON.stringify(files.sort())}) { + try { const ns = await import(pathToFileURL(f).href); for (const v of Object.values(ns)) if (v instanceof HonoBase) apps.add(v); } + catch (e) { err = f + ': ' + String(e.message || e).split('\\n')[0]; } +} +if (!apps.size) { console.log('NOAPP ' + (err || '')); } +const bodies = ${JSON.stringify(Object.fromEntries(bodies))}; +for (const rq of JSON.parse(process.env.AX_REQUESTS)) { + const [method, p] = rq.split('\\t'); + const init = rq in bodies ? { method, body: bodies[rq], headers: { 'content-type': 'application/json' } } : { method }; + for (const app of apps) { try { await app.request(p, init); } catch (e) { console.log('REQERR ' + p + ' ' + e.message); } } +} +globalThis.__axFlush && globalThis.__axFlush(); +`; +fs.writeFileSync(path.join(realW, '.ax-driver.mjs'), driver); +// one traced process per call: `requests` in order, each to every exported app; a fresh trace dir per call +let nrun = 0; +const traceRun = (rqs) => { + const out = path.join(realW, `.ax-trace${nrun++}`); fs.mkdirSync(out); + const run = spawnSync(process.execPath, [path.join(realW, '.ax-driver.mjs')], { cwd: realW, encoding: 'utf8', env: { ...process.env, AX_TRACE_OUT: out, AX_REQUESTS: JSON.stringify(rqs) } }); + const noapp = (run.stdout || '').split('\n').find(l => l.startsWith('NOAPP')); + if (run.status !== 0 || noapp) return { skip: noapp ? noapp.slice(6) : (run.stderr || '').split('\n').find(Boolean) }; + return { out }; +}; +const first = traceRun(requests); +if (first.skip !== undefined) { console.log(`runtime skip not loadable (${first.skip})`); cleanup(); process.exit(0); } + +// id tables -> `::` keys, the same spelling as the static oracles +const tsv = f => fs.readFileSync(path.join(T, f), 'utf8').split('\n').slice(1).filter(Boolean).map(l => l.split('\t')); +const relOf = f => unTs(path.relative(realW, path.resolve(realW, f)).split(path.sep).join('/')); +const declKey = new Map(tsv('decls.tsv').map(r => [r[0], `${relOf(r[1])}:${r[2]}:${r[3]}`])); +const siteFrom = new Map(tsv('sites.tsv').map(r => [r[0], r[8]])); +const siteLoc = new Map(tsv('sites.tsv').map(r => [r[0], `${relOf(r[1])}:${r[2]}:${r[3]}`])); +// a function expression handed over as an argument at a site in FROM (callbacks.tsv): when the host runs it later +// (a promise continuation, a timer) no site is open, so it is entered `unlinked`; the hop FROM -> it is then +// observed as "handed over by FROM, and ran" +const handed = new Set(tsv('callbacks.tsv').map(r => `${declKey.get(siteFrom.get(r[0]))}\t${declKey.get(r[1])}`)); +const readTrace = (out) => { + const t = { seen: new Set(), seenAt: new Set(), entered: new Set(), unlinked: new Set() }; + for (const f of fs.readdirSync(out)) for (const l of fs.readFileSync(path.join(out, f), 'utf8').split('\n')) { + const c = l.split('\t'); + if (c[0] === 'unlinked' || c[0] === 'pair') { const d = declKey.get(c[2]); if (d) { t.entered.add(d); if (c[0] === 'unlinked') t.unlinked.add(d); } } + if (c[0] !== 'pair') continue; + const from = declKey.get(siteFrom.get(c[1])), to = declKey.get(c[2]); + if (from && to) { t.seen.add(`${from}\t${to}`); t.seenAt.add(`${from}\t${to}\t${siteLoc.get(c[1])}`); } + } + return t; +}; +const { seen, seenAt, entered, unlinked } = readTrace(first.out); +// with the call site: a callback a LIBRARY invokes (`xs.map(cb)`) is attributed to whatever site is open, so the +// checker keeps a run-time edge only at a site the compiler itself could not resolve +if (EDGES) for (const e of [...seenAt].sort()) console.log(`rtedge\t${e}`); +const bad = []; +let hops = 0, viaLib = 0; +for (const r of chains) { + const fns = r.slice(4); + let alone = null; + for (let i = 0; i + 1 < fns.length; i++) { + hops++; const k = `${fns[i]}\t${fns[i + 1]}`; + if (seen.has(k) || (handed.has(k) && entered.has(fns[i + 1]))) continue; + // a hop through LIBRARY code that awaits before it continues (a json validator reads the body, then calls + // next): no project site is open when the next function is entered, so it is `unlinked`. It is observed when a + // process that sends ONLY this chain's request runs both functions, the second entered from library code. + if (unlinked.has(fns[i + 1])) { + if (!alone) { const t = traceRun([`${r[2]}\t${r[3]}`]); alone = t.out ? readTrace(t.out) : { entered: new Set(), unlinked: new Set() }; } + if (alone.entered.has(fns[i]) && alone.unlinked.has(fns[i + 1])) { viaLib++; continue; } + } + bad.push(`chain ${r[1]} hop ${i}: ${fns[i]} -> ${fns[i + 1]} not observed at run time`); + } +} +for (const r of rtEdges) if (!seen.has(`${r[1]}\t${r[2]}`)) bad.push(`edge ${r[1]} -> ${r[2]} (rt) not observed at run time`); +cleanup(); +if (bad.length) { for (const b of bad) console.log(`runtime DISAGREES ${b}`); process.exit(1); } +console.log(`runtime chains ${hops} hops over ${chains.length} chains, ${rtEdges.length} rt edges${viaLib ? `, ${viaLib} hops entered from library code in a one-request run` : ''}`); From c742fe55026c4b4c4a79897eda8b0a919f29c063 Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Fri, 30 Oct 2026 06:10:42 -0700 Subject: [PATCH 8/9] typescript: a function handed to a constructor parameter property is a hop when the class reads the field `constructor(private readonly onTimeout: () => void) {}` never names the parameter in the constructor body, so the liveness guard on callback_handed held the hand-over dead. The class runs it through the field instead: `this.onTimeout()` in a method, or `setTimeout(this.onTimeout, ms)`. A parameter that declares a field now counts as run wherever that field is read. A parameter property that nothing reads stays dead, as before. --- graph/typescript/engine/call-edge-generation/call_chain.dl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/graph/typescript/engine/call-edge-generation/call_chain.dl b/graph/typescript/engine/call-edge-generation/call_chain.dl index e51eeffd4..f46673609 100644 --- a/graph/typescript/engine/call-edge-generation/call_chain.dl +++ b/graph/typescript/engine/call-edge-generation/call_chain.dl @@ -167,6 +167,9 @@ cb_handed_live(ce, pos) :- cb_handed_arg(ce, pos, _), call_runs_method(ce, b), m cb_handed_live(ce, pos) :- cb_handed_arg(ce, pos, _), call_runs_method(ce, b), method_prov(b, "client"), param_decl("client", _, pos, _, b, p), !kr_registry_param(p), cb_param_runs(p). cb_param_runs(p) :- expr_referenced("client", "PARAMETER", p, e), !cb_ref_inert(e). +// a parameter property (`constructor(private readonly onTimeout: () => void)`) is also a field: the class +// runs it through `this.onTimeout` wherever the field is read, though the constructor never names it +cb_param_runs(p) :- param_declares_field("client", f, p), expr_holder(_, f). cb_ref_inert(e) :- expr_referenced("client", "PARAMETER", _, e), expr_child("client", x, _, _, e), expr_operator("client", op, x), cb_inert_op(op). cb_inert_op("==="). From 02ac6eb6e0e459e32aa9529f3690121d06bb9dba Mon Sep 17 00:00:00 2001 From: Swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Fri, 30 Oct 2026 06:10:43 -0700 Subject: [PATCH 9/9] tests: TypeScript case for a function handed to a constructor parameter property 87-handed-parameter-property: the field is invoked in a method (Watchdog) or handed on to a timer (Ticker), and both hand-overs are callback_handed hops. The controls are a parameter property no member reads (Silent) and a plain constructor parameter the body never reads (Ignores); neither gets a hop. --- .../87-handed-parameter-property/src/app.ts | 15 ++++++++ .../src/timers.ts | 37 +++++++++++++++++++ .../87-handed-parameter-property.edges | 12 ++++++ .../87-handed-parameter-property.entries | 3 ++ .../87-handed-parameter-property.envelope | 4 ++ .../87-handed-parameter-property.fields | 5 +++ ...87-handed-parameter-property.fields-oracle | 8 ++++ .../87-handed-parameter-property.oracle | 1 + .../87-handed-parameter-property.type-use | 5 +++ .../87-handed-parameter-property.types-oracle | 7 ++++ 10 files changed, 97 insertions(+) create mode 100644 graph/test/typescript/cases/87-handed-parameter-property/src/app.ts create mode 100644 graph/test/typescript/cases/87-handed-parameter-property/src/timers.ts create mode 100644 graph/test/typescript/expected/87-handed-parameter-property.edges create mode 100644 graph/test/typescript/expected/87-handed-parameter-property.entries create mode 100644 graph/test/typescript/expected/87-handed-parameter-property.envelope create mode 100644 graph/test/typescript/expected/87-handed-parameter-property.fields create mode 100644 graph/test/typescript/expected/87-handed-parameter-property.fields-oracle create mode 100644 graph/test/typescript/expected/87-handed-parameter-property.oracle create mode 100644 graph/test/typescript/expected/87-handed-parameter-property.type-use create mode 100644 graph/test/typescript/expected/87-handed-parameter-property.types-oracle diff --git a/graph/test/typescript/cases/87-handed-parameter-property/src/app.ts b/graph/test/typescript/cases/87-handed-parameter-property/src/app.ts new file mode 100644 index 000000000..e499eba4f --- /dev/null +++ b/graph/test/typescript/cases/87-handed-parameter-property/src/app.ts @@ -0,0 +1,15 @@ +import { Watchdog, Ticker, Silent, Ignores } from "./timers"; + +function reportTimeout(): void {} +function countTick(): void {} +function finishQuietly(): void {} +function finishIgnored(): void {} + +export function start(): string { + const dog = new Watchdog(reportTimeout, "dog"); + const ticker = new Ticker(countTick, 10); + ticker.start(); + const quiet = new Silent(finishQuietly); + new Ignores(finishIgnored); + return dog.expire() + quiet.name(); +} diff --git a/graph/test/typescript/cases/87-handed-parameter-property/src/timers.ts b/graph/test/typescript/cases/87-handed-parameter-property/src/timers.ts new file mode 100644 index 000000000..85b6a9da1 --- /dev/null +++ b/graph/test/typescript/cases/87-handed-parameter-property/src/timers.ts @@ -0,0 +1,37 @@ +// A function handed to a constructor lands in a parameter PROPERTY. The constructor +// body never reads the parameter; a method of the class runs it through the field. + +export class Watchdog { + constructor(private readonly onTimeout: () => void, private readonly label: string) {} + + expire(): string { + this.onTimeout(); + return this.label; + } +} + +// The field is read and handed on to a timer, not invoked on the spot. +export class Ticker { + private handle: ReturnType | undefined; + + constructor(private readonly onTick: () => void, private readonly ms: number) {} + + start(): void { + clearTimeout(this.handle); + this.handle = setTimeout(this.onTick, this.ms); + } +} + +// Control: the parameter property is never read, so nothing the class does can run it. +export class Silent { + constructor(private readonly onDone: () => void) {} + + name(): string { + return "silent"; + } +} + +// Control: a plain constructor parameter that the body never reads. +export class Ignores { + constructor(_onDone: () => void) {} +} diff --git a/graph/test/typescript/expected/87-handed-parameter-property.edges b/graph/test/typescript/expected/87-handed-parameter-property.edges new file mode 100644 index 000000000..66c55fca3 --- /dev/null +++ b/graph/test/typescript/expected/87-handed-parameter-property.edges @@ -0,0 +1,12 @@ +ambiguous_unknown FUNCTION_CALL Ticker#start() @L20 -> - +ambiguous_unknown FUNCTION_CALL Ticker#start() @L21 -> - +callback_handed CONSTRUCTOR_CALL app#start() @L10 -> app#countTick() +callback_handed CONSTRUCTOR_CALL app#start() @L9 -> app#reportTimeout() +known_edge CONSTRUCTOR_CALL app#start() @L10 -> Ticker#(() =,number) +known_edge CONSTRUCTOR_CALL app#start() @L12 -> Silent#(() =) +known_edge CONSTRUCTOR_CALL app#start() @L13 -> Ignores#(() =) +known_edge CONSTRUCTOR_CALL app#start() @L9 -> Watchdog#(() =,string) +known_edge METHOD_CALL Watchdog#expire() @L8 -> Watchdog#() +known_edge METHOD_CALL app#start() @L11 -> Ticker#start() +known_edge METHOD_CALL app#start() @L14 -> Silent#name() +known_edge METHOD_CALL app#start() @L14 -> Watchdog#expire() diff --git a/graph/test/typescript/expected/87-handed-parameter-property.entries b/graph/test/typescript/expected/87-handed-parameter-property.entries new file mode 100644 index 000000000..a8437099d --- /dev/null +++ b/graph/test/typescript/expected/87-handed-parameter-property.entries @@ -0,0 +1,3 @@ +── entry_point (2) ── + exported_from_entry_module app#start app.ts:8 + unimported_module app# app.ts:1 diff --git a/graph/test/typescript/expected/87-handed-parameter-property.envelope b/graph/test/typescript/expected/87-handed-parameter-property.envelope new file mode 100644 index 000000000..4a7e97865 --- /dev/null +++ b/graph/test/typescript/expected/87-handed-parameter-property.envelope @@ -0,0 +1,4 @@ +value timers#@17:40 -> app#countTick +value timers#@27:40 -> app#finishQuietly +value timers#@36:24 -> app#finishIgnored +value timers#@5:43 -> app#reportTimeout diff --git a/graph/test/typescript/expected/87-handed-parameter-property.fields b/graph/test/typescript/expected/87-handed-parameter-property.fields new file mode 100644 index 000000000..40fc20fd6 --- /dev/null +++ b/graph/test/typescript/expected/87-handed-parameter-property.fields @@ -0,0 +1,5 @@ +known_edge read Ticker#start() -> Ticker#handle +known_edge read Ticker#start() -> Ticker#ms +known_edge read Ticker#start() -> Ticker#onTick +known_edge read Watchdog#expire() -> Watchdog#label +known_edge write Ticker#start() -> Ticker#handle diff --git a/graph/test/typescript/expected/87-handed-parameter-property.fields-oracle b/graph/test/typescript/expected/87-handed-parameter-property.fields-oracle new file mode 100644 index 000000000..65b55d096 --- /dev/null +++ b/graph/test/typescript/expected/87-handed-parameter-property.fields-oracle @@ -0,0 +1,8 @@ +87-handed-parameter-property [fields] + precision 1.0000 (5 correct, 0 wrong) + recall 0.8333 (5 of 6 the compiler resolved) + sites 5 resolved 5 (100.0%) + tiers known_edge=5 + access read=4 write=1 + not scored: 0 rows whose target is not a client declaration + MISSING Watchdog#expire() READ Watchdog#onTimeout diff --git a/graph/test/typescript/expected/87-handed-parameter-property.oracle b/graph/test/typescript/expected/87-handed-parameter-property.oracle new file mode 100644 index 000000000..00f772edc --- /dev/null +++ b/graph/test/typescript/expected/87-handed-parameter-property.oracle @@ -0,0 +1 @@ +oracle=8 engine=8 agree=8 missing=0 (known 0, NEW 0) extra=0 diff --git a/graph/test/typescript/expected/87-handed-parameter-property.type-use b/graph/test/typescript/expected/87-handed-parameter-property.type-use new file mode 100644 index 000000000..18394f40c --- /dev/null +++ b/graph/test/typescript/expected/87-handed-parameter-property.type-use @@ -0,0 +1,5 @@ +ambiguous_unknown TYPE_ELEMENT 1 Ticker [FIELD] -> - +known_edge OBJECT_CREATION_TYPE 0 app [EXPRESSION] -> Ignores +known_edge OBJECT_CREATION_TYPE 0 app [EXPRESSION] -> Silent +known_edge OBJECT_CREATION_TYPE 0 app [EXPRESSION] -> Ticker +known_edge OBJECT_CREATION_TYPE 0 app [EXPRESSION] -> Watchdog diff --git a/graph/test/typescript/expected/87-handed-parameter-property.types-oracle b/graph/test/typescript/expected/87-handed-parameter-property.types-oracle new file mode 100644 index 000000000..2c69d5dd1 --- /dev/null +++ b/graph/test/typescript/expected/87-handed-parameter-property.types-oracle @@ -0,0 +1,7 @@ +87-handed-parameter-property [types] + precision 1.0000 (4 correct, 0 wrong) + recall 1.0000 (4 of 4 the compiler resolved) + sites 5 resolved 4 (80.0%) + tiers ambiguous_unknown=1 known_edge=4 + contexts OBJECT_CREATION_TYPE=4 TYPE_ELEMENT=1 + not scored: 1 rows whose target is not a client declaration