selaws 0.0.0-stage → 0.1.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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +355 -2
  3. package/dist/evidence.d.ts +45 -0
  4. package/dist/evidence.d.ts.map +1 -0
  5. package/dist/evidence.js +22 -0
  6. package/dist/evidence.js.map +1 -0
  7. package/dist/identity.d.ts +52 -0
  8. package/dist/identity.d.ts.map +1 -0
  9. package/dist/identity.js +22 -0
  10. package/dist/identity.js.map +1 -0
  11. package/dist/index.d.ts +35 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +18 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/internal/callback.d.ts +13 -0
  16. package/dist/internal/callback.d.ts.map +1 -0
  17. package/dist/internal/callback.js +2 -0
  18. package/dist/internal/callback.js.map +1 -0
  19. package/dist/internal/promise-like.d.ts +8 -0
  20. package/dist/internal/promise-like.d.ts.map +1 -0
  21. package/dist/internal/promise-like.js +12 -0
  22. package/dist/internal/promise-like.js.map +1 -0
  23. package/dist/internal/scalar.d.ts +18 -0
  24. package/dist/internal/scalar.d.ts.map +1 -0
  25. package/dist/internal/scalar.js +6 -0
  26. package/dist/internal/scalar.js.map +1 -0
  27. package/dist/option.d.ts +90 -0
  28. package/dist/option.d.ts.map +1 -0
  29. package/dist/option.js +96 -0
  30. package/dist/option.js.map +1 -0
  31. package/dist/protocol.d.ts +42 -0
  32. package/dist/protocol.d.ts.map +1 -0
  33. package/dist/protocol.js +67 -0
  34. package/dist/protocol.js.map +1 -0
  35. package/dist/result/capture.d.ts +42 -0
  36. package/dist/result/capture.d.ts.map +1 -0
  37. package/dist/result/capture.js +41 -0
  38. package/dist/result/capture.js.map +1 -0
  39. package/dist/result/core.d.ts +98 -0
  40. package/dist/result/core.d.ts.map +1 -0
  41. package/dist/result/core.js +103 -0
  42. package/dist/result/core.js.map +1 -0
  43. package/dist/result/index.d.ts +4 -0
  44. package/dist/result/index.d.ts.map +1 -0
  45. package/dist/result/index.js +4 -0
  46. package/dist/result/index.js.map +1 -0
  47. package/dist/result/throw.d.ts +4 -0
  48. package/dist/result/throw.d.ts.map +1 -0
  49. package/dist/result/throw.js +8 -0
  50. package/dist/result/throw.js.map +1 -0
  51. package/dist/validation.d.ts +126 -0
  52. package/dist/validation.d.ts.map +1 -0
  53. package/dist/validation.js +240 -0
  54. package/dist/validation.js.map +1 -0
  55. package/dist/variant.d.ts +97 -0
  56. package/dist/variant.d.ts.map +1 -0
  57. package/dist/variant.js +102 -0
  58. package/dist/variant.js.map +1 -0
  59. package/docs/API.md +543 -0
  60. package/docs/GUIDE.md +739 -0
  61. package/docs/SEMANTICS.md +319 -0
  62. package/docs/laws/evidence.md +113 -0
  63. package/docs/laws/identity.md +126 -0
  64. package/docs/laws/match.md +163 -0
  65. package/docs/laws/option.md +100 -0
  66. package/docs/laws/protocol.md +152 -0
  67. package/docs/laws/result.md +124 -0
  68. package/docs/laws/validation.md +111 -0
  69. package/docs/laws/variant.md +251 -0
  70. package/package.json +87 -3
  71. package/src/evidence.ts +120 -0
  72. package/src/identity.ts +129 -0
  73. package/src/index.ts +54 -0
  74. package/src/internal/callback.ts +54 -0
  75. package/src/internal/promise-like.ts +32 -0
  76. package/src/internal/scalar.ts +43 -0
  77. package/src/option.ts +214 -0
  78. package/src/protocol.ts +174 -0
  79. package/src/result/capture.ts +209 -0
  80. package/src/result/core.ts +229 -0
  81. package/src/result/index.ts +3 -0
  82. package/src/result/throw.ts +13 -0
  83. package/src/validation.ts +491 -0
  84. package/src/variant.ts +363 -0
@@ -0,0 +1,319 @@
1
+ # Selaws semantics
2
+
3
+ Selaws is one distribution package containing independent semantic owners.
4
+ Package co-location does not merge their laws.
5
+
6
+ | Owner | Question |
7
+ | --- | --- |
8
+ | Identity | What scalar value is this? |
9
+ | Evidence | What stable fact is established about this scalar? |
10
+ | Protocol | Which labeled state transitions are admissible? |
11
+ | Variant | Which closed labeled alternative is this, and what payload does it carry? |
12
+ | Option | Is a value present? |
13
+ | Validation | Which independently available checks have issues? |
14
+ | Result | Did a recoverable computation succeed? |
15
+
16
+ The detailed owner contracts are:
17
+
18
+ - [Identity](./laws/identity.md)
19
+ - [Evidence](./laws/evidence.md)
20
+ - [Protocol](./laws/protocol.md)
21
+ - [Variant](./laws/variant.md)
22
+ - [Option](./laws/option.md)
23
+ - [Validation](./laws/validation.md)
24
+ - [Result](./laws/result.md)
25
+
26
+ Cross-owner semantics can also be first-class shared laws without becoming new
27
+ owners. The shared Match contract is [laws/match.md](./laws/match.md).
28
+
29
+ The [Guide](./GUIDE.md) shows application patterns. This file defines the
30
+ shared package laws those patterns must preserve.
31
+
32
+ ## 1. Semantic selection law
33
+
34
+ Choose an owner by the meaning the application needs, not by implementation
35
+ shape.
36
+
37
+ A tagged union does not automatically imply Variant. Option, Validation, and
38
+ Result are also sum-shaped, but their branches carry different laws:
39
+
40
+ ```text
41
+ Variant closed alternative identity + correlated payload
42
+ Option presence / reasonless absence
43
+ Validation independent issue accumulation
44
+ Result recoverable success / fail-fast error
45
+ ```
46
+
47
+ Likewise, a list of state-like strings does not automatically imply Protocol.
48
+ Protocol begins only when the application needs an admissible labeled relation
49
+ between scalar identifiers.
50
+
51
+ Using one owner as a carrier for another owner's meaning does not transfer the
52
+ second owner's laws automatically.
53
+
54
+ ## 2. Ownership law
55
+
56
+ Each primitive owns one dominant meaning. Similar implementation shapes do not
57
+ create a shared semantic owner.
58
+
59
+ Cross-owner operations are named on the target owner because the conversion
60
+ introduces the target meaning:
61
+
62
+ ```text
63
+ Result.fromOption
64
+ Result.fromValidation
65
+
66
+ Validation.fromOption
67
+ Validation.fromResult
68
+ ```
69
+
70
+ The source information preserved by each boundary is part of the target
71
+ owner's law.
72
+
73
+ Protocol is independent of every other semantic owner. It consumes
74
+ application-owned scalar identifiers without importing Identity, Evidence, or
75
+ Variant meaning.
76
+
77
+ Variant owns exact closed labeled alternatives and correlated payload
78
+ formation. Option, Validation, and Result retain their presence, accumulation,
79
+ and recoverable-failure meanings even when their carriers are also tagged
80
+ unions.
81
+
82
+ Identity and Evidence remain independent scalar meanings. Identity says which
83
+ domain scalar this is. Evidence says which stable fact has been established
84
+ about that same scalar.
85
+
86
+ Application code composes owners through ordinary typed values.
87
+
88
+ ## 3. Composition law
89
+
90
+ Owner independence does not prohibit useful composition. It determines which
91
+ owner is responsible for each statement.
92
+
93
+ Examples:
94
+
95
+ ```text
96
+ Identity + Evidence
97
+ UserId carrying a separately established NonEmpty fact
98
+
99
+ Variant inside Result
100
+ Result<User, LoadUserError>
101
+ where LoadUserError is a closed Variant family
102
+
103
+ Validation -> Result
104
+ independent field issues accumulate first,
105
+ then the complete issue collection becomes one Result error
106
+
107
+ Variant label + Protocol
108
+ Variant owns event payload/case identity;
109
+ Protocol can use the scalar event tag as a transition label
110
+
111
+ Promise<Result<T,E>>
112
+ Promise owns scheduling/awaiting;
113
+ Result owns recoverable success/error data
114
+ ```
115
+
116
+ Composition must preserve those boundaries. A convenience helper that makes one
117
+ owner silently perform another owner's job changes the semantic surface and
118
+ requires its own owner justification.
119
+
120
+ ## 4. Match law
121
+
122
+ Match is the shared elimination law for Option, Result, Validation, and Variant.
123
+ It is not another semantic owner and introduces no root value, type, dispatcher,
124
+ or package subpath.
125
+
126
+ The owning carrier determines the complete branch universe and the payload
127
+ associated with each branch. Typed Match requires a handler for every branch in
128
+ that universe even when the current value is already narrowed.
129
+
130
+ For the selected branch, Match:
131
+
132
+ ```text
133
+ invokes exactly one handler exactly once
134
+ does not read or invoke unselected handler properties
135
+ preserves the owner-defined branch payload and arity
136
+ supplies no library-defined this receiver
137
+ returns the selected handler completion unchanged
138
+ ```
139
+
140
+ Therefore a returned Promise remains a native Promise and a thrown handler
141
+ remains abrupt. Match does not capture, await, wrap, or normalize completion.
142
+
143
+ Runtime validation beyond those shared elimination laws remains owner-specific.
144
+ Variant can validate its runtime family representation because Variant owns a
145
+ runtime case declaration. Fixed structural owners retain their own typed
146
+ boundaries.
147
+
148
+ The public grammar remains owner-scoped:
149
+
150
+ ```text
151
+ Option.match
152
+ Result.match
153
+ Validation.match
154
+ VariantFamily.match
155
+ ```
156
+
157
+ A centralized `Match(value, handlers)` could not recover semantic ownership
158
+ from transparent structural data, while `Match(owner, value, handlers)` would
159
+ duplicate the owner and weaken TypeScript inference. Shared law therefore does
160
+ not imply shared dispatch.
161
+
162
+ ## 5. Representation law
163
+
164
+ Identity and Evidence enrich immutable JavaScript scalar values:
165
+
166
+ ```ts
167
+ string | number | bigint | boolean | symbol
168
+ ```
169
+
170
+ Successful formation returns that same primitive representation.
171
+
172
+ Protocol uses those JavaScript scalar kinds as application-owned state and
173
+ label identifiers. `Protocol.define` snapshots readonly
174
+ `[from, label, to]` declarations into a private membership relation. The
175
+ runtime relation is not application state.
176
+
177
+ Variant snapshots a finite case declaration into immutable family constructors
178
+ and elimination behavior. Its values remain transparent tagged JavaScript
179
+ objects:
180
+
181
+ ```ts
182
+ { tag: caseName }
183
+ { tag: caseName, value: payload }
184
+ ```
185
+
186
+ Variant phantom family identity adds no runtime brand.
187
+
188
+ Option, Validation, and Result also use ordinary structural object and array
189
+ data. Their TypeScript `Readonly` contracts describe typed use; runtime values
190
+ remain transparent JavaScript data.
191
+
192
+ Transparent representation is not permission to bypass each owner's formation
193
+ and boundary laws in typed application code.
194
+
195
+ ## 6. Phantom identity law
196
+
197
+ Named identity, evidence, and Variant families use Selaws-owned,
198
+ package-copy-stable structural keys:
199
+
200
+ ```text
201
+ ~selaws.identity:<Name>
202
+ ~selaws.evidence:<Name>
203
+ ~selaws.variant:<Name>
204
+ ```
205
+
206
+ A concrete string name therefore defines a shared structural identity contract.
207
+
208
+ Declaration-owned identity, evidence, and Variant families use a caller-owned
209
+ symbol as the phantom property key. The bound symbol determines declaration
210
+ identity.
211
+
212
+ The category payloads remain distinct. Reusing one symbol for independent
213
+ owners does not make Identity imply Evidence or make either owner become
214
+ Variant.
215
+
216
+ Variant family markers additionally retain the closed case-name universe and
217
+ case signatures needed for typed compatibility. Case-name universes are exact;
218
+ payload positions retain ordinary TypeScript structural variance.
219
+
220
+ These encodings are compatibility law: duplicate compatible Selaws
221
+ installations can agree on the same declared named meaning without sharing a
222
+ package-local unique-symbol brand.
223
+
224
+ ## 7. Completion law
225
+
226
+ Ordinary transformation, recovery, fallback, observation, predicate,
227
+ conversion, and Variant elimination callbacks follow JavaScript completion
228
+ semantics. A thrown callback remains abrupt unless an explicit Result capture
229
+ boundary owns the conversion.
230
+
231
+ Synchronous observation helpers require synchronous completion. Promise-like
232
+ completion is rejected rather than silently discarded. At runtime,
233
+ Promise-like means a non-null object or function with a callable `then`.
234
+
235
+ Result capture helpers are the explicit boundary that maps thrown or rejected
236
+ JavaScript completion into recoverable Result error data.
237
+
238
+ ## 8. Async law
239
+
240
+ Native Promise composition owns scheduling, awaiting, and rejection.
241
+
242
+ ```ts
243
+ Promise<Result<T, E>>
244
+ Promise<Option<T>>
245
+ Promise<Validation<T, E>>
246
+ ```
247
+
248
+ Selaws does not introduce an asynchronous carrier wrapper.
249
+
250
+ Result does not introduce a generator or asynchronous control-flow runtime.
251
+ Dependent async composition remains ordinary JavaScript control flow over
252
+ `Promise<Result<T, E>>` values.
253
+
254
+ Independent asynchronous work may be scheduled with Promise first and then
255
+ combined by the relevant Selaws data owner.
256
+
257
+ ## 9. Information law
258
+
259
+ Owner boundaries preserve information according to the target contract.
260
+
261
+ - Option None gains an error only when converted to Result.
262
+ - Option None gains an issue only when converted to Validation.
263
+ - Invalid's complete non-empty issue collection becomes one Result error value.
264
+ - One Result Err value becomes one Validation issue, even when that value is
265
+ itself an array.
266
+ - Variant payloads remain attached to their declared case; conversion through
267
+ another owner must not erase that correlation unless that boundary explicitly
268
+ defines such a projection.
269
+ - Protocol labels remain part of relation identity even when source and target
270
+ states are otherwise equal.
271
+
272
+ Applications can state a different projection explicitly before or after an
273
+ owner boundary.
274
+
275
+ ## 10. Snapshot law
276
+
277
+ Protocol and Variant both define immutable runtime declarations from
278
+ caller-provided declaration data.
279
+
280
+ `Protocol.define` snapshots transition triples.
281
+
282
+ `Variant.define` snapshots case names and case kinds.
283
+
284
+ Protocol and Variant typed declarations require readonly finite tuples and
285
+ reject directly mutable declaration arrays. This prevents mutation through the
286
+ declaration type itself. TypeScript can still create a readonly view over a
287
+ separately mutable alias and mutate the same backing array before definition;
288
+ that language-level unsound aliasing is governed by the trust model below.
289
+
290
+ At runtime, both owners snapshot caller-provided declaration data. Later
291
+ mutation of caller-owned JavaScript arrays does not change the already-defined
292
+ Protocol or Variant family.
293
+
294
+ Snapshotting the declaration does not freeze application payloads, application
295
+ state, or external storage.
296
+
297
+ ## 11. TypeScript trust model
298
+
299
+ Selaws expresses semantic guarantees for values whose runtime state is still
300
+ described by their ordinary TypeScript type. Formation APIs centralize honest
301
+ introduction of phantom meaning. Assertions, `any`, and TypeScript's unsound mutable aliasing can break that
302
+ relationship; Selaws does not reify erased static types at runtime. A readonly
303
+ view is therefore trusted to describe the current backing value when it crosses
304
+ a Selaws typed boundary.
305
+
306
+ Protocol runtime membership establishes only whether one concrete triple was
307
+ declared. It does not establish authorization, current-state freshness, or an
308
+ atomic state mutation.
309
+
310
+ Variant runtime formation establishes the selected declared case and preserves
311
+ the supplied payload. Runtime payload schema validity still belongs to the
312
+ application boundary that decoded or produced that value.
313
+
314
+ Identity and Evidence predicates can validate their own scalar formation or
315
+ fact condition. They are not general object-schema decoders.
316
+
317
+ Runtime authorization, mutable freshness, revocation, schema decoding,
318
+ normalization, persistence, version negotiation, and resource enforcement
319
+ remain owned by application layers that can establish those facts at runtime.
@@ -0,0 +1,113 @@
1
+ # Evidence law
2
+
3
+ Evidence answers:
4
+
5
+ ```text
6
+ What stable fact is established about this scalar value?
7
+ ```
8
+
9
+ Use Evidence when the application already has the right scalar value and must
10
+ record that a separate predicate has been established without replacing the
11
+ value's identity.
12
+
13
+ ```ts
14
+ const NonEmpty = evidence.string(
15
+ "NonEmpty",
16
+ (value) => value.length > 0,
17
+ );
18
+
19
+ const checked = NonEmpty(userId);
20
+ ```
21
+
22
+ Evidence applies to immutable scalar carriers, so aliasing cannot mutate the
23
+ underlying value after the fact is established.
24
+
25
+ ## Named evidence
26
+
27
+ ```ts
28
+ import { evidence } from "selaws/evidence";
29
+
30
+ const NonEmpty = evidence.string(
31
+ "NonEmpty",
32
+ (value) => value.length > 0,
33
+ );
34
+ ```
35
+
36
+ A concrete literal name maps to
37
+ `~selaws.evidence:<Name>`. This Selaws-owned key keeps compatible producers
38
+ and duplicate Selaws installations structurally compatible.
39
+
40
+ Every Evidence factory has a predicate because evidence is introduced only
41
+ after the fact is established.
42
+
43
+ ## Declaration-owned evidence
44
+
45
+ ```ts
46
+ import {
47
+ defineFact,
48
+ type Evidence,
49
+ } from "selaws/evidence";
50
+
51
+ const nonEmptyKey: unique symbol =
52
+ Symbol("NonEmpty");
53
+
54
+ type NonEmpty<T extends string> =
55
+ Evidence<T, typeof nonEmptyKey>;
56
+
57
+ const NonEmpty = defineFact<string>()(
58
+ nonEmptyKey,
59
+ (establish) => ({
60
+ check<T extends string>(value: T) {
61
+ return value.length > 0
62
+ ? establish(value)
63
+ : undefined;
64
+ },
65
+ }),
66
+ );
67
+ ```
68
+
69
+ A non-empty finite set of narrow symbol fact keys represents accumulated
70
+ declaration-owned evidence.
71
+
72
+ `defineFact` keeps the establishment operation private to the declaration
73
+ callback so the declaring module owns how the fact becomes available.
74
+
75
+ ## Composition
76
+
77
+ Establishing evidence preserves identity and earlier evidence on the same
78
+ scalar.
79
+
80
+ ```text
81
+ UserId
82
+ + NonEmpty
83
+ + Ascii
84
+ ```
85
+
86
+ Identity and Evidence use distinct phantom categories. Reusing one symbol for
87
+ both categories does not make identity imply evidence.
88
+
89
+ `evidence.Proven<typeof Fact, Value>` expresses a named or declaration-owned
90
+ fact on an existing compatible scalar type when inference alone is not
91
+ sufficient.
92
+
93
+ ## Transformation
94
+
95
+ Evidence applies to the scalar value that was established.
96
+
97
+ ```ts
98
+ const checked = NonEmpty(userId);
99
+
100
+ if (checked !== undefined) {
101
+ const trimmed = checked.trim();
102
+
103
+ const checkedTrimmed =
104
+ NonEmpty(trimmed);
105
+ }
106
+ ```
107
+
108
+ An operation such as `.trim()` produces another scalar value. Relevant
109
+ evidence is established again on that new value.
110
+
111
+ Evidence does not claim mutable freshness, authorization, external provenance,
112
+ or facts about aggregate objects. Those meanings remain with owners that can
113
+ establish them.
@@ -0,0 +1,126 @@
1
+ # Identity law
2
+
3
+ Identity answers:
4
+
5
+ ```text
6
+ What scalar value is this?
7
+ ```
8
+
9
+ Use Identity when two values can have the same JavaScript scalar
10
+ representation but belong to different application domains.
11
+
12
+ ```ts
13
+ const UserId = identity.string("UserId");
14
+ const OrderId = identity.string("OrderId");
15
+
16
+ type UserId = identity.Value<typeof UserId>;
17
+ type OrderId = identity.Value<typeof OrderId>;
18
+ ```
19
+
20
+ A `UserId` and an `OrderId` may both be strings at runtime while remaining
21
+ distinct typed meanings.
22
+
23
+ ## Carrier
24
+
25
+ Identity applies to immutable scalar carriers:
26
+
27
+ ```ts
28
+ string | number | bigint | boolean | symbol
29
+ ```
30
+
31
+ Formation preserves the original runtime primitive. Identity does not wrap the
32
+ value in an object.
33
+
34
+ ## Named identity
35
+
36
+ ```ts
37
+ import { identity } from "selaws/identity";
38
+
39
+ const UserId = identity.string("UserId");
40
+ type UserId = identity.Value<typeof UserId>;
41
+
42
+ const id = UserId("u_1");
43
+ ```
44
+
45
+ A concrete literal name maps to the Selaws-owned structural phantom key
46
+ `~selaws.identity:<Name>`.
47
+
48
+ ```text
49
+ same carrier + same literal name => same named identity
50
+ different literal names => different named identities
51
+ ```
52
+
53
+ The name must denote one concrete identity. Broad strings, unions, patterns,
54
+ and branded string spaces do not satisfy that requirement.
55
+
56
+ Named identity is appropriate when compatible producers intentionally share the
57
+ same structural identity contract.
58
+
59
+ ## Declaration-owned identity
60
+
61
+ ```ts
62
+ import {
63
+ defineIdentity,
64
+ type Identity,
65
+ } from "selaws/identity";
66
+
67
+ const userIdKey: unique symbol =
68
+ Symbol("UserId");
69
+
70
+ type UserId =
71
+ Identity<string, typeof userIdKey>;
72
+
73
+ const UserId = defineIdentity<string>()(
74
+ userIdKey,
75
+ (mint) => ({
76
+ fromString(value: string): UserId {
77
+ return mint(value);
78
+ },
79
+ }),
80
+ );
81
+ ```
82
+
83
+ The token itself is the phantom property key. One identity requires one narrow
84
+ symbol token. Separate symbols denote separate identities.
85
+
86
+ `defineIdentity` supplies `mint` only inside the definition callback. The
87
+ declaring module chooses which public formation operations can introduce the
88
+ identity.
89
+
90
+ ## Checked local formation
91
+
92
+ A predicate can own a local scalar formation condition:
93
+
94
+ ```ts
95
+ const Port = identity.number(
96
+ "Port",
97
+ (value) =>
98
+ Number.isInteger(value) &&
99
+ value >= 0 &&
100
+ value <= 65_535,
101
+ );
102
+
103
+ const port = Port(input);
104
+ // Port | undefined
105
+ ```
106
+
107
+ Accepted scalar input returns the identity-bearing value. Rejected or
108
+ wrong-carrier input returns `undefined`.
109
+
110
+ The predicate establishes only this owner's scalar condition. Identity does not
111
+ become an object-schema decoder, normalizer, authorization system, or external
112
+ data validator.
113
+
114
+ ## Composition
115
+
116
+ Forming a new identity on a scalar preserves existing intersections carried by
117
+ that same value. Independent identities can therefore coexist when explicitly
118
+ formed.
119
+
120
+ Named identity keeps its structural key spelling unchanged, so compatible
121
+ producers and duplicate Selaws installations preserve the same identity.
122
+ Declaration-owned identity remains keyed by the caller-owned symbol rather than
123
+ a package-owned unique symbol.
124
+
125
+ Identity and Evidence are independent. A value's identity does not imply that a
126
+ fact has been established about it.