Migrate fjs/effects/memory, mock, eff, node, and fjs/text/sgr to .f.mjs - #1488
Conversation
Leaf module. Updates 13 dependents across cas, mcp, protocol/mcp, and effects/node to import runtime values from .f.mjs and types from types.ts separately, and fixes two markdown link hrefs.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
functionalscript | d6cf1b1 | Commit Preview URL Branch Preview URL |
Aug 11 2026, 01:32 PM |
Leaf module with no proof.f.ts of its own. Updates 4 dependents (effects/node/virtual, effects/memory, protocol/mcp, emergent_testing) to import runtime values from .f.mjs and types from types.ts separately.
proof.f.ts stays TypeScript for now: it has a runtime dependency on ../proof.f.ts (fjs/effects/proof.f.ts), still unmigrated. Updates 4 dependents (protocol/mcp, cas, cas/evo, emergent_testing) and 3 doc references.
…into types.ts The Node.js effect operations module: filesystem, networking, exec, console, sandbox, test registration, and the NodeOp/NodeProgram types. Nearly the entire public surface moves to types.ts unchanged (real TypeScript); runtime combinators become JSDoc-typed .mjs, using @template/@PARAM on writeFromStream and both where an inline @type can't expose its generics to the body, and an any-cast on both's all(a, b) call to carry over the original's unchecked `as` widening. Updates 26 dependents across the repo (djs, text/sgr, cli, dev, ci, media/type, cas, mcp, protocol/mcp, emergent_testing, website, and effects' own submodules) to import runtime values from .f.mjs and types from types.ts separately, plus a batch of stale module.f.ts doc references (including three with stale line-number citations that now split across module.f.mjs and types.ts). proof.f.ts stays TypeScript for now.
o2alexanderfedin
left a comment
There was a problem hiding this comment.
Reviewed at 91bd993. Four modules — effects/memory, effects/mock, effects/eff, effects/node. All four are equivalent to main's compiled output, npx tsc is clean, 2356 pass / 0 fail, links are 144/144 with none added and 0 label-target mismatches, and the CHANGELOG covers all four. One thing to fix.
Two apparent differences turned out to be artifacts of comparing against compiled output, and both are worth naming because they are the good outcome:
effects/nodelooks like it changes(lenV & 0b111n)to(lenV & 7n). It doesn't —main's source at line 216 already reads0b111n, and TypeScript's emitter is what normalizes it to7n. The branch preserves the binary spelling, so the "low three bits" intent of the mask survives.effects/memory'sasBase/asNominaldeclarations gain a named_MemKeyHashwheremaininlined the raw hash literal"3f114fa6…". That is a readability improvement, not a change: the alias was non-exported onmainso the emitter had to inline it, and now that it is exported (correctly_-prefixed) it can be named.
Also noticed read's parameter was quietly fixed from ket to key.
log and error dropped the Console annotation
main:
export const log: Console = writeString('stdout')
export const error: Console = writeString('stderr')this branch:
/** Writes a line to `stdout`. Replaces the retired `Log` effect. */
export const log = writeString('stdout')With no annotation the type is inferred from writeString('stdout'), so the emitted declarations expand to (s: string) => Effect<Write, void> instead of naming Console. Console is (s: string) => Effect<Write, void>, so the types are identical and no caller can tell — nothing is weakened.
What makes it worth fixing is the second-order effect: Console is still exported from types.ts, unchanged, and after this change nothing in the emitted surface references it. An exported alias that no declaration mentions reads as dead, and the next person to touch this file has no signal that log/error are meant to be the same shape.
This is the same case as eof losing TerminalRange on #1485. Fix is /** @type {Console} */ above each, plus adding Console to the @import list in module.f.mjs — it isn't currently imported there. I swept the rest of the diff for this shape (an export whose main declaration was a bare alias that still exists as an exported type): these two are the only ones.
Minor — a stale line in the description
proof.f.tsstays TypeScript for now — it has a runtime dependency on../mock/module.f.ts, still unmigrated.
mock is migrated by the third commit of this same PR, so at this head every runtime import in effects/memory/proof.f.ts is already .f.mjs. Leaving the proof as .f.ts is fine either way — §2 says renaming an implementation never requires renaming its proof — so this is only the stated reason having gone stale mid-PR, not a problem with the outcome. (eff/proof.f.ts genuinely is still blocked, on ../proof.f.ts.)
No correctness objection; happy to approve once the two @type {Console} annotations are back.
…node Without the annotation, log/error's declaration emit expanded to the inline (s: string) => Effect<Write, void> shape instead of naming the still-exported Console alias, leaving it referenced by nothing.
proof.f.ts stays TypeScript for now: it has a runtime dependency on effects/node/virtual/module.f.ts, still unmigrated. Updates the emergent_testing dependent and stale line-number citations across three todo docs.
o2alexanderfedin
left a comment
There was a problem hiding this comment.
Approving. Re-reviewed at d6cf1b1. The Console annotation is back and fjs/text/sgr joins as a fifth module. CI green.
The fix works
log and error emit as Console again, matching main:
export declare const log: Console;
export declare const error: Console;They no longer appear in my declaration diff against main at all.
One note, and I checked before raising it so it stays a note rather than a request. The fix uses an inline cast — /** @type {Console} */ (writeString('stdout')) — where the declaration-annotation form would also work:
/**
* Writes a line to `stdout`. Replaces the retired `Log` effect.
* @type {Console}
*/
export const log = writeString('stdout')I assumed the cast would be the weaker of the two and tested it: substituting a deliberately wrong value (writeString instead of writeString('stdout')) produces exactly one error under both forms. So there is no safety difference here, and the emitted declaration is identical either way. The only argument for the annotation is consistency with the "annotated const over inline cast" change made on #1486 — genuinely minor, and not worth a commit on its own.
fjs/text/sgr verified
- Token-identical to
main's compiled output. csiandsgrchange only by theEnd→_End/Csi→_Csiprefix renames; both were non-exported onmain, so nothing public was renamed.csiWrite's signature differs only in the parameter name ({ std }destructuring becameoptions) — same type.proof.f.tscorrectly stays TypeScript: it importseffects/node/virtual/module.f.ts, still unmigrated.
Whole-PR state at this head
npx tscclean; 2356 pass / 0 fail.- Five modules; four token-identical, and
effects/nodediffers only by the0b111n→7nnormalization TypeScript's emitter applies —main's source already spells it0b111nand this branch preserves it. - Public API: 0 removed, 0 non-
_additions. The changed signatures are all prefix renames plus two parameter-name differences. - No
anyas a type in any emitted declaration. - Links 144/144 with none added, 0 label/target mismatches.
- CHANGELOG covers all five.
Still stale, from last round
The summary's line about fjs/effects/memory:
proof.f.tsstays TypeScript for now — it has a runtime dependency on../mock/module.f.ts, still unmigrated.
mock migrated in this same PR, so every runtime import in that proof is .f.mjs now. Leaving the proof as .f.ts is fine — §2 never requires moving it — so this is only the stated reason, not the outcome. Worth a touch-up before merge since the summary is what describes this in history.
Summary
Five modules migrate from authored TypeScript to JSDoc-typed
.f.mjs, pertodo/migrate-typescript-to-mjs.md, each splitting its public type-level API into a siblingtypes.ts:fjs/effects/memory/module.f.ts—Key,MemCreate,MemRead,MemWrite,MemOp, and the privateMemKeyHash→_MemKeyHash. Updates 13 dependents (cas,mcp,protocol/mcp,effects/nodeand itsvirtual/memorysubmodules), and fixes two markdown link hrefs (cas/evo/module.f.ts's docstring,cas/evo/README.md) that pointed at the old.f.tspath.fjs/effects/mock/module.f.ts—MemOperationMap,RunInstance. Updates 4 dependents.fjs/effects/eff/module.f.ts— theEff<O,T,P>type. Updates 4 dependents plus 3 doc references.fjs/effects/node/module.f.ts(566 lines) — the Node.js effect operations module: filesystem, networking, exec, console, sandbox, test registration, andNodeOp/NodeProgram. Updates 26 dependents across the repo, plus a batch of stalemodule.f.tsdoc references (including several with stale line-number citations that now split acrossmodule.f.mjsandtypes.ts).fjs/text/sgr/module.f.ts—Stdout,WriteText,CsiConsole. Updates theemergent_testingdependent and stale line-number citations across three todo docs.All five
proof.f.tsfiles stay TypeScript for now (their remaining runtime dependencies:effects/proof.f.ts,effects/node/virtual/module.f.ts— both still unmigrated).Test plan
npx tsc --noEmitcleannode ./fjs/module.ts t— 2356/2356 pass