as-test 1.1.10 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # Change Log
2
2
 
3
+ ## 2026-05-22 - v1.3.0
4
+
5
+ ### `features` config array + arbitrary `--enable` passthrough
6
+
7
+ - feat: new top-level `"features": ["try-as", "simd"]` array in `as-test.config.json` (and per-mode override). `try-as` is the only as-test-internal feature today and wires the `try-as/transform` + `AS_TEST_TRY_AS=1` build flags as before. Any other name in the array is passed through to `asc` as `--enable <name>` — so `"simd"`, `"threads"`, `"reference-types"`, `"gc"`, etc. now work without hand-editing `buildOptions.args`.
8
+ - feat: `ast test|run|build --enable <name>` and `--disable <name>` now accept arbitrary feature names. CLI flags override the config array (CLI `--disable simd` removes a config-listed feature; CLI `--enable simd` adds it). The known special-case `coverage` flag still routes to the dedicated top-level `coverage` config field rather than the features array. Both flags accept comma-separated lists too: `--enable try-as,coverage,simd` and `--disable try-as,coverage`. The same syntax is honored by `ast init`.
9
+ - feat: `ast init` interactive prompt now includes a multi-select "Features" step (↑/↓ to move, space to toggle, enter to confirm) with `coverage` and `try-as` options. `--enable`/`--disable` flags on `ast init` skip the prompt with explicit selections. The generated `as-test.config.json` writes `coverage` at the top level and `features` as a string array; when try-as is selected, `try-as` is also added to `devDependencies`.
10
+ - chore: schema validation rejects malformed shapes (object form, non-string array entries) with a fix hint pointing at the new shape.
11
+
12
+ ### `mode()` registration gate + `AS_TEST_MODE_NAME`
13
+
14
+ - feat: new `mode(matchers: string[], fn: () => void)` helper in the `as-test` runtime, plus an `AS_TEST_MODE_NAME: string` compile-time constant. Use `mode(["node:bindings"], () => { ... })` to gate suite/test registrations on the active mode name. Matcher semantics: positive entries OR; `!name` entries exclude; `[]` is a no-op; positive + negative entries combine as "any positive matches AND no negative matches."
15
+ - feat: build-side wiring — `build-core.ts` injects `AS_TEST_MODE_NAME=<mode>` into the asc env per-mode build, and the as-test transform rewrites the initializer of `AS_TEST_MODE_NAME` in `assembly/src/mode.ts` (an `afterParse` AST patch) so the value is baked into the wasm at compile time. Default value when no mode is selected is `"default"`.
16
+ - chore: bundled `assembly/__tests__/mode.spec.ts` exercises positive/negative/mixed matchers, empty matchers, and per-mode counter behaviour end-to-end against the project's own `node:bindings` / `node:wasi` modes.
17
+
18
+ ### Tooling
19
+
20
+ - chore: `build:transform` now runs `prettier -w ./transform/` after the TypeScript build so generated output stays formatted.
21
+
22
+ ## 2026-05-20 - v1.2.0
23
+
24
+ ### Directory-preserving artifact layout
25
+
26
+ - feat: build artifacts, fuzz artifacts, snapshots, readable logs, coverage logs, and crash records now mirror the source tree under the configured input globs instead of being flattened into a single directory with a `____`-mangled disambiguator suffix. For `assembly/__tests__/nested/array.spec.ts` the artifact is `outDir/<mode>/nested/array.spec.wasm` (previously `outDir/<mode>/array.<mode>.<target>.assembly____tests____nested.wasm`).
27
+ - feat: filename simplified to `<stem>.wasm` — the `.<mode>.<target>` suffix has been dropped since the mode is already a directory level and the target is implied by the mode config.
28
+ - feat: add `resolveGlobBase`, `resolveSpecRelativePath`, and `resolveArtifactPath` in `cli/util.ts` as the shared path helpers used by every code path that writes or looks up a per-spec artifact. Glob bases are computed component-wise (so `assembly/__tests` is not a prefix of `assembly/__tests__/foo.spec.ts`) and the longest matching base wins when multiple configured input patterns overlap.
29
+ - feat: add an up-front collision check in `build()` that throws a clear error naming both source files when two configured inputs would resolve to the same artifact path.
30
+ - fix: `ast test <one-spec>`, `ast run <one-spec>`, and `ast fuzz <one-spec>` no longer drop the disambiguator when only one of two same-basename files is being built. The build side and the runner side now compute the same path from the same configured input set.
31
+ - fix: build sites now `mkdir -p` the artifact's parent directory before invoking `asc` — pinned `assemblyscript@0.28.17`'s `-o` flag does not create parents and would otherwise ENOENT for any new nested directory.
32
+ - fix: `persistCrashRecord` now `mkdir -p`'s the entry's parent directory, supporting `/` in entry keys so nested specs and fuzz failures get their own crash files instead of clobbering by basename.
33
+ - fix: replace the hardcoded `/__tests__/` and `/__fuzz__/` markers in snapshot and readable-log path resolution with proper glob-base computation, so projects with custom input layouts now nest correctly instead of falling back to basename-only paths.
34
+
35
+ ### `.toThrow()` is a real matcher
36
+
37
+ - feat: `expect((): void => { throw new Error("boom"); }).toThrow()` now invokes the wrapped callback and asserts it threw, using try-as's `__ExceptionState.Failures` counter to detect the throw. Calling `.toThrow()` on a non-function value reports a clear "needs a function" failure.
38
+ - feat: requires `--enable try-as`. Without the feature flag, `.toThrow()` warns once and is a no-op (existing behavior preserved).
39
+ - chore: the bundled try-as integration spec lives at `assembly/__tests__/try-as.spec.ts` and is run by `npm test`, which now passes `--enable try-as`.
40
+
41
+ ### Breaking
42
+
43
+ - chore: clean break on snapshot file layout — the legacy `${base}.snap.json` and `${base}.${disambiguator}.snap.json` fallbacks have been removed. After upgrading, run `--create-snapshots` or `--overwrite-snapshots` once so snapshots are written at their new relative-path locations.
44
+ - chore: artifact filenames no longer carry the `<mode>.<target>` suffix; tooling that grepped the old shape needs adjusting. `ast clean` removes any orphan artifacts.
45
+ - chore: `.toThrow()` no longer accepts a bare value — it now requires a `() => void` callback and the try-as feature flag.
46
+
47
+ ### Tooling
48
+
49
+ - chore: husky pre-commit / commit-msg / pre-push hooks (build → format → typecheck → lint on commit; conventional-commits enforcement; full test gate only on push to `main` of `JairusSW/as-test`).
50
+
3
51
  ## 2026-05-19 - v1.1.10
4
52
 
5
53
  - feat: when the user already declares `--transform json-as/...` in `buildOptions.args` or in their referenced `asconfig.json` (top-level `options.transform`, any `targets.*.transform`, or via a single level of `extends`), as-test no longer adds its own auto-include — letting users bring their own json-as version or load path. Detection matches bare specifiers (`json-as`, `json-as/transform`), absolute paths, and `./node_modules/...` paths.
@@ -180,6 +180,14 @@
180
180
  ],
181
181
  "default": false
182
182
  },
183
+ "features": {
184
+ "type": "array",
185
+ "description": "Enabled feature names. \"try-as\" wires up the try-as transform; any other name is passed through to asc as --enable <name> (e.g. \"simd\", \"threads\", \"reference-types\"). CLI --enable/--disable flags override this list. Coverage has its own top-level \"coverage\" field.",
186
+ "items": {
187
+ "type": "string"
188
+ },
189
+ "default": []
190
+ },
183
191
  "env": {
184
192
  "description": "Environment variables injected when building/running. Accepts a .env file path, an array of KEY=value strings, or an object map.",
185
193
  "oneOf": [
@@ -400,6 +408,13 @@
400
408
  }
401
409
  ]
402
410
  },
411
+ "features": {
412
+ "type": "array",
413
+ "description": "Mode-specific feature list. Replaces the base list entirely when set.",
414
+ "items": {
415
+ "type": "string"
416
+ }
417
+ },
403
418
  "fuzz": {
404
419
  "type": "object",
405
420
  "additionalProperties": false,
@@ -13,18 +13,17 @@ export class CoverPoint {
13
13
 
14
14
  export class Coverage {
15
15
  public all: CoverPoint[] = [];
16
- public allIndex: Map<string, i32> = new Map<string, i32>();
17
- public hashes: Map<string, CoverPoint> = new Map<string, CoverPoint>();
18
- public points: i32 = 0;
16
+ public byHash: Map<string, CoverPoint> = new Map<string, CoverPoint>();
17
+ public uncovered: i32 = 0;
19
18
  static SN: Coverage = new Coverage();
20
19
  }
21
20
 
22
21
  export function __REGISTER(point: CoverPoint): void {
23
- if (Coverage.SN.allIndex.has(point.hash)) return;
24
- Coverage.SN.points++;
25
- Coverage.SN.allIndex.set(point.hash, Coverage.SN.all.length);
26
- Coverage.SN.all.push(point);
27
- Coverage.SN.hashes.set(point.hash, point);
22
+ const cov = Coverage.SN;
23
+ if (cov.byHash.has(point.hash)) return;
24
+ cov.byHash.set(point.hash, point);
25
+ cov.all.push(point);
26
+ cov.uncovered++;
28
27
  }
29
28
 
30
29
  export function __REGISTER_RAW(
@@ -38,7 +37,8 @@ export function __REGISTER_RAW(
38
37
  scopeName: string = "",
39
38
  depth: i32 = 0,
40
39
  ): void {
41
- if (Coverage.SN.allIndex.has(hash)) return;
40
+ const cov = Coverage.SN;
41
+ if (cov.byHash.has(hash)) return;
42
42
  const point = new CoverPoint();
43
43
  point.file = file;
44
44
  point.hash = hash;
@@ -49,32 +49,28 @@ export function __REGISTER_RAW(
49
49
  point.scopeKind = scopeKind;
50
50
  point.scopeName = scopeName;
51
51
  point.depth = depth;
52
- Coverage.SN.points++;
53
- Coverage.SN.allIndex.set(hash, Coverage.SN.all.length);
54
- Coverage.SN.all.push(point);
55
- Coverage.SN.hashes.set(hash, point);
52
+ cov.byHash.set(hash, point);
53
+ cov.all.push(point);
54
+ cov.uncovered++;
56
55
  }
57
56
 
57
+ // Hot path: invoked at every instrumented point. After first hit, subsequent
58
+ // hits short-circuit on `executed` before any writes.
58
59
  export function __COVER(hash: string): void {
59
- if (Coverage.SN.allIndex.has(hash)) {
60
- const index = Coverage.SN.allIndex.get(hash);
61
- if (index < Coverage.SN.all.length) {
62
- unchecked(Coverage.SN.all[index]).executed = true;
63
- }
64
- }
65
- if (Coverage.SN.hashes.has(hash)) Coverage.SN.hashes.delete(hash);
66
- }
67
-
68
- export function __HASHES(): Map<string, CoverPoint> {
69
- return Coverage.SN.hashes;
60
+ const cov = Coverage.SN;
61
+ if (!cov.byHash.has(hash)) return;
62
+ const point = cov.byHash.get(hash);
63
+ if (point.executed) return;
64
+ point.executed = true;
65
+ cov.uncovered--;
70
66
  }
71
67
 
72
68
  export function __POINTS(): i32 {
73
- return Coverage.SN.points;
69
+ return Coverage.SN.all.length;
74
70
  }
75
71
 
76
72
  export function __UNCOVERED(): i32 {
77
- return Coverage.SN.hashes.size;
73
+ return Coverage.SN.uncovered;
78
74
  }
79
75
 
80
76
  export function __ALL_POINTS(): CoverPoint[] {
package/assembly/index.ts CHANGED
@@ -752,3 +752,5 @@ function formatTime(time: f64): string {
752
752
 
753
753
  return `${_us}μs`;
754
754
  }
755
+
756
+ export { mode, AS_TEST_MODE_NAME } from "./src/mode";