Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,37 @@ history.

## Unreleased

- **BREAKING CHANGES:** `fjs/text/sgr` migrates from authored
TypeScript (`.f.ts`) to JSDoc-typed JavaScript (`.f.mjs`), splitting
its type-level API into a sibling `types.ts` — importers must use
the `.f.mjs` specifier for runtime values and the `types.ts`
specifier for types. `proof.f.ts` stays TypeScript for now
[#1488](https://github.com/functionalscript/functionalscript/pull/1488)
- **BREAKING CHANGES:** `fjs/effects/node/module.f.ts` migrates from
authored TypeScript (`.f.ts`) to JSDoc-typed JavaScript (`.f.mjs`),
splitting its type-level API into a sibling `types.ts` — importers
must use the `.f.mjs` specifier for runtime values and the
`types.ts` specifier for types. Updates 26 dependents across the
repo; `proof.f.ts` stays TypeScript for now
[#1488](https://github.com/functionalscript/functionalscript/pull/1488)
- **BREAKING CHANGES:** `fjs/effects/eff` migrates from authored
TypeScript (`.f.ts`) to JSDoc-typed JavaScript (`.f.mjs`), splitting
the `Eff` type into a sibling `types.ts` — importers must use the
`.f.mjs` specifier for runtime values and the `types.ts` specifier
for types. `proof.f.ts` stays TypeScript for now
[#1488](https://github.com/functionalscript/functionalscript/pull/1488)
- **BREAKING CHANGES:** `fjs/effects/mock` migrates from authored
TypeScript (`.f.ts`) to JSDoc-typed JavaScript (`.f.mjs`), splitting
its type-level API into a sibling `types.ts` — importers must use
the `.f.mjs` specifier for runtime values and the `types.ts`
specifier for types
[#1488](https://github.com/functionalscript/functionalscript/pull/1488)
- **BREAKING CHANGES:** `fjs/effects/memory` migrates from authored
TypeScript (`.f.ts`) to JSDoc-typed JavaScript (`.f.mjs`), splitting
its type-level API into a sibling `types.ts` — importers must use
the `.f.mjs` specifier for runtime values and the `types.ts`
specifier for types. `proof.f.ts` stays TypeScript for now
[#1488](https://github.com/functionalscript/functionalscript/pull/1488)
- **BREAKING CHANGES:** `fjs/effects/list` migrates from authored
TypeScript (`.f.ts`) to JSDoc-typed JavaScript (`.f.mjs`), splitting
its type-level API into a sibling `types.ts` — importers must use
Expand Down
2 changes: 1 addition & 1 deletion fjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ A module intended to be run with `fjs run` must export a named `main` constant
of type `NodeProgram`:

```ts
import type { NodeProgram } from '../effects/node/module.f.ts'
import type { NodeProgram } from '../effects/node/types.ts'

export const main: NodeProgram = options => {
// options.args — command-line arguments passed after the module path
Expand Down
13 changes: 3 additions & 10 deletions fjs/cas/cli/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,10 @@
import { sha256 } from '../../crypto/sha2/module.f.mjs'
import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.mjs'
import { forEachStep, pure, step } from '../../effects/module.f.mjs'
import {
errorExit,
log,
writeFromStream,
type All,
type Read,
type Write,
type WriteFile
} from '../../effects/node/module.f.ts'
import { errorExit, log, writeFromStream } from '../../effects/node/module.f.mjs'
import type { All, Read, Write, WriteFile } from '../../effects/node/types.ts'
import { dispatch, type Commands } from '../../cli/module.f.ts'
import { type MemOp } from '../../effects/memory/module.f.ts'
import type { MemOp } from '../../effects/memory/types.ts'
import { casAddFile, fileCas, type FileCasOperation } from '../module.f.ts'

export const commands: Commands<FileCasOperation | WriteFile | Write | All | MemOp | Read> = [
Expand Down
2 changes: 1 addition & 1 deletion fjs/cas/cli/proof.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { commands } from './module.f.ts'
import { computeSync, sha256 } from '../../crypto/sha2/module.f.mjs'
import { maxLength, vec, vec8 } from '../../types/bit_vec/module.f.mjs'
import { defaultNodeProgramOptions, emptyState, virtual } from '../../effects/node/virtual/module.f.ts'
import { type NodeProgramOptions } from '../../effects/node/module.f.ts'
import type { NodeProgramOptions } from '../../effects/node/types.ts'
import { dispatch } from '../../cli/module.f.ts'
import { vecToCBase32 } from '../../basen/cbase32/module.f.mjs'
import { assert, assertEq } from '../../asserts/module.f.mjs'
Expand Down
2 changes: 1 addition & 1 deletion fjs/cas/evo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ bytes under a hash. Resolving "what are the current heads of subject X"
means walking every stored revision and reversing the `parents` links — too
expensive to redo per request. `fjs/cas/evo/module.f.ts` scans the whole store
once into an in-memory `Cache` (keyed with
[`fjs/effects/memory`](../../effects/memory/module.f.ts)) mapping subject →
[`fjs/effects/memory`](../../effects/memory/module.f.mjs)) mapping subject →
head hashes, then keeps that cache current as new revisions are `add`ed
through it.

Expand Down
10 changes: 6 additions & 4 deletions fjs/cas/evo/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* are the current heads of subject X" from that store means walking every
* stored revision and reversing the parent links — too expensive to redo on
* every query. This module scans the store once into an in-memory `Cache`
* ([`fjs/effects/memory`](../../effects/memory/module.f.ts)) mapping subject →
* ([`fjs/effects/memory`](../../effects/memory/module.f.mjs)) mapping subject →
* head hashes, then keeps the cache current as new revisions are `add`ed
* through it — or, for a revision stored some other way (e.g. the generic
* `cas_add` MCP tool), via {@link syncRevision}.
Expand Down Expand Up @@ -44,8 +44,9 @@
*/
import { pure, foldStep } from '../../effects/module.f.mjs'
import type { Effect, Operation } from '../../effects/types.ts'
import { eff } from '../../effects/eff/module.f.ts'
import { create, read, write, type Key, type MemOp } from '../../effects/memory/module.f.ts'
import { eff } from '../../effects/eff/module.f.mjs'
import { create, read, write } from '../../effects/memory/module.f.mjs'
import type { Key, MemOp } from '../../effects/memory/types.ts'
import { collectRead, type Cas } from '../module.f.ts'
import { cBase32ToVec, vecToCBase32 } from '../../basen/cbase32/module.f.mjs'
import { fromVec } from '../../text/utf8/module.f.mjs'
Expand All @@ -58,7 +59,8 @@ import { at, definedEntries } from '../../types/object/module.f.mjs'
import type { StringMap } from '../../types/object/types.ts'
import { unwrap } from '../../types/nullable/module.f.mjs'
import type { Vec } from '../../types/bit_vec/types.ts'
import { isNotFound, type IoResult } from '../../effects/node/module.f.ts'
import { isNotFound } from '../../effects/node/module.f.mjs'
import type { IoResult } from '../../effects/node/types.ts'

/** A cBase32 content hash, as accepted/returned by `Cas<O>`. */
export type Hash = string
Expand Down
2 changes: 1 addition & 1 deletion fjs/cas/evo/proof.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { unwrap } from '../../types/nullable/module.f.mjs'
import type { Ok } from '../../types/result/types.ts'
import { ok, error } from '../../types/result/module.f.mjs'
import { nonEmpty, empty as elEmpty } from '../../effects/list/module.f.mjs'
import type { IoResult } from '../../effects/node/module.f.ts'
import type { IoResult } from '../../effects/node/types.ts'
import { tryUtf8 } from '../../text/module.f.mjs'
import { dialect as revisionDialect } from '../../media/revision/module.f.ts'
import {
Expand Down
30 changes: 16 additions & 14 deletions fjs/cas/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { empty, length, maxLength, maxLengthBytes, msb, vec } from '../types/bit
import { cBase32ToVec, vecToCBase32 } from '../basen/cbase32/module.f.mjs'
import { foldStep, forEachStep, history, historyStep, okStep, pure, step } from '../effects/module.f.mjs'
import type { Effect, Operation } from '../effects/types.ts'
import { eff } from '../effects/eff/module.f.ts'
import { eff } from '../effects/eff/module.f.mjs'
import {
access,
createExclusive,
Expand All @@ -25,19 +25,21 @@ import {
rm,
stat,
writeBytes,
type Access,
type CreateExclusive,
type IoResult,
type Mkdir,
type Now,
type RandomInt,
type ReadBytes,
type Readdir,
type Rename,
type Rm,
type Stat,
type WriteBytes,
} from '../effects/node/module.f.ts'
} from '../effects/node/module.f.mjs'
import type {
Access,
CreateExclusive,
IoResult,
Mkdir,
Now,
RandomInt,
ReadBytes,
Readdir,
Rename,
Rm,
Stat,
WriteBytes,
} from '../effects/node/types.ts'
import { toOption } from '../types/nullable/module.f.mjs'
import { error, ok, unwrap } from '../types/result/module.f.mjs'
import { splitAt } from '../types/string/module.f.mjs'
Expand Down
2 changes: 1 addition & 1 deletion fjs/cas/plan/staging-lease.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ used — it fails to sort across DST transitions and offset changes.

The design needs **no `FileHandle` type** and no held OS resource — the uploader's state is
plain data (`path`, `offset`, `hash`). Every effect is stateless and path-based, like the
existing `readBytes` / `writeFile` / `rename` effects in `fjs/effects/node/module.f.ts`.
existing `readBytes` / `writeFile` / `rename` effects in `fjs/effects/node/module.f.mjs`.

| Effect | Signature | Notes |
|---|---|---|
Expand Down
3 changes: 2 additions & 1 deletion fjs/cas/proof.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { computeSync, sha256 } from '../crypto/sha2/module.f.mjs'
import { fileCas, casAddFile, collectRead, type FileCasOperation, casUpload } from './module.f.ts'
import { match, pure, runPure, step } from '../effects/module.f.mjs'
import type { Effect } from '../effects/types.ts'
import { mkdir, writeFile, rm, readFile, type ReadFile, type WriteFile, type Rm, type Mkdir, type IoResult, access } from '../effects/node/module.f.ts'
import { mkdir, writeFile, rm, readFile, access } from '../effects/node/module.f.mjs'
import type { ReadFile, WriteFile, Rm, Mkdir, IoResult } from '../effects/node/types.ts'
import type { Ok } from '../types/result/types.ts'
import { error, ok } from '../types/result/module.f.mjs'
import { emptyState, virtual } from '../effects/node/virtual/module.f.ts'
Expand Down
4 changes: 2 additions & 2 deletions fjs/cas/todo/web-api-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ set over HTTP(S):
natural fit for this transport where MCP is capped at 128 KiB of inline
content. But the current HTTP effects do not stream:
`IncomingMessage.body` / `ServerResponse.body`
(`fjs/effects/node/module.f.ts`) are single `Vec`s, and the Node runner
(`fjs/effects/node/module.f.mjs`) are single `Vec`s, and the Node runner
buffers the whole request (`collect(req)` → `listToVec`) — past the
128 KiB `Vec` limit it throws. The CAS store side already streams
(`Cas.read`/`Cas.write` deal in chunk lists), so lifting the cap needs
Expand Down Expand Up @@ -96,6 +96,6 @@ HTML form is an exposure-matrix decision for
navigable DAG.
- `fjs/effects/node/todo/requestlistener-stateful.md` — HTTP listener
effects groundwork.
- `fjs/effects/node/module.f.ts` (`IncomingMessage`/`ServerResponse`) — the
- `fjs/effects/node/module.f.mjs` (`IncomingMessage`/`ServerResponse`) — the
whole-body `Vec` HTTP effects that need a streaming redesign before this
transport can carry blobs past 128 KiB.
3 changes: 2 additions & 1 deletion fjs/ci/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
*/
import { mapStep, step } from '../effects/module.f.mjs'
import type { Effect } from '../effects/types.ts'
import { access, writeUtf8File, type NodeOp } from '../effects/node/module.f.ts'
import { access, writeUtf8File } from '../effects/node/module.f.mjs'
import type { NodeOp } from '../effects/node/types.ts'
import { functionalscript, images } from './config/module.f.mjs'
import {
architecture,
Expand Down
3 changes: 2 additions & 1 deletion fjs/ci/nix/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
*/
import { forEachStep, mapStep, pure, step } from '../../effects/module.f.mjs'
import type { Effect } from '../../effects/types.ts'
import { mkdir, writeUtf8File, type Mkdir, type WriteFile } from '../../effects/node/module.f.ts'
import { mkdir, writeUtf8File } from '../../effects/node/module.f.mjs'
import type { Mkdir, WriteFile } from '../../effects/node/types.ts'
import { nixToString } from '../../media/nix/module.f.mjs'
import type { Expression } from '../../media/nix/types.ts'
import { fromUndefined, unwrap as unwrapNullable } from '../../types/nullable/module.f.mjs'
Expand Down
2 changes: 1 addition & 1 deletion fjs/ci/nix/proof.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
import { assert, assertEq } from '../../asserts/module.f.mjs'
import { step } from '../../effects/module.f.mjs'
import { readUtf8File } from '../../effects/node/module.f.ts'
import { readUtf8File } from '../../effects/node/module.f.mjs'
import { emptyState, virtual } from '../../effects/node/virtual/module.f.ts'
import { nixpkgs } from '../config/module.f.mjs'
import { nodeNixJobs } from '../node/module.f.ts'
Expand Down
3 changes: 2 additions & 1 deletion fjs/cli/module.f.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { errorExit, log, type NodeOp, type NodeProgramOptions, type Write } from '../effects/node/module.f.ts'
import { errorExit, log } from '../effects/node/module.f.mjs'
import type { NodeOp, NodeProgramOptions, Write } from '../effects/node/types.ts'
import { pure, step } from '../effects/module.f.mjs'
import type { Effect } from '../effects/types.ts'
import { at, fromEntries } from '../types/object/module.f.mjs'
Expand Down
2 changes: 1 addition & 1 deletion fjs/cli/proof.f.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { pure } from '../effects/module.f.mjs'
import type { NodeOp, NodeProgramOptions } from '../effects/node/module.f.ts'
import type { NodeOp, NodeProgramOptions } from '../effects/node/types.ts'
import { defaultNodeProgramOptions, emptyState, virtual } from '../effects/node/virtual/module.f.ts'
import { dispatch, type Commands } from './module.f.ts'
import { assert, assertEq } from '../asserts/module.f.mjs'
Expand Down
12 changes: 2 additions & 10 deletions fjs/dev/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,8 @@
*
* @module
*/
import {
all,
import_,
readdir,
type Access,
type All,
type Env,
type Import,
type Readdir
} from '../effects/node/module.f.ts'
import { all, import_, readdir } from '../effects/node/module.f.mjs'
import type { Access, All, Env, Import, Readdir } from '../effects/node/types.ts'
import { cmp as strCmp } from '../types/string/module.f.mjs'
import type { StringMap } from '../types/object/types.ts'
import { unwrap } from '../types/result/module.f.mjs'
Expand Down
3 changes: 2 additions & 1 deletion fjs/dev/update/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
*/
import { history, historyStep, mapStep, step } from '../../effects/module.f.mjs'
import type { Effect } from '../../effects/types.ts'
import { mkdir, type Mkdir, type NodeProgram, readUtf8File, type ReadFile, type WriteFile, writeUtf8File } from '../../effects/node/module.f.ts'
import { mkdir, readUtf8File, writeUtf8File } from '../../effects/node/module.f.mjs'
import type { Mkdir, NodeProgram, ReadFile, WriteFile } from '../../effects/node/types.ts'
import { unwrap } from '../../types/result/module.f.mjs'

const source = '.copilot/mcp.json' as const
Expand Down
2 changes: 1 addition & 1 deletion fjs/dev/update/proof.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
import { assert, assertEq } from '../../asserts/module.f.mjs'
import { utf8 } from '../../text/module.f.mjs'
import { readUtf8File } from '../../effects/node/module.f.ts'
import { readUtf8File } from '../../effects/node/module.f.mjs'
import { defaultNodeProgramOptions, emptyState, virtual } from '../../effects/node/virtual/module.f.ts'
import { main, syncMcp } from './module.f.ts'
import { step } from '../../effects/module.f.mjs'
Expand Down
8 changes: 2 additions & 6 deletions fjs/djs/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@ import { stringify, stringifyAsTree } from './serializer/module.f.ts'
import { sort } from '../types/object/module.f.mjs'
import { pure, step } from '../effects/module.f.mjs'
import type { Effect } from '../effects/types.ts'
import {
writeUtf8File,
type WriteFile, type ReadFile,
type Write,
error,
} from '../effects/node/module.f.ts'
import { writeUtf8File, error } from '../effects/node/module.f.mjs'
import type { WriteFile, ReadFile, Write } from '../effects/node/types.ts'

export type Object = { readonly[k in string]?: Unknown }

Expand Down
3 changes: 2 additions & 1 deletion fjs/djs/transpiler/module.f.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { type ParseError, parseFromTokens } from '../parser/module.f.ts'
import { run, type AstModule } from '../ast/module.f.ts'
import { foldStep, pure, step } from '../../effects/module.f.mjs'
import type { Effect } from '../../effects/types.ts'
import { readUtf8File, type ReadFile } from '../../effects/node/module.f.ts'
import { readUtf8File } from '../../effects/node/module.f.mjs'
import type { ReadFile } from '../../effects/node/types.ts'

/**
* State threaded through the recursive transpilation of a DJS module graph.
Expand Down
68 changes: 68 additions & 0 deletions fjs/effects/eff/module.f.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* A fluent, method-chaining wrapper over the raw `Effect`.
*
* See `./types.ts` for the `Eff` type-level API.
*
* @module
*/
import { history, historyStep, mapStep, pure } from '../module.f.mjs'
/** @import { Effect, Operation } from '../types.ts' */
/** @import { Eff } from './types.ts' */

/**
* Builds an `Eff` from two views of the same chain: `value`, the effect for the
* current value alone, and `h`, a thunk for the `[current, ...history]` tuple.
*
* **The two must denote the same computation** — `value` has to be what `h()`
* produces with the history dropped. Nothing enforces it. The
* redundancy is deliberate: `value` used to be derived from `h` on demand,
* which made disagreement impossible but also forced the entry case to rebuild
* an effect it was already holding. Passing it in is what lets {@link eff} hand
* the original back.
*
* The asymmetry between the two — one built, one deferred — is the point.
* `value` is either already in hand ({@link eff} was given it) or already built
* (`.step` has just composed the chain it projects from), so storing it costs
* no more than the projection itself. `h` is different: nothing needs the
* history tuple until a later `.step` asks for it, and composing effects is
* eager, so holding a thunk is the only way to not compose it yet. That keeps
* `eff(e)` free of work entirely.
*
* `.step` calls `h()` once and closes over the effect, so everything built
* from that link shares it. `eff`'s thunk is not memoized, so calling `.step`
* twice on the same `eff(e)` rebuilds and re-forces `e` — harmless under
* `Pure`'s contract, which requires the thunk to be pure and to tolerate
* repeat calls.
*
* @template {Operation} O
* @template T
* @template {readonly unknown[]} P
* @param {Effect<O, T>} value
* @param {() => Effect<O, readonly[T, ...P]>} h
* @returns {Eff<O, T, P>}
*/
const create = (value, h) => {
// `self` is named so `.map` can be *defined* as the `.step` call it is
// documented to equal, rather than as a second copy of `.step`'s body that
// has to be re-checked against it.
/** @type {Eff<O, T, P>} */
const self = {
value,
step: f => {
const x1 = historyStep(h(), f)
return create(mapStep(x1, ([t]) => t), () => x1)
},
map: f => self.step((...tp) => pure(f(...tp)))
}
return self
}

/**
* Wraps a raw {@link Effect}; the bridge into the `Eff` world, with an empty
* history. The empty tuple is spelled out because {@link Eff} deliberately has
* no default for `P` — see its docs.
*
* @type {<O extends Operation, T>(value: Effect<O, T>) => Eff<O, T, readonly[]>}
*/
export const eff = value =>
create(value, () => history(value))
2 changes: 1 addition & 1 deletion fjs/effects/eff/proof.f.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { do_, match, pure } from '../module.f.mjs'
import { assert, assertEq } from '../../asserts/module.f.mjs'
import { assertPure } from '../proof.f.ts'
import { eff } from './module.f.ts'
import { eff } from './module.f.mjs'

type AddOp = readonly['add', (a: number, b: number) => number]

Expand Down
Loading
Loading