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
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,48 @@ history.

## Unreleased

- **BREAKING CHANGES:** `fjs/djs/examples/input.f.ts` and
`fjs/djs/examples/m.f.ts` migrate to `.f.mjs`. Being `.mjs` they now
ship in the tarball, which `.f.ts` did not
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/types/nominal/proof.f.ts` migrates to
`.f.mjs`. Its two `unique symbol` brands move to `types.ts`, exported
as `_SymbolKeyBranded` and `_SymbolIntersectionBranded`
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/types/ts/proof.f.ts` migrates to `.f.mjs`.
Its `declare const` index-signature block moves to `types.ts`,
non-exported, so no public type changes
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/emergent_testing/example.f.ts` migrates
from authored TypeScript to JSDoc-typed JavaScript (`.f.mjs`) — the
four typed consts become JSDoc `@type` annotations
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/types/rtti/validate/proof.f.ts` migrates to
`.f.mjs` — 23 compile-time round-trips stay function-local as JSDoc
`@typedef`s and are not emitted
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/types/rtti/parse/proof.f.ts` migrates to
`.f.mjs` — compile-time round-trips stay function-local as JSDoc
`@typedef`s and are not emitted
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/emergent_testing/proof.f.ts` migrates to
`.f.mjs`. Six module-local types become `_`-prefixed `@typedef`s, as a
JSDoc `@typedef` has no non-exported form
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/effects/node/proof.f.ts` migrates to
`.f.mjs` — explicit generic instantiations become checked `@type`
declarations, as JSDoc has no call-site form
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/js/tokenizer/proof.f.ts` migrates to
`.f.mjs` — typed consts become JSDoc `@type` annotations
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/media/json/parser/proof.f.ts` migrates to
`.f.mjs` — the typed const becomes a JSDoc `@type` annotation
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `todo/proof.f.ts` migrates to `.f.mjs`. The
`TemplateType` alias is renamed `_TemplateType`: it was never exported,
but a JSDoc `@typedef` has no non-exported form
[#1505](https://github.com/functionalscript/functionalscript/pull/1505)
- **BREAKING CHANGES:** `fjs/media/json/tokenizer/proof.f.ts` migrates
from authored TypeScript to JSDoc-typed JavaScript (`.f.mjs`) — the
typed `tokenizeString` const becomes a JSDoc `@type` annotation, and
Expand Down
2 changes: 1 addition & 1 deletion fjs/djs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ holding the same reference must yield the same object, not two equal copies —
so the AST keeps the constants addressable and refers to them by index
instead of inlining them. That is also what makes serialization a real
choice: a value referenced more than once is emitted as a `const` and reused.
See [examples/input.f.ts](./examples/input.f.ts).
See [examples/input.f.mjs](./examples/input.f.mjs).

## Next steps

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import c from "./m.f.ts"
import c from "./m.f.mjs"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update the README link for the renamed example

Renaming this fixture to input.f.mjs leaves fjs/djs/README.md:22 linking to the deleted examples/input.f.ts, so readers following the documentation now receive a broken link. Update that link as part of the rename.

Useful? React with 👍 / 👎.

const a = 1
const b = 2
export default [a, a, b, c, c]
File renamed without changes.
2 changes: 1 addition & 1 deletion fjs/djs/todo/66e-parser-container-stack-bookkeeping.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ one and can land independently of 157.
- [ ] Apply the same shape to `fjs/djs/parser/module.f.mjs`, preserving the
`{ ...state }` spread and the `endArray` non-array fallback inside the
`build` callback.
- [ ] Run `npx tsc` and `fjs t`; confirm `fjs/media/json/parser/proof.f.ts` and
- [ ] Run `npx tsc` and `fjs t`; confirm `fjs/media/json/parser/proof.f.mjs` and
`fjs/djs/parser/proof.f.mjs` still pass with full line/branch coverage
(behaviour is unchanged — this is a pure refactor).

Expand Down
40 changes: 26 additions & 14 deletions fjs/effects/node/proof.f.ts → fjs/effects/node/proof.f.mjs
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import type { Vec } from "../../types/bit_vec/types.ts"
/**
* @import { Vec } from "../../types/bit_vec/types.ts"
* @import { IoResult, ReadFile } from "./types.ts"
* @import { Dir } from "./virtual/types.ts"
* @import { List } from "../list/types.ts"
* @import { OperationMap } from "../types.ts"
*/

import { empty, isVec, uint, vec, vec8 } from "../../types/bit_vec/module.f.mjs"
import { utf8, utf8ToString } from "../../text/module.f.mjs"
import { match, pure, step } from "../module.f.mjs"
import { both, fetch, mkdir, now, readdir, readFile, readUtf8File, rm, sandbox, writeFile, writeUtf8File, rename, readBytes, randomInt, writeFromStream, usesInlineTestContext, versionLessThan } from "./module.f.mjs"
import type { IoResult, ReadFile } from "./types.ts"
import { create as memCreate, read as memRead, write as memWrite } from "../memory/module.f.mjs"
import { empty as listEmpty, nonEmpty as listNonEmpty } from "../list/module.f.mjs"
import { emptyState, virtual } from "./virtual/module.f.mjs"
import type { Dir } from "./virtual/types.ts"
import { assert, assertEq, assertNotNullish } from '../../asserts/module.f.mjs'
import { ok } from '../../types/result/module.f.mjs'

// Answers the one command the `map` proof below drives. Routing the loop
// through `match` keeps the `Pure`/`Do` layout out of this module: the map key
// is the command assertion, and `MatchResult` types the continuation.
const readHello = match<ReadFile, IoResult<Vec>>({
/** @type {OperationMap<ReadFile, IoResult<Vec>>} */
const readHelloMap = {
readFile: path => {
assertEq(path, 'hello')
return ok(vec8(0x15n))
},
})
}

const readHello = match(readHelloMap)

export const proof = {
externalTestContext: () => {
Expand Down Expand Up @@ -74,7 +82,7 @@ export const proof = {
assert(t === 'ok', result)
const tmp = state.root.tmp
assert(!(typeof tmp !== 'object' || Array.isArray(tmp)), state.root)
const cache = (tmp as Dir).cache
const cache = (/** @type {Dir} */ (tmp)).cache
assert(!(typeof cache !== 'object' || Array.isArray(cache)), tmp)
},
nonRec: () => {
Expand Down Expand Up @@ -114,7 +122,7 @@ export const proof = {
nestedPath: () => {
const [_, [t, result]] = virtual(emptyState)(readFile('tmp/cache'))
assert(t === 'error', result)
if ((result as { code?: unknown }).code !== 'ENOENT') { throw result }
if ((/** @type {{ code?: unknown }} */ (result)).code !== 'ENOENT') { throw result }
},
withinLimit: () => {
// Test with a small file well within the 131,072 byte limit
Expand Down Expand Up @@ -265,7 +273,7 @@ export const proof = {
assert(t === 'ok', result)
const tmp = state.root.tmp
assert(!(typeof tmp !== 'object' || Array.isArray(tmp)), state.root)
assertEq((tmp as Dir).cache, undefined, tmp)
assertEq((/** @type {Dir} */ (tmp)).cache, undefined, tmp)
},
noSuchFile: () => {
const [_, [t, result]] = virtual(emptyState)(rm('hello'))
Expand Down Expand Up @@ -305,15 +313,15 @@ export const proof = {
// (and `duration`) instead of the runner measuring.
ok: () => {
const [_, { result, duration }] = virtual(emptyState)(
sandbox(() => ({ result: ['ok', 42], duration: 0 }) as never))
sandbox(() => /** @type {never} */ ({ result: ['ok', 42], duration: 0 })))
assert(result[0] === 'ok', result)
assertEq(result[1], 42)
assertEq(duration, 0)
},
error: () => {
const err = new Error('fail')
const [_, { result }] = virtual(emptyState)(
sandbox(() => ({ result: ['error', err], duration: 0 }) as never))
sandbox(() => /** @type {never} */ ({ result: ['error', err], duration: 0 })))
assert(result[0] === 'error', result)
assertEq(result[1], err)
},
Expand Down Expand Up @@ -343,7 +351,7 @@ export const proof = {
assert(t === 'ok', result)
assertEq(state.root.src, undefined, state.root)
assert(Array.isArray(state.root.dst), state.root)
assertEq(uint((state.root.dst as readonly Vec[])[0]), 0x2An, state.root)
assertEq(uint((/** @type {readonly Vec[]} */ (state.root.dst))[0]), 0x2An, state.root)
},
nestedRename: () => {
const [state, [t, result]] = virtual({
Expand All @@ -353,7 +361,7 @@ export const proof = {
assert(t === 'ok', result)
const tmp = state.root.tmp
assert(!(typeof tmp !== 'object' || Array.isArray(tmp)), state.root)
assertEq((tmp as Dir).src, undefined, tmp)
assertEq((/** @type {Dir} */ (tmp)).src, undefined, tmp)
},
dirOverFile: () => {
const [state, [t, result]] = virtual({
Expand Down Expand Up @@ -411,19 +419,23 @@ export const proof = {
createExclusiveFails: () => {
// The destination already exists, so `createExclusive` fails (EEXIST) and
// the error propagates without ever touching `writeBytes`.
/** @type {List<never, IoResult<Vec>>} */
const chunks = listEmpty()
const [state, [t, result]] = virtual({
...emptyState,
root: { hello: [vec8(0x2An)] },
})(writeFromStream('hello', listEmpty<never, IoResult<Vec>>()))
})(writeFromStream('hello', chunks))
assert(t === 'error', result)
const file = state.root.hello
assert(!(!Array.isArray(file) || uint(file[0]) !== 0x2An), file)
},
invalidBufferSize: () => {
// A chunk whose bit length isn't a multiple of 8 trips the
// byte-alignment guard before `writeBytes` is ever called.
/** @type {List<never, IoResult<Vec>>} */
const chunks = listNonEmpty(['ok', vec(4n)(0b1010n)], listEmpty())
const [_, [t, result]] = virtual(emptyState)(
writeFromStream('hello', listNonEmpty<never, IoResult<Vec>>(['ok', vec(4n)(0b1010n)], listEmpty()))
writeFromStream('hello', chunks)
)
assert(t === 'error', result)
assertEq(result, 'invalid buffer size')
Expand Down
2 changes: 1 addition & 1 deletion fjs/effects/node/todo/ornotfound-combinator.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ list: () => access(storePrefix).step(orNotFound<readonly Vec[]>([])(() =>
- [ ] Add `orNotFound` beside `isNotFound` in `fjs/effects/node/module.f.mjs`
(once a second consumer exists).
- [ ] Rewrite `list` in `fjs/cas/module.f.ts` on top of it.
- [ ] Cover all three branches (`ok`, `ENOENT`, non-`ENOENT` throw) in `fjs/effects/node/proof.f.ts`.
- [ ] Cover all three branches (`ok`, `ENOENT`, non-`ENOENT` throw) in `fjs/effects/node/proof.f.mjs`.

### Related

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
export const add = (a: number, b: number): number => a + b
/** @type {(a: number, b: number) => number} */
export const add = (a, b) => a + b

export const mul = (a: number, b: number): number => a * b
/** @type {(a: number, b: number) => number} */
export const mul = (a, b) => a * b

export const sqr = (a: number) => mul(a, a)
/** @type {(a: number) => number} */
export const sqr = a => mul(a, a)

export const todo = () => { throw "not implemented" }

const checkMul = (a: number, b: number, r: number) => {
/** @type {(a: number, b: number, r: number) => void} */
const checkMul = (a, b, r) => {
if (mul(a, b) !== r) { throw `mul(${a}, ${b}) !== ${r}` }
}

Expand Down
Loading
Loading