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
3 changes: 3 additions & 0 deletions changelog/unreleased/1771.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- `package`: generated `private.d.ts` files are no longer published.
`package.json`'s `files` excludes them, dropping 16 files. They were
implementation-private by contract, so nothing public depended on them.
8 changes: 4 additions & 4 deletions fjs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@ reaches no reader, the tag buys nothing; `proof.*` is the clear case.
Which reader differs by file kind, and the tag does not decide it.
`module.f.mjs` and `types.ts` are public API surface. `private.ts` is not: it
holds implementation-private types outside the public declaration closure, and
[`todo/separate-private-types.md`](./todo/separate-private-types.md) plans to
drop its generated declarations from the package altogether. Its prose is for
contributors reading the sources, so the tag belongs there — but a public
documentation build must not be pointed at it.
its generated declarations are excluded from the package entirely
([`fsc/README.md`](./fsc/README.md)). Its prose is for contributors reading the
sources, so the tag belongs there — but a public documentation build must not be
pointed at it.

Put it in the leading block, followed by one blank line before the first
source-level import or declaration.
Expand Down
10 changes: 6 additions & 4 deletions fjs/ci/todo/ci-integration-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ Open questions:
`fjs/ci/common/types.ts`, and cover the new field in the proof. Without
it the two stages race and the consumer fails at `download-artifact`:
red for the wrong reason, which is the one failure mode that trains
people to re-run a check instead of reading it. This blocks the stage
split below and the packed-declaration check in
[`../../todo/separate-private-types.md`](../../todo/separate-private-types.md)
alike, so it is owned here rather than by either consumer.
people to re-run a check instead of reading it. This blocked the stage
split below and the packed-declaration check alike, so it is owned here
rather than by either consumer. The `needs` field landed in
[#1762](https://github.com/functionalscript/functionalscript/pull/1762)
and its first consumer in
[#1767](https://github.com/functionalscript/functionalscript/pull/1767).
- [ ] Implement scenario job generation: download artifact, install, run `main`.
- [ ] Port existing demo/smoke-test steps (`fjs t`, `deno run … t`, `bunx … t`) to the scenario model.
- [ ] Document the scenario authoring convention.
39 changes: 25 additions & 14 deletions fjs/ci/todo/f-mjs-package-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,11 @@ exposes private types as `_`-prefixed names in `types.d.ts` and as generated
`private.d.ts` files. Both are package-private by contract, not public API:
clean-consumer tests must exercise documented public types and must not turn
`_`-prefixed declaration artifacts into supported API merely because TypeScript
emitted them. Unshipping generated `private.d.ts` is the second stage of
[`fjs/todo/separate-private-types.md`](../../todo/separate-private-types.md),
by a `!**/private.d.ts` negation in `package.json`'s `files` — an exclusion at
pack time, with `prepack` unchanged and the working tree left alone. An earlier
draft of that design deleted the files instead; do not reintroduce a deletion
step. Once it lands, `private.d.ts` is no longer among the package-private
emitted them. Generated `private.d.ts` is no longer shipped: `package.json`'s
`files` carries a `!**/private.d.ts` negation — an exclusion at pack time, with
`prepack` unchanged and the working tree left alone. An earlier design draft
deleted the files instead; do not reintroduce a deletion step. So `private.d.ts`
is no longer among the package-private
artifacts above — what remains is the `_`-prefixed names that still ship by
design: `_` types emitted into `types.d.ts` and exported `_` constants emitted
into `module.d.mts`. The leak-tolerance contract narrows to those, and stays
Expand Down Expand Up @@ -247,8 +246,18 @@ emission, `npm pack`, and a clean consumer.
in particular **not** this fixture. A hand-written import list would
name the fixture, so a violation placed here fails under a fixed list
too and proves nothing about enumeration. Measured end to end with
`fjs/emergent_testing` in
[`../../todo/separate-private-types.md`](../../todo/separate-private-types.md).
`fjs/emergent_testing`, which had no `private.ts`: given one, plus an
exported binding whose signature names it, the job exits 2 with
`TS2307` on the packed declaration.

The same violation with the `files` negation dropped is **green**,
because the private declaration then ships and the reference
resolves. So this job does not detect a dropped negation, and nothing
else does either: an assertion over the packed listing was written for
that and removed as not worth its complexity — what it caught was
declaration noise in the tarball, which the `_` contract already
tolerates, not a broken package. The negation is one line in
`package.json` and losing it is a visible diff in review.
Scope: the fixture exercises the
supported, fully erased `import type` form only. The forbidden inline `import { type X }` /
`import * as` / side-effect forms are a documented one-time measurement
Expand Down Expand Up @@ -330,9 +339,11 @@ emission, `npm pack`, and a clean consumer.
typescript` lets the registry change the verdict with no repository
change. The version is readable without a checkout: `npm pack` keeps
`devDependencies` in the packed `package.json`.
The private-declaration assertion this job carries for
[`../../todo/separate-private-types.md`](../../todo/separate-private-types.md)
is a condition on it, specified there; the job itself belongs here.
The private-declaration assertion this job carries is a condition on it:
every packed declaration is type-checked from the installed artifact, so a
public declaration that came to depend on an unshipped private module is a
red build. Landed in
[#1767](https://github.com/functionalscript/functionalscript/pull/1767).
- [x] Update `AGENTS.md` to the asymmetric `.f.ts` / `.f.mjs` migration policy.
- [x] Decide, based on the fixture, whether the second TypeScript runtime-emission
pass can ever be removed while authored `types.ts` files remain, or whether
Expand Down Expand Up @@ -398,9 +409,9 @@ not, and the pipeline is simplified accordingly.
two-pass `prepack`.
- [`todo/migrate-typescript-to-mjs.md`](../../../todo/migrate-typescript-to-mjs.md)
— repository-wide stage-1 implementation source migration.
- [`fjs/todo/separate-private-types.md`](../../todo/separate-private-types.md)
— private-type placement rules and the packaging stage that unships
generated private declarations.
- [`fjs/AGENTS.md`](../../AGENTS.md) §3.2 — private-type placement rules;
[`fjs/fsc/README.md`](../../fsc/README.md) — the `_` contract and why
generated private declarations are not packaged.
- [microsoft/TypeScript#46407](https://github.com/microsoft/TypeScript/issues/46407)
— upstream JSDoc typedef stripping limitation; no longer a blocker here, since
no authored `.mjs` declares a file-scope typedef to strip.
Expand Down
27 changes: 18 additions & 9 deletions fjs/fsc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,8 @@ types — function-local in a proof.
Private types and private runtime constants keep a leading `_`, even when
linkage requires an export. The underscore is an API contract, not
declaration-level visibility: generated `.d.ts` / `.d.mts` may still contain
`export type _Type = number` (and, until the packaging stage of
[`../todo/separate-private-types.md`](../todo/separate-private-types.md) lands,
a generated `private.d.ts` still ships), but names that begin with `_` are
private FunctionalScript implementation details. Consumers must not rely on
`export type _Type = number`, but names that begin with `_` are private
FunctionalScript implementation details. Consumers must not rely on
those names directly, so renaming or removing a `_`-prefixed name is not a
breaking change solely because TypeScript emitted it. The public contract still
governs transitive effects: if a public type depends on `_Type`, changing
Expand Down Expand Up @@ -199,11 +197,22 @@ module's public vocabulary may be published under an ordinary name even though
its TypeScript alias was module-private, and a former export may become `_`
when it only ever described an implementation detail.

Removing shipped private declaration artifacts (`private.d.ts`) from the
package is the second stage of
[`../todo/separate-private-types.md`](../todo/separate-private-types.md); the
`_` contract itself is permanent, since `_` helpers in `types.ts` and exported
`_` constants keep shipping in emitted declarations regardless.
No generated `private.d.ts` ships: `package.json`'s `files` excludes them with
a `!**/private.d.ts` negation.

What CI checks is the consequence, not the exclusion. Every declaration the
package does carry is type-checked as an outside consumer installs it, so a
public declaration that came to depend on a private module is a red build
rather than a broken package. Losing the negation itself is *not* caught: the
private declarations come back, every reference to them resolves, and that job
stays green. It is one line, and losing it is a visible diff in review — see
[`../ci/todo/f-mjs-package-support.md`](../ci/todo/f-mjs-package-support.md)
for why an assertion over the packed listing was written for that and then
removed.

The `_` contract is permanent and independent of that. `_` helpers retained in
`types.ts` by the public declaration closure, and exported `_` constants, keep
shipping in emitted declarations; they are still not API.

When the last authored implementation/proof `.ts` / `.f.ts` file is gone,
authored `types.ts` files may remain. The TypeScript runtime-emission pass is
Expand Down
Loading
Loading