Commit Graph
583 Commits
Author SHA1 Message Date
Chris Tate 3b374f3201 Check with the shared tsc-ts server by default on Linux and macOS
Builds on Linux and macOS now connect to a shared `tsc-ts --api --listen`
server by default, which keeps the files it parsed and bound, so a later
build in the same working directory does not parse the TypeScript libs and
scriptc's declaration files again. SCRIPTC_CHECKER_SERVER=0 turns it off.
When CI is set to a value other than 0 or false, builds use their own tsc-ts
process unless SCRIPTC_CHECKER_SERVER=1.

- The server exits after 600 seconds without a build (was 300).
- Its name also digests the compiler executable's identity, so each scriptc
  installation and build connects only to servers it started.
- The server process runs in the root directory, so it does not keep the
  first build's directory in use; its --cwd argument is unchanged.
- When the connection to the server fails after it answered, through a
  failed transfer or an ended stream, the native CLI runs the command again
  in a new process with SCRIPTC_CHECKER_SERVER=0 and exits with its status.
- SCRIPTC_CHECKER_SERVER no longer changes the frontend cache key: the
  server's answers are the build's own checker's.

The CLI docs describe the background process, its socket directory, when it
exits and how to stop it.
2026-10-27 08:21:48 -05:00
Chris Tate 52a228d0f5 Keep declarations a removed branch shares with another 2026-10-26 07:50:59 -05:00
Chris Tate de17158254 Merge main 2026-10-26 06:51:26 -05:00
Chris Tate 4553941d84 Merge main 2026-10-26 04:25:15 -05:00
Chris Tate 0ba138b57e Merge main 2026-10-26 03:25:54 -05:00
Chris Tate 2d24d6ad80 Merge main 2026-10-26 00:41:23 -05:00
Chris Tate 42e5d28684 Read undeclared literal keys as undefined and keep filled-in JavaScript vars dynamic 2026-10-25 23:05:10 -05:00
Chris Tate d48e060076 Merge main 2026-10-25 22:34:40 -05:00
Chris Tate 80ae660be6 Merge main 2026-10-25 21:22:05 -05:00
Chris Tate 59e78f298a Name the package declaration and its patch when a declared-type check fails (#2268) 2026-10-25 21:20:34 -05:00
Chris Tate 5dfa62b9fc Merge main 2026-10-25 18:54:10 -05:00
Chris Tate 061202a844 Merge main 2026-10-25 18:51:28 -05:00
Chris Tate 46785f8eba Address review feedback 2026-10-25 17:35:38 -05:00
Malte UblandClaude Opus 5.5 56e8417746 Name the package declaration and its patch when a declared-type check fails
A value that contradicts a type an npm package declares used to throw
"expected number at $, got undefined" and nothing else. Such a check now
names the declaration and the type patch that admits the value:

    profilekit@3.1.0 declares Profile.email as string (index.d.ts:7)
    if that declaration is wrong, patch it in package.json: ...

Origins are found for values a package function returns under a written
return type, for properties of records typed by a package-declared type
(record projections into such a type are keyed by it), and for
declaration-boundary casts, whose origin the projection records from the
declaration-world checker. Generic declarations get none: their types
come from the program's own instantiation.

Checked conversions carry dynCheck.origin, an index into
IrModule.declOrigins; the site's existing cold exception block calls
scr_type_origin_note (new runtime unit behind the typeOrigins feature),
which appends the note to the pending TypeError and derives the selector
suffix and the admitted kind from the failure. Union extractions get the
note per arm at compile time. The hot path is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 15:06:50 -07:00
Chris Tate 35161d1883 Cross package values at their declared type where the JS literal type lacks members (#2262) 2026-10-25 16:38:00 -05:00
Chris Tate 110396ee2d Replace a package's declaration files or supply its declarations with type patches (#2245) 2026-10-25 16:03:41 -05:00
Chris Tate 94446a086a Answer Object.prototype's methods on typed records (#2244) 2026-10-25 15:43:30 -05:00
Chris Tate 20574b1932 Merge main 2026-10-25 15:18:27 -05:00
Chris Tate 826519133f Merge main 2026-10-25 14:49:01 -05:00
Chris Tate f791c1d006 Address review feedback 2026-10-25 14:47:24 -05:00
Chris Tate 7d458303a6 Check type patch files again on a cached configuration, and supply .jsx entries
- The project configuration records the local files a type patch names,
  with whether each existed. A configuration read again from an unchanged
  package.json is reused only while every answer still holds, so creating
  or deleting a local declaration file between builds in one process
  validates again.
- A types patch supplies the declaration TypeScript probes for a .jsx
  JavaScript entry (x.d.ts for x.jsx), as it does for .js, .mjs and .cjs.
- Remove the two tests/fixtures/effect4/node_modules links the previous
  commit added; that directory is ignored and pnpm creates them.
2026-10-25 14:32:00 -05:00
Chris Tate 94d3b691ea Fix wrong npm declarations locally with type patches (#2238) 2026-10-25 14:31:10 -05:00
Malte UblandClaude Opus 5.5 f4825e05a7 Replace a package's declaration files or supply its declarations with type patches
A scriptc.typePatches entry can now replace a declaration file of the
package (or of its @types twin, written @types/<name>/<path>) with a
local .d.ts ("files"), or give the package a declaration entry ("types").
A supplied entry answers the package's types file and the declarations
TypeScript probes beside its JavaScript entries, and exists for the
program host, the resolver and npm-static's untyped-package test, so a
package without declarations becomes a typed package. Declaration patches
still apply on top. A files target the installed copy does not ship is
SC8031.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:05:52 -07:00
Chris Tate 701173a2ae Read builtin globals through the global object, and support Node's navigator 2026-10-25 13:55:44 -05:00
Malte UblandClaude Opus 5.5 177b2fde49 Cross package values at their declared type where the JS literal type lacks members
An opted-in package's JavaScript types its own values. The lowering types
a consumer's view of such a value from that JavaScript, and the
declaration boundary (decl-projection.ts) casts to the declared type only
where the JavaScript world answers `any`. A function that returns an
object literal has a precise JavaScript type, so it never crossed, even
where that literal type lacks members the declarations promise:

- `{ [key: string]: string }` over `{ size: "12px" }`: the consumer read
  `settings["font"] ?? "x"` through the literal's fixed key set. The
  checker types the miss `any` (a JavaScript literal), but the keyed read
  took the declared fields' common type, so the miss trapped with
  "record has no key" and aborted the process.
- an expando `out.max = ...` on a non-empty literal, which the JavaScript
  checker leaves out of the literal's type: reading `stats(xs).max` was
  refused with SC1090.

A package value whose JavaScript type is an object literal that lacks a
declared property or string index signature now crosses at its declared
type, so missing keys read undefined through the index signature and
expando members are read from the object. An index signature whose values
do not project (a package interface) keeps the value untyped (`as any`),
so its reads are the JavaScript reads. Only programs compiled with
--npm-static whose consumers read such values are rewritten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 11:51:24 -07:00
Chris Tate e8a64267cb Call toJSON on foreign elements and merge main 2026-10-25 13:13:02 -05:00
Malte UblandClaude Opus 5.5 c46f70e451 Fix wrong npm declarations locally with type patches
A project can now patch a package's declarations in package.json:
"scriptc": { "typePatches": { "<package>@<range>": { "declarations":
{ "<selector>": "<type>" } } } }. A leading "|" widens the declared type,
any other type replaces it. Selectors name declarations by path
(Type.member, fn(), fn(param), list()[], Env[string], export=(), default,
file.js:local()) and apply to the package's .d.ts, its JSDoc and its
@types twin alike.

Patches edit file text where every consumer reads it: the program host's
source reads (both checker worlds) and the npm-static passes that read
package files themselves. Only type nodes and JSDoc type tags change, and
no edit adds or removes a line. Installed copies are selected by version;
an @types twin by the runtime package's installed version. Patches that do
not apply as written warn (SC8030-SC8033) and the warnings repeat on
executable cache hits. Without the setting nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:27:52 -07:00
Chris Tate 303bfcd922 Merge main 2026-10-25 05:25:41 -05:00
Chris Tate 1b40ac0c64 Merge main 2026-10-25 05:15:58 -05:00
Chris Tate 6bd675420b Merge main 2026-10-25 02:06:50 -05:00
Chris Tate 2e052854c5 Merge main 2026-10-25 01:21:07 -05:00
Chris Tate 9d7f0182e5 Keep CommonJS wrapper names and redeclared exports out of namespace rewrites
- A guarded declaration of a name the CommonJS wrapper defines (`var
  exports = exports || {}`, and `module`, `require`, `__filename`,
  `__dirname`, `arguments`) reads the wrapper's value, not undefined, so
  it keeps its text instead of becoming a fresh object.
- The write scan counts destructuring assignments, and destructuring
  for-in/for-of targets, as writes, so a hoisted function that sets the
  namespace before its guarded declaration keeps the declaration as it
  is.
- An exported `var` that a later declaration of the same name sets
  again is not settled at the export: importers would read the
  redeclared value, where Node copies the first one into
  module.exports. It stays refused (SC1090), as before. The npm case
  var-export-redeclared pins it.
2026-10-25 01:04:55 -05:00
Chris Tate 5d3e0c7a8b Support X509Certificate fields and methods, TLS certificates and tls.Server contexts (#2052) 2026-10-25 01:00:24 -05:00
Chris Tate 7141af5cc9 Merge main 2026-10-25 00:07:23 -05:00
Chris Tate 688c1e03a1 Keep undefined results, literal text and hoisted vars in the shape overlay
- A function's results include undefined when a call can run off the end
  of its body or reach a bare return, so the overlay's return type keeps
  undefined and a caller's branch on it stays reachable. `new` of an ES5
  constructor ignores primitive results, and functions called with `new`
  get no @returns tag.
- Package JSDoc tags are neutralized only in comments: string, template
  and regular expression literals and JSX text keep their text.
- A branch replaced as unreachable keeps the var names it declares that
  its scope declares nowhere else, so references outside it read
  undefined. A branch that declares a function in a block is kept.
- The shape-overlay-facts npm case covers all three; four files of the
  overlay change are formatted.
2026-10-24 22:52:52 -05:00
Malte UblandClaude Opus 5.5 d1432eeb18 Compile namespace-style npm packages: guarded declarations and var exports
Two refusals kept pre-module JavaScript packages from compiling:

- `var ns = ns || {}` (and `?? {}`): the read in the variable's own
  initializer made the checker type the namespace `any`, and requirers
  then failed to reach the binding (SC1030). In a module, the read sees
  the hoisted `undefined` whenever nothing else writes the variable, so a
  new package-source normalization respells the declaration as its
  default (`var ns = ({})`) when no other declaration, assignment, update,
  function, class, parameter or local of that name exists in the file and
  no earlier top-level statement mentions it.
- `module.exports = { ns }` with a `var` binding was refused as exporting
  a mutable binding by reference. A `var` (or `let`) whose initialized
  declaration precedes the export statement at top level and that nothing
  reassigns exports exactly the value Node copies, so it now aliases like
  a const.

The namespace benchmark (results/design-shape-inference/nsbench) now
compiles and matches Node; the new npm case legacy-namespaces pins a
namespace library with guarded declarations, constructor functions with
prototype methods, enums, a revealing module and a handler registry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:13:26 -07:00
Chris Tate e3bda96fd1 Merge main 2026-10-24 20:31:45 -05:00
Chris Tate adeddf8724 Check with a tsc-ts server shared by later builds (opt-in) (#2153) 2026-10-24 19:12:05 -05:00
Chris Tate f26cc427c2 Merge main 2026-10-24 18:51:06 -05:00
Chris Tate d6c09279a7 Compile createRequire probes for optional addons as deferred refusals (#2015) 2026-10-24 18:41:05 -05:00
Chris Tate 9e55835014 Compile more npm and CommonJS JavaScript dependencies statically (#2122) 2026-10-24 18:30:56 -05:00
Chris Tate 6c18c58708 Merge main 2026-10-24 18:21:25 -05:00
Chris Tate c0a4496900 Check with a tsc-ts server shared by later builds (opt-in)
With SCRIPTC_CHECKER_SERVER=1 on Linux and macOS, the native compiler's
checker connections go to a shared `tsc-ts --api --listen` server, which
keeps the files it parsed for the next build. When the variable is unset,
nothing changes.

checkerServerName names the server by a digest of the tsc-ts executable's
identity, the working directory, the file system callbacks, the timing
flag and the TSC_TS_* environment. scriptc_ts7_attach takes the server's
lock file with a non-blocking flock, connects to its socket in this
user's private server directory ($XDG_RUNTIME_DIR/scriptc or
/tmp/scriptc-<uid>), and starts the server when none answers: in its own
process group, with its standard streams on /dev/null and no other
descriptor of the build, with --idle-exit 300 and TSC_TS_LISTEN_MEMORY=2048
unless the environment sets it. A build that finds the server in use,
cannot start or reach it, or gets no answer to a ping checks with its own
tsc-ts process, as without the variable; so does a tsc-ts without
--listen, which exits as it starts. The syntax-only source parser, which
reads no libraries, always uses its own process and leaves the server to
the program's checker.
2026-10-24 17:50:45 -05:00
Chris Tate 9d08b301fc Merge main 2026-10-24 17:50:19 -05:00
Chris Tate 055c9ffa25 Evaluate the arguments of deferred require.resolve calls before they throw
Preflight admits a createRequire loader's require.resolve call with a
computed request inside a try block, but lowering refused the call, so
in JavaScript source the whole statement became a runtime fence. The
fence threw before the request expression ran, and before anything else
in the statement: `try { where = load.resolve(candidate(name)); }`
skipped candidate()'s side effects, so the catch clause saw a different
state than in Node.

Such calls (a computed request, another argument shape, or
runtime-computed options, on require.resolve and require.resolve.paths)
now evaluate their arguments in order and then throw the fence, as a
computed require call does. The addon-probe fixture checks that a
computed candidate path runs before each failed lookup.
2026-10-24 17:07:44 -05:00
Chris Tate ea495056d8 Answer 'in' on native Node handles from their declared members (#1949) 2026-10-24 14:51:03 -05:00
Chris Tate 4f0975cbbe Compile more npm and CommonJS JavaScript dependencies statically 2026-10-24 12:01:26 -05:00
Chris Tate 73389fd513 Merge main 2026-10-24 06:53:30 -05:00
Chris Tate 1d5a7ff48a Merge main 2026-10-24 06:47:59 -05:00
Chris Tate dd4ae4e508 Merge main 2026-10-24 01:57:53 -05:00