lawspec 0.10.0 → 0.11.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/HASKELL.md CHANGED
@@ -41,6 +41,7 @@ have no constructors. They can appear in inhabited containers such as
41
41
  | `Nullable a` | `LS.Nullable a`, with `NullValue` and `NullableValue` |
42
42
  | `Optional a` | `LS.Optional a`, with `UndefinedValue` and `OptionalValue` |
43
43
  | `BigInt`, `BigUInt`, `Integer` | `Integer`, with domain checks |
44
+ | `Integer` adapter result | `LS.IntegerValue`, built from any `Integral` with `LS.integerValue` |
44
45
  | `Decimal` | `LS.Decimal`, wrapping an exact finite base-ten `Rational` |
45
46
  | `Rational` | `Rational` |
46
47
  | `Complex64`, `Complex128` | `Complex Float`, `Complex Double` |
package/KOTLIN.md CHANGED
@@ -61,6 +61,11 @@ arbitrary integers, and machine-profile integers use `BigInteger` with explicit
61
61
  domain checks. These portable machine-profile representations do not bind a
62
62
  host machine-sized primitive.
63
63
 
64
+ An adapter whose result is the abstract `Integer` returns Kotlin `Number`.
65
+ `Integer` is the top of the integral tower, so an implementation may return
66
+ `Int`, `Long` or `BigInteger`; the result bridge rejects non-integral values
67
+ such as `Double` and checks the logical domain. Arguments remain `BigInteger`.
68
+
64
69
  Decimal uses exact `BigDecimal`; Rational uses the normalized
65
70
  `LawSpecRuntime.Ratio`. Complex components use `LawSpecRuntime.Complex`, with
66
71
  Float32 precision validated for Complex64. Raw code-point text uses `IntArray`,
package/LANGUAGE.md CHANGED
@@ -1,4 +1,4 @@
1
- # LawSpec language and compiler boundary (0.10)
1
+ # LawSpec language and compiler boundary (0.11)
2
2
 
3
3
  LawSpec describes portable laws, concrete examples, and adapter contracts. The
4
4
  compiler is written in Haskell. Rust is an output backend alongside Java, Python,
@@ -102,7 +102,8 @@ scrutinee once; each branch binds that constructor's fields in the same order.
102
102
  Bindings are scoped to the branch. Matching must be exhaustive and cannot repeat
103
103
  a constructor. Lists match with `Nil` and `Cons head tail`; Maybe and Either use
104
104
  their constructors above. Recursive declarations must be strictly positive.
105
- General indexed constructors and GADT result signatures are not supported.
105
+ Constructors may refine natural indices; see [indexed families](#natural-indexed-families).
106
+ GADT result signatures that refine type arguments are not supported.
106
107
 
107
108
  Equality is structural and type-directed, including named fields and nested
108
109
  containers. Native public declarations retain their names and type parameters;
@@ -129,6 +130,58 @@ of recursive and nonrecursive named type constructors are supported; see
129
130
  constructor fields use checked constructor contracts. Recursive payload predicates
130
131
  follow stored type arguments and preserve outer dependent inputs.
131
132
 
133
+ ## Natural-indexed families
134
+
135
+ A data declaration may take `Natural` parameters. Each constructor states how it
136
+ determines them with `where <index> = <expression>`:
137
+
138
+ ```lawspec
139
+ type Vec (n :: Natural) (a :: Type) is
140
+ | VNil where n = 0
141
+ | VCons head :: a tail :: Vec m a where n = m + 1
142
+ end
143
+
144
+ type Tree (n :: Natural) (a :: Type) is
145
+ | Tip where n = 0
146
+ | Bin left :: Tree l a value :: a right :: Tree r a where n = l + r + 1
147
+ end
148
+
149
+ append :: (xs :: Vec n Int8) -> (ys :: Vec m Int8) -> (r :: Vec (n + m) Int8)
150
+ zip :: (xs :: Vec n Int8) -> (ys :: Vec n Bool) -> (r :: Vec n Bool)
151
+ ```
152
+
153
+ Index expressions are sums of natural literals and index variables. A variable
154
+ such as `m` is bound by the field whose type mentions it, and every index needs
155
+ exactly one equation in every constructor. `Natural` is also an ordinary value
156
+ type: an unbounded integer that is at least zero.
157
+
158
+ Indices are evidence, not a second type system. The compiler elaborates a family
159
+ before inference into three ordinary declarations:
160
+
161
+ - erased data `Vec a` with the same constructors, which is the native
162
+ representation on every target;
163
+ - a checked structural measure for each index, named `<index>Of<Type>` (here
164
+ `nOfVec` and `nOfTree`), recomputed from the constructor equations;
165
+ - a refinement, so `Vec e a` in any signature or quantifier means
166
+ `(v :: Vec a where nOfVec v == e)`.
167
+
168
+ `append` above is therefore an adapter contract: its result must have length
169
+ `nOfVec xs + nOfVec ys`, and a native implementation that drops an element
170
+ fails with the postcondition. An index variable that is otherwise unbound, like
171
+ `n` and `m` in `append`, is implicit. It is determined by the first binder whose
172
+ family type mentions it alone, and later occurrences read that binder's measure.
173
+ Implicit indices must not first appear inside an expression, and a result cannot
174
+ introduce one.
175
+
176
+ Generation follows the index. For a free index, as in `append`, values come from
177
+ the erased type and the index is their measure. For a fixed index (`Vec 3 Int8`)
178
+ or a shared one (`zip`'s `ys`), the target is solved backwards through the
179
+ constructor equations: `VCons` for `n = 3` needs a tail with index 2, and `Bin`
180
+ splits `n - 1` between its subtrees. Samples are constructed, not filtered, and
181
+ shrinking stays within the index on every target. The same planning applies to
182
+ any user-written measure over declared data whose branches are a constant plus
183
+ the same measure of that branch's fields.
184
+
132
185
  ## Total definitions
133
186
 
134
187
  A unit can supply an implementation as a checked total definition:
@@ -332,12 +385,15 @@ API schema v3 uses separately defined wire views, with lossless tagged scalar
332
385
  values. It does not serialize internal AST constructors. See the
333
386
  [API migration guide](API-MIGRATION.md).
334
387
 
335
- ## Beyond 0.10
388
+ ## Beyond 0.11
336
389
 
337
- GADTs, indexed families, and general dependent types are planned after 0.10.
338
- The Core type model distinguishes type arguments from index arguments, but that
339
- representation is not a claim that arbitrary dependent programs are accepted.
340
- User-defined products and sums have ordinary uniform type parameters.
390
+ Natural-indexed families are implemented in 0.11 by elaboration to erased data,
391
+ measures and refinements. GADTs that refine type arguments, non-linear or
392
+ non-natural indices, index equalities between sibling fields (such as perfect
393
+ trees whose subtrees share one index), and general dependent types remain future
394
+ work. The Core type model distinguishes type arguments from index arguments, but
395
+ that representation is not a claim that arbitrary dependent programs are
396
+ accepted.
341
397
  External type bindings and custom generator bindings are implemented in the
342
398
  0.10 release; see [the binding reference](NATIVE-BINDINGS.md) for their
343
399
  interface and acceptance status. They configure native representations alongside
@@ -973,7 +973,10 @@ Gradle's coordination socket. The saved logs confirm successful native tests,
973
973
  seven expected failures among 45 tests for the deliberately incorrect fee, and
974
974
  zero planned regeneration changes in both configurations. This verifies the
975
975
  installed CLI/Gradle path as well as the separate compilation/runtime harness.
976
- The CI additions have not yet run remotely.
976
+ The remote run of these CI additions exposed failures in the Kotlin and Haskell
977
+ target jobs. They came from the 0.9 narrowing of abstract `Integer` adapter
978
+ results, not from bindings, and are fixed in 0.11
979
+ ([release notes](RELEASE-0.11.md#tower-polymorphic-integer-results-restored)).
977
980
 
978
981
  The runnable assets live in `examples/native-payments/` and are copied into the
979
982
  npm package by `tools/wasm.sh`. The original `lawspec examples` command retains its
@@ -996,8 +999,8 @@ The full npm regression suite passes 77 tests, and the package smoke test passes
996
999
  installed API generation on all eight targets plus executable Rust scaffold,
997
1000
  doctor, adapter and regeneration checks. The package contains the binding guide,
998
1001
  migration notes, release notes and all eight application example configurations.
999
- These checks do not replace the pending remote CI gate described above. Release
1000
- status remains incomplete until that result has been reviewed.
1002
+ The remote CI gate for these checks was reviewed during the 0.11 release, which
1003
+ re-ran every target job, including both installed native-binding profiles.
1001
1004
 
1002
1005
  The empty-parameter audit must distinguish an empty type from an inhabited type
1003
1006
  that mentions it. A native factory for `Phantom Empty` may ignore its child
package/PRIMITIVES.md CHANGED
@@ -1,4 +1,4 @@
1
- # LawSpec scalar reference (0.10.0)
1
+ # LawSpec scalar reference (0.11.0)
2
2
 
3
3
  A scalar has a declared domain, checked literals, equality, property inputs, and
4
4
  boundary fixtures. General collections, objects, pointers, and type-only constructs
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **State the law once. Check it everywhere.**
4
4
 
5
- LawSpec 0.10 compiles reusable laws into native property tests, executable examples,
5
+ LawSpec 0.11 compiles reusable laws into native property tests, executable examples,
6
6
  and implementation adapters. The compiler is Haskell, distributed as prebuilt
7
7
  WebAssembly with a Node CLI and an asynchronous, typed JavaScript API.
8
8
 
@@ -47,6 +47,19 @@ the [release notes](RELEASE-0.10.md), and
47
47
  [schema 4 migration notes](API-MIGRATION.md#schema-4-native-bindings).
48
48
  Existing specifications without bindings retain their behavior.
49
49
 
50
+ ## Natural-indexed families in 0.11
51
+
52
+ LawSpec 0.11 adds data indexed by natural numbers, such as length-indexed
53
+ vectors and size-indexed trees. Each constructor states its index equation;
54
+ the compiler elaborates the family to ordinary native data, a checked measure,
55
+ and a refinement. Dependent signatures like
56
+ `append :: (xs :: Vec n a) -> (ys :: Vec m a) -> (r :: Vec (n + m) a)` become
57
+ checked adapter contracts, and generators construct values with a required
58
+ index instead of filtering for it. See the
59
+ [language reference](LANGUAGE.md#natural-indexed-families), the
60
+ [indexed example](examples/specs/indexed_families.lawspec), and the
61
+ [release notes](RELEASE-0.11.md).
62
+
50
63
  ## Install and try it
51
64
 
52
65
  LawSpec has its own [syntax-highlighting grammar and VS Code extension](editors/vscode/README.md)
@@ -56,7 +69,7 @@ support to use it; GitHub currently uses the Haskell fallback.
56
69
  Install [LawSpec from npm](https://www.npmjs.com/package/lawspec):
57
70
 
58
71
  ```sh
59
- npm install --save-dev lawspec@0.10.0
72
+ npm install --save-dev lawspec@0.11.0
60
73
  npx lawspec --version
61
74
  ```
62
75
 
@@ -70,8 +83,8 @@ local dependencies so LawSpec can create its `package.json` and test script:
70
83
  ```sh
71
84
  mkdir lawspec-example
72
85
  cd lawspec-example
73
- npm exec --package=lawspec@0.10.0 -- lawspec init --target javascript
74
- npm install --save-dev lawspec@0.10.0
86
+ npm exec --package=lawspec@0.11.0 -- lawspec init --target javascript
87
+ npm install --save-dev lawspec@0.11.0
75
88
  npx lawspec check
76
89
  npx lawspec explain 'example.atoi_codec::itoa and then atoi yields a'
77
90
  npx lawspec doctor
@@ -281,7 +294,7 @@ reusable laws accept these curried functions, their partial applications, and
281
294
  scalar parameters. Quantified test inputs remain scalar.
282
295
 
283
296
  The prelude defines the following laws. Every row has an executable example in
284
- [algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/algebra.lawspec), including both sides of every
297
+ [algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.11.0/examples/specs/algebra.lawspec), including both sides of every
285
298
  combined law. `f` and `g` are binary operations, `inverse` is unary, and `e` and
286
299
  `zero` are scalar parameters. All these laws require equality of the element type.
287
300
 
@@ -349,7 +362,7 @@ their entire consequence. Every conjunct is type-checked and emitted. As with
349
362
  existing assertions, the first failure stops that individual test. `and` is now
350
363
  a reserved word.
351
364
 
352
- [Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/currying.lawspec) demonstrate a four-argument
365
+ [Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.11.0/examples/specs/currying.lawspec) demonstrate a four-argument
353
366
  function partially applied twice, a formatter with four heterogeneous arguments,
354
367
  and composition after partial application. Each example states its exact outputs.
355
368
  Run `node npm/bin/lawspec.mjs examples` after rebuilding to inspect all nine units
@@ -401,7 +414,7 @@ law `valid ports round trip` is
401
414
  end
402
415
  ```
403
416
 
404
- The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/parse_port.lawspec)
417
+ The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.11.0/examples/specs/parse_port.lawspec)
405
418
  defines valid ports as 1–65535, and covers both endpoints, ordinary ports, zero,
406
419
  negative values, and 65536. All explicit `expect` assertions run regardless of
407
420
  the law's condition. A false condition skips only the consequence: invalid ports
@@ -419,7 +432,7 @@ The prelude includes `satisfies predicate` (the predicate holds for every input)
419
432
  and `left inverse when predicate parse render` (the guarded round trip above).
420
433
  These reusable laws preserve the condition and its lexical bindings when expanded.
421
434
  `equivalent` can also compare two predicates, since `Bool` supports equality.
422
- The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/boolean_flags.lawspec)
435
+ The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.11.0/examples/specs/boolean_flags.lawspec)
423
436
  checks that flipping twice restores both `false` and `true`; all targets generate
424
437
  Boolean property inputs and explicit tests for both Boolean boundary values. Java caps Boolean-only JetCheck runs at the number of
425
438
  possible input combinations (up to 100), avoiding generator exhaustion.
@@ -498,7 +511,7 @@ This expands to `for all (x :: Int32) . render (x) = referenceRender (x)`.
498
511
  The example inherits the input name `x` from the prelude. Both functions are
499
512
  user-owned adapter functions; either may delegate to your existing code.
500
513
 
501
- [The complete example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/equivalent.lawspec) compares decimal
514
+ [The complete example](https://github.com/brain-fuel/lawspec/blob/v0.11.0/examples/specs/equivalent.lawspec) compares decimal
502
515
  renderers and two implementations that clamp negative integers to zero. For
503
516
  JavaScript, their adapters can be:
504
517
 
@@ -535,7 +548,7 @@ law `normalizers agree` is
535
548
  end
536
549
  ```
537
550
 
538
- The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/slug.lawspec)
551
+ The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.11.0/examples/specs/slug.lawspec)
539
552
  compares two implementations of ASCII-space replacement. It includes empty,
540
553
  Unicode and escaped text. Each target uses its native string generator:
541
554
  JetCheck `Generator.stringsOf(Generator.asciiPrintableChars())`, Hypothesis `st.text()`, fast-check `fc.string()`,
@@ -562,7 +575,7 @@ law `canonicalization reaches a fixed point` is
562
575
  end
563
576
  ```
564
577
 
565
- The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/canonical_url.lawspec)
578
+ The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.11.0/examples/specs/canonical_url.lawspec)
566
579
  uses removal of **all trailing slashes** as a small fixed-point demonstration,
567
580
  not a complete URL canonicalization algorithm. For JavaScript:
568
581
 
@@ -571,7 +584,7 @@ export const canonicalize = value => value.replace(/\/+$/, "");
571
584
  ```
572
585
 
573
586
  Removing just one trailing slash fails the supplied repeated-slash example.
574
- The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.10.0/examples/specs/mixed_inputs.lawspec)
587
+ The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.11.0/examples/specs/mixed_inputs.lawspec)
575
588
  shows `Text` and `Int32` in the same quantified property and executable example.
576
589
  The JavaScript API represents example bindings as typed records whose values use
577
590
  `DataValue`: lossless tagged scalar payloads or structural constructors. Expected results are
@@ -654,7 +667,7 @@ by the JS shim.
654
667
  ## Build and verify
655
668
 
656
669
  For contributors working from a repository checkout, build a local archive with
657
- `npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.10.0.tgz`.
670
+ `npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.11.0.tgz`.
658
671
  The package payload lives in `npm/`.
659
672
 
660
673
  ```sh
package/REFINEMENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- # Refinements and abstract integers (0.10.0)
1
+ # Refinements and abstract integers (0.11.0)
2
2
 
3
3
  A refinement restricts a scalar domain with a pure Boolean expression. LawSpec
4
4
  checks concrete examples, generates satisfying input tuples, and checks adapter
@@ -154,6 +154,11 @@ seeds direct comparison values and boundaries, and checks the complete predicate
154
154
  Predicates outside that analysis use bounded sampling. Small finite base-domain
155
155
  products are enumerated exhaustively, retaining only satisfying tuples.
156
156
 
157
+ A refinement `m x == e` over declared data, where `m` is a linear structural
158
+ measure (each branch is a constant plus `m` of that branch's fields), is solved
159
+ rather than sampled: values are constructed with measure exactly `e`. Indexed
160
+ families rely on this; see [natural-indexed families](LANGUAGE.md#natural-indexed-families).
161
+
157
162
  Shrinking checks refinements again and repairs dependent later inputs when an
158
163
  earlier value changes. An overflowing counterexample can shrink to `(1, 127)`;
159
164
  it cannot shrink to `(0, 127)` because that pair is outside the domain.
@@ -0,0 +1,62 @@
1
+ # LawSpec 0.11.0
2
+
3
+ ## Natural-indexed families
4
+
5
+ Data declarations may take `Natural` parameters, and each constructor states
6
+ its index equation:
7
+
8
+ ```lawspec
9
+ type Vec (n :: Natural) (a :: Type) is
10
+ | VNil where n = 0
11
+ | VCons head :: a tail :: Vec m a where n = m + 1
12
+ end
13
+
14
+ append :: (xs :: Vec n Int8) -> (ys :: Vec m Int8) -> (r :: Vec (n + m) Int8)
15
+ ```
16
+
17
+ A family elaborates before inference into ordinary erased data, a checked
18
+ structural measure per index (`nOfVec`), and a refinement relating the measure
19
+ to the index. Core and all eight emitters are unchanged. Native code works with
20
+ the erased type, and every index claim is evidence checked at test time: a
21
+ dependent result such as `Vec (n + m) Int8` is an adapter postcondition.
22
+
23
+ An index variable that is otherwise unbound is implicit. The first binder whose
24
+ family type mentions it determines it, and later occurrences read that binder's
25
+ measure. Index expressions are sums of natural literals and index variables.
26
+ `Natural` is also available as a value type. Diagnostics use the `indexed` code.
27
+
28
+ ## Index-directed generation
29
+
30
+ A quantifier constrained by a linear structural measure, including a fixed index
31
+ (`Vec 3 Int8`) or a shared one (`zip`'s second argument), is generated by solving
32
+ the constructor equations backwards instead of filtering. Multi-field equations
33
+ such as `Bin … where n = l + r + 1` split the remaining index across fields.
34
+ Python, JavaScript, TypeScript, Java, Kotlin, Go, Haskell and Rust construct these
35
+ values natively, and framework shrinking stays within the index. The same
36
+ planning applies to user-written linear measures over declared data.
37
+
38
+ `examples/specs/indexed_families.lawspec` runs on all eight targets through
39
+ `tools/indexed-integration.mjs`. Correct adapters pass free, fixed and shared
40
+ index laws; mutants that add, drop or lose elements fail their dependent
41
+ contracts.
42
+
43
+ ## Tower-polymorphic Integer results restored
44
+
45
+ 0.9 had narrowed Kotlin and Haskell adapters whose result is the abstract
46
+ `Integer` to `BigInteger` and `Integer`. They again return `Number` (Kotlin) and
47
+ `LS.IntegerValue` (Haskell), as in 0.8 and as Java always did. `Integer` is the top
48
+ of the integral tower: an implementation may return any integral native value,
49
+ and the result bridge rejects non-integral values and checks the logical domain.
50
+ Adapters written against 0.9 or 0.10 stubs that return `BigInteger` or `Integer`
51
+ still compile, because both are integral.
52
+
53
+ This regression had kept the Kotlin and Haskell CI target jobs failing since
54
+ 0.9. Those jobs, and the scalar mutation fixtures, now pass with the documented
55
+ typed Symbol, Decimal, Utf16Text and Optional representations.
56
+
57
+ ## Compatibility
58
+
59
+ Specifications without indexed families generate the same code as 0.10, apart
60
+ from the Integer result signatures above. API schemas 3 and 4 are unchanged.
61
+ GADTs that refine type arguments, non-linear indices and index equalities between
62
+ sibling fields remain future work.
package/bin/lawspec.mjs CHANGED
@@ -173,13 +173,13 @@ async function main() {
173
173
  }
174
174
  if (!verb || ["help", "--help", "-h"].includes(verb)) {
175
175
  output(
176
- "LawSpec 0.10.0\nUsage: lawspec init --target <language> [--project <directory>] [--minify]\n lawspec check | doctor | explain <unit>::<law> | generate\n lawspec examples [--example payments] [--target <language>] [--output <directory>]\nOptions: --config <path>, --target <language>, --machine-bits <32|64>, --json\nGeneration: --dry-run, --check, --minify\nTargets: " +
176
+ "LawSpec 0.11.0\nUsage: lawspec init --target <language> [--project <directory>] [--minify]\n lawspec check | doctor | explain <unit>::<law> | generate\n lawspec examples [--example payments] [--target <language>] [--output <directory>]\nOptions: --config <path>, --target <language>, --machine-bits <32|64>, --json\nGeneration: --dry-run, --check, --minify\nTargets: " +
177
177
  targets.join(", "),
178
178
  );
179
179
  return;
180
180
  }
181
181
  if (verb === "--version") {
182
- output("0.10.0");
182
+ output("0.11.0");
183
183
  return;
184
184
  }
185
185
  if (positional.length > (verb === "explain" ? 1 : 0))
package/build.json CHANGED
@@ -75,6 +75,7 @@
75
75
  "src/LawSpec/HaskellProperties.hs",
76
76
  "src/LawSpec/HaskellTestHelpers.hs",
77
77
  "src/LawSpec/HaskellTypeRefs.hs",
78
+ "src/LawSpec/Indexed.hs",
78
79
  "src/LawSpec/Inference.hs",
79
80
  "src/LawSpec/JavaData.hs",
80
81
  "src/LawSpec/JavaDefinitions.hs",
@@ -126,23 +127,23 @@
126
127
  "wasm/lawspec-wasm.cabal"
127
128
  ],
128
129
  "digests": {
129
- "lawspec.cabal": "b38f73ab83f833725b64ae4d0df07783efbdeacf367e99e378c18dddbb864f9d",
130
- "package.yaml": "c682a129e7fdec4ae5d60d229c622ad05714fd52c6d83a3c44815b232fb4459d",
130
+ "lawspec.cabal": "e250011f2652a4a00c937de6a46763c51e674859db1bd45228d6c560454fc8ec",
131
+ "package.yaml": "012f2333a714b4a4c542245250cdc1a1da3620215dcb5379eda9f3ffc3067fea",
131
132
  "runtime/LawSpecCodecs.hs": "90cb1ed15d7ab664a17a054580c849408d77cfd2b2adcb4a3228b05132caeaca",
132
- "runtime/LawSpecDataStrategies.hs": "0b6639d200101bfc3c850278133a7f686bfc3423c81ccc7d82f161847700c914",
133
- "runtime/LawSpecDataStrategies.java": "ade2e132f116d261c6e8c198c6f87515193ceda8ab83b1006627eebb72321ed1",
133
+ "runtime/LawSpecDataStrategies.hs": "cbe9f1946228db1076c19cd3158e8798fe32766cfdbea3f66b63d22d3d217508",
134
+ "runtime/LawSpecDataStrategies.java": "e3416efe8a0d7b1276cb4d1a8105fd01b1f5f3f02a803bf55d955d9e3ec39095",
134
135
  "runtime/LawSpecKotlin.kt": "0cf3b7de0406155225ccca33ee3dfdf8f0456c47d577f6be8f1265c13df838cb",
135
136
  "runtime/LawSpecKotlinCodecs.kt": "6210f77dbbe24ba2088db48dae5415abf0993fe552fb3a57d29430807efbc297",
136
- "runtime/LawSpecKotlinStrategies.kt": "73deed2d5d13529220147b530743a5f9bee72467e940bb357b2c3152ee6ec4ee",
137
+ "runtime/LawSpecKotlinStrategies.kt": "c07ef4da5ccdab9045f50f08dbfcc4a7349a505af76bd5d3ddc8c00e879437f3",
137
138
  "runtime/LawSpecRuntime.hs": "5ced6a6a86a892ae63a544d4c4a4429bf756a730ea786478d820c8070cc96402",
138
139
  "runtime/LawSpecRuntime.java": "c7b7cfc9b7c22b082edee14a52d95ef80ebe01ee56988c22702f629ea24b8a4f",
139
140
  "runtime/LawSpecSchema.hs": "9912d9f525426a598f9c03c29cfc7bacf80218f73c9d537d8c1257bae245aa33",
140
141
  "runtime/LawSpecSchema.java": "885f5f17a9ea1bdb86915dc184aea25b72c3062d6ddb9605530310540b1cc166",
141
142
  "runtime/LawSpecStrategies.kt": "569f7775c0ac44765d05567cbf62d026558ac193c080ca71681ec6ee49c21794",
142
143
  "runtime/lawspec_codecs.go": "b3024fe60b3b66df9ebffe995ab5bb0294a609a82c611d54957e092eac29f2f5",
143
- "runtime/lawspec_data_strategies.go": "cbddf44fbd222ad39bda3278dd433c6592a90c1808a5c157e02d8be931244c9a",
144
- "runtime/lawspec_data_strategies.mjs": "f2e04a8d1b418afa6b8b6a993e222bdd5d84fca800e7f5071157b7888af0907c",
145
- "runtime/lawspec_data_strategies.py": "685212fa4ecdde0139336d9fb9a6b8aca15ed5d3eab02c33b4cca4b39ad21eb5",
144
+ "runtime/lawspec_data_strategies.go": "ec151dbdd1af89f57ce11c10d057d4bbc44691b0472035e0756ced1e0769a640",
145
+ "runtime/lawspec_data_strategies.mjs": "e74a508bf2cd382ad1ca6bc90b6be19b10170e6261dbb902f58522fa2880c2fc",
146
+ "runtime/lawspec_data_strategies.py": "3469bab1af47b5fb9583569d0a07f3c2a0485303d79347afe509cf34b6a21296",
146
147
  "runtime/lawspec_runtime.go": "af6ef660387b625b3bb5fcd57f06260c821d4f65eda158db44ad12d66e1d70f8",
147
148
  "runtime/lawspec_runtime.mjs": "3b7877c1ee523afba30ed24cd452e2a6126d9e741c7675600e867600d7a73f46",
148
149
  "runtime/lawspec_runtime.py": "2f780bc92762eb1115f734a36cc224ae34504929b4ff0ec506735cfa9e3ab02b",
@@ -150,9 +151,9 @@
150
151
  "runtime/lawspec_schema.go": "bdfb6f2552bfccf56073d52f7cdbe1d784cba2d956c29762d77df7ad6bba8d67",
151
152
  "runtime/lawspec_schema.mjs": "60eb4204461eaa2c207fc7c3394014e2a814e4383b7045c065b4dae7c7a202c2",
152
153
  "runtime/lawspec_schema.py": "b6967ef1a33fe6a1a180d5573ec37cd8bb2079c06e961ac2eeb05f23cffcdd07",
153
- "runtime/lawspec_strategies.rs": "ee2d42c4bc86bf1ccfcd3a8efc5628475829ea2551c4321e217485fca63433fa",
154
- "runtime/rust/Cargo.lock": "03b17464317ee1e91281576aab67007cd8956e58a9d22665bc29045acdbbfa4e",
155
- "runtime/rust/Cargo.toml": "615f3f40d2901e36b8ca5f2c105863264c93327c939a04f61469c658d352255f",
154
+ "runtime/lawspec_strategies.rs": "cac9ca301d4614fff9acfe61e7cc5678fabc044edadfa89ec11599b6780dc5f3",
155
+ "runtime/rust/Cargo.lock": "a541c57b690f3ece7c0aecb3aef3f1a991a760d3aa04018a8bec9d4567978170",
156
+ "runtime/rust/Cargo.toml": "6929f520456dcb6a702bcf40df08c8caeaee41b304b06deb07f86221d378e940",
156
157
  "src/LawSpec/Api.hs": "392f6e8c1a9ae81a95e219ac071e5b4bea03ee330d63b829036f8f54b2eda8a7",
157
158
  "src/LawSpec/Backend.hs": "03c3b0cd62467ca677c88d29d7b12b2ab505cc0076d93d1f48fbddb0e9bcf133",
158
159
  "src/LawSpec/Capabilities.hs": "e43d4fc3a4481fbb8aa3ff1c3f41f7e7fca5db01bfe256a30162a45d75a00c1c",
@@ -176,8 +177,8 @@
176
177
  "src/LawSpec/Core/Validate.hs": "0f045241215e068081d6cd7cf451f0166671bae5fffafda71a2e2320e8014c27",
177
178
  "src/LawSpec/Core/Value.hs": "f2b2e4d82491e184d60b1217922b25c57a06cbbaecb2cb6f31174af50268fbc9",
178
179
  "src/LawSpec/CoreEmit.hs": "075f06ef1be2437542967b171c535686f33ed4645f6525a93c1024a9fbf68582",
179
- "src/LawSpec/CoreNativeScalarEmit.hs": "6caf801922e8969eac7926bef641d950aebf5cd32b2e4b7ce3f6f32f91f41d99",
180
- "src/LawSpec/CoreScalarEmit.hs": "ea87bcf97a7be02cb4e83728b9dd4f5fa5a0c461e3f31f89e4d28242c148b43c",
180
+ "src/LawSpec/CoreNativeScalarEmit.hs": "4082661ba5f8b1b09b4811f62ba767fa1ea401a38dd10011aea442c5a27892af",
181
+ "src/LawSpec/CoreScalarEmit.hs": "d1a51f7fe9b23817eeac7b50dac70ec02219df66d4b0218c5c9ae5237da244fd",
181
182
  "src/LawSpec/Data.hs": "9dbeafa120276b114dad790ec69e617f7ad0c632e3c718962f2f7faf8ca327f4",
182
183
  "src/LawSpec/DefinitionTotality.hs": "7671b7efb0cac5d796b074d48b2c10940e5bd0b5df082b926aee694903285efa",
183
184
  "src/LawSpec/Domain.hs": "222d15f0012ae630802238a6d7e1ee78d1e718b599f5ebb5a1ddc4e8d29d68c3",
@@ -190,33 +191,34 @@
190
191
  "src/LawSpec/GoDefinitions.hs": "09e9e4b60cfea893d35589cd18bc8a6099789d81e0f57ae5cb018bd95bd4838b",
191
192
  "src/LawSpec/GoExpr.hs": "da3a7cc0e3d2906886dc44f6b990e72098bb351558bbb31ee335da7382fac58a",
192
193
  "src/LawSpec/GoNativeBinding.hs": "1ad8ea4cec6187c7e6005651c0edeec69e196eb329fb958c833da7751225aa1b",
193
- "src/LawSpec/GoProperties.hs": "b0b92af071b70058802627bfb971e1b64e05847f27ef0645b2d7325a3867fe1d",
194
+ "src/LawSpec/GoProperties.hs": "1eee75d852b0a1f8b6fe2cfb1cd3ed7dc57f44ca0ee137697729e8e1583d85fe",
194
195
  "src/LawSpec/GoTestHelpers.hs": "dee55ee8505370e932773b8c685f4a1f380129e6f43f8806bf1e82efb99451b1",
195
196
  "src/LawSpec/GoTypeRefs.hs": "7f239f5ac3ac8bd21c8f76c328049f7715d8562ee1382befa0a58abd4f15f726",
196
197
  "src/LawSpec/HaskellData.hs": "51374f35de2b1d6c78d15597e41e6f0695fe5fdb040cbb35a7d03d23fa3a3179",
197
198
  "src/LawSpec/HaskellDefinitions.hs": "535a8d644d5ddb98c22fa6bdf34cf2baa787aa1f5b900815c621d0d65ede0f0f",
198
199
  "src/LawSpec/HaskellExpr.hs": "f4144cf2022ef0a3bec6d796ebe7d2f42563e9d25758b9f362cd5545b47e802b",
199
200
  "src/LawSpec/HaskellNativeBinding.hs": "9f98aa0c8659b572bd6486a0e29c4692bc6432db8639fa74a09bbf1b771bbfa6",
200
- "src/LawSpec/HaskellProperties.hs": "4a7c2aafbb3418e7e926d420d59e810a70e80b8e61a5efaad73d3282038c9d3c",
201
+ "src/LawSpec/HaskellProperties.hs": "5d64d5f45b86b5bb148383588d69ad717b0e158926b76938cc5cef54ed3adecc",
201
202
  "src/LawSpec/HaskellTestHelpers.hs": "51e59b2abc9e85455e3c8e2c6baadb27e5d319081cb730d2ce56be5dc8053897",
202
203
  "src/LawSpec/HaskellTypeRefs.hs": "2225b2a5e2ab3662e7202d23b24f4d6db48353b35ae28df8acd2b92fd2b97574",
204
+ "src/LawSpec/Indexed.hs": "bfb24a4023517acf9f34e6f3327c5623f0e18b1576e5453a43d7a6cd9e691e00",
203
205
  "src/LawSpec/Inference.hs": "78285145897f03c3c4adc37acf0f490ea779bab3975fb2830628dae3a152fe38",
204
206
  "src/LawSpec/JavaData.hs": "177713377f7a38d69fbfd7891899357fc55782cf1d361f2e9dd5baf8725d4067",
205
207
  "src/LawSpec/JavaDefinitions.hs": "9c0fe11bd4849af09bbe3d941d89ba609a2dabecdc1c8b09b6ddbf69671c8f92",
206
208
  "src/LawSpec/JavaExpr.hs": "6f237b0fc1eabe2d53c160f4cb781f8b77b478383f1a77ca4ccc8f08ac0dbcd0",
207
209
  "src/LawSpec/JavaNativeBinding.hs": "8abbae78959a5cb61b7c6ae1c9071e43d10e26c2f421766d431fa18031f87173",
208
- "src/LawSpec/JavaProperties.hs": "ecc1c94bb212d214f1eb8b2897204b65390aad7be858afd8e118e71e28d46bf2",
210
+ "src/LawSpec/JavaProperties.hs": "d953771df667aa193b110dbc898b0936b9ff5a32f6ebc9d2ff25d44a6cd4dfec",
209
211
  "src/LawSpec/JavaTestHelpers.hs": "e6f4b9bbb7e5f07ece6ab1845e12aa661e62e8b926680b11454223fc3ec1523d",
210
212
  "src/LawSpec/KotlinData.hs": "7785fc0c091e50010bce3002355f01c1d237fce05bca6a7ce6de6d207737e228",
211
213
  "src/LawSpec/KotlinDefinitions.hs": "97d7d407d4bbdb4ce16035d607dbbcb8e3d6f5db513f2123a2c269e3dc6d2b38",
212
214
  "src/LawSpec/KotlinExpr.hs": "20312f0774f1d0832a21a19b93c8103e2791ecd32a827d8e9c4f2b4830a996a0",
213
215
  "src/LawSpec/KotlinNativeBinding.hs": "1aec7854758f07a4d2288de13931fbb88ef32220f85d3c84fe8233ad67b6d095",
214
- "src/LawSpec/KotlinProperties.hs": "9d71af0bd05db95ab4cbd80210730c126f235d5b16dc07ca76c6230119b788c6",
216
+ "src/LawSpec/KotlinProperties.hs": "ee8c68bc5ee10a601a643356edb1e0a0c50d6e72a4520feaccb716c6f02ac99c",
215
217
  "src/LawSpec/KotlinTestHelpers.hs": "74810d65faafc6ed4d64b1fb0596cda4186e586b7144a7441641b798795bc324",
216
218
  "src/LawSpec/Model.hs": "1d7ceb7bd66e3d496eef89eaa86403e6896472317ca9a4be86e0c933a7cce403",
217
219
  "src/LawSpec/NativeBinding.hs": "33959b0fd432ad4150f3b685f6438e284b801c2a59d9ef3132486a1cc7c8925a",
218
220
  "src/LawSpec/NativeRequest.hs": "fb2dc88f42424ca520533700eab68c407a5de83807d1b0d8c976e6c4f3d65212",
219
- "src/LawSpec/Parser.hs": "ad11e8f06371e98f295fae0245edceac8142320f054d0c3cb5600bc72588e17e",
221
+ "src/LawSpec/Parser.hs": "7b11b5cbdc89a2f706623929dbdc61df84e003fac2b48b3bbafee1e2cb87a025",
220
222
  "src/LawSpec/PortableGenerator.hs": "69d457e39f539c77fc7a4c08205e75352bbecd9dfdff031a283a39bf44b22d49",
221
223
  "src/LawSpec/PortableTestHelpers.hs": "8ba4d87183c8c11f6ef1ce21c170127335797154f29fb97ad3429f032a167fd3",
222
224
  "src/LawSpec/Prelude.hs": "b1e155c12f58e7c346551a0c65c5d5af76d045b3f1625977ca2a5d1c97cb8c46",
@@ -224,23 +226,23 @@
224
226
  "src/LawSpec/PythonData.hs": "07e9bb54a1515f3f4744b7b9bb43633cbdbf07b3f9e35efef511753f97e216d5",
225
227
  "src/LawSpec/PythonDefinitions.hs": "bc7b971be97339f2d9e4962ad98100878266176aeccd2a9dfd014e2db9550bd8",
226
228
  "src/LawSpec/PythonExpr.hs": "23d7d24b5fb4efff9cb63d38c02a93d95744ec79b4565daf1412088fc7c4ba59",
227
- "src/LawSpec/PythonNativeBinding.hs": "6b7d7f34ce97fb518aef7413d3f72a3a199c2202f0c25a922d860a21c93acc62",
229
+ "src/LawSpec/PythonNativeBinding.hs": "4e35ff8781062d0f614e45ea45f98038c8d609ab78bd671cb0a239eaa9db1690",
228
230
  "src/LawSpec/PythonTypes.hs": "ef928bfeb4324eb6e53c99cae247cc2fa84816075d81de4589d14abaa326656a",
229
231
  "src/LawSpec/Refinement.hs": "8058a9f7e433b4b00ef0966a9ea2be3c91ae93b591ed58a1ffcb792d316193d3",
230
- "src/LawSpec/RuntimeSources.hs": "27ee0e26827a9f6fe9bdbaf29447980b0fd3289aa8350fda9996d59218718e79",
232
+ "src/LawSpec/RuntimeSources.hs": "22c9f7e4f977b4f548363f494c9d66edeb4d3247c7a1e7e7cad470b1059019be",
231
233
  "src/LawSpec/RustData.hs": "8fda864335ccb5478c39f56cb714d5275edf36fcbdc2d152bbe94960b644ad98",
232
234
  "src/LawSpec/RustDefinitions.hs": "5d0328a8b17e0cb5067f65d23f0f2cd2a96b92e4b1c3d6e22b7cb08255697177",
233
- "src/LawSpec/RustEmit.hs": "e01be4504cde6ebfeae0327a1304e149bd49e2ef5ff96ec0cb9cf718c7041888",
235
+ "src/LawSpec/RustEmit.hs": "0687d8589940e7b39daa953c439009d1416eba7da54e9936d2f5595316744d21",
234
236
  "src/LawSpec/RustExpr.hs": "04b2ef81417b811c7b98a98acdadab4e588ca8757964a63ba8dc5936c36479e6",
235
237
  "src/LawSpec/RustNativeBinding.hs": "9d08348da571ac335e335e48c1ad4a67108f7bca54119f5960480e97a6d1da84",
236
238
  "src/LawSpec/RustNativeGenerators.hs": "a3b1484d9ff0008e4f961aa81d0aa51da57ec0cd7909124e80c98d56aacc8ce9",
237
239
  "src/LawSpec/Scalar.hs": "52e61fec81a337bb5b35e9e6f501446cf0b18bffa6c6af2ddc5aeac2d6e07131",
238
240
  "src/LawSpec/SpecializeDefinitions.hs": "08f2fe674a5e00d1be396d9edab0dd2c948b7beb12fa99f2a6aba00cbdf01e33",
239
- "src/LawSpec/Testing.hs": "db1a427ac2197e447faf2a36eccfe965c58eaa0ba07bc494bf1ba24e4af43ad4",
241
+ "src/LawSpec/Testing.hs": "7b0a2e58bcef24365883b479b6a5af720cc2ac4c217ae23bb771c2621d82aec3",
240
242
  "src/LawSpec/WebData.hs": "919af27be12f63f17762e0c052341ab82dcee5d45951a4fd9c66abead98d1ee0",
241
243
  "src/LawSpec/WebDefinitions.hs": "fd985db70c778c8014b780666ac327018e05260be610fa717b8c5477ba9638b4",
242
244
  "src/LawSpec/WebExpr.hs": "0389dcd5cf7479dd23ef034b1012de6ebd70bf1dc9e3d90981dfb0ba2702596f",
243
- "src/LawSpec/WebNativeBinding.hs": "512c224b8fbe979eac3f490bb035f9371097eb13399bd361c016b397f8c2c777",
245
+ "src/LawSpec/WebNativeBinding.hs": "465251be1a93d24a775aa57955bb9ec612af7364c53c83017da051f44943eda9",
244
246
  "src/LawSpec/WebTypes.hs": "f77ffee1e0c7929964a889fb252c1c5d0977afed94664ffcb0f8770c37b98c70",
245
247
  "stack.yaml": "20ccf4d599e355e60b7aa4f814a7cd4299fe2048616cc2e6dbdc22a7bd8cec73",
246
248
  "stack.yaml.lock": "ae222b9c81af920c56e50fa4596fa57786e7fa5a7b461390362b2b2ff63818c7",
@@ -248,8 +250,8 @@
248
250
  "wasm/app/Exports.hs": "4ecbdac8faa2449e6fc61b93c82e6278fa14374154f29b4dacf0eef433f06a47",
249
251
  "wasm/cabal.project": "021e560afdc5eb4cb7169e7119ecb8f92c9ee170245af909c94b612941bff5cc",
250
252
  "wasm/cabal.project.freeze": "733dbed3d2ecccb26e874fd58f136296dad772184deb1658c196b5d54a0814dc",
251
- "wasm/lawspec-wasm.cabal": "3915e30a899fa1aaaa811c225965bd8b39a25bc639299ae6077587cd09020688",
252
- "npm/core.wasm": "6424553b1a5df71da4f7bb54acdd09fd15d6335954df76035e0015de18124faf",
253
+ "wasm/lawspec-wasm.cabal": "01198a858a300d47ea5a9d766523ab577092add5139d09333dbe6fbbf1834c5b",
254
+ "npm/core.wasm": "ecf8b4ee304838dd5ea7d9b708dd6e4c2572f76d60e3ac346b3c740b8f0318e7",
253
255
  "npm/core_jsffi.js": "88d136efe92f7cff5758c8fec8d9b6bbc9707fe37741cebeeb415fe34ce3d72b",
254
256
  "npm/api.mjs": "e93251db289ff0609abaff78ad67f0a9af82508c1141c6a999604f67c56b6e7d",
255
257
  "npm/index.d.ts": "2674f8830bd20a2b9ef7dfa99e183598125b6bcc17e8d454e8f7cca1bbd2c9fe"
package/core.wasm CHANGED
Binary file
@@ -0,0 +1,59 @@
1
+ unit example.indexed
2
+
3
+ -- A list indexed by its length. Each constructor states how it changes the
4
+ -- index; nOfVec is the checked measure that recomputes it from a value.
5
+ type Vec (n :: Natural) (a :: Type) is
6
+ | VNil where n = 0
7
+ | VCons head :: a tail :: Vec m a where n = m + 1
8
+ end
9
+
10
+ -- A binary tree indexed by its number of values.
11
+ type Tree (n :: Natural) (a :: Type) is
12
+ | Tip where n = 0
13
+ | Bin left :: Tree l a value :: a right :: Tree r a where n = l + r + 1
14
+ end
15
+
16
+ -- Result indices are checked contracts over the native implementations.
17
+ replicate :: (n :: Natural where n < 64) -> (x :: Int8) -> (r :: Vec n Int8)
18
+ append :: (xs :: Vec n Int8) -> (ys :: Vec m Int8) -> (r :: Vec (n + m) Int8)
19
+ zip :: (xs :: Vec n Int8) -> (ys :: Vec n Bool) -> (r :: Vec n Bool)
20
+ flatten :: (tree :: Tree n Int8) -> (r :: Vec n Int8)
21
+
22
+ law `replicate has the requested length` is
23
+ definition is
24
+ `for all` (n :: Natural where n < 16) (x :: Int8) . nOfVec (replicate n x) = n
25
+ end
26
+ example `three copies` is
27
+ n = 3
28
+ x = 7
29
+ expect nOfVec (replicate n x) = 3
30
+ end
31
+ end
32
+
33
+ law `append adds lengths` is
34
+ definition is
35
+ `for all` (xs :: Vec n Int8) (ys :: Vec m Int8) .
36
+ nOfVec (append xs ys) = n + m
37
+ end
38
+ end
39
+
40
+ -- A fixed index is generated directly rather than filtered.
41
+ law `vectors of three elements` is
42
+ definition is
43
+ `for all` (xs :: Vec 3 Int8) . nOfVec (append xs xs) = 6
44
+ end
45
+ end
46
+
47
+ -- A shared index constrains the second input by the first.
48
+ law `zip preserves a shared length` is
49
+ definition is
50
+ `for all` (xs :: Vec n Int8) (ys :: Vec n Bool) . nOfVec (zip xs ys) = n
51
+ end
52
+ end
53
+
54
+ -- Generation splits the remaining index across both subtrees.
55
+ law `flattening five values` is
56
+ definition is
57
+ `for all` (tree :: Tree 5 Int8) . nOfVec (flatten tree) = 5
58
+ end
59
+ end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lawspec",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "State the law once. Check it everywhere.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -8,6 +8,6 @@
8
8
  "bin": { "lawspec": "bin/lawspec.mjs" },
9
9
  "exports": { ".": { "types": "./index.d.ts", "import": "./api.mjs" } },
10
10
  "types": "index.d.ts",
11
- "files": ["*.mjs", "*.json", "index.d.ts", "bin", "core.wasm", "core_jsffi.js", "README.md", "NATIVE-BINDINGS.md", "PRIMITIVES.md", "REFINEMENTS.md", "RUST.md", "JAVA.md", "PYTHON.md", "GO.md", "HASKELL.md", "KOTLIN.md", "WEB.md", "LANGUAGE.md", "API-MIGRATION.md", "RELEASE-0.9.md", "RELEASE-0.10.md", "LICENSE", "starter.lawspec", "examples"],
11
+ "files": ["*.mjs", "*.json", "index.d.ts", "bin", "core.wasm", "core_jsffi.js", "README.md", "NATIVE-BINDINGS.md", "PRIMITIVES.md", "REFINEMENTS.md", "RUST.md", "JAVA.md", "PYTHON.md", "GO.md", "HASKELL.md", "KOTLIN.md", "WEB.md", "LANGUAGE.md", "API-MIGRATION.md", "RELEASE-0.9.md", "RELEASE-0.10.md", "RELEASE-0.11.md", "LICENSE", "starter.lawspec", "examples"],
12
12
  "scripts": { "test": "node --test test/*.test.mjs" }
13
13
  }