@bcts/gstp 1.0.0-alpha.22 → 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.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { t as __exportAll } from "./chunk-DQk6qfdC.mjs";
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
7
  //#region src/error.ts
8
8
  /**
9
9
  * Copyright © 2023-2026 Blockchain Commons, LLC
@@ -58,58 +58,57 @@ var GstpError = class GstpError extends Error {
58
58
  * Returned when the sender is missing an encryption key.
59
59
  */
60
60
  static senderMissingEncryptionKey() {
61
- return new GstpError(GstpErrorCode.SENDER_MISSING_ENCRYPTION_KEY, "sender must have an encryption key");
61
+ return new GstpError("SENDER_MISSING_ENCRYPTION_KEY", "sender must have an encryption key");
62
62
  }
63
63
  /**
64
64
  * Returned when the recipient is missing an encryption key.
65
65
  */
66
66
  static recipientMissingEncryptionKey() {
67
- return new GstpError(GstpErrorCode.RECIPIENT_MISSING_ENCRYPTION_KEY, "recipient must have an encryption key");
67
+ return new GstpError("RECIPIENT_MISSING_ENCRYPTION_KEY", "recipient must have an encryption key");
68
68
  }
69
69
  /**
70
70
  * Returned when the sender is missing a verification key.
71
71
  */
72
72
  static senderMissingVerificationKey() {
73
- return new GstpError(GstpErrorCode.SENDER_MISSING_VERIFICATION_KEY, "sender must have a verification key");
73
+ return new GstpError("SENDER_MISSING_VERIFICATION_KEY", "sender must have a verification key");
74
74
  }
75
75
  /**
76
76
  * Returned when the continuation has expired.
77
77
  */
78
78
  static continuationExpired() {
79
- return new GstpError(GstpErrorCode.CONTINUATION_EXPIRED, "continuation expired");
79
+ return new GstpError("CONTINUATION_EXPIRED", "continuation expired");
80
80
  }
81
81
  /**
82
82
  * Returned when the continuation ID is invalid.
83
83
  */
84
84
  static continuationIdInvalid() {
85
- return new GstpError(GstpErrorCode.CONTINUATION_ID_INVALID, "continuation ID invalid");
85
+ return new GstpError("CONTINUATION_ID_INVALID", "continuation ID invalid");
86
86
  }
87
87
  /**
88
88
  * Returned when the peer continuation is not encrypted.
89
89
  */
90
90
  static peerContinuationNotEncrypted() {
91
- return new GstpError(GstpErrorCode.PEER_CONTINUATION_NOT_ENCRYPTED, "peer continuation must be encrypted");
91
+ return new GstpError("PEER_CONTINUATION_NOT_ENCRYPTED", "peer continuation must be encrypted");
92
92
  }
93
93
  /**
94
94
  * Returned when a request is missing the peer continuation.
95
95
  */
96
96
  static missingPeerContinuation() {
97
- return new GstpError(GstpErrorCode.MISSING_PEER_CONTINUATION, "requests must contain a peer continuation");
97
+ return new GstpError("MISSING_PEER_CONTINUATION", "requests must contain a peer continuation");
98
98
  }
99
99
  /**
100
100
  * Envelope error wrapper.
101
101
  */
102
102
  static envelope(cause) {
103
- return new GstpError(GstpErrorCode.ENVELOPE, "envelope error", cause);
103
+ return new GstpError("ENVELOPE", "envelope error", cause);
104
104
  }
105
105
  /**
106
106
  * XID error wrapper.
107
107
  */
108
108
  static xid(cause) {
109
- return new GstpError(GstpErrorCode.XID, "XID error", cause);
109
+ return new GstpError("XID", "XID error", cause);
110
110
  }
111
111
  };
112
-
113
112
  //#endregion
114
113
  //#region src/continuation.ts
115
114
  /**
@@ -123,7 +122,22 @@ var GstpError = class GstpError extends Error {
123
122
  * eliminating the need for local state storage and enhancing security
124
123
  * for devices with limited storage or requiring distributed state management.
125
124
  *
126
- * 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.
127
141
  */
128
142
  /**
129
143
  * Represents an encrypted state continuation.
@@ -230,8 +244,10 @@ var Continuation = class Continuation {
230
244
  /**
231
245
  * Checks if the continuation is valid at the given time.
232
246
  *
233
- * If no valid_until is set, always returns true.
234
- * If no time is provided, always returns true.
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.
235
251
  *
236
252
  * @param now - The time to check against, or undefined to skip time validation
237
253
  * @returns true if the continuation is valid at the given time
@@ -239,7 +255,7 @@ var Continuation = class Continuation {
239
255
  isValidDate(now) {
240
256
  if (this._validUntil === void 0) return true;
241
257
  if (now === void 0) return true;
242
- return now.getTime() <= this._validUntil.getTime();
258
+ return now.getTime() < this._validUntil.getTime();
243
259
  }
244
260
  /**
245
261
  * Checks if the continuation has the expected ID.
@@ -268,21 +284,39 @@ var Continuation = class Continuation {
268
284
  /**
269
285
  * Converts the continuation to an envelope.
270
286
  *
271
- * If a recipient is provided, the envelope is encrypted to that recipient.
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.
272
298
  *
273
299
  * @param recipient - Optional recipient to encrypt the envelope to
274
300
  * @returns The continuation as an envelope
275
301
  */
276
302
  toEnvelope(recipient) {
277
- let envelope = this._state;
303
+ let envelope = this._state.wrap();
278
304
  if (this._validId !== void 0) envelope = envelope.addAssertion(ID, this._validId);
279
- if (this._validUntil !== void 0) envelope = envelope.addAssertion(VALID_UNTIL, this._validUntil.toISOString());
305
+ if (this._validUntil !== void 0) envelope = envelope.addAssertion(VALID_UNTIL, CborDate.fromDatetime(this._validUntil));
280
306
  if (recipient !== void 0) envelope = envelope.encryptToRecipients([recipient]);
281
307
  return envelope;
282
308
  }
283
309
  /**
284
310
  * Parses a continuation from an envelope.
285
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
+ *
286
320
  * @param encryptedEnvelope - The envelope to parse
287
321
  * @param expectedId - Optional ID to validate against
288
322
  * @param now - Optional time to validate against
@@ -297,23 +331,32 @@ var Continuation = class Continuation {
297
331
  } catch (e) {
298
332
  throw GstpError.envelope(e instanceof Error ? e : new Error(String(e)));
299
333
  }
300
- const state = envelope.subject();
301
- let validId;
334
+ let state;
302
335
  try {
303
- const idObj = envelope.objectForPredicate(ID);
304
- if (idObj !== void 0) {
305
- const leafCbor = idObj.asLeaf();
306
- if (leafCbor !== void 0) validId = ARID.fromTaggedCborData(leafCbor.toData());
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)));
307
348
  }
308
- } catch {}
349
+ }
309
350
  let validUntil;
310
- try {
311
- const validUntilObj = envelope.objectForPredicate(VALID_UNTIL);
312
- if (validUntilObj !== void 0) {
313
- const dateStr = validUntilObj.asText();
314
- if (dateStr !== void 0) validUntil = new Date(dateStr);
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)));
315
358
  }
316
- } catch {}
359
+ }
317
360
  const continuation = new Continuation(state, validId, validUntil);
318
361
  if (!continuation.isValidDate(now)) throw GstpError.continuationExpired();
319
362
  if (!continuation.isValidId(expectedId)) throw GstpError.continuationIdInvalid();
@@ -347,10 +390,50 @@ var Continuation = class Continuation {
347
390
  return `${parts.join(", ")})`;
348
391
  }
349
392
  };
350
-
351
393
  //#endregion
352
394
  //#region src/sealed-request.ts
353
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
+ /**
354
437
  * A sealed request that combines a Request with sender information and
355
438
  * state continuations for secure communication.
356
439
  *
@@ -442,25 +525,32 @@ var SealedRequest = class SealedRequest {
442
525
  }
443
526
  /**
444
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.
445
534
  */
446
535
  objectsForParameter(param) {
447
- const obj = this._request.body().getParameter(param);
448
- return obj !== void 0 ? [obj] : [];
536
+ return this._request.body().objectsForParameter(param);
449
537
  }
450
538
  /**
451
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.
452
549
  */
453
550
  extractObjectForParameter(param) {
454
551
  const envelope = this.objectForParameter(param);
455
552
  if (envelope === void 0) throw GstpError.envelope(/* @__PURE__ */ new Error(`Parameter not found: ${param}`));
456
- return envelope.extractSubject((cbor) => {
457
- if (cbor.isInteger()) return cbor.toInteger();
458
- if (cbor.isText()) return cbor.toText();
459
- if (cbor.isBool()) return cbor.toBool();
460
- if (cbor.isNumber()) return cbor.toNumber();
461
- if (cbor.isByteString()) return cbor.toByteString();
462
- return cbor;
463
- });
553
+ return envelope.extractSubject((cbor) => extractCborAsT(cbor));
464
554
  }
465
555
  /**
466
556
  * Extracts an optional object for a parameter.
@@ -468,14 +558,7 @@ var SealedRequest = class SealedRequest {
468
558
  extractOptionalObjectForParameter(param) {
469
559
  const envelope = this.objectForParameter(param);
470
560
  if (envelope === void 0) return;
471
- return envelope.extractSubject((cbor) => {
472
- if (cbor.isInteger()) return cbor.toInteger();
473
- if (cbor.isText()) return cbor.toText();
474
- if (cbor.isBool()) return cbor.toBool();
475
- if (cbor.isNumber()) return cbor.toNumber();
476
- if (cbor.isByteString()) return cbor.toByteString();
477
- return cbor;
478
- });
561
+ return envelope.extractSubject((cbor) => extractCborAsT(cbor));
479
562
  }
480
563
  /**
481
564
  * Extracts all objects for a parameter as a specific type.
@@ -607,7 +690,7 @@ var SealedRequest = class SealedRequest {
607
690
  */
608
691
  toEnvelopeForRecipients(validUntil, signer, recipients) {
609
692
  const continuation = new Continuation(this._state ?? Envelope.new(null), this.id(), validUntil);
610
- const senderEncryptionKey = this._sender.inceptionKey()?.publicKeys()?.encapsulationPublicKey();
693
+ const senderEncryptionKey = this._sender.encryptionKey();
611
694
  if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
612
695
  const senderContinuation = continuation.toEnvelope(senderEncryptionKey);
613
696
  let result = this._request.toEnvelope();
@@ -649,7 +732,7 @@ var SealedRequest = class SealedRequest {
649
732
  } catch (e) {
650
733
  throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
651
734
  }
652
- const senderVerificationKey = sender.inceptionKey()?.publicKeys()?.signingPublicKey();
735
+ const senderVerificationKey = sender.verificationKey();
653
736
  if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
654
737
  let requestEnvelope;
655
738
  try {
@@ -695,7 +778,6 @@ var SealedRequest = class SealedRequest {
695
778
  return true;
696
779
  }
697
780
  };
698
-
699
781
  //#endregion
700
782
  //#region src/sealed-response.ts
701
783
  /**
@@ -912,7 +994,7 @@ var SealedResponse = class SealedResponse {
912
994
  let senderContinuation;
913
995
  if (this._state !== void 0) {
914
996
  const continuation = new Continuation(this._state, void 0, validUntil);
915
- const senderEncryptionKey = this._sender.inceptionKey()?.publicKeys()?.encapsulationPublicKey();
997
+ const senderEncryptionKey = this._sender.encryptionKey();
916
998
  if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
917
999
  senderContinuation = continuation.toEnvelope(senderEncryptionKey);
918
1000
  }
@@ -955,7 +1037,7 @@ var SealedResponse = class SealedResponse {
955
1037
  } catch (e) {
956
1038
  throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
957
1039
  }
958
- const senderVerificationKey = sender.inceptionKey()?.publicKeys()?.signingPublicKey();
1040
+ const senderVerificationKey = sender.verificationKey();
959
1041
  if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
960
1042
  let responseEnvelope;
961
1043
  try {
@@ -1005,7 +1087,6 @@ var SealedResponse = class SealedResponse {
1005
1087
  return true;
1006
1088
  }
1007
1089
  };
1008
-
1009
1090
  //#endregion
1010
1091
  //#region src/sealed-event.ts
1011
1092
  /**
@@ -1175,7 +1256,7 @@ var SealedEvent = class SealedEvent {
1175
1256
  * @returns The sealed event as an envelope
1176
1257
  */
1177
1258
  toEnvelopeForRecipients(validUntil, signer, recipients) {
1178
- const senderEncryptionKey = this._sender.inceptionKey()?.publicKeys()?.encapsulationPublicKey();
1259
+ const senderEncryptionKey = this._sender.encryptionKey();
1179
1260
  if (senderEncryptionKey === void 0) throw GstpError.senderMissingEncryptionKey();
1180
1261
  let senderContinuation;
1181
1262
  if (this._state !== void 0) senderContinuation = new Continuation(this._state, void 0, validUntil).toEnvelope(senderEncryptionKey);
@@ -1220,7 +1301,7 @@ var SealedEvent = class SealedEvent {
1220
1301
  } catch (e) {
1221
1302
  throw GstpError.xid(e instanceof Error ? e : new Error(String(e)));
1222
1303
  }
1223
- const senderVerificationKey = sender.inceptionKey()?.publicKeys()?.signingPublicKey();
1304
+ const senderVerificationKey = sender.verificationKey();
1224
1305
  if (senderVerificationKey === void 0) throw GstpError.senderMissingVerificationKey();
1225
1306
  let eventEnvelope;
1226
1307
  try {
@@ -1267,7 +1348,6 @@ var SealedEvent = class SealedEvent {
1267
1348
  return true;
1268
1349
  }
1269
1350
  };
1270
-
1271
1351
  //#endregion
1272
1352
  //#region src/prelude.ts
1273
1353
  var prelude_exports = /* @__PURE__ */ __exportAll({
@@ -1278,11 +1358,10 @@ var prelude_exports = /* @__PURE__ */ __exportAll({
1278
1358
  SealedRequest: () => SealedRequest,
1279
1359
  SealedResponse: () => SealedResponse
1280
1360
  });
1281
-
1282
1361
  //#endregion
1283
1362
  //#region src/index.ts
1284
1363
  const VERSION = "0.13.0";
1285
-
1286
1364
  //#endregion
1287
1365
  export { Continuation, GstpError, GstpErrorCode, SealedEvent, SealedRequest, SealedResponse, VERSION, prelude_exports as prelude };
1366
+
1288
1367
  //# sourceMappingURL=index.mjs.map