diff --git a/cli/app/shell.ts b/cli/app/shell.ts index c3f80acdd2..1d2e06bd8d 100644 --- a/cli/app/shell.ts +++ b/cli/app/shell.ts @@ -178,7 +178,7 @@ export function createApp(config: AppConfig): App { if (spinnerInterval) return; spinnerInterval = setInterval(() => { render(); - }, 80) as unknown as number; + }, 80); } function stopSpinner(): void { diff --git a/cli/app/startup.ts b/cli/app/startup.ts index 660d4c3fb0..725b39dbd1 100644 --- a/cli/app/startup.ts +++ b/cli/app/startup.ts @@ -45,7 +45,7 @@ export interface StartupProgressDeps { function platformDeps(): StartupProgressDeps { return { write: (text) => writeStdout(text), - setInterval: (fn, ms) => setInterval(fn, ms) as unknown as number, + setInterval: (fn, ms) => setInterval(fn, ms), clearInterval: (handle) => clearInterval(handle), }; } diff --git a/cli/auth/callback-server.ts b/cli/auth/callback-server.ts index 2c0a81f3a8..e56d36abd8 100644 --- a/cli/auth/callback-server.ts +++ b/cli/auth/callback-server.ts @@ -231,7 +231,7 @@ function tryStartDenoServer(port: number, options: CallbackServerOptions = {}): }); // Access native Deno.serve via `self` to bypass dnt shim transform. - const nativeDeno = (self as unknown as Record)["Deno"]!; + const nativeDeno = (self as typeof self & { Deno?: typeof Deno })["Deno"]!; const server = nativeDeno.serve( { port, hostname: "127.0.0.1", onListen: () => {} }, (request: Request) => { diff --git a/cli/commands/test/handler.ts b/cli/commands/test/handler.ts index 4c9c64b8db..4a7c504054 100644 --- a/cli/commands/test/handler.ts +++ b/cli/commands/test/handler.ts @@ -9,7 +9,7 @@ import { isJsonMode, outputJson, } from "../../shared/json-output.ts"; -import { parseTestOutput } from "./command.ts"; +import { parseTestOutput, type TestResult } from "./command.ts"; const getTestArgsSchema = defineSchema((v) => v.object({ @@ -62,7 +62,7 @@ export async function handleTestCommand(args: ParsedArgs): Promise { code: "TEST_FAILURE", slug: "tests-failed", message: `${parsed.summary.failed} test(s) failed`, - context: parsed as unknown as Record, + context: parsed as TestResult & Record, })); } } else { diff --git a/deno.json b/deno.json index fe9ed1ce7e..9e80a429ab 100644 --- a/deno.json +++ b/deno.json @@ -457,7 +457,8 @@ }, "tasks": { "setup": "deno run --allow-all scripts/setup.ts", - "generate": "deno run -A scripts/build/generate-templates-manifest.ts && deno task generate:dev-ui && deno run -A scripts/build/prebundle-client-scripts.ts && deno run -A scripts/build/prebundle-bridge.ts && deno run -A scripts/build/prebundle-rsc-scripts.ts && deno run -A scripts/build/prebundle-hydration-runtime.ts", + "generate": "deno run -A scripts/build/run-generate.ts", + "generate:force": "deno run -A scripts/build/run-generate.ts --force", "generate:dev-ui": "deno run -A extensions/ext-dev-ui-react/scripts/generate-styles.ts && deno run -A extensions/ext-dev-ui-react/scripts/prebundle.ts", "generate:dev-ui:check": "deno run -A extensions/ext-dev-ui-react/scripts/generate-styles.ts --check && deno run -A extensions/ext-dev-ui-react/scripts/prebundle.ts --check", "generate:manifests:check": "deno run -A scripts/build/generate-templates-manifest.ts --check && deno task generate:dev-ui:check && deno run -A scripts/build/prebundle-hydration-runtime.ts --check && deno run -A scripts/build/prebundle-client-scripts.ts --check && deno run -A scripts/build/prebundle-bridge.ts --check && deno run -A scripts/build/prebundle-rsc-scripts.ts --check", @@ -496,12 +497,12 @@ "build:storybook": "npm --prefix storybook run build-storybook", "storybook:check": "deno test --no-lock --config=scripts/test.deno.json --no-check --allow-read scripts/storybook/storybook-workbench.test.ts", "lint": "DENO_NO_PACKAGE_JSON=1 deno lint && deno lint --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-meta-property-safety.ts scripts/build/dnt-meta-property-safety.test.ts scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts scripts/build/npm-package-metadata.test.ts scripts/build/prepare-framework-sources.test.ts && deno lint --config=scripts/codemods/deno.json scripts/codemods/", - "lint:ci": "deno task lint && deno task lint:core-deps && deno task lint:cross-runtime-jsr && deno task lint:dependency-boundaries && deno task lint:module-boundaries && deno task lint:client-bundle && deno task lint:extension-contracts && deno task lint:extension-capabilities && deno task lint:ban-test-only && deno task lint:sanitizer-baseline && deno task lint:skipped-tests && deno task lint:chat-ratchets && deno task lint:chat-composability && deno task lint:rfc-status && deno task lint:esm-sh-codemod && deno task lint:test-typecheck && deno task lint:cwd-relative-test-reads && deno task lint:dnt-meta-properties && deno task storybook:check && deno task docs:api-reference:check && deno task docs:errors:check && deno task docs:public:check && deno test --frozen --config=scripts/test.deno.json --no-check --allow-read --allow-write --allow-run=bash scripts/ci/setup-deno-workflow.test.ts scripts/ci/prepare-rc-build.test.ts scripts/build/generated-artifact-checks.test.ts scripts/release.test.ts", + "lint:ci": "deno task lint && deno task lint:core-deps && deno task lint:cross-runtime-jsr && deno task lint:dependency-boundaries && deno task lint:module-boundaries && deno task lint:client-bundle && deno task lint:extension-contracts && deno task lint:extension-capabilities && deno task lint:ban-test-only && deno task lint:sanitizer-baseline && deno task lint:skipped-tests && deno task lint:chat-ratchets && deno task lint:chat-composability && deno task lint:rfc-status && deno task lint:esm-sh-codemod && deno task lint:test-typecheck && deno task lint:cwd-relative-test-reads && deno task lint:anti-slop && deno task lint:dnt-meta-properties && deno task storybook:check && deno task docs:api-reference:check && deno task docs:errors:check && deno task docs:public:check && deno test --frozen --config=scripts/test.deno.json --no-check --allow-read --allow-write --allow-run=bash scripts/ci/setup-deno-workflow.test.ts scripts/ci/prepare-rc-build.test.ts scripts/build/generated-artifact-checks.test.ts scripts/release.test.ts", "fmt": "deno fmt src/ cli/ react/ templates/ && deno fmt --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-meta-property-safety.ts scripts/build/dnt-meta-property-safety.test.ts scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts scripts/build/prepare-framework-sources.test.ts && deno fmt --config=scripts/codemods/deno.json scripts/codemods/", "fmt:check": "deno fmt --check src/ cli/ react/ templates/ && deno fmt --check --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-meta-property-safety.ts scripts/build/dnt-meta-property-safety.test.ts scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts scripts/build/prepare-framework-sources.test.ts && deno fmt --check --config=scripts/codemods/deno.json scripts/codemods/", "typecheck": "deno task generate:manifests:check && deno check src/index.ts cli/main.ts src/server/index.ts src/routing/api/index.ts src/rendering/index.ts src/platform/index.ts src/platform/adapters/index.ts src/build/index.ts src/build/production-build/index.ts src/transforms/index.ts src/config/index.ts src/utils/index.ts src/data/index.ts src/security/index.ts src/middleware/index.ts src/server/handlers/dev/index.ts src/server/handlers/request/api/index.ts src/rendering/cache/index.ts src/rendering/cache/stores/index.ts src/rendering/rsc/actions/index.ts src/html/index.ts src/html/hydration-script-builder/runtime/main.ts src/modules/index.ts src/proxy/main.ts src/react/components/ui/index.ts src/chat/index.ts src/markdown/index.ts src/mdx/index.ts src/fs/index.ts src/oauth/index.ts src/agent/index.ts src/agent/service/route-export.check.ts src/eval/index.ts src/tool/index.ts src/workflow/index.ts src/prompt/index.ts src/resource/index.ts src/runs/index.ts src/mcp/index.ts src/provider/index.ts", - "verify": "deno task generate:manifests:check && deno task fmt:check && deno task lint && deno task lint:style && deno task lint:chat-composability && deno task lint:rfc-status && deno task lint:chat-ratchets && deno task lint:esm-sh-codemod && deno task lint:cli-boundary && deno task lint:wildcard-exports && deno task lint:barrel-jsdoc && deno task lint:ban-test-only && deno task lint:sanitizer-baseline && deno task lint:skipped-tests && deno task lint:ban-zod && deno task lint:cwd-relative-test-reads && deno task lint:core-deps && deno task lint:cross-runtime-jsr && deno task lint:dependency-boundaries && deno task lint:module-boundaries && deno task lint:extension-contracts && deno task lint:extension-capabilities && deno task docs:api-reference:check && deno task docs:errors:check && deno task docs:validate && deno task typecheck && deno task typecheck:consumer && deno task test && deno task test:scripts && deno task test:e2e:binary", - "verify:quick": "deno task generate:manifests:check && deno task fmt:check && deno task lint && deno task lint:style && deno task lint:chat-composability && deno task lint:rfc-status && deno task lint:chat-ratchets && deno task lint:esm-sh-codemod && deno task lint:cli-boundary && deno task lint:wildcard-exports && deno task lint:barrel-jsdoc && deno task lint:ban-test-only && deno task lint:sanitizer-baseline && deno task lint:skipped-tests && deno task lint:ban-zod && deno task lint:cwd-relative-test-reads && deno task lint:core-deps && deno task lint:cross-runtime-jsr && deno task lint:dependency-boundaries && deno task lint:module-boundaries && deno task lint:extension-contracts && deno task lint:extension-capabilities && deno task docs:api-reference:check && deno task docs:errors:check && deno task docs:validate && deno task typecheck", + "verify": "deno task generate:manifests:check && deno task fmt:check && deno task lint && deno task lint:style && deno task lint:chat-composability && deno task lint:rfc-status && deno task lint:chat-ratchets && deno task lint:esm-sh-codemod && deno task lint:cli-boundary && deno task lint:wildcard-exports && deno task lint:barrel-jsdoc && deno task lint:ban-test-only && deno task lint:sanitizer-baseline && deno task lint:skipped-tests && deno task lint:ban-zod && deno task lint:cwd-relative-test-reads && deno task lint:anti-slop && deno task lint:core-deps && deno task lint:cross-runtime-jsr && deno task lint:dependency-boundaries && deno task lint:module-boundaries && deno task lint:extension-contracts && deno task lint:extension-capabilities && deno task docs:api-reference:check && deno task docs:errors:check && deno task docs:validate && deno task typecheck && deno task typecheck:consumer && deno task test && deno task test:scripts && deno task test:e2e:binary", + "verify:quick": "deno task generate:manifests:check && deno task fmt:check && deno task lint && deno task lint:style && deno task lint:chat-composability && deno task lint:rfc-status && deno task lint:chat-ratchets && deno task lint:esm-sh-codemod && deno task lint:cli-boundary && deno task lint:wildcard-exports && deno task lint:barrel-jsdoc && deno task lint:ban-test-only && deno task lint:sanitizer-baseline && deno task lint:skipped-tests && deno task lint:ban-zod && deno task lint:cwd-relative-test-reads && deno task lint:anti-slop && deno task lint:core-deps && deno task lint:cross-runtime-jsr && deno task lint:dependency-boundaries && deno task lint:module-boundaries && deno task lint:extension-contracts && deno task lint:extension-capabilities && deno task docs:api-reference:check && deno task docs:errors:check && deno task docs:validate && deno task typecheck", "typecheck:consumer": "deno run --allow-read --allow-run --allow-env --allow-write scripts/typecheck/run-consumer-typecheck.ts", "codemod:chat": "deno run --frozen --config=scripts/codemods/deno.json --allow-read --allow-write --allow-env=BABEL_TYPES_8_BREAKING scripts/codemods/migrate-chat-composition.ts", "codemod:esm-sh": "deno run --frozen --config=scripts/codemods/deno.json --allow-read --allow-write --allow-env=BABEL_TYPES_8_BREAKING scripts/codemods/migrate-esm-sh-imports.ts", @@ -547,8 +548,9 @@ "lint:sanitizer-baseline": "deno run --allow-read scripts/lint/check-sanitizer-baseline.ts", "lint:skipped-tests": "deno run --allow-read scripts/lint/check-skipped-tests-baseline.ts", "lint:cwd-relative-test-reads": "deno run --allow-read scripts/lint/audit-cwd-relative-test-reads.ts", + "lint:anti-slop": "deno run --allow-read scripts/lint/audit-anti-slop.ts", "lint:dnt-meta-properties": "deno run --config=scripts/test.deno.json --frozen --allow-read scripts/build/dnt-meta-property-safety.ts", - "test:scripts": "deno test --config=scripts/test.deno.json --no-check --allow-read --allow-write --allow-run scripts/ci/prepare-rc-build.test.ts scripts/ci/publish-npm-packages.test.ts scripts/ci/setup-deno-workflow.test.ts scripts/build/compile-binary.test.ts scripts/build/dnt-meta-property-safety.test.ts scripts/build/dnt-polyfill.test.ts scripts/build/generate-sbom.test.ts scripts/build/generated-artifact-checks.test.ts scripts/build/npm-dependency-sources.test.ts scripts/build/npm-extension-package-metadata.test.ts scripts/build/npm-package-metadata.test.ts scripts/build/npm-react-shims.test.ts scripts/build/npm-runtime-helper-contract.test.ts scripts/build/prepare-framework-sources.test.ts scripts/build/report-artifact-sizes.test.ts scripts/docs/docs-coverage.test.ts scripts/docs/generate-api-reference.test.ts scripts/docs/guide-validation.test.ts scripts/lint/audit-chat-composability.test.ts scripts/lint/audit-rfc-status.test.ts scripts/lint/audit-core-deps.test.ts scripts/lint/audit-cwd-relative-test-reads.test.ts scripts/lint/audit-cross-runtime-jsr.test.ts scripts/lint/audit-dependency-boundaries.test.ts scripts/lint/audit-extension-capabilities.test.ts scripts/lint/audit-extension-contracts.test.ts scripts/lint/audit-deps.test.ts scripts/lint/check-module-boundaries.test.ts scripts/lint/lint-config.test.ts scripts/lint/ban-test-only.test.ts scripts/lint/check-sanitizer-baseline.test.ts scripts/lint/check-skipped-tests-baseline.test.ts scripts/lint/check-test-typecheck-baseline.test.ts scripts/lint/check-coverage.test.ts scripts/security/audit-npm.test.ts scripts/security/submit-dependency-snapshot.test.ts scripts/test/template-runtime-e2e.test.ts && deno task test:tool-search-live", + "test:scripts": "deno test --config=scripts/test.deno.json --no-check --allow-read --allow-write --allow-run scripts/ci/prepare-rc-build.test.ts scripts/ci/publish-npm-packages.test.ts scripts/ci/setup-deno-workflow.test.ts scripts/build/compile-binary.test.ts scripts/build/dnt-meta-property-safety.test.ts scripts/build/dnt-polyfill.test.ts scripts/build/generate-sbom.test.ts scripts/build/generated-artifact-checks.test.ts scripts/build/npm-dependency-sources.test.ts scripts/build/npm-extension-package-metadata.test.ts scripts/build/npm-package-metadata.test.ts scripts/build/npm-react-shims.test.ts scripts/build/npm-runtime-helper-contract.test.ts scripts/build/prepare-framework-sources.test.ts scripts/build/report-artifact-sizes.test.ts scripts/build/run-generate.test.ts scripts/docs/docs-coverage.test.ts scripts/docs/generate-api-reference.test.ts scripts/docs/guide-validation.test.ts scripts/lint/audit-chat-composability.test.ts scripts/lint/audit-rfc-status.test.ts scripts/lint/audit-core-deps.test.ts scripts/lint/audit-cwd-relative-test-reads.test.ts scripts/lint/audit-cross-runtime-jsr.test.ts scripts/lint/audit-dependency-boundaries.test.ts scripts/lint/audit-extension-capabilities.test.ts scripts/lint/audit-extension-contracts.test.ts scripts/lint/audit-deps.test.ts scripts/lint/check-module-boundaries.test.ts scripts/lint/lint-config.test.ts scripts/lint/ban-test-only.test.ts scripts/lint/check-sanitizer-baseline.test.ts scripts/lint/check-skipped-tests-baseline.test.ts scripts/lint/audit-anti-slop.test.ts scripts/lint/check-test-typecheck-baseline.test.ts scripts/lint/check-coverage.test.ts scripts/security/audit-npm.test.ts scripts/security/submit-dependency-snapshot.test.ts scripts/test/template-runtime-e2e.test.ts && deno task test:tool-search-live", "test:sentry-runtime-packages": "deno test --config=scripts/test.deno.json --no-check --no-lock --allow-read --allow-write --allow-run --allow-env=DENO_DIR,HOME,XDG_CACHE_HOME,LOCALAPPDATA,USERPROFILE scripts/build/sentry-runtime-packages.test.ts", "test:tool-search-live": "VF_DISABLE_LRU_INTERVAL=1 deno test --no-check -A tests/agent/verify-tool-search-live.test.ts", "test:cross-runtime": "deno run --allow-all src/platform/compat/cross-runtime.test.ts", @@ -577,6 +579,17 @@ "submit-deps": "deno run --allow-read --allow-env --allow-net=api.github.com scripts/security/submit-dependency-snapshot.ts", "audit": "deno run --allow-read --allow-run --allow-write scripts/security/audit-npm.ts && npm --prefix storybook audit --package-lock-only --audit-level=high" }, + "test": { + "include": [ + "src/", + "cli/", + "templates/", + "tests/", + "react/", + "extensions/", + "scripts/" + ] + }, "lint": { "include": [ "src/**/*.ts", diff --git a/docs/api-reference/veryfront/agent.md b/docs/api-reference/veryfront/agent.md index 33b080abd9..27b4f06e5e 100644 --- a/docs/api-reference/veryfront/agent.md +++ b/docs/api-reference/veryfront/agent.md @@ -714,7 +714,7 @@ Input delivered to a hosted agent-service detached execution callback. | `createInitialForkRuntimeMessages` | Create initial fork runtime messages. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/fork-runtime-stream.ts#L429) | | `createInputRequest` | Request payload for create input. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/input/request-protocol.ts#L210) | | `createLiveStudioMcpTools` | Create live studio MCP tools. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/project/live-studio-mcp-tools.ts#L115) | -| `createMemory` | Create memory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/memory/memory.ts#L352) | +| `createMemory` | Create memory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/memory/memory.ts#L353) | | `createMirroredToolChunkState` | State for create mirrored tool chunk. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/streaming/mirrored-tool-chunk-state.ts#L40) | | `createNodeAgentServiceRuntimeInfrastructure` | Create node agent service runtime infrastructure. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/service/node-runtime-infrastructure.ts#L52) | | `createNodeVeryfrontCloudAgentServiceRuntime` | Create node Veryfront Cloud agent service runtime. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/agent/hosted/veryfront-cloud-agent-service.ts#L204) | diff --git a/docs/api-reference/veryfront/errors.md b/docs/api-reference/veryfront/errors.md index 5072c2c656..758652f773 100644 --- a/docs/api-reference/veryfront/errors.md +++ b/docs/api-reference/veryfront/errors.md @@ -183,7 +183,7 @@ throw INVALID_WIDGET.create({ detail: "The widget id is malformed." }); | `createProblemResponse` | Create an RFC 9457 error Response from raw parameters | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/http-error.ts#L70) | | `createSimpleError` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/catalog/factory.ts#L29) | | `defineError` | Define an error in the registry | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/types.ts#L98) | -| `ensureError` | Ensure a value is an Error while preserving the established identity contract for ordinary Error instances. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/veryfront-error.ts#L837) | +| `ensureError` | Ensure a value is an Error while preserving the established identity contract for ordinary Error instances. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/veryfront-error.ts#L834) | | `errorToResponse` | Convert any error to an RFC 9457 Response | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/http-error.ts#L106) | | `errorToRFC9457Response` | Convert any error to an RFC 9457 Response with environment-aware filtering | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/middleware/http-error-boundary.ts#L98) | | `formatCLIError` | Format any error for CLI output | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/middleware/cli-error-boundary.ts#L163) | @@ -192,7 +192,7 @@ throw INVALID_WIDGET.create({ detail: "The widget id is malformed." }); | `fromError` | Decode legacy Veryfront error data attached by `toError()`. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/legacy-error-codec.ts#L17) | | `getAllSlugs` | Get all registered slugs | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/error-registry.ts#L72) | | `getErrorBySlug` | Get an error definition by slug | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/error-registry.ts#L58) | -| `getErrorMessage` | Extract error message from any error type | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/veryfront-error.ts#L678) | +| `getErrorMessage` | Extract error message from any error type | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/veryfront-error.ts#L675) | | `getErrorsByCategory` | Get all errors in a category | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/error-registry.ts#L65) | | `getErrorSolution` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/catalog/index.ts#L44) | | `handleErrorWithFallback` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/error-handlers.ts#L41) | @@ -208,7 +208,7 @@ throw INVALID_WIDGET.create({ detail: "The widget id is malformed." }); | `safeReadDir` | Safe directory read with logging | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/error-context.ts#L163) | | `sanitizeTerminalDiagnosticText` | Prepare one untrusted diagnostic field for terminal or plain-text output. Apply framework-owned ANSI styling only after this sanitizer returns. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/safe-diagnostics.ts#L97) | | `searchErrors` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/catalog/index.ts#L48) | -| `toError` | Convert a VeryfrontErrorData (plain object) to a throwable Error instance. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/veryfront-error.ts#L622) | +| `toError` | Convert a VeryfrontErrorData (plain object) to a throwable Error instance. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/veryfront-error.ts#L619) | | `withErrorContext` | Execute async operation with error logging and fallback | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/error-context.ts#L109) | | `withErrorContextSync` | Execute sync operation with error logging and fallback | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/error-context.ts#L123) | | `wrapErrorHandler` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/errors/user-friendly/error-wrapper.ts#L26) | diff --git a/docs/api-reference/veryfront/skill.md b/docs/api-reference/veryfront/skill.md index 8a651573cd..7162588a6d 100644 --- a/docs/api-reference/veryfront/skill.md +++ b/docs/api-reference/veryfront/skill.md @@ -50,9 +50,9 @@ validateSkillMetadata(parsed.frontmatter, "review"); | Name | Description | Source | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| `createExecuteSkillScriptTool` | Create the execute_skill_script tool. Executes a script from a skill's scripts/ directory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/tools.ts#L597) | -| `createLoadSkillReferenceTool` | Create the load_skill_reference tool. Reads a reference file from a skill's references/, resources/, or assets/ directory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/tools.ts#L553) | -| `createLoadSkillTool` | Create the load_skill tool. Loads a skill's full instructions, available references, and scripts. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/tools.ts#L483) | +| `createExecuteSkillScriptTool` | Create the execute_skill_script tool. Executes a script from a skill's scripts/ directory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/tools.ts#L598) | +| `createLoadSkillReferenceTool` | Create the load_skill_reference tool. Reads a reference file from a skill's references/, resources/, or assets/ directory. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/tools.ts#L554) | +| `createLoadSkillTool` | Create the load_skill tool. Loads a skill's full instructions, available references, and scripts. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/tools.ts#L484) | | `filterToolNamesForSkill` | Filter provider-native or other name-only tool inventories through the same boundary. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/allowed-tools.ts#L86) | | `filterToolsForSkill` | Filter tool definitions before sending them to the model. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/allowed-tools.ts#L64) | | `getAllSkills` | | [source](https://github.com/veryfront/veryfront-code/blob/main/src/skill/registry.ts#L277) | diff --git a/extensions/ext-auth-jwt/src/index.ts b/extensions/ext-auth-jwt/src/index.ts index a32e72e7c9..1fd0c2e5a7 100644 --- a/extensions/ext-auth-jwt/src/index.ts +++ b/extensions/ext-auth-jwt/src/index.ts @@ -55,7 +55,7 @@ export interface ExtJwtConfig { } function defaultJwksResolverFactory(jwksUrl: string): JwksResolver { - return createRemoteJWKSet(new URL(jwksUrl)) as unknown as JwksResolver; + return createRemoteJWKSet(new URL(jwksUrl)); } function toUint8Array(secret: string | Uint8Array): Uint8Array { @@ -140,8 +140,8 @@ function createAuthProvider(config: ExtJwtConfig): AuthProvider { if (algorithms) verifyOpts.algorithms = algorithms; const { payload } = await jwtVerify( token, - resolver as unknown as KeyLike, - verifyOpts, + resolver, + verifyOpts as Parameters[2], ); return payload as TokenPayload; }, diff --git a/extensions/ext-blob-s3/src/s3-storage.ts b/extensions/ext-blob-s3/src/s3-storage.ts index a10da40a31..1ed420ccd9 100644 --- a/extensions/ext-blob-s3/src/s3-storage.ts +++ b/extensions/ext-blob-s3/src/s3-storage.ts @@ -405,7 +405,7 @@ function createDefaultResources(config: NormalizedS3Config): DefaultS3Resources useFipsEndpoint: config.useFipsEndpoint, userAgentAppId: "veryfront-ext-blob-s3", }); - const send = client.send.bind(client) as unknown as S3BlobStorageClient["send"]; + const send = client.send.bind(client) as S3BlobStorageClient["send"]; return { client: { send, @@ -585,7 +585,7 @@ function toNodeReadable(stream: ReadableStream): Readable { // The npm AWS SDK selects its Node transport in Deno, Bun, and Node. Convert // the framework's web-stream contract at this extension boundary. return Readable.fromWeb( - stream as unknown as Parameters[0], + stream as Parameters[0], { objectMode: false }, ); } diff --git a/extensions/ext-content-mdx/src/compiler/mdx-compile.ts b/extensions/ext-content-mdx/src/compiler/mdx-compile.ts index 0938cd4ab5..7d6cb36b3e 100644 --- a/extensions/ext-content-mdx/src/compiler/mdx-compile.ts +++ b/extensions/ext-content-mdx/src/compiler/mdx-compile.ts @@ -25,14 +25,14 @@ export async function compileMdx(options: ContentCompileOptions): Promise, + ); } catch (cause) { throw new TypeError( "ext-css-lightning browserQueries could not be inspected", diff --git a/extensions/ext-db-sqlite/src/index.ts b/extensions/ext-db-sqlite/src/index.ts index a5b8e1b878..040177b9b2 100644 --- a/extensions/ext-db-sqlite/src/index.ts +++ b/extensions/ext-db-sqlite/src/index.ts @@ -9,12 +9,13 @@ import type { ExtensionFactory } from "veryfront/extensions"; import type { SqliteDatabase, SqliteStore } from "veryfront/extensions/compat"; +type SqliteDatabaseCtor = new (path: string) => SqliteDatabase; + async function loadSqliteDatabase(path?: string): Promise { const mod = await import("better-sqlite3"); - // deno-lint-ignore no-explicit-any - const DatabaseCtor = (mod as any).default ?? mod; - // deno-lint-ignore no-explicit-any - return new DatabaseCtor(path ?? ":memory:") as any as SqliteDatabase; + const DatabaseCtor = ((mod as { default?: SqliteDatabaseCtor }).default ?? + mod) as SqliteDatabaseCtor; + return new DatabaseCtor(path ?? ":memory:"); } export class BetterSqliteStore implements SqliteStore { diff --git a/extensions/ext-dev-ui-react/src/dashboard/components/MCPTab.tsx b/extensions/ext-dev-ui-react/src/dashboard/components/MCPTab.tsx index 6216bf0001..ebb32826e3 100644 --- a/extensions/ext-dev-ui-react/src/dashboard/components/MCPTab.tsx +++ b/extensions/ext-dev-ui-react/src/dashboard/components/MCPTab.tsx @@ -49,7 +49,7 @@ export function MCPTab({ tools, resources, prompts }: MCPTabProps): React.JSX.El setSelectedId(e.detail.itemId); } - const listener = handleNavigate as unknown as EventListener; + const listener = handleNavigate as EventListener; globalThis.addEventListener("mcp-navigate", listener); return () => globalThis.removeEventListener("mcp-navigate", listener); }, []); diff --git a/extensions/ext-document-kreuzberg/src/kreuzberg.ts b/extensions/ext-document-kreuzberg/src/kreuzberg.ts index e447899c7c..e048567f10 100644 --- a/extensions/ext-document-kreuzberg/src/kreuzberg.ts +++ b/extensions/ext-document-kreuzberg/src/kreuzberg.ts @@ -18,7 +18,7 @@ type KreuzbergModule = KreuzbergExtractor & { export async function loadKreuzbergNative(): Promise { try { - return await import("@kreuzberg/node") as unknown as KreuzbergExtractor; + return await import("@kreuzberg/node") as KreuzbergExtractor; } catch (error) { if (!isMissingPackageError(error)) throw error; throw new Error( @@ -60,7 +60,7 @@ export async function loadKreuzberg(): Promise { async function importKreuzbergWasm(): Promise { try { - return await import("@kreuzberg/wasm") as unknown as KreuzbergModule; + return await import("@kreuzberg/wasm") as KreuzbergModule; } catch (error) { if (!isMissingPackageError(error)) throw error; throw new Error( diff --git a/extensions/ext-parser-babel/src/index.ts b/extensions/ext-parser-babel/src/index.ts index f03669658b..2092f3163c 100644 --- a/extensions/ext-parser-babel/src/index.ts +++ b/extensions/ext-parser-babel/src/index.ts @@ -85,7 +85,7 @@ function functionHasDirective(node: ASTNode, directive: string): boolean { class BabelCodeParser extends BabelParseOnlyParser implements CodeParser { traverse(ast: ASTNode, visitor: TraverseVisitor): void { - traverse(ast, visitor as unknown as Record); + traverse(ast, visitor); } generate(ast: ASTNode, options?: GenerateOptions): Promise { diff --git a/extensions/ext-parser-babel/src/parser-only.ts b/extensions/ext-parser-babel/src/parser-only.ts index 77f580c6c2..3e429e2ddb 100644 --- a/extensions/ext-parser-babel/src/parser-only.ts +++ b/extensions/ext-parser-babel/src/parser-only.ts @@ -53,6 +53,7 @@ export class BabelParseOnlyParser implements BabelParseOnlyParserContract { allowReturnOutsideFunction: options.filePath?.toLowerCase().endsWith(".cjs") === true, plugins: pickPlugins(options.filePath), }); - return Promise.resolve(ast as unknown as ASTNode); + const node: { type: string } = ast; + return Promise.resolve(node as ASTNode); } } diff --git a/extensions/ext-schema-zod/src/adapter.ts b/extensions/ext-schema-zod/src/adapter.ts index 2b045f6c62..0dd669e228 100644 --- a/extensions/ext-schema-zod/src/adapter.ts +++ b/extensions/ext-schema-zod/src/adapter.ts @@ -40,9 +40,12 @@ const addFormats = addFormatsModule as unknown as ( // deno-lint-ignore no-explicit-any -- zod's chainable APIs return parametric types type AnyZodSchema = z.ZodType; +/** A wrapped Schema carrying its backing zod schema for adapter round-trips. */ +type ZodBackedSchema = Schema & { __zod: AnyZodSchema }; + /** Unwrap our opaque Schema back to the underlying zod schema. */ function toZod(schema: Schema): AnyZodSchema { - return (schema as unknown as { __zod: AnyZodSchema }).__zod; + return (schema as ZodBackedSchema).__zod; } /** Wrap a zod schema as an opaque Schema with chainables routed through zod. */ @@ -53,7 +56,7 @@ function wrap(zs: AnyZodSchema): Schema { // deno-lint-ignore no-explicit-any -- safe within this adapter const anyZs = zs as any; const s: Schema = { - _output: undefined as unknown as T, + _output: undefined as T, optional: () => wrap(zs.optional()), nullable: () => wrap(zs.nullable()), nullish: () => wrap(zs.nullish()), @@ -131,7 +134,7 @@ function wrap(zs: AnyZodSchema): Schema { }; // Attach the underlying zod schema for adapter round-trips without // widening the public Schema surface. - (s as unknown as { __zod: AnyZodSchema }).__zod = zs; + (s as ZodBackedSchema).__zod = zs; return s; } @@ -817,7 +820,7 @@ export function createZodAdapter(): SchemaValidator { function: (): Schema<(...args: unknown[]) => unknown> => // zod 4's z.function() is callable without args/returns and produces a // schema accepting any function. We wrap it as Schema. - wrap(z.function() as unknown as AnyZodSchema), + wrap(z.function() as AnyZodSchema), object: >>(shape: S): Schema> => wrap(z.object(toZodShape(shape))), @@ -827,7 +830,7 @@ export function createZodAdapter(): SchemaValidator { tuple: []>( items: T, ): Schema<{ [K in keyof T]: T[K] extends Schema ? U : never }> => { - const zodItems = items.map((s) => toZod(s)) as unknown as [ + const zodItems = items.map((s) => toZod(s)) as [ AnyZodSchema, ...AnyZodSchema[], ]; @@ -837,12 +840,12 @@ export function createZodAdapter(): SchemaValidator { record: ( keys: Schema, values: Schema, - ): Schema> => wrap(z.record(toZod(keys) as unknown as z.ZodString, toZod(values))), + ): Schema> => wrap(z.record(toZod(keys) as z.ZodString, toZod(values))), union: , ...Schema[]]>( schemas: T, ): Schema ? U : never> => { - const zodSchemas = schemas.map((s: Schema) => toZod(s)) as unknown as [ + const zodSchemas = schemas.map((s: Schema) => toZod(s)) as [ AnyZodSchema, AnyZodSchema, ...AnyZodSchema[], @@ -857,7 +860,7 @@ export function createZodAdapter(): SchemaValidator { discriminator: K, schemas: T, ): Schema ? U : never> => { - const zodSchemas = schemas.map((s: Schema) => toZod(s)) as unknown as [ + const zodSchemas = schemas.map((s: Schema) => toZod(s)) as [ // deno-lint-ignore no-explicit-any -- discriminated-union variants widen here z.ZodObject, // deno-lint-ignore no-explicit-any -- discriminated-union variants widen here @@ -873,7 +876,7 @@ export function createZodAdapter(): SchemaValidator { enum: (values: T): Schema => wrap( - (z.enum as unknown as (v: readonly [string, ...string[]]) => AnyZodSchema)(values), + (z.enum as (v: readonly [string, ...string[]]) => AnyZodSchema)(values), ), lazy: (factory: () => Schema): Schema => wrap(z.lazy(() => toZod(factory()))), @@ -883,7 +886,7 @@ export function createZodAdapter(): SchemaValidator { // contract uses a slimmer `new`-able shape; bridge with a single cast // through unknown. wrap( - (z.instanceof as unknown as (c: unknown) => AnyZodSchema)(ctor), + (z.instanceof as (c: unknown) => AnyZodSchema)(ctor), ), custom: (check?: (value: unknown) => boolean, message?: string): Schema => diff --git a/react/react.ts b/react/react.ts index 7bc45c19fb..bff5dfddf7 100644 --- a/react/react.ts +++ b/react/react.ts @@ -44,14 +44,15 @@ export { } from "@veryfront/react-upstream"; export const __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = ( - ReactUpstream as unknown as { + ReactUpstream as typeof ReactUpstream & { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: unknown; } ).__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE; -export const __COMPILER_RUNTIME = (ReactUpstream as unknown as { __COMPILER_RUNTIME: unknown }) - .__COMPILER_RUNTIME; +export const __COMPILER_RUNTIME = + (ReactUpstream as typeof ReactUpstream & { __COMPILER_RUNTIME: unknown }) + .__COMPILER_RUNTIME; export const unstable_useCacheRefresh = - (ReactUpstream as unknown as { unstable_useCacheRefresh: unknown }) + (ReactUpstream as typeof ReactUpstream & { unstable_useCacheRefresh: unknown }) .unstable_useCacheRefresh; export type { diff --git a/scripts/build/generated-artifact-checks.test.ts b/scripts/build/generated-artifact-checks.test.ts index a2f45aee72..a1aeeb843e 100644 --- a/scripts/build/generated-artifact-checks.test.ts +++ b/scripts/build/generated-artifact-checks.test.ts @@ -11,10 +11,16 @@ * This checks the wiring rather than the bundling. Running the generators for * real costs an esbuild pass each, and `generate:manifests:check` already * proves they work on every PR. + * + * `generate` runs through the run-generate.ts orchestrator, so the + * generate-side script list comes from its UNITS table rather than from + * parsing the task string; the check side is still parsed out of the task + * chain, so a unit added without a `--check` counterpart still fails here. */ import { assertEquals } from "#veryfront/testing/assert.ts"; import { describe, it } from "#veryfront/testing/bdd.ts"; +import { UNITS } from "./run-generate.ts"; type Tasks = Record; @@ -53,7 +59,13 @@ function scriptInvocations(command: string): Map { return found; } -const generators = scriptInvocations(expandTask("generate")); +const generators = new Map(); +for (const unit of UNITS) { + for (const argv of unit.commands) { + const script = argv.find((arg) => arg.endsWith(".ts")); + if (script !== undefined) generators.set(script, argv.includes("--check")); + } +} const checks = scriptInvocations(expandTask("generate:manifests:check")); describe("generated artifact checks", () => { diff --git a/scripts/build/run-generate.test.ts b/scripts/build/run-generate.test.ts new file mode 100644 index 0000000000..b0f9bd6710 --- /dev/null +++ b/scripts/build/run-generate.test.ts @@ -0,0 +1,108 @@ +import { assertEquals, assertNotEquals } from "#veryfront/testing/assert.ts"; +import { describe, it } from "#veryfront/testing/bdd.ts"; +import { + digestEntries, + isFingerprintExcluded, + selectUnitsToRun, + UNITS, +} from "./run-generate.ts"; + +describe("isFingerprintExcluded", () => { + it("excludes generated outputs wherever they live", () => { + assertEquals(isFingerprintExcluded("templates/manifest.generated.ts"), true); + assertEquals( + isFingerprintExcluded("src/html/hydration-script-builder/hydration-runtime.generated.ts"), + true, + ); + }); + + it("excludes the outputs that do not follow the generated naming", () => { + assertEquals(isFingerprintExcluded("templates/manifest.json"), true); + assertEquals(isFingerprintExcluded("src/build/production-build/templates.ts"), true); + }); + + it("excludes test files and keeps ordinary sources", () => { + assertEquals(isFingerprintExcluded("src/utils/version.test.ts"), true); + assertEquals(isFingerprintExcluded("src/utils/version.ts"), false); + assertEquals(isFingerprintExcluded("templates/basic/deno.json"), false); + }); +}); + +describe("digestEntries", () => { + const a = { path: "a.ts", digest: "aaa" }; + const b = { path: "b.ts", digest: "bbb" }; + + it("is order-independent over entries", async () => { + assertEquals( + await digestEntries([a, b], "s"), + await digestEntries([b, a], "s"), + ); + }); + + it("changes when a content digest or the salt changes", async () => { + const base = await digestEntries([a, b], "s"); + assertNotEquals(base, await digestEntries([{ ...a, digest: "changed" }, b], "s")); + assertNotEquals(base, await digestEntries([a, b], "other-deno")); + }); +}); + +describe("selectUnitsToRun", () => { + it("selects stale and unstamped units, skips matching ones", () => { + const selected = selectUnitsToRun( + { fresh: "h1", stale: "h2", unstamped: "h3" }, + { fresh: "h1", stale: "old" }, + false, + ); + + assertEquals(selected.sort(), ["stale", "unstamped"]); + }); + + it("selects a stamped unit whose declared output is missing", () => { + assertEquals( + selectUnitsToRun({ a: "h" }, { a: "h" }, false, (name) => name === "a"), + ["a"], + ); + assertEquals( + selectUnitsToRun({ a: "h" }, { a: "h" }, false, () => false), + [], + ); + }); + + it("selects everything under force", () => { + assertEquals( + selectUnitsToRun({ a: "h" }, { a: "h" }, true), + ["a"], + ); + }); +}); + +describe("UNITS", () => { + it("covers the six generator steps of the stock chain", () => { + assertEquals(UNITS.map((u) => u.name).sort(), [ + "bridge", + "client-scripts", + "dev-ui", + "hydration-runtime", + "rsc-scripts", + "templates-manifest", + ]); + }); + + it("declares its own generator script as an input for every scripts/build unit", () => { + for (const unit of UNITS) { + const script = unit.commands[0][unit.commands[0].length - 1]; + if (script.startsWith("scripts/build/")) { + assertEquals(unit.inputFiles.includes(script), true, unit.name); + } + } + }); + + it("declares at least one output per unit, all excluded from fingerprints", () => { + for (const unit of UNITS) { + assertEquals(unit.outputs.length > 0, true, unit.name); + for (const output of unit.outputs) { + assertEquals(isFingerprintExcluded(output), true, output); + } + } + }); +}); diff --git a/scripts/build/run-generate.ts b/scripts/build/run-generate.ts new file mode 100644 index 0000000000..b9c654f3a3 --- /dev/null +++ b/scripts/build/run-generate.ts @@ -0,0 +1,317 @@ +#!/usr/bin/env -S deno run -A +/** + * Orchestrates the `generate` task: skip-when-unchanged, run-when-needed, + * and run independent generators concurrently. + * + * The stock chain ran six generator processes serially on every invocation, + * which put a multi-minute prefix in front of `deno task test` even when no + * input had changed. Each generator is deterministic over its inputs, so a + * generator whose inputs are byte-for-byte where they were after its last + * successful run cannot produce different output and is safe to skip. + * + * Inputs are fingerprinted by content digest (path + SHA-256 of bytes) over + * coarse input roots — a superset of what each generator actually reads. + * That direction of error is deliberate: an input-set superset can only + * over-trigger, never skip a needed run. The fingerprint also folds in the + * Deno version (bundler and gzip output change across versions) and + * deno.json (import map changes reach every bundle). A stamp is honored + * only while every declared output exists on disk — deleting a generated + * artifact forces its unit to rebuild. + * + * Generator OUTPUTS under the input roots are excluded from fingerprints — + * `*.generated.*` plus the two outputs that don't follow that naming + * (`templates/manifest.json`, `src/build/production-build/templates.ts`). + * Without this, a unit would invalidate itself by running. Test files are + * excluded too: no bundle imports them. + * + * Stamps live in `.cache/generate-stamps.json` (gitignored). CI checkouts + * are cold, so CI always runs everything, exactly as before. `--force` + * bypasses the stamps locally. + */ + +import { fromFileUrl } from "#std/path"; + +export interface GeneratorUnit { + name: string; + /** argv lists run sequentially within the unit. */ + commands: string[][]; + /** Directories whose files form the input fingerprint (coarse superset). */ + inputRoots: string[]; + /** Individual files folded into the fingerprint (the generator itself, config). */ + inputFiles: string[]; + /** Files the unit writes. A missing output forces a run, whatever the stamp says. */ + outputs: string[]; +} + +export const UNITS: GeneratorUnit[] = [ + { + name: "templates-manifest", + commands: [["deno", "run", "-A", "scripts/build/generate-templates-manifest.ts"]], + inputRoots: ["templates"], + inputFiles: ["scripts/build/generate-templates-manifest.ts", "deno.json"], + outputs: ["templates/manifest.json", "templates/manifest.generated.ts"], + }, + { + name: "dev-ui", + commands: [ + ["deno", "run", "-A", "extensions/ext-dev-ui-react/scripts/generate-styles.ts"], + ["deno", "run", "-A", "extensions/ext-dev-ui-react/scripts/prebundle.ts"], + ], + inputRoots: [ + "extensions/ext-dev-ui-react", + "extensions/ext-css-lightning", + "extensions/ext-css-tailwind", + "src", + ], + inputFiles: ["deno.json"], + outputs: [ + "extensions/ext-dev-ui-react/src/dev-ui-styles.generated.ts", + "extensions/ext-dev-ui-react/src/dev-ui-bundle.generated.ts", + ], + }, + { + name: "client-scripts", + commands: [["deno", "run", "-A", "scripts/build/prebundle-client-scripts.ts"]], + inputRoots: ["src", "extensions/ext-bundler-esbuild"], + inputFiles: ["scripts/build/prebundle-client-scripts.ts", "deno.json"], + outputs: [ + "src/build/production-build/templates.ts", + "src/server/handlers/dev/framework-candidates.generated.ts", + ], + }, + { + name: "bridge", + commands: [["deno", "run", "-A", "scripts/build/prebundle-bridge.ts"]], + inputRoots: ["src"], + inputFiles: ["scripts/build/prebundle-bridge.ts", "deno.json"], + outputs: ["src/studio/bridge/bridge-bundle.generated.ts"], + }, + { + name: "rsc-scripts", + commands: [["deno", "run", "-A", "scripts/build/prebundle-rsc-scripts.ts"]], + inputRoots: ["src"], + inputFiles: ["scripts/build/prebundle-rsc-scripts.ts", "deno.json"], + outputs: ["src/server/services/rsc/endpoints/rsc-bundles.generated.ts"], + }, + { + name: "hydration-runtime", + commands: [["deno", "run", "-A", "scripts/build/prebundle-hydration-runtime.ts"]], + inputRoots: ["src"], + inputFiles: ["scripts/build/prebundle-hydration-runtime.ts", "deno.json"], + outputs: ["src/html/hydration-script-builder/hydration-runtime.generated.ts"], + }, +]; + +/** Generator outputs that do not follow the `*.generated.*` naming. */ +const OUTPUT_FILES = new Set([ + "templates/manifest.json", + "src/build/production-build/templates.ts", +]); + +/** True for files that must not participate in an input fingerprint. */ +export function isFingerprintExcluded(relPath: string): boolean { + const base = relPath.slice(relPath.lastIndexOf("/") + 1); + if (base.includes(".generated.")) return true; + if (OUTPUT_FILES.has(relPath)) return true; + return base.endsWith(".test.ts") || base.endsWith(".test.tsx"); +} + +export interface FileEntry { + path: string; + /** Hex SHA-256 of the file's bytes — content, not metadata. */ + digest: string; +} + +/** + * A stable digest over file entries. Order-independent: entries are sorted + * by path before hashing, so directory-walk order cannot flip the hash. + * Entries carry content digests, so a same-length in-place edit — invisible + * to mtime/size fingerprints — still changes the unit hash. + */ +export async function digestEntries( + entries: readonly FileEntry[], + salt: string, +): Promise { + const sorted = [...entries].sort((a, b) => a.path.localeCompare(b.path)); + const text = salt + "\n" + + sorted.map((e) => `${e.path}|${e.digest}`).join("\n"); + return await sha256Hex(new TextEncoder().encode(text)); +} + +async function sha256Hex(bytes: Uint8Array): Promise { + const digest = await crypto.subtle.digest( + "SHA-256", + bytes as unknown as BufferSource, + ); + return Array.from(new Uint8Array(digest)) + .map((b) => b.toString(16).padStart(2, "0")).join(""); +} + +/** + * Units whose stamp is missing or stale, whose declared outputs are not all + * present on disk, or all of them under `force`. The output check keeps a + * stamp from vouching for artifacts that were deleted after it was written. + */ +export function selectUnitsToRun( + hashes: Record, + stamps: Record, + force: boolean, + missingOutputs: (name: string) => boolean = () => false, +): string[] { + return Object.keys(hashes).filter((name) => + force || stamps[name] !== hashes[name] || missingOutputs(name) + ); +} + +async function walkPaths( + repoRoot: string, + root: string, + out: string[], +): Promise { + let entries: AsyncIterable; + try { + entries = Deno.readDir(`${repoRoot}${root}`); + } catch { + return; // an input root may not exist in every checkout + } + for await (const entry of entries) { + const rel = `${root}/${entry.name}`; + if (entry.isDirectory) { + if (entry.name === "node_modules" || entry.name.startsWith(".")) continue; + await walkPaths(repoRoot, rel, out); + } else if (entry.isFile && !isFingerprintExcluded(rel)) { + out.push(rel); + } + } +} + +const HASH_CONCURRENCY = 64; + +/** Content-digest a set of files with bounded concurrency. */ +async function hashFiles( + repoRoot: string, + paths: readonly string[], +): Promise { + const out: FileEntry[] = []; + for (let i = 0; i < paths.length; i += HASH_CONCURRENCY) { + const batch = paths.slice(i, i + HASH_CONCURRENCY); + out.push(...await Promise.all(batch.map(async (path) => { + try { + return { path, digest: await sha256Hex(await Deno.readFile(`${repoRoot}${path}`)) }; + } catch { + // a missing declared input keeps the unit running every time + return { path, digest: "missing" }; + } + }))); + } + return out; +} + +const STAMP_PATH = ".cache/generate-stamps.json"; + +async function main(): Promise { + const repoRoot = fromFileUrl(new URL("../../", import.meta.url)); + const force = Deno.args.includes("--force"); + + const rootCache = new Map>(); + const entriesFor = (root: string): Promise => { + let cached = rootCache.get(root); + if (cached === undefined) { + cached = (async () => { + const paths: string[] = []; + await walkPaths(repoRoot, root, paths); + return await hashFiles(repoRoot, paths); + })(); + rootCache.set(root, cached); + } + return cached; + }; + + const salt = `deno=${Deno.version.deno}`; + const hashes: Record = {}; + for (const unit of UNITS) { + const entries: FileEntry[] = []; + for (const root of unit.inputRoots) entries.push(...await entriesFor(root)); + entries.push(...await hashFiles(repoRoot, unit.inputFiles)); + hashes[unit.name] = await digestEntries(entries, salt); + } + + let stamps: Record = {}; + try { + const parsed: unknown = JSON.parse( + await Deno.readTextFile(`${repoRoot}${STAMP_PATH}`), + ); + if ( + typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && + Object.values(parsed).every((v) => typeof v === "string") + ) { + stamps = parsed as Record; + } + } catch { + // no stamps yet (or an unreadable file): every unit runs + } + + const outputExists = (path: string): boolean => { + try { + Deno.statSync(`${repoRoot}${path}`); + return true; + } catch { + return false; + } + }; + const unitsByName = new Map(UNITS.map((u) => [u.name, u])); + const toRun = new Set(selectUnitsToRun( + hashes, + stamps, + force, + (name) => { + const missing = unitsByName.get(name)?.outputs.some((o) => !outputExists(o)) ?? false; + if (missing) console.log(`[generate] output missing, rebuilding: ${name}`); + return missing; + }, + )); + const skipped = UNITS.filter((u) => !toRun.has(u.name)).map((u) => u.name); + if (skipped.length > 0) { + console.log(`[generate] up to date, skipping: ${skipped.join(", ")}`); + } + if (toRun.size === 0) return; + + const results = await Promise.all( + UNITS.filter((u) => toRun.has(u.name)).map(async (unit) => { + for (const argv of unit.commands) { + const output = await new Deno.Command(argv[0], { + args: argv.slice(1), + cwd: repoRoot, + stdout: "piped", + stderr: "piped", + }).output(); + const text = new TextDecoder().decode(output.stdout) + + new TextDecoder().decode(output.stderr); + if (text.trim().length > 0) { + console.log(text.trimEnd().split("\n").map((l) => `[${unit.name}] ${l}`).join("\n")); + } + if (!output.success) return { name: unit.name, ok: false }; + } + return { name: unit.name, ok: true }; + }), + ); + + for (const result of results) { + if (result.ok) stamps[result.name] = hashes[result.name]; + } + await Deno.mkdir(`${repoRoot}.cache`, { recursive: true }); + await Deno.writeTextFile( + `${repoRoot}${STAMP_PATH}`, + JSON.stringify(stamps, null, 2) + "\n", + ); + + const failed = results.filter((r) => !r.ok).map((r) => r.name); + if (failed.length > 0) { + console.error(`[generate] FAILED: ${failed.join(", ")}`); + Deno.exit(1); + } +} + +if (import.meta.main) { + await main(); +} diff --git a/scripts/lint/anti-slop-baseline.json b/scripts/lint/anti-slop-baseline.json new file mode 100644 index 0000000000..a9d3647932 --- /dev/null +++ b/scripts/lint/anti-slop-baseline.json @@ -0,0 +1,100 @@ +{ + "no-chained-type-assertions": { + "extensions/ext-bundler-esbuild/src/esbuild-bundler.ts": 1, + "extensions/ext-redis/src/rate-limit-store.ts": 1, + "extensions/ext-redis/src/redis-client-manager.ts": 1, + "extensions/ext-redis/src/redis-runtime-provider.ts": 1, + "extensions/ext-redis/src/routing-invalidation-bus.ts": 1, + "extensions/ext-schema-zod/src/adapter.ts": 1, + "extensions/ext-schema-zod/src/json-schema.ts": 1, + "src/extensions/distributed/redis-runtime-provider.ts": 1, + "src/platform/adapters/fs/veryfront/adapter.test-helpers.ts": 1 + }, + "no-object-parameters": { + "extensions/ext-css-lightning/src/index.ts": 1, + "extensions/ext-css-purgecss/src/index.ts": 1, + "extensions/ext-image-sharp/src/sharp-runtime.ts": 22, + "extensions/ext-llm-anthropic/src/anthropic-request-builder.ts": 10, + "extensions/ext-llm-google/src/google-thought-signatures.ts": 1, + "extensions/ext-redis/src/event-publisher.ts": 1, + "extensions/ext-redis/src/redis-client-manager.ts": 1, + "extensions/ext-redis/src/redis-runtime-provider.ts": 2, + "extensions/ext-schema-zod/src/adapter.ts": 1, + "src/agent/conversation/private-run-event.ts": 1, + "src/agent/hosted/child-run-event-writer-token.ts": 3, + "src/agent/hosted/child-status.ts": 1, + "src/agent/runtime/error-utils.ts": 5, + "src/agent/runtime/index.ts": 3, + "src/agent/runtime/skill-prompt.ts": 1, + "src/build/asset-pipeline/css-optimizer/data-snapshot.ts": 1, + "src/build/asset-pipeline/css-optimizer/optimization-engine.ts": 1, + "src/build/asset-pipeline/image-optimizer/optimization-engine.ts": 2, + "src/cache/capabilities.ts": 2, + "src/chat/json-value.ts": 3, + "src/client/spa/path-utils.ts": 1, + "src/config/declarative-evaluator-worker-protocol.ts": 4, + "src/config/declarative-evaluator.ts": 7, + "src/config/loader.ts": 9, + "src/config/snapshot.ts": 8, + "src/errors/request-instance.ts": 1, + "src/errors/tenant-classification.ts": 1, + "src/errors/veryfront-error.ts": 2, + "src/extensions/distributed/redis-runtime-provider.ts": 3, + "src/extensions/entrypoint-identity.ts": 2, + "src/extensions/property-inspection.ts": 2, + "src/extensions/websocket/node-websocket-server-provider.ts": 3, + "src/html/client-head-manager.ts": 2, + "src/html/managed-head-protocol.ts": 1, + "src/html/styles-builder/css-hash-cache.ts": 1, + "src/html/styles-builder/prepared-project-css-cache.ts": 1, + "src/html/styles-builder/tailwind-compiler-utils.ts": 1, + "src/modules/import-map/preloader.ts": 2, + "src/oauth/state-utils.ts": 1, + "src/oauth/token-utils.ts": 2, + "src/observability/telemetry-error.ts": 2, + "src/observability/tracing/api-shim.ts": 1, + "src/observability/tracing/service-tracer.ts": 1, + "src/platform/adapters/base.ts": 1, + "src/platform/adapters/file-system-capabilities.ts": 4, + "src/platform/adapters/native-file-system-provenance.ts": 1, + "src/platform/adapters/runtime/node/http-server.ts": 3, + "src/platform/compat/error-introspection.ts": 3, + "src/platform/compat/fs.ts": 1, + "src/platform/compat/native-brand-checks.ts": 1, + "src/platform/compat/not-found-error.ts": 6, + "src/provider/runtime-loader/json-snapshot.ts": 3, + "src/proxy/retry.ts": 1, + "src/react/components/chat/chat/persistence/conversation-codec.ts": 5, + "src/react/components/ui/command.tsx": 4, + "src/react/components/ui/select.tsx": 16, + "src/registry/project-scoped-registry-manager.ts": 1, + "src/release-assets/manifest-schema.ts": 1, + "src/rendering/cache/cache-payload.ts": 2, + "src/rendering/element-validator/element-inspector.ts": 1, + "src/routing/api/response-normalization.ts": 1, + "src/runtime/runtime-bridge.ts": 2, + "src/schemas/json-value.ts": 1, + "src/schemas/lazy.ts": 2, + "src/security/sandbox/project-worker.ts": 1, + "src/security/sandbox/worker-script.ts": 1, + "src/security/secure-fs.ts": 2, + "src/server/project-env/snapshot.ts": 5, + "src/server/services/rsc/endpoints/action-authorization-snapshot.ts": 8, + "src/server/services/rsc/endpoints/action-handler.ts": 1, + "src/server/services/rsc/endpoints/action-parser.ts": 1, + "src/server/shared/browser-module-bundler.ts": 1, + "src/skill/document-parser.ts": 2, + "src/skill/executor.ts": 1, + "src/skill/id-admission.ts": 1, + "src/skill/tools.ts": 1, + "src/tool/data-properties.ts": 5, + "src/transforms/pipeline/cache-identity.ts": 2, + "src/trigger/target.ts": 2, + "src/utils/css-candidate-admission.ts": 1, + "src/utils/import-lockfile.ts": 3, + "src/utils/logger/redact.ts": 3, + "src/utils/logger/serialization.ts": 1, + "src/workflow/executor/workflow-definition-snapshot.ts": 5, + "templates/integrations/_base/files/lib/encrypted-token-store.ts": 2 + } +} diff --git a/scripts/lint/audit-anti-slop.test.ts b/scripts/lint/audit-anti-slop.test.ts new file mode 100644 index 0000000000..d152bc48fa --- /dev/null +++ b/scripts/lint/audit-anti-slop.test.ts @@ -0,0 +1,201 @@ +import { assertEquals, assertThrows } from "#veryfront/testing/assert.ts"; +import { describe, it } from "#veryfront/testing/bdd.ts"; +import { + compareBaseline, + countsByRuleAndFile, + findAntiSlop, + parseBaseline, + ParseFailure, + toRepoRelative, +} from "./audit-anti-slop.ts"; + +const rulesOf = (source: string, file = "a.ts") => + findAntiSlop(source, file).map((finding) => finding.rule); + +describe("no-chained-type-assertions", () => { + it("reports `as unknown as` once, at the outermost link", () => { + const findings = findAntiSlop( + `const value = input as unknown as { id: string };`, + "a.ts", + ); + + assertEquals(findings.length, 1); + assertEquals(findings[0]?.rule, "no-chained-type-assertions"); + assertEquals(findings[0]?.line, 1); + }); + + it("reports a parenthesized chain", () => { + assertEquals(rulesOf(`const value = (input as never) as string;`), [ + "no-chained-type-assertions", + ]); + }); + + it("reports a three-link chain once", () => { + assertEquals( + rulesOf(`const value = input as unknown as object as string;`).length, + 1, + ); + }); + + it("allows a single assertion", () => { + assertEquals(rulesOf(`const value = input as string;`), []); + }); + + it("allows an all-const chain", () => { + assertEquals(rulesOf(`const value = ([1, 2] as const) as const;`), []); + }); + + it("reports a chain that mixes const with a real assertion", () => { + assertEquals( + rulesOf(`const value = [1, 2] as const as readonly unknown[];`), + ["no-chained-type-assertions"], + ); + }); + + it("reports angle-bracket chains in .ts sources", () => { + assertEquals(rulesOf(`const value = input;`), [ + "no-chained-type-assertions", + ]); + }); +}); + +describe("no-unknown-type-aliases", () => { + it("reports a direct alias to unknown", () => { + const findings = findAntiSlop(`type Payload = unknown;`, "a.ts"); + + assertEquals(findings.length, 1); + assertEquals(findings[0]?.rule, "no-unknown-type-aliases"); + assertEquals(findings[0]?.detail, "Payload"); + }); + + it("reports an exported alias chain that resolves to unknown", () => { + assertEquals( + rulesOf(`type Inner = unknown;\nexport type Outer = Inner;`).length, + 2, + ); + }); + + it("allows aliases to concrete types and self-referential cycles", () => { + assertEquals(rulesOf(`type Payload = { id: string };`), []); + assertEquals(rulesOf(`type Loop = Loop;`), []); + }); + + it("allows generic aliases and references with type arguments", () => { + assertEquals(rulesOf(`type Wrap = T;\ntype Value = Wrap;`), []); + }); +}); + +describe("no-object-parameters", () => { + it("reports a parameter annotated with the broad object type", () => { + const findings = findAntiSlop( + `function inspect(value: object): void {}`, + "a.ts", + ); + + assertEquals(findings.length, 1); + assertEquals(findings[0]?.rule, "no-object-parameters"); + assertEquals(findings[0]?.detail, "value"); + }); + + it("reports object inside a union and on arrow/method/type signatures", () => { + assertEquals(rulesOf(`const f = (value: object | null) => value;`), [ + "no-object-parameters", + ]); + assertEquals(rulesOf(`interface I { handle(value: object): void; }`), [ + "no-object-parameters", + ]); + assertEquals(rulesOf(`type Fn = (value: object) => void;`), [ + "no-object-parameters", + ]); + }); + + it("flags defaulted parameters but not object-array rest parameters", () => { + // `object[]` is an array type, not the broad `object` keyword. + assertEquals(rulesOf(`function f(...values: object[]): void {}`), []); + assertEquals(rulesOf(`function f(value: object = {}): void {}`), [ + "no-object-parameters", + ]); + }); + + it("allows named types, Record, and unannotated parameters", () => { + assertEquals( + rulesOf( + `function f(a: { id: string }, b: Record, c) {}`, + ), + [], + ); + }); +}); + +describe("findAntiSlop parsing", () => { + it("parses JSX in .tsx sources", () => { + assertEquals( + rulesOf(`export const El = () =>
{x as unknown as string}
;`, "a.tsx"), + ["no-chained-type-assertions"], + ); + }); + + it("fails closed on unparsable sources", () => { + assertThrows(() => findAntiSlop(`const = ;`, "a.ts"), ParseFailure); + }); + + it("ignores violations spelled inside comments", () => { + assertEquals(rulesOf(`// const v = x as unknown as string;`), []); + }); +}); + +describe("baseline mechanics", () => { + it("counts findings per rule per file, sorted", () => { + const counts = countsByRuleAndFile([ + { rule: "no-object-parameters", file: "b.ts", line: 1, detail: "v" }, + { rule: "no-chained-type-assertions", file: "b.ts", line: 2, detail: "2" }, + { rule: "no-chained-type-assertions", file: "a.ts", line: 3, detail: "2" }, + { rule: "no-chained-type-assertions", file: "a.ts", line: 9, detail: "2" }, + ]); + + assertEquals(counts, { + "no-chained-type-assertions": { "a.ts": 2, "b.ts": 1 }, + "no-object-parameters": { "b.ts": 1 }, + }); + }); + + it("flags per-file growth as a regression even for a known file", () => { + const { regressions, improvements } = compareBaseline( + { "no-chained-type-assertions": { "a.ts": 3 } }, + { "no-chained-type-assertions": { "a.ts": 2 } }, + ); + + assertEquals(regressions, ["no-chained-type-assertions a.ts: 2 -> 3"]); + assertEquals(improvements, []); + }); + + it("flags shrinkage and disappearance as improvements", () => { + const { regressions, improvements } = compareBaseline( + {}, + { "no-object-parameters": { "a.ts": 1 } }, + ); + + assertEquals(regressions, []); + assertEquals(improvements, ["no-object-parameters a.ts: 1 -> 0"]); + }); + + it("flags a new rule entry for an unlisted file as a regression", () => { + const { regressions } = compareBaseline( + { "no-unknown-type-aliases": { "new.ts": 1 } }, + {}, + ); + + assertEquals(regressions, ["no-unknown-type-aliases new.ts: 0 -> 1"]); + }); + + it("rejects malformed baselines", () => { + assertThrows(() => parseBaseline([], "p")); + assertThrows(() => parseBaseline({ rule: 1 }, "p")); + assertThrows(() => parseBaseline({ rule: { "a.ts": 0 } }, "p")); + assertThrows(() => parseBaseline({ rule: { "a.ts": 1.5 } }, "p")); + }); + + it("normalises baseline keys to posix repo-relative paths", () => { + assertEquals(toRepoRelative("/repo/src\\x.ts", "/repo/"), "src/x.ts"); + }); +}); diff --git a/scripts/lint/audit-anti-slop.ts b/scripts/lint/audit-anti-slop.ts new file mode 100644 index 0000000000..0928816945 --- /dev/null +++ b/scripts/lint/audit-anti-slop.ts @@ -0,0 +1,519 @@ +#!/usr/bin/env -S deno run --allow-read +/** + * Ratchet on low-evidence type patterns — the assertions and broad types + * that make code look typed while the compiler has been told to stop + * checking. `deno lint` has no baseline mechanism, so these checks live here + * instead of as lint rules, enforced through the per-file ratchet this + * directory already uses for cwd-relative test reads. + * + * Candidate patterns that were measured against `src/` and deliberately NOT + * enforced: + * + * - ad hoc `typeof` narrowing, `unknown` parameters, and mandatory safety + * comments on assertions: thousands of hits each. Cross-runtime code + * (`typeof Deno`, `typeof process`) and untyped I/O boundaries are how + * this codebase works, not slop. + * - `Reflect.apply` / `Reflect.get`: `Reflect.*` here is deliberate typed + * dispatch in sandbox/proxy code (e.g. `src/proxy/cache/validation.ts`). + * - `*Shape` symbol names: an established naming convention in this repo + * (`SleepToolInputShape`, `ContractSchemaShape`). + * + * ## The enforced rules + * + * - `no-chained-type-assertions` — `x as unknown as Y` (and any nested + * assertion chain that is not all-`const`) fabricates type evidence: the + * compiler is told to forget what it knew and then told something new + * with no proof. Parse or narrow at the boundary instead. + * - `no-unknown-type-aliases` — `type Foo = unknown` (directly or through + * alias chains) hides the fact that a value is unparsed. `unknown` must + * stay visible where it exists. + * - `no-object-parameters` — a parameter typed `object` (including union + * members) accepts nearly anything while promising nothing; take a named + * domain type instead. Local aliases to `object` are not resolved — + * direct annotations only, which keeps the check free of scope analysis. + * + * Test files (`*.test.ts(x)`) are not scanned: `as unknown as` is the + * idiomatic way to build partial doubles in tests, and banning it there + * fights ~1300 existing sites for little signal. + * + * Counts are frozen per rule per file in `anti-slop-baseline.json` and may + * only shrink. Regenerate after paying debt down with: + * + * deno task lint:anti-slop -- --print-baseline > scripts/lint/anti-slop-baseline.json + */ + +import { parse } from "npm:@babel/parser@7.29.2"; +import { fromFileUrl } from "#std/path"; + +export type AntiSlopRule = + | "no-chained-type-assertions" + | "no-unknown-type-aliases" + | "no-object-parameters"; + +export interface AntiSlopFinding { + rule: AntiSlopRule; + file: string; + line: number; + /** What was flagged: the alias name, the parameter name, or a chain label. */ + detail: string; +} + +interface Node { + type: string; + loc?: { start: { line: number } }; + [key: string]: unknown; +} + +function isNode(value: unknown): value is Node { + return typeof value === "object" && value !== null && + typeof (value as { type?: unknown }).type === "string"; +} + +/** + * Attached comments carry a `type` too, so the walk would descend into them. + * Nothing in a comment can violate these rules, and skipping them makes that + * structural rather than incidental. + */ +const COMMENT_KEYS = new Set([ + "leadingComments", + "trailingComments", + "innerComments", + "comments", +]); + +const ASSERTION_TYPES = new Set(["TSAsExpression", "TSTypeAssertion"]); + +/** `x as const` — the one assertion form that adds evidence instead of discarding it. */ +function isConstAssertion(node: Node): boolean { + const annotation = node.typeAnnotation; + if (!isNode(annotation) || annotation.type !== "TSTypeReference") { + return false; + } + const name = annotation.typeName; + return isNode(name) && name.type === "Identifier" && name.name === "const"; +} + +/** + * Walk `.expression` through a nested assertion chain. Babel keeps + * parentheses transparent, so `(x as A) as B` nests directly. + */ +function assertionChain(node: Node): { length: number; hasNonConst: boolean } { + let length = 0; + let hasNonConst = false; + let current: unknown = node; + while (isNode(current) && ASSERTION_TYPES.has(current.type)) { + length += 1; + if (!isConstAssertion(current)) hasNonConst = true; + current = current.expression; + } + return { length, hasNonConst }; +} + +/** Node types that own a function parameter list, under either babel key. */ +const PARAMETER_OWNERS = new Set([ + "FunctionDeclaration", + "FunctionExpression", + "ArrowFunctionExpression", + "ObjectMethod", + "ClassMethod", + "ClassPrivateMethod", + "TSDeclareFunction", + "TSDeclareMethod", + "TSFunctionType", + "TSConstructorType", + "TSMethodSignature", + "TSCallSignatureDeclaration", + "TSConstructSignatureDeclaration", +]); + +/** Plain functions store `params`; TS signature nodes store `parameters`. */ +function parametersOf(node: Node): Node[] { + const raw = node.params ?? node.parameters; + return Array.isArray(raw) ? raw.filter(isNode) : []; +} + +function parameterAnnotation(parameter: Node): Node | undefined { + if (parameter.type === "TSParameterProperty") { + return isNode(parameter.parameter) + ? parameterAnnotation(parameter.parameter) + : undefined; + } + if (parameter.type === "AssignmentPattern") { + const own = parameter.typeAnnotation; + if (isNode(own)) return own; + return isNode(parameter.left) + ? parameterAnnotation(parameter.left) + : undefined; + } + const annotation = parameter.typeAnnotation; + if (isNode(annotation)) return annotation; + if (parameter.type === "RestElement" && isNode(parameter.argument)) { + return parameterAnnotation(parameter.argument); + } + return undefined; +} + +function parameterName(parameter: Node): string { + if (parameter.type === "Identifier") return parameter.name as string; + if (parameter.type === "RestElement" && isNode(parameter.argument)) { + return `...${parameterName(parameter.argument)}`; + } + if (parameter.type === "AssignmentPattern" && isNode(parameter.left)) { + return parameterName(parameter.left); + } + if (parameter.type === "TSParameterProperty" && isNode(parameter.parameter)) { + return parameterName(parameter.parameter); + } + return `<${parameter.type}>`; +} + +/** The broad `object` keyword, directly or as a union member. */ +function isBroadObjectType(type: Node): boolean { + if (type.type === "TSObjectKeyword") return true; + if (type.type === "TSUnionType" && Array.isArray(type.types)) { + return type.types.some((member) => isNode(member) && isBroadObjectType(member)); + } + return false; +} + +/** Top-level type aliases by name, including `export type` forms. */ +function collectTopLevelAliases(program: Node): Map { + const aliases = new Map(); + const body = Array.isArray(program.body) ? program.body : []; + for (const statement of body) { + if (!isNode(statement)) continue; + const declaration = statement.type === "ExportNamedDeclaration" + ? statement.declaration + : statement; + if (isNode(declaration) && declaration.type === "TSTypeAliasDeclaration") { + const id = declaration.id; + if (isNode(id) && typeof id.name === "string") { + aliases.set(id.name, declaration); + } + } + } + return aliases; +} + +function resolvesToUnknown( + type: Node, + aliases: Map, + visited: Set, +): boolean { + if (type.type === "TSUnknownKeyword") return true; + if (type.type !== "TSTypeReference") return false; + const name = type.typeName; + if (!isNode(name) || name.type !== "Identifier") return false; + // A reference with type arguments is not a bare alias to `unknown`. + const args = type.typeParameters ?? type.typeArguments; + if (isNode(args) && Array.isArray(args.params) && args.params.length > 0) { + return false; + } + const aliasName = name.name as string; + if (visited.has(aliasName)) return false; + const alias = aliases.get(aliasName); + // Generic aliases would need instantiation to resolve; leave them alone. + if (alias === undefined || isNode(alias.typeParameters)) return false; + const annotation = alias.typeAnnotation; + if (!isNode(annotation)) return false; + return resolvesToUnknown( + annotation, + aliases, + new Set(visited).add(aliasName), + ); +} + +/** Raised when a scanned file cannot be parsed, so the audit fails closed. */ +export class ParseFailure extends Error {} + +/** + * Report every anti-slop violation in `source`. + * + * `.ts` sources are parsed without the JSX plugin so angle-bracket type + * assertions (`value`) parse as assertions rather than as JSX. + */ +export function findAntiSlop(source: string, file: string): AntiSlopFinding[] { + let ast; + try { + ast = parse(source, { + sourceType: "module", + allowAwaitOutsideFunction: true, + allowReturnOutsideFunction: true, + errorRecovery: false, + plugins: file.endsWith(".tsx") + ? ["typescript", "jsx", "decorators-legacy", "importAttributes"] + : ["typescript", "decorators-legacy", "importAttributes"], + }); + } catch (error) { + throw new ParseFailure( + `${file}: ${error instanceof Error ? error.message : String(error)}`, + ); + } + + const program = ast.program as unknown as Node; + const findings: AntiSlopFinding[] = []; + + const aliases = collectTopLevelAliases(program); + for (const [name, alias] of aliases) { + const annotation = alias.typeAnnotation; + if (!isNode(annotation)) continue; + if (!resolvesToUnknown(annotation, aliases, new Set([name]))) continue; + findings.push({ + rule: "no-unknown-type-aliases", + file, + line: alias.loc?.start.line ?? 0, + detail: name, + }); + } + + /** + * `underAssertion` marks a node reached through an assertion's + * `.expression`, so only the outermost link of a chain reports. + */ + const visit = (node: Node, underAssertion: boolean): void => { + if (ASSERTION_TYPES.has(node.type) && !underAssertion) { + const { length, hasNonConst } = assertionChain(node); + if (length > 1 && hasNonConst) { + findings.push({ + rule: "no-chained-type-assertions", + file, + line: node.loc?.start.line ?? 0, + detail: `${length} chained assertions`, + }); + } + } + + if (PARAMETER_OWNERS.has(node.type)) { + for (const parameter of parametersOf(node)) { + const annotation = parameterAnnotation(parameter); + const type = annotation === undefined + ? undefined + : annotation.typeAnnotation; + if (!isNode(type) || !isBroadObjectType(type)) continue; + findings.push({ + rule: "no-object-parameters", + file, + line: (isNode(annotation) ? annotation.loc?.start.line : undefined) ?? + 0, + detail: parameterName(parameter), + }); + } + } + + for (const key of Object.keys(node)) { + if (key === "loc" || COMMENT_KEYS.has(key)) continue; + const value = node[key]; + const intoExpression = ASSERTION_TYPES.has(node.type) && + key === "expression"; + if (Array.isArray(value)) { + for (const item of value) if (isNode(item)) visit(item, false); + } else if (isNode(value)) { + visit(value, intoExpression); + } + } + }; + + visit(program, false); + return findings.sort((a, b) => a.line - b.line); +} + +/** Per-rule, per-file counts — the shape stored in the baseline. */ +export type AntiSlopBaseline = Record>; + +export function countsByRuleAndFile( + findings: readonly AntiSlopFinding[], +): AntiSlopBaseline { + const counts: Record> = {}; + for (const finding of findings) { + const perFile = counts[finding.rule] ?? (counts[finding.rule] = {}); + perFile[finding.file] = (perFile[finding.file] ?? 0) + 1; + } + const sorted: AntiSlopBaseline = {}; + for (const rule of Object.keys(counts).sort()) { + sorted[rule] = Object.fromEntries( + Object.entries(counts[rule]).sort(([a], [b]) => a.localeCompare(b)), + ); + } + return sorted; +} + +export interface BaselineComparison { + /** `rule file: then -> now` where a count grew. The ratchet slipping. */ + regressions: string[]; + /** Where a count shrank. The ratchet earning a new floor. */ + improvements: string[]; +} + +/** + * Compare current counts with the frozen baseline, per rule per file: a file + * already carrying two chained assertions must not quietly grow a third. + */ +export function compareBaseline( + current: AntiSlopBaseline, + baseline: AntiSlopBaseline, +): BaselineComparison { + const regressions: string[] = []; + const improvements: string[] = []; + const rules = new Set([...Object.keys(current), ...Object.keys(baseline)]); + for (const rule of rules) { + const now = current[rule] ?? {}; + const then = baseline[rule] ?? {}; + for (const file of new Set([...Object.keys(now), ...Object.keys(then)])) { + const a = now[file] ?? 0; + const b = then[file] ?? 0; + if (a > b) regressions.push(`${rule} ${file}: ${b} -> ${a}`); + else if (a < b) improvements.push(`${rule} ${file}: ${b} -> ${a}`); + } + } + return { regressions: regressions.sort(), improvements: improvements.sort() }; +} + +export function parseBaseline(value: unknown, path: string): AntiSlopBaseline { + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error(`Invalid anti-slop baseline: ${path}`); + } + for (const [rule, files] of Object.entries(value)) { + if (typeof files !== "object" || files === null || Array.isArray(files)) { + throw new Error(`Invalid anti-slop baseline entry for ${rule}: ${path}`); + } + for (const [file, count] of Object.entries(files)) { + if (typeof count !== "number" || !Number.isInteger(count) || count < 1) { + throw new Error( + `Invalid anti-slop baseline count for ${rule} ${file}: ${count}`, + ); + } + } + } + return value as AntiSlopBaseline; +} + +/** + * Baseline key for a scanned file: repo-relative, always posix separators, + * so a Windows checkout produces the same keys a Linux one does. + */ +export function toRepoRelative(file: string, repoRoot: string): string { + return file.slice(repoRoot.length).replaceAll("\\", "/"); +} + +const BASELINE_PATH = "scripts/lint/anti-slop-baseline.json"; +/** Mirrors the production surface of `lint.include` in deno.json. */ +const SCAN_ROOTS = ["src", "cli", "templates", "extensions", "react"] as const; +/** Mirrors `lint.exclude` in deno.json. */ +const EXCLUDED_PREFIXES = ["src/studio/bridge/"] as const; + +function isProdSource(name: string): boolean { + if (!name.endsWith(".ts") && !name.endsWith(".tsx")) return false; + if (name.endsWith(".test.ts") || name.endsWith(".test.tsx")) return false; + return !name.endsWith(".d.ts"); +} + +async function collectProdFiles(root: string): Promise { + const files: string[] = []; + let entries: AsyncIterable; + try { + entries = Deno.readDir(root); + } catch { + return files; // expected: a scan root may not exist in every checkout + } + for await (const entry of entries) { + const path = `${root}/${entry.name}`; + if (entry.isDirectory) { + if (entry.name === "node_modules" || entry.name.startsWith(".")) continue; + // Mirrors lint.exclude: emitted output inside a scan root is not source. + if (entry.name === "dist" || entry.name === "coverage") continue; + files.push(...await collectProdFiles(path)); + } else if (entry.isFile && isProdSource(entry.name)) { + files.push(path); + } + } + return files; +} + +function printFindings(title: string, findings: readonly string[]): void { + if (findings.length === 0) return; + console.error(`\n${title}`); + for (const finding of findings) console.error(` ${finding}`); +} + +async function main(): Promise { + // `fromFileUrl`, not `URL.pathname`: pathname keeps the URL's leading slash + // and percent encoding, so a Windows checkout would scan `/C:/...`. + const repoRoot = fromFileUrl(new URL("../../", import.meta.url)); + const findings: AntiSlopFinding[] = []; + const parseFailures: string[] = []; + + for (const root of SCAN_ROOTS) { + for (const file of await collectProdFiles(`${repoRoot}${root}`)) { + const relative = toRepoRelative(file, repoRoot); + if (EXCLUDED_PREFIXES.some((prefix) => relative.startsWith(prefix))) { + continue; + } + try { + findings.push(...findAntiSlop(await Deno.readTextFile(file), relative)); + } catch (error) { + parseFailures.push( + error instanceof Error ? error.message : String(error), + ); + } + } + } + + const current = countsByRuleAndFile(findings); + if (Deno.args.includes("--print-baseline")) { + console.log(JSON.stringify(current, null, 2)); + return; + } + + const baseline = parseBaseline( + JSON.parse(await Deno.readTextFile(`${repoRoot}${BASELINE_PATH}`)), + BASELINE_PATH, + ); + const { regressions, improvements } = compareBaseline(current, baseline); + + printFindings("Files that could not be parsed:", parseFailures); + if (regressions.length > 0) { + const regressedFiles = new Set( + regressions.map((entry) => entry.split(" ")[1]?.replace(/:$/, "")), + ); + printFindings( + "Anti-slop counts above the baseline (new low-evidence type patterns):", + regressions, + ); + printFindings( + "Current findings in the regressed files:", + findings + .filter((finding) => regressedFiles.has(finding.file)) + .map((finding) => + `${finding.file}:${finding.line} ${finding.rule} (${finding.detail})` + ), + ); + } + + if (parseFailures.length > 0 || regressions.length > 0) { + console.error( + `\nKeep the precise type or parse at the boundary instead of asserting ` + + `through it — see the header of scripts/lint/audit-anti-slop.ts. ` + + `Do not raise ${BASELINE_PATH} for new violations.`, + ); + Deno.exit(1); + } + + if (improvements.length > 0) { + printFindings("Anti-slop debt decreased:", improvements); + console.log( + `\nRegenerate ${BASELINE_PATH} with ` + + `\`deno task lint:anti-slop -- --print-baseline > ${BASELINE_PATH}\` to lock in the improvement.`, + ); + return; + } + + const total = findings.length; + const fileCount = new Set(findings.map((finding) => finding.file)).size; + console.log( + `Anti-slop baseline ok: ${total} baselined finding(s) across ${fileCount} file(s).`, + ); +} + +if (import.meta.main) { + await main(); +} diff --git a/src/agent/conversation/lifecycle-run-event-adapter.ts b/src/agent/conversation/lifecycle-run-event-adapter.ts index b65b643bd0..53bada7601 100644 --- a/src/agent/conversation/lifecycle-run-event-adapter.ts +++ b/src/agent/conversation/lifecycle-run-event-adapter.ts @@ -48,15 +48,9 @@ export function createLifecycleRunEventAdapter(input: { DEFAULT_MAX_BUFFERED_CONTENT_BYTES; const flushDelayMs = input.flushDelayMs ?? DEFAULT_FLUSH_DELAY_MS; const setTimer = input.setTimer ?? - ((callback: () => void, delayMs: number) => - globalThis.setTimeout(callback, delayMs) as unknown as number); + ((callback: () => void, delayMs: number) => globalThis.setTimeout(callback, delayMs)); const clearTimer = input.clearTimer ?? - ((timerId: number) => - globalThis.clearTimeout( - timerId as unknown as ReturnType< - typeof globalThis.setTimeout - >, - )); + ((timerId: number) => globalThis.clearTimeout(timerId)); let logicalSequence = 0; let pending: PendingDurableContent | null = null; diff --git a/src/agent/memory/memory.ts b/src/agent/memory/memory.ts index 7da037dadc..3bd9312935 100644 --- a/src/agent/memory/memory.ts +++ b/src/agent/memory/memory.ts @@ -196,19 +196,20 @@ export class SummaryMemory implements () => { if (!this.summary) return [...this.messages]; - const summaryMessage = { + const summaryParts = [ + { + type: "text", + text: `${SUMMARY_MESSAGE_PREFIX}${this.summary}`, + }, + ]; + const summaryMessage: MinimalMessage = { id: "summary", - role: "system" as const, - parts: [ - { - type: "text" as const, - text: `${SUMMARY_MESSAGE_PREFIX}${this.summary}`, - }, - ], + role: "system", + parts: summaryParts, timestamp: Date.now(), - } as unknown as M; + }; - return [summaryMessage, ...this.messages]; + return [summaryMessage as M, ...this.messages]; }, { "memory.type": "summary", "memory.has_summary": !!this.summary }, ), diff --git a/src/agent/react/use-voice-input.ts b/src/agent/react/use-voice-input.ts index 4a884a03a3..7845b37dc0 100644 --- a/src/agent/react/use-voice-input.ts +++ b/src/agent/react/use-voice-input.ts @@ -120,14 +120,14 @@ export function useVoiceInput( const isSupported = React.useMemo((): boolean => { if (typeof globalThis === "undefined") return false; - const g = globalThis as unknown as GlobalWithSpeechRecognition; + const g = globalThis as GlobalWithSpeechRecognition; return Boolean(g.SpeechRecognition ?? g.webkitSpeechRecognition); }, []); React.useEffect(() => { if (!isSupported) return; - const g = globalThis as unknown as GlobalWithSpeechRecognition; + const g = globalThis as GlobalWithSpeechRecognition; const SpeechRecognitionAPI = g.SpeechRecognition ?? g.webkitSpeechRecognition; if (!SpeechRecognitionAPI) return; diff --git a/src/agent/runtime/chat-stream-handler.test-helpers.ts b/src/agent/runtime/chat-stream-handler.test-helpers.ts index 14f50cba5f..e34bab5f05 100644 --- a/src/agent/runtime/chat-stream-handler.test-helpers.ts +++ b/src/agent/runtime/chat-stream-handler.test-helpers.ts @@ -14,7 +14,7 @@ export function createSSECollector() { events.push(JSON.parse(line.slice(6))); } }, - } as unknown as ReadableStreamDefaultController; + } as ReadableStreamDefaultController; return { events, controller, encoder }; } diff --git a/src/agent/runtime/error-utils.ts b/src/agent/runtime/error-utils.ts index 979c5ddf7c..4fccdafd8d 100644 --- a/src/agent/runtime/error-utils.ts +++ b/src/agent/runtime/error-utils.ts @@ -40,7 +40,7 @@ const MAX_BEST_EFFORT_DEPTH = 8; const MAX_BEST_EFFORT_NODES = 256; const OMIT_DIAGNOSTIC_VALUE = Symbol("omit-diagnostic-value"); -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return apply(objectHasOwnProperty, object, [key]) as boolean; } @@ -169,7 +169,7 @@ function inspectOwnDescriptor( } function defineDiagnosticProperty( - target: object, + target: BestEffortDiagnosticValue[] | Record, key: PropertyKey, value: BestEffortDiagnosticValue, ): void { @@ -181,7 +181,7 @@ function defineDiagnosticProperty( }); } -function defineDiagnosticSerializationGuard(target: object): void { +function defineDiagnosticSerializationGuard(target: BestEffortDiagnosticValue[]): void { objectDefineProperty(target, "toJSON", { configurable: false, enumerable: false, diff --git a/src/agent/runtime/project-skill-loader.ts b/src/agent/runtime/project-skill-loader.ts index a1a1ac3033..612f6d0daa 100644 --- a/src/agent/runtime/project-skill-loader.ts +++ b/src/agent/runtime/project-skill-loader.ts @@ -42,7 +42,7 @@ const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; const ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty; const ReflectApply = Reflect.apply; -function hasOwnProperty(value: object, key: PropertyKey): boolean { +function hasOwnProperty(value: Readonly>, key: PropertyKey): boolean { return ReflectApply(ObjectPrototypeHasOwnProperty, value, [key]) as boolean; } diff --git a/src/agent/runtime/skill-metadata.ts b/src/agent/runtime/skill-metadata.ts index d0be65bba1..54f03291d5 100644 --- a/src/agent/runtime/skill-metadata.ts +++ b/src/agent/runtime/skill-metadata.ts @@ -48,7 +48,7 @@ const ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty; const ReflectApply = Reflect.apply; const ReflectOwnKeys = Reflect.ownKeys; -function hasOwnProperty(value: object, key: PropertyKey): boolean { +function hasOwnProperty(value: Record, key: PropertyKey): boolean { return ReflectApply(ObjectPrototypeHasOwnProperty, value, [key]) as boolean; } diff --git a/src/agent/runtime/skill-prompt.ts b/src/agent/runtime/skill-prompt.ts index 3500789d7a..590e5cde5f 100644 --- a/src/agent/runtime/skill-prompt.ts +++ b/src/agent/runtime/skill-prompt.ts @@ -52,7 +52,7 @@ if (typeof mapIteratorNext !== "function") { throw new NativeError("Map iterator next intrinsic is unavailable"); } -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor | Record, key: PropertyKey): boolean { return apply(hasOwnProperty, value, [key]) as boolean; } diff --git a/src/agent/streaming/lifecycle/testing.ts b/src/agent/streaming/lifecycle/testing.ts index 84abd5bfae..e101ab7e83 100644 --- a/src/agent/streaming/lifecycle/testing.ts +++ b/src/agent/streaming/lifecycle/testing.ts @@ -186,7 +186,7 @@ export function createScriptedStreamProvider( }; }, decode(part: T, _snapshot: Readonly): readonly StreamSignal[] { - return [part as unknown as StreamSignal]; + return [part as StreamSignal]; }, classifyError(): StreamProviderError { return { diff --git a/src/channels/control-plane.ts b/src/channels/control-plane.ts index 4baa829a18..90670c82ae 100644 --- a/src/channels/control-plane.ts +++ b/src/channels/control-plane.ts @@ -492,7 +492,7 @@ function requireSignedRequestBinding( } } -function readExpectedRequestBinding(options: object): { +function readExpectedRequestBinding(options: { requestMethod: string; requestPath: string }): { method: string; path: string; } { @@ -784,7 +784,7 @@ export function getRuntimeAgentPublicMetadata( id: string, agent: Agent, ): RuntimeAgentPublicMetadata { - const rawConfig = agent.config as unknown as Record; + const rawConfig = agent.config; const suggestionsParseResult = rawConfig.suggestions === undefined ? null : RuntimeSuggestionsSchema.safeParse( @@ -809,7 +809,7 @@ export function getRuntimeAgentPublicMetadata( } function getRuntimeAgentMetadata(id: string, agent: Agent): RuntimeAgent { - const rawConfig = agent.config as unknown as Record; + const rawConfig = agent.config as { version?: unknown }; const publicMetadata = getRuntimeAgentPublicMetadata(id, agent); return RuntimeAgentSchema.parse({ diff --git a/src/client/spa/component-loader.ts b/src/client/spa/component-loader.ts index ea2892c1ec..0d19ca7510 100644 --- a/src/client/spa/component-loader.ts +++ b/src/client/spa/component-loader.ts @@ -198,7 +198,11 @@ export function clearComponentCache(): void { // Expose component loader globally for hydration scripts if (typeof window !== "undefined") { - const global = window as unknown as Record; + const global = window as { + __VERYFRONT_LOAD_COMPONENT__?: typeof loadComponent; + __VERYFRONT_PRELOAD_COMPONENT__?: typeof preloadComponent; + __VERYFRONT_GET_CACHED_COMPONENT__?: typeof getCachedComponent; + }; global.__VERYFRONT_LOAD_COMPONENT__ = loadComponent; global.__VERYFRONT_PRELOAD_COMPONENT__ = preloadComponent; global.__VERYFRONT_GET_CACHED_COMPONENT__ = getCachedComponent; diff --git a/src/config/declarative-evaluator-worker-protocol.ts b/src/config/declarative-evaluator-worker-protocol.ts index 276caddfd1..f49281150b 100644 --- a/src/config/declarative-evaluator-worker-protocol.ts +++ b/src/config/declarative-evaluator-worker-protocol.ts @@ -182,12 +182,12 @@ const ERROR_REASON_TABLE = ObjectFreeze( } as const satisfies Readonly>, ); -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return ReflectApply(ObjectPrototypeHasOwnProperty, value, [key]) as boolean; } function defineDataProperty( - target: object, + target: Record, key: PropertyKey, value: unknown, ): void { @@ -318,7 +318,10 @@ function captureStringMap(value: unknown): Readonly> { return ObjectFreeze(captured); } -function isKnownEnumValue(value: unknown, table: object): value is string { +function isKnownEnumValue( + value: unknown, + table: Readonly>, +): value is string { if (typeof value !== "string") return false; let descriptor: PropertyDescriptor | undefined; try { diff --git a/src/config/loader.ts b/src/config/loader.ts index 3f68218958..0b68f17565 100644 --- a/src/config/loader.ts +++ b/src/config/loader.ts @@ -902,9 +902,7 @@ function createHostedConfigSourceReadFlight( // Register the deferred operation in the caller's async context now. A // queued multi-project read must not inherit the request context of whichever // earlier flight later releases capacity. - const promise = thenPromise(start.promise, operation) as unknown as Promise< - HostedConfigSourceSelection | null - >; + const promise = thenPromise(start.promise, operation); const flight: HostedConfigSourceReadFlight = { key, start, diff --git a/src/errors/safe-diagnostics.ts b/src/errors/safe-diagnostics.ts index 40546ce2e2..1bc72877c8 100644 --- a/src/errors/safe-diagnostics.ts +++ b/src/errors/safe-diagnostics.ts @@ -108,7 +108,7 @@ export function sanitizeOptionalDiagnosticText(value: unknown): string | undefin } function ownDataField( - value: object, + value: Error, key: PropertyKey, ): unknown | typeof MISSING_DATA_FIELD { const descriptor = getOwnPropertyDescriptor(value, key); diff --git a/src/errors/veryfront-error.ts b/src/errors/veryfront-error.ts index bf77fea00c..5531b7176a 100644 --- a/src/errors/veryfront-error.ts +++ b/src/errors/veryfront-error.ts @@ -177,10 +177,7 @@ function snapshotPlainValue( state.seen.add(value); try { if (arrayIsArray(value)) { - const descriptors = getOwnPropertyDescriptors(value) as unknown as Record< - string, - PropertyDescriptor - >; + const descriptors: Record = getOwnPropertyDescriptors(value); const lengthDescriptor = descriptors["length"]; if ( !lengthDescriptor || diff --git a/src/extensions/auth/rsc-action-authorization-provider.ts b/src/extensions/auth/rsc-action-authorization-provider.ts index 2530001e00..845489a623 100644 --- a/src/extensions/auth/rsc-action-authorization-provider.ts +++ b/src/extensions/auth/rsc-action-authorization-provider.ts @@ -21,7 +21,7 @@ const objectPrototype = Object.prototype; const ownKeys = Reflect.ownKeys; const NativeTypeError = TypeError; -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return apply(hasOwnProperty, value, [key]) as boolean; } diff --git a/src/extensions/discovery.ts b/src/extensions/discovery.ts index 2c6f8487d5..afd23f31cc 100644 --- a/src/extensions/discovery.ts +++ b/src/extensions/discovery.ts @@ -145,7 +145,7 @@ function parseContractMetadata(value: unknown): PackageContractMetadata | undefi } function readActivationMode( - metadata: Record, + metadata: unknown, ): | ExtensionActivationMode | typeof MISSING_METADATA_PROPERTY @@ -171,7 +171,7 @@ function quotedPath(path: string): string { export function resolvePackageActivation( metadata: PackageMetadata, ): ExtensionActivationMode { - const activation = readActivationMode(metadata as unknown as Record); + const activation = readActivationMode(metadata); return activation === MISSING_METADATA_PROPERTY || activation === "auto" ? "auto" : "explicit"; } diff --git a/src/extensions/distributed/redis-runtime-provider.ts b/src/extensions/distributed/redis-runtime-provider.ts index ae5876c7e7..3ae1baf92c 100644 --- a/src/extensions/distributed/redis-runtime-provider.ts +++ b/src/extensions/distributed/redis-runtime-provider.ts @@ -342,7 +342,7 @@ function captureNodeRedisClient(value: unknown): NodeRedisClient { on(event: "error", listener: (error: unknown) => void): unknown { return Reflect.apply(on, value, [event, listener]); }, - }) as unknown as NodeRedisClient; + }) as NodeRedisClient; } function captureRedisModule(value: unknown): NodeRedisModule { diff --git a/src/extensions/entrypoint-identity.ts b/src/extensions/entrypoint-identity.ts index 2690f09884..726cc6a179 100644 --- a/src/extensions/entrypoint-identity.ts +++ b/src/extensions/entrypoint-identity.ts @@ -64,7 +64,7 @@ async function statWithIdentity(path: string): Promise { if (isNode || isBun) { const fs = await import("node:fs/promises"); - const info = await fs.stat(path, { bigint: true }) as unknown as NodeBigIntFileInfo; + const info: NodeBigIntFileInfo = await fs.stat(path, { bigint: true }); return { isFile: info.isFile(), isDirectory: info.isDirectory(), diff --git a/src/extensions/manifest-reader.ts b/src/extensions/manifest-reader.ts index ce042cf0d5..bbf50d4f53 100644 --- a/src/extensions/manifest-reader.ts +++ b/src/extensions/manifest-reader.ts @@ -183,8 +183,8 @@ async function defaultLstat(path: string): Promise { if (isNode || isBun) { const fs = await import("node:fs/promises"); - const info = await fs.lstat(path, { bigint: true }); - return fromNodeFileInfo(info as unknown as NodeBigIntFileInfo); + const info: NodeBigIntFileInfo = await fs.lstat(path, { bigint: true }); + return fromNodeFileInfo(info); } throw new Error("The current runtime does not provide filesystem access"); @@ -203,7 +203,7 @@ async function defaultOpen(path: string): Promise { if (isNode || isBun) { const fs = await import("node:fs/promises"); - const handle = await fs.open(path, "r") as unknown as NodeFileHandle; + const handle: NodeFileHandle = await fs.open(path, "r"); return { async read(buffer): Promise { const bufferLength = reflectApply(typedArrayByteLength, buffer, []) as number; @@ -228,7 +228,7 @@ function errorContext(path: string, operation: string): Record return { path, operation }; } -function ownStringProperty(value: object, property: string): string | undefined { +function ownStringProperty(value: Error, property: string): string | undefined { const descriptor = reflectApply(objectGetOwnPropertyDescriptor, undefined, [value, property]) as | PropertyDescriptor | undefined; diff --git a/src/extensions/parser/skill-document-parser.ts b/src/extensions/parser/skill-document-parser.ts index 5755805daf..965e722482 100644 --- a/src/extensions/parser/skill-document-parser.ts +++ b/src/extensions/parser/skill-document-parser.ts @@ -36,7 +36,7 @@ function call(fn: (...args: never[]) => T, receiver: unknown, args: unknown[] return apply(fn, receiver, args) as T; } -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return call(objectHasOwnProperty, value, [key]); } diff --git a/src/extensions/promise-intrinsics-internal.ts b/src/extensions/promise-intrinsics-internal.ts index 752294a6ba..bc5ede4e42 100644 --- a/src/extensions/promise-intrinsics-internal.ts +++ b/src/extensions/promise-intrinsics-internal.ts @@ -39,7 +39,7 @@ defineProperty( ); freeze(safePromiseSpeciesHolder); -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return apply(hasOwnProperty, object, [key]) as boolean; } diff --git a/src/extensions/validation.ts b/src/extensions/validation.ts index 5b7b19a262..5fc99ab0ad 100644 --- a/src/extensions/validation.ts +++ b/src/extensions/validation.ts @@ -226,7 +226,7 @@ function snapshotLegacyProvides( } function readOwnMetadataField( - extension: Extension, + extension: Extension | Record, field: "contracts" | "provides", ): unknown { let descriptor: PropertyDescriptor | undefined; @@ -450,12 +450,12 @@ export function validateExtension(ext: unknown): string[] { let contractsValue: unknown; let legacyProvidesValue: unknown; try { - contractsValue = readOwnMetadataField(candidate as unknown as Extension, "contracts"); + contractsValue = readOwnMetadataField(candidate, "contracts"); } catch (error) { issues.push(describeThrownValue(error)); } try { - legacyProvidesValue = readOwnMetadataField(candidate as unknown as Extension, "provides"); + legacyProvidesValue = readOwnMetadataField(candidate, "provides"); } catch (error) { issues.push(describeThrownValue(error)); } diff --git a/src/html/hydration-script-builder/runtime/main.ts b/src/html/hydration-script-builder/runtime/main.ts index 8c1273565f..e1c41b4744 100644 --- a/src/html/hydration-script-builder/runtime/main.ts +++ b/src/html/hydration-script-builder/runtime/main.ts @@ -21,10 +21,8 @@ import type { HydrationRuntimeEnv, ModuleNamespace, ReactLike, - ReactRoot, RuntimeDocument, RuntimeFetchInit, - RuntimeResponse, RuntimeWindow, } from "./env.ts"; import { createLogging, moduleServerUrl } from "./shared.ts"; @@ -39,18 +37,18 @@ import { createRouterRuntime } from "./router.ts"; import { createHydrationRenderer } from "./renderer.ts"; import { resolveNavigationStore } from "./navigation-store.ts"; -const runtimeWindow = globalThis as unknown as RuntimeWindow; -const runtimeDocument = (globalThis as unknown as { document: RuntimeDocument }).document; +const runtimeWindow: RuntimeWindow = globalThis as typeof globalThis & RuntimeWindow; +const runtimeDocument: RuntimeDocument = + (globalThis as typeof globalThis & { document: RuntimeDocument }).document; const env: HydrationRuntimeEnv = { window: runtimeWindow, document: runtimeDocument, - fetch: (url: string, init?: RuntimeFetchInit) => - fetch(url, init as RequestInit) as unknown as Promise, - React: React as unknown as ReactLike, + fetch: (url: string, init?: RuntimeFetchInit) => fetch(url, init as RequestInit), + React: React as typeof React & ReactLike, RouterProvider, PageContextProvider, - createRoot: (container: unknown) => createRoot(container as HTMLElement) as unknown as ReactRoot, + createRoot: (container: unknown) => createRoot(container as HTMLElement), importModule: (moduleUrl: string) => import(moduleUrl) as Promise, useRouterFromModule, setTimeout: (handler: () => void, timeout?: number) => setTimeout(handler, timeout), @@ -68,7 +66,7 @@ const snapshotModules = createSnapshotModuleImporter({ importModule: env.importModule, fetchModule: env.fetch, reloadDocument: () => runtimeWindow.location.reload(), - recoveryState: runtimeWindow as unknown as Record, + recoveryState: runtimeWindow as RuntimeWindow & Record, }); const componentLoader = createComponentLoader({ diff --git a/src/html/hydration-script-builder/runtime/navigation-store.ts b/src/html/hydration-script-builder/runtime/navigation-store.ts index 85dfcd16bf..69530a005d 100644 --- a/src/html/hydration-script-builder/runtime/navigation-store.ts +++ b/src/html/hydration-script-builder/runtime/navigation-store.ts @@ -48,7 +48,9 @@ export function resolveNavigationStore( usesRegistryFallback, getNavigationStore: () => { const storeKey = Symbol.for(NAVIGATION_STORE_REGISTRY_KEY); - const registry = globalThis as unknown as Record; + const registry = globalThis as + & typeof globalThis + & Record; const existing = registry[storeKey]; if (existing) return existing; diff --git a/src/html/hydration-script-builder/runtime/renderer.ts b/src/html/hydration-script-builder/runtime/renderer.ts index 17b5622e25..903410d7af 100644 --- a/src/html/hydration-script-builder/runtime/renderer.ts +++ b/src/html/hydration-script-builder/runtime/renderer.ts @@ -394,7 +394,7 @@ export function createHydrationRenderer(deps: HydrationRendererDeps): HydrationR container.__reactRoot.render(tree); log("Client-side React app rendered successfully"); } else { - const { hydrateRoot } = await import("react-dom/client") as unknown as { + const { hydrateRoot } = await import("react-dom/client") as { hydrateRoot: (container: unknown, tree: unknown, options?: unknown) => ReactRoot; }; const options = { diff --git a/src/html/hydration-script-builder/runtime/route-timing.ts b/src/html/hydration-script-builder/runtime/route-timing.ts index afc52c6da4..47415da839 100644 --- a/src/html/hydration-script-builder/runtime/route-timing.ts +++ b/src/html/hydration-script-builder/runtime/route-timing.ts @@ -189,7 +189,7 @@ export function createRouteTimingRecorder( if (!entries.length) return null; for (let index = entries.length - 1; index >= 0; index--) { - const entry = entries[index] as unknown as Record; + const entry = entries[index] as PerformanceEntry & Record; const responseEnd = entry?.responseEnd; if ( typeof responseEnd === "number" && diff --git a/src/html/hydration-script-builder/runtime/router.ts b/src/html/hydration-script-builder/runtime/router.ts index bf2a2149fd..595e4b6c49 100644 --- a/src/html/hydration-script-builder/runtime/router.ts +++ b/src/html/hydration-script-builder/runtime/router.ts @@ -7,6 +7,7 @@ import type { ClientRouter, HydrationRuntimeEnv, PageDataPayload, + RuntimeDocument, RuntimeElement, RuntimeEvent, RuntimeFetchInit, @@ -415,11 +416,11 @@ export function createRouterRuntime(deps: RouterRuntimeDeps): RouterRuntime { function handlePageDataVersionMismatch( path: string, data: PageDataPayload, - ): PageDataPayload { + ): PageDataPayload | Promise { if (data.buildVersion && checkVersionMismatch(data.buildVersion)) { log("Version mismatch detected, performing full page reload to:", path); navigateDocument(path); - return new Promise(() => {}) as unknown as PageDataPayload; + return new Promise(() => {}); } return data; @@ -705,7 +706,7 @@ export function createRouterRuntime(deps: RouterRuntimeDeps): RouterRuntime { handoffClientRouteMetadata( pageData.frontmatter ?? {}, - document as unknown as Document, + document as RuntimeDocument & Document, ); if (pageData.css) { @@ -1035,7 +1036,7 @@ export function createRouterRuntime(deps: RouterRuntimeDeps): RouterRuntime { viewportPrefetchObserver?.unobserve(entry.target); const href = getInternalRouteHrefFromLink( - entry.target as unknown as RuntimeElement, + entry.target as Element & RuntimeElement, ); if (href) prefetchPage(href); } @@ -1052,7 +1053,7 @@ export function createRouterRuntime(deps: RouterRuntimeDeps): RouterRuntime { if (observedPrefetchLinks.has(link)) continue; observedPrefetchLinks.add(link); - observer.observe(link as unknown as Element); + observer.observe(link as RuntimeElement & Element); } } diff --git a/src/html/styles-builder/css-hash-cache.ts b/src/html/styles-builder/css-hash-cache.ts index ae7a20a13b..8a8290c176 100644 --- a/src/html/styles-builder/css-hash-cache.ts +++ b/src/html/styles-builder/css-hash-cache.ts @@ -34,7 +34,7 @@ const MAX_SERIALIZED_CSS_CACHE_BYTES = 128 * 1024 * 1024; export interface CSSCacheEntry { readonly css: string; - readonly candidates: string[]; + readonly candidates: readonly string[]; readonly stylesheet: string; readonly pipelineIdentity?: string; } @@ -134,7 +134,7 @@ function createCSSCacheEntry( ); return Object.freeze({ css: detachRetainedString(css), - candidates: Object.freeze(candidates) as unknown as string[], + candidates: Object.freeze(candidates), stylesheet, pipelineIdentity, }); @@ -309,7 +309,7 @@ export async function persistRegeneratedCSSEntry( throw new TypeError("Regenerated CSS entry requires a pipeline identity"); } await cacheCSSAsync(entry.css, hash, { - candidates: entry.candidates, + candidates: [...entry.candidates], stylesheet: entry.stylesheet, pipelineIdentity: entry.pipelineIdentity, }); diff --git a/src/internal-agents/ag-ui-sse.ts b/src/internal-agents/ag-ui-sse.ts index 8d09193ee9..2fddf648df 100644 --- a/src/internal-agents/ag-ui-sse.ts +++ b/src/internal-agents/ag-ui-sse.ts @@ -39,16 +39,13 @@ function buildAgUiEventPayloadSchemas(): Record, + shape: Record>, ): Schema> => - // deno-lint-ignore no-explicit-any - (v.object({ + v.object({ ...shape, elapsedMs: v.number().optional(), emittedAt: v.number().optional(), - } as any) as unknown) as Schema< - Record - >; + }); const schemas: Record>> = { RunStarted: withTiming({ runId: v.string().min(1), diff --git a/src/internal-agents/run-stream.ts b/src/internal-agents/run-stream.ts index 00efa0b3b9..db1c28322e 100644 --- a/src/internal-agents/run-stream.ts +++ b/src/internal-agents/run-stream.ts @@ -1032,10 +1032,7 @@ export async function createRuntimeAgentStreamResponse( // Replays whatever the first model call already produced, then // forwards later steps as they happen. RunStarted stays first. modelCallContextRelay.attach((event) => - enqueueIfAttached( - MODEL_CALL_CONTEXT_SSE_EVENT_NAME, - event as unknown as Record, - ) + enqueueIfAttached(MODEL_CALL_CONTEXT_SSE_EVENT_NAME, event) ); heartbeatTimer = setInterval( enqueueHeartbeatIfAttached, diff --git a/src/modules/import-map/loader-primordial-poisoning.worker.ts b/src/modules/import-map/loader-primordial-poisoning.worker.ts index dce259ad38..ed91c85195 100644 --- a/src/modules/import-map/loader-primordial-poisoning.worker.ts +++ b/src/modules/import-map/loader-primordial-poisoning.worker.ts @@ -8,7 +8,7 @@ const denoJson = JSON.stringify({ package: "https://example.com/deno-package.ts", }, }); -const adapter = { +const adapterMock: unknown = { fs: { getAdapterType: () => "VeryfrontFSAdapter", getUnderlyingAdapter: () => ({}), @@ -17,7 +17,8 @@ const adapter = { readFile: () => denoJson, }, env: { get: () => undefined }, -} as unknown as RuntimeAdapter; +}; +const adapter = adapterMock as RuntimeAdapter; const config = { resolve: { importMap: { diff --git a/src/modules/import-map/loader.ts b/src/modules/import-map/loader.ts index ae950f77f0..51665a0842 100644 --- a/src/modules/import-map/loader.ts +++ b/src/modules/import-map/loader.ts @@ -42,8 +42,8 @@ function stringSlice(value: string, start: number, end?: number): string { ) as string; } -function hasOwn(object: object, key: PropertyKey): boolean { - return ReflectApply(ObjectPrototypeHasOwnProperty, object, [key]) as boolean; +function hasOwn(descriptor: PropertyDescriptor, key: PropertyKey): boolean { + return ReflectApply(ObjectPrototypeHasOwnProperty, descriptor, [key]) as boolean; } function isFrameworkOwnedSpecifier(specifier: string): boolean { @@ -64,7 +64,7 @@ function removeFrameworkOwnedMappings(record: Record): void { } function readOwnDataProperty( - value: object, + value: Record, key: PropertyKey, label: string, ): unknown { @@ -78,7 +78,10 @@ function readOwnDataProperty( return descriptor.value; } -function assertPlainObject(value: unknown, label: string): asserts value is object { +function assertPlainObject( + value: unknown, + label: string, +): asserts value is Record { if (value === null || typeof value !== "object" || ArrayIsArray(value)) { throw IMPORT_MAP_INVALID.create({ detail: `${label} must be a plain object` }); } diff --git a/src/modules/import-map/merger.ts b/src/modules/import-map/merger.ts index 0969af7ef1..e8df2d8635 100644 --- a/src/modules/import-map/merger.ts +++ b/src/modules/import-map/merger.ts @@ -11,8 +11,8 @@ const ReflectApply = Reflect.apply; const ReflectOwnKeys = Reflect.ownKeys; const IntrinsicTypeError = TypeError; -function hasOwn(object: object, key: PropertyKey): boolean { - return ReflectApply(ObjectPrototypeHasOwnProperty, object, [key]) as boolean; +function hasOwn(descriptor: PropertyDescriptor, key: PropertyKey): boolean { + return ReflectApply(ObjectPrototypeHasOwnProperty, descriptor, [key]) as boolean; } function copySnapshotField( diff --git a/src/modules/import-map/preloader-primordial-poisoning.worker.ts b/src/modules/import-map/preloader-primordial-poisoning.worker.ts index 66894d8ba1..46594238bf 100644 --- a/src/modules/import-map/preloader-primordial-poisoning.worker.ts +++ b/src/modules/import-map/preloader-primordial-poisoning.worker.ts @@ -2,10 +2,11 @@ import type { VeryfrontConfig } from "#veryfront/config"; import type { RuntimeAdapter } from "#veryfront/platform/adapters/base.ts"; import { ImportMapPreloader } from "./preloader.ts"; -const adapter = { +const adapterMock: unknown = { fs: {}, env: {}, -} as unknown as RuntimeAdapter; +}; +const adapter = adapterMock as RuntimeAdapter; const configA = { resolve: { importMap: { diff --git a/src/modules/import-map/preloader.ts b/src/modules/import-map/preloader.ts index 23f4ad4632..972e50334d 100644 --- a/src/modules/import-map/preloader.ts +++ b/src/modules/import-map/preloader.ts @@ -68,8 +68,8 @@ function monotonicNow(): number { return ReflectApply(PerformanceNow, IntrinsicPerformance, []) as number; } -function hasOwn(object: object, key: PropertyKey): boolean { - return ReflectApply(ObjectPrototypeHasOwnProperty, object, [key]) as boolean; +function hasOwn(descriptor: PropertyDescriptor, key: PropertyKey): boolean { + return ReflectApply(ObjectPrototypeHasOwnProperty, descriptor, [key]) as boolean; } function snapshotEmbeddedImportMap(value: unknown): ImportMapConfig { diff --git a/src/modules/react-loader/transformed-module-coordinator.ts b/src/modules/react-loader/transformed-module-coordinator.ts index e55db91e19..164fadb714 100644 --- a/src/modules/react-loader/transformed-module-coordinator.ts +++ b/src/modules/react-loader/transformed-module-coordinator.ts @@ -837,7 +837,8 @@ export class TransformedModuleCoordinator { }); }, this.#heartbeatIntervalMs); - const timer = this.#heartbeatTimer as unknown as { unref?: () => void }; + const rawTimer: unknown = this.#heartbeatTimer; + const timer = rawTimer as { unref?: () => void }; if (typeof timer.unref === "function") timer.unref(); const deno = (globalThis as { Deno?: { unrefTimer?: (id: number) => void } }).Deno; if (typeof this.#heartbeatTimer === "number" && typeof deno?.unrefTimer === "function") { diff --git a/src/oauth/providers/base.ts b/src/oauth/providers/base.ts index 3dba952a71..beda6501e1 100644 --- a/src/oauth/providers/base.ts +++ b/src/oauth/providers/base.ts @@ -286,7 +286,7 @@ function cloneProviderConfig(config: OAuthProviderConfig): OAuthProviderConfig { raw.defaultScopes = defaultScopes; } - const snapshot: Record = Object.create(null); + const snapshot: OAuthProviderConfig = Object.create(null); for (const [key, value] of Object.entries(raw)) { Object.defineProperty(snapshot, key, { configurable: true, @@ -302,7 +302,7 @@ function cloneProviderConfig(config: OAuthProviderConfig): OAuthProviderConfig { if (tokenMapping.snapshot !== undefined) { snapshot.tokenResponseMapping = tokenMapping.snapshot; } - return snapshot as unknown as OAuthProviderConfig; + return snapshot; } function encodeBasicCredentials(clientId: string, clientSecret: string): string { diff --git a/src/observability/application-errors.ts b/src/observability/application-errors.ts index f08bc9d907..06ec6fd1a2 100644 --- a/src/observability/application-errors.ts +++ b/src/observability/application-errors.ts @@ -236,7 +236,7 @@ const TENANT_BUILD_ERROR_CLASS = "tenant-build"; */ const TENANT_BUILD_FAILURE_TAG = Symbol.for("veryfront.module-loader.tenant-build-failure"); -function hasOwnTrueSymbol(value: object, key: symbol): boolean { +function hasOwnTrueSymbol(value: Error, key: symbol): boolean { const descriptor = ReflectGetOwnPropertyDescriptor(value, key); return descriptor !== undefined && ReflectApply(ObjectPrototypeHasOwnProperty, descriptor, ["value"]) === true && diff --git a/src/observability/auto-instrument.test-helpers.ts b/src/observability/auto-instrument.test-helpers.ts index 89c84298d6..c8c0280db5 100644 --- a/src/observability/auto-instrument.test-helpers.ts +++ b/src/observability/auto-instrument.test-helpers.ts @@ -17,5 +17,5 @@ export function createResolvedFetch(response: Response): typeof fetch { export function createThrowingFetch(error: Error): typeof fetch { return (() => { throw error; - }) as unknown as typeof fetch; + }) as typeof fetch; } diff --git a/src/observability/telemetry-error.ts b/src/observability/telemetry-error.ts index 89241bf2e1..374e25eb04 100644 --- a/src/observability/telemetry-error.ts +++ b/src/observability/telemetry-error.ts @@ -43,12 +43,12 @@ const URL_HREF_GETTER = readOwnDescriptorGetter(NativeURL.prototype, "href"); const INVALID_ERROR_FIELD = Symbol("invalid-error-field"); -function hasOwn(object: object, key: PropertyKey): boolean { - return apply(objectHasOwnProperty, object, [key]) as boolean; +function hasOwn(descriptor: PropertyDescriptor, key: PropertyKey): boolean { + return apply(objectHasOwnProperty, descriptor, [key]) as boolean; } function readOwnDescriptorGetter( - object: object, + object: URL, key: PropertyKey, ): ((this: unknown) => unknown) | undefined { try { diff --git a/src/platform/adapters/file-system-capabilities.ts b/src/platform/adapters/file-system-capabilities.ts index 0e475f4ac0..e5bb02c34d 100644 --- a/src/platform/adapters/file-system-capabilities.ts +++ b/src/platform/adapters/file-system-capabilities.ts @@ -128,7 +128,7 @@ export interface CapturedStaticReaders { }; } -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return apply(objectHasOwnProperty, value, [key]) as boolean; } diff --git a/src/platform/adapters/fs/integration.ts b/src/platform/adapters/fs/integration.ts index 63bab312b3..e89f0beb0d 100644 --- a/src/platform/adapters/fs/integration.ts +++ b/src/platform/adapters/fs/integration.ts @@ -43,7 +43,7 @@ function materializeAdapterWithFS( adapter: RuntimeAdapter, wrappedFS: RuntimeAdapter["fs"], ): RuntimeAdapter { - const enhanced: Record = {}; + const enhanced = {} as RuntimeAdapter & Record; const seen = new Set(); let current: object | null = adapter; @@ -58,7 +58,7 @@ function materializeAdapterWithFS( } enhanced.fs = wrappedFS; - return enhanced as unknown as RuntimeAdapter; + return enhanced; } export function enhanceAdapterWithFS( diff --git a/src/platform/adapters/fs/veryfront/request-context.ts b/src/platform/adapters/fs/veryfront/request-context.ts index a1da4ac074..e1c58faaa1 100644 --- a/src/platform/adapters/fs/veryfront/request-context.ts +++ b/src/platform/adapters/fs/veryfront/request-context.ts @@ -40,7 +40,7 @@ export function wrapWithCurrentContext unknown>( return ((...args: Parameters) => { return asyncLocalStorage.run(store, () => fn(...args)); - }) as unknown as T; + }) as T; } export function getRequestScopedFile(cacheKey: string): string | undefined { diff --git a/src/platform/adapters/fs/wrapper.ts b/src/platform/adapters/fs/wrapper.ts index c1e3a5d4f1..8e9ca116a0 100644 --- a/src/platform/adapters/fs/wrapper.ts +++ b/src/platform/adapters/fs/wrapper.ts @@ -18,7 +18,7 @@ import { type CapturedMethod = (...args: never[]) => unknown; -function captureOptionalMethod(value: object, key: string): CapturedMethod | undefined { +function captureOptionalMethod(value: FSAdapter, key: string): CapturedMethod | undefined { const seen = new Set(); let owner: object | null = value; for (let depth = 0; owner !== null && depth < 64; depth++) { @@ -42,7 +42,7 @@ function captureOptionalMethod(value: object, key: string): CapturedMethod | und return undefined; } -function publishFrozen(target: object, key: PropertyKey, value: unknown): void { +function publishFrozen(target: FSAdapterWrapper, key: PropertyKey, value: unknown): void { Object.defineProperty(target, key, { configurable: false, enumerable: true, diff --git a/src/platform/adapters/runtime/deno/filesystem-adapter.ts b/src/platform/adapters/runtime/deno/filesystem-adapter.ts index dd8b2829cd..51a90d7844 100644 --- a/src/platform/adapters/runtime/deno/filesystem-adapter.ts +++ b/src/platform/adapters/runtime/deno/filesystem-adapter.ts @@ -42,7 +42,7 @@ interface DenoFileSystemCapabilityOptions extends NodeFileSystemCapabilityOption readonly denoCreateRuntime?: DenoCreateRuntime | null; } -function hasOwn(value: object, property: PropertyKey): boolean { +function hasOwn(value: DenoFileSystemCapabilityOptions, property: PropertyKey): boolean { return Object.prototype.hasOwnProperty.call(value, property); } diff --git a/src/platform/adapters/runtime/deno/http-server.ts b/src/platform/adapters/runtime/deno/http-server.ts index f01b1d6233..08fa7a4152 100644 --- a/src/platform/adapters/runtime/deno/http-server.ts +++ b/src/platform/adapters/runtime/deno/http-server.ts @@ -254,7 +254,7 @@ export function createDenoServer( }); } return createDenoServerWithRuntime( - runtime as unknown as DenoServeRuntime, + runtime as DenoServeRuntime, handler, options, ); diff --git a/src/platform/adapters/runtime/node/http-server.ts b/src/platform/adapters/runtime/node/http-server.ts index 9857d6de15..2644fe19ea 100644 --- a/src/platform/adapters/runtime/node/http-server.ts +++ b/src/platform/adapters/runtime/node/http-server.ts @@ -160,7 +160,7 @@ export class NodeServer implements Server { /** @internal Native transport for compatibility facades that expose Node's server. */ get nativeHttpServer(): import("node:http").Server { - return this.server as unknown as import("node:http").Server; + return this.server as import("node:http").Server; } /** @internal Update an ephemeral (`port: 0`) listener with its bound port. */ @@ -853,7 +853,7 @@ async function createNodeServerInternal( }; const nodeServer = new NodeServer( - server as unknown as NodeHttpServer, + server as NodeHttpServer, hostname, port, disposeUpgrades, diff --git a/src/platform/adapters/runtime/shared/node-filesystem-adapter.ts b/src/platform/adapters/runtime/shared/node-filesystem-adapter.ts index cb57e2d6e6..30ae0eff89 100644 --- a/src/platform/adapters/runtime/shared/node-filesystem-adapter.ts +++ b/src/platform/adapters/runtime/shared/node-filesystem-adapter.ts @@ -125,7 +125,7 @@ const nodeFileSystemOperations: NodeFileSystemOperations = { }, }; -function hasOwn(value: object, property: PropertyKey): boolean { +function hasOwn(value: NodeFileSystemCapabilityOptions, property: PropertyKey): boolean { return Object.prototype.hasOwnProperty.call(value, property); } diff --git a/src/platform/compat/error-introspection.ts b/src/platform/compat/error-introspection.ts index 7b8fbbee46..30e0338b61 100644 --- a/src/platform/compat/error-introspection.ts +++ b/src/platform/compat/error-introspection.ts @@ -18,7 +18,7 @@ const NativeError = Error; const NativeAsyncFunctionPrototype = getPrototypeOf(async function () {}); const toStringTagSymbol = Symbol.toStringTag; -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return apply(objectHasOwnProperty, object, [key]) as boolean; } diff --git a/src/platform/compat/fs.ts b/src/platform/compat/fs.ts index 7f236cd92d..33f1e37a0c 100644 --- a/src/platform/compat/fs.ts +++ b/src/platform/compat/fs.ts @@ -180,7 +180,7 @@ class NodeFileSystem implements FileSystem { import("node:path"), ]); - this.fs = fsModule as unknown as NodeFsPromises; + this.fs = fsModule as NodeFsPromises; this.os = osModule; this.path = pathModule; this.initialized = true; diff --git a/src/platform/compat/http/native-response.ts b/src/platform/compat/http/native-response.ts index c3220918b3..265fc4b22c 100644 --- a/src/platform/compat/http/native-response.ts +++ b/src/platform/compat/http/native-response.ts @@ -89,7 +89,7 @@ export function toNativeResponse( // Re-wrap polyfilled Response as native Response. // At runtime, `response` may be an undici Response (from the dnt shim) that // fails Deno's native instanceof check. Cast to access its properties. - const r = response as unknown as Response; + const r = response as Response; return new NativeResponse(r.body, { status: r.status, statusText: r.statusText, diff --git a/src/platform/compat/http/pinned-fetch.ts b/src/platform/compat/http/pinned-fetch.ts index 3e9b06dd5f..fd323a012c 100644 --- a/src/platform/compat/http/pinned-fetch.ts +++ b/src/platform/compat/http/pinned-fetch.ts @@ -206,7 +206,7 @@ async function writeRequestBody(request: ClientRequest, body: BodyInit | null): const { Readable } = await import("node:stream"); const webStream = body instanceof Blob ? body.stream() : body; const source = Readable.fromWeb( - webStream as unknown as import("node:stream/web").ReadableStream, + webStream as import("node:stream/web").ReadableStream, ); await new Promise((resolve, reject) => { source.once("error", reject); diff --git a/src/platform/compat/kv/factory.ts b/src/platform/compat/kv/factory.ts index d9dea43f4c..33eee959f2 100644 --- a/src/platform/compat/kv/factory.ts +++ b/src/platform/compat/kv/factory.ts @@ -42,7 +42,7 @@ export async function openKv(path?: string): Promise { const db = await sqliteStore.openSqliteDatabase(path); // Extension SqliteDatabase is structurally identical to SqliteDatabase; // cast to satisfy the SqliteKv constructor's nominal type check. - return new SqliteKv(db as unknown as SqliteDatabase); + return new SqliteKv(db as SqliteDatabase); } catch (error) { backendFailures.push(error); serverLogger.warn( diff --git a/src/platform/compat/native-brand-checks.ts b/src/platform/compat/native-brand-checks.ts index bcea357b2c..c372208b69 100644 --- a/src/platform/compat/native-brand-checks.ts +++ b/src/platform/compat/native-brand-checks.ts @@ -24,7 +24,7 @@ const freeze = Object.freeze; const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; const objectHasOwnProperty = Object.prototype.hasOwnProperty; -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return apply(objectHasOwnProperty, object, [key]) as boolean; } diff --git a/src/platform/compat/not-found-error.ts b/src/platform/compat/not-found-error.ts index 8bcc2a2255..6870632f5b 100644 --- a/src/platform/compat/not-found-error.ts +++ b/src/platform/compat/not-found-error.ts @@ -16,7 +16,7 @@ const reflectApply = Reflect.apply; const setAdd = Set.prototype.add; const setHas = Set.prototype.has; -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return reflectApply(hasOwnProperty, value, [key]) as boolean; } diff --git a/src/platform/compat/process/command.ts b/src/platform/compat/process/command.ts index 5262a616da..df595a4aac 100644 --- a/src/platform/compat/process/command.ts +++ b/src/platform/compat/process/command.ts @@ -573,7 +573,7 @@ export async function runCommand( } if (IS_BUN) { - const bunGlobal = globalThis as unknown as { + const bunGlobal = globalThis as typeof globalThis & { Bun: BunCommandRuntime; }; diff --git a/src/platform/compat/process/lifecycle.ts b/src/platform/compat/process/lifecycle.ts index 3c7d7453e8..c33cdf3b07 100644 --- a/src/platform/compat/process/lifecycle.ts +++ b/src/platform/compat/process/lifecycle.ts @@ -124,7 +124,7 @@ export function getRuntimeVersion(): string { const deno = IS_DENO ? getDenoRuntime() : undefined; if (deno) return `Deno ${deno.version.deno}`; if ("Bun" in globalThis) { - return `Bun ${(globalThis as unknown as { Bun: { version: string } }).Bun.version}`; + return `Bun ${(globalThis as typeof globalThis & { Bun: { version: string } }).Bun.version}`; } if (runtimeProcess) return `Node.js ${runtimeProcess.version}`; return "unknown"; diff --git a/src/platform/compat/std/expect.ts b/src/platform/compat/std/expect.ts index e0d503bd6e..404b9addfe 100644 --- a/src/platform/compat/std/expect.ts +++ b/src/platform/compat/std/expect.ts @@ -695,14 +695,14 @@ let expect: ExpectFn; if (isDeno) { const stdExpect = await import("#std/expect.ts"); - expect = stdExpect.expect as unknown as ExpectFn; + expect = stdExpect.expect as ExpectFn; } else if (isBun) { const importBunTest = new Function("return import('bun:test')") as () => Promise<{ expect?: ExternalExpectFn; default?: { expect?: ExternalExpectFn }; }>; const bunTestModule = await importBunTest(); - expect = (bunTestModule.expect ?? bunTestModule.default?.expect) as unknown as ExpectFn; + expect = (bunTestModule.expect ?? bunTestModule.default?.expect) as ExpectFn; } else { expect = createNodeExpect(); } diff --git a/src/platform/compat/std/fs.ts b/src/platform/compat/std/fs.ts index d12aff48cd..cd0cbde2b3 100644 --- a/src/platform/compat/std/fs.ts +++ b/src/platform/compat/std/fs.ts @@ -69,7 +69,7 @@ interface NodeFileSystemModule { // API cannot lazily await its implementation at call time, so load it once only // in runtimes that actually provide Node-compatible filesystem APIs. const nodeFileSystem = isNode || isBun - ? await import("node:fs") as unknown as NodeFileSystemModule + ? await import("node:fs") as NodeFileSystemModule : undefined; const denoRuntime = getDenoRuntime(); diff --git a/src/platform/compat/std/testing/time.ts b/src/platform/compat/std/testing/time.ts index be5d1ce492..4d97e6bd05 100644 --- a/src/platform/compat/std/testing/time.ts +++ b/src/platform/compat/std/testing/time.ts @@ -28,7 +28,13 @@ type TimerGlobals = { Date: DateConstructor; }; -type MutableGlobals = Record; +type MutableGlobals = { + setTimeout: unknown; + clearTimeout: unknown; + setInterval: unknown; + clearInterval: unknown; + Date: unknown; +}; // A callback that reschedules itself with no delay would otherwise spin until // the process is killed, which reads as a hung suite rather than a bad test. @@ -69,7 +75,7 @@ export class FakeTime { throw new Error("FakeTime is already installed; restore the previous instance first"); } - const globals = globalThis as unknown as MutableGlobals; + const globals = globalThis as MutableGlobals; this.#originals = { setTimeout: globalThis.setTimeout, clearTimeout: globalThis.clearTimeout, @@ -145,7 +151,7 @@ export class FakeTime { if (this.#restored) return; this.#restored = true; - const globals = globalThis as unknown as MutableGlobals; + const globals = globalThis as MutableGlobals; globals.setTimeout = this.#originals.setTimeout; globals.clearTimeout = this.#originals.clearTimeout; globals.setInterval = this.#originals.setInterval; diff --git a/src/prompt/validation.ts b/src/prompt/validation.ts index dea5cb010f..6ae1c00e0a 100644 --- a/src/prompt/validation.ts +++ b/src/prompt/validation.ts @@ -19,7 +19,7 @@ function isObjectRecord(value: unknown): value is Record { } function readOwnDataProperty( - object: object, + object: Record | readonly unknown[], property: PropertyKey, field: string, ): OwnDataProperty { diff --git a/src/provider/runtime-loader/json-snapshot.ts b/src/provider/runtime-loader/json-snapshot.ts index b293b1eb72..5ca557a404 100644 --- a/src/provider/runtime-loader/json-snapshot.ts +++ b/src/provider/runtime-loader/json-snapshot.ts @@ -38,7 +38,7 @@ const weakSetHas = WeakSet.prototype.has; const NativeArrayPrototype = Array.prototype; const NativeObjectPrototype = Object.prototype; -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return apply(objectHasOwnProperty, object, [key]) as boolean; } @@ -319,7 +319,7 @@ function assertRawJsonTextWithinByteLimit(value: string, maxBytes: number): void } } -function inspectPrototype(value: object): object | null { +function inspectPrototype(value: Record | unknown[]): object | null { try { return objectGetPrototypeOf(value); } catch { @@ -327,7 +327,7 @@ function inspectPrototype(value: object): object | null { } } -function inspectOwnKeys(value: object): (string | symbol)[] { +function inspectOwnKeys(value: Record | unknown[]): (string | symbol)[] { try { return ownKeys(value); } catch { @@ -336,7 +336,7 @@ function inspectOwnKeys(value: object): (string | symbol)[] { } function inspectOwnDescriptor( - value: object, + value: Record | unknown[], key: string | symbol, ): PropertyDescriptor { try { @@ -351,7 +351,7 @@ function inspectOwnDescriptor( } function readDataProperty( - value: object, + value: Record | unknown[], key: string, requireEnumerable: boolean, ): unknown { diff --git a/src/proxy/routing-invalidation-redis.ts b/src/proxy/routing-invalidation-redis.ts index b37585ba40..7c5f011b49 100644 --- a/src/proxy/routing-invalidation-redis.ts +++ b/src/proxy/routing-invalidation-redis.ts @@ -88,7 +88,7 @@ function parseSignedEnvelope(message: string): SignedRoutingInvalidationEnvelope return null; } if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null; - const envelope = parsed as Record; + const envelope = parsed as Partial; if ( envelope.version !== 1 || typeof envelope.issuedAtMs !== "number" || @@ -102,7 +102,7 @@ function parseSignedEnvelope(message: string): SignedRoutingInvalidationEnvelope ) { return null; } - return envelope as unknown as SignedRoutingInvalidationEnvelope; + return envelope as SignedRoutingInvalidationEnvelope; } function signatureDomainPrefix(domain: SignatureDomain): string { @@ -223,7 +223,7 @@ function parseEvent(message: string): ProxyRoutingInvalidationEvent | null { return null; } if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null; - const event = parsed as Record; + const event = parsed as Partial; if ( event.version !== 1 || typeof event.eventId !== "string" || !event.eventId || @@ -236,7 +236,7 @@ function parseEvent(message: string): ProxyRoutingInvalidationEvent | null { ) { return null; } - return event as unknown as ProxyRoutingInvalidationEvent; + return event as ProxyRoutingInvalidationEvent; } function parseAcknowledgement(message: string): RoutingInvalidationAcknowledgement | null { @@ -248,14 +248,14 @@ function parseAcknowledgement(message: string): RoutingInvalidationAcknowledgeme return null; } if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null; - const acknowledgement = parsed as Record; + const acknowledgement = parsed as Partial; if ( typeof acknowledgement.eventId !== "string" || !acknowledgement.eventId || typeof acknowledgement.replicaId !== "string" || !acknowledgement.replicaId ) { return null; } - return acknowledgement as unknown as RoutingInvalidationAcknowledgement; + return acknowledgement as RoutingInvalidationAcknowledgement; } async function createDefaultClient(redisUrl: string): Promise { diff --git a/src/proxy/shutdown-hooks.ts b/src/proxy/shutdown-hooks.ts index e1e500e377..fa094f43ad 100644 --- a/src/proxy/shutdown-hooks.ts +++ b/src/proxy/shutdown-hooks.ts @@ -43,7 +43,7 @@ function appendArrayValue(array: T[], value: T): void { } function defineOwnDataProperty( - object: object, + object: Record, key: PropertyKey, value: unknown, ): void { @@ -60,7 +60,7 @@ export function createProxyShutdownAggregateError( failures: readonly unknown[], message: string, ): AggregateError { - const iterable = createObject(null) as Record; + const iterable = createObject(null) as Record & Iterable; defineOwnDataProperty(iterable, arrayIteratorSymbol, () => { let index = 0; const iterator = createObject(null) as Record; @@ -77,7 +77,7 @@ export function createProxyShutdownAggregateError( return iterator; }); return new NativeAggregateError( - iterable as unknown as Iterable, + iterable, message, ); } @@ -98,7 +98,7 @@ function defineArrayValue(array: T[], index: number, value: T): void { defineProperty(array, index, descriptor); } -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: readonly unknown[], key: PropertyKey): boolean { return apply(hasOwnProperty, object, [key]) as boolean; } diff --git a/src/proxy/shutdown-intrinsics.ts b/src/proxy/shutdown-intrinsics.ts index 77b703a2a0..a4ab592a8b 100644 --- a/src/proxy/shutdown-intrinsics.ts +++ b/src/proxy/shutdown-intrinsics.ts @@ -18,7 +18,7 @@ const hasOwnProperty = Object.prototype.hasOwnProperty; const nativePromiseThen = Promise.prototype.then; const promiseSpecies = Symbol.species; -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return apply(hasOwnProperty, object, [key]) as boolean; } diff --git a/src/proxy/shutdown-lifecycle.ts b/src/proxy/shutdown-lifecycle.ts index 5004f450f9..d895235eba 100644 --- a/src/proxy/shutdown-lifecycle.ts +++ b/src/proxy/shutdown-lifecycle.ts @@ -70,7 +70,7 @@ function defineRecordValue( defineProperty(record, key, descriptor); } -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: Record, key: PropertyKey): boolean { return apply(hasOwnProperty, object, [key]) as boolean; } diff --git a/src/react/components/chat/chat/hooks/attachment-csrf.test.tsx b/src/react/components/chat/chat/hooks/attachment-csrf.test.tsx index 59f15fad33..6c580e0b33 100644 --- a/src/react/components/chat/chat/hooks/attachment-csrf.test.tsx +++ b/src/react/components/chat/chat/hooks/attachment-csrf.test.tsx @@ -111,7 +111,7 @@ function installCsrfEdge( const statuses = new Map(); const handler = new CsrfHandler(); - globalThis.fetch = async (input, init) => { + globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => { const url = new URL(String(input), document.baseURI); const headers = new Headers(init?.headers); if (document.cookie) headers.set("cookie", document.cookie); @@ -125,7 +125,7 @@ function installCsrfEdge( const response = result.response ?? endpoint(req); statuses.set(`${req.method} ${url.pathname}`, response.status); return response; - }; + }) as typeof fetch; return { restore: () => (globalThis.fetch = originalFetch), statuses }; } @@ -192,7 +192,7 @@ function installXhrCsrfEdge(): { function renderUpload( options: Parameters[0], -): { upload: () => UseUploadResult; unmount: () => void } { +): { upload: () => UseUploadResult; unmount: () => Promise } { let latest: UseUploadResult | null = null; function Capture(): null { latest = useUpload(options); @@ -205,7 +205,7 @@ function renderUpload( function renderAttachments( url: string, -): { attachments: () => UseAttachmentsResult; unmount: () => void } { +): { attachments: () => UseAttachmentsResult; unmount: () => Promise } { let latest: UseAttachmentsResult | null = null; function Capture(): null { latest = useAttachments({ url }); @@ -233,7 +233,7 @@ describe("chat attachment CSRF", () => { assertEquals(edge.statuses.get("POST /api/uploads"), 200); } finally { - view.unmount(); + await view.unmount(); edge.restore(); restoreDom(); } @@ -250,7 +250,7 @@ describe("chat attachment CSRF", () => { assertEquals(edge.tokens.get("POST /api/uploads"), null); } finally { - view.unmount(); + await view.unmount(); edge.restore(); restoreDom(); } @@ -271,7 +271,7 @@ describe("chat attachment CSRF", () => { assertEquals(edge.statuses.get("POST /api/uploads"), 200); } finally { - view.unmount(); + await view.unmount(); edge.restore(); restoreDom(); } @@ -294,7 +294,7 @@ describe("chat attachment CSRF", () => { assertEquals(edge.statuses.get("DELETE /api/uploads"), 204); } finally { - view.unmount(); + await view.unmount(); edge.restore(); restoreDom(); } @@ -306,7 +306,7 @@ describe("chat attachment CSRF", () => { const originalFetch = globalThis.fetch; let sentToken: string | null | undefined; - globalThis.fetch = (input, init) => { + globalThis.fetch = ((input: RequestInfo | URL, init?: RequestInit) => { const method = init?.method ?? "GET"; if (method === "POST") sentToken = new Headers(init?.headers).get("x-csrf-token"); return Promise.resolve( @@ -314,7 +314,7 @@ describe("chat attachment CSRF", () => { ? Response.json({ items: [] }) : Response.json({ id: "up-1", name: "a.txt", url: `${String(input)}/a.txt`, size: 1 }), ); - }; + }) as typeof fetch; const view = renderAttachments("https://uploads.example.net/api/uploads"); try { view.attachments().upload([new File(["a"], "a.txt", { type: "text/plain" })]); @@ -322,7 +322,7 @@ describe("chat attachment CSRF", () => { assertEquals(sentToken, null); } finally { - view.unmount(); + await view.unmount(); globalThis.fetch = originalFetch; restoreDom(); } diff --git a/src/react/components/chat/chat/persistence/conversation-codec.ts b/src/react/components/chat/chat/persistence/conversation-codec.ts index 6f45dc51df..54a712e0ab 100644 --- a/src/react/components/chat/chat/persistence/conversation-codec.ts +++ b/src/react/components/chat/chat/persistence/conversation-codec.ts @@ -228,7 +228,11 @@ function descriptorValue(descriptor: PropertyDescriptor, path: string): unknown return descriptor.value; } -function defineDataProperty(target: object, key: PropertyKey, value: JsonValue): void { +function defineDataProperty( + target: JsonValue[] | JsonRecord, + key: PropertyKey, + value: JsonValue, +): void { const descriptor = ObjectCreate(null) as PropertyDescriptor; descriptor.configurable = true; descriptor.enumerable = true; @@ -813,10 +817,10 @@ function validateConversationValue(value: JsonValue): Conversation { } ids.add(id); }); - return value as unknown as Conversation; + return value as JsonRecord & Conversation; } -function validateSummaryValue(value: JsonValue, path: string): ConversationSummary { +function validateSummaryValue(value: JsonValue, path: string): JsonRecord & ConversationSummary { if (!isRecord(value)) { return fail(path, "conversation summary must be a record"); } @@ -841,14 +845,14 @@ function validateSummaryValue(value: JsonValue, path: string): ConversationSumma `exceeds ${CONVERSATION_STORAGE_LIMITS.maxMessagesPerConversation}`, ); } - return value as unknown as ConversationSummary; + return value as JsonRecord & ConversationSummary; } function byNewest(left: ConversationSummary, right: ConversationSummary): number { return right.updatedAt - left.updatedAt; } -function validateIndexValue(value: JsonValue): ConversationSummary[] { +function validateIndexValue(value: JsonValue): (JsonRecord & ConversationSummary)[] { if (!ArrayIsArray(value)) { return fail("$", "conversation index must be an array"); } @@ -1067,7 +1071,7 @@ export function encodeConversationRecord(conversation: Conversation): EncodedCon snapshot, CONVERSATION_STORAGE_LIMITS.maxConversationBytes, ); - const value = rehydrateJsonValue(snapshot) as unknown as Conversation; + const value = rehydrateJsonValue(snapshot) as JsonRecord & Conversation; return { serialized, value, @@ -1089,7 +1093,7 @@ export function decodeConversationRecord(raw: string): DecodedConversationRecord ); validateConversationValue(snapshot); return { - value: rehydrateJsonValue(snapshot) as unknown as Conversation, + value: rehydrateJsonValue(snapshot) as JsonRecord & Conversation, legacy: unwrapped.legacy, }; } @@ -1105,12 +1109,10 @@ export function encodeConversationIndex( const canonical = validateIndexValue(snapshot); const serialized = serializeEnvelope( "index", - canonical as unknown as JsonValue, + canonical, CONVERSATION_STORAGE_LIMITS.maxIndexBytes, ); - const value = rehydrateJsonValue( - canonical as unknown as JsonValue, - ) as unknown as ConversationSummary[]; + const value = rehydrateJsonValue(canonical) as (JsonRecord & ConversationSummary)[]; return { serialized, value, @@ -1132,9 +1134,7 @@ export function decodeConversationIndex(raw: string): DecodedConversationIndex { ); const canonical = validateIndexValue(snapshot); return { - value: rehydrateJsonValue( - canonical as unknown as JsonValue, - ) as unknown as ConversationSummary[], + value: rehydrateJsonValue(canonical) as (JsonRecord & ConversationSummary)[], legacy: unwrapped.legacy, }; } diff --git a/src/react/components/ui/adapter/tabs.conformance.test.tsx b/src/react/components/ui/adapter/tabs.conformance.test.tsx index bd644f6a7a..980bf80f96 100644 --- a/src/react/components/ui/adapter/tabs.conformance.test.tsx +++ b/src/react/components/ui/adapter/tabs.conformance.test.tsx @@ -41,7 +41,7 @@ function installDom(dom: JSDOM): () => void { }; } -function render(el: React.ReactElement): { host: HTMLElement; unmount: () => void } { +function render(el: React.ReactElement): { host: HTMLElement; unmount: () => Promise } { const dom = new JSDOM(`
`); const restore = installDom(dom); const host = dom.window.document.getElementById("root")!; @@ -49,9 +49,12 @@ function render(el: React.ReactElement): { host: HTMLElement; unmount: () => voi flushSync(() => root.render(el)); return { host: host as unknown as HTMLElement, - unmount: () => { + unmount: async () => { try { root.unmount(); + // Drain one macrotask so jsdom's selectionchange 0ms timer (started + // by element.focus()) completes inside the test's sanitizer window. + await new Promise((resolve) => setTimeout(resolve, 0)); } finally { restore(); } @@ -88,7 +91,7 @@ function Harness(): React.ReactElement { function runTabsConformance(label: string, Wrap: React.FC<{ children: React.ReactNode }>): void { describe(`Tabs adapter conformance - ${label}`, () => { - it("role=tablist/tab; clicking a tab selects it (aria-selected + data-state)", () => { + it("role=tablist/tab; clicking a tab selects it (aria-selected + data-state)", async () => { const { host, unmount } = render( @@ -107,7 +110,7 @@ function runTabsConformance(label: string, Wrap: React.FC<{ children: React.Reac assert(a!.getAttribute("aria-selected") === "false", "a deselected"); assert(b!.getAttribute("data-state") === "active", "b data-state active"); } finally { - unmount(); + await unmount(); } }); }); @@ -118,7 +121,7 @@ const Identity: React.FC<{ children: React.ReactNode }> = ({ children }) => <>{c runTabsConformance("builtin (default)", Identity); describe("Tabs adapter conformance - builtin keyboard navigation", () => { - it("uses one tab stop and selects tabs with roving keyboard commands", () => { + it("uses one tab stop and selects tabs with roving keyboard commands", async () => { const { host, unmount } = render( @@ -141,7 +144,7 @@ describe("Tabs adapter conformance - builtin keyboard navigation", () => { key(a!, "End"); assert(b!.getAttribute("aria-selected") === "true", "End selects the last tab"); } finally { - unmount(); + await unmount(); } }); }); diff --git a/src/react/components/ui/context-menu.behaviour.test.tsx b/src/react/components/ui/context-menu.behaviour.test.tsx index a1d1980065..4a8b54f718 100644 --- a/src/react/components/ui/context-menu.behaviour.test.tsx +++ b/src/react/components/ui/context-menu.behaviour.test.tsx @@ -102,8 +102,11 @@ function mountInScope(element: React.ReactElement): { root, win: dom.window as unknown as Window & typeof globalThis, rightClickTrigger, - cleanup: () => { + cleanup: async () => { root.unmount(); + // Drain one macrotask so jsdom's selectionchange 0ms timer (started by + // element.focus()) completes inside the test's sanitizer window. + await new Promise((resolve) => setTimeout(resolve, 0)); restore(); }, }; @@ -129,7 +132,7 @@ function Menu( } describe("ContextMenu behaviour (builtin)", () => { - it("is closed until a right-click; contextmenu opens it, portalled into the token scope", () => { + it("is closed until a right-click; contextmenu opens it, portalled into the token scope", async () => { const { scope, rightClickTrigger, cleanup } = mountInScope(); try { assertEquals(scope.querySelector('[role="menu"]'), null, "closed initially"); @@ -142,11 +145,11 @@ describe("ContextMenu behaviour (builtin)", () => { "portalled surface stays within the token scope, not document.body", ); } finally { - cleanup(); + await cleanup(); } }); - it("makes the default trigger focusable and opens from Shift+F10", () => { + it("makes the default trigger focusable and opens from Shift+F10", async () => { const { scope, cleanup } = mountInScope(); try { const trigger = scope.querySelector('[data-testid="trigger"]')!; @@ -166,11 +169,11 @@ describe("ContextMenu behaviour (builtin)", () => { }); assert(scope.querySelector('[role="menu"]'), "Shift+F10 opens the menu"); } finally { - cleanup(); + await cleanup(); } }); - it("renders items as role=menuitem with their labels", () => { + it("renders items as role=menuitem with their labels", async () => { const { scope, rightClickTrigger, cleanup } = mountInScope(); try { rightClickTrigger(); @@ -188,11 +191,11 @@ describe("ContextMenu behaviour (builtin)", () => { "disabled native activation keeps the menu open", ); } finally { - cleanup(); + await cleanup(); } }); - it("suppresses disabled asChild activation at the composed-control boundary", () => { + it("suppresses disabled asChild activation at the composed-control boundary", async () => { let childClicks = 0; let selections = 0; const { scope, rightClickTrigger, cleanup } = mountInScope( @@ -218,11 +221,11 @@ describe("ContextMenu behaviour (builtin)", () => { assertEquals(selections, 0, "the item selection handler does not run"); assert(scope.querySelector('[role="menu"]'), "the menu stays open"); } finally { - cleanup(); + await cleanup(); } }); - it("suppresses the native menu (preventDefault) on the contextmenu event", () => { + it("suppresses the native menu (preventDefault) on the contextmenu event", async () => { const { scope, cleanup } = mountInScope(); try { const trigger = scope.querySelector('[data-testid="trigger"]')!; @@ -235,11 +238,11 @@ describe("ContextMenu behaviour (builtin)", () => { flushSync(() => trigger.dispatchEvent(evt)); assert(evt.defaultPrevented, "native context menu is prevented"); } finally { - cleanup(); + await cleanup(); } }); - it("selecting an item fires onSelect and closes the menu", () => { + it("selecting an item fires onSelect and closes the menu", async () => { let selected = 0; const { scope, rightClickTrigger, cleanup } = mountInScope( selected++} />, @@ -253,7 +256,7 @@ describe("ContextMenu behaviour (builtin)", () => { assertEquals(selected, 1, "onSelect fired once"); assertEquals(scope.querySelector('[role="menu"]'), null, "menu closed on select"); } finally { - cleanup(); + await cleanup(); } }); @@ -284,11 +287,11 @@ describe("ContextMenu behaviour (builtin)", () => { assertEquals(scope.querySelector('[role="menu"]'), null, "menu closed on keyboard select"); assertEquals(document.activeElement, trigger, "focus returns to the context menu trigger"); } finally { - cleanup(); + await cleanup(); } }); - it("honors a consumer-cancelled click before selecting or closing", () => { + it("honors a consumer-cancelled click before selecting or closing", async () => { let clicks = 0; let selections = 0; const { scope, rightClickTrigger, cleanup } = mountInScope( @@ -318,11 +321,11 @@ describe("ContextMenu behaviour (builtin)", () => { assertEquals(selections, 0, "onSelect does not run after cancellation"); assert(scope.querySelector('[role="menu"]'), "cancelled click keeps the menu open"); } finally { - cleanup(); + await cleanup(); } }); - it("closes on Escape (native document keydown listener)", () => { + it("closes on Escape (native document keydown listener)", async () => { const { scope, rightClickTrigger, cleanup } = mountInScope(); try { rightClickTrigger(); @@ -334,11 +337,11 @@ describe("ContextMenu behaviour (builtin)", () => { }); assertEquals(scope.querySelector('[role="menu"]'), null, "Escape dismissed the menu"); } finally { - cleanup(); + await cleanup(); } }); - it("closes on outside mousedown (native document pointer listener)", () => { + it("closes on outside mousedown (native document pointer listener)", async () => { const { scope, rightClickTrigger, cleanup } = mountInScope(); try { rightClickTrigger(); @@ -350,7 +353,7 @@ describe("ContextMenu behaviour (builtin)", () => { }); assertEquals(scope.querySelector('[role="menu"]'), null, "outside click dismissed the menu"); } finally { - cleanup(); + await cleanup(); } }); }); diff --git a/src/react/components/ui/tooltip.tsx b/src/react/components/ui/tooltip.tsx index cbc52785bb..11b26f359a 100644 --- a/src/react/components/ui/tooltip.tsx +++ b/src/react/components/ui/tooltip.tsx @@ -597,7 +597,7 @@ export function TooltipTrigger( if (event.key === "Escape") context?.dismiss(); }; - const triggerProps: React.HTMLAttributes & { + const triggerProps: AnyProps & React.HTMLAttributes & { ref: React.RefCallback; } = { ...props, @@ -614,8 +614,8 @@ export function TooltipTrigger( if (!child) return {children}; const mergedProps = mergeAsChildProps( - triggerProps as unknown as AnyProps, - childProps as unknown as AnyProps, + triggerProps, + childProps as AnyProps, ); mergedProps["aria-describedby"] = resolvedDescribedBy; mergedProps.id = resolvedId; diff --git a/src/react/primitives/input-box.tsx b/src/react/primitives/input-box.tsx index dcd00f27fc..3478f261d6 100644 --- a/src/react/primitives/input-box.tsx +++ b/src/react/primitives/input-box.tsx @@ -44,7 +44,7 @@ export function handleInputBoxKeyDown( const nativeEvent = e.nativeEvent as KeyboardEvent | undefined; const isComposing = nativeEvent?.isComposing === true || - (e as unknown as { isComposing?: boolean }).isComposing === true || + (e as { isComposing?: boolean }).isComposing === true || e.keyCode === 229; if (e.key !== "Enter" || e.shiftKey || isComposing || !onSubmit) return; diff --git a/src/react/server-render-context.ts b/src/react/server-render-context.ts index 1553d42a7d..0d6148966a 100644 --- a/src/react/server-render-context.ts +++ b/src/react/server-render-context.ts @@ -76,7 +76,7 @@ if (!installedRegistry) { * is stored globally; request data is carried only by the provider stack. */ export function getServerRenderContext( - react: ReactContextRuntime = React as unknown as ReactContextRuntime, + react: ReactContextRuntime = React as ReactContextRuntime, ): unknown { return contextOwner[SERVER_RENDER_CONTEXT_REGISTRY_SYMBOL]!.get(react); } diff --git a/src/registry/project-scoped-registry-manager.ts b/src/registry/project-scoped-registry-manager.ts index 62e31c86e3..c667cbd59c 100644 --- a/src/registry/project-scoped-registry-manager.ts +++ b/src/registry/project-scoped-registry-manager.ts @@ -147,7 +147,7 @@ interface RequestScopeBinding { function trackRegistryManager(manager: ProjectScopedRegistryManager): void { const reference = new WeakRef( - manager as unknown as ProjectScopedRegistryManager, + manager as ProjectScopedRegistryManager, ); registryManagerReferences.add(reference); registryManagerFinalizer.register(manager, reference, reference); diff --git a/src/release-assets/dependency-artifact-builder.ts b/src/release-assets/dependency-artifact-builder.ts index 2f70cbcbc3..95cdfde130 100644 --- a/src/release-assets/dependency-artifact-builder.ts +++ b/src/release-assets/dependency-artifact-builder.ts @@ -134,8 +134,11 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function hasOnlyKeys(value: Record, allowed: readonly string[]): boolean { - const allowedKeys = new Set(allowed); +function hasOnlyKeys( + value: Record, + allowed: readonly K[], +): value is { [P in K]?: unknown } { + const allowedKeys = new Set(allowed); return Object.keys(value).every((key) => allowedKeys.has(key)); } @@ -183,7 +186,7 @@ function parseIdentity(value: unknown): DependencyArtifactIdentity { : value.profile === "standard-v1"; if (!profileMatches) return invalidTaskInput(); - return value as unknown as DependencyArtifactIdentity; + return value as DependencyArtifactIdentity; } function parsePolicy(value: unknown): DependencyArtifactPolicyDecision { diff --git a/src/release-assets/manifest-schema.ts b/src/release-assets/manifest-schema.ts index 2317ee2d4c..2923d46295 100644 --- a/src/release-assets/manifest-schema.ts +++ b/src/release-assets/manifest-schema.ts @@ -173,7 +173,7 @@ function hasUniqueStrings(values: readonly string[]): boolean { return new Set(values).size === values.length; } -function recordEntryCountWithin(value: object, limit: number): boolean { +function recordEntryCountWithin(value: Record, limit: number): boolean { return Object.keys(value).length <= limit; } diff --git a/src/rendering/client/router.ts b/src/rendering/client/router.ts index 5530da9e5b..f1df0224b4 100644 --- a/src/rendering/client/router.ts +++ b/src/rendering/client/router.ts @@ -194,7 +194,8 @@ export class VeryfrontRouter { return; } - const ReactDOMToUse = (globalThis as unknown as GlobalWithReactDOM).ReactDOM ?? ReactDOM; + const ReactDOMToUse = (globalThis as typeof globalThis & GlobalWithReactDOM).ReactDOM ?? + ReactDOM; this.root = ReactDOMToUse.createRoot(rootElement); document.addEventListener("click", this.handleClick); diff --git a/src/rendering/client/state-bridge.ts b/src/rendering/client/state-bridge.ts index 81a4474a8b..5bb6b9fe2f 100644 --- a/src/rendering/client/state-bridge.ts +++ b/src/rendering/client/state-bridge.ts @@ -66,7 +66,7 @@ class StateBridge implements StateStore { this.listeners.set(key, callbacks); } - const typedCallback = callback as unknown as (value: unknown) => void; + const typedCallback = callback as (value: unknown) => void; callbacks.add(typedCallback); return () => { diff --git a/src/rendering/orchestrator/html.ts b/src/rendering/orchestrator/html.ts index a1b19dbc25..4795f44d0f 100644 --- a/src/rendering/orchestrator/html.ts +++ b/src/rendering/orchestrator/html.ts @@ -70,7 +70,8 @@ function toShellFrontmatter( // frontmatter index, while the HTML pipeline supports structured meta/link/ // script/style fields. This boundary narrows only the type view; the shell // immediately validates and snapshots every structured value before use. - return frontmatter as unknown as NonNullable; + const record: Record = frontmatter; + return record as NonNullable; } function injectHeadScriptsAfterImportMap(html: string, scripts: string): string { diff --git a/src/rendering/rsc/server-renderer/tree-processor.ts b/src/rendering/rsc/server-renderer/tree-processor.ts index 67fc1ca1ee..3893ceb6ca 100644 --- a/src/rendering/rsc/server-renderer/tree-processor.ts +++ b/src/rendering/rsc/server-renderer/tree-processor.ts @@ -34,7 +34,7 @@ export async function renderTree { const keys = Object.keys(child); const result = keys.length === 1 && keys[0] === "children" - ? (child as unknown as { children: React.ReactNode }).children + ? (child as { children?: React.ReactNode }).children : child; cache.set(child, result); diff --git a/src/routing/client/dom-utils.test-helpers.ts b/src/routing/client/dom-utils.test-helpers.ts index 3add7f59dc..d12ba55b30 100644 --- a/src/routing/client/dom-utils.test-helpers.ts +++ b/src/routing/client/dom-utils.test-helpers.ts @@ -101,7 +101,7 @@ export function createMockAnchor( href: string, attributes: Record = {}, ): HTMLAnchorElement { - return new MockHTMLAnchorElement(href, attributes) as unknown as HTMLAnchorElement; + return new MockHTMLAnchorElement(href, attributes) as MockHTMLAnchorElement & HTMLAnchorElement; } export function createMockElement( @@ -112,8 +112,8 @@ export function createMockElement( return new MockHTMLElement( tagName, attributes, - parent as unknown as MockHTMLElement | MockHTMLAnchorElement | null, - ) as unknown as HTMLElement; + parent as (HTMLElement & MockHTMLElement) | (HTMLAnchorElement & MockHTMLAnchorElement) | null, + ) as MockHTMLElement & HTMLElement; } function setupGlobalMock( @@ -132,7 +132,7 @@ function setupGlobalMock( export function setupHTMLAnchorElementMock(): { cleanup: () => void } { return setupGlobalMock( "HTMLAnchorElement", - MockHTMLAnchorElement as unknown as typeof HTMLAnchorElement, + MockHTMLAnchorElement as typeof MockHTMLAnchorElement & typeof HTMLAnchorElement, originalHTMLAnchorElement, ); } @@ -140,13 +140,17 @@ export function setupHTMLAnchorElementMock(): { cleanup: () => void } { export function setupHTMLElementMock(): { cleanup: () => void } { return setupGlobalMock( "HTMLElement", - MockHTMLElement as unknown as typeof HTMLElement, + MockHTMLElement as typeof MockHTMLElement & typeof HTMLElement, originalHTMLElement, ); } export function setupElementMock(): { cleanup: () => void } { - return setupGlobalMock("Element", MockElement as unknown as typeof Element, originalElement); + return setupGlobalMock( + "Element", + MockElement as typeof MockElement & typeof Element, + originalElement, + ); } export function setupDOMMocks(): { cleanup: () => void } { diff --git a/src/runtime/model-call-context.ts b/src/runtime/model-call-context.ts index dafe9964eb..c0a7473261 100644 --- a/src/runtime/model-call-context.ts +++ b/src/runtime/model-call-context.ts @@ -51,11 +51,11 @@ export type ModelCallTool = * provider options contain only validated prompt-cache metadata. Other * provider-specific values are excluded because run events are durable. */ -export interface AgentRunModelCallContextEvent { +export type AgentRunModelCallContextEvent = { type: "AGENT_RUN_MODEL_CALL_CONTEXT"; messages: ModelCallMessage[]; tools?: ModelCallTool[]; -} +}; /** Event produced by an agent run runtime boundary. */ export type AgentRunEvent = AgentRunModelCallContextEvent; diff --git a/src/schemas/lazy.ts b/src/schemas/lazy.ts index 63c679a8c9..401ff5f1f3 100644 --- a/src/schemas/lazy.ts +++ b/src/schemas/lazy.ts @@ -59,7 +59,7 @@ export function lazySchema(getSchema: () => Schema): Schema { } }; const facade: Schema = { - _output: undefined as unknown as T, + _output: undefined as never, optional: () => schema().optional(), nullable: () => schema().nullable(), nullish: () => schema().nullish(), diff --git a/src/security/http/response/builder.ts b/src/security/http/response/builder.ts index c992a4112f..f2fe3908d6 100644 --- a/src/security/http/response/builder.ts +++ b/src/security/http/response/builder.ts @@ -52,7 +52,7 @@ export class ResponseBuilder implements FluentMethodsContext, ResponseMethodsCon // but TS can't verify this because property-assigned methods with generic // `this` parameters resolve to the constraint type, not the class type. staticHelpers.setResponseBuilderClass( - ResponseBuilder as unknown as Parameters[0], + ResponseBuilder as Parameters[0], ); export function createResponseBuilder(config?: ResponseBuilderConfig): ResponseBuilder { diff --git a/src/security/sandbox/project-worker.ts b/src/security/sandbox/project-worker.ts index 1600f14217..082d3bb547 100644 --- a/src/security/sandbox/project-worker.ts +++ b/src/security/sandbox/project-worker.ts @@ -1135,7 +1135,7 @@ export class ProjectWorker { return; } clearTimeout(pending.timer); - pending.resolve(data as unknown as WorkerResponse); + pending.resolve(data as WorkerResponse); this.pending.delete(id); } return; diff --git a/src/security/sandbox/worker-egress-guard.ts b/src/security/sandbox/worker-egress-guard.ts index 0bb72593e2..d219751f01 100644 --- a/src/security/sandbox/worker-egress-guard.ts +++ b/src/security/sandbox/worker-egress-guard.ts @@ -1775,7 +1775,7 @@ export function installWorkerEgressGuard( ); } return await guardedWorkerConnect( - options as unknown as Deno.ConnectOptions, + options as Deno.ConnectOptions & Record, baseOptions, runtime, ); diff --git a/src/security/secure-fs.ts b/src/security/secure-fs.ts index b80f0e8da1..5bbb719085 100644 --- a/src/security/secure-fs.ts +++ b/src/security/secure-fs.ts @@ -120,7 +120,7 @@ const SECURE_FS_IMMUTABLE_AUTHORITY_KEYS = [ "maxWholeFileReadBytes", ] as const; -function hardenSecureFsAuthority(target: object): void { +function hardenSecureFsAuthority(target: SecureFs): void { for (const key of SECURE_FS_IMMUTABLE_AUTHORITY_KEYS) { const descriptor = objectGetOwnPropertyDescriptor(target, key); if (descriptor === undefined || !("value" in descriptor)) { diff --git a/src/server/handlers/dev/dashboard/api.ts b/src/server/handlers/dev/dashboard/api.ts index a7cb7a5022..c45b7c93da 100644 --- a/src/server/handlers/dev/dashboard/api.ts +++ b/src/server/handlers/dev/dashboard/api.ts @@ -191,7 +191,7 @@ function handleListAgents(): Response { const allTools = Array.from(toolRegistry.getAll().entries()); const list = Array.from(agentRegistry.getAll().entries()).map(([id, agent]) => { - const cfg = agent.config as unknown as Record; + const cfg = agent.config; let system: string | null = null; if (typeof cfg.system === "string") system = cfg.system; diff --git a/src/server/handlers/dev/framework-candidates.generated.ts b/src/server/handlers/dev/framework-candidates.generated.ts index fac7aa2f01..64c046da29 100644 --- a/src/server/handlers/dev/framework-candidates.generated.ts +++ b/src/server/handlers/dev/framework-candidates.generated.ts @@ -3080,6 +3080,9 @@ export const FRAMEWORK_CANDIDATES: readonly string[] = [ "ConversationStore}.", "ConversationSummary", "ConversationSummary):", + "ConversationSummary)[]", + "ConversationSummary)[],", + "ConversationSummary)[];", "ConversationSummary,", "ConversationSummary;", "ConversationSummary[]", @@ -9318,6 +9321,7 @@ export const FRAMEWORK_CANDIDATES: readonly string[] = [ "cannot", "cannot.", "canonical", + "canonical,", "canonical;", "canvas", "capabilities", @@ -11268,9 +11272,9 @@ export const FRAMEWORK_CANDIDATES: readonly string[] = [ "deferredRefreshScopesRef.current.delete(candidate)", "deferredRefreshScopesRef.current.delete(previous);", "deferredRefreshScopesRef.current.delete(scope)", + "defineDataProperty(", "defineDataProperty(output,", "defineDataProperty(target,", - "defineDataProperty(target:", "defineJsonProperty(", "defineJsonProperty(envelope,", "defineJsonProperty(output,", @@ -18326,6 +18330,7 @@ export const FRAMEWORK_CANDIDATES: readonly string[] = [ "registryReady:", "regular", "rehydrateJsonValue(", + "rehydrateJsonValue(canonical)", "rehydrateJsonValue(snapshot)", "rehydrateJsonValue(value:", "reject", diff --git a/src/server/handlers/request/internal-agent-run.test-helpers.ts b/src/server/handlers/request/internal-agent-run.test-helpers.ts index ceccee24b6..ef23f5b435 100644 --- a/src/server/handlers/request/internal-agent-run.test-helpers.ts +++ b/src/server/handlers/request/internal-agent-run.test-helpers.ts @@ -86,7 +86,7 @@ export async function createControlPlaneSignature( } export function createCtx(publicKeyPem?: string): HandlerContext { - return { + const ctx = { projectDir: "/project", adapter: { env: { @@ -99,7 +99,8 @@ export function createCtx(publicKeyPem?: string): HandlerContext { projectSlug: "demo-project", projectId: "proj-1", isLocalProject: false, - } as unknown as HandlerContext; + }; + return ctx as HandlerContext & typeof ctx; } export function createAgent(id = "agent-1"): Agent { diff --git a/src/server/handlers/request/ssr/ssr.handler.test-helpers.ts b/src/server/handlers/request/ssr/ssr.handler.test-helpers.ts index 5fe83ebe8f..a4b68c3a7b 100644 --- a/src/server/handlers/request/ssr/ssr.handler.test-helpers.ts +++ b/src/server/handlers/request/ssr/ssr.handler.test-helpers.ts @@ -3,7 +3,7 @@ import type { HandlerContext } from "../../types.ts"; import type { SSRRenderOptions, SSRServiceLike } from "../../../services/rendering/ssr.service.ts"; export function createMockAdapter(): RuntimeAdapter { - return { + const adapter = { id: "memory", name: "mock", capabilities: { @@ -31,7 +31,8 @@ export function createMockAdapter(): RuntimeAdapter { }, server: { createHandler: () => () => new Response() }, serve: () => Promise.resolve({ close: () => Promise.resolve() } as any), - } as unknown as RuntimeAdapter; + }; + return adapter as RuntimeAdapter & typeof adapter; } export function makeCtx(overrides: Partial = {}): HandlerContext { diff --git a/src/server/index.ts b/src/server/index.ts index 55eba8e64c..b9185e6338 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -204,7 +204,7 @@ function toNativeResponse(res: Response): Response { if (res instanceof _NativeResponse) return res; // TS narrows to `never` after the instanceof check because it can't see the // runtime class divergence between DNT's polyfill Response and native Response. - const src = res as unknown as Response; + const src = res as Response; return new _NativeResponse(src.body, { status: src.status, statusText: src.statusText, @@ -444,8 +444,8 @@ export async function createHandler( }; nodeUpgradeLifecycle.attach( - httpServer as unknown as NodeUpgradeEventSource, - upgradeListener as unknown as (...args: unknown[]) => void, + httpServer as NodeUpgradeEventSource, + upgradeListener as (...args: unknown[]) => void, ); }; diff --git a/src/server/project-env/hosted-authorization.test.ts b/src/server/project-env/hosted-authorization.test.ts index b52d050716..bde3673f57 100644 --- a/src/server/project-env/hosted-authorization.test.ts +++ b/src/server/project-env/hosted-authorization.test.ts @@ -64,7 +64,7 @@ describe("hosted project environment authorization", () => { setEnv("VERYFRONT_API_INTERNAL_USER", "test-internal-user"); setEnv("VERYFRONT_API_INTERNAL_PASS", "test-internal-pass"); const requests: Array<{ url: string; authorization: string | null }> = []; - globalThis.fetch = ((input, init) => { + globalThis.fetch = ((input: RequestInfo | URL, init?: RequestInit) => { const url = input instanceof Request ? input.url : String(input); requests.push({ url, diff --git a/src/server/services/rsc/endpoints/action-authorization-snapshot.ts b/src/server/services/rsc/endpoints/action-authorization-snapshot.ts index 198a875285..35ea7eeec3 100644 --- a/src/server/services/rsc/endpoints/action-authorization-snapshot.ts +++ b/src/server/services/rsc/endpoints/action-authorization-snapshot.ts @@ -243,7 +243,7 @@ function snapshotArray( iteratorSymbol, freeze(() => createStableArrayIterator(output)), ); - return freeze(output) as unknown as Readonly; + return freeze(output) as Readonly; } finally { leaveContainer(state, value); } diff --git a/src/server/services/rsc/endpoints/action-parser.ts b/src/server/services/rsc/endpoints/action-parser.ts index 4b73cc97e2..7cf48dd18b 100644 --- a/src/server/services/rsc/endpoints/action-parser.ts +++ b/src/server/services/rsc/endpoints/action-parser.ts @@ -51,7 +51,7 @@ function snapshotArgs(value: unknown): unknown[] | null { ) return null; try { - const descriptors = getOwnPropertyDescriptors(value) as unknown as Record< + const descriptors = getOwnPropertyDescriptors(value) as Record< string, PropertyDescriptor >; diff --git a/src/server/services/rsc/endpoints/endpoint-router.test-helpers.ts b/src/server/services/rsc/endpoints/endpoint-router.test-helpers.ts index 5933118746..06a780bc4a 100644 --- a/src/server/services/rsc/endpoints/endpoint-router.test-helpers.ts +++ b/src/server/services/rsc/endpoints/endpoint-router.test-helpers.ts @@ -28,7 +28,7 @@ export function createMockAdapter( if (await exists(path)) return ""; throw new Deno.errors.NotFound("not found"); }); - return { + const adapter = { id: "memory", name: "mock", capabilities: { @@ -69,7 +69,8 @@ export function createMockAdapter( createHandler: () => () => new Response(), }, serve: () => Promise.resolve({ close: () => Promise.resolve() } as any), - } as unknown as RuntimeAdapter; + }; + return adapter as RuntimeAdapter & typeof adapter; } function createKnownFilesReader( @@ -106,15 +107,15 @@ function createKnownFilesReader( /** Config with RSC enabled */ export const rscEnabledConfig: VeryfrontConfig = { experimental: { rsc: true }, -} as unknown as VeryfrontConfig; +} as VeryfrontConfig & { experimental: { rsc: boolean } }; /** Config with RSC disabled */ export const rscDisabledConfig: VeryfrontConfig = { experimental: { rsc: false }, -} as unknown as VeryfrontConfig; +} as VeryfrontConfig & { experimental: { rsc: boolean } }; /** Config with no experimental section */ -export const noExperimentalConfig: VeryfrontConfig = {} as unknown as VeryfrontConfig; +export const noExperimentalConfig: VeryfrontConfig = {} as VeryfrontConfig; export function makeParams( overrides: Partial & { pathname: string }, diff --git a/src/server/unhandled-rejection-guard.ts b/src/server/unhandled-rejection-guard.ts index 0438b0c6bd..0b2019e2fd 100644 --- a/src/server/unhandled-rejection-guard.ts +++ b/src/server/unhandled-rejection-guard.ts @@ -96,7 +96,7 @@ function describeReason(reason: unknown): { error: string; stack?: string } { } function resolveDefaultTarget(): GuardEventTarget | undefined { - const candidate = globalThis as unknown as Partial; + const candidate = globalThis as Partial; return typeof candidate.addEventListener === "function" && typeof candidate.removeEventListener === "function" ? candidate as GuardEventTarget diff --git a/src/skill/document-parser.ts b/src/skill/document-parser.ts index 59aa92ca0d..f94e12f916 100644 --- a/src/skill/document-parser.ts +++ b/src/skill/document-parser.ts @@ -70,7 +70,7 @@ function invalidFrontmatter(): never { ); } -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return call(objectHasOwnProperty, value, [key]); } diff --git a/src/skill/parser.ts b/src/skill/parser.ts index 0af719b0c8..a52806d892 100644 --- a/src/skill/parser.ts +++ b/src/skill/parser.ts @@ -58,7 +58,7 @@ function trim(value: string): string { return apply(stringTrim, value, []) as string; } -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return apply(objectHasOwnProperty, value, [key]) as boolean; } diff --git a/src/skill/path-safety.ts b/src/skill/path-safety.ts index 37c6effee2..b65975c38e 100644 --- a/src/skill/path-safety.ts +++ b/src/skill/path-safety.ts @@ -46,7 +46,7 @@ const stringReplaceAll = String.prototype.replaceAll; const stringSplit = String.prototype.split; const stringStartsWith = String.prototype.startsWith; -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return apply(objectHasOwnProperty, value, [key]) as boolean; } diff --git a/src/skill/tools.ts b/src/skill/tools.ts index 6d833e5f3b..7a1a5450d1 100644 --- a/src/skill/tools.ts +++ b/src/skill/tools.ts @@ -13,7 +13,8 @@ import { defineSchema } from "#veryfront/schemas/index.ts"; import { LOAD_SKILL_POLICY_CLAUSES } from "./load-skill-policy.ts"; import { tool } from "#veryfront/tool/factory.ts"; import type { Tool, ToolExecutionContext } from "#veryfront/tool"; -import { createFileSystem } from "#veryfront/platform/compat/fs.ts"; +import { createFileSystem, type FileSystem } from "#veryfront/platform/compat/fs.ts"; +import type { FileSystemAdapter } from "#veryfront/platform/adapters/base.ts"; import { isProxyWithoutHooks } from "#veryfront/platform/compat/error-introspection.ts"; import { captureByteReadCapabilities, @@ -153,7 +154,7 @@ async function readSkillFile( } function requireExactSkillReader( - fileSystem: object, + fileSystem: FileSystemAdapter | FileSystem, ): (path: string, byteLimit: number) => Promise { const reader = captureByteReadCapabilities(fileSystem, "Skill filesystem").exact; if (!reader) { diff --git a/src/skill/validation.ts b/src/skill/validation.ts index 2399e4eb96..70a36ff22a 100644 --- a/src/skill/validation.ts +++ b/src/skill/validation.ts @@ -33,7 +33,7 @@ const NativeRangeError = RangeError; const NativeTypeError = TypeError; const ownKeys = Reflect.ownKeys; -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return apply(hasOwnProperty, object, [key]) as boolean; } @@ -322,7 +322,7 @@ export function normalizeSkillDefinition(id: string, value: Skill): Skill { id: registryId, metadata, rootPath, - ...(fsAdapter === undefined ? {} : { fsAdapter: fsAdapter as unknown as FileSystemAdapter }), + ...(fsAdapter === undefined ? {} : { fsAdapter: fsAdapter as FileSystemAdapter }), ...(ownerAgentId === undefined ? {} : { ownerAgentId }), ...(shortName === undefined ? {} : { shortName }), }); diff --git a/src/tool/data-properties.ts b/src/tool/data-properties.ts index b5d79996af..0289fd4c19 100644 --- a/src/tool/data-properties.ts +++ b/src/tool/data-properties.ts @@ -19,7 +19,7 @@ function rejectProxy(value: object, label: string): void { if (isProxyWithoutHooks(value)) throw invalidDataProperties(label); } -function hasOwn(value: object, key: PropertyKey): boolean { +function hasOwn(value: PropertyDescriptor, key: PropertyKey): boolean { return ReflectApply(ObjectPrototypeHasOwnProperty, value, [key]) as boolean; } diff --git a/src/tool/factory.ts b/src/tool/factory.ts index 30d5adf418..e09acb454a 100644 --- a/src/tool/factory.ts +++ b/src/tool/factory.ts @@ -17,7 +17,7 @@ const objectHasOwnProperty = Object.prototype.hasOwnProperty; const ownKeys = Reflect.ownKeys; const structuredCloneValue = globalThis.structuredClone; -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return apply(objectHasOwnProperty, object, [key]) as boolean; } diff --git a/src/tool/remote-mcp.ts b/src/tool/remote-mcp.ts index 433314f4a7..4763bfb3dc 100644 --- a/src/tool/remote-mcp.ts +++ b/src/tool/remote-mcp.ts @@ -95,7 +95,7 @@ function isJsonRpcErrorObject( function isJsonRpcToolErrorResult(value: unknown): value is JsonRpcToolErrorResult { if (!isRecord(value)) return false; - const candidate = value as unknown as Partial; + const candidate = value as Partial; return candidate[JSON_RPC_TOOL_ERROR_RESULT] === true && isRecord(candidate.result); } diff --git a/src/tool/sleep.ts b/src/tool/sleep.ts index 66f3c0a9e2..851ea1d7f0 100644 --- a/src/tool/sleep.ts +++ b/src/tool/sleep.ts @@ -38,7 +38,7 @@ function createSleepToolInputSchema(maxSeconds: number): Schema; + }) as Schema; } /** Input payload for sleep tool. */ diff --git a/src/transforms/esm/http-cache-types.ts b/src/transforms/esm/http-cache-types.ts index b7398f6a6a..d63867b444 100644 --- a/src/transforms/esm/http-cache-types.ts +++ b/src/transforms/esm/http-cache-types.ts @@ -61,7 +61,7 @@ export function brand>( ): TBranded { // Single, centralized unsound widening: attaching a phantom brand to a // runtime value. This is the only place this cast should occur. - return value as unknown as TBranded; + return value as TBranded; } /** diff --git a/src/transforms/esm/http-cache.test.ts b/src/transforms/esm/http-cache.test.ts index 1ed5ab43b0..351329602e 100644 --- a/src/transforms/esm/http-cache.test.ts +++ b/src/transforms/esm/http-cache.test.ts @@ -283,7 +283,7 @@ describe("HTTP Bundle Cache", { sanitizeResources: false, sanitizeOps: false }, it("allows a cold HTTP module response to exceed five seconds", async () => { let fetchCount = 0; - const mockFetch = ((_input, init) => { + const mockFetch = ((_input: RequestInfo | URL, init?: RequestInit) => { fetchCount += 1; return new Promise((resolve, reject) => { const signal = init?.signal; @@ -326,7 +326,7 @@ describe("HTTP Bundle Cache", { sanitizeResources: false, sanitizeOps: false }, releaseFetch = resolve; }); - const mockFetch = ((_input, init) => { + const mockFetch = ((_input: RequestInfo | URL, init?: RequestInit) => { fetchCount += 1; markFetchStarted(); return new Promise((resolve, reject) => { @@ -759,7 +759,7 @@ describe("HTTP Bundle Cache", { sanitizeResources: false, sanitizeOps: false }, let releaseFetch!: () => void; const fetchStarted = Promise.withResolvers(); - const mockFetch = ((_input, init) => { + const mockFetch = ((_input: RequestInfo | URL, init?: RequestInit) => { fetchCount += 1; fetchStarted.resolve(); return new Promise((resolve, reject) => { diff --git a/src/transforms/mdx/compiler/mdx-compiler.ts b/src/transforms/mdx/compiler/mdx-compiler.ts index 11e6ee64af..bd2348ace9 100644 --- a/src/transforms/mdx/compiler/mdx-compiler.ts +++ b/src/transforms/mdx/compiler/mdx-compiler.ts @@ -15,7 +15,7 @@ const ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty; const ReflectApply = Reflect.apply; const ReflectGetOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor; -function readOwnDataProperty(value: object, key: PropertyKey): unknown { +function readOwnDataProperty(value: Error, key: PropertyKey): unknown { try { const descriptor = ReflectGetOwnPropertyDescriptor(value, key); if ( diff --git a/src/transforms/mdx/esm-module-loader/jsx/runtime-loader.ts b/src/transforms/mdx/esm-module-loader/jsx/runtime-loader.ts index 712a869586..9a038c3e9d 100644 --- a/src/transforms/mdx/esm-module-loader/jsx/runtime-loader.ts +++ b/src/transforms/mdx/esm-module-loader/jsx/runtime-loader.ts @@ -6,7 +6,12 @@ export interface JSXRuntime { } export async function loadJSXRuntime(): Promise { - const runtime = (await import("react/jsx-dev-runtime")) as unknown as Record; + const runtime: { + Fragment?: unknown; + jsx?: unknown; + jsxs?: unknown; + jsxDEV?: unknown; + } = await import("react/jsx-dev-runtime"); return { Fragment: runtime.Fragment, diff --git a/src/transforms/mdx/index.ts b/src/transforms/mdx/index.ts index 3d30502f04..288510e6ce 100644 --- a/src/transforms/mdx/index.ts +++ b/src/transforms/mdx/index.ts @@ -180,7 +180,7 @@ export const mdxRenderer = new Proxy({} as MDXRenderer, { }, set(_target, prop, value) { const instance = getMDXRendererInstance(); - (instance as unknown as Record)[prop] = value; + (instance as MDXRenderer & Record)[prop] = value; return true; }, has(_target, prop) { diff --git a/src/transforms/pipeline/cache-identity.ts b/src/transforms/pipeline/cache-identity.ts index 6fe24e4f10..d1772e03a8 100644 --- a/src/transforms/pipeline/cache-identity.ts +++ b/src/transforms/pipeline/cache-identity.ts @@ -52,7 +52,7 @@ function encodedByteLength(value: string): number { return ReflectApply(TypedArrayByteLengthGetter, bytes, []) as number; } -function hasOwn(object: object, key: PropertyKey): boolean { +function hasOwn(object: PropertyDescriptor, key: PropertyKey): boolean { return ReflectApply(ObjectPrototypeHasOwnProperty, object, [key]) as boolean; } diff --git a/src/transforms/pipeline/stages/browser-server-exports-strip.ts b/src/transforms/pipeline/stages/browser-server-exports-strip.ts index de4b076def..761d0fbf0a 100644 --- a/src/transforms/pipeline/stages/browser-server-exports-strip.ts +++ b/src/transforms/pipeline/stages/browser-server-exports-strip.ts @@ -107,7 +107,7 @@ function nodeName(value: unknown): string | null { function bodyOf(ast: ASTNode): Node[] { const program = (ast as { program?: unknown }).program; - const source = isNode(program) ? program : (ast as unknown as Node); + const source: Node = isNode(program) ? program : ast; const body = source.body; return Array.isArray(body) ? body.filter(isNode) : []; } @@ -1009,7 +1009,7 @@ function dropUnusedImportBindings(body: Node[], hookClosure: Set): Node[ function setBody(ast: ASTNode, body: Node[]): void { const program = (ast as { program?: unknown }).program; - const target = isNode(program) ? program : (ast as unknown as Node); + const target: Node = isNode(program) ? program : ast; target.body = body; } diff --git a/src/types/entities/getEntityInfo.ts b/src/types/entities/getEntityInfo.ts index 4d2aa43630..b9748f3684 100644 --- a/src/types/entities/getEntityInfo.ts +++ b/src/types/entities/getEntityInfo.ts @@ -1,12 +1,12 @@ /** Bounded page and layout entity discovery. @module types/entities/getEntityInfo */ import { extract } from "#std/front-matter/yaml.ts"; -import { createFileSystem } from "#veryfront/platform/compat/fs.ts"; +import { createFileSystem, type FileSystem } from "#veryfront/platform/compat/fs.ts"; import { isCanonicalNotFoundError } from "#veryfront/platform/compat/not-found-error.ts"; import * as pathHelper from "#veryfront/compat/path"; import { detectEntityType, normalizeFrontmatter } from "../entities.ts"; import type { Entity, EntityInfo, Frontmatter } from "../entities.ts"; -import type { RuntimeAdapter } from "#veryfront/platform/adapters/base.ts"; +import type { FileSystemAdapter, RuntimeAdapter } from "#veryfront/platform/adapters/base.ts"; import { withSpan } from "#veryfront/observability/tracing/otlp-setup.ts"; import { logger as baseLogger } from "#veryfront/utils/logger/index.ts"; import { DEFAULT_MAX_FILE_SIZE_BYTES } from "#veryfront/utils/constants/buffers.ts"; @@ -1639,7 +1639,9 @@ function isLexicallyWithinRoot( : hasPathPrefix(filePath, virtualRoot); } -function hasNoSymlinkSemantics(fileSystem: object): boolean { +function hasNoSymlinkSemantics( + fileSystem: FileSystemAdapter | FileSystem, +): boolean { try { const descriptor = Reflect.getOwnPropertyDescriptor(fileSystem, "symlinkSemantics"); return descriptor !== undefined && diff --git a/src/utils/import-lockfile.ts b/src/utils/import-lockfile.ts index 0ba764da1c..c06b8ab554 100644 --- a/src/utils/import-lockfile.ts +++ b/src/utils/import-lockfile.ts @@ -291,7 +291,7 @@ function isRecord(value: unknown): value is Record { } function getOwnDataProperty( - value: object, + value: Record | readonly unknown[], key: PropertyKey, ): { readonly value: unknown } | undefined { const descriptor = objectGetOwnPropertyDescriptor(value, key); diff --git a/src/utils/response-body.ts b/src/utils/response-body.ts index 887f4d9f90..3cda7adaa8 100644 --- a/src/utils/response-body.ts +++ b/src/utils/response-body.ts @@ -31,7 +31,7 @@ const arrayBufferByteLengthGetter = arrayBufferByteLengthGetterCandidate; function requireTypedArrayGetter( property: "buffer" | "byteLength" | "byteOffset", -): (this: object) => unknown { +): (this: Uint8Array) => unknown { const getter = Object.getOwnPropertyDescriptor(typedArrayPrototype, property)?.get; if (typeof getter !== "function") { throw new TypeError(`Required Uint8Array ${property} intrinsic is unavailable`); diff --git a/src/webhook/validation.ts b/src/webhook/validation.ts index 604f53bd55..e4fd779a27 100644 --- a/src/webhook/validation.ts +++ b/src/webhook/validation.ts @@ -338,7 +338,7 @@ function normalizeFilter(value: unknown): WebhookEventFilter | undefined { return snapshotFilterValue( normalized, "Webhook eventFilter", - ) as unknown as WebhookEventFilter; + ) as WebhookEventFilter; } function normalizeAgentMessage( diff --git a/src/workflow/claude-code/tool.ts b/src/workflow/claude-code/tool.ts index 808c670234..0ad554b4e5 100644 --- a/src/workflow/claude-code/tool.ts +++ b/src/workflow/claude-code/tool.ts @@ -5,7 +5,7 @@ */ import { defineSchema } from "#veryfront/schemas/index.ts"; -import type { InferSchema, Schema } from "#veryfront/extensions/schema/index.ts"; +import type { InferSchema } from "#veryfront/extensions/schema/index.ts"; import type { Tool } from "#veryfront/tool"; import { executeAgent } from "./agent.ts"; import type { ClaudeCodeMode, ClaudeCodeResult } from "./types.ts"; @@ -81,7 +81,7 @@ export const claudeCodeTool: Tool = { type: "function", description: "Run a Claude Code agent for complex coding tasks. " + "Supports file editing, bash commands, and iterative problem-solving.", - inputSchema: getClaudeCodeInputSchema() as unknown as Schema, + inputSchema: getClaudeCodeInputSchema(), inputSchemaJson: { type: "object", properties: { diff --git a/src/workflow/claude-code/wire-protocol.ts b/src/workflow/claude-code/wire-protocol.ts index 90ed4faa6c..7bcdb6f55d 100644 --- a/src/workflow/claude-code/wire-protocol.ts +++ b/src/workflow/claude-code/wire-protocol.ts @@ -52,13 +52,13 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function readDataProperty(record: object, key: string): unknown { +function readDataProperty(record: Record, key: string): unknown { const descriptor = Object.getOwnPropertyDescriptor(record, key); return descriptor?.enumerable === true && "value" in descriptor ? descriptor.value : undefined; } function hasExactDataProperties( - record: object, + record: Record, allowedKeys: readonly string[], ): boolean { const allowed = new Set(allowedKeys); @@ -69,7 +69,10 @@ function hasExactDataProperties( }); } -function hasExactEventProperties(record: object, eventKeys: readonly string[]): boolean { +function hasExactEventProperties( + record: Record, + eventKeys: readonly string[], +): boolean { return hasExactDataProperties(record, [...BASE_EVENT_KEYS, ...eventKeys]); } diff --git a/src/workflow/dsl/workflow.ts b/src/workflow/dsl/workflow.ts index 3d5ff8124d..4b48c8c509 100644 --- a/src/workflow/dsl/workflow.ts +++ b/src/workflow/dsl/workflow.ts @@ -69,7 +69,7 @@ export function workflow( // Auto-register for discovery in dev tools // Use type assertion since registry only stores metadata, not the full generic type - workflowRegistry.register(wf as unknown as Workflow); + workflowRegistry.register(wf as Workflow); return wf; } diff --git a/src/workflow/executor/workflow-definition-snapshot.ts b/src/workflow/executor/workflow-definition-snapshot.ts index fc7d4f37f0..68ed8b8c59 100644 --- a/src/workflow/executor/workflow-definition-snapshot.ts +++ b/src/workflow/executor/workflow-definition-snapshot.ts @@ -650,7 +650,7 @@ export function captureWorkflowStringList( seen.add(entry); captured.push(entry); } - return Object.freeze(captured) as unknown as string[]; + return Object.freeze(captured) as string[]; } function captureRetryConfig(value: unknown, label: string): RetryConfig | undefined { @@ -1040,7 +1040,7 @@ function captureNodeList( captured.push(captureNode(values[index], `${label} node at index ${index}`, state, depth)); } validateDependencyGraph(captured, label); - return Object.freeze(captured) as unknown as WorkflowNode[]; + return Object.freeze(captured) as WorkflowNode[]; } function captureDefinition( @@ -1153,7 +1153,7 @@ export function captureWorkflowDefinitions( seenIds.add(workflow.id); captured.push(workflow); } - return Object.freeze(captured) as unknown as WorkflowDefinition[]; + return Object.freeze(captured) as WorkflowDefinition[]; } /** Capture nodes returned by a workflow or composite builder. */ @@ -1179,5 +1179,5 @@ export function captureWorkflowNodes( export function captureWorkflowMapItems(value: unknown, label: string): unknown[] { const entries = inspectDenseArrayValues(value, label); const captured = captureWorkflowStaticValue(entries, label); - return Object.freeze(captured) as unknown as unknown[]; + return Object.freeze(captured) as unknown[]; } diff --git a/src/workflow/react/use-workflow-list.ts b/src/workflow/react/use-workflow-list.ts index 0d2321daa6..f0f38321a3 100644 --- a/src/workflow/react/use-workflow-list.ts +++ b/src/workflow/react/use-workflow-list.ts @@ -99,7 +99,7 @@ export function useWorkflowList(options: UseWorkflowListOptions = {}): UseWorkfl const data: { runs?: WorkflowRun[]; cursor?: string; totalCount?: number } = await response .json(); - const fetchedRuns: WorkflowRun[] = data.runs ?? (data as unknown as WorkflowRun[]); + const fetchedRuns: WorkflowRun[] = data.runs ?? (data as WorkflowRun[]); const nextCursor: string | undefined = data.cursor; const total: number | undefined = data.totalCount; diff --git a/src/workflow/registry.ts b/src/workflow/registry.ts index 17606b417e..1b427b3919 100644 --- a/src/workflow/registry.ts +++ b/src/workflow/registry.ts @@ -156,7 +156,14 @@ function extractMetadata(definition: WorkflowDefinition): WorkflowMetadata { dependsOn: node.dependsOn === undefined ? undefined : Object.freeze([...node.dependsOn]), }; - const config = node.config as unknown as Record; + const config = node.config as { + agent?: unknown; + tool?: unknown; + message?: unknown; + nodes?: unknown; + then?: unknown; + else?: unknown; + }; if (type === "step") { const agentValue = config.agent; diff --git a/src/workflow/types.ts b/src/workflow/types.ts index ce4f7f650e..c161246e97 100644 --- a/src/workflow/types.ts +++ b/src/workflow/types.ts @@ -432,7 +432,7 @@ export function captureApprovalApprovers( seen.add(approver); captured.push(approver); } - return Object.freeze(captured) as unknown as string[]; + return Object.freeze(captured) as string[]; } /**