Skip to content

Commit fe3b7ed

Browse files
committed
fix(ts): browser bundle throws on load — move node:fs import into validateFile (1.0.1)
1 parent cab9ab3 commit fe3b7ed

15 files changed

Lines changed: 380 additions & 16 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,48 @@ The error-code vocabulary is versioned **independently** of the package via `met
1414

1515
Staged for the next release. Not yet published.
1616

17+
## [1.0.1] - 2026-07-04
18+
19+
A hotfix republish. The 1.0.0 browser IIFE/ESM bundle
20+
([`dist/browser/jsonstat-validator.min.js`](packages/ts/dist/browser/jsonstat-validator.min.js)) threw
21+
on `<script src>` load in browsers: [`pipeline.ts`](packages/ts/src/pipeline.ts) imported `node:fs` at
22+
module top level, and esbuild preserved that top-level static import in the browser build as an eager
23+
`__require("node:fs")` that runs at load time (browsers have no `require`). The Node smoke test missed
24+
it because Node supplies a real `require`.
25+
26+
### Fixed — broken browser bundle
27+
- Moved the `node:fs` import out of the top of [`pipeline.ts`](packages/ts/src/pipeline.ts) and into
28+
[`validateFile()`](packages/ts/src/pipeline.ts) as a dynamic `await import("node:fs")`. Since the
29+
browser entry ([`browser.ts`](packages/ts/src/browser.ts)) does not export `validateFile`, esbuild
30+
now tree-shakes it — and the `node:fs` dependency — out of the browser bundle entirely. The
31+
documented jsDelivr / unpkg `<script src>` path works again. `validateFile()` is unchanged for Node
32+
consumers.
33+
34+
### Added — regression guards (both run in `npm run build` via the `verify:browser` script)
35+
- [`tools/verify-browser-bundle.mjs`](packages/ts/tools/verify-browser-bundle.mjs): a static-content
36+
guard that fails the build if `dist/browser/*` references a Node builtin (`node:*` or a core-module
37+
`require`/`import`). Encodes the invariant the 1.0.0 build violated.
38+
- [`tools/smoke-browser-bundle.mjs`](packages/ts/tools/smoke-browser-bundle.mjs): a functional guard
39+
that loads the built IIFE in a Node `vm` context with no `require`/`module` globals (browser-like)
40+
and drives `validate()` — the test that would have caught 1.0.0.
41+
42+
### Changed — version bump (lockstep across all surfaces)
43+
- Patch `1.0.0 → 1.0.1` across all four surfaces — [`@jsonstat-validator/ts`](packages/ts),
44+
[`jsonstat-validate`](cli), [`@jsonstat-validator/wasm`](packages/wasm), and the
45+
[`jsonstat-validator`](crates/validator) Rust crate — plus the lockfiles. The bug is confined to the
46+
TS browser bundle, but this release keeps the repo's all-surfaces-in-lockstep convention.
47+
- `engineVersion` `1.0.0 → 1.0.1` in [`rules-manifest.json`](rules-manifest.json) and the Rust
48+
[`_vendored`](crates/validator/src/_vendored/manifest/rules-manifest.json) snapshot (one commit, as
49+
[`vendored_parity.rs`](crates/validator/tests/vendored_parity.rs) requires). **`ruleSetVersion`
50+
stays `1.0.0`** — no codes/severities changed, the diagnostic vocabulary is unchanged.
51+
- Refreshed the [`packages/ts/README.md`](packages/ts/README.md) `meta.engineVersion` example to
52+
`1.0.1`.
53+
54+
### Deprecated
55+
- [`@jsonstat-validator/ts@1.0.0`](https://www.npmjs.com/package/@jsonstat-validator/ts/v/1.0.0) on npm
56+
— its browser IIFE/ESM bundle is broken; upgrade to `>=1.0.1` for CDN / `<script src>` use. Node
57+
usage of 1.0.0 is unaffected.
58+
1759
## [1.0.0] - 2026-07-01
1860

1961
The `1.0.0` cut. The work tracked in the README "Toward 1.0.0" section is complete; this release
@@ -112,7 +154,8 @@ follow-up release.
112154
Initial TypeScript engine, full S/D/C rule set, shared corpus, and the CLI (M1/M2), and the Rust
113155
port with corpus parity (M3). See the git history for details.
114156

115-
[Unreleased]: https://github.com/jsonstat/validator/compare/v1.0.0...HEAD
157+
[Unreleased]: https://github.com/jsonstat/validator/compare/v1.0.1...HEAD
158+
[1.0.1]: https://github.com/jsonstat/validator/releases/tag/v1.0.1
116159
[1.0.0]: https://github.com/jsonstat/validator/releases/tag/v1.0.0
117160
[0.3.0]: https://github.com/jsonstat/validator/releases/tag/v0.3.0
118161
[0.2.0]: https://github.com/jsonstat/validator/releases/tag/v0.2.0

‎cli/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jsonstat-validate",
3-
"version": "1.0.0",
3+
"version": "1.0.1",
44
"description": "Command-line semantic validator for JSON-stat 2.0.",
55
"license": "Apache-2.0",
66
"author": "JSON-stat contributors",

‎crates/validator/Cargo.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎crates/validator/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "jsonstat-validator"
3-
version = "1.0.0"
3+
version = "1.0.1"
44
edition = "2021"
55
license = "Apache-2.0"
66
description = "Semantic validator for JSON-stat 2.0 — cross-field cube invariants on top of the official JSON Schema 2020-12 definitions."

‎crates/validator/src/_vendored/manifest/rules-manifest.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"engineVersion": "1.0.0",
2+
"engineVersion": "1.0.1",
33
"ruleSetVersion": "1.0.0",
44
"schemaVersion": "1.05",
55
"description": "Single source of truth for the jsonstat-validator rule catalogue, error codes, severities and message hints. Loaded verbatim by the TypeScript surface (fs) and the Rust surface (include_str!). Codes are append-only within a ruleSetVersion major.",

‎package-lock.json‎

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/ts/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const result = await validateFile("./my-cube.json");
8787
],
8888
"summary": { "errors": 1, "warnings": 0, "infos": 0, "structuralErrors": 0, "byCode": { "VALUE_LEN_MISMATCH": 1 } },
8989
"options": { /* resolved ValidateOptions */ },
90-
"meta": { "engineVersion": "1.0.0", "ruleSetVersion": "1.0.0", "schemaVersion": "1.05", "durationMs": 3 }
90+
"meta": { "engineVersion": "1.0.1", "ruleSetVersion": "1.0.0", "schemaVersion": "1.05", "durationMs": 3 }
9191
}
9292
```
9393

‎packages/ts/package-lock.json‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/ts/package.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@jsonstat-validator/ts",
3-
"version": "1.0.0",
3+
"version": "1.0.1",
44
"description": "Semantic validator for JSON-stat 2.0 — layers cross-field cube invariants on top of the official JSON Schema 2020-12 definitions.",
55
"license": "Apache-2.0",
66
"author": "JSON-stat contributors",
@@ -55,7 +55,8 @@
5555
"scripts": {
5656
"gen": "node tools/gen-assets.mjs",
5757
"sync-license": "node tools/sync-license.mjs",
58-
"build": "npm run sync-license && npm run gen && tsc && node tools/build-node-cjs.mjs && node tools/build-browser.mjs",
58+
"build": "npm run sync-license && npm run gen && tsc && node tools/build-node-cjs.mjs && node tools/build-browser.mjs && npm run verify:browser",
59+
"verify:browser": "node tools/verify-browser-bundle.mjs && node tools/smoke-browser-bundle.mjs",
5960
"build:types": "npm run gen && tsc",
6061
"test": "node --loader ./tools/ts-loader.mjs --test test/rules.test.ts test/corpus.test.ts test/curated-parity.test.ts",
6162
"test:rules": "node --loader ./tools/ts-loader.mjs --test test/rules.test.ts",

‎packages/ts/src/pipeline.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// The validate pipeline: parse -> structural (JSON Schema) -> semantic rules -> aggregate.
2-
import { readFileSync, statSync } from "node:fs";
32
import { analyzeDataset, runSemantic } from "./engine.js";
43
import { validateStructural } from "./structural.js";
54
import { lookupRule } from "./manifest.js";
@@ -151,6 +150,11 @@ function runCollection(doc: any, rootPointer: string, opts: ResolvedOptions, sin
151150
}
152151

153152
export async function validateFile(file: string, options?: ValidateOptions): Promise<ValidationResult> {
153+
// Imported dynamically so `node:fs` is never a top-level static import — that keeps the browser
154+
// (IIFE/ESM) bundle free of any eager `__require("node:fs")` at load time. validateFile is
155+
// Node-only and not exported by src/browser.ts, so this whole function is tree-shaken out of the
156+
// browser build; the dynamic import is belt-and-suspenders for any consumer that does reference it.
157+
const { statSync, readFileSync } = await import("node:fs");
154158
const opts = resolveOptions(options);
155159
const size = statSync(file).size;
156160
if (size > opts.budget.maxBytes) {

0 commit comments

Comments
 (0)