@bcts/gstp 1.0.0-alpha.23 → 1.0.0-beta.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/dist/index.cjs +130 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +38 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +131 -46
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +130 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/continuation.ts +81 -41
- package/src/sealed-event.ts +9 -6
- package/src/sealed-request.ts +69 -27
- package/src/sealed-response.ts +7 -6
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
2
|
import { ARID } from "@bcts/components";
|
|
3
3
|
import { Envelope, Event, Request, Response } from "@bcts/envelope";
|
|
4
|
+
import { CborDate } from "@bcts/dcbor";
|
|
4
5
|
import { ID, RECIPIENT_CONTINUATION, SENDER, SENDER_CONTINUATION, VALID_UNTIL } from "@bcts/known-values";
|
|
5
6
|
import { XIDDocument } from "@bcts/xid";
|
|
6
7
|
//#region src/error.ts
|
|
@@ -121,7 +122,22 @@ var GstpError = class GstpError extends Error {
|
|
|
121
122
|
* eliminating the need for local state storage and enhancing security
|
|
122
123
|
* for devices with limited storage or requiring distributed state management.
|
|
123
124
|
*
|
|
124
|
-
* Ported from gstp-rust/src/continuation.rs
|
|
125
|
+
* Ported from gstp-rust/src/continuation.rs.
|
|
126
|
+
*
|
|
127
|
+
* Wire shape — mirrors Rust:
|
|
128
|
+
* ```
|
|
129
|
+
* {
|
|
130
|
+
* <state envelope>
|
|
131
|
+
* } [
|
|
132
|
+
* 'id': ARID(...)
|
|
133
|
+
* 'validUntil': Date(...) ← CBOR tag 1, not ISO 8601 text
|
|
134
|
+
* ]
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* The `state` envelope is **wrapped** before assertions are attached,
|
|
138
|
+
* matching Rust `self.state.wrap().add_optional_assertion(...)`. The
|
|
139
|
+
* earlier port attached the assertions directly to the un-wrapped state,
|
|
140
|
+
* producing a different digest tree.
|
|
125
141
|
*/
|
|
126
142
|
/**
|
|
127
143
|
* Represents an encrypted state continuation.
|
|
@@ -228,8 +244,10 @@ var Continuation = class Continuation {
|
|
|
228
244
|
/**
|
|
229
245
|
* Checks if the continuation is valid at the given time.
|
|
230
246
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
247
|
+
* Mirrors Rust `is_valid_date(now)`: at the exact `valid_until`
|
|
248
|
+
* instant, the continuation is **expired** (returns `false`). The
|
|
249
|
+
* earlier port used `<=` here, which differed from Rust by one
|
|
250
|
+
* millisecond at the boundary.
|
|
233
251
|
*
|
|
234
252
|
* @param now - The time to check against, or undefined to skip time validation
|
|
235
253
|
* @returns true if the continuation is valid at the given time
|
|
@@ -237,7 +255,7 @@ var Continuation = class Continuation {
|
|
|
237
255
|
isValidDate(now) {
|
|
238
256
|
if (this._validUntil === void 0) return true;
|
|
239
257
|
if (now === void 0) return true;
|
|
240
|
-
return now.getTime()
|
|
258
|
+
return now.getTime() < this._validUntil.getTime();
|
|
241
259
|
}
|
|
242
260
|
/**
|
|
243
261
|
* Checks if the continuation has the expected ID.
|
|
@@ -266,21 +284,39 @@ var Continuation = class Continuation {
|
|
|
266
284
|
/**
|
|
267
285
|
* Converts the continuation to an envelope.
|
|
268
286
|
*
|
|
269
|
-
*
|
|
287
|
+
* Mirrors Rust `Continuation::to_envelope`:
|
|
288
|
+
*
|
|
289
|
+
* ```rust
|
|
290
|
+
* self.state.wrap()
|
|
291
|
+
* .add_optional_assertion(ID, self.valid_id)
|
|
292
|
+
* .add_optional_assertion(VALID_UNTIL, self.valid_until)
|
|
293
|
+
* ```
|
|
294
|
+
*
|
|
295
|
+
* The state is wrapped first; the optional assertions then live on
|
|
296
|
+
* the wrap node. `valid_until` is encoded as a CBOR-tagged Date
|
|
297
|
+
* (tag 1) — never as a plain ISO 8601 string.
|
|
270
298
|
*
|
|
271
299
|
* @param recipient - Optional recipient to encrypt the envelope to
|
|
272
300
|
* @returns The continuation as an envelope
|
|
273
301
|
*/
|
|
274
302
|
toEnvelope(recipient) {
|
|
275
|
-
let envelope = this._state;
|
|
303
|
+
let envelope = this._state.wrap();
|
|
276
304
|
if (this._validId !== void 0) envelope = envelope.addAssertion(ID, this._validId);
|
|
277
|
-
if (this._validUntil !== void 0) envelope = envelope.addAssertion(VALID_UNTIL, this._validUntil
|
|
305
|
+
if (this._validUntil !== void 0) envelope = envelope.addAssertion(VALID_UNTIL, CborDate.fromDatetime(this._validUntil));
|
|
278
306
|
if (recipient !== void 0) envelope = envelope.encryptToRecipients([recipient]);
|
|
279
307
|
return envelope;
|
|
280
308
|
}
|
|
281
309
|
/**
|
|
282
310
|
* Parses a continuation from an envelope.
|
|
283
311
|
*
|
|
312
|
+
* Mirrors Rust `Continuation::try_from_envelope`:
|
|
313
|
+
*
|
|
314
|
+
* ```rust
|
|
315
|
+
* state: envelope.try_unwrap()?, // unwrap
|
|
316
|
+
* valid_id: envelope.extract_optional_object_for_predicate(ID)?,
|
|
317
|
+
* valid_until: envelope.extract_optional_object_for_predicate(VALID_UNTIL)?,
|
|
318
|
+
* ```
|
|
319
|
+
*
|
|
284
320
|
* @param encryptedEnvelope - The envelope to parse
|
|
285
321
|
* @param expectedId - Optional ID to validate against
|
|
286
322
|
* @param now - Optional time to validate against
|
|
@@ -295,23 +331,32 @@ var Continuation = class Continuation {
|
|
|
295
331
|
} catch (e) {
|
|
296
332
|
throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
|
|
297
333
|
}
|
|
298
|
-
|
|
299
|
-
let validId;
|
|
334
|
+
let state;
|
|
300
335
|
try {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
336
|
+
state = envelope.tryUnwrap();
|
|
337
|
+
} catch (e) {
|
|
338
|
+
throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
|
|
339
|
+
}
|
|
340
|
+
let validId;
|
|
341
|
+
const idObj = envelope.optionalObjectForPredicate(ID);
|
|
342
|
+
if (idObj !== void 0) {
|
|
343
|
+
const leafCbor = idObj.asLeaf();
|
|
344
|
+
if (leafCbor !== void 0) try {
|
|
345
|
+
validId = ARID.fromTaggedCbor(leafCbor);
|
|
346
|
+
} catch (e) {
|
|
347
|
+
throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
|
|
305
348
|
}
|
|
306
|
-
}
|
|
349
|
+
}
|
|
307
350
|
let validUntil;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
351
|
+
const validUntilObj = envelope.optionalObjectForPredicate(VALID_UNTIL);
|
|
352
|
+
if (validUntilObj !== void 0) {
|
|
353
|
+
const leafCbor = validUntilObj.asLeaf();
|
|
354
|
+
if (leafCbor !== void 0) try {
|
|
355
|
+
validUntil = CborDate.fromTaggedCbor(leafCbor).datetime();
|
|
356
|
+
} catch (e) {
|
|
357
|
+
throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
|
|
313
358
|
}
|
|
314
|
-
}
|
|
359
|
+
}
|
|
315
360
|
const continuation = new Continuation(state, validId, validUntil);
|
|
316
361
|
if (!continuation.isValidDate(now)) throw GstpError.continuationExpired();
|
|
317
362
|
if (!continuation.isValidId(expectedId)) throw GstpError.continuationIdInvalid();
|
|
@@ -348,6 +393,47 @@ var Continuation = class Continuation {
|
|
|
348
393
|
//#endregion
|
|
349
394
|
//#region src/sealed-request.ts
|
|
350
395
|
/**
|
|
396
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
397
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
398
|
+
*
|
|
399
|
+
*
|
|
400
|
+
* SealedRequest - Sealed request messages for GSTP
|
|
401
|
+
*
|
|
402
|
+
* A SealedRequest wraps a Request with sender information and state
|
|
403
|
+
* continuations for secure, authenticated request messages.
|
|
404
|
+
*
|
|
405
|
+
* Ported from gstp-rust/src/sealed_request.rs
|
|
406
|
+
*/
|
|
407
|
+
/**
|
|
408
|
+
* Decode a CBOR value into a typed JS value.
|
|
409
|
+
*
|
|
410
|
+
* Mirrors the type-driven `T: TryFrom<CBOR>` dispatch Rust's
|
|
411
|
+
* `extract_object_for_parameter` relies on. TS lacks compile-time
|
|
412
|
+
* trait dispatch, so we hand-roll the most common cases:
|
|
413
|
+
* - tag 1 (`Date`) → JS `Date`,
|
|
414
|
+
* - tag 40012 (`ARID`) → `ARID`,
|
|
415
|
+
* - integer / text / bool / number / byte-string primitives,
|
|
416
|
+
* - everything else → the raw `Cbor` value.
|
|
417
|
+
*
|
|
418
|
+
* Callers needing other typed extraction should use
|
|
419
|
+
* `objectForParameter()` directly and decode the envelope themselves.
|
|
420
|
+
*/
|
|
421
|
+
function extractCborAsT(cbor) {
|
|
422
|
+
const tagged = cbor.asTagged();
|
|
423
|
+
if (tagged !== void 0) {
|
|
424
|
+
const [tag] = tagged;
|
|
425
|
+
const tagNumber = Number(tag.value);
|
|
426
|
+
if (tagNumber === 1) return CborDate.fromTaggedCbor(cbor).datetime();
|
|
427
|
+
if (tagNumber === 40012) return ARID.fromTaggedCbor(cbor);
|
|
428
|
+
}
|
|
429
|
+
if (cbor.isInteger()) return cbor.toInteger();
|
|
430
|
+
if (cbor.isText()) return cbor.toText();
|
|
431
|
+
if (cbor.isBool()) return cbor.toBool();
|
|
432
|
+
if (cbor.isNumber()) return cbor.toNumber();
|
|
433
|
+
if (cbor.isByteString()) return cbor.toByteString();
|
|
434
|
+
return cbor;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
351
437
|
* A sealed request that combines a Request with sender information and
|
|
352
438
|
* state continuations for secure communication.
|
|
353
439
|
*
|
|
@@ -439,25 +525,32 @@ var SealedRequest = class SealedRequest {
|
|
|
439
525
|
}
|
|
440
526
|
/**
|
|
441
527
|
* Returns all objects for a parameter.
|
|
528
|
+
*
|
|
529
|
+
* Mirrors Rust `SealedRequest::objects_for_parameter` which delegates
|
|
530
|
+
* to `Expression::objects_for_parameter`. GSTP requests can carry
|
|
531
|
+
* multiple parameters with the same ID — e.g. a DKG invite has
|
|
532
|
+
* one `participant` per group member — and a decoder must see
|
|
533
|
+
* every one of them.
|
|
442
534
|
*/
|
|
443
535
|
objectsForParameter(param) {
|
|
444
|
-
|
|
445
|
-
return obj !== void 0 ? [obj] : [];
|
|
536
|
+
return this._request.body().objectsForParameter(param);
|
|
446
537
|
}
|
|
447
538
|
/**
|
|
448
539
|
* Extracts an object for a parameter as a specific type.
|
|
540
|
+
*
|
|
541
|
+
* Mirrors Rust `SealedRequest::extract_object_for_parameter` — Rust
|
|
542
|
+
* uses a `T: TryFrom<CBOR>` constraint and dispatches to whatever
|
|
543
|
+
* `From<CBOR> for T` impl is in scope (e.g. tag-1 CBOR decodes to
|
|
544
|
+
* `chrono::DateTime`, tag-40012 to `ARID`, etc.). TS lacks that
|
|
545
|
+
* trait dispatch, so we recognise the most common tagged types
|
|
546
|
+
* (`Date` via tag 1, `ARID` via tag 40012) plus the primitive
|
|
547
|
+
* fall-through. Callers needing other typed extraction should use
|
|
548
|
+
* `objectForParameter()` directly and decode the envelope themselves.
|
|
449
549
|
*/
|
|
450
550
|
extractObjectForParameter(param) {
|
|
451
551
|
const envelope = this.objectForParameter(param);
|
|
452
552
|
if (envelope === void 0) throw GstpError.envelope(/* @__PURE__ */ new Error(`Parameter not found: ${param}`));
|
|
453
|
-
return envelope.extractSubject((cbor) =>
|
|
454
|
-
if (cbor.isInteger()) return cbor.toInteger();
|
|
455
|
-
if (cbor.isText()) return cbor.toText();
|
|
456
|
-
if (cbor.isBool()) return cbor.toBool();
|
|
457
|
-
if (cbor.isNumber()) return cbor.toNumber();
|
|
458
|
-
if (cbor.isByteString()) return cbor.toByteString();
|
|
459
|
-
return cbor;
|
|
460
|
-
});
|
|
553
|
+
return envelope.extractSubject((cbor) => extractCborAsT(cbor));
|
|
461
554
|
}
|
|
462
555
|
/**
|
|
463
556
|
* Extracts an optional object for a parameter.
|
|
@@ -465,14 +558,7 @@ var SealedRequest = class SealedRequest {
|
|
|
465
558
|
extractOptionalObjectForParameter(param) {
|
|
466
559
|
const envelope = this.objectForParameter(param);
|
|
467
560
|
if (envelope === void 0) return;
|
|
468
|
-
return envelope.extractSubject((cbor) =>
|
|
469
|
-
if (cbor.isInteger()) return cbor.toInteger();
|
|
470
|
-
if (cbor.isText()) return cbor.toText();
|
|
471
|
-
if (cbor.isBool()) return cbor.toBool();
|
|
472
|
-
if (cbor.isNumber()) return cbor.toNumber();
|
|
473
|
-
if (cbor.isByteString()) return cbor.toByteString();
|
|
474
|
-
return cbor;
|
|
475
|
-
});
|
|
561
|
+
return envelope.extractSubject((cbor) => extractCborAsT(cbor));
|
|
476
562
|
}
|
|
477
563
|
/**
|
|
478
564
|
* Extracts all objects for a parameter as a specific type.
|
|
@@ -604,7 +690,7 @@ var SealedRequest = class SealedRequest {
|
|
|
604
690
|
*/
|
|
605
691
|
toEnvelopeForRecipients(validUntil, signer, recipients) {
|
|
606
692
|
const continuation = new Continuation(this._state ?? Envelope.new(null), this.id(), validUntil);
|
|
607
|
-
const senderEncryptionKey = this._sender.
|
|
693
|
+
const senderEncryptionKey = this._sender.encryptionKey();
|
|
608
694
|
if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
|
|
609
695
|
const senderContinuation = continuation.toEnvelope(senderEncryptionKey);
|
|
610
696
|
let result = this._request.toEnvelope();
|
|
@@ -646,7 +732,7 @@ var SealedRequest = class SealedRequest {
|
|
|
646
732
|
} catch (e) {
|
|
647
733
|
throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
|
|
648
734
|
}
|
|
649
|
-
const senderVerificationKey = sender.
|
|
735
|
+
const senderVerificationKey = sender.verificationKey();
|
|
650
736
|
if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
|
|
651
737
|
let requestEnvelope;
|
|
652
738
|
try {
|
|
@@ -908,7 +994,7 @@ var SealedResponse = class SealedResponse {
|
|
|
908
994
|
let senderContinuation;
|
|
909
995
|
if (this._state !== void 0) {
|
|
910
996
|
const continuation = new Continuation(this._state, void 0, validUntil);
|
|
911
|
-
const senderEncryptionKey = this._sender.
|
|
997
|
+
const senderEncryptionKey = this._sender.encryptionKey();
|
|
912
998
|
if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
|
|
913
999
|
senderContinuation = continuation.toEnvelope(senderEncryptionKey);
|
|
914
1000
|
}
|
|
@@ -951,7 +1037,7 @@ var SealedResponse = class SealedResponse {
|
|
|
951
1037
|
} catch (e) {
|
|
952
1038
|
throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
|
|
953
1039
|
}
|
|
954
|
-
const senderVerificationKey = sender.
|
|
1040
|
+
const senderVerificationKey = sender.verificationKey();
|
|
955
1041
|
if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
|
|
956
1042
|
let responseEnvelope;
|
|
957
1043
|
try {
|
|
@@ -1170,7 +1256,7 @@ var SealedEvent = class SealedEvent {
|
|
|
1170
1256
|
* @returns The sealed event as an envelope
|
|
1171
1257
|
*/
|
|
1172
1258
|
toEnvelopeForRecipients(validUntil, signer, recipients) {
|
|
1173
|
-
const senderEncryptionKey = this._sender.
|
|
1259
|
+
const senderEncryptionKey = this._sender.encryptionKey();
|
|
1174
1260
|
if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
|
|
1175
1261
|
let senderContinuation;
|
|
1176
1262
|
if (this._state !== void 0) senderContinuation = new Continuation(this._state, void 0, validUntil).toEnvelope(senderEncryptionKey);
|
|
@@ -1215,7 +1301,7 @@ var SealedEvent = class SealedEvent {
|
|
|
1215
1301
|
} catch (e) {
|
|
1216
1302
|
throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
|
|
1217
1303
|
}
|
|
1218
|
-
const senderVerificationKey = sender.
|
|
1304
|
+
const senderVerificationKey = sender.verificationKey();
|
|
1219
1305
|
if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
|
|
1220
1306
|
let eventEnvelope;
|
|
1221
1307
|
try {
|