lawspec 0.9.0 → 0.10.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.
Files changed (42) hide show
  1. package/API-MIGRATION.md +78 -2
  2. package/LANGUAGE.md +10 -7
  3. package/NATIVE-BINDINGS.md +1046 -0
  4. package/PRIMITIVES.md +1 -1
  5. package/README.md +73 -23
  6. package/REFINEMENTS.md +1 -1
  7. package/RELEASE-0.10.md +60 -0
  8. package/api.mjs +23 -3
  9. package/bin/lawspec.mjs +17 -4
  10. package/build.json +56 -36
  11. package/core.wasm +0 -0
  12. package/examples/native-payments/go/example/payments/domain.go +38 -0
  13. package/examples/native-payments/go/example/payments/native_generators_test.go +13 -0
  14. package/examples/native-payments/go/lawspec.json +136 -0
  15. package/examples/native-payments/haskell/lawspec.json +149 -0
  16. package/examples/native-payments/haskell/src/PaymentsDomain.hs +21 -0
  17. package/examples/native-payments/haskell/test/PaymentGenerators.hs +12 -0
  18. package/examples/native-payments/java/lawspec.json +165 -0
  19. package/examples/native-payments/java/src/main/java/domain/PaymentsDomain.java +32 -0
  20. package/examples/native-payments/java/src/test/java/domain/PaymentGenerators.java +16 -0
  21. package/examples/native-payments/javascript/lawspec.json +149 -0
  22. package/examples/native-payments/javascript/src/payments_domain.mjs +44 -0
  23. package/examples/native-payments/javascript/test/lawspec_generators.mjs +6 -0
  24. package/examples/native-payments/kotlin/lawspec.json +165 -0
  25. package/examples/native-payments/kotlin/src/main/kotlin/domain/PaymentsDomain.kt +17 -0
  26. package/examples/native-payments/kotlin/src/test/kotlin/domain/PaymentGenerators.kt +12 -0
  27. package/examples/native-payments/python/lawspec.json +149 -0
  28. package/examples/native-payments/python/src/payments_domain.py +60 -0
  29. package/examples/native-payments/python/tests/lawspec_generators.py +15 -0
  30. package/examples/native-payments/rust/lawspec.json +167 -0
  31. package/examples/native-payments/rust/src/domain.rs +37 -0
  32. package/examples/native-payments/rust/src/lib.rs +2 -0
  33. package/examples/native-payments/rust/tests/support/lawspec_generators.rs +11 -0
  34. package/examples/native-payments/typescript/lawspec.json +149 -0
  35. package/examples/native-payments/typescript/src/payments_domain.ts +42 -0
  36. package/examples/native-payments/typescript/test/lawspec_generators.ts +8 -0
  37. package/examples/specs/payments.lawspec +76 -0
  38. package/examples-command.mjs +5 -0
  39. package/files.mjs +6 -6
  40. package/index.d.ts +53 -2
  41. package/native-examples.mjs +81 -0
  42. package/package.json +2 -2
@@ -0,0 +1,149 @@
1
+ {
2
+ "version": 1,
3
+ "sources": [
4
+ "laws"
5
+ ],
6
+ "machineBits": 64,
7
+ "targets": [
8
+ {
9
+ "language": "javascript",
10
+ "root": ".",
11
+ "nativeBindings": {
12
+ "types": [
13
+ {
14
+ "type": "example.payments::type::Currency",
15
+ "native": [
16
+ "payments_domain",
17
+ "CurrencyCode"
18
+ ],
19
+ "constructors": [
20
+ {
21
+ "constructor": "USD",
22
+ "native": [
23
+ "payments_domain",
24
+ "Dollars"
25
+ ],
26
+ "style": "unit",
27
+ "fields": []
28
+ },
29
+ {
30
+ "constructor": "EUR",
31
+ "native": [
32
+ "payments_domain",
33
+ "Euros"
34
+ ],
35
+ "style": "unit",
36
+ "fields": []
37
+ },
38
+ {
39
+ "constructor": "GBP",
40
+ "native": [
41
+ "payments_domain",
42
+ "Pounds"
43
+ ],
44
+ "style": "unit",
45
+ "fields": []
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "type": "example.payments::type::Money",
51
+ "native": [
52
+ "payments_domain",
53
+ "Price"
54
+ ],
55
+ "constructors": [
56
+ {
57
+ "constructor": "Money",
58
+ "native": [
59
+ "payments_domain",
60
+ "Price"
61
+ ],
62
+ "style": "record",
63
+ "fields": [
64
+ {
65
+ "field": "amount",
66
+ "native": "major"
67
+ },
68
+ {
69
+ "field": "currency",
70
+ "native": "unit"
71
+ }
72
+ ]
73
+ }
74
+ ]
75
+ },
76
+ {
77
+ "type": "example.payments::type::Payment",
78
+ "native": [
79
+ "payments_domain",
80
+ "PaymentStatus"
81
+ ],
82
+ "constructors": [
83
+ {
84
+ "constructor": "Paid",
85
+ "native": [
86
+ "payments_domain",
87
+ "Settled"
88
+ ],
89
+ "style": "variant",
90
+ "fields": [
91
+ {
92
+ "field": "value",
93
+ "native": "price"
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ "constructor": "Declined",
99
+ "native": [
100
+ "payments_domain",
101
+ "Rejected"
102
+ ],
103
+ "style": "variant",
104
+ "fields": [
105
+ {
106
+ "field": "reason",
107
+ "native": "explanation"
108
+ }
109
+ ]
110
+ }
111
+ ]
112
+ }
113
+ ],
114
+ "functions": [
115
+ {
116
+ "declaration": "example.payments::addFee",
117
+ "native": [
118
+ "payments_domain",
119
+ "apply_fee"
120
+ ]
121
+ },
122
+ {
123
+ "declaration": "example.payments::roundTrip",
124
+ "native": [
125
+ "payments_domain",
126
+ "restore"
127
+ ]
128
+ },
129
+ {
130
+ "declaration": "example.payments::archive",
131
+ "native": [
132
+ "payments_domain",
133
+ "store"
134
+ ]
135
+ }
136
+ ],
137
+ "generators": [
138
+ {
139
+ "type": "example.payments::type::Money",
140
+ "factory": [
141
+ "lawspec_generators",
142
+ "prices"
143
+ ]
144
+ }
145
+ ]
146
+ }
147
+ }
148
+ ]
149
+ }
@@ -0,0 +1,44 @@
1
+ // Application-owned domain types; no generated domain declarations.
2
+ import * as ls from './lawspec_runtime.mjs';
3
+ export class CurrencyCode {
4
+ }
5
+ export class Dollars extends CurrencyCode {
6
+ }
7
+ export class Euros extends CurrencyCode {
8
+ }
9
+ export class Pounds extends CurrencyCode {
10
+ }
11
+ export class Price {
12
+ major;
13
+ unit;
14
+ constructor({ unit, major }) {
15
+ this.unit = unit;
16
+ this.major = major;
17
+ }
18
+ }
19
+ export class PaymentStatus {
20
+ }
21
+ export class Settled extends PaymentStatus {
22
+ price;
23
+ constructor({ price }) {
24
+ super();
25
+ this.price = price;
26
+ }
27
+ }
28
+ export class Rejected extends PaymentStatus {
29
+ explanation;
30
+ constructor({ explanation }) {
31
+ super();
32
+ this.explanation = explanation;
33
+ }
34
+ }
35
+ export function apply_fee(price) {
36
+ const major = ls.binary('+', price.major, new ls.Decimal(2n, -1n), 'Decimal', 'Decimal');
37
+ return new Price({ major, unit: price.unit });
38
+ }
39
+ export function restore(payment) {
40
+ return payment;
41
+ }
42
+ export function store(payments) {
43
+ return payments;
44
+ }
@@ -0,0 +1,6 @@
1
+ import * as fc from 'fast-check';
2
+ import * as ls from '../src/lawspec_runtime.mjs';
3
+ import { Price, Euros } from '../src/payments_domain.mjs';
4
+ export function prices() {
5
+ return fc.integer({ min: 100, max: 200 }).map(cents => new Price({ major: new ls.Decimal(BigInt(cents), -2n), unit: new Euros() }));
6
+ }
@@ -0,0 +1,165 @@
1
+ {
2
+ "version": 1,
3
+ "sources": [
4
+ "laws"
5
+ ],
6
+ "machineBits": 64,
7
+ "targets": [
8
+ {
9
+ "language": "kotlin",
10
+ "root": ".",
11
+ "nativeBindings": {
12
+ "types": [
13
+ {
14
+ "type": "example.payments::type::Currency",
15
+ "native": [
16
+ "domain",
17
+ "PaymentsDomain",
18
+ "CurrencyCode"
19
+ ],
20
+ "constructors": [
21
+ {
22
+ "constructor": "USD",
23
+ "native": [
24
+ "domain",
25
+ "PaymentsDomain",
26
+ "CurrencyCode",
27
+ "Dollars"
28
+ ],
29
+ "style": "unit",
30
+ "fields": []
31
+ },
32
+ {
33
+ "constructor": "EUR",
34
+ "native": [
35
+ "domain",
36
+ "PaymentsDomain",
37
+ "CurrencyCode",
38
+ "Euros"
39
+ ],
40
+ "style": "unit",
41
+ "fields": []
42
+ },
43
+ {
44
+ "constructor": "GBP",
45
+ "native": [
46
+ "domain",
47
+ "PaymentsDomain",
48
+ "CurrencyCode",
49
+ "Pounds"
50
+ ],
51
+ "style": "unit",
52
+ "fields": []
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "type": "example.payments::type::Money",
58
+ "native": [
59
+ "domain",
60
+ "PaymentsDomain",
61
+ "Price"
62
+ ],
63
+ "constructors": [
64
+ {
65
+ "constructor": "Money",
66
+ "native": [
67
+ "domain",
68
+ "PaymentsDomain",
69
+ "Price"
70
+ ],
71
+ "style": "record",
72
+ "fields": [
73
+ {
74
+ "field": "amount",
75
+ "native": "major"
76
+ },
77
+ {
78
+ "field": "currency",
79
+ "native": "unit"
80
+ }
81
+ ]
82
+ }
83
+ ]
84
+ },
85
+ {
86
+ "type": "example.payments::type::Payment",
87
+ "native": [
88
+ "domain",
89
+ "PaymentsDomain",
90
+ "PaymentStatus"
91
+ ],
92
+ "constructors": [
93
+ {
94
+ "constructor": "Paid",
95
+ "native": [
96
+ "domain",
97
+ "PaymentsDomain",
98
+ "Settled"
99
+ ],
100
+ "style": "variant",
101
+ "fields": [
102
+ {
103
+ "field": "value",
104
+ "native": "price"
105
+ }
106
+ ]
107
+ },
108
+ {
109
+ "constructor": "Declined",
110
+ "native": [
111
+ "domain",
112
+ "PaymentsDomain",
113
+ "Rejected"
114
+ ],
115
+ "style": "variant",
116
+ "fields": [
117
+ {
118
+ "field": "reason",
119
+ "native": "explanation"
120
+ }
121
+ ]
122
+ }
123
+ ]
124
+ }
125
+ ],
126
+ "functions": [
127
+ {
128
+ "declaration": "example.payments::addFee",
129
+ "native": [
130
+ "domain",
131
+ "PaymentsDomain",
132
+ "apply_fee"
133
+ ]
134
+ },
135
+ {
136
+ "declaration": "example.payments::roundTrip",
137
+ "native": [
138
+ "domain",
139
+ "PaymentsDomain",
140
+ "restore"
141
+ ]
142
+ },
143
+ {
144
+ "declaration": "example.payments::archive",
145
+ "native": [
146
+ "domain",
147
+ "PaymentsDomain",
148
+ "store"
149
+ ]
150
+ }
151
+ ],
152
+ "generators": [
153
+ {
154
+ "type": "example.payments::type::Money",
155
+ "factory": [
156
+ "domain",
157
+ "PaymentGenerators",
158
+ "prices"
159
+ ]
160
+ }
161
+ ]
162
+ }
163
+ }
164
+ ]
165
+ }
@@ -0,0 +1,17 @@
1
+ package domain
2
+
3
+ import java.math.BigDecimal
4
+ import lawspec.runtime.LawSpecRuntime.Maybe
5
+
6
+ // Application-owned types; no generated LawSpec domain declarations.
7
+ object PaymentsDomain {
8
+ enum class CurrencyCode { Dollars, Euros, Pounds }
9
+ data class Price(val major: BigDecimal, val unit: CurrencyCode)
10
+ sealed interface PaymentStatus
11
+ data class Settled(val price: Price) : PaymentStatus
12
+ data class Rejected(val explanation: String) : PaymentStatus
13
+
14
+ fun apply_fee(price: Price) = Price(price.major + BigDecimal("0.2"), price.unit)
15
+ fun restore(payment: PaymentStatus) = payment
16
+ fun store(payments: List<Maybe<PaymentStatus>>) = payments
17
+ }
@@ -0,0 +1,12 @@
1
+ package domain
2
+
3
+ import io.kotest.property.Arb
4
+ import io.kotest.property.arbitrary.int
5
+ import io.kotest.property.arbitrary.map
6
+ import java.math.BigDecimal
7
+
8
+ object PaymentGenerators {
9
+ fun prices(): Arb<PaymentsDomain.Price> = Arb.int(100..200).map { cents ->
10
+ PaymentsDomain.Price(BigDecimal.valueOf(cents.toLong(), 2), PaymentsDomain.CurrencyCode.Euros)
11
+ }
12
+ }
@@ -0,0 +1,149 @@
1
+ {
2
+ "version": 1,
3
+ "sources": [
4
+ "laws"
5
+ ],
6
+ "machineBits": 64,
7
+ "targets": [
8
+ {
9
+ "language": "python",
10
+ "root": ".",
11
+ "nativeBindings": {
12
+ "types": [
13
+ {
14
+ "type": "example.payments::type::Currency",
15
+ "native": [
16
+ "payments_domain",
17
+ "CurrencyCode"
18
+ ],
19
+ "constructors": [
20
+ {
21
+ "constructor": "USD",
22
+ "native": [
23
+ "payments_domain",
24
+ "Dollars"
25
+ ],
26
+ "style": "unit",
27
+ "fields": []
28
+ },
29
+ {
30
+ "constructor": "EUR",
31
+ "native": [
32
+ "payments_domain",
33
+ "Euros"
34
+ ],
35
+ "style": "unit",
36
+ "fields": []
37
+ },
38
+ {
39
+ "constructor": "GBP",
40
+ "native": [
41
+ "payments_domain",
42
+ "Pounds"
43
+ ],
44
+ "style": "unit",
45
+ "fields": []
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "type": "example.payments::type::Money",
51
+ "native": [
52
+ "payments_domain",
53
+ "Price"
54
+ ],
55
+ "constructors": [
56
+ {
57
+ "constructor": "Money",
58
+ "native": [
59
+ "payments_domain",
60
+ "Price"
61
+ ],
62
+ "style": "record",
63
+ "fields": [
64
+ {
65
+ "field": "amount",
66
+ "native": "major"
67
+ },
68
+ {
69
+ "field": "currency",
70
+ "native": "unit"
71
+ }
72
+ ]
73
+ }
74
+ ]
75
+ },
76
+ {
77
+ "type": "example.payments::type::Payment",
78
+ "native": [
79
+ "payments_domain",
80
+ "PaymentStatus"
81
+ ],
82
+ "constructors": [
83
+ {
84
+ "constructor": "Paid",
85
+ "native": [
86
+ "payments_domain",
87
+ "Settled"
88
+ ],
89
+ "style": "variant",
90
+ "fields": [
91
+ {
92
+ "field": "value",
93
+ "native": "price"
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ "constructor": "Declined",
99
+ "native": [
100
+ "payments_domain",
101
+ "Rejected"
102
+ ],
103
+ "style": "variant",
104
+ "fields": [
105
+ {
106
+ "field": "reason",
107
+ "native": "explanation"
108
+ }
109
+ ]
110
+ }
111
+ ]
112
+ }
113
+ ],
114
+ "functions": [
115
+ {
116
+ "declaration": "example.payments::addFee",
117
+ "native": [
118
+ "payments_domain",
119
+ "apply_fee"
120
+ ]
121
+ },
122
+ {
123
+ "declaration": "example.payments::roundTrip",
124
+ "native": [
125
+ "payments_domain",
126
+ "restore"
127
+ ]
128
+ },
129
+ {
130
+ "declaration": "example.payments::archive",
131
+ "native": [
132
+ "payments_domain",
133
+ "store"
134
+ ]
135
+ }
136
+ ],
137
+ "generators": [
138
+ {
139
+ "type": "example.payments::type::Money",
140
+ "factory": [
141
+ "lawspec_generators",
142
+ "prices"
143
+ ]
144
+ }
145
+ ]
146
+ }
147
+ }
148
+ ]
149
+ }
@@ -0,0 +1,60 @@
1
+ """Application-owned payment types; no generated domain declarations."""
2
+
3
+ from dataclasses import dataclass
4
+ from decimal import Decimal
5
+ from fractions import Fraction
6
+
7
+ import lawspec_runtime as ls
8
+
9
+
10
+ class CurrencyCode:
11
+ pass
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class Dollars(CurrencyCode):
16
+ pass
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class Euros(CurrencyCode):
21
+ pass
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class Pounds(CurrencyCode):
26
+ pass
27
+
28
+
29
+ @dataclass(frozen=True, kw_only=True)
30
+ class Price:
31
+ # Order differs deliberately; bridges must use the mapped field names.
32
+ unit: CurrencyCode
33
+ major: Decimal
34
+
35
+
36
+ class PaymentStatus:
37
+ pass
38
+
39
+
40
+ @dataclass(frozen=True, kw_only=True)
41
+ class Settled(PaymentStatus):
42
+ price: Price
43
+
44
+
45
+ @dataclass(frozen=True, kw_only=True)
46
+ class Rejected(PaymentStatus):
47
+ explanation: str
48
+
49
+
50
+ def apply_fee(price):
51
+ amount = ls.finite_decimal(Fraction(price.major) + Fraction(1, 5))
52
+ return Price(major=amount, unit=price.unit)
53
+
54
+
55
+ def restore(payment):
56
+ return payment
57
+
58
+
59
+ def store(payments):
60
+ return payments
@@ -0,0 +1,15 @@
1
+ """Application strategies retain ordinary Hypothesis shrinking."""
2
+
3
+ from decimal import Decimal
4
+
5
+ from hypothesis import strategies as st
6
+
7
+ from payments_domain import Euros, Price
8
+
9
+
10
+ def prices():
11
+ def price(cents):
12
+ digits = tuple(map(int, str(cents)))
13
+ return Price(major=Decimal((0, digits, -2)), unit=Euros())
14
+
15
+ return st.integers(min_value=100, max_value=200).map(price)