@bcts/envelope 1.0.0-alpha.5

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 (44) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +23 -0
  3. package/dist/index.cjs +2646 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +978 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +978 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +2644 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +2552 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +85 -0
  14. package/src/base/assertion.ts +179 -0
  15. package/src/base/assertions.ts +304 -0
  16. package/src/base/cbor.ts +122 -0
  17. package/src/base/digest.ts +204 -0
  18. package/src/base/elide.ts +526 -0
  19. package/src/base/envelope-decodable.ts +229 -0
  20. package/src/base/envelope-encodable.ts +71 -0
  21. package/src/base/envelope.ts +790 -0
  22. package/src/base/error.ts +421 -0
  23. package/src/base/index.ts +56 -0
  24. package/src/base/leaf.ts +226 -0
  25. package/src/base/queries.ts +374 -0
  26. package/src/base/walk.ts +241 -0
  27. package/src/base/wrap.ts +72 -0
  28. package/src/extension/attachment.ts +369 -0
  29. package/src/extension/compress.ts +293 -0
  30. package/src/extension/encrypt.ts +379 -0
  31. package/src/extension/expression.ts +404 -0
  32. package/src/extension/index.ts +72 -0
  33. package/src/extension/proof.ts +276 -0
  34. package/src/extension/recipient.ts +557 -0
  35. package/src/extension/salt.ts +223 -0
  36. package/src/extension/signature.ts +463 -0
  37. package/src/extension/types.ts +222 -0
  38. package/src/format/diagnostic.ts +116 -0
  39. package/src/format/hex.ts +25 -0
  40. package/src/format/index.ts +13 -0
  41. package/src/format/tree.ts +168 -0
  42. package/src/index.ts +32 -0
  43. package/src/utils/index.ts +8 -0
  44. package/src/utils/string.ts +48 -0
@@ -0,0 +1,404 @@
1
+ import { Envelope } from "../base/envelope";
2
+ import { type EnvelopeEncodableValue } from "../base/envelope-encodable";
3
+ import { EnvelopeError } from "../base/error";
4
+
5
+ /// Extension for envelope expressions.
6
+ ///
7
+ /// This module implements the Gordian Envelope expression syntax as specified
8
+ /// in BCR-2023-012. Expressions enable encoding of machine-evaluatable
9
+ /// expressions using envelopes, providing a foundation for distributed
10
+ /// function calls and computation.
11
+ ///
12
+ /// ## Expression Structure
13
+ ///
14
+ /// An expression consists of:
15
+ /// - A function identifier (the subject)
16
+ /// - Zero or more parameters (as assertions)
17
+ /// - Optional metadata (non-parameter assertions)
18
+ ///
19
+ /// ## CBOR Tags
20
+ ///
21
+ /// - Function: #6.40006
22
+ /// - Parameter: #6.40007
23
+ /// - Placeholder: #6.40008
24
+ /// - Replacement: #6.40009
25
+ ///
26
+ /// @example
27
+ /// ```typescript
28
+ /// // Create a simple addition expression: add(lhs: 2, rhs: 3)
29
+ /// const expr = new Function('add')
30
+ /// .withParameter('lhs', 2)
31
+ /// .withParameter('rhs', 3);
32
+ ///
33
+ /// const envelope = expr.envelope();
34
+ /// ```
35
+
36
+ /// CBOR tag for function identifiers
37
+ export const CBOR_TAG_FUNCTION = 40006;
38
+
39
+ /// CBOR tag for parameter identifiers
40
+ export const CBOR_TAG_PARAMETER = 40007;
41
+
42
+ /// CBOR tag for placeholder identifiers
43
+ export const CBOR_TAG_PLACEHOLDER = 40008;
44
+
45
+ /// CBOR tag for replacement identifiers
46
+ export const CBOR_TAG_REPLACEMENT = 40009;
47
+
48
+ /// Well-known function identifiers (numeric)
49
+ export const FUNCTION_IDS = {
50
+ ADD: 1, // addition
51
+ SUB: 2, // subtraction
52
+ MUL: 3, // multiplication
53
+ DIV: 4, // division
54
+ NEG: 5, // unary negation
55
+ LT: 6, // less than
56
+ LE: 7, // less than or equal
57
+ GT: 8, // greater than
58
+ GE: 9, // greater than or equal
59
+ EQ: 10, // equal to
60
+ NE: 11, // not equal to
61
+ AND: 12, // logical and
62
+ OR: 13, // logical or
63
+ XOR: 14, // logical xor
64
+ NOT: 15, // logical not
65
+ } as const;
66
+
67
+ /// Well-known parameter identifiers (numeric)
68
+ export const PARAMETER_IDS = {
69
+ BLANK: 1, // blank/implicit parameter (_)
70
+ LHS: 2, // left-hand side
71
+ RHS: 3, // right-hand side
72
+ } as const;
73
+
74
+ /// Type for function identifier (number or string)
75
+ export type FunctionID = number | string;
76
+
77
+ /// Type for parameter identifier (number or string)
78
+ export type ParameterID = number | string;
79
+
80
+ /// Represents a function identifier in an expression
81
+ export class Function {
82
+ readonly #id: FunctionID;
83
+
84
+ constructor(id: FunctionID) {
85
+ this.#id = id;
86
+ }
87
+
88
+ /// Returns the function identifier
89
+ id(): FunctionID {
90
+ return this.#id;
91
+ }
92
+
93
+ /// Returns true if this is a numeric function ID
94
+ isNumeric(): boolean {
95
+ return typeof this.#id === "number";
96
+ }
97
+
98
+ /// Returns true if this is a string function ID
99
+ isString(): boolean {
100
+ return typeof this.#id === "string";
101
+ }
102
+
103
+ /// Creates an expression envelope with this function as the subject
104
+ envelope(): Envelope {
105
+ // For now, create a simple envelope with the function ID
106
+ // In a full implementation, this would use CBOR tag 40006
107
+ const functionStr = typeof this.#id === "number" ? `«${this.#id}»` : `«"${this.#id}"»`;
108
+ return Envelope.new(functionStr);
109
+ }
110
+
111
+ /// Creates an expression with a parameter
112
+ withParameter(param: ParameterID, value: EnvelopeEncodableValue): Expression {
113
+ const expr = new Expression(this);
114
+ return expr.withParameter(param, value);
115
+ }
116
+
117
+ /// Creates a function from a known numeric ID
118
+ static fromNumeric(id: number): Function {
119
+ return new Function(id);
120
+ }
121
+
122
+ /// Creates a function from a string name
123
+ static fromString(name: string): Function {
124
+ return new Function(name);
125
+ }
126
+
127
+ /// Returns a string representation for display
128
+ toString(): string {
129
+ return typeof this.#id === "number" ? `«${this.#id}»` : `«"${this.#id}"»`;
130
+ }
131
+ }
132
+
133
+ /// Represents a parameter in an expression
134
+ export class Parameter {
135
+ readonly #id: ParameterID;
136
+ readonly #value: Envelope;
137
+
138
+ constructor(id: ParameterID, value: Envelope) {
139
+ this.#id = id;
140
+ this.#value = value;
141
+ }
142
+
143
+ /// Returns the parameter identifier
144
+ id(): ParameterID {
145
+ return this.#id;
146
+ }
147
+
148
+ /// Returns the parameter value as an envelope
149
+ value(): Envelope {
150
+ return this.#value;
151
+ }
152
+
153
+ /// Returns true if this is a numeric parameter ID
154
+ isNumeric(): boolean {
155
+ return typeof this.#id === "number";
156
+ }
157
+
158
+ /// Returns true if this is a string parameter ID
159
+ isString(): boolean {
160
+ return typeof this.#id === "string";
161
+ }
162
+
163
+ /// Creates a parameter envelope
164
+ /// In a full implementation, this would use CBOR tag 40007
165
+ envelope(): Envelope {
166
+ const paramStr = typeof this.#id === "number" ? `❰${this.#id}❱` : `❰"${this.#id}"❱`;
167
+ return Envelope.newAssertion(paramStr, this.#value);
168
+ }
169
+
170
+ /// Creates a parameter from known IDs
171
+ static blank(value: EnvelopeEncodableValue): Parameter {
172
+ return new Parameter(PARAMETER_IDS.BLANK, Envelope.new(value));
173
+ }
174
+
175
+ static lhs(value: EnvelopeEncodableValue): Parameter {
176
+ return new Parameter(PARAMETER_IDS.LHS, Envelope.new(value));
177
+ }
178
+
179
+ static rhs(value: EnvelopeEncodableValue): Parameter {
180
+ return new Parameter(PARAMETER_IDS.RHS, Envelope.new(value));
181
+ }
182
+
183
+ /// Returns a string representation for display
184
+ toString(): string {
185
+ const idStr = typeof this.#id === "number" ? `❰${this.#id}❱` : `❰"${this.#id}"❱`;
186
+ return `${idStr}: ${this.#value.asText()}`;
187
+ }
188
+ }
189
+
190
+ /// Represents a complete expression with function and parameters
191
+ export class Expression {
192
+ readonly #function: Function;
193
+ readonly #parameters = new Map<string, Parameter>();
194
+ #envelope: Envelope | null = null;
195
+
196
+ constructor(func: Function) {
197
+ this.#function = func;
198
+ }
199
+
200
+ /// Returns the function
201
+ function(): Function {
202
+ return this.#function;
203
+ }
204
+
205
+ /// Returns all parameters
206
+ parameters(): Parameter[] {
207
+ return Array.from(this.#parameters.values());
208
+ }
209
+
210
+ /// Adds a parameter to the expression
211
+ withParameter(param: ParameterID, value: EnvelopeEncodableValue): Expression {
212
+ const key = typeof param === "number" ? param.toString() : param;
213
+ this.#parameters.set(key, new Parameter(param, Envelope.new(value)));
214
+ this.#envelope = null; // Invalidate cached envelope
215
+ return this;
216
+ }
217
+
218
+ /// Adds multiple parameters at once
219
+ withParameters(params: Record<string, EnvelopeEncodableValue>): Expression {
220
+ for (const [key, value] of Object.entries(params)) {
221
+ this.withParameter(key, value);
222
+ }
223
+ return this;
224
+ }
225
+
226
+ /// Gets a parameter value by ID
227
+ getParameter(param: ParameterID): Envelope | undefined {
228
+ const key = typeof param === "number" ? param.toString() : param;
229
+ return this.#parameters.get(key)?.value();
230
+ }
231
+
232
+ /// Checks if a parameter exists
233
+ hasParameter(param: ParameterID): boolean {
234
+ const key = typeof param === "number" ? param.toString() : param;
235
+ return this.#parameters.has(key);
236
+ }
237
+
238
+ /// Converts the expression to an envelope
239
+ envelope(): Envelope {
240
+ if (this.#envelope !== null) {
241
+ return this.#envelope;
242
+ }
243
+
244
+ // Start with function envelope
245
+ let env = this.#function.envelope();
246
+
247
+ // Add all parameters as assertions
248
+ for (const param of this.#parameters.values()) {
249
+ const paramEnv = param.envelope();
250
+ // Extract the assertion from the parameter envelope
251
+ const assertion = paramEnv.assertions()[0];
252
+ if (assertion !== undefined) {
253
+ const predicate = assertion.subject().asPredicate();
254
+ const object = assertion.subject().asObject();
255
+ if (predicate !== undefined && object !== undefined) {
256
+ env = env.addAssertion(predicate.asText(), object);
257
+ }
258
+ }
259
+ }
260
+
261
+ this.#envelope = env;
262
+ return env;
263
+ }
264
+
265
+ /// Creates an expression from an envelope
266
+ /// Note: This is a simplified implementation
267
+ static fromEnvelope(envelope: Envelope): Expression {
268
+ // Extract function from subject
269
+ const subject = envelope.subject();
270
+ const subjectText = subject.asText();
271
+ if (subjectText === undefined) {
272
+ throw EnvelopeError.general("Not a valid function envelope");
273
+ }
274
+
275
+ // Parse function identifier
276
+ let funcId: FunctionID;
277
+ if (subjectText.startsWith("«") && subjectText.endsWith("»")) {
278
+ const inner = subjectText.slice(1, -1);
279
+ if (inner.startsWith('"') && inner.endsWith('"')) {
280
+ funcId = inner.slice(1, -1); // String function
281
+ } else {
282
+ funcId = parseInt(inner, 10); // Numeric function
283
+ }
284
+ } else {
285
+ throw EnvelopeError.general("Not a valid function envelope");
286
+ }
287
+
288
+ const func = new Function(funcId);
289
+ const expr = new Expression(func);
290
+
291
+ // Extract parameters from assertions
292
+ for (const assertion of envelope.assertions()) {
293
+ try {
294
+ const pred = assertion.subject().asPredicate();
295
+ const obj = assertion.subject().asObject();
296
+
297
+ if (pred !== undefined && obj !== undefined) {
298
+ const predText = pred.asText();
299
+ if (predText !== undefined && predText.startsWith("❰") && predText.endsWith("❱")) {
300
+ const inner = predText.slice(1, -1);
301
+ let paramId: ParameterID;
302
+ if (inner.startsWith('"') && inner.endsWith('"')) {
303
+ paramId = inner.slice(1, -1);
304
+ } else {
305
+ paramId = parseInt(inner, 10);
306
+ }
307
+ expr.withParameter(paramId, obj);
308
+ }
309
+ }
310
+ } catch {
311
+ // Skip non-parameter assertions
312
+ continue;
313
+ }
314
+ }
315
+
316
+ return expr;
317
+ }
318
+
319
+ /// Returns a string representation for display
320
+ toString(): string {
321
+ const params = Array.from(this.#parameters.values())
322
+ .map((p) => p.toString())
323
+ .join(", ");
324
+ return `${this.#function.toString()} [${params}]`;
325
+ }
326
+ }
327
+
328
+ /// Helper functions for creating common expressions
329
+
330
+ /// Creates an addition expression: lhs + rhs
331
+ export function add(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
332
+ return Function.fromNumeric(FUNCTION_IDS.ADD)
333
+ .withParameter(PARAMETER_IDS.LHS, lhs)
334
+ .withParameter(PARAMETER_IDS.RHS, rhs);
335
+ }
336
+
337
+ /// Creates a subtraction expression: lhs - rhs
338
+ export function sub(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
339
+ return Function.fromNumeric(FUNCTION_IDS.SUB)
340
+ .withParameter(PARAMETER_IDS.LHS, lhs)
341
+ .withParameter(PARAMETER_IDS.RHS, rhs);
342
+ }
343
+
344
+ /// Creates a multiplication expression: lhs * rhs
345
+ export function mul(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
346
+ return Function.fromNumeric(FUNCTION_IDS.MUL)
347
+ .withParameter(PARAMETER_IDS.LHS, lhs)
348
+ .withParameter(PARAMETER_IDS.RHS, rhs);
349
+ }
350
+
351
+ /// Creates a division expression: lhs / rhs
352
+ export function div(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
353
+ return Function.fromNumeric(FUNCTION_IDS.DIV)
354
+ .withParameter(PARAMETER_IDS.LHS, lhs)
355
+ .withParameter(PARAMETER_IDS.RHS, rhs);
356
+ }
357
+
358
+ /// Creates a negation expression: -value
359
+ export function neg(value: EnvelopeEncodableValue): Expression {
360
+ return Function.fromNumeric(FUNCTION_IDS.NEG).withParameter(PARAMETER_IDS.BLANK, value);
361
+ }
362
+
363
+ /// Creates a less-than expression: lhs < rhs
364
+ export function lt(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
365
+ return Function.fromNumeric(FUNCTION_IDS.LT)
366
+ .withParameter(PARAMETER_IDS.LHS, lhs)
367
+ .withParameter(PARAMETER_IDS.RHS, rhs);
368
+ }
369
+
370
+ /// Creates a greater-than expression: lhs > rhs
371
+ export function gt(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
372
+ return Function.fromNumeric(FUNCTION_IDS.GT)
373
+ .withParameter(PARAMETER_IDS.LHS, lhs)
374
+ .withParameter(PARAMETER_IDS.RHS, rhs);
375
+ }
376
+
377
+ /// Creates an equality expression: lhs == rhs
378
+ export function eq(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
379
+ return Function.fromNumeric(FUNCTION_IDS.EQ)
380
+ .withParameter(PARAMETER_IDS.LHS, lhs)
381
+ .withParameter(PARAMETER_IDS.RHS, rhs);
382
+ }
383
+
384
+ /// Creates a logical AND expression: lhs && rhs
385
+ export function and(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
386
+ return Function.fromNumeric(FUNCTION_IDS.AND)
387
+ .withParameter(PARAMETER_IDS.LHS, lhs)
388
+ .withParameter(PARAMETER_IDS.RHS, rhs);
389
+ }
390
+
391
+ /// Creates a logical OR expression: lhs || rhs
392
+ export function or(lhs: EnvelopeEncodableValue, rhs: EnvelopeEncodableValue): Expression {
393
+ return Function.fromNumeric(FUNCTION_IDS.OR)
394
+ .withParameter(PARAMETER_IDS.LHS, lhs)
395
+ .withParameter(PARAMETER_IDS.RHS, rhs);
396
+ }
397
+
398
+ /// Creates a logical NOT expression: !value
399
+ export function not(value: EnvelopeEncodableValue): Expression {
400
+ return Function.fromNumeric(FUNCTION_IDS.NOT).withParameter(PARAMETER_IDS.BLANK, value);
401
+ }
402
+
403
+ // Export types and classes
404
+ export {};
@@ -0,0 +1,72 @@
1
+ /// Extension module exports for Gordian Envelope.
2
+ ///
3
+ /// This module provides extended functionality for working with Gordian
4
+ /// Envelopes, including type system support, encryption, compression,
5
+ /// signatures, and more.
6
+
7
+ // Type system
8
+ export { IS_A } from "./types";
9
+
10
+ // Salt support
11
+ export { SALT } from "./salt";
12
+
13
+ // Compression support
14
+ export { Compressed, registerCompressExtension } from "./compress";
15
+
16
+ // Encryption support
17
+ export { SymmetricKey, EncryptedMessage, registerEncryptExtension } from "./encrypt";
18
+
19
+ // Signature support
20
+ export {
21
+ Signature,
22
+ SigningPrivateKey,
23
+ SigningPublicKey,
24
+ SignatureMetadata,
25
+ type Signer,
26
+ type Verifier,
27
+ SIGNED,
28
+ VERIFIED_BY,
29
+ NOTE,
30
+ } from "./signature";
31
+
32
+ // Attachment support
33
+ export { Attachments, ATTACHMENT, VENDOR, CONFORMS_TO } from "./attachment";
34
+
35
+ // Recipient support (public-key encryption)
36
+ export { PublicKeyBase, PrivateKeyBase, SealedMessage, HAS_RECIPIENT } from "./recipient";
37
+
38
+ // Expression support
39
+ export {
40
+ Function,
41
+ Parameter,
42
+ Expression,
43
+ FUNCTION_IDS,
44
+ PARAMETER_IDS,
45
+ CBOR_TAG_FUNCTION,
46
+ CBOR_TAG_PARAMETER,
47
+ CBOR_TAG_PLACEHOLDER,
48
+ CBOR_TAG_REPLACEMENT,
49
+ add,
50
+ sub,
51
+ mul,
52
+ div,
53
+ neg,
54
+ lt,
55
+ gt,
56
+ eq,
57
+ and,
58
+ or,
59
+ not,
60
+ type FunctionID,
61
+ type ParameterID,
62
+ } from "./expression";
63
+
64
+ // Import side-effect modules to register prototype extensions
65
+ import "./types";
66
+ import "./salt";
67
+ import "./compress";
68
+ import "./encrypt";
69
+ import "./signature";
70
+ import "./attachment";
71
+ import "./recipient";
72
+ import "./proof";