@bjornpagen/bumbledb 0.1.0 → 0.2.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/COOKBOOK.md +1394 -0
- package/README.md +4 -0
- package/dist/closed.d.ts +60 -37
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +159 -40
- package/dist/closed.js.map +1 -1
- package/dist/count.d.ts +64 -17
- package/dist/count.d.ts.map +1 -1
- package/dist/count.js +37 -14
- package/dist/count.js.map +1 -1
- package/dist/db.d.ts +15 -7
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +13 -5
- package/dist/db.js.map +1 -1
- package/dist/exhume.d.ts +12 -11
- package/dist/exhume.d.ts.map +1 -1
- package/dist/exhume.js +7 -6
- package/dist/exhume.js.map +1 -1
- package/dist/face.d.ts +79 -19
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +14 -15
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +158 -129
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +80 -81
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +21 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -12
- package/dist/index.js.map +1 -1
- package/dist/lower.d.ts +6 -3
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +48 -17
- package/dist/lower.js.map +1 -1
- package/dist/marshal.d.ts +36 -21
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js +41 -25
- package/dist/marshal.js.map +1 -1
- package/dist/native.js +2 -2
- package/dist/query/atom.d.ts +332 -174
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +72 -172
- package/dist/query/atom.js.map +1 -1
- package/dist/query/lower.d.ts +295 -107
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +860 -388
- package/dist/query/lower.js.map +1 -1
- package/dist/query/predicate.d.ts +69 -88
- package/dist/query/predicate.d.ts.map +1 -1
- package/dist/query/predicate.js +118 -72
- package/dist/query/predicate.js.map +1 -1
- package/dist/query/run.d.ts +19 -16
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +31 -24
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +139 -123
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +71 -115
- package/dist/query/scope.js.map +1 -1
- package/dist/query/select.d.ts +102 -80
- package/dist/query/select.d.ts.map +1 -1
- package/dist/query/select.js +39 -34
- package/dist/query/select.js.map +1 -1
- package/dist/relation.d.ts +33 -36
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +15 -16
- package/dist/relation.js.map +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +19 -8
- package/dist/schema.js.map +1 -1
- package/dist/spec.d.ts +6 -5
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +56 -31
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +38 -13
- package/dist/statements.js.map +1 -1
- package/package.json +4 -3
- package/src/closed.ts +271 -93
- package/src/count.ts +112 -18
- package/src/db.ts +22 -12
- package/src/exhume.ts +12 -11
- package/src/face.ts +114 -22
- package/src/fields.ts +261 -217
- package/src/index.ts +50 -59
- package/src/lower.ts +62 -20
- package/src/marshal.ts +48 -30
- package/src/native.ts +2 -2
- package/src/query/atom.ts +480 -376
- package/src/query/lower.ts +1341 -542
- package/src/query/predicate.ts +198 -161
- package/src/query/run.ts +35 -25
- package/src/query/scope.ts +188 -218
- package/src/query/select.ts +168 -93
- package/src/relation.ts +38 -44
- package/src/schema.ts +22 -12
- package/src/spec.ts +6 -5
- package/src/statements.ts +69 -26
- package/dist/brand.d.ts +0 -59
- package/dist/brand.d.ts.map +0 -1
- package/dist/brand.js +0 -47
- package/dist/brand.js.map +0 -1
- package/src/brand.ts +0 -82
package/src/statements.ts
CHANGED
|
@@ -4,18 +4,39 @@
|
|
|
4
4
|
* form, conditional containment, the bidirectional `==` abbreviation, and
|
|
5
5
|
* the cardinality window. A statement value is opaque and inert — no
|
|
6
6
|
* methods, no fluent continuation: a fact about the theory, not a builder.
|
|
7
|
+
*
|
|
8
|
+
* Every field reference is checked against the relation it names in the
|
|
9
|
+
* TYPE — existence through {@link FaceFields} (`on(R, "nope")` does not
|
|
10
|
+
* compile) and DOMAIN compatibility through {@link SameDomains}: the two
|
|
11
|
+
* faces' projected domain labels are read structurally off the schema type
|
|
12
|
+
* (S1's `F["domain"]`) and constrained positionwise equal, so a
|
|
13
|
+
* cross-domain pair is a compile error by string-literal comparison of
|
|
14
|
+
* descriptor shapes — never by a value brand (the structural design's
|
|
15
|
+
* ratified check). What is only a SEMANTIC property — the target side of a
|
|
16
|
+
* containment resolving a declared key of its relation — is DELIBERATELY
|
|
17
|
+
* not (and cannot be) stated here: whether `B(y)` is a key of `B` depends
|
|
18
|
+
* on which `key()` statements the surrounding `schema()` collects, a set no
|
|
19
|
+
* face type can see; it stays the engine's typed `SchemaError` judgment at
|
|
20
|
+
* `Db.create`/`Db.open` (the two-boundary split, engine as final
|
|
21
|
+
* authority).
|
|
7
22
|
*/
|
|
8
23
|
|
|
9
24
|
import * as errors from "@superbuilders/errors"
|
|
10
|
-
import { phantom } from "#brand.ts"
|
|
11
25
|
import type { Count } from "#count.ts"
|
|
12
|
-
import { type AnyFace, type FaceData, renderFace, type SameArity } from "#face.ts"
|
|
26
|
+
import { type AnyFace, type FaceData, renderFace, type SameArity, type SameDomains } from "#face.ts"
|
|
13
27
|
import type { AnyRelation, RelationFields } from "#relation.ts"
|
|
14
28
|
import { renderWindow, type WindowSpec } from "#spec.ts"
|
|
15
29
|
|
|
30
|
+
/** A `key()` statement's runtime description — owner and projection carried at exact types. */
|
|
31
|
+
interface KeyData<R extends AnyRelation, Projection extends readonly string[]> {
|
|
32
|
+
readonly kind: "key"
|
|
33
|
+
readonly owner: R
|
|
34
|
+
readonly projection: Projection
|
|
35
|
+
}
|
|
36
|
+
|
|
16
37
|
/** One statement's runtime description, tagged by form. */
|
|
17
38
|
type StatementData =
|
|
18
|
-
|
|
|
39
|
+
| KeyData<AnyRelation, readonly string[]>
|
|
19
40
|
| {
|
|
20
41
|
readonly kind: "containment"
|
|
21
42
|
readonly source: FaceData
|
|
@@ -35,15 +56,16 @@ interface Statement {
|
|
|
35
56
|
}
|
|
36
57
|
|
|
37
58
|
/**
|
|
38
|
-
* A `key()` statement as a TYPED value:
|
|
39
|
-
*
|
|
59
|
+
* A `key()` statement as a TYPED value: its `data` carries the owner
|
|
60
|
+
* relation and the projection tuple at their EXACT types (honest runtime
|
|
61
|
+
* properties — no phantom), which is what the key-statement-selected
|
|
40
62
|
* `get(relation, keyStatement, key)` overload types its key object by
|
|
41
|
-
* (`docs/architecture/70-api.md` § the freeze, the multi-key typed get)
|
|
42
|
-
*
|
|
43
|
-
*
|
|
63
|
+
* (`docs/architecture/70-api.md` § the freeze, the multi-key typed get) and
|
|
64
|
+
* what resolves each projected field's domain label through the owner's
|
|
65
|
+
* schema type. Structurally still a plain {@link Statement}.
|
|
44
66
|
*/
|
|
45
67
|
interface KeyStatement<R extends AnyRelation, Projection extends readonly string[]> extends Statement {
|
|
46
|
-
readonly
|
|
68
|
+
readonly data: KeyData<R, Projection>
|
|
47
69
|
}
|
|
48
70
|
|
|
49
71
|
/**
|
|
@@ -51,9 +73,11 @@ interface KeyStatement<R extends AnyRelation, Projection extends readonly string
|
|
|
51
73
|
* parameter exists (the FD-with-selection shape is unrepresentable, as in
|
|
52
74
|
* the grammar), and only ordinary relations are accepted: a closed
|
|
53
75
|
* relation's key `R(id) -> R` is materialized by the engine, so an
|
|
54
|
-
* explicit one would only ever be a duplicate.
|
|
55
|
-
*
|
|
56
|
-
*
|
|
76
|
+
* explicit one would only ever be a duplicate. Every projected name is
|
|
77
|
+
* checked against `R`'s field block in the type, and the tuple is carried
|
|
78
|
+
* in the returned value's type ({@link KeyStatement}) — keyed point reads
|
|
79
|
+
* through THIS statement are typed field-for-field, domains resolvable
|
|
80
|
+
* through the owner's schema type.
|
|
57
81
|
*/
|
|
58
82
|
function key<
|
|
59
83
|
R extends AnyRelation,
|
|
@@ -64,21 +88,27 @@ function key<
|
|
|
64
88
|
`key(${relation.name}, ...): closedness already materializes ${relation.name}(id) -> ${relation.name} — an explicit key on a closed relation is rejected as a duplicate`
|
|
65
89
|
)
|
|
66
90
|
}
|
|
67
|
-
const data:
|
|
91
|
+
const data: KeyData<R, Projection> = Object.freeze({
|
|
68
92
|
kind: "key",
|
|
69
93
|
owner: relation,
|
|
70
|
-
projection: Object.freeze(
|
|
94
|
+
projection: Object.freeze(fields)
|
|
71
95
|
})
|
|
72
96
|
return Object.freeze({ data })
|
|
73
97
|
}
|
|
74
98
|
|
|
75
99
|
/**
|
|
76
|
-
* `A(X|φ) <= B(Y|ψ)` — conditional inclusion, source left.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
100
|
+
* `A(X|φ) <= B(Y|ψ)` — conditional inclusion, source left. Arity mismatch
|
|
101
|
+
* between the two faces is a type error ({@link SameArity}); a cross-domain
|
|
102
|
+
* pair is a type error ({@link SameDomains} — positionwise string-literal
|
|
103
|
+
* equality of the projected S1 domain labels). The target side must
|
|
104
|
+
* resolve a declared key of B — a SEMANTIC property of the whole statement
|
|
105
|
+
* set that no face type can state, DELIBERATELY judged by the engine at
|
|
106
|
+
* `Db.create`/`Db.open` (`SchemaError`), never re-checked here.
|
|
80
107
|
*/
|
|
81
|
-
function contained<A extends AnyFace, B extends AnyFace>(
|
|
108
|
+
function contained<A extends AnyFace, B extends AnyFace>(
|
|
109
|
+
source: A,
|
|
110
|
+
target: B & SameArity<A, B> & SameDomains<A, B>
|
|
111
|
+
): Statement {
|
|
82
112
|
const data: StatementData = Object.freeze({
|
|
83
113
|
kind: "containment",
|
|
84
114
|
source: source.data,
|
|
@@ -89,11 +119,18 @@ function contained<A extends AnyFace, B extends AnyFace>(source: A, target: B &
|
|
|
89
119
|
}
|
|
90
120
|
|
|
91
121
|
/**
|
|
92
|
-
* `A(X|φ) == B(Y|ψ)` — the bidirectional abbreviation, one utterance
|
|
93
|
-
*
|
|
94
|
-
* (
|
|
122
|
+
* `A(X|φ) == B(Y|ψ)` — the bidirectional abbreviation, one utterance: the
|
|
123
|
+
* selected `==` bijection, a keyed one-to-one correspondence between the
|
|
124
|
+
* two faces (each side contains the other). It lowers to the two adjacent
|
|
125
|
+
* containments in the `A <= B` first order (macro parity — the engine
|
|
126
|
+
* performs the split, source-first) and renders as `==` once, in the
|
|
127
|
+
* written orientation. Faces pair by arity AND domain, exactly as
|
|
128
|
+
* {@link contained}.
|
|
95
129
|
*/
|
|
96
|
-
function mirrors<A extends AnyFace, B extends AnyFace>(
|
|
130
|
+
function mirrors<A extends AnyFace, B extends AnyFace>(
|
|
131
|
+
source: A,
|
|
132
|
+
target: B & SameArity<A, B> & SameDomains<A, B>
|
|
133
|
+
): Statement {
|
|
97
134
|
const data: StatementData = Object.freeze({
|
|
98
135
|
kind: "containment",
|
|
99
136
|
source: source.data,
|
|
@@ -108,9 +145,15 @@ function mirrors<A extends AnyFace, B extends AnyFace>(source: A, target: B & Sa
|
|
|
108
145
|
* LEFT face is the window's TARGET, the per-group parent (B-family,
|
|
109
146
|
* target-left — macro parity), and the RIGHT face is the counted source.
|
|
110
147
|
* `window(on(Holder, "id"), atMost(3n), on(Account, "holder"))` says: each
|
|
111
|
-
* Holder id groups at most three Account rows by holder.
|
|
148
|
+
* Holder id groups at most three Account rows by holder. The two faces
|
|
149
|
+
* pair by arity AND domain ({@link SameDomains}), exactly as containment —
|
|
150
|
+
* the grouping join reads the same positionwise field pairing.
|
|
112
151
|
*/
|
|
113
|
-
function window<B extends AnyFace, A extends AnyFace>(
|
|
152
|
+
function window<B extends AnyFace, A extends AnyFace>(
|
|
153
|
+
target: B,
|
|
154
|
+
count: Count,
|
|
155
|
+
source: A & SameArity<B, A> & SameDomains<B, A>
|
|
156
|
+
): Statement {
|
|
114
157
|
const data: StatementData = Object.freeze({
|
|
115
158
|
kind: "window",
|
|
116
159
|
target: target.data,
|
|
@@ -144,5 +187,5 @@ function renderStatement(statement: Statement): string {
|
|
|
144
187
|
}
|
|
145
188
|
}
|
|
146
189
|
|
|
147
|
-
export type { KeyStatement, Statement, StatementData }
|
|
190
|
+
export type { KeyData, KeyStatement, Statement, StatementData }
|
|
148
191
|
export { contained, key, mirrors, renderStatement, window }
|
package/dist/brand.d.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Nominal branding — the TS analog of the Rust macro's host newtypes
|
|
3
|
-
* (`docs/architecture/10-data-model.md`, the nominal-safety layer). A brand
|
|
4
|
-
* is a phantom: it exists only in the type, tsc polices the wall exactly as
|
|
5
|
-
* rustc polices newtype domains, and nothing is allocated or wrapped at
|
|
6
|
-
* runtime.
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* The brand key. A real runtime symbol (so modules can import it without a
|
|
10
|
-
* `declare`-only lie), but no branded value ever carries the property — the
|
|
11
|
-
* brand is purely a typing device.
|
|
12
|
-
*/
|
|
13
|
-
declare const brand: unique symbol;
|
|
14
|
-
/**
|
|
15
|
-
* The phantom-value key used by field values, field references, and faces
|
|
16
|
-
* to carry their value type without any runtime representation.
|
|
17
|
-
*/
|
|
18
|
-
declare const phantom: unique symbol;
|
|
19
|
-
/**
|
|
20
|
-
* A branded scalar: `T` walled off under the literal name `Name`. A
|
|
21
|
-
* `Brand<bigint, "HolderId">` is not assignable where a
|
|
22
|
-
* `Brand<bigint, "AccountId">` is expected — the Rust newtype wall,
|
|
23
|
-
* verbatim. Scalars brand as `bigint` (u64/i64), `Uint8Array` (bytes), and
|
|
24
|
-
* whole interval objects (`Interval<Name>`); `bool` and `str` take no
|
|
25
|
-
* newtype, exactly as the macro's `as` grammar refuses them.
|
|
26
|
-
*/
|
|
27
|
-
type Brand<T, Name extends string> = T & {
|
|
28
|
-
readonly [brand]: Name;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* A half-open interval `[start, end)` as a plain value object. The ray is
|
|
32
|
-
* representable (`end` = the element domain's MAX_END); widths and
|
|
33
|
-
* signedness are NOT modeled here — the engine judges widths at the typed
|
|
34
|
-
* write boundary, the brand blocks cross-field assignment, and nothing
|
|
35
|
-
* else is TS's business. Interval newtypes derive no order (the Rust
|
|
36
|
-
* refusal, `docs/architecture/10-data-model.md`), so no comparators exist.
|
|
37
|
-
*/
|
|
38
|
-
interface IntervalValue {
|
|
39
|
-
readonly start: bigint;
|
|
40
|
-
readonly end: bigint;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* A branded interval: the whole `{ start, end }` object walled under
|
|
44
|
-
* `Name` — the `interval<i64> as ActiveDuring` analog.
|
|
45
|
-
*/
|
|
46
|
-
type Interval<Name extends string> = Brand<IntervalValue, Name>;
|
|
47
|
-
/**
|
|
48
|
-
* Constructs an interval literal — the `start..end` spelling. Half-open
|
|
49
|
-
* and nonempty by construction: `start >= end` is a typed construction
|
|
50
|
-
* error (parse, don't validate — the same invariant Rust's
|
|
51
|
-
* `Interval::new` enforces at the host boundary). The default `never`
|
|
52
|
-
* brand makes a fresh literal assignable to any interval field or brand,
|
|
53
|
-
* the wrap-at-construction idiom; pass the brand explicitly
|
|
54
|
-
* (`span<"ActiveDuring">(0n, 10n)`) to pin it.
|
|
55
|
-
*/
|
|
56
|
-
declare function span<Name extends string = never>(start: bigint, end: bigint): Interval<Name>;
|
|
57
|
-
export type { Brand, Interval, IntervalValue };
|
|
58
|
-
export { brand, phantom, span };
|
|
59
|
-
//# sourceMappingURL=brand.d.ts.map
|
package/dist/brand.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH;;;;GAIG;AACH,QAAA,MAAM,KAAK,EAAE,OAAO,MAAiC,CAAA;AAErD;;;GAGG;AACH,QAAA,MAAM,OAAO,EAAE,OAAO,MAAmC,CAAA;AAEzD;;;;;;;GAOG;AACH,KAAK,KAAK,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,CAAA;CAAE,CAAA;AAEnE;;;;;;;GAOG;AACH,UAAU,aAAa;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,KAAK,QAAQ,CAAC,IAAI,SAAS,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;AAE/D;;;;;;;;GAQG;AACH,iBAAS,IAAI,CAAC,IAAI,SAAS,MAAM,GAAG,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAMrF;AAED,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAA;AAC9C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA"}
|
package/dist/brand.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Nominal branding — the TS analog of the Rust macro's host newtypes
|
|
3
|
-
* (`docs/architecture/10-data-model.md`, the nominal-safety layer). A brand
|
|
4
|
-
* is a phantom: it exists only in the type, tsc polices the wall exactly as
|
|
5
|
-
* rustc polices newtype domains, and nothing is allocated or wrapped at
|
|
6
|
-
* runtime.
|
|
7
|
-
*/
|
|
8
|
-
import * as errors from "@superbuilders/errors";
|
|
9
|
-
/**
|
|
10
|
-
* The brand-minting guard behind {@link span} — the one nominal step, as a
|
|
11
|
-
* type guard carrying the interval's REAL invariant (`start < end`, the
|
|
12
|
-
* same check Rust's `Interval::new` runs): a value that passes IS a legal
|
|
13
|
-
* interval of any brand, exactly as a Rust newtype wraps a checked
|
|
14
|
-
* `Interval<T>` at construction.
|
|
15
|
-
*/
|
|
16
|
-
function isNonemptyInterval(value) {
|
|
17
|
-
return value.start < value.end;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* The brand key. A real runtime symbol (so modules can import it without a
|
|
21
|
-
* `declare`-only lie), but no branded value ever carries the property — the
|
|
22
|
-
* brand is purely a typing device.
|
|
23
|
-
*/
|
|
24
|
-
const brand = Symbol("bumbledb.brand");
|
|
25
|
-
/**
|
|
26
|
-
* The phantom-value key used by field values, field references, and faces
|
|
27
|
-
* to carry their value type without any runtime representation.
|
|
28
|
-
*/
|
|
29
|
-
const phantom = Symbol("bumbledb.phantom");
|
|
30
|
-
/**
|
|
31
|
-
* Constructs an interval literal — the `start..end` spelling. Half-open
|
|
32
|
-
* and nonempty by construction: `start >= end` is a typed construction
|
|
33
|
-
* error (parse, don't validate — the same invariant Rust's
|
|
34
|
-
* `Interval::new` enforces at the host boundary). The default `never`
|
|
35
|
-
* brand makes a fresh literal assignable to any interval field or brand,
|
|
36
|
-
* the wrap-at-construction idiom; pass the brand explicitly
|
|
37
|
-
* (`span<"ActiveDuring">(0n, 10n)`) to pin it.
|
|
38
|
-
*/
|
|
39
|
-
function span(start, end) {
|
|
40
|
-
const value = Object.freeze({ start, end });
|
|
41
|
-
if (!isNonemptyInterval(value)) {
|
|
42
|
-
throw errors.new(`interval is half-open and nonempty: start must be < end (got ${start}..${end})`);
|
|
43
|
-
}
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
export { brand, phantom, span };
|
|
47
|
-
//# sourceMappingURL=brand.js.map
|
package/dist/brand.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAA;AAE/C;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAsB,KAAoB;IACpE,OAAO,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAA;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,KAAK,GAAkB,MAAM,CAAC,gBAAgB,CAAC,CAAA;AAErD;;;GAGG;AACH,MAAM,OAAO,GAAkB,MAAM,CAAC,kBAAkB,CAAC,CAAA;AA+BzD;;;;;;;;GAQG;AACH,SAAS,IAAI,CAA8B,KAAa,EAAE,GAAW;IACpE,MAAM,KAAK,GAAkB,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;IAC1D,IAAI,CAAC,kBAAkB,CAAO,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,MAAM,CAAC,GAAG,CAAC,gEAAgE,KAAK,KAAK,GAAG,GAAG,CAAC,CAAA;IACnG,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAGD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA"}
|
package/src/brand.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Nominal branding — the TS analog of the Rust macro's host newtypes
|
|
3
|
-
* (`docs/architecture/10-data-model.md`, the nominal-safety layer). A brand
|
|
4
|
-
* is a phantom: it exists only in the type, tsc polices the wall exactly as
|
|
5
|
-
* rustc polices newtype domains, and nothing is allocated or wrapped at
|
|
6
|
-
* runtime.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import * as errors from "@superbuilders/errors"
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The brand-minting guard behind {@link span} — the one nominal step, as a
|
|
13
|
-
* type guard carrying the interval's REAL invariant (`start < end`, the
|
|
14
|
-
* same check Rust's `Interval::new` runs): a value that passes IS a legal
|
|
15
|
-
* interval of any brand, exactly as a Rust newtype wraps a checked
|
|
16
|
-
* `Interval<T>` at construction.
|
|
17
|
-
*/
|
|
18
|
-
function isNonemptyInterval<Name extends string>(value: IntervalValue): value is Interval<Name> {
|
|
19
|
-
return value.start < value.end
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* The brand key. A real runtime symbol (so modules can import it without a
|
|
24
|
-
* `declare`-only lie), but no branded value ever carries the property — the
|
|
25
|
-
* brand is purely a typing device.
|
|
26
|
-
*/
|
|
27
|
-
const brand: unique symbol = Symbol("bumbledb.brand")
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The phantom-value key used by field values, field references, and faces
|
|
31
|
-
* to carry their value type without any runtime representation.
|
|
32
|
-
*/
|
|
33
|
-
const phantom: unique symbol = Symbol("bumbledb.phantom")
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* A branded scalar: `T` walled off under the literal name `Name`. A
|
|
37
|
-
* `Brand<bigint, "HolderId">` is not assignable where a
|
|
38
|
-
* `Brand<bigint, "AccountId">` is expected — the Rust newtype wall,
|
|
39
|
-
* verbatim. Scalars brand as `bigint` (u64/i64), `Uint8Array` (bytes), and
|
|
40
|
-
* whole interval objects (`Interval<Name>`); `bool` and `str` take no
|
|
41
|
-
* newtype, exactly as the macro's `as` grammar refuses them.
|
|
42
|
-
*/
|
|
43
|
-
type Brand<T, Name extends string> = T & { readonly [brand]: Name }
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* A half-open interval `[start, end)` as a plain value object. The ray is
|
|
47
|
-
* representable (`end` = the element domain's MAX_END); widths and
|
|
48
|
-
* signedness are NOT modeled here — the engine judges widths at the typed
|
|
49
|
-
* write boundary, the brand blocks cross-field assignment, and nothing
|
|
50
|
-
* else is TS's business. Interval newtypes derive no order (the Rust
|
|
51
|
-
* refusal, `docs/architecture/10-data-model.md`), so no comparators exist.
|
|
52
|
-
*/
|
|
53
|
-
interface IntervalValue {
|
|
54
|
-
readonly start: bigint
|
|
55
|
-
readonly end: bigint
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* A branded interval: the whole `{ start, end }` object walled under
|
|
60
|
-
* `Name` — the `interval<i64> as ActiveDuring` analog.
|
|
61
|
-
*/
|
|
62
|
-
type Interval<Name extends string> = Brand<IntervalValue, Name>
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Constructs an interval literal — the `start..end` spelling. Half-open
|
|
66
|
-
* and nonempty by construction: `start >= end` is a typed construction
|
|
67
|
-
* error (parse, don't validate — the same invariant Rust's
|
|
68
|
-
* `Interval::new` enforces at the host boundary). The default `never`
|
|
69
|
-
* brand makes a fresh literal assignable to any interval field or brand,
|
|
70
|
-
* the wrap-at-construction idiom; pass the brand explicitly
|
|
71
|
-
* (`span<"ActiveDuring">(0n, 10n)`) to pin it.
|
|
72
|
-
*/
|
|
73
|
-
function span<Name extends string = never>(start: bigint, end: bigint): Interval<Name> {
|
|
74
|
-
const value: IntervalValue = Object.freeze({ start, end })
|
|
75
|
-
if (!isNonemptyInterval<Name>(value)) {
|
|
76
|
-
throw errors.new(`interval is half-open and nonempty: start must be < end (got ${start}..${end})`)
|
|
77
|
-
}
|
|
78
|
-
return value
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export type { Brand, Interval, IntervalValue }
|
|
82
|
-
export { brand, phantom, span }
|