@bcts/provenance-mark 1.0.0-alpha.11 → 1.0.0-alpha.13
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 +232 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +106 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +233 -5
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +227 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
- package/src/envelope.ts +232 -0
- package/src/generator.ts +131 -1
- package/src/index.ts +11 -0
- package/src/mark.ts +81 -3
package/src/envelope.ts
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Envelope support for Provenance Marks
|
|
3
|
+
*
|
|
4
|
+
* This module provides Gordian Envelope integration for ProvenanceMark and
|
|
5
|
+
* ProvenanceMarkGenerator, enabling them to be used with the bc-envelope
|
|
6
|
+
* ecosystem.
|
|
7
|
+
*
|
|
8
|
+
* Ported from provenance-mark-rust/src/mark.rs and generator.rs (envelope feature)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Envelope } from "@bcts/envelope";
|
|
12
|
+
import { type Cbor } from "@bcts/dcbor";
|
|
13
|
+
import { PROVENANCE_MARK } from "@bcts/tags";
|
|
14
|
+
import { ProvenanceMark } from "./mark.js";
|
|
15
|
+
import { ProvenanceMarkGenerator } from "./generator.js";
|
|
16
|
+
import { resolutionFromCbor, resolutionToNumber } from "./resolution.js";
|
|
17
|
+
import { ProvenanceSeed } from "./seed.js";
|
|
18
|
+
import { RngState } from "./rng-state.js";
|
|
19
|
+
import { ProvenanceMarkError, ProvenanceMarkErrorType } from "./error.js";
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Tag Registration
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Registers provenance mark tags in the global format context.
|
|
27
|
+
*
|
|
28
|
+
* This function sets up a summarizer for the PROVENANCE_MARK tag that displays
|
|
29
|
+
* provenance marks in a human-readable format.
|
|
30
|
+
*/
|
|
31
|
+
export function registerTags(): void {
|
|
32
|
+
// In TypeScript, we don't have a global format context like Rust.
|
|
33
|
+
// Tag summarizers are typically handled at the envelope level.
|
|
34
|
+
// This function is provided for API parity.
|
|
35
|
+
registerTagsIn(globalTagsContext);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Registers provenance mark tags in a specific format context.
|
|
40
|
+
*
|
|
41
|
+
* @param context - The format context to register tags in
|
|
42
|
+
*/
|
|
43
|
+
export function registerTagsIn(context: TagsContext): void {
|
|
44
|
+
context.setSummarizer(Number(PROVENANCE_MARK.value), (cborValue: Cbor) => {
|
|
45
|
+
const mark = ProvenanceMark.fromUntaggedCbor(cborValue);
|
|
46
|
+
return mark.toString();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Simple tags context interface for registration
|
|
51
|
+
export interface TagsContext {
|
|
52
|
+
setSummarizer(tag: number, summarizer: (cbor: Cbor) => string): void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Global tags context (minimal implementation)
|
|
56
|
+
const globalTagsContext: TagsContext = {
|
|
57
|
+
setSummarizer(_tag: number, _summarizer: (cbor: Cbor) => string): void {
|
|
58
|
+
// Tag summarizers are handled by the envelope package's format context
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// ProvenanceMark Envelope Support
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Convert a ProvenanceMark to an Envelope.
|
|
68
|
+
*
|
|
69
|
+
* The envelope contains the tagged CBOR representation of the mark.
|
|
70
|
+
*
|
|
71
|
+
* @param mark - The provenance mark to convert
|
|
72
|
+
* @returns An envelope containing the mark
|
|
73
|
+
*/
|
|
74
|
+
export function provenanceMarkToEnvelope(mark: ProvenanceMark): Envelope {
|
|
75
|
+
return Envelope.new(mark.toCborData());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Extract a ProvenanceMark from an Envelope.
|
|
80
|
+
*
|
|
81
|
+
* @param envelope - The envelope to extract from
|
|
82
|
+
* @returns The extracted provenance mark
|
|
83
|
+
* @throws ProvenanceMarkError if extraction fails
|
|
84
|
+
*/
|
|
85
|
+
export function provenanceMarkFromEnvelope(envelope: Envelope): ProvenanceMark {
|
|
86
|
+
// The envelope contains the CBOR-encoded bytes of the mark
|
|
87
|
+
// Use asByteString to extract the raw bytes, then decode
|
|
88
|
+
const bytes = envelope.asByteString();
|
|
89
|
+
if (bytes !== undefined) {
|
|
90
|
+
return ProvenanceMark.fromCborData(bytes);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Try extracting from subject if it's a node
|
|
94
|
+
const envCase = envelope.case();
|
|
95
|
+
if (envCase.type === "node") {
|
|
96
|
+
const subject = envCase.subject;
|
|
97
|
+
const subjectBytes = subject.asByteString();
|
|
98
|
+
if (subjectBytes !== undefined) {
|
|
99
|
+
return ProvenanceMark.fromCborData(subjectBytes);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
104
|
+
message: "Could not extract ProvenanceMark from envelope",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// ProvenanceMarkGenerator Envelope Support
|
|
110
|
+
// ============================================================================
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Convert a ProvenanceMarkGenerator to an Envelope.
|
|
114
|
+
*
|
|
115
|
+
* The envelope contains structured assertions for all generator fields:
|
|
116
|
+
* - type: "provenance-generator"
|
|
117
|
+
* - res: The resolution
|
|
118
|
+
* - seed: The seed
|
|
119
|
+
* - next-seq: The next sequence number
|
|
120
|
+
* - rng-state: The RNG state
|
|
121
|
+
*
|
|
122
|
+
* @param generator - The generator to convert
|
|
123
|
+
* @returns An envelope containing the generator
|
|
124
|
+
*/
|
|
125
|
+
export function provenanceMarkGeneratorToEnvelope(generator: ProvenanceMarkGenerator): Envelope {
|
|
126
|
+
// Create envelope with chain ID as subject
|
|
127
|
+
let envelope = Envelope.new(generator.chainId());
|
|
128
|
+
|
|
129
|
+
// Add type assertion
|
|
130
|
+
envelope = envelope.addAssertion("isA", "provenance-generator");
|
|
131
|
+
|
|
132
|
+
// Add resolution
|
|
133
|
+
envelope = envelope.addAssertion("res", resolutionToNumber(generator.res()));
|
|
134
|
+
|
|
135
|
+
// Add seed
|
|
136
|
+
envelope = envelope.addAssertion("seed", generator.seed().toBytes());
|
|
137
|
+
|
|
138
|
+
// Add next sequence number
|
|
139
|
+
envelope = envelope.addAssertion("next-seq", generator.nextSeq());
|
|
140
|
+
|
|
141
|
+
// Add RNG state
|
|
142
|
+
envelope = envelope.addAssertion("rng-state", generator.rngState().toBytes());
|
|
143
|
+
|
|
144
|
+
return envelope;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Type extension for envelope with extra methods
|
|
148
|
+
type EnvelopeExt = Envelope & {
|
|
149
|
+
asByteString(): Uint8Array | undefined;
|
|
150
|
+
hasType(t: string): boolean;
|
|
151
|
+
assertionsWithPredicate(p: string): Envelope[];
|
|
152
|
+
subject(): Envelope;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Extract a ProvenanceMarkGenerator from an Envelope.
|
|
157
|
+
*
|
|
158
|
+
* @param envelope - The envelope to extract from
|
|
159
|
+
* @returns The extracted generator
|
|
160
|
+
* @throws ProvenanceMarkError if extraction fails
|
|
161
|
+
*/
|
|
162
|
+
export function provenanceMarkGeneratorFromEnvelope(envelope: Envelope): ProvenanceMarkGenerator {
|
|
163
|
+
const env = envelope as EnvelopeExt;
|
|
164
|
+
|
|
165
|
+
// Check type
|
|
166
|
+
if (!env.hasType("provenance-generator")) {
|
|
167
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
168
|
+
message: "Envelope is not a provenance-generator",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Extract chain ID from subject
|
|
173
|
+
const subject = env.subject() as EnvelopeExt;
|
|
174
|
+
const chainId = subject.asByteString();
|
|
175
|
+
if (chainId === undefined) {
|
|
176
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
177
|
+
message: "Could not extract chain ID",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Helper to extract assertion object value
|
|
182
|
+
const extractAssertion = (predicate: string): { cbor: Cbor; bytes: Uint8Array | undefined } => {
|
|
183
|
+
const assertions = env.assertionsWithPredicate(predicate);
|
|
184
|
+
if (assertions.length === 0) {
|
|
185
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
186
|
+
message: `Missing ${predicate} assertion`,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
const assertionCase = assertions[0].case();
|
|
190
|
+
if (assertionCase.type !== "assertion") {
|
|
191
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
192
|
+
message: `Invalid ${predicate} assertion`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const obj = assertionCase.assertion.object() as EnvelopeExt;
|
|
196
|
+
const objCase = obj.case();
|
|
197
|
+
if (objCase.type === "leaf") {
|
|
198
|
+
return { cbor: objCase.cbor, bytes: obj.asByteString() };
|
|
199
|
+
}
|
|
200
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
201
|
+
message: `Invalid ${predicate} value`,
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Extract resolution
|
|
206
|
+
const resValue = extractAssertion("res");
|
|
207
|
+
const res = resolutionFromCbor(resValue.cbor);
|
|
208
|
+
|
|
209
|
+
// Extract seed
|
|
210
|
+
const seedValue = extractAssertion("seed");
|
|
211
|
+
if (seedValue.bytes === undefined) {
|
|
212
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
213
|
+
message: "Invalid seed data",
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const seed = ProvenanceSeed.fromBytes(seedValue.bytes);
|
|
217
|
+
|
|
218
|
+
// Extract next-seq
|
|
219
|
+
const seqValue = extractAssertion("next-seq");
|
|
220
|
+
const nextSeq = Number(seqValue.cbor);
|
|
221
|
+
|
|
222
|
+
// Extract rng-state
|
|
223
|
+
const rngValue = extractAssertion("rng-state");
|
|
224
|
+
if (rngValue.bytes === undefined) {
|
|
225
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
226
|
+
message: "Invalid rng-state data",
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const rngState = RngState.fromBytes(rngValue.bytes);
|
|
230
|
+
|
|
231
|
+
return ProvenanceMarkGenerator.new(res, seed, chainId, nextSeq, rngState);
|
|
232
|
+
}
|
package/src/generator.ts
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import { toBase64, fromBase64, bytesToHex } from "./utils.js";
|
|
4
4
|
import { type Cbor } from "@bcts/dcbor";
|
|
5
|
+
import { Envelope } from "@bcts/envelope";
|
|
5
6
|
|
|
6
7
|
import { ProvenanceMarkError, ProvenanceMarkErrorType } from "./error.js";
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type ProvenanceMarkResolution,
|
|
10
|
+
linkLength,
|
|
11
|
+
resolutionToNumber,
|
|
12
|
+
resolutionFromCbor,
|
|
13
|
+
} from "./resolution.js";
|
|
8
14
|
import { ProvenanceSeed } from "./seed.js";
|
|
9
15
|
import { RngState } from "./rng-state.js";
|
|
10
16
|
import { sha256 } from "./crypto-utils.js";
|
|
@@ -179,4 +185,128 @@ export class ProvenanceMarkGenerator {
|
|
|
179
185
|
const rngState = RngState.fromBytes(fromBase64(json["rngState"] as string));
|
|
180
186
|
return ProvenanceMarkGenerator.new(res, seed, chainId, nextSeq, rngState);
|
|
181
187
|
}
|
|
188
|
+
|
|
189
|
+
// ============================================================================
|
|
190
|
+
// Envelope Support (EnvelopeEncodable)
|
|
191
|
+
// ============================================================================
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Convert this generator to a Gordian Envelope.
|
|
195
|
+
*
|
|
196
|
+
* The envelope contains structured assertions for all generator fields:
|
|
197
|
+
* - isA: "provenance-generator"
|
|
198
|
+
* - res: The resolution
|
|
199
|
+
* - seed: The seed
|
|
200
|
+
* - next-seq: The next sequence number
|
|
201
|
+
* - rng-state: The RNG state
|
|
202
|
+
*
|
|
203
|
+
* Note: Use provenanceMarkGeneratorToEnvelope() for a standalone function alternative.
|
|
204
|
+
*/
|
|
205
|
+
intoEnvelope(): Envelope {
|
|
206
|
+
// Create envelope with chain ID as subject
|
|
207
|
+
let envelope = Envelope.new(this._chainId);
|
|
208
|
+
|
|
209
|
+
// Add type assertion
|
|
210
|
+
envelope = envelope.addAssertion("isA", "provenance-generator");
|
|
211
|
+
|
|
212
|
+
// Add resolution
|
|
213
|
+
envelope = envelope.addAssertion("res", resolutionToNumber(this._res));
|
|
214
|
+
|
|
215
|
+
// Add seed
|
|
216
|
+
envelope = envelope.addAssertion("seed", this._seed.toBytes());
|
|
217
|
+
|
|
218
|
+
// Add next sequence number
|
|
219
|
+
envelope = envelope.addAssertion("next-seq", this._nextSeq);
|
|
220
|
+
|
|
221
|
+
// Add RNG state
|
|
222
|
+
envelope = envelope.addAssertion("rng-state", this._rngState.toBytes());
|
|
223
|
+
|
|
224
|
+
return envelope;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Extract a ProvenanceMarkGenerator from a Gordian Envelope.
|
|
229
|
+
*
|
|
230
|
+
* @param envelope - The envelope to extract from
|
|
231
|
+
* @returns The extracted generator
|
|
232
|
+
* @throws ProvenanceMarkError if extraction fails
|
|
233
|
+
*/
|
|
234
|
+
static fromEnvelope(envelope: Envelope): ProvenanceMarkGenerator {
|
|
235
|
+
type EnvelopeExt = Envelope & {
|
|
236
|
+
asByteString(): Uint8Array | undefined;
|
|
237
|
+
hasType(t: string): boolean;
|
|
238
|
+
assertionsWithPredicate(p: string): Envelope[];
|
|
239
|
+
subject(): Envelope;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const env = envelope as EnvelopeExt;
|
|
243
|
+
|
|
244
|
+
// Check type
|
|
245
|
+
if (!env.hasType("provenance-generator")) {
|
|
246
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
247
|
+
message: "Envelope is not a provenance-generator",
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Extract chain ID from subject
|
|
252
|
+
const subject = env.subject() as EnvelopeExt;
|
|
253
|
+
const chainId = subject.asByteString();
|
|
254
|
+
if (chainId === undefined) {
|
|
255
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
256
|
+
message: "Could not extract chain ID",
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Helper to extract assertion object value
|
|
261
|
+
const extractAssertion = (predicate: string): { cbor: Cbor; bytes: Uint8Array | undefined } => {
|
|
262
|
+
const assertions = env.assertionsWithPredicate(predicate);
|
|
263
|
+
if (assertions.length === 0) {
|
|
264
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
265
|
+
message: `Missing ${predicate} assertion`,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const assertionCase = assertions[0].case();
|
|
269
|
+
if (assertionCase.type !== "assertion") {
|
|
270
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
271
|
+
message: `Invalid ${predicate} assertion`,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const obj = assertionCase.assertion.object() as EnvelopeExt;
|
|
275
|
+
const objCase = obj.case();
|
|
276
|
+
if (objCase.type === "leaf") {
|
|
277
|
+
return { cbor: objCase.cbor, bytes: obj.asByteString() };
|
|
278
|
+
}
|
|
279
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
280
|
+
message: `Invalid ${predicate} value`,
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Extract resolution
|
|
285
|
+
const resValue = extractAssertion("res");
|
|
286
|
+
const res = resolutionFromCbor(resValue.cbor);
|
|
287
|
+
|
|
288
|
+
// Extract seed
|
|
289
|
+
const seedValue = extractAssertion("seed");
|
|
290
|
+
if (seedValue.bytes === undefined) {
|
|
291
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
292
|
+
message: "Invalid seed data",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const seed = ProvenanceSeed.fromBytes(seedValue.bytes);
|
|
296
|
+
|
|
297
|
+
// Extract next-seq
|
|
298
|
+
const seqValue = extractAssertion("next-seq");
|
|
299
|
+
const nextSeq = Number(seqValue.cbor);
|
|
300
|
+
|
|
301
|
+
// Extract rng-state
|
|
302
|
+
const rngValue = extractAssertion("rng-state");
|
|
303
|
+
if (rngValue.bytes === undefined) {
|
|
304
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
305
|
+
message: "Invalid rng-state data",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
const rngState = RngState.fromBytes(rngValue.bytes);
|
|
309
|
+
|
|
310
|
+
return ProvenanceMarkGenerator.new(res, seed, chainId, nextSeq, rngState);
|
|
311
|
+
}
|
|
182
312
|
}
|
package/src/index.ts
CHANGED
|
@@ -88,3 +88,14 @@ export {
|
|
|
88
88
|
|
|
89
89
|
// Mark Info
|
|
90
90
|
export { ProvenanceMarkInfo } from "./mark-info.js";
|
|
91
|
+
|
|
92
|
+
// Envelope support
|
|
93
|
+
export {
|
|
94
|
+
registerTags,
|
|
95
|
+
registerTagsIn,
|
|
96
|
+
provenanceMarkToEnvelope,
|
|
97
|
+
provenanceMarkFromEnvelope,
|
|
98
|
+
provenanceMarkGeneratorToEnvelope,
|
|
99
|
+
provenanceMarkGeneratorFromEnvelope,
|
|
100
|
+
type TagsContext,
|
|
101
|
+
} from "./envelope.js";
|
package/src/mark.ts
CHANGED
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
decodeBytewords,
|
|
10
10
|
encodeBytewordsIdentifier,
|
|
11
11
|
encodeBytemojisIdentifier,
|
|
12
|
+
UR,
|
|
12
13
|
} from "@bcts/uniform-resources";
|
|
14
|
+
import { Envelope } from "@bcts/envelope";
|
|
13
15
|
|
|
14
16
|
import { ProvenanceMarkError, ProvenanceMarkErrorType } from "./error.js";
|
|
15
17
|
import {
|
|
@@ -403,7 +405,7 @@ export class ProvenanceMark {
|
|
|
403
405
|
}
|
|
404
406
|
|
|
405
407
|
/**
|
|
406
|
-
* Encode for URL (minimal bytewords of CBOR).
|
|
408
|
+
* Encode for URL (minimal bytewords of tagged CBOR).
|
|
407
409
|
*/
|
|
408
410
|
toUrlEncoding(): string {
|
|
409
411
|
return encodeBytewords(this.toCborData(), BytewordsStyle.Minimal);
|
|
@@ -418,6 +420,27 @@ export class ProvenanceMark {
|
|
|
418
420
|
return ProvenanceMark.fromTaggedCbor(cborValue);
|
|
419
421
|
}
|
|
420
422
|
|
|
423
|
+
/**
|
|
424
|
+
* Get the UR string representation (e.g., "ur:provenance/...").
|
|
425
|
+
*/
|
|
426
|
+
urString(): string {
|
|
427
|
+
const ur = UR.new("provenance", this.untaggedCbor());
|
|
428
|
+
return ur.string();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Create from a UR string.
|
|
433
|
+
*/
|
|
434
|
+
static fromURString(urString: string): ProvenanceMark {
|
|
435
|
+
const ur = UR.fromURString(urString);
|
|
436
|
+
if (ur.urTypeStr() !== "provenance") {
|
|
437
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
438
|
+
message: `Expected UR type 'provenance', got '${ur.urTypeStr()}'`,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
return ProvenanceMark.fromUntaggedCbor(ur.cbor());
|
|
442
|
+
}
|
|
443
|
+
|
|
421
444
|
/**
|
|
422
445
|
* Build a URL with this mark as a query parameter.
|
|
423
446
|
*/
|
|
@@ -518,19 +541,29 @@ export class ProvenanceMark {
|
|
|
518
541
|
|
|
519
542
|
/**
|
|
520
543
|
* Detailed debug representation.
|
|
544
|
+
* Matches Rust format exactly for parity.
|
|
521
545
|
*/
|
|
522
546
|
toDebugString(): string {
|
|
547
|
+
// Format date without milliseconds to match Rust format
|
|
548
|
+
const dateStr = this._date.toISOString().replace(".000Z", "Z");
|
|
523
549
|
const components = [
|
|
524
550
|
`key: ${bytesToHex(this._key)}`,
|
|
525
551
|
`hash: ${bytesToHex(this._hash)}`,
|
|
526
552
|
`chainID: ${bytesToHex(this._chainId)}`,
|
|
527
553
|
`seq: ${this._seq}`,
|
|
528
|
-
`date: ${
|
|
554
|
+
`date: ${dateStr}`,
|
|
529
555
|
];
|
|
530
556
|
|
|
531
557
|
const info = this.info();
|
|
532
558
|
if (info !== undefined) {
|
|
533
|
-
|
|
559
|
+
// Format info as the underlying string value, matching Rust Debug format
|
|
560
|
+
const textValue = info.asText();
|
|
561
|
+
if (textValue !== undefined) {
|
|
562
|
+
components.push(`info: "${textValue}"`);
|
|
563
|
+
} else {
|
|
564
|
+
// For non-text values, use diagnostic format
|
|
565
|
+
components.push(`info: ${info.toDiagnostic()}`);
|
|
566
|
+
}
|
|
534
567
|
}
|
|
535
568
|
|
|
536
569
|
return `ProvenanceMark(${components.join(", ")})`;
|
|
@@ -583,6 +616,51 @@ export class ProvenanceMark {
|
|
|
583
616
|
|
|
584
617
|
return new ProvenanceMark(res, key, hash, chainId, seqBytes, dateBytes, infoBytes, seq, date);
|
|
585
618
|
}
|
|
619
|
+
|
|
620
|
+
// ============================================================================
|
|
621
|
+
// Envelope Support (EnvelopeEncodable)
|
|
622
|
+
// ============================================================================
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Convert this provenance mark to a Gordian Envelope.
|
|
626
|
+
*
|
|
627
|
+
* The envelope contains the tagged CBOR representation of the mark.
|
|
628
|
+
*
|
|
629
|
+
* Note: Use provenanceMarkToEnvelope() for a standalone function alternative.
|
|
630
|
+
*/
|
|
631
|
+
intoEnvelope(): Envelope {
|
|
632
|
+
return Envelope.new(this.toCborData());
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Extract a ProvenanceMark from a Gordian Envelope.
|
|
637
|
+
*
|
|
638
|
+
* @param envelope - The envelope to extract from
|
|
639
|
+
* @returns The extracted provenance mark
|
|
640
|
+
* @throws ProvenanceMarkError if extraction fails
|
|
641
|
+
*/
|
|
642
|
+
static fromEnvelope(envelope: Envelope): ProvenanceMark {
|
|
643
|
+
// The envelope contains the CBOR-encoded bytes of the mark
|
|
644
|
+
// Use asByteString to extract the raw bytes, then decode
|
|
645
|
+
const bytes = envelope.asByteString();
|
|
646
|
+
if (bytes !== undefined) {
|
|
647
|
+
return ProvenanceMark.fromCborData(bytes);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Try extracting from subject if it's a node
|
|
651
|
+
const envCase = envelope.case();
|
|
652
|
+
if (envCase.type === "node") {
|
|
653
|
+
const subject = envCase.subject;
|
|
654
|
+
const subjectBytes = subject.asByteString();
|
|
655
|
+
if (subjectBytes !== undefined) {
|
|
656
|
+
return ProvenanceMark.fromCborData(subjectBytes);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
throw new ProvenanceMarkError(ProvenanceMarkErrorType.CborError, undefined, {
|
|
661
|
+
message: "Could not extract ProvenanceMark from envelope",
|
|
662
|
+
});
|
|
663
|
+
}
|
|
586
664
|
}
|
|
587
665
|
|
|
588
666
|
/**
|