Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem - #7166

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url
Aug 10, 2026
Merged

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem#7166
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url

Conversation

@CDVolvik

Copy link
Copy Markdown
Contributor

Type

  • Bug Fix

Description

Migrator.fromFileSystem passes the directory and file name straight to import. On Windows that produces a specifier such as D:\migrations\1_init.ts, which the ESM loader rejects:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader.
On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

The loader now builds the specifier with the Path service, which already knows how to produce a file URL for the host platform:

Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory,basename))),(url)=> ...)

Two things worth calling out, since neither is free:

fromFileSystem widens from Loader<FileSystem> to Loader<FileSystem | Path>. Core has no Windows Path implementation and hardcoding node:url here would be wrong, so the platform has to supply it. Callers on an aggregate layer such as NodeServices.layer are unaffected; callers providing FileSystem alone now also need a Path layer, and on Windows it has to be a platform-aware one rather than the POSIX Path.layer. The changeset spells this out.

toFileUrl fails in the typed error channel with BadArgument, while loadMigration only normalizes defects. Without orDie that failure would escape the MigrationError | SqlError channel that make advertises, so it is turned back into a defect and reported as an import error like any other.

Validation

  • pnpm check
  • pnpm lint
  • pnpm vitest run --project effect packages/effect/test/Migrator.test.ts (5 passed)

Both new tests were checked against the unfixed code rather than only the fixed code. Reverting the URL conversion fails the first with expected 'Error: Cannot find module /@id/C:\m…' to include 'file:///C:/migrations/0001_first.js', and dropping orDie fails the second with expected 'no defect' to include 'defect:'.

The Windows case is covered with a stand-in Path provider, because core has no win32 implementation and packages/effect should not depend on @effect/platform-node-shared for a test.

Related

Closes#4297

@changeset-bot

changeset-botBot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 282b9e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
NameType
effectPatch
@effect/ai-anthropicPatch
@effect/ai-openaiPatch
@effect/ai-openai-compatPatch
@effect/ai-openrouterPatch
@effect/atom-reactPatch
@effect/atom-solidPatch
@effect/atom-vuePatch
@effect/docgenPatch
@effect/doctestPatch
@effect/openapi-generatorPatch
@effect/opentelemetryPatch
@effect/platform-browserPatch
@effect/platform-bunPatch
@effect/platform-denoPatch
@effect/platform-nodePatch
@effect/platform-node-sharedPatch
@effect/sql-clickhousePatch
@effect/sql-d1Patch
@effect/sql-libsqlPatch
@effect/sql-mssqlPatch
@effect/sql-mysql2Patch
@effect/sql-pgPatch
@effect/sql-pglitePatch
@effect/sql-sqlite-bunPatch
@effect/sql-sqlite-doPatch
@effect/sql-sqlite-nodePatch
@effect/sql-sqlite-react-nativePatch
@effect/sql-sqlite-wasmPatch
@effect/vitestPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File NameCurrent SizePrevious SizeDifference
basic.ts6.92 KB6.92 KB0.00 KB (0.00%)
batching.ts9.72 KB9.72 KB0.00 KB (0.00%)
brand.ts6.60 KB6.60 KB0.00 KB (0.00%)
cache.ts10.63 KB10.63 KB0.00 KB (0.00%)
config.ts20.91 KB20.91 KB0.00 KB (0.00%)
differ.ts19.77 KB19.77 KB0.00 KB (0.00%)
http-client.ts21.55 KB21.55 KB0.00 KB (0.00%)
logger.ts10.84 KB10.84 KB0.00 KB (0.00%)
metric.ts8.86 KB8.86 KB0.00 KB (0.00%)
optic.ts6.68 KB6.68 KB0.00 KB (0.00%)
pubsub.ts14.90 KB14.90 KB0.00 KB (0.00%)
queue.ts11.57 KB11.57 KB0.00 KB (0.00%)
schedule.ts10.74 KB10.74 KB0.00 KB (0.00%)
schema-class.ts19.48 KB19.48 KB0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts29.41 KB29.41 KB0.00 KB (0.00%)
schema-representation-roundtrip.ts25.63 KB25.63 KB0.00 KB (0.00%)
schema-string-transformation.ts13.58 KB13.58 KB0.00 KB (0.00%)
schema-string.ts11.09 KB11.09 KB0.00 KB (0.00%)
schema-template-literal.ts15.38 KB15.38 KB0.00 KB (0.00%)
schema-toArbitrary.ts21.52 KB21.52 KB0.00 KB (0.00%)
schema-toCodeDocument.ts24.00 KB24.00 KB0.00 KB (0.00%)
schema-toCodecJson.ts18.74 KB18.74 KB0.00 KB (0.00%)
schema-toEquivalence.ts18.57 KB18.57 KB0.00 KB (0.00%)
schema-toFormatter.ts18.43 KB18.43 KB0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts22.59 KB22.59 KB0.00 KB (0.00%)
schema-toRepresentation.ts19.08 KB19.08 KB0.00 KB (0.00%)
schema.ts18.73 KB18.73 KB0.00 KB (0.00%)
stm.ts12.63 KB12.63 KB0.00 KB (0.00%)
stream.ts9.67 KB9.67 KB0.00 KB (0.00%)

@pullfrogpullfrogBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Two minor observations — the core fix is correct, well-reasoned, and I verified the suite passes.

Reviewed changes

  • Migrator.fromFileSystem (packages/effect/src/unstable/sql/Migrator.ts): the migration specifier is now resolved through the Path service (path.join + path.toFileUrl) instead of a bare `${directory}/${basename}` string, so Windows absolute paths become importable file:// URLs. The loader type widens to Loader<FileSystem | Path>, and orDie keeps toFileUrl's typed BadArgument failure a defect so loadMigration reports it as a normal import error.
  • Two new loader tests (packages/effect/test/Migrator.test.ts): a Windows-like Path stand-in proves import receives file:///C:/migrations/0001_first.js, and a failing toFileUrl is surfaced as an import error. The existing filesystem test now provides Path.layer.
  • A patch changeset documenting the breaking requirement widening.

I ran npx vitest run --project effect packages/effect/test/Migrator.test.ts → 5 passed, and confirmed the platform NodePath.layer (via node:url.pathToFileURL) plus NodeServices.layer make the fix correct on Windows without breaking aggregate-layer callers. The orDie reasoning checks out against loadMigration's catchDefect. Two rough edges inline.

Pullfrog⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

// `import` needs a file URL: on Windows an absolute path such as
// `D:\migrations\1_init.ts` is rejected by the ESM loader. `orDie` keeps the
// failure a defect so `loadMigration` reports it as an import error.
Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, mostly worth a confirmation: for a relative directory value, this now routes through path.toFileUrlresolve, so the specifier base silently changes from "resolved against the library module (broken at runtime)" to "resolved against the current working directory" — which is actually consistent with how readDirectory already treats the directory. That's an improvement, but it's a semantic change beyond the Windows fix and isn't called out in the PR description or changeset. If relative-directory (./migrations) support is intended to be CWD-relative, it's fine as-is; otherwise worth a note.

)

// the raw path would have been rejected as protocol "c:"
assert.include(specifier, "file:///C:/migrations/0001_first.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the assertion is coupled to the importer's error-message format. I confirmed plain Node's ERR_MODULE_NOT_FOUND strips the scheme (Cannot find module '/C:/migrations/0001_first.js') — it's only Vitest's wrapper that currently echoes the full file:///... URL, which is why this passes here and fails when reverted. It's a valid regression test today, but a future loader/vitest message change could break it spuriously. Consider asserting on something less format-dependent, e.g. that the defect text contains the normalized forward-slash path without a \ backslash, or pin the message check more loosely.

@tim-smart
tim-smart enabled auto-merge (rebase) August 10, 2026 04:10
@tim-smart
tim-smart merged commit bc80d14 into Effect-TS:mainAug 10, 2026
13 of 16 checks passed
@effect-boteffect-bot mentioned this pull request Aug 10, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrator.fromFileSystem fails on Windows under moduleResolution:bundler option in tsconfig.json

2 participants

@CDVolvik@tim-smart
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem - #7166

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url
Aug 10, 2026
Merged

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem#7166
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url

Conversation

@CDVolvik

Copy link
Copy Markdown
Contributor

Type

  • Bug Fix

Description

Migrator.fromFileSystem passes the directory and file name straight to import. On Windows that produces a specifier such as D:\migrations\1_init.ts, which the ESM loader rejects:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader.
On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

The loader now builds the specifier with the Path service, which already knows how to produce a file URL for the host platform:

Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory,basename))),(url)=> ...)

Two things worth calling out, since neither is free:

fromFileSystem widens from Loader<FileSystem> to Loader<FileSystem | Path>. Core has no Windows Path implementation and hardcoding node:url here would be wrong, so the platform has to supply it. Callers on an aggregate layer such as NodeServices.layer are unaffected; callers providing FileSystem alone now also need a Path layer, and on Windows it has to be a platform-aware one rather than the POSIX Path.layer. The changeset spells this out.

toFileUrl fails in the typed error channel with BadArgument, while loadMigration only normalizes defects. Without orDie that failure would escape the MigrationError | SqlError channel that make advertises, so it is turned back into a defect and reported as an import error like any other.

Validation

  • pnpm check
  • pnpm lint
  • pnpm vitest run --project effect packages/effect/test/Migrator.test.ts (5 passed)

Both new tests were checked against the unfixed code rather than only the fixed code. Reverting the URL conversion fails the first with expected 'Error: Cannot find module /@id/C:\m…' to include 'file:///C:/migrations/0001_first.js', and dropping orDie fails the second with expected 'no defect' to include 'defect:'.

The Windows case is covered with a stand-in Path provider, because core has no win32 implementation and packages/effect should not depend on @effect/platform-node-shared for a test.

Related

Closes#4297

@changeset-bot

changeset-botBot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 282b9e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
NameType
effectPatch
@effect/ai-anthropicPatch
@effect/ai-openaiPatch
@effect/ai-openai-compatPatch
@effect/ai-openrouterPatch
@effect/atom-reactPatch
@effect/atom-solidPatch
@effect/atom-vuePatch
@effect/docgenPatch
@effect/doctestPatch
@effect/openapi-generatorPatch
@effect/opentelemetryPatch
@effect/platform-browserPatch
@effect/platform-bunPatch
@effect/platform-denoPatch
@effect/platform-nodePatch
@effect/platform-node-sharedPatch
@effect/sql-clickhousePatch
@effect/sql-d1Patch
@effect/sql-libsqlPatch
@effect/sql-mssqlPatch
@effect/sql-mysql2Patch
@effect/sql-pgPatch
@effect/sql-pglitePatch
@effect/sql-sqlite-bunPatch
@effect/sql-sqlite-doPatch
@effect/sql-sqlite-nodePatch
@effect/sql-sqlite-react-nativePatch
@effect/sql-sqlite-wasmPatch
@effect/vitestPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File NameCurrent SizePrevious SizeDifference
basic.ts6.92 KB6.92 KB0.00 KB (0.00%)
batching.ts9.72 KB9.72 KB0.00 KB (0.00%)
brand.ts6.60 KB6.60 KB0.00 KB (0.00%)
cache.ts10.63 KB10.63 KB0.00 KB (0.00%)
config.ts20.91 KB20.91 KB0.00 KB (0.00%)
differ.ts19.77 KB19.77 KB0.00 KB (0.00%)
http-client.ts21.55 KB21.55 KB0.00 KB (0.00%)
logger.ts10.84 KB10.84 KB0.00 KB (0.00%)
metric.ts8.86 KB8.86 KB0.00 KB (0.00%)
optic.ts6.68 KB6.68 KB0.00 KB (0.00%)
pubsub.ts14.90 KB14.90 KB0.00 KB (0.00%)
queue.ts11.57 KB11.57 KB0.00 KB (0.00%)
schedule.ts10.74 KB10.74 KB0.00 KB (0.00%)
schema-class.ts19.48 KB19.48 KB0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts29.41 KB29.41 KB0.00 KB (0.00%)
schema-representation-roundtrip.ts25.63 KB25.63 KB0.00 KB (0.00%)
schema-string-transformation.ts13.58 KB13.58 KB0.00 KB (0.00%)
schema-string.ts11.09 KB11.09 KB0.00 KB (0.00%)
schema-template-literal.ts15.38 KB15.38 KB0.00 KB (0.00%)
schema-toArbitrary.ts21.52 KB21.52 KB0.00 KB (0.00%)
schema-toCodeDocument.ts24.00 KB24.00 KB0.00 KB (0.00%)
schema-toCodecJson.ts18.74 KB18.74 KB0.00 KB (0.00%)
schema-toEquivalence.ts18.57 KB18.57 KB0.00 KB (0.00%)
schema-toFormatter.ts18.43 KB18.43 KB0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts22.59 KB22.59 KB0.00 KB (0.00%)
schema-toRepresentation.ts19.08 KB19.08 KB0.00 KB (0.00%)
schema.ts18.73 KB18.73 KB0.00 KB (0.00%)
stm.ts12.63 KB12.63 KB0.00 KB (0.00%)
stream.ts9.67 KB9.67 KB0.00 KB (0.00%)

@pullfrogpullfrogBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Two minor observations — the core fix is correct, well-reasoned, and I verified the suite passes.

Reviewed changes

  • Migrator.fromFileSystem (packages/effect/src/unstable/sql/Migrator.ts): the migration specifier is now resolved through the Path service (path.join + path.toFileUrl) instead of a bare `${directory}/${basename}` string, so Windows absolute paths become importable file:// URLs. The loader type widens to Loader<FileSystem | Path>, and orDie keeps toFileUrl's typed BadArgument failure a defect so loadMigration reports it as a normal import error.
  • Two new loader tests (packages/effect/test/Migrator.test.ts): a Windows-like Path stand-in proves import receives file:///C:/migrations/0001_first.js, and a failing toFileUrl is surfaced as an import error. The existing filesystem test now provides Path.layer.
  • A patch changeset documenting the breaking requirement widening.

I ran npx vitest run --project effect packages/effect/test/Migrator.test.ts → 5 passed, and confirmed the platform NodePath.layer (via node:url.pathToFileURL) plus NodeServices.layer make the fix correct on Windows without breaking aggregate-layer callers. The orDie reasoning checks out against loadMigration's catchDefect. Two rough edges inline.

Pullfrog⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

// `import` needs a file URL: on Windows an absolute path such as
// `D:\migrations\1_init.ts` is rejected by the ESM loader. `orDie` keeps the
// failure a defect so `loadMigration` reports it as an import error.
Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, mostly worth a confirmation: for a relative directory value, this now routes through path.toFileUrlresolve, so the specifier base silently changes from "resolved against the library module (broken at runtime)" to "resolved against the current working directory" — which is actually consistent with how readDirectory already treats the directory. That's an improvement, but it's a semantic change beyond the Windows fix and isn't called out in the PR description or changeset. If relative-directory (./migrations) support is intended to be CWD-relative, it's fine as-is; otherwise worth a note.

)

// the raw path would have been rejected as protocol "c:"
assert.include(specifier, "file:///C:/migrations/0001_first.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the assertion is coupled to the importer's error-message format. I confirmed plain Node's ERR_MODULE_NOT_FOUND strips the scheme (Cannot find module '/C:/migrations/0001_first.js') — it's only Vitest's wrapper that currently echoes the full file:///... URL, which is why this passes here and fails when reverted. It's a valid regression test today, but a future loader/vitest message change could break it spuriously. Consider asserting on something less format-dependent, e.g. that the defect text contains the normalized forward-slash path without a \ backslash, or pin the message check more loosely.

@tim-smart
tim-smart enabled auto-merge (rebase) August 10, 2026 04:10
@tim-smart
tim-smart merged commit bc80d14 into Effect-TS:mainAug 10, 2026
13 of 16 checks passed
@effect-boteffect-bot mentioned this pull request Aug 10, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrator.fromFileSystem fails on Windows under moduleResolution:bundler option in tsconfig.json

2 participants

@CDVolvik@tim-smart
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem - #7166

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url
Aug 10, 2026
Merged

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem#7166
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url

Conversation

@CDVolvik

Copy link
Copy Markdown
Contributor

Type

  • Bug Fix

Description

Migrator.fromFileSystem passes the directory and file name straight to import. On Windows that produces a specifier such as D:\migrations\1_init.ts, which the ESM loader rejects:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader.
On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

The loader now builds the specifier with the Path service, which already knows how to produce a file URL for the host platform:

Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory,basename))),(url)=> ...)

Two things worth calling out, since neither is free:

fromFileSystem widens from Loader<FileSystem> to Loader<FileSystem | Path>. Core has no Windows Path implementation and hardcoding node:url here would be wrong, so the platform has to supply it. Callers on an aggregate layer such as NodeServices.layer are unaffected; callers providing FileSystem alone now also need a Path layer, and on Windows it has to be a platform-aware one rather than the POSIX Path.layer. The changeset spells this out.

toFileUrl fails in the typed error channel with BadArgument, while loadMigration only normalizes defects. Without orDie that failure would escape the MigrationError | SqlError channel that make advertises, so it is turned back into a defect and reported as an import error like any other.

Validation

  • pnpm check
  • pnpm lint
  • pnpm vitest run --project effect packages/effect/test/Migrator.test.ts (5 passed)

Both new tests were checked against the unfixed code rather than only the fixed code. Reverting the URL conversion fails the first with expected 'Error: Cannot find module /@id/C:\m…' to include 'file:///C:/migrations/0001_first.js', and dropping orDie fails the second with expected 'no defect' to include 'defect:'.

The Windows case is covered with a stand-in Path provider, because core has no win32 implementation and packages/effect should not depend on @effect/platform-node-shared for a test.

Related

Closes#4297

@changeset-bot

changeset-botBot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 282b9e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
NameType
effectPatch
@effect/ai-anthropicPatch
@effect/ai-openaiPatch
@effect/ai-openai-compatPatch
@effect/ai-openrouterPatch
@effect/atom-reactPatch
@effect/atom-solidPatch
@effect/atom-vuePatch
@effect/docgenPatch
@effect/doctestPatch
@effect/openapi-generatorPatch
@effect/opentelemetryPatch
@effect/platform-browserPatch
@effect/platform-bunPatch
@effect/platform-denoPatch
@effect/platform-nodePatch
@effect/platform-node-sharedPatch
@effect/sql-clickhousePatch
@effect/sql-d1Patch
@effect/sql-libsqlPatch
@effect/sql-mssqlPatch
@effect/sql-mysql2Patch
@effect/sql-pgPatch
@effect/sql-pglitePatch
@effect/sql-sqlite-bunPatch
@effect/sql-sqlite-doPatch
@effect/sql-sqlite-nodePatch
@effect/sql-sqlite-react-nativePatch
@effect/sql-sqlite-wasmPatch
@effect/vitestPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File NameCurrent SizePrevious SizeDifference
basic.ts6.92 KB6.92 KB0.00 KB (0.00%)
batching.ts9.72 KB9.72 KB0.00 KB (0.00%)
brand.ts6.60 KB6.60 KB0.00 KB (0.00%)
cache.ts10.63 KB10.63 KB0.00 KB (0.00%)
config.ts20.91 KB20.91 KB0.00 KB (0.00%)
differ.ts19.77 KB19.77 KB0.00 KB (0.00%)
http-client.ts21.55 KB21.55 KB0.00 KB (0.00%)
logger.ts10.84 KB10.84 KB0.00 KB (0.00%)
metric.ts8.86 KB8.86 KB0.00 KB (0.00%)
optic.ts6.68 KB6.68 KB0.00 KB (0.00%)
pubsub.ts14.90 KB14.90 KB0.00 KB (0.00%)
queue.ts11.57 KB11.57 KB0.00 KB (0.00%)
schedule.ts10.74 KB10.74 KB0.00 KB (0.00%)
schema-class.ts19.48 KB19.48 KB0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts29.41 KB29.41 KB0.00 KB (0.00%)
schema-representation-roundtrip.ts25.63 KB25.63 KB0.00 KB (0.00%)
schema-string-transformation.ts13.58 KB13.58 KB0.00 KB (0.00%)
schema-string.ts11.09 KB11.09 KB0.00 KB (0.00%)
schema-template-literal.ts15.38 KB15.38 KB0.00 KB (0.00%)
schema-toArbitrary.ts21.52 KB21.52 KB0.00 KB (0.00%)
schema-toCodeDocument.ts24.00 KB24.00 KB0.00 KB (0.00%)
schema-toCodecJson.ts18.74 KB18.74 KB0.00 KB (0.00%)
schema-toEquivalence.ts18.57 KB18.57 KB0.00 KB (0.00%)
schema-toFormatter.ts18.43 KB18.43 KB0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts22.59 KB22.59 KB0.00 KB (0.00%)
schema-toRepresentation.ts19.08 KB19.08 KB0.00 KB (0.00%)
schema.ts18.73 KB18.73 KB0.00 KB (0.00%)
stm.ts12.63 KB12.63 KB0.00 KB (0.00%)
stream.ts9.67 KB9.67 KB0.00 KB (0.00%)

@pullfrogpullfrogBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Two minor observations — the core fix is correct, well-reasoned, and I verified the suite passes.

Reviewed changes

  • Migrator.fromFileSystem (packages/effect/src/unstable/sql/Migrator.ts): the migration specifier is now resolved through the Path service (path.join + path.toFileUrl) instead of a bare `${directory}/${basename}` string, so Windows absolute paths become importable file:// URLs. The loader type widens to Loader<FileSystem | Path>, and orDie keeps toFileUrl's typed BadArgument failure a defect so loadMigration reports it as a normal import error.
  • Two new loader tests (packages/effect/test/Migrator.test.ts): a Windows-like Path stand-in proves import receives file:///C:/migrations/0001_first.js, and a failing toFileUrl is surfaced as an import error. The existing filesystem test now provides Path.layer.
  • A patch changeset documenting the breaking requirement widening.

I ran npx vitest run --project effect packages/effect/test/Migrator.test.ts → 5 passed, and confirmed the platform NodePath.layer (via node:url.pathToFileURL) plus NodeServices.layer make the fix correct on Windows without breaking aggregate-layer callers. The orDie reasoning checks out against loadMigration's catchDefect. Two rough edges inline.

Pullfrog⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

// `import` needs a file URL: on Windows an absolute path such as
// `D:\migrations\1_init.ts` is rejected by the ESM loader. `orDie` keeps the
// failure a defect so `loadMigration` reports it as an import error.
Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, mostly worth a confirmation: for a relative directory value, this now routes through path.toFileUrlresolve, so the specifier base silently changes from "resolved against the library module (broken at runtime)" to "resolved against the current working directory" — which is actually consistent with how readDirectory already treats the directory. That's an improvement, but it's a semantic change beyond the Windows fix and isn't called out in the PR description or changeset. If relative-directory (./migrations) support is intended to be CWD-relative, it's fine as-is; otherwise worth a note.

)

// the raw path would have been rejected as protocol "c:"
assert.include(specifier, "file:///C:/migrations/0001_first.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the assertion is coupled to the importer's error-message format. I confirmed plain Node's ERR_MODULE_NOT_FOUND strips the scheme (Cannot find module '/C:/migrations/0001_first.js') — it's only Vitest's wrapper that currently echoes the full file:///... URL, which is why this passes here and fails when reverted. It's a valid regression test today, but a future loader/vitest message change could break it spuriously. Consider asserting on something less format-dependent, e.g. that the defect text contains the normalized forward-slash path without a \ backslash, or pin the message check more loosely.

@tim-smart
tim-smart enabled auto-merge (rebase) August 10, 2026 04:10
@tim-smart
tim-smart merged commit bc80d14 into Effect-TS:mainAug 10, 2026
13 of 16 checks passed
@effect-boteffect-bot mentioned this pull request Aug 10, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrator.fromFileSystem fails on Windows under moduleResolution:bundler option in tsconfig.json

2 participants

@CDVolvik@tim-smart
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem - #7166

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url
Aug 10, 2026
Merged

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem#7166
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url

Conversation

@CDVolvik

Copy link
Copy Markdown
Contributor

Type

  • Bug Fix

Description

Migrator.fromFileSystem passes the directory and file name straight to import. On Windows that produces a specifier such as D:\migrations\1_init.ts, which the ESM loader rejects:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader.
On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

The loader now builds the specifier with the Path service, which already knows how to produce a file URL for the host platform:

Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory,basename))),(url)=> ...)

Two things worth calling out, since neither is free:

fromFileSystem widens from Loader<FileSystem> to Loader<FileSystem | Path>. Core has no Windows Path implementation and hardcoding node:url here would be wrong, so the platform has to supply it. Callers on an aggregate layer such as NodeServices.layer are unaffected; callers providing FileSystem alone now also need a Path layer, and on Windows it has to be a platform-aware one rather than the POSIX Path.layer. The changeset spells this out.

toFileUrl fails in the typed error channel with BadArgument, while loadMigration only normalizes defects. Without orDie that failure would escape the MigrationError | SqlError channel that make advertises, so it is turned back into a defect and reported as an import error like any other.

Validation

  • pnpm check
  • pnpm lint
  • pnpm vitest run --project effect packages/effect/test/Migrator.test.ts (5 passed)

Both new tests were checked against the unfixed code rather than only the fixed code. Reverting the URL conversion fails the first with expected 'Error: Cannot find module /@id/C:\m…' to include 'file:///C:/migrations/0001_first.js', and dropping orDie fails the second with expected 'no defect' to include 'defect:'.

The Windows case is covered with a stand-in Path provider, because core has no win32 implementation and packages/effect should not depend on @effect/platform-node-shared for a test.

Related

Closes#4297

@changeset-bot

changeset-botBot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 282b9e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
NameType
effectPatch
@effect/ai-anthropicPatch
@effect/ai-openaiPatch
@effect/ai-openai-compatPatch
@effect/ai-openrouterPatch
@effect/atom-reactPatch
@effect/atom-solidPatch
@effect/atom-vuePatch
@effect/docgenPatch
@effect/doctestPatch
@effect/openapi-generatorPatch
@effect/opentelemetryPatch
@effect/platform-browserPatch
@effect/platform-bunPatch
@effect/platform-denoPatch
@effect/platform-nodePatch
@effect/platform-node-sharedPatch
@effect/sql-clickhousePatch
@effect/sql-d1Patch
@effect/sql-libsqlPatch
@effect/sql-mssqlPatch
@effect/sql-mysql2Patch
@effect/sql-pgPatch
@effect/sql-pglitePatch
@effect/sql-sqlite-bunPatch
@effect/sql-sqlite-doPatch
@effect/sql-sqlite-nodePatch
@effect/sql-sqlite-react-nativePatch
@effect/sql-sqlite-wasmPatch
@effect/vitestPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File NameCurrent SizePrevious SizeDifference
basic.ts6.92 KB6.92 KB0.00 KB (0.00%)
batching.ts9.72 KB9.72 KB0.00 KB (0.00%)
brand.ts6.60 KB6.60 KB0.00 KB (0.00%)
cache.ts10.63 KB10.63 KB0.00 KB (0.00%)
config.ts20.91 KB20.91 KB0.00 KB (0.00%)
differ.ts19.77 KB19.77 KB0.00 KB (0.00%)
http-client.ts21.55 KB21.55 KB0.00 KB (0.00%)
logger.ts10.84 KB10.84 KB0.00 KB (0.00%)
metric.ts8.86 KB8.86 KB0.00 KB (0.00%)
optic.ts6.68 KB6.68 KB0.00 KB (0.00%)
pubsub.ts14.90 KB14.90 KB0.00 KB (0.00%)
queue.ts11.57 KB11.57 KB0.00 KB (0.00%)
schedule.ts10.74 KB10.74 KB0.00 KB (0.00%)
schema-class.ts19.48 KB19.48 KB0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts29.41 KB29.41 KB0.00 KB (0.00%)
schema-representation-roundtrip.ts25.63 KB25.63 KB0.00 KB (0.00%)
schema-string-transformation.ts13.58 KB13.58 KB0.00 KB (0.00%)
schema-string.ts11.09 KB11.09 KB0.00 KB (0.00%)
schema-template-literal.ts15.38 KB15.38 KB0.00 KB (0.00%)
schema-toArbitrary.ts21.52 KB21.52 KB0.00 KB (0.00%)
schema-toCodeDocument.ts24.00 KB24.00 KB0.00 KB (0.00%)
schema-toCodecJson.ts18.74 KB18.74 KB0.00 KB (0.00%)
schema-toEquivalence.ts18.57 KB18.57 KB0.00 KB (0.00%)
schema-toFormatter.ts18.43 KB18.43 KB0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts22.59 KB22.59 KB0.00 KB (0.00%)
schema-toRepresentation.ts19.08 KB19.08 KB0.00 KB (0.00%)
schema.ts18.73 KB18.73 KB0.00 KB (0.00%)
stm.ts12.63 KB12.63 KB0.00 KB (0.00%)
stream.ts9.67 KB9.67 KB0.00 KB (0.00%)

@pullfrogpullfrogBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Two minor observations — the core fix is correct, well-reasoned, and I verified the suite passes.

Reviewed changes

  • Migrator.fromFileSystem (packages/effect/src/unstable/sql/Migrator.ts): the migration specifier is now resolved through the Path service (path.join + path.toFileUrl) instead of a bare `${directory}/${basename}` string, so Windows absolute paths become importable file:// URLs. The loader type widens to Loader<FileSystem | Path>, and orDie keeps toFileUrl's typed BadArgument failure a defect so loadMigration reports it as a normal import error.
  • Two new loader tests (packages/effect/test/Migrator.test.ts): a Windows-like Path stand-in proves import receives file:///C:/migrations/0001_first.js, and a failing toFileUrl is surfaced as an import error. The existing filesystem test now provides Path.layer.
  • A patch changeset documenting the breaking requirement widening.

I ran npx vitest run --project effect packages/effect/test/Migrator.test.ts → 5 passed, and confirmed the platform NodePath.layer (via node:url.pathToFileURL) plus NodeServices.layer make the fix correct on Windows without breaking aggregate-layer callers. The orDie reasoning checks out against loadMigration's catchDefect. Two rough edges inline.

Pullfrog⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

// `import` needs a file URL: on Windows an absolute path such as
// `D:\migrations\1_init.ts` is rejected by the ESM loader. `orDie` keeps the
// failure a defect so `loadMigration` reports it as an import error.
Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, mostly worth a confirmation: for a relative directory value, this now routes through path.toFileUrlresolve, so the specifier base silently changes from "resolved against the library module (broken at runtime)" to "resolved against the current working directory" — which is actually consistent with how readDirectory already treats the directory. That's an improvement, but it's a semantic change beyond the Windows fix and isn't called out in the PR description or changeset. If relative-directory (./migrations) support is intended to be CWD-relative, it's fine as-is; otherwise worth a note.

)

// the raw path would have been rejected as protocol "c:"
assert.include(specifier, "file:///C:/migrations/0001_first.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the assertion is coupled to the importer's error-message format. I confirmed plain Node's ERR_MODULE_NOT_FOUND strips the scheme (Cannot find module '/C:/migrations/0001_first.js') — it's only Vitest's wrapper that currently echoes the full file:///... URL, which is why this passes here and fails when reverted. It's a valid regression test today, but a future loader/vitest message change could break it spuriously. Consider asserting on something less format-dependent, e.g. that the defect text contains the normalized forward-slash path without a \ backslash, or pin the message check more loosely.

@tim-smart
tim-smart enabled auto-merge (rebase) August 10, 2026 04:10
@tim-smart
tim-smart merged commit bc80d14 into Effect-TS:mainAug 10, 2026
13 of 16 checks passed
@effect-boteffect-bot mentioned this pull request Aug 10, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrator.fromFileSystem fails on Windows under moduleResolution:bundler option in tsconfig.json

2 participants

@CDVolvik@tim-smart
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem - #7166

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url
Aug 10, 2026
Merged

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem#7166
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url

Conversation

@CDVolvik

Copy link
Copy Markdown
Contributor

Type

  • Bug Fix

Description

Migrator.fromFileSystem passes the directory and file name straight to import. On Windows that produces a specifier such as D:\migrations\1_init.ts, which the ESM loader rejects:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader.
On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

The loader now builds the specifier with the Path service, which already knows how to produce a file URL for the host platform:

Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory,basename))),(url)=> ...)

Two things worth calling out, since neither is free:

fromFileSystem widens from Loader<FileSystem> to Loader<FileSystem | Path>. Core has no Windows Path implementation and hardcoding node:url here would be wrong, so the platform has to supply it. Callers on an aggregate layer such as NodeServices.layer are unaffected; callers providing FileSystem alone now also need a Path layer, and on Windows it has to be a platform-aware one rather than the POSIX Path.layer. The changeset spells this out.

toFileUrl fails in the typed error channel with BadArgument, while loadMigration only normalizes defects. Without orDie that failure would escape the MigrationError | SqlError channel that make advertises, so it is turned back into a defect and reported as an import error like any other.

Validation

  • pnpm check
  • pnpm lint
  • pnpm vitest run --project effect packages/effect/test/Migrator.test.ts (5 passed)

Both new tests were checked against the unfixed code rather than only the fixed code. Reverting the URL conversion fails the first with expected 'Error: Cannot find module /@id/C:\m…' to include 'file:///C:/migrations/0001_first.js', and dropping orDie fails the second with expected 'no defect' to include 'defect:'.

The Windows case is covered with a stand-in Path provider, because core has no win32 implementation and packages/effect should not depend on @effect/platform-node-shared for a test.

Related

Closes#4297

@changeset-bot

changeset-botBot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 282b9e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
NameType
effectPatch
@effect/ai-anthropicPatch
@effect/ai-openaiPatch
@effect/ai-openai-compatPatch
@effect/ai-openrouterPatch
@effect/atom-reactPatch
@effect/atom-solidPatch
@effect/atom-vuePatch
@effect/docgenPatch
@effect/doctestPatch
@effect/openapi-generatorPatch
@effect/opentelemetryPatch
@effect/platform-browserPatch
@effect/platform-bunPatch
@effect/platform-denoPatch
@effect/platform-nodePatch
@effect/platform-node-sharedPatch
@effect/sql-clickhousePatch
@effect/sql-d1Patch
@effect/sql-libsqlPatch
@effect/sql-mssqlPatch
@effect/sql-mysql2Patch
@effect/sql-pgPatch
@effect/sql-pglitePatch
@effect/sql-sqlite-bunPatch
@effect/sql-sqlite-doPatch
@effect/sql-sqlite-nodePatch
@effect/sql-sqlite-react-nativePatch
@effect/sql-sqlite-wasmPatch
@effect/vitestPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File NameCurrent SizePrevious SizeDifference
basic.ts6.92 KB6.92 KB0.00 KB (0.00%)
batching.ts9.72 KB9.72 KB0.00 KB (0.00%)
brand.ts6.60 KB6.60 KB0.00 KB (0.00%)
cache.ts10.63 KB10.63 KB0.00 KB (0.00%)
config.ts20.91 KB20.91 KB0.00 KB (0.00%)
differ.ts19.77 KB19.77 KB0.00 KB (0.00%)
http-client.ts21.55 KB21.55 KB0.00 KB (0.00%)
logger.ts10.84 KB10.84 KB0.00 KB (0.00%)
metric.ts8.86 KB8.86 KB0.00 KB (0.00%)
optic.ts6.68 KB6.68 KB0.00 KB (0.00%)
pubsub.ts14.90 KB14.90 KB0.00 KB (0.00%)
queue.ts11.57 KB11.57 KB0.00 KB (0.00%)
schedule.ts10.74 KB10.74 KB0.00 KB (0.00%)
schema-class.ts19.48 KB19.48 KB0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts29.41 KB29.41 KB0.00 KB (0.00%)
schema-representation-roundtrip.ts25.63 KB25.63 KB0.00 KB (0.00%)
schema-string-transformation.ts13.58 KB13.58 KB0.00 KB (0.00%)
schema-string.ts11.09 KB11.09 KB0.00 KB (0.00%)
schema-template-literal.ts15.38 KB15.38 KB0.00 KB (0.00%)
schema-toArbitrary.ts21.52 KB21.52 KB0.00 KB (0.00%)
schema-toCodeDocument.ts24.00 KB24.00 KB0.00 KB (0.00%)
schema-toCodecJson.ts18.74 KB18.74 KB0.00 KB (0.00%)
schema-toEquivalence.ts18.57 KB18.57 KB0.00 KB (0.00%)
schema-toFormatter.ts18.43 KB18.43 KB0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts22.59 KB22.59 KB0.00 KB (0.00%)
schema-toRepresentation.ts19.08 KB19.08 KB0.00 KB (0.00%)
schema.ts18.73 KB18.73 KB0.00 KB (0.00%)
stm.ts12.63 KB12.63 KB0.00 KB (0.00%)
stream.ts9.67 KB9.67 KB0.00 KB (0.00%)

@pullfrogpullfrogBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Two minor observations — the core fix is correct, well-reasoned, and I verified the suite passes.

Reviewed changes

  • Migrator.fromFileSystem (packages/effect/src/unstable/sql/Migrator.ts): the migration specifier is now resolved through the Path service (path.join + path.toFileUrl) instead of a bare `${directory}/${basename}` string, so Windows absolute paths become importable file:// URLs. The loader type widens to Loader<FileSystem | Path>, and orDie keeps toFileUrl's typed BadArgument failure a defect so loadMigration reports it as a normal import error.
  • Two new loader tests (packages/effect/test/Migrator.test.ts): a Windows-like Path stand-in proves import receives file:///C:/migrations/0001_first.js, and a failing toFileUrl is surfaced as an import error. The existing filesystem test now provides Path.layer.
  • A patch changeset documenting the breaking requirement widening.

I ran npx vitest run --project effect packages/effect/test/Migrator.test.ts → 5 passed, and confirmed the platform NodePath.layer (via node:url.pathToFileURL) plus NodeServices.layer make the fix correct on Windows without breaking aggregate-layer callers. The orDie reasoning checks out against loadMigration's catchDefect. Two rough edges inline.

Pullfrog⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

// `import` needs a file URL: on Windows an absolute path such as
// `D:\migrations\1_init.ts` is rejected by the ESM loader. `orDie` keeps the
// failure a defect so `loadMigration` reports it as an import error.
Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, mostly worth a confirmation: for a relative directory value, this now routes through path.toFileUrlresolve, so the specifier base silently changes from "resolved against the library module (broken at runtime)" to "resolved against the current working directory" — which is actually consistent with how readDirectory already treats the directory. That's an improvement, but it's a semantic change beyond the Windows fix and isn't called out in the PR description or changeset. If relative-directory (./migrations) support is intended to be CWD-relative, it's fine as-is; otherwise worth a note.

)

// the raw path would have been rejected as protocol "c:"
assert.include(specifier, "file:///C:/migrations/0001_first.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the assertion is coupled to the importer's error-message format. I confirmed plain Node's ERR_MODULE_NOT_FOUND strips the scheme (Cannot find module '/C:/migrations/0001_first.js') — it's only Vitest's wrapper that currently echoes the full file:///... URL, which is why this passes here and fails when reverted. It's a valid regression test today, but a future loader/vitest message change could break it spuriously. Consider asserting on something less format-dependent, e.g. that the defect text contains the normalized forward-slash path without a \ backslash, or pin the message check more loosely.

@tim-smart
tim-smart enabled auto-merge (rebase) August 10, 2026 04:10
@tim-smart
tim-smart merged commit bc80d14 into Effect-TS:mainAug 10, 2026
13 of 16 checks passed
@effect-boteffect-bot mentioned this pull request Aug 10, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrator.fromFileSystem fails on Windows under moduleResolution:bundler option in tsconfig.json

2 participants

@CDVolvik@tim-smart
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem - #7166

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url
Aug 10, 2026
Merged

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem#7166
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url

Conversation

@CDVolvik

Copy link
Copy Markdown
Contributor

Type

  • Bug Fix

Description

Migrator.fromFileSystem passes the directory and file name straight to import. On Windows that produces a specifier such as D:\migrations\1_init.ts, which the ESM loader rejects:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader.
On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

The loader now builds the specifier with the Path service, which already knows how to produce a file URL for the host platform:

Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory,basename))),(url)=> ...)

Two things worth calling out, since neither is free:

fromFileSystem widens from Loader<FileSystem> to Loader<FileSystem | Path>. Core has no Windows Path implementation and hardcoding node:url here would be wrong, so the platform has to supply it. Callers on an aggregate layer such as NodeServices.layer are unaffected; callers providing FileSystem alone now also need a Path layer, and on Windows it has to be a platform-aware one rather than the POSIX Path.layer. The changeset spells this out.

toFileUrl fails in the typed error channel with BadArgument, while loadMigration only normalizes defects. Without orDie that failure would escape the MigrationError | SqlError channel that make advertises, so it is turned back into a defect and reported as an import error like any other.

Validation

  • pnpm check
  • pnpm lint
  • pnpm vitest run --project effect packages/effect/test/Migrator.test.ts (5 passed)

Both new tests were checked against the unfixed code rather than only the fixed code. Reverting the URL conversion fails the first with expected 'Error: Cannot find module /@id/C:\m…' to include 'file:///C:/migrations/0001_first.js', and dropping orDie fails the second with expected 'no defect' to include 'defect:'.

The Windows case is covered with a stand-in Path provider, because core has no win32 implementation and packages/effect should not depend on @effect/platform-node-shared for a test.

Related

Closes#4297

@changeset-bot

changeset-botBot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 282b9e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
NameType
effectPatch
@effect/ai-anthropicPatch
@effect/ai-openaiPatch
@effect/ai-openai-compatPatch
@effect/ai-openrouterPatch
@effect/atom-reactPatch
@effect/atom-solidPatch
@effect/atom-vuePatch
@effect/docgenPatch
@effect/doctestPatch
@effect/openapi-generatorPatch
@effect/opentelemetryPatch
@effect/platform-browserPatch
@effect/platform-bunPatch
@effect/platform-denoPatch
@effect/platform-nodePatch
@effect/platform-node-sharedPatch
@effect/sql-clickhousePatch
@effect/sql-d1Patch
@effect/sql-libsqlPatch
@effect/sql-mssqlPatch
@effect/sql-mysql2Patch
@effect/sql-pgPatch
@effect/sql-pglitePatch
@effect/sql-sqlite-bunPatch
@effect/sql-sqlite-doPatch
@effect/sql-sqlite-nodePatch
@effect/sql-sqlite-react-nativePatch
@effect/sql-sqlite-wasmPatch
@effect/vitestPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File NameCurrent SizePrevious SizeDifference
basic.ts6.92 KB6.92 KB0.00 KB (0.00%)
batching.ts9.72 KB9.72 KB0.00 KB (0.00%)
brand.ts6.60 KB6.60 KB0.00 KB (0.00%)
cache.ts10.63 KB10.63 KB0.00 KB (0.00%)
config.ts20.91 KB20.91 KB0.00 KB (0.00%)
differ.ts19.77 KB19.77 KB0.00 KB (0.00%)
http-client.ts21.55 KB21.55 KB0.00 KB (0.00%)
logger.ts10.84 KB10.84 KB0.00 KB (0.00%)
metric.ts8.86 KB8.86 KB0.00 KB (0.00%)
optic.ts6.68 KB6.68 KB0.00 KB (0.00%)
pubsub.ts14.90 KB14.90 KB0.00 KB (0.00%)
queue.ts11.57 KB11.57 KB0.00 KB (0.00%)
schedule.ts10.74 KB10.74 KB0.00 KB (0.00%)
schema-class.ts19.48 KB19.48 KB0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts29.41 KB29.41 KB0.00 KB (0.00%)
schema-representation-roundtrip.ts25.63 KB25.63 KB0.00 KB (0.00%)
schema-string-transformation.ts13.58 KB13.58 KB0.00 KB (0.00%)
schema-string.ts11.09 KB11.09 KB0.00 KB (0.00%)
schema-template-literal.ts15.38 KB15.38 KB0.00 KB (0.00%)
schema-toArbitrary.ts21.52 KB21.52 KB0.00 KB (0.00%)
schema-toCodeDocument.ts24.00 KB24.00 KB0.00 KB (0.00%)
schema-toCodecJson.ts18.74 KB18.74 KB0.00 KB (0.00%)
schema-toEquivalence.ts18.57 KB18.57 KB0.00 KB (0.00%)
schema-toFormatter.ts18.43 KB18.43 KB0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts22.59 KB22.59 KB0.00 KB (0.00%)
schema-toRepresentation.ts19.08 KB19.08 KB0.00 KB (0.00%)
schema.ts18.73 KB18.73 KB0.00 KB (0.00%)
stm.ts12.63 KB12.63 KB0.00 KB (0.00%)
stream.ts9.67 KB9.67 KB0.00 KB (0.00%)

@pullfrogpullfrogBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Two minor observations — the core fix is correct, well-reasoned, and I verified the suite passes.

Reviewed changes

  • Migrator.fromFileSystem (packages/effect/src/unstable/sql/Migrator.ts): the migration specifier is now resolved through the Path service (path.join + path.toFileUrl) instead of a bare `${directory}/${basename}` string, so Windows absolute paths become importable file:// URLs. The loader type widens to Loader<FileSystem | Path>, and orDie keeps toFileUrl's typed BadArgument failure a defect so loadMigration reports it as a normal import error.
  • Two new loader tests (packages/effect/test/Migrator.test.ts): a Windows-like Path stand-in proves import receives file:///C:/migrations/0001_first.js, and a failing toFileUrl is surfaced as an import error. The existing filesystem test now provides Path.layer.
  • A patch changeset documenting the breaking requirement widening.

I ran npx vitest run --project effect packages/effect/test/Migrator.test.ts → 5 passed, and confirmed the platform NodePath.layer (via node:url.pathToFileURL) plus NodeServices.layer make the fix correct on Windows without breaking aggregate-layer callers. The orDie reasoning checks out against loadMigration's catchDefect. Two rough edges inline.

Pullfrog⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

// `import` needs a file URL: on Windows an absolute path such as
// `D:\migrations\1_init.ts` is rejected by the ESM loader. `orDie` keeps the
// failure a defect so `loadMigration` reports it as an import error.
Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, mostly worth a confirmation: for a relative directory value, this now routes through path.toFileUrlresolve, so the specifier base silently changes from "resolved against the library module (broken at runtime)" to "resolved against the current working directory" — which is actually consistent with how readDirectory already treats the directory. That's an improvement, but it's a semantic change beyond the Windows fix and isn't called out in the PR description or changeset. If relative-directory (./migrations) support is intended to be CWD-relative, it's fine as-is; otherwise worth a note.

)

// the raw path would have been rejected as protocol "c:"
assert.include(specifier, "file:///C:/migrations/0001_first.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the assertion is coupled to the importer's error-message format. I confirmed plain Node's ERR_MODULE_NOT_FOUND strips the scheme (Cannot find module '/C:/migrations/0001_first.js') — it's only Vitest's wrapper that currently echoes the full file:///... URL, which is why this passes here and fails when reverted. It's a valid regression test today, but a future loader/vitest message change could break it spuriously. Consider asserting on something less format-dependent, e.g. that the defect text contains the normalized forward-slash path without a \ backslash, or pin the message check more loosely.

@tim-smart
tim-smart enabled auto-merge (rebase) August 10, 2026 04:10
@tim-smart
tim-smart merged commit bc80d14 into Effect-TS:mainAug 10, 2026
13 of 16 checks passed
@effect-boteffect-bot mentioned this pull request Aug 10, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrator.fromFileSystem fails on Windows under moduleResolution:bundler option in tsconfig.json

2 participants

@CDVolvik@tim-smart
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem - #7166

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url
Aug 10, 2026
Merged

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem#7166
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url

Conversation

@CDVolvik

Copy link
Copy Markdown
Contributor

Type

  • Bug Fix

Description

Migrator.fromFileSystem passes the directory and file name straight to import. On Windows that produces a specifier such as D:\migrations\1_init.ts, which the ESM loader rejects:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader.
On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

The loader now builds the specifier with the Path service, which already knows how to produce a file URL for the host platform:

Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory,basename))),(url)=> ...)

Two things worth calling out, since neither is free:

fromFileSystem widens from Loader<FileSystem> to Loader<FileSystem | Path>. Core has no Windows Path implementation and hardcoding node:url here would be wrong, so the platform has to supply it. Callers on an aggregate layer such as NodeServices.layer are unaffected; callers providing FileSystem alone now also need a Path layer, and on Windows it has to be a platform-aware one rather than the POSIX Path.layer. The changeset spells this out.

toFileUrl fails in the typed error channel with BadArgument, while loadMigration only normalizes defects. Without orDie that failure would escape the MigrationError | SqlError channel that make advertises, so it is turned back into a defect and reported as an import error like any other.

Validation

  • pnpm check
  • pnpm lint
  • pnpm vitest run --project effect packages/effect/test/Migrator.test.ts (5 passed)

Both new tests were checked against the unfixed code rather than only the fixed code. Reverting the URL conversion fails the first with expected 'Error: Cannot find module /@id/C:\m…' to include 'file:///C:/migrations/0001_first.js', and dropping orDie fails the second with expected 'no defect' to include 'defect:'.

The Windows case is covered with a stand-in Path provider, because core has no win32 implementation and packages/effect should not depend on @effect/platform-node-shared for a test.

Related

Closes#4297

@changeset-bot

changeset-botBot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 282b9e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
NameType
effectPatch
@effect/ai-anthropicPatch
@effect/ai-openaiPatch
@effect/ai-openai-compatPatch
@effect/ai-openrouterPatch
@effect/atom-reactPatch
@effect/atom-solidPatch
@effect/atom-vuePatch
@effect/docgenPatch
@effect/doctestPatch
@effect/openapi-generatorPatch
@effect/opentelemetryPatch
@effect/platform-browserPatch
@effect/platform-bunPatch
@effect/platform-denoPatch
@effect/platform-nodePatch
@effect/platform-node-sharedPatch
@effect/sql-clickhousePatch
@effect/sql-d1Patch
@effect/sql-libsqlPatch
@effect/sql-mssqlPatch
@effect/sql-mysql2Patch
@effect/sql-pgPatch
@effect/sql-pglitePatch
@effect/sql-sqlite-bunPatch
@effect/sql-sqlite-doPatch
@effect/sql-sqlite-nodePatch
@effect/sql-sqlite-react-nativePatch
@effect/sql-sqlite-wasmPatch
@effect/vitestPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File NameCurrent SizePrevious SizeDifference
basic.ts6.92 KB6.92 KB0.00 KB (0.00%)
batching.ts9.72 KB9.72 KB0.00 KB (0.00%)
brand.ts6.60 KB6.60 KB0.00 KB (0.00%)
cache.ts10.63 KB10.63 KB0.00 KB (0.00%)
config.ts20.91 KB20.91 KB0.00 KB (0.00%)
differ.ts19.77 KB19.77 KB0.00 KB (0.00%)
http-client.ts21.55 KB21.55 KB0.00 KB (0.00%)
logger.ts10.84 KB10.84 KB0.00 KB (0.00%)
metric.ts8.86 KB8.86 KB0.00 KB (0.00%)
optic.ts6.68 KB6.68 KB0.00 KB (0.00%)
pubsub.ts14.90 KB14.90 KB0.00 KB (0.00%)
queue.ts11.57 KB11.57 KB0.00 KB (0.00%)
schedule.ts10.74 KB10.74 KB0.00 KB (0.00%)
schema-class.ts19.48 KB19.48 KB0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts29.41 KB29.41 KB0.00 KB (0.00%)
schema-representation-roundtrip.ts25.63 KB25.63 KB0.00 KB (0.00%)
schema-string-transformation.ts13.58 KB13.58 KB0.00 KB (0.00%)
schema-string.ts11.09 KB11.09 KB0.00 KB (0.00%)
schema-template-literal.ts15.38 KB15.38 KB0.00 KB (0.00%)
schema-toArbitrary.ts21.52 KB21.52 KB0.00 KB (0.00%)
schema-toCodeDocument.ts24.00 KB24.00 KB0.00 KB (0.00%)
schema-toCodecJson.ts18.74 KB18.74 KB0.00 KB (0.00%)
schema-toEquivalence.ts18.57 KB18.57 KB0.00 KB (0.00%)
schema-toFormatter.ts18.43 KB18.43 KB0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts22.59 KB22.59 KB0.00 KB (0.00%)
schema-toRepresentation.ts19.08 KB19.08 KB0.00 KB (0.00%)
schema.ts18.73 KB18.73 KB0.00 KB (0.00%)
stm.ts12.63 KB12.63 KB0.00 KB (0.00%)
stream.ts9.67 KB9.67 KB0.00 KB (0.00%)

@pullfrogpullfrogBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Two minor observations — the core fix is correct, well-reasoned, and I verified the suite passes.

Reviewed changes

  • Migrator.fromFileSystem (packages/effect/src/unstable/sql/Migrator.ts): the migration specifier is now resolved through the Path service (path.join + path.toFileUrl) instead of a bare `${directory}/${basename}` string, so Windows absolute paths become importable file:// URLs. The loader type widens to Loader<FileSystem | Path>, and orDie keeps toFileUrl's typed BadArgument failure a defect so loadMigration reports it as a normal import error.
  • Two new loader tests (packages/effect/test/Migrator.test.ts): a Windows-like Path stand-in proves import receives file:///C:/migrations/0001_first.js, and a failing toFileUrl is surfaced as an import error. The existing filesystem test now provides Path.layer.
  • A patch changeset documenting the breaking requirement widening.

I ran npx vitest run --project effect packages/effect/test/Migrator.test.ts → 5 passed, and confirmed the platform NodePath.layer (via node:url.pathToFileURL) plus NodeServices.layer make the fix correct on Windows without breaking aggregate-layer callers. The orDie reasoning checks out against loadMigration's catchDefect. Two rough edges inline.

Pullfrog⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

// `import` needs a file URL: on Windows an absolute path such as
// `D:\migrations\1_init.ts` is rejected by the ESM loader. `orDie` keeps the
// failure a defect so `loadMigration` reports it as an import error.
Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, mostly worth a confirmation: for a relative directory value, this now routes through path.toFileUrlresolve, so the specifier base silently changes from "resolved against the library module (broken at runtime)" to "resolved against the current working directory" — which is actually consistent with how readDirectory already treats the directory. That's an improvement, but it's a semantic change beyond the Windows fix and isn't called out in the PR description or changeset. If relative-directory (./migrations) support is intended to be CWD-relative, it's fine as-is; otherwise worth a note.

)

// the raw path would have been rejected as protocol "c:"
assert.include(specifier, "file:///C:/migrations/0001_first.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the assertion is coupled to the importer's error-message format. I confirmed plain Node's ERR_MODULE_NOT_FOUND strips the scheme (Cannot find module '/C:/migrations/0001_first.js') — it's only Vitest's wrapper that currently echoes the full file:///... URL, which is why this passes here and fails when reverted. It's a valid regression test today, but a future loader/vitest message change could break it spuriously. Consider asserting on something less format-dependent, e.g. that the defect text contains the normalized forward-slash path without a \ backslash, or pin the message check more loosely.

@tim-smart
tim-smart enabled auto-merge (rebase) August 10, 2026 04:10
@tim-smart
tim-smart merged commit bc80d14 into Effect-TS:mainAug 10, 2026
13 of 16 checks passed
@effect-boteffect-bot mentioned this pull request Aug 10, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrator.fromFileSystem fails on Windows under moduleResolution:bundler option in tsconfig.json

2 participants

@CDVolvik@tim-smart
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem - #7166

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url
Aug 10, 2026
Merged

Import migrations through a file URL so Windows paths work in Migrator.fromFileSystem#7166
tim-smart merged 2 commits into
Effect-TS:mainfrom
CDVolvik:fix/migrator-windows-file-url

Conversation

@CDVolvik

Copy link
Copy Markdown
Contributor

Type

  • Bug Fix

Description

Migrator.fromFileSystem passes the directory and file name straight to import. On Windows that produces a specifier such as D:\migrations\1_init.ts, which the ESM loader rejects:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader.
On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

The loader now builds the specifier with the Path service, which already knows how to produce a file URL for the host platform:

Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory,basename))),(url)=> ...)

Two things worth calling out, since neither is free:

fromFileSystem widens from Loader<FileSystem> to Loader<FileSystem | Path>. Core has no Windows Path implementation and hardcoding node:url here would be wrong, so the platform has to supply it. Callers on an aggregate layer such as NodeServices.layer are unaffected; callers providing FileSystem alone now also need a Path layer, and on Windows it has to be a platform-aware one rather than the POSIX Path.layer. The changeset spells this out.

toFileUrl fails in the typed error channel with BadArgument, while loadMigration only normalizes defects. Without orDie that failure would escape the MigrationError | SqlError channel that make advertises, so it is turned back into a defect and reported as an import error like any other.

Validation

  • pnpm check
  • pnpm lint
  • pnpm vitest run --project effect packages/effect/test/Migrator.test.ts (5 passed)

Both new tests were checked against the unfixed code rather than only the fixed code. Reverting the URL conversion fails the first with expected 'Error: Cannot find module /@id/C:\m…' to include 'file:///C:/migrations/0001_first.js', and dropping orDie fails the second with expected 'no defect' to include 'defect:'.

The Windows case is covered with a stand-in Path provider, because core has no win32 implementation and packages/effect should not depend on @effect/platform-node-shared for a test.

Related

Closes#4297

@changeset-bot

changeset-botBot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 282b9e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
NameType
effectPatch
@effect/ai-anthropicPatch
@effect/ai-openaiPatch
@effect/ai-openai-compatPatch
@effect/ai-openrouterPatch
@effect/atom-reactPatch
@effect/atom-solidPatch
@effect/atom-vuePatch
@effect/docgenPatch
@effect/doctestPatch
@effect/openapi-generatorPatch
@effect/opentelemetryPatch
@effect/platform-browserPatch
@effect/platform-bunPatch
@effect/platform-denoPatch
@effect/platform-nodePatch
@effect/platform-node-sharedPatch
@effect/sql-clickhousePatch
@effect/sql-d1Patch
@effect/sql-libsqlPatch
@effect/sql-mssqlPatch
@effect/sql-mysql2Patch
@effect/sql-pgPatch
@effect/sql-pglitePatch
@effect/sql-sqlite-bunPatch
@effect/sql-sqlite-doPatch
@effect/sql-sqlite-nodePatch
@effect/sql-sqlite-react-nativePatch
@effect/sql-sqlite-wasmPatch
@effect/vitestPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File NameCurrent SizePrevious SizeDifference
basic.ts6.92 KB6.92 KB0.00 KB (0.00%)
batching.ts9.72 KB9.72 KB0.00 KB (0.00%)
brand.ts6.60 KB6.60 KB0.00 KB (0.00%)
cache.ts10.63 KB10.63 KB0.00 KB (0.00%)
config.ts20.91 KB20.91 KB0.00 KB (0.00%)
differ.ts19.77 KB19.77 KB0.00 KB (0.00%)
http-client.ts21.55 KB21.55 KB0.00 KB (0.00%)
logger.ts10.84 KB10.84 KB0.00 KB (0.00%)
metric.ts8.86 KB8.86 KB0.00 KB (0.00%)
optic.ts6.68 KB6.68 KB0.00 KB (0.00%)
pubsub.ts14.90 KB14.90 KB0.00 KB (0.00%)
queue.ts11.57 KB11.57 KB0.00 KB (0.00%)
schedule.ts10.74 KB10.74 KB0.00 KB (0.00%)
schema-class.ts19.48 KB19.48 KB0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts29.41 KB29.41 KB0.00 KB (0.00%)
schema-representation-roundtrip.ts25.63 KB25.63 KB0.00 KB (0.00%)
schema-string-transformation.ts13.58 KB13.58 KB0.00 KB (0.00%)
schema-string.ts11.09 KB11.09 KB0.00 KB (0.00%)
schema-template-literal.ts15.38 KB15.38 KB0.00 KB (0.00%)
schema-toArbitrary.ts21.52 KB21.52 KB0.00 KB (0.00%)
schema-toCodeDocument.ts24.00 KB24.00 KB0.00 KB (0.00%)
schema-toCodecJson.ts18.74 KB18.74 KB0.00 KB (0.00%)
schema-toEquivalence.ts18.57 KB18.57 KB0.00 KB (0.00%)
schema-toFormatter.ts18.43 KB18.43 KB0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts22.59 KB22.59 KB0.00 KB (0.00%)
schema-toRepresentation.ts19.08 KB19.08 KB0.00 KB (0.00%)
schema.ts18.73 KB18.73 KB0.00 KB (0.00%)
stm.ts12.63 KB12.63 KB0.00 KB (0.00%)
stream.ts9.67 KB9.67 KB0.00 KB (0.00%)

@pullfrogpullfrogBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Two minor observations — the core fix is correct, well-reasoned, and I verified the suite passes.

Reviewed changes

  • Migrator.fromFileSystem (packages/effect/src/unstable/sql/Migrator.ts): the migration specifier is now resolved through the Path service (path.join + path.toFileUrl) instead of a bare `${directory}/${basename}` string, so Windows absolute paths become importable file:// URLs. The loader type widens to Loader<FileSystem | Path>, and orDie keeps toFileUrl's typed BadArgument failure a defect so loadMigration reports it as a normal import error.
  • Two new loader tests (packages/effect/test/Migrator.test.ts): a Windows-like Path stand-in proves import receives file:///C:/migrations/0001_first.js, and a failing toFileUrl is surfaced as an import error. The existing filesystem test now provides Path.layer.
  • A patch changeset documenting the breaking requirement widening.

I ran npx vitest run --project effect packages/effect/test/Migrator.test.ts → 5 passed, and confirmed the platform NodePath.layer (via node:url.pathToFileURL) plus NodeServices.layer make the fix correct on Windows without breaking aggregate-layer callers. The orDie reasoning checks out against loadMigration's catchDefect. Two rough edges inline.

Pullfrog⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

// `import` needs a file URL: on Windows an absolute path such as
// `D:\migrations\1_init.ts` is rejected by the ESM loader. `orDie` keeps the
// failure a defect so `loadMigration` reports it as an import error.
Effect.flatMap(Effect.orDie(path.toFileUrl(path.join(directory, basename))), (url) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, mostly worth a confirmation: for a relative directory value, this now routes through path.toFileUrlresolve, so the specifier base silently changes from "resolved against the library module (broken at runtime)" to "resolved against the current working directory" — which is actually consistent with how readDirectory already treats the directory. That's an improvement, but it's a semantic change beyond the Windows fix and isn't called out in the PR description or changeset. If relative-directory (./migrations) support is intended to be CWD-relative, it's fine as-is; otherwise worth a note.

)

// the raw path would have been rejected as protocol "c:"
assert.include(specifier, "file:///C:/migrations/0001_first.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the assertion is coupled to the importer's error-message format. I confirmed plain Node's ERR_MODULE_NOT_FOUND strips the scheme (Cannot find module '/C:/migrations/0001_first.js') — it's only Vitest's wrapper that currently echoes the full file:///... URL, which is why this passes here and fails when reverted. It's a valid regression test today, but a future loader/vitest message change could break it spuriously. Consider asserting on something less format-dependent, e.g. that the defect text contains the normalized forward-slash path without a \ backslash, or pin the message check more loosely.

@tim-smart
tim-smart enabled auto-merge (rebase) August 10, 2026 04:10
@tim-smart
tim-smart merged commit bc80d14 into Effect-TS:mainAug 10, 2026
13 of 16 checks passed
@effect-boteffect-bot mentioned this pull request Aug 10, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrator.fromFileSystem fails on Windows under moduleResolution:bundler option in tsconfig.json

2 participants

@CDVolvik@tim-smart