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,251 @@
1
+ # Variant law
2
+
3
+ Variant represents one exact closed family of labeled alternatives.
4
+
5
+ For one family declaration with case names `K` and payload assignment `P`:
6
+
7
+ ```text
8
+ Variant(F) = sum over k in K of P(k)
9
+ ```
10
+
11
+ A unit case has one nullary alternative. A payload case carries one value of its
12
+ declared payload type.
13
+
14
+ Use Variant when a domain owns a finite alternative vocabulary whose case name
15
+ and payload type must stay correlated.
16
+
17
+ ```ts
18
+ const Message = Variant.define("Message", [
19
+ ["quit", Variant.unit],
20
+ ["write", Variant.payload<string>()],
21
+ ]);
22
+
23
+ type Message =
24
+ Variant.Value<typeof Message>;
25
+ ```
26
+
27
+ ## 1. Closed family
28
+
29
+ One `Variant.define` call owns one finite readonly tuple of
30
+ `[caseName, caseSpec]` entries. That tuple is the complete case universe for
31
+ the family.
32
+
33
+ The outer tuple and every case-entry pair must be readonly, its length must be
34
+ statically concrete, and every case name must be one concrete string literal.
35
+ Mutable tuples, broad arrays, optional/union entry sets, duplicate names, and
36
+ patterned/broad string names do not define one exact closed family. Empty
37
+ declarations are valid and produce an uninhabited Variant value type.
38
+
39
+ Declaration order carries no meaning.
40
+
41
+ The finite readonly tuple is part of the typed boundary. Ordinary record width
42
+ subtyping can hide runtime properties behind a narrower static record type, so
43
+ Variant derives the family from an exact tuple rather than record keys. Directly
44
+ mutable declaration tuples are rejected. A readonly view over separately
45
+ mutable backing data can still be invalidated through TypeScript's unsound
46
+ aliasing and is governed by the trust model in
47
+ [SEMANTICS.md](../SEMANTICS.md).
48
+
49
+ Adding or removing a case changes the family declaration and therefore the
50
+ family-level exhaustive handling obligation.
51
+
52
+ ## 2. Family identity
53
+
54
+ A string family name gives package-copy-stable named identity. A bound
55
+ `Symbol` gives declaration-owned identity.
56
+
57
+ Named family values use the structural phantom key:
58
+
59
+ ```text
60
+ ~selaws.variant:<Name>
61
+ ```
62
+
63
+ Declaration-owned families use the caller-owned symbol as the phantom property
64
+ key.
65
+
66
+ The phantom marker includes the Variant category, closed case-name universe,
67
+ and case signatures. Case-name universes are exact; payload positions retain
68
+ ordinary TypeScript structural variance.
69
+
70
+ Duplicate compatible Selaws installations therefore agree on an identically
71
+ declared named family, while distinct names and distinct caller-owned symbols
72
+ remain separate families.
73
+
74
+ ## 3. Case formation
75
+
76
+ `Variant.unit` declares a nullary case.
77
+
78
+ `Variant.payload<T>()` declares a case carrying one `T`.
79
+
80
+ These declaration markers are opaque, package-copy-stable sentinels; their
81
+ runtime representation carries no domain payload.
82
+
83
+ `Variant.define` produces one `make` constructor for every declared case.
84
+ Formation preserves the selected case name and payload:
85
+
86
+ ```ts
87
+ Message.make.quit();
88
+ // { tag: "quit" }
89
+
90
+ Message.make.write("hello");
91
+ // { tag: "write", value: "hello" }
92
+ ```
93
+
94
+ A unit case and a payload case remain distinct even when the payload type is
95
+ `undefined` or `never`.
96
+
97
+ ## 4. Representation
98
+
99
+ Variant values are ordinary transparent JavaScript objects.
100
+
101
+ A unit case has the representation:
102
+
103
+ ```ts
104
+ { tag: caseName }
105
+ ```
106
+
107
+ A payload case has the representation:
108
+
109
+ ```ts
110
+ { tag: caseName, value: payload }
111
+ ```
112
+
113
+ The family phantom identity is static TypeScript information. Formation adds no
114
+ runtime brand to the value.
115
+
116
+ Constructed values are ordinary structural data. The family declaration and
117
+ its `make` namespace are immutable; payload values themselves are not
118
+ deep-frozen by Variant.
119
+
120
+ ## 5. Exhaustive elimination
121
+
122
+ Variant family `match` conforms to the shared [Match law](./match.md). The
123
+ Variant family owns the branch universe: one branch for every declared case,
124
+ with the payload assignment defined by that family.
125
+
126
+ Typed family `match` requires one handler for every declared case. The
127
+ complete family declaration, rather than the current narrowing of the input
128
+ value, determines the required handler object.
129
+
130
+ ```ts
131
+ const length = Message.match(
132
+ Message.make.write("hello"),
133
+ {
134
+ quit: () => 0,
135
+ write: (text) => text.length,
136
+ },
137
+ );
138
+ ```
139
+
140
+ Exactly the selected own data-property handler runs once and without a
141
+ library-defined `this` receiver. Inherited properties and accessors do not
142
+ satisfy handler availability. Unit handlers receive zero arguments. Payload
143
+ handlers receive exactly the stored payload. Unselected handler properties are
144
+ not read by Match execution.
145
+
146
+ At runtime, `match` requires the Variant tag to be an own data property.
147
+ Payload cases also require an own data `value` property. Inherited properties
148
+ and accessors therefore cannot supply either part of the Variant
149
+ representation. `match` then validates that the selected tag belongs to the
150
+ family and that the selected handler is an own function property. TypeScript
151
+ owns whole-handler exhaustiveness for honestly typed calls.
152
+
153
+ Handler return and abrupt completion follow ordinary JavaScript semantics.
154
+ Promise values remain native Promise values.
155
+
156
+ Normal TypeScript discriminant narrowing remains available when local control
157
+ flow owns the branch:
158
+
159
+ ```ts
160
+ if (message.tag === "write") {
161
+ message.value;
162
+ // string
163
+ }
164
+ ```
165
+
166
+ ## 6. Generic and recursive families
167
+
168
+ A generic family can be an ordinary factory:
169
+
170
+ ```ts
171
+ const Remote = <T>() =>
172
+ Variant.define("Remote", [
173
+ ["idle", Variant.unit],
174
+ ["success", Variant.payload<T>()],
175
+ ]);
176
+
177
+ type Remote<T> =
178
+ Variant.Value<ReturnType<typeof Remote<T>>>;
179
+ ```
180
+
181
+ Recursive payloads can refer back to the Variant value through an ordinary
182
+ object or interface boundary:
183
+
184
+ ```ts
185
+ interface AddPayload {
186
+ readonly left: Expr;
187
+ readonly right: Expr;
188
+ }
189
+
190
+ const Expr = Variant.define("Expr", [
191
+ ["literal", Variant.payload<number>()],
192
+ ["add", Variant.payload<AddPayload>()],
193
+ ]);
194
+
195
+ type Expr =
196
+ Variant.Value<typeof Expr>;
197
+ ```
198
+
199
+ Variant does not introduce a separate recursive-data runtime.
200
+
201
+ ## 7. Snapshot stability
202
+
203
+ `Variant.define` snapshots its case-entry tuple into constructor and
204
+ elimination behavior. Later mutation of caller-owned JavaScript arrays cannot
205
+ change the already-defined family. Typed declarations reject directly mutable outer and inner tuples. A readonly
206
+ view whose backing array is mutated through another alias before definition is
207
+ a TypeScript trust-model escape rather than a guarantee Variant can reify at
208
+ runtime.
209
+
210
+ The returned family and its `make` namespace are immutable runtime
211
+ declarations. Constructed Variant values remain ordinary structural data.
212
+
213
+ ## 8. Typed boundary
214
+
215
+ Variant formation consumes already-typed payloads.
216
+
217
+ Runtime schema decoding, normalization, authorization, mutable freshness, and
218
+ version negotiation belong to application owners that can establish those
219
+ facts.
220
+
221
+ The runtime declaration boundary recognizes its finite case grammar. `match`
222
+ checks selected case membership and selected handler availability. Payload type
223
+ validity remains a TypeScript guarantee inside the ordinary Selaws trust model.
224
+
225
+ ```ts
226
+ const UserEvent = Variant.define("UserEvent", [
227
+ ["loaded", Variant.payload<User>()],
228
+ ]);
229
+ ```
230
+
231
+ This declaration does not validate unknown JSON as `User`; the decoding
232
+ boundary must do that before constructing `loaded`.
233
+
234
+ ## 9. Owner independence
235
+
236
+ Variant owns labeled alternative identity, correlated payload formation, and
237
+ the complete case universe used for elimination. Its family `match` conforms
238
+ to the shared Match law; Match does not become a Variant-owned or independent
239
+ carrier.
240
+
241
+ Protocol owns admissible labeled transitions between application-owned state
242
+ identifiers.
243
+
244
+ Option owns presence.
245
+
246
+ Result owns recoverable success or failure.
247
+
248
+ Validation owns deterministic accumulation of independent issues.
249
+
250
+ Those owners may use sum-shaped structural data or compose values without
251
+ transferring their domain laws to Variant.
package/package.json CHANGED
@@ -1,6 +1,90 @@
1
1
  {
2
2
  "name": "selaws",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Independent semantic primitives for TypeScript with explicit laws.",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "typescript",
8
+ "semantic",
9
+ "result",
10
+ "option",
11
+ "validation",
12
+ "nominal-typing",
13
+ "refinement-types",
14
+ "domain-modeling"
15
+ ],
16
+ "type": "module",
17
+ "types": "./dist/index.d.ts",
18
+ "sideEffects": false,
19
+ "files": [
20
+ "dist",
21
+ "src",
22
+ "docs",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/amatzk/selaws.git"
29
+ },
30
+ "homepage": "https://github.com/amatzk/selaws",
31
+ "bugs": {
32
+ "url": "https://github.com/amatzk/selaws/issues"
33
+ },
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/index.d.ts",
37
+ "import": "./dist/index.js",
38
+ "default": "./dist/index.js"
39
+ },
40
+ "./identity": {
41
+ "types": "./dist/identity.d.ts",
42
+ "import": "./dist/identity.js",
43
+ "default": "./dist/identity.js"
44
+ },
45
+ "./evidence": {
46
+ "types": "./dist/evidence.d.ts",
47
+ "import": "./dist/evidence.js",
48
+ "default": "./dist/evidence.js"
49
+ },
50
+ "./protocol": {
51
+ "types": "./dist/protocol.d.ts",
52
+ "import": "./dist/protocol.js",
53
+ "default": "./dist/protocol.js"
54
+ },
55
+ "./variant": {
56
+ "types": "./dist/variant.d.ts",
57
+ "import": "./dist/variant.js",
58
+ "default": "./dist/variant.js"
59
+ },
60
+ "./result": {
61
+ "types": "./dist/result/index.d.ts",
62
+ "import": "./dist/result/index.js",
63
+ "default": "./dist/result/index.js"
64
+ },
65
+ "./option": {
66
+ "types": "./dist/option.d.ts",
67
+ "import": "./dist/option.js",
68
+ "default": "./dist/option.js"
69
+ },
70
+ "./validation": {
71
+ "types": "./dist/validation.d.ts",
72
+ "import": "./dist/validation.js",
73
+ "default": "./dist/validation.js"
74
+ }
75
+ },
76
+ "devDependencies": {
77
+ "@biomejs/biome": "2.5.15",
78
+ "typescript": "7.0.2"
79
+ },
80
+ "scripts": {
81
+ "build": "node scripts/clean-dist.mjs && tsc -p tsconfig.build.json",
82
+ "format": "biome format --write .",
83
+ "lint": "biome lint .",
84
+ "fix": "biome check --write .",
85
+ "test:types": "tsc -p tsconfig.test.json",
86
+ "test:runtime": "node --test test/*.test.mjs",
87
+ "test": "pnpm run build && pnpm run test:types && pnpm run test:runtime",
88
+ "check": "biome ci . && pnpm run test"
89
+ }
6
90
  }
@@ -0,0 +1,120 @@
1
+ import {
2
+ isBigint,
3
+ isBoolean,
4
+ isNumber,
5
+ isString,
6
+ isSymbol,
7
+ type NarrowSymbolSet,
8
+ type Scalar,
9
+ type SingleName,
10
+ type SingleSymbol,
11
+ type Token,
12
+ } from "./internal/scalar.js";
13
+
14
+ export type { Scalar } from "./internal/scalar.js";
15
+
16
+ type EvidenceMarker = {
17
+ readonly "~selaws.evidence": true;
18
+ };
19
+
20
+ /**
21
+ * A stable fact established about a scalar value.
22
+ *
23
+ * Independent fact tokens occupy independent phantom properties, so evidence
24
+ * composes while preserving existing identity and evidence on the same scalar.
25
+ */
26
+ export type Evidence<T extends Scalar, Fact extends symbol> = [Fact] extends [never]
27
+ ? never
28
+ : [Fact] extends [NarrowSymbolSet<Fact>]
29
+ ? T & {
30
+ readonly [Key in Fact]: EvidenceMarker;
31
+ }
32
+ : never;
33
+
34
+ type Establish<T extends Scalar, Fact extends symbol> = <Value extends T>(
35
+ value: Value,
36
+ ) => Evidence<Value, Fact>;
37
+
38
+ /** Defines an owner-scoped formation boundary for one stable fact. */
39
+ export function defineFact<T extends Scalar>() {
40
+ return function define<const Fact extends symbol, Api>(
41
+ token: Fact & SingleSymbol<Fact>,
42
+ build: (establish: Establish<T, Fact>) => Api,
43
+ ): Api {
44
+ void token;
45
+
46
+ const establish = <Value extends T>(value: Value): Evidence<Value, Fact> =>
47
+ value as Evidence<Value, Fact>;
48
+
49
+ return build(establish);
50
+ };
51
+ }
52
+
53
+ type NamedEvidence<T extends Scalar, Name extends string> = T & {
54
+ readonly [Key in `~selaws.evidence:${Name}`]: true;
55
+ };
56
+
57
+ type FactEvidence<
58
+ T extends Scalar,
59
+ TokenValue extends Token,
60
+ > = TokenValue extends string
61
+ ? NamedEvidence<T, TokenValue>
62
+ : TokenValue extends symbol
63
+ ? Evidence<T, TokenValue>
64
+ : never;
65
+
66
+ type Predicate<T> = (value: T) => boolean;
67
+
68
+ interface Fact<T extends Scalar, TokenValue extends Token> {
69
+ <Value extends T>(value: Value): FactEvidence<Value, TokenValue> | undefined;
70
+ (value: unknown): FactEvidence<T, TokenValue> | undefined;
71
+ }
72
+
73
+ interface FactFactory<T extends Scalar> {
74
+ <const Name extends string>(
75
+ name: Name & SingleName<Name>,
76
+ predicate: Predicate<T>,
77
+ ): Fact<T, Name>;
78
+
79
+ <const Key extends symbol>(
80
+ key: Key & SingleSymbol<Key>,
81
+ predicate: Predicate<T>,
82
+ ): Fact<T, Key>;
83
+ }
84
+
85
+ const factFactory = <T extends Scalar>(
86
+ isCarrier: (value: unknown) => value is T,
87
+ ): FactFactory<T> =>
88
+ ((_token: Token, predicate: Predicate<T>) => (value: unknown) =>
89
+ isCarrier(value) && predicate(value)
90
+ ? value
91
+ : undefined) as unknown as FactFactory<T>;
92
+
93
+ type EvidenceFacade = Readonly<{
94
+ bigint: FactFactory<bigint>;
95
+ boolean: FactFactory<boolean>;
96
+ define: typeof defineFact;
97
+ number: FactFactory<number>;
98
+ string: FactFactory<string>;
99
+ symbol: FactFactory<symbol>;
100
+ }>;
101
+
102
+ /** Stable scalar facts with named or declaration-owned evidence. */
103
+ export const evidence: EvidenceFacade = {
104
+ bigint: factFactory(isBigint),
105
+ boolean: factFactory(isBoolean),
106
+ define: defineFact,
107
+ number: factFactory(isNumber),
108
+ string: factFactory(isString),
109
+ symbol: factFactory(isSymbol),
110
+ };
111
+
112
+ export namespace evidence {
113
+ /** Applies an evidence declaration's fact type to an existing scalar value. */
114
+ export type Proven<F, Value extends Scalar> =
115
+ F extends Fact<infer T, infer TokenValue>
116
+ ? Value extends T
117
+ ? FactEvidence<Value, TokenValue>
118
+ : never
119
+ : never;
120
+ }
@@ -0,0 +1,129 @@
1
+ import {
2
+ isBigint,
3
+ isBoolean,
4
+ isNumber,
5
+ isString,
6
+ isSymbol,
7
+ type Scalar,
8
+ type SingleName,
9
+ type SingleSymbol,
10
+ type Token,
11
+ } from "./internal/scalar.js";
12
+
13
+ export type { Scalar } from "./internal/scalar.js";
14
+
15
+ type IdentityMarker = {
16
+ readonly "~selaws.identity": true;
17
+ };
18
+
19
+ /**
20
+ * A scalar carrying one declaration-owned nominal identity.
21
+ *
22
+ * The domain token is the phantom property key itself, so identity belongs to
23
+ * the declaration that owns the token and remains stable across duplicate
24
+ * Selaws installations.
25
+ */
26
+ export type Identity<T extends Scalar, Id extends symbol> = [Id] extends [never]
27
+ ? never
28
+ : [Id] extends [SingleSymbol<Id>]
29
+ ? T & {
30
+ readonly [Key in Id]: IdentityMarker;
31
+ }
32
+ : never;
33
+
34
+ type Mint<T extends Scalar, Id extends symbol> = <Value extends T>(
35
+ value: Value,
36
+ ) => Identity<Value, Id>;
37
+
38
+ /** Defines an owner-scoped formation boundary for one nominal identity. */
39
+ export function defineIdentity<T extends Scalar>() {
40
+ return function define<const Id extends symbol, Api>(
41
+ token: Id & SingleSymbol<Id>,
42
+ build: (mint: Mint<T, Id>) => Api,
43
+ ): Api {
44
+ void token;
45
+
46
+ const mint = <Value extends T>(value: Value): Identity<Value, Id> =>
47
+ value as Identity<Value, Id>;
48
+
49
+ return build(mint);
50
+ };
51
+ }
52
+
53
+ type NamedIdentity<T extends Scalar, Name extends string> = T & {
54
+ readonly [Key in `~selaws.identity:${Name}`]: true;
55
+ };
56
+
57
+ type DomainIdentity<
58
+ T extends Scalar,
59
+ TokenValue extends Token,
60
+ > = TokenValue extends string
61
+ ? NamedIdentity<T, TokenValue>
62
+ : TokenValue extends symbol
63
+ ? Identity<T, TokenValue>
64
+ : never;
65
+
66
+ type Predicate<T> = (value: T) => boolean;
67
+
68
+ interface OpenIdentity<T extends Scalar, TokenValue extends Token> {
69
+ <Value extends T>(value: Value): DomainIdentity<Value, TokenValue>;
70
+ (value: unknown): DomainIdentity<T, TokenValue> | undefined;
71
+ }
72
+
73
+ interface CheckedIdentity<T extends Scalar, TokenValue extends Token> {
74
+ <Value extends T>(value: Value): DomainIdentity<Value, TokenValue> | undefined;
75
+ (value: unknown): DomainIdentity<T, TokenValue> | undefined;
76
+ }
77
+
78
+ interface IdentityFactory<T extends Scalar> {
79
+ <const Name extends string>(name: Name & SingleName<Name>): OpenIdentity<T, Name>;
80
+
81
+ <const Name extends string>(
82
+ name: Name & SingleName<Name>,
83
+ predicate: Predicate<T>,
84
+ ): CheckedIdentity<T, Name>;
85
+
86
+ <const Key extends symbol>(key: Key & SingleSymbol<Key>): OpenIdentity<T, Key>;
87
+
88
+ <const Key extends symbol>(
89
+ key: Key & SingleSymbol<Key>,
90
+ predicate: Predicate<T>,
91
+ ): CheckedIdentity<T, Key>;
92
+ }
93
+
94
+ const identityFactory = <T extends Scalar>(
95
+ isCarrier: (value: unknown) => value is T,
96
+ ): IdentityFactory<T> =>
97
+ ((_token: Token, predicate?: Predicate<T>) => (value: unknown) =>
98
+ isCarrier(value) && (predicate === undefined || predicate(value))
99
+ ? value
100
+ : undefined) as unknown as IdentityFactory<T>;
101
+
102
+ type IdentityFacade = Readonly<{
103
+ bigint: IdentityFactory<bigint>;
104
+ boolean: IdentityFactory<boolean>;
105
+ define: typeof defineIdentity;
106
+ number: IdentityFactory<number>;
107
+ string: IdentityFactory<string>;
108
+ symbol: IdentityFactory<symbol>;
109
+ }>;
110
+
111
+ /** Scalar identity formation with named or declaration-owned identity. */
112
+ export const identity: IdentityFacade = {
113
+ bigint: identityFactory(isBigint),
114
+ boolean: identityFactory(isBoolean),
115
+ define: defineIdentity,
116
+ number: identityFactory(isNumber),
117
+ string: identityFactory(isString),
118
+ symbol: identityFactory(isSymbol),
119
+ };
120
+
121
+ export namespace identity {
122
+ /** Extracts the scalar identity value type produced by an identity declaration. */
123
+ export type Value<Domain> =
124
+ Domain extends OpenIdentity<infer T, infer TokenValue>
125
+ ? DomainIdentity<T, TokenValue>
126
+ : Domain extends CheckedIdentity<infer T, infer TokenValue>
127
+ ? DomainIdentity<T, TokenValue>
128
+ : never;
129
+ }
package/src/index.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { Option as OptionFacade } from "./option.js";
2
+ import { Protocol as ProtocolFacade } from "./protocol.js";
3
+ import { Result as ResultFacade } from "./result/core.js";
4
+ import { Validation as ValidationFacade } from "./validation.js";
5
+ import { Variant as VariantFacade } from "./variant.js";
6
+
7
+ export { type Evidence, evidence } from "./evidence.js";
8
+ export { type Identity, identity, type Scalar } from "./identity.js";
9
+
10
+ /** Root-entry Protocol carrier paired with the Protocol facade value. */
11
+ export type Protocol<
12
+ Transitions extends readonly import("./protocol.js").Transition[],
13
+ > = import("./protocol.js").Protocol<Transitions>;
14
+ /** Root-entry Protocol facade. */
15
+ export const Protocol: typeof ProtocolFacade = ProtocolFacade;
16
+
17
+ /** Root-entry Variant carrier paired with the Variant facade value. */
18
+ export type Variant<Family> = import("./variant.js").Variant<Family>;
19
+ /** Root-entry Variant facade. */
20
+ export const Variant: typeof VariantFacade = VariantFacade;
21
+ export namespace Variant {
22
+ /** Extracts the closed value union produced by one Variant family. */
23
+ export type Value<Family> = import("./variant.js").Variant<Family>;
24
+ }
25
+
26
+ export type { None, OptionValue, Some } from "./option.js";
27
+ /** Root-entry Option carrier paired with the Option facade value. */
28
+ export type Option<T> = import("./option.js").Option<T>;
29
+ /** Root-entry Option facade. */
30
+ export const Option: typeof OptionFacade = OptionFacade;
31
+
32
+ export type {
33
+ Invalid,
34
+ Valid,
35
+ ValidationError,
36
+ ValidationIssues,
37
+ ValidationValue,
38
+ } from "./validation.js";
39
+ /** Root-entry Validation carrier paired with the Validation facade value. */
40
+ export type Validation<T, E> = import("./validation.js").Validation<T, E>;
41
+ /** Root-entry Validation facade. */
42
+ export const Validation: typeof ValidationFacade = ValidationFacade;
43
+
44
+ export type {
45
+ AsyncResult,
46
+ Err,
47
+ Ok,
48
+ ResultError,
49
+ ResultValue,
50
+ } from "./result/core.js";
51
+ /** Root-entry Result carrier paired with the Result facade value. */
52
+ export type Result<T, E> = import("./result/core.js").Result<T, E>;
53
+ /** Root-entry Result facade. */
54
+ export const Result: typeof ResultFacade = ResultFacade;
@@ -0,0 +1,54 @@
1
+ type Same<A, B> =
2
+ (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2
3
+ ? (<T>() => T extends B ? 1 : 2) extends <T>() => T extends A ? 1 : 2
4
+ ? true
5
+ : false
6
+ : false;
7
+
8
+ type UnknownCallback = (this: void, ...args: never[]) => unknown;
9
+
10
+ type OverloadUnion<
11
+ Callback,
12
+ Partial = unknown,
13
+ Previous = never,
14
+ Repeated extends boolean = false,
15
+ Accumulated = never,
16
+ > = Callback extends (this: infer This, ...args: infer Args) => infer Result
17
+ ? ((this: This, ...args: Args) => Result) extends infer Current
18
+ ? Same<Current, Previous> extends true
19
+ ? Repeated extends true
20
+ ? Accumulated | UnknownCallback
21
+ : Partial extends Callback
22
+ ? Accumulated
23
+ : OverloadUnion<
24
+ Partial & Callback,
25
+ Partial & Current,
26
+ Current,
27
+ true,
28
+ Accumulated | Current
29
+ >
30
+ : Partial extends Callback
31
+ ? Accumulated
32
+ : OverloadUnion<
33
+ Partial & Callback,
34
+ Partial & Current,
35
+ Current,
36
+ false,
37
+ Accumulated | Current
38
+ >
39
+ : Accumulated
40
+ : Accumulated;
41
+
42
+ /**
43
+ * Extracts a conservative union of callable return types.
44
+ *
45
+ * TypeScript exposes overloads as intersections. Generic callable reflection
46
+ * can repeat one instantiated signature indefinitely; repeated reflection
47
+ * widens to unknown instead of exhausting compiler instantiation depth.
48
+ */
49
+ export type CallbackResult<Callback> =
50
+ OverloadUnion<Callback> extends infer One
51
+ ? One extends (...args: never[]) => infer Result
52
+ ? Result
53
+ : never
54
+ : never;