overengineeringstudio / overengineeringstudio/effect-utils

Add export runtime-safety checks for package entrypoints

Open
#727 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:tooling origin:agent
Dominant language
TypeScript
Stars
82
Forks
2
Avg merge
1d 8h
Merged PRs (30d)
121

Description

Parent context: #698 and #719

Problem

This repo now has package entrypoints that must be import-safe for different runtimes. For example, @overeng/notion-datasource-sync/cli/effect-command must remain safe for the Bun-compiled root notion CLI, while store/replica/runtime entrypoints may reach Node 24 node:sqlite. We need a principled gate so future exports cannot accidentally reach a forbidden runtime dependency transitively.

Direction

Prefer an Oxc/Oxlint-aligned implementation over dependency-cruiser. We already use Oxlint/Oxfmt, and the long-term check should stay close to that toolchain instead of adding an older standalone dependency graph stack.

Use two layers:

  1. Direct import guard in Oxlint now. Add no-restricted-imports or a small local Oxlint JS plugin rule to ban direct node:sqlite imports outside explicitly owned datasource-sync runtime files. This gives fast editor/CI feedback for the simplest class of mistakes.
  2. Oxc-powered export graph checker for transitive safety. Build a small repo-local checker that uses Oxc tooling to parse and resolve the module graph from package export entrypoints, then validates reachability against explicit runtime policy.

Do not rely on plain no-restricted-imports for the core invariant. Oxlint’s built-in rule is per-file/static-import oriented; the invariant we need is export-entrypoint reachability.

Proposed Implementation

Use the Oxc ecosystem as the graph substrate:

  • oxc-parser: parse TS/TSX quickly and collect ESM import/export metadata.
  • oxc-resolver: resolve relative imports, package subpaths, TS paths, and package exports with Node-compatible semantics.
  • A repo-local policy file mapping package export subpaths to runtime classes.

Initial runtime classes:

type ExportRuntime = 'browser' | 'bun' | 'node' | 'node24-sqlite'

Initial policy sketch:

  • browser: no reachable node:*, no bun:*, no Node-only workspace exports.
  • bun: allow only a reviewed Bun-compatible builtin allowlist; explicitly deny node:sqlite.
  • node: allow Node builtins except node:sqlite.
  • node24-sqlite: allow node:sqlite and require engines.node >=24.

Concrete first classifications:

  • @overeng/notion-datasource-sync ./cli/effect-command: bun
  • @overeng/notion-datasource-sync ./cli: node24-sqlite
  • @overeng/notion-datasource-sync ./store: node24-sqlite
  • @overeng/notion-datasource-sync ./replica: node24-sqlite

Algorithm:

  1. Read workspace package exports and publishConfig.exports.
  2. Assert source and publish export keys match.
  3. Resolve each export target, including conditional exports where relevant.
  4. Require every export to be classified in the explicit policy map. New exports fail until classified.
  5. Parse source files with oxc-parser and collect static imports/re-exports plus any dynamic import forms we decide to support/fail on.
  6. Resolve import specifiers with oxc-resolver using repo tsconfig/package export semantics.
  7. Build a transitive closure per export entrypoint.
  8. Fail when a reachable builtin/package/export violates the declared runtime class.
  9. Print a readable path trace, for example:
@overeng/notion-datasource-sync ./cli/effect-command is declared bun-safe but reaches node:sqlite:
  src/cli/effect-command.ts -> src/cli/main.ts -> node:sqlite

Oxlint Integration Shape

Start with a standalone devenv task because it naturally needs a whole-repo graph:

tasks."package:check:exports" = {
  after = [ "pnpm:install" ];
  exec = "bun run scripts/check-export-safety.ts";
  description = "Check package export runtime/import safety";
};

Then decide whether to expose the same logic as an Oxlint JS plugin rule. Use an Oxlint plugin only if the plugin API can handle project-wide cached graph state cleanly without turning a per-file lint rule into a hidden global analysis pass.

Secondary Gates

Keep these separate from runtime-taint enforcement:

  • publint: package metadata, invalid exports, missing files, and package compatibility checks.
  • @arethetypeswrong/cli: type resolution and ESM/CJS declaration compatibility.
  • Oxlint direct import rules: fast local guardrails for direct forbidden imports.

Acceptance Criteria

  • New package exports fail until classified by runtime policy.
  • @overeng/notion-datasource-sync ./cli/effect-command is enforced as Bun/root-CLI safe.
  • node:sqlite reachability is allowed only from explicitly classified Node 24 SQLite exports.
  • Failures include a readable path trace such as export -> file -> node:sqlite.
  • Direct Oxlint restrictions catch simple forbidden imports early.
  • The transitive export-safety check runs as a devenv task and is included in the quick semantic gate once stable.
  • The implementation uses Oxc/Oxlint-aligned tooling rather than dependency-cruiser unless Oxc resolver/parser blockers make that impractical.

Research Notes

  • Oxlint no-restricted-imports is useful for direct static imports but is not sufficient for transitive export reachability.
  • Oxlint supports JavaScript plugins, so a local rule is possible for direct or narrowly scoped checks.
  • oxc-parser exposes fast TS/JS parsing and module metadata.
  • oxc-resolver provides Node-compatible resolution including package subpath/export behavior.
  • publint and ATTW remain valuable for package shape/type quality but do not enforce runtime/environment taint.
Posted on behalf of @schickling
field value
agent_name 🪩 co3-playa
agent_session_id 4a8a20a2-8712-4752-87fc-db098c203de6
agent_tool Codex CLI
agent_tool_version 0.131.0
agent_runtime Codex CLI 0.131.0
agent_model unknown
worktree effect-utils/schickling/2026-05-25-notion-md-db
machine dev3
tooling_profile dotfiles@4db6783

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the workspace package exports and existing devenv tasks, then review the proposed scripts/check-export-safety.ts entry point and the Oxlint/Oxc research notes. Trace the listed @overeng/notion-datasource-sync exports and their runtime classifications. Done means unclassified exports and forbidden transitive dependencies fail with readable path traces, with the check running as the package:check:exports task.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
build-system, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.