@bcts/known-values 1.0.0-alpha.10
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/LICENSE +48 -0
- package/README.md +11 -0
- package/dist/index.cjs +1017 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +664 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +664 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +1019 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +912 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +74 -0
- package/src/index.ts +116 -0
- package/src/known-value.ts +425 -0
- package/src/known-values-registry.ts +317 -0
- package/src/known-values-store.ts +327 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A value in a namespace of unsigned integers that represents a stand-alone
|
|
3
|
+
* ontological concept.
|
|
4
|
+
*
|
|
5
|
+
* Known Values provide a compact, deterministic way to represent commonly used
|
|
6
|
+
* ontological concepts such as relationships between entities, classes of
|
|
7
|
+
* entities, properties, or enumerated values. They are particularly useful as
|
|
8
|
+
* predicates in Gordian Envelope assertions, offering a more compact and
|
|
9
|
+
* deterministic alternative to URIs. However, known values are not exclusive
|
|
10
|
+
* to Gordian Envelopes and can be used in any context where a compact, unique
|
|
11
|
+
* identifier for a concept is needed.
|
|
12
|
+
*
|
|
13
|
+
* A Known Value is represented as a 64-bit unsigned integer with an optional
|
|
14
|
+
* human-readable name. This approach ensures:
|
|
15
|
+
*
|
|
16
|
+
* - **Compact binary representation** - Each Known Value requires only 1-9
|
|
17
|
+
* bytes depending on value range
|
|
18
|
+
* - **Deterministic encoding** - Every concept has exactly one valid binary
|
|
19
|
+
* representation
|
|
20
|
+
* - **Enhanced security** - Eliminates URI manipulation vulnerabilities
|
|
21
|
+
* - **Standardized semantics** - Values are registered in a central registry
|
|
22
|
+
*
|
|
23
|
+
* While Known Values are most commonly used as predicates in assertions, they
|
|
24
|
+
* can appear in any position in an Envelope (subject, predicate, or object).
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { KnownValue } from '@bcts/known-values';
|
|
29
|
+
*
|
|
30
|
+
* // Create a Known Value with a numeric value
|
|
31
|
+
* const knownValue = new KnownValue(42);
|
|
32
|
+
* console.log(knownValue.value()); // 42
|
|
33
|
+
*
|
|
34
|
+
* // Create a Known Value with a name
|
|
35
|
+
* const namedValue = new KnownValue(1, 'isA');
|
|
36
|
+
* console.log(namedValue.value()); // 1
|
|
37
|
+
* console.log(namedValue.name()); // "isA"
|
|
38
|
+
*
|
|
39
|
+
* // CBOR encoding
|
|
40
|
+
* const cbor = namedValue.taggedCbor();
|
|
41
|
+
* const bytes = namedValue.toCborData();
|
|
42
|
+
*
|
|
43
|
+
* // CBOR decoding
|
|
44
|
+
* const decoded = KnownValue.fromTaggedCbor(cbor);
|
|
45
|
+
* const decodedFromBytes = KnownValue.fromCborData(bytes);
|
|
46
|
+
*
|
|
47
|
+
* // Use a pre-defined Known Value from the registry
|
|
48
|
+
* import { IS_A } from '@bcts/known-values';
|
|
49
|
+
* console.log(IS_A.value()); // 1
|
|
50
|
+
* console.log(IS_A.name()); // "isA"
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @specification
|
|
54
|
+
*
|
|
55
|
+
* Known Values are defined in
|
|
56
|
+
* [BCR-2023-002](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2023-002-known-value.md)
|
|
57
|
+
* and implemented as an Envelope extension in
|
|
58
|
+
* [BCR-2023-003](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2023-003-envelope-known-value.md).
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
import {
|
|
62
|
+
type Cbor,
|
|
63
|
+
type Tag,
|
|
64
|
+
type CborTaggedEncodable,
|
|
65
|
+
type CborTaggedDecodable,
|
|
66
|
+
cbor,
|
|
67
|
+
cborData,
|
|
68
|
+
decodeCbor,
|
|
69
|
+
MajorType,
|
|
70
|
+
} from "@bcts/dcbor";
|
|
71
|
+
import { KNOWN_VALUE } from "@bcts/components";
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The numeric value for the CBOR tag used for Known Values.
|
|
75
|
+
* This is Tag 40000 as defined in the Blockchain Commons registry.
|
|
76
|
+
*/
|
|
77
|
+
export const TAG_KNOWN_VALUE = KNOWN_VALUE.value;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The CBOR tag used for Known Values.
|
|
81
|
+
* This is Tag 40000 as defined in the Blockchain Commons registry.
|
|
82
|
+
*/
|
|
83
|
+
export const KNOWN_VALUE_TAG: Tag = KNOWN_VALUE;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Type for values that can be used to create a KnownValue.
|
|
87
|
+
* Supports both number (for values up to 2^53-1) and bigint (for full 64-bit range).
|
|
88
|
+
*/
|
|
89
|
+
export type KnownValueInput = number | bigint;
|
|
90
|
+
|
|
91
|
+
export class KnownValue implements CborTaggedEncodable, CborTaggedDecodable<KnownValue> {
|
|
92
|
+
private readonly _value: bigint;
|
|
93
|
+
private readonly _assignedName: string | undefined;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates a new KnownValue with the given numeric value and optional name.
|
|
97
|
+
*
|
|
98
|
+
* @param value - The numeric value (number or bigint). Numbers are converted to bigint internally.
|
|
99
|
+
* @param assignedName - Optional human-readable name for the value
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const knownValue = new KnownValue(42);
|
|
104
|
+
* console.log(knownValue.value()); // 42
|
|
105
|
+
*
|
|
106
|
+
* const namedValue = new KnownValue(1, 'isA');
|
|
107
|
+
* console.log(namedValue.name()); // "isA"
|
|
108
|
+
*
|
|
109
|
+
* // Using bigint for large values
|
|
110
|
+
* const largeValue = new KnownValue(9007199254740993n);
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
constructor(value: KnownValueInput, assignedName?: string) {
|
|
114
|
+
this._value = typeof value === "bigint" ? value : BigInt(value);
|
|
115
|
+
this._assignedName = assignedName;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ===========================================================================
|
|
119
|
+
// Value Accessors (backward compatible API)
|
|
120
|
+
// ===========================================================================
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Returns the numeric value of the KnownValue.
|
|
124
|
+
*
|
|
125
|
+
* This is the raw unsigned integer that identifies the concept.
|
|
126
|
+
* Returns a number for backward compatibility. For values > MAX_SAFE_INTEGER,
|
|
127
|
+
* use `valueBigInt()`.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* import { IS_A, NOTE } from '@bcts/known-values';
|
|
132
|
+
* console.log(IS_A.value()); // 1
|
|
133
|
+
* console.log(NOTE.value()); // 4
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
value(): number {
|
|
137
|
+
if (this._value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
138
|
+
throw new RangeError(
|
|
139
|
+
`KnownValue ${this._value} exceeds MAX_SAFE_INTEGER. Use valueBigInt() instead.`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return Number(this._value);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns the numeric value of the KnownValue as a bigint.
|
|
147
|
+
*
|
|
148
|
+
* Use this for values that may exceed Number.MAX_SAFE_INTEGER (2^53-1).
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* const largeValue = new KnownValue(9007199254740993n);
|
|
153
|
+
* console.log(largeValue.valueBigInt()); // 9007199254740993n
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
valueBigInt(): bigint {
|
|
157
|
+
return this._value;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Returns the assigned name of the KnownValue, if one exists.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const namedValue = new KnownValue(1, 'isA');
|
|
166
|
+
* console.log(namedValue.assignedName()); // "isA"
|
|
167
|
+
*
|
|
168
|
+
* const unnamedValue = new KnownValue(42);
|
|
169
|
+
* console.log(unnamedValue.assignedName()); // undefined
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
assignedName(): string | undefined {
|
|
173
|
+
return this._assignedName;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Returns a human-readable name for the KnownValue.
|
|
178
|
+
*
|
|
179
|
+
* If the KnownValue has an assigned name, that name is returned.
|
|
180
|
+
* Otherwise, the string representation of the numeric value is returned.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* const namedValue = new KnownValue(1, 'isA');
|
|
185
|
+
* console.log(namedValue.name()); // "isA"
|
|
186
|
+
*
|
|
187
|
+
* const unnamedValue = new KnownValue(42);
|
|
188
|
+
* console.log(unnamedValue.name()); // "42"
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
name(): string {
|
|
192
|
+
return this._assignedName ?? this._value.toString();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ===========================================================================
|
|
196
|
+
// Equality and Hashing
|
|
197
|
+
// ===========================================================================
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Compares this KnownValue with another for equality.
|
|
201
|
+
* Equality is based solely on the numeric value, ignoring the name.
|
|
202
|
+
*
|
|
203
|
+
* @param other - The KnownValue to compare with
|
|
204
|
+
* @returns true if the values are equal
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* const kv1 = new KnownValue(1, 'isA');
|
|
209
|
+
* const kv2 = new KnownValue(1, 'different');
|
|
210
|
+
* console.log(kv1.equals(kv2)); // true (same value, different name)
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
equals(other: KnownValue): boolean {
|
|
214
|
+
return this._value === other._value;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Hash code based on the numeric value.
|
|
219
|
+
* Useful for using KnownValue in hash-based collections.
|
|
220
|
+
*/
|
|
221
|
+
hashCode(): number {
|
|
222
|
+
// Convert bigint to a 32-bit hash
|
|
223
|
+
return Number(this._value & BigInt(0xffffffff));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* String representation of the KnownValue.
|
|
228
|
+
*
|
|
229
|
+
* If a name is assigned, the name is displayed. Otherwise, the numeric value
|
|
230
|
+
* is displayed.
|
|
231
|
+
*/
|
|
232
|
+
toString(): string {
|
|
233
|
+
return this.name();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ===========================================================================
|
|
237
|
+
// CBOR Encoding (CborTaggedEncodable interface)
|
|
238
|
+
// ===========================================================================
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Returns the CBOR tags associated with KnownValue.
|
|
242
|
+
*
|
|
243
|
+
* The primary tag is TAG_KNOWN_VALUE (201).
|
|
244
|
+
*
|
|
245
|
+
* @returns Array containing the KnownValue tag
|
|
246
|
+
*/
|
|
247
|
+
cborTags(): Tag[] {
|
|
248
|
+
return [KNOWN_VALUE_TAG];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Returns the untagged CBOR encoding of this KnownValue.
|
|
253
|
+
*
|
|
254
|
+
* The untagged representation is simply the unsigned integer value.
|
|
255
|
+
*
|
|
256
|
+
* @returns CBOR representation of the value (unsigned integer)
|
|
257
|
+
*/
|
|
258
|
+
untaggedCbor(): Cbor {
|
|
259
|
+
return cbor(this._value);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Returns the tagged CBOR encoding of this KnownValue.
|
|
264
|
+
*
|
|
265
|
+
* This wraps the unsigned integer value with tag 201.
|
|
266
|
+
*
|
|
267
|
+
* @returns Tagged CBOR representation
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* const kv = new KnownValue(1, 'isA');
|
|
272
|
+
* const tagged = kv.taggedCbor();
|
|
273
|
+
* console.log(tagged.toHex()); // "d8c901" (tag 201, value 1)
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
taggedCbor(): Cbor {
|
|
277
|
+
return cbor({
|
|
278
|
+
tag: TAG_KNOWN_VALUE,
|
|
279
|
+
value: this._value,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Returns the tagged CBOR encoding as binary data.
|
|
285
|
+
*
|
|
286
|
+
* @returns Binary CBOR representation
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```typescript
|
|
290
|
+
* const kv = new KnownValue(1, 'isA');
|
|
291
|
+
* const bytes = kv.toCborData();
|
|
292
|
+
* // bytes is Uint8Array containing the CBOR encoding
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
toCborData(): Uint8Array {
|
|
296
|
+
return cborData(this.taggedCbor());
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Alias for `toCborData()` to match the dcbor interface.
|
|
301
|
+
*/
|
|
302
|
+
taggedCborData(): Uint8Array {
|
|
303
|
+
return this.toCborData();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ===========================================================================
|
|
307
|
+
// CBOR Decoding (CborTaggedDecodable interface)
|
|
308
|
+
// ===========================================================================
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Creates a KnownValue from untagged CBOR (an unsigned integer).
|
|
312
|
+
* Instance method for interface compliance.
|
|
313
|
+
*
|
|
314
|
+
* @param cborValue - The CBOR value (must be an unsigned integer)
|
|
315
|
+
* @returns A new KnownValue
|
|
316
|
+
* @throws {Error} If the CBOR is not an unsigned integer
|
|
317
|
+
*/
|
|
318
|
+
fromUntaggedCbor(cborValue: Cbor): KnownValue {
|
|
319
|
+
return KnownValue.fromUntaggedCbor(cborValue);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Creates a KnownValue from tagged CBOR (tag 201).
|
|
324
|
+
* Instance method for interface compliance.
|
|
325
|
+
*
|
|
326
|
+
* @param cborValue - The tagged CBOR value
|
|
327
|
+
* @returns A new KnownValue
|
|
328
|
+
* @throws {Error} If the CBOR is not properly tagged or contains invalid data
|
|
329
|
+
*/
|
|
330
|
+
fromTaggedCbor(cborValue: Cbor): KnownValue {
|
|
331
|
+
return KnownValue.fromTaggedCbor(cborValue);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ===========================================================================
|
|
335
|
+
// Static Factory Methods
|
|
336
|
+
// ===========================================================================
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Creates a KnownValue from untagged CBOR (an unsigned integer).
|
|
340
|
+
*
|
|
341
|
+
* @param cborValue - The CBOR value (must be an unsigned integer)
|
|
342
|
+
* @returns A new KnownValue
|
|
343
|
+
* @throws {Error} If the CBOR is not an unsigned integer
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```typescript
|
|
347
|
+
* const cborValue = cbor(42);
|
|
348
|
+
* const kv = KnownValue.fromUntaggedCbor(cborValue);
|
|
349
|
+
* console.log(kv.value()); // 42
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
static fromUntaggedCbor(cborValue: Cbor): KnownValue {
|
|
353
|
+
if (cborValue.type !== MajorType.Unsigned) {
|
|
354
|
+
throw new Error(`Expected unsigned integer for KnownValue, got major type ${cborValue.type}`);
|
|
355
|
+
}
|
|
356
|
+
const numValue = cborValue.value as number | bigint;
|
|
357
|
+
return new KnownValue(typeof numValue === "bigint" ? numValue : BigInt(numValue));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Creates a KnownValue from tagged CBOR (tag 201).
|
|
362
|
+
*
|
|
363
|
+
* @param cborValue - The tagged CBOR value
|
|
364
|
+
* @returns A new KnownValue
|
|
365
|
+
* @throws {Error} If the CBOR is not properly tagged or contains invalid data
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```typescript
|
|
369
|
+
* const kv = KnownValue.fromTaggedCbor(taggedCborValue);
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
static fromTaggedCbor(cborValue: Cbor): KnownValue {
|
|
373
|
+
if (cborValue.type !== MajorType.Tagged) {
|
|
374
|
+
throw new Error(`Expected tagged CBOR for KnownValue, got major type ${cborValue.type}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const tag = cborValue.tag;
|
|
378
|
+
if (tag !== BigInt(TAG_KNOWN_VALUE) && tag !== TAG_KNOWN_VALUE) {
|
|
379
|
+
throw new Error(`Expected tag ${TAG_KNOWN_VALUE} for KnownValue, got ${tag}`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return KnownValue.fromUntaggedCbor(cborValue.value);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Creates a KnownValue from binary CBOR data.
|
|
387
|
+
*
|
|
388
|
+
* @param data - Binary CBOR data (must be a tagged KnownValue)
|
|
389
|
+
* @returns A new KnownValue
|
|
390
|
+
* @throws {Error} If the data cannot be decoded or is not a valid KnownValue
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* const bytes = new Uint8Array([0xd8, 0xc9, 0x01]); // tag 201, value 1
|
|
395
|
+
* const kv = KnownValue.fromCborData(bytes);
|
|
396
|
+
* console.log(kv.value()); // 1
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
static fromCborData(data: Uint8Array): KnownValue {
|
|
400
|
+
const cborValue = decodeCbor(data);
|
|
401
|
+
return KnownValue.fromTaggedCbor(cborValue);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Creates a KnownValue from a CBOR value, automatically detecting
|
|
406
|
+
* whether it's tagged or untagged.
|
|
407
|
+
*
|
|
408
|
+
* @param cborValue - The CBOR value (tagged or untagged)
|
|
409
|
+
* @returns A new KnownValue
|
|
410
|
+
* @throws {Error} If the CBOR cannot be converted to a KnownValue
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* ```typescript
|
|
414
|
+
* // Works with both tagged and untagged
|
|
415
|
+
* const kv1 = KnownValue.fromCbor(cbor(42));
|
|
416
|
+
* const kv2 = KnownValue.fromCbor(taggedCborValue);
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
static fromCbor(cborValue: Cbor): KnownValue {
|
|
420
|
+
if (cborValue.type === MajorType.Tagged) {
|
|
421
|
+
return KnownValue.fromTaggedCbor(cborValue);
|
|
422
|
+
}
|
|
423
|
+
return KnownValue.fromUntaggedCbor(cborValue);
|
|
424
|
+
}
|
|
425
|
+
}
|