feat(mds)!: basePath fix (#180), browser lint exports (#215), option-type second pass (#213) - #302
Merged
Merged
Conversation
… option-type second pass (#213) **#180 (live bug)** basePath was accepted by the unknown-option validator but silently discarded before reaching the backend: the old forwarding builders (compileOpt/varsOpt) never included it. Templates using @import/@extends with a string-source call and a basePath option would resolve against the wrong directory or fail silently. Fix: add basePath to CompileOptions and CheckOptions; propagate through four new typed builders (compileSrcOpt, checkSrcOpt, fileCompileOpt, fileCheckOpt). The WASM backend now rejects non-null basePath on compile/check/lint with mds::invalid_options (OD-1 — errors instead of silently ignoring). The file-surface guard (compileFile/checkFile) returns Promise.reject(fileBasePathError()) so callers using assert.rejects()/.catch() receive a proper rejection; unknown-key and pre-init errors still throw synchronously (existing contract preserved per U-OV-12/U-B11). **#215** lint() and lintVirtual() exported from the browser entry point. All seven lint types (LintDiagnostic, LintFileOptions, LintFileReport, LintOptions, LintResult, LintRuleName, LintSpan, RuleSeverity) and LINT_RULE_NAMES are now available from both the Node.js and browser entries. **#213** options.ts second pass: delete _CompileBackendOpts/_CheckBackendOpts stubs; replace compileOpt/varsOpt with four typed per-surface builders; update METHOD_KEYS witnesses to the real public types; add BASEPATH_PASSTHROUGH set for compileFile/ checkFile so assertKnownKeys lets basePath through to the purpose-built guard. BREAKING: FileOptions no longer extends CompileOptions (D-TS-02); checkFile parameter changed from CheckOptions to CheckFileOptions; WASM backend rejects non-null basePath on string-surface methods instead of silently ignoring. New: tsconfig.types.json + __test__/types/ type-level matrix test (consumer-node.ts, consumer-browser.ts) with @ts-expect-error guards as positive controls (ADR-010). Closes #180 #213 #215
…rror wrapper - options.ts: extract `pickDefined<T>` helper to replace 24 lines of identical null-check + build + empty-guard boilerplate shared across compileSrcOpt, checkSrcOpt, and fileCompileOpt. Each builder is now a one-liner; fileCheckOpt is unchanged (already a one-liner with a distinct return type). - node.ts: remove `throwFileBasePathError()` — a 4-line wrapper that existed only to call `throw fileBasePathError()`. Replace both call sites with the direct throw. TypeScript's control-flow analysis handles throw in async functions correctly; the never-typed helper was adding indirection with no benefit. - browser.ts: expand single-line lintVirtual JSDoc to a multi-line block consistent with lint and the surrounding exported functions. All 295 tests pass. TypeScript build and type-fixture (tsconfig.types.json) both exit 0. Source hygiene gate exits 0. Rust scope unchanged.
P0 — Windows CI breakage (options-validation.spec.mjs) Three new sites used `new URL(..., import.meta.url).pathname` to derive filesystem paths. On win32 that yields "/C:/..." (leading slash, drive letter) and it percent-encodes spaces on every platform, so path.join and execFileSync's cwd receive unusable paths. windows-latest is in the `js` CI matrix, and every other spec in this package already uses fileURLToPath. Switched to fileURLToPath (TEST_DIR / PKG_DIR). P1 — U-OV-27 was tautological The test claimed to compare the compileFile/checkFile basePath rejection message across the native and WASM backends, but node.ts's public file methods short-circuit basePath BEFORE assertReady(), so neither side ever reached a backend. Verified empirically: the subprocess reports "BACKEND NEVER INITIALIZED". Both operands came from the same fileBasePathError(), i.e. the test asserted X === X and could not detect drift. Rewritten with the authoritative leg — wrapper vs the RAW napi addon, which owns the canonical string — plus the backend-independence leg and per-leg non-empty positive controls. Confirmed the new assertion fails when the wrapper message is mutated. P1 — U-BR17 assertion could not fail `assert.ok(fileNames.length >= 0)` holds for an empty array. Replaced with the assertions the plan specified: the entry key is present in the report and carries an unused-variable diagnostic. P1 — AC-P3-08 coverage gap U-OV-29 covered only compile/check. Extended to compileFile/checkFile, the interesting half: napi gates on property PRESENCE, so a builder that forwarded `basePath: undefined` verbatim would throw on native but not on WASM. Confirmed these legs reach the real WASM backend. P2 — accuracy and reachability - native.ts NapiAddon JSDoc claimed lintFile/lintVirtual accept basePath; napi rejects it on both. - Recorded the OD-5 residual on BASEPATH_PASSTHROUGH: lintFile/lintVirtual deliberately emit the generic message and diverge from napi's. - Corrected the consumer-node.ts comment that claimed invalid rule names are rejected (D-224-1 makes them a runtime warning) and added the real negative control: an invalid SEVERITY must be a type error. - Exported SourceMapV3 from node.ts and browser.ts. MarkdownResult.sourceMap is typed as it, but it was only re-exported from the unreachable index.ts barrel, so consumers could hold the value without naming its type. Additive; covered by both type fixtures. - Clarified the fileBasePathError JSDoc: napi never emits this message for a public call, so U-OV-27 is the only thing keeping the strings in lockstep. Rust untouched (git diff --name-only ...crates/ Cargo.toml Cargo.lock is empty, with packages/mds/ as positive control), so the WASM binary and the 850,000-byte guard are unaffected.
Amendment 4 of the Cross-PR Conflict Audit required comparing the browser `lint` result against the Node `lint` result at RUNTIME for the same fixture instead of asserting browser-local golden values. PF-007: per-surface goldens each lock in their OWN value and cannot prove cross-surface parity. U-BR-PARITY runs both surfaces on the same fixture with deepStrictEqual, closing the fifth-surface parity gap. Also imports `nodeInit` so the node backend is ready before the post-init describe block executes. PR body simultaneously updated (via gh pr edit) to: - Remove stale `throwFileBasePathError` references (removed in 0330597) - Add Packaging note documenting index.ts unreachability (AC-P3-21)
`_WasmCompileInput` (Omit<CompileOptions,'basePath'> & { filename?,
modules? }) is already structurally assignable to FileOptions | undefined
because it carries the same vars/sourceMap/sourcesContent fields with
identical types and the extra optional properties are permitted on
non-fresh values.
The `options as Parameters<typeof fileCompileOpt>[0]` cast was therefore
redundant and obscured the signature of fileCompileOpt behind an
indirection that would have hidden any future incompatibility.
Co-Authored-By: Claude <noreply@anthropic.com>
All seven NapiAddon members now use a named option type:
NapiCompileOpts, NapiCheckOpts, NapiFileCompileOpts, NapiFileCheckOpts,
NapiLintOpts, NapiLintFileOpts (shared by lintFile/lintVirtual).
Previously checkFile used an inline { vars?: Record<string, unknown> }
which was inconsistent with the other six members and obscured the
file-surface / string-surface split. The new NapiFileCheckOpts type
mirrors CheckFileOptions (the public equivalent) and makes the split
visible in one place.
Co-Authored-By: Claude <noreply@anthropic.com>
…ile methods
- Move compileFile/checkFile basePath rejection from a **Note:** under Fixed
into its own BREAKING subsection; the silent-success -> Promise.reject
transition is a runtime breaking change on a published API (AC-P3-24).
- Restate the FileOptions/CompileOptions type reshape as source-compatible:
before this PR CompileOptions had no basePath, so the old extends resolved
to the same shape as the new standalone interface. The false migration step
('now needs an explicit mapping') is removed.
- Restate the checkFile parameter narrowing as source-compatible: CheckOptions
was structurally equivalent to CheckFileOptions before this PR. The real
break is the runtime rejection documented in the new subsection, which tsc
does not flag for variable-typed callers.
- Update the BREAKING section header to lead with the file-method rejection.
Co-Authored-By: Claude <noreply@anthropic.com>
compile.spec.mjs U-C7 comment cited 'varsOpt' which was removed and replaced by compileSrcOpt/fileCompileOpt (via pickDefined). Update the comment to name the actual functions responsible for the != null filter. source-map.spec.mjs U-SM6 block comment cited the adapter's compileOpt() as the key-filtering mechanism. That function was replaced by assertKnownKeys(), which throws on unknown keys. Update the comment to match. Co-Authored-By: Claude <noreply@anthropic.com>
Browser usage section (#215): - Line 38: "compile/check" → "compile/check/lint" (matches browser.ts:98) - Browser import example: add lint and lintVirtual; show minimal usage - Node-only callout: add lintFile to the list of unavailable methods Unknown-option rejection paragraph (AC-P3-25/PF-015): - Drop hard-coded "any of the seven public methods" count that drifts - Qualify the format guarantee: basePath on compileFile/checkFile surfaces as a rejected promise with a purpose-built message, not a synchronous throw with an accepted-keys list (avoids PF-015) Also removes stale (OD-1 — ) design-ID annotation from LintOptions inline comment (stale-comment cleanup, same spirit as 7ef8369). Co-Authored-By: Claude <noreply@anthropic.com>
The AC-P3-20 comment block contained two contradictions: 1. "basePath is NOT accepted on CompileOptions or CheckOptions from the browser entry — it IS in those types (D-TS-01)" — the two halves assert opposite things; the code at lines 28-30 (no @ts-expect-error) correctly shows basePath IS accepted at the type level, which is what AC-P3-20 mandates. 2. "Same @ts-expect-error guards as consumer-node.ts for FileOptions etc." — false; this fixture has no guard for FileOptions or CheckFileOptions because neither is exported from the browser entry. The only file-surface negative case present is LintFileOptions. Replaced with a precise statement: - basePath IS accepted at the TYPE level (same d.ts on both entries, D-TS-01) - the WASM backend rejects a non-null basePath at RUNTIME - FileOptions/CheckFileOptions have no guard because they are not exported from the browser entry — the only file-surface negative case is LintFileOptions Also commit the pre-existing (uncommitted) variable-passing case that verifies LintOptions is not assignable to LintFileOptions via a typed variable (not only a fresh object literal) — correct and tsc-verified. Co-Authored-By: Claude <noreply@anthropic.com>
- basePath propagation claim (#180 Fixed): was "all four per-surface option builders (compileSrcOpt, checkSrcOpt, fileCompileOpt, fileCheckOpt)". fileCompileOpt/fileCheckOpt deliberately NEVER forward basePath (D-TS-02/ D-TS-05), and the BREAKING subsection 30 lines later says the opposite. Correct to: propagates to the backend for the string-source methods (compile, check); compileFile/checkFile deliberately exclude basePath. - Lint-types count (#215 Added): "All seven lint types" followed by a list of eight. The list is correct; fix the prose count to "eight". - Lint-types export scope (#215 Added): "exported from both the Node.js and browser entry points" overstates what changed — all eight were already exported from node.ts at the wave base. Only the browser entry is newly gaining them. Correct to "from the browser entry point as well as the Node.js entry point". - Drop stale lintVirtual references in basePath migration guidance; native backend is the correct migration path. Co-Authored-By: Claude <noreply@anthropic.com>
…ption forwarding
- Add `basePath?: never` to FileOptions, CheckFileOptions, and LintFileOptions
so structural assignment from a string-surface type (basePath?: string) fails
at compile time. This satisfies the AC-P3-20 variable-passing negative cases
and aligns with `@ts-expect-error` guards in consumer-node.ts.
- Replace per-surface option builder functions (compileSrcOpt, checkSrcOpt,
fileCompileOpt, fileCheckOpt, lintOpt, lintFileOpt) with a single forwardOpts
helper keyed on METHOD_KEYS. Single authoritative source for which keys are
forwarded per method name.
- Add makeFileBasePathError factory; change BASEPATH_PASSTHROUGH from ReadonlySet
to ReadonlyMap<MethodName, factory> so adding a new file-surface method without
an error factory is a TypeScript error at the Map literal.
- Convert U-OV-25/U-OV-26 from async assert.rejects to synchronous assert.throws:
the basePath guard now throws before any I/O (per U-OV-32), and assert.rejects
in Node.js v22 does not intercept synchronous throws.
- Fix compileFile/checkFile basePath cast: (options as unknown as { basePath?: string })
is required because FileOptions.basePath?: never is structurally incompatible with
CompileOptions for a direct cast.
…n node.ts The forwardOpts import was added but never called directly in node.ts — all option forwarding in node.ts is handled by the backend adapters (native.ts / wasm.ts). Remove to keep the zero-warnings policy. Also update a comment in the WASM-path checkFile that still referenced the deleted fileCompileOpt helper; now names fileOpts and forwardOpts correctly.
…OV-34 The prior commit replaced per-surface option builders (compileSrcOpt, fileCompileOpt) with a single forwardOpts helper, but the explanatory comment in the U-OV-34 test block still referenced the old names. Update the comment to reference forwardOpts to match the implementation.
Commit 225d5a6 removed the `forwardOpts` import from node.ts, but `wrapWithFileOps.lintFile` calls `forwardOpts(options, 'lintFile')` to forward vars/rules through METHOD_KEYS rather than accessing them directly. Restores the import so the build compiles cleanly.
The prior commit (50b37c9) removed the entire explanatory comment above `export { isMdsError, LINT_RULE_NAMES }` when it should have only dropped the final sentence ("The browser entry gains it with the browser lint surface; today it has no lint API to configure.") which became false once browser.ts gained lint/lintVirtual/LINT_RULE_NAMES exports. The first three lines — explaining why LINT_RULE_NAMES must be re-exported from node.ts/browser.ts rather than index.ts (the exports map never routes to dist/index.js, so index.ts is unreachable for consumers) — remain accurate and are restored here. Co-Authored-By: Claude <noreply@anthropic.com>
…omments The MAINTENANCE NOTE and positive-control description in the forwarding-parity test block still referred to the deleted per-surface helpers (pickDefined, lintOpt, lintFileOpt). Update both comments to reference forwardOpts, which is now the single forwarding path keyed on METHOD_KEYS.
…r METHOD_KEYS consistency
wasm.ts checkOpts still accessed options?.vars directly instead of going through
forwardOpts, making it inconsistent with the sibling compileOpts (which uses
forwardOpts('compileFile')). If CheckOptions gained a new key in METHOD_KEYS.checkFile
the drift would be silent.
node.ts lintFile (WASM path) called forwardOpts then manually copied individual
keys (forwarded?.vars, forwarded?.rules) rather than spreading forwarded. A new
key added to METHOD_KEYS.lintFile would be silently dropped by the manual copying.
Fix:
- checkOpts now uses forwardOpts(options, 'checkFile'), matching the compileOpts
pattern, so METHOD_KEYS.checkFile is the single source of truth (avoids PF-004)
- lintFile WASM path spreads ...forwarded directly, matching lint/lintVirtual
No observable behaviour change: forwardOpts already filters to the same key set
that the manual code extracted. Identical results on all existing test inputs.
300/300 tests pass; tsc exits 0; source-hygiene gate clean.
Co-Authored-By: Claude <noreply@anthropic.com>
…kFile checkOpts(options?: CheckOptions) was calling forwardOpts(options, 'checkFile'), which uses METHOD_KEYS.checkFile — derived from CheckFileOptions, a different interface from the function's own parameter type. This recreated the PF-004 /#180 silent-drop topology on the WASM check path: a future key added to CheckOptions would update METHOD_KEYS.check (via the keysOf witness) but forwardOpts would still select METHOD_KEYS.checkFile, silently dropping the new key while the native backend honoured it. Fix: call forwardOpts(options, 'check') so the key list is derived from CheckOptions — the same interface as the parameter. METHOD_KEYS.check includes basePath, but forwardOpts only includes keys whose value is != null; the call site already throws mds::invalid_options before reaching checkOpts whenever basePath is non-null, so basePath is never included in the forwarded object. Applies PF-004. Verified by U-WB23 (basePath rejection) and U-WB25 (basePath: undefined accepted) — both continue to pass. Co-Authored-By: Claude <noreply@anthropic.com>
…aims Five confirmed review findings, all rooted in a sequencing issue: commit 891db04 wrote CHANGELOG and README before commit 3a9e2be added `basePath?: never` to FileOptions, CheckFileOptions, and LintFileOptions — leaving the docs contradicting the shipped types. Changes: - CHANGELOG.md: "return a rejected promise" → "throw synchronously" for compileFile/checkFile basePath rejection (node.ts:280,299 are sync throws). - CHANGELOG.md migration paragraph: remove "runtime-only break" and "TypeScript does not flag it"; add sync-throw / try-catch guidance and pointer to compile-time compatibility notes below. - CHANGELOG.md FileOptions compatibility: replace "source-compatible" with "compile-time break"; add shared-variable migration with destructuring example. basePath?: never means CompileOptions is not assignable to FileOptions (verified by consumer-node.ts:72-73 @ts-expect-error, ADR-009). - CHANGELOG.md CheckFileOptions compatibility: same correction. CheckOptions is not assignable to CheckFileOptions for the same reason. - README.md: "surfaces as a rejected promise" → "throws synchronously"; add try/catch guidance consistent with node.ts JSDoc. - types.ts: clarify CheckFileOptions JSDoc to explain the compile-time break that `basePath?: never` intentionally introduces. The LintFileOptions section already correctly describes this as a compile-time break and is unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
…eted symbols compileSrcOpt, fileCompileOpt, and pickDefined were removed by commit 3a9e2be and replaced with forwardOpts. The U-C7 comment was rewritten in that same commit to reference those symbols, which were then immediately absent. Sibling cleanup commits 7ef8369 and cfd84d5 fixed this staleness class in options-validation.spec.mjs but missed this site. Rewrite the comment to name forwardOpts, which filters via != null over METHOD_KEYS — the current and only forwarding path for all seven public methods. Co-Authored-By: Claude <noreply@anthropic.com>
…r, comment hygiene, and drop backend interfaces from public surface Five confirmed findings from PR review: [medium] Remove MdsBackend/MdsBaseBackend/MdsNodeBackend from node.ts export type block and MdsBaseBackend from browser.ts. No public method signature accepts or returns these types; AC-P3-21 scopes the requirement to option types a consumer must name to call a public method. Publishing the backend seam would make any future backend-method addition a semver-major event for `implements MdsBaseBackend` callers. Update consumer-node.ts/consumer-browser.ts to remove the now-removed imports and corresponding type tests. [low] Add defense-in-depth basePath guard in wrapWithFileOps.compileFile and wrapWithFileOps.checkFile (PF-004). The public wrappers already throw synchronously, but a future internal caller bypassing the public layer would previously get the silent-drop semantics that caused #180. The inner async guard rejects as a promise rejection (consistent with their async contract). [low] Fix lintOpts spread order in wrapWithFileOps.lintFile: put ...forwarded first so filename and modules are always last. A future key in METHOD_KEYS.lintFile named filename or modules could silently clobber WASM-internal values with the old order (avoids PF-004). [low] Rewrite stale LINT_RULE_NAMES comment that referenced deleted src/index.ts in the present tense. Per the project leave-the-end-state rule, the comment now states where the value is exported rather than explaining the history of why. Also committed from pre-existing working-tree changes: - forwardOpts<T>: generic T parameter so callers receive Partial<T> and are cast-free (options.ts). Removes all explicit casts in native.ts and wasm.ts. - U-BR18: browser entry compile/check/lint reject basePath via WASM guard with PF-013 positive control (browser.spec.mjs). Co-Authored-By: Claude <noreply@anthropic.com>
U-OV-27 (AC-P3-06 / findings 1+2): Add MDS_BACKEND=wasm subprocess leg. The guard fires in node.ts before assertReady() so the WASM path produces the same message today — but the byte-equality assertion is the PF-004 regression barrier: if the guard is ever refactored into the backend layer a WASM-specific divergence is caught here before reaching production. Test-plan item 5 mandated assert.strictEqual(nativeMsg, wasmMsg); this leg was deleted in 3a9e2be alongside the wrapWithFileOps guards. U-OV-24 (finding 3): Harden the self-referential fullOpts oracle. Add an assert.ok(KEY_VALUES[k] !== undefined) guard inside fullOpts() so a key added to METHOD_KEYS but absent from KEY_VALUES fails loudly at lookup time rather than producing a confusing deepStrictEqual mismatch. Add an independent hand-typed literal oracle for the lint surface so that a systematic corruption of KEY_VALUES is caught by something other than the self-referential loop (avoids PF-013). U-OV-34 Part 2 (finding 4): Strengthen DEFAULT_COMPILE_OPTS identity check. The identity assertion (compileOpts(undefined) === compileOpts({})) proves a singleton is reused but not WHICH singleton. The new deepStrictEqual against { filename: 'input.mds', modules: {} } ensures the fast path reuses the documented default, not an incidental frozen object (AC-P3-22).
Commit 3a9e2be stripped D-TS-01, D-TS-02, and OD-1 from src/types.ts but left the same class of identifier in other shipped files. Apply the same treatment uniformly: - node.ts: exported compileFile JSDoc had U-OV-12, U-OV-32, U-B11; checkFile JSDoc and inline guard comments had U-OV-33. The behavioral prose (sync-throw contract description) is preserved exactly — only the bare IDs are dropped. - backend/wasm.ts: _WasmCompileInput JSDoc, compileOpts/checkOpts inline comments, and throwWasmBasePathError JSDoc cited D-TS-06, OD-1, AC-P3-10, and U-WB22. The three OD-1 callsites in createWasmBackend are also cleaned up for consistency. - util/options.ts: METHOD_KEYS JSDoc cited U-OV-14; makeFileBasePathError JSDoc cited U-OV-27; BASEPATH_REJECTORS comment cited OD-5, U-OV-7, U-OV-13; forwardOpts JSDoc cited D-TS-03/D-TS-05. No logic changes. Zero TypeScript errors; source-hygiene gate clean. Co-Authored-By: Claude <noreply@anthropic.com>
…omment options.ts: "Nothing enforces the two strings staying in sync except\nKeep the two in lockstep" was a sentence fragment — 'except' had no object and the claim was factually wrong (U-OV-27 does enforce parity). Replaced with a direct imperative that names the test. wasm.ts checkOpts: the 8-line comment restated the identical rationale already given for compileOpts directly above it. Reduced to three lines that reference compileOpts and state only the check-specific detail (basePath exclusion). No behavior change; TypeScript build and source-hygiene gate both pass.
Two test-side gaps found in the PR3 9-pillar self-review.
1. Four basePath assertions were backend-dependent (plan section 0, OD-1).
U-OV-8, U-OV-9, U-OV-15 and U-OV-16 asserted a bare
`assert.doesNotThrow(() => compile('Hello\n', { basePath: '.' }))`.
Under OD-1 the WASM backend now rejects a non-null basePath, so on any
machine that falls back to WASM (node.ts:225) these failed with a
misleading "wrapper reconciliation" message — misattributing correct
WASM behaviour to a wrapper regression. Verified: under MDS_BACKEND=wasm
both calls throw mds::invalid_options today.
Replaced with assertWrapperAcceptsBasePath(), which asserts the property
the tests actually exist for — that assertKnownKeys no longer intercepts
basePath on the string surface (#180) — on BOTH backends. The WASM branch
is not a free pass: it requires the error to be the WASM-backend rejection
and explicitly NOT the generic `unknown option key` form (avoids PF-013).
Confirmed passing under both the default (native) and MDS_BACKEND=wasm runs.
2. New U-OV-35 pins the checkFile WASM forwarding invariant.
On the WASM backend, checkFile routes through prepareFileArgs -> fileOpts,
which forwards via METHOD_KEYS.compileFile rather than METHOD_KEYS.checkFile.
That is correct only while checkFile's keys are a subset of compileFile's.
Should CheckFileOptions ever gain a key FileOptions lacks, assertKnownKeys
would accept it while the WASM path silently dropped it — the exact #180
validated-then-discarded bug class, on one backend only, invisible to a
native-only run (avoids PF-004). Carries its own positive controls so the
subset assertion cannot pass vacuously.
Also refreshed the stale file docblock (claimed "U-OV-1 through U-OV-20";
the file now runs through U-OV-35) and recorded that basePath behaviour is
NOT backend-agnostic.
No production code changed.
dean0x
added a commit
that referenced
this pull request
Aug 16, 2026
…89 [#209] AC-209-04 contained an absolute prohibition on `allow(deprecated)` in crates/*/src/ that was already violated on the wave base: config.rs lines 287 and 289 carry #[allow(deprecated)] inside the compiled doctest for LintConfig::from_rules (itself deprecated since #302, before this branch). The audit grep (`allow(deprecated)|expect(deprecated)` over crates/*/src/) therefore returns two hits the original criterion did not account for. A reviewer running the audit could not distinguish a genuine suppression leak from a legacy carve-out. Fix (applies PF-015 — absolute completeness claim is a liability): - Restate AC-209-04 with an enumerated whitelist of exactly three permitted locations: fix.rs >1006, crates/mds-core/tests/, and config.rs:287,289. - Add a provenance note: (c) is pre-existing from #302, confirmed via `git show wave/v0.4.0-wave1:crates/mds-core/src/lint/config.rs`. - Update the test plan section 4 expected outcome to match, including the confirmation command reviewers should run to verify the carve-out. No source code changed. Only the plan document is updated. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
basePathonCompileOptionsto matchLintOptions#180 (live bug — basePath silently dropped):basePathwas accepted by the unknown-option validator but discarded before reaching the backend. The forwarding builders (compileOpt/varsOpt) never included it. Templates using@import/@extendswithcompile(source, { basePath })resolved imports against the wrong directory or failed silently. Fixed by addingbasePathtoCompileOptions/CheckOptionsand propagating through four new typed builders (compileSrcOpt,checkSrcOpt,fileCompileOpt,fileCheckOpt). The WASM backend now rejects non-nullbasePathoncompile/check/lintwithmds::invalid_options(OD-1: errors instead of silently ignoring).lint/lintFile/lintVirtualexports tobrowser.ts(pre-existing gap) #215 (browser lint exports):lint()andlintVirtual()added tobrowser.ts. All seven lint types andLINT_RULE_NAMESare now exported from both Node.js and browser entries.varsOpttype narrowing #213 (options.ts second pass): Deleted_CompileBackendOpts/_CheckBackendOptsstubs; replacedcompileOpt/varsOptwith four typed per-surface builders; updatedMETHOD_KEYSwitnesses; addedBASEPATH_PASSTHROUGHset forcompileFile/checkFile.Changes
packages/mds/src/types.ts—CheckOptionsgainsbasePath?;FileOptionsbroken fromextends CompileOptions(D-TS-02); newCheckFileOptionstype;MdsNodeBackend.checkFileparam changed.packages/mds/src/util/options.ts— Four typed builders (compileSrcOpt,checkSrcOpt,fileCompileOpt,fileCheckOpt);BASEPATH_PASSTHROUGHset; updatedMETHOD_KEYS.packages/mds/src/backend/native.ts— All four new builders wired up.packages/mds/src/backend/wasm.ts— OD-1 guards oncompile/check/lint;_WasmCompileInput extends Omit<CompileOptions,'basePath'>(D-TS-06).packages/mds/src/node.ts—fileBasePathError()helper; publiccompileFile/checkFileusePromise.reject(fileBasePathError())forbasePath(preserves sync-throw contract for unknown keys and pre-init).packages/mds/src/browser.ts—lint()andlintVirtual()exported; all lint types re-exported.packages/mds/__test__/— 25 new test cases (U-OV-21–31, U-WB22–25, U-BR14–21); type-level matrix in__test__/types/.packages/mds/tsconfig.types.json— New; runs type-level tests viatsc --noEmitas part ofnpm test.Breaking Changes
FileOptionsno longer extendsCompileOptions—FileOptionsis now a standalone interface;basePathis NOT present.checkFileparameter changed fromCheckOptionstoCheckFileOptions—CheckFileOptionshas onlyvars;basePathand source-map options are rejected.basePathoncompile/check/lint— previously silently ignored; now throwsmds::invalid_options.Packaging note
src/index.tsis NOT in the packageexportsmap and has nomain/typesfallback —dist/index.jsis unreachable to consumers. This barrel serves internal repo tooling only. All public types and values are exported fromdist/node.d.ts(Node) anddist/browser.d.ts(browser) via the"."exports-map entry inpackage.json(AC-P3-21).Reviewer Focus Areas
src/node.ts:fileBasePathError()—Promise.reject(fileBasePathError())in publiccompileFile/checkFile; the asyncwrapWithFileOpsguard also calls the same helper for the error path.src/util/options.ts:BASEPATH_PASSTHROUGH—assertKnownKeysskipsbasePathforcompileFile/checkFileso the purpose-built error fires instead of the generic "unknown option key".__test__/types/:@ts-expect-errorguards are positive controls — an unused directive fails the build ifbasePathis ever added to a file-surface type (ADR-010 pattern).__test__/options-validation.spec.mjs: U-OV-27 asserts byte-identical messages across native and WASM paths (avoids PF-007); U-OV-29 subprocess includesawait init()for WASM path.