@originals/sdk 1.8.0 → 1.8.2

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 (145) hide show
  1. package/dist/utils/hash.js +1 -0
  2. package/package.json +6 -5
  3. package/src/adapters/FeeOracleMock.ts +9 -0
  4. package/src/adapters/index.ts +5 -0
  5. package/src/adapters/providers/OrdHttpProvider.ts +126 -0
  6. package/src/adapters/providers/OrdMockProvider.ts +101 -0
  7. package/src/adapters/types.ts +66 -0
  8. package/src/bitcoin/BitcoinManager.ts +329 -0
  9. package/src/bitcoin/BroadcastClient.ts +54 -0
  10. package/src/bitcoin/OrdinalsClient.ts +120 -0
  11. package/src/bitcoin/PSBTBuilder.ts +106 -0
  12. package/src/bitcoin/fee-calculation.ts +38 -0
  13. package/src/bitcoin/providers/OrdNodeProvider.ts +92 -0
  14. package/src/bitcoin/providers/OrdinalsProvider.ts +56 -0
  15. package/src/bitcoin/providers/types.ts +59 -0
  16. package/src/bitcoin/transactions/commit.ts +465 -0
  17. package/src/bitcoin/transactions/index.ts +13 -0
  18. package/src/bitcoin/transfer.ts +43 -0
  19. package/src/bitcoin/utxo-selection.ts +322 -0
  20. package/src/bitcoin/utxo.ts +113 -0
  21. package/src/cel/ExternalReferenceManager.ts +87 -0
  22. package/src/cel/OriginalsCel.ts +460 -0
  23. package/src/cel/algorithms/createEventLog.ts +68 -0
  24. package/src/cel/algorithms/deactivateEventLog.ts +109 -0
  25. package/src/cel/algorithms/index.ts +11 -0
  26. package/src/cel/algorithms/updateEventLog.ts +99 -0
  27. package/src/cel/algorithms/verifyEventLog.ts +306 -0
  28. package/src/cel/algorithms/witnessEvent.ts +87 -0
  29. package/src/cel/cli/create.ts +330 -0
  30. package/src/cel/cli/index.ts +383 -0
  31. package/src/cel/cli/inspect.ts +549 -0
  32. package/src/cel/cli/migrate.ts +473 -0
  33. package/src/cel/cli/verify.ts +249 -0
  34. package/src/cel/hash.ts +71 -0
  35. package/src/cel/index.ts +16 -0
  36. package/src/cel/layers/BtcoCelManager.ts +408 -0
  37. package/src/cel/layers/PeerCelManager.ts +371 -0
  38. package/src/cel/layers/WebVHCelManager.ts +361 -0
  39. package/src/cel/layers/index.ts +27 -0
  40. package/src/cel/serialization/cbor.ts +189 -0
  41. package/src/cel/serialization/index.ts +10 -0
  42. package/src/cel/serialization/json.ts +209 -0
  43. package/src/cel/types.ts +160 -0
  44. package/src/cel/witnesses/BitcoinWitness.ts +184 -0
  45. package/src/cel/witnesses/HttpWitness.ts +241 -0
  46. package/src/cel/witnesses/WitnessService.ts +51 -0
  47. package/src/cel/witnesses/index.ts +11 -0
  48. package/src/contexts/credentials-v1.json +237 -0
  49. package/src/contexts/credentials-v2-examples.json +5 -0
  50. package/src/contexts/credentials-v2.json +340 -0
  51. package/src/contexts/credentials.json +237 -0
  52. package/src/contexts/data-integrity-v2.json +81 -0
  53. package/src/contexts/dids.json +58 -0
  54. package/src/contexts/ed255192020.json +93 -0
  55. package/src/contexts/ordinals-plus.json +23 -0
  56. package/src/contexts/originals.json +22 -0
  57. package/src/core/OriginalsSDK.ts +420 -0
  58. package/src/crypto/Multikey.ts +194 -0
  59. package/src/crypto/Signer.ts +262 -0
  60. package/src/crypto/noble-init.ts +138 -0
  61. package/src/did/BtcoDidResolver.ts +231 -0
  62. package/src/did/DIDManager.ts +705 -0
  63. package/src/did/Ed25519Verifier.ts +68 -0
  64. package/src/did/KeyManager.ts +239 -0
  65. package/src/did/WebVHManager.ts +499 -0
  66. package/src/did/createBtcoDidDocument.ts +60 -0
  67. package/src/did/providers/OrdinalsClientProviderAdapter.ts +68 -0
  68. package/src/events/EventEmitter.ts +222 -0
  69. package/src/events/index.ts +19 -0
  70. package/src/events/types.ts +331 -0
  71. package/src/examples/basic-usage.ts +78 -0
  72. package/src/examples/create-module-original.ts +435 -0
  73. package/src/examples/full-lifecycle-flow.ts +514 -0
  74. package/src/examples/run.ts +60 -0
  75. package/src/index.ts +204 -0
  76. package/src/kinds/KindRegistry.ts +320 -0
  77. package/src/kinds/index.ts +74 -0
  78. package/src/kinds/types.ts +470 -0
  79. package/src/kinds/validators/AgentValidator.ts +257 -0
  80. package/src/kinds/validators/AppValidator.ts +211 -0
  81. package/src/kinds/validators/DatasetValidator.ts +242 -0
  82. package/src/kinds/validators/DocumentValidator.ts +311 -0
  83. package/src/kinds/validators/MediaValidator.ts +269 -0
  84. package/src/kinds/validators/ModuleValidator.ts +225 -0
  85. package/src/kinds/validators/base.ts +276 -0
  86. package/src/kinds/validators/index.ts +12 -0
  87. package/src/lifecycle/BatchOperations.ts +381 -0
  88. package/src/lifecycle/LifecycleManager.ts +2156 -0
  89. package/src/lifecycle/OriginalsAsset.ts +524 -0
  90. package/src/lifecycle/ProvenanceQuery.ts +280 -0
  91. package/src/lifecycle/ResourceVersioning.ts +163 -0
  92. package/src/migration/MigrationManager.ts +587 -0
  93. package/src/migration/audit/AuditLogger.ts +176 -0
  94. package/src/migration/checkpoint/CheckpointManager.ts +112 -0
  95. package/src/migration/checkpoint/CheckpointStorage.ts +101 -0
  96. package/src/migration/index.ts +33 -0
  97. package/src/migration/operations/BaseMigration.ts +126 -0
  98. package/src/migration/operations/PeerToBtcoMigration.ts +105 -0
  99. package/src/migration/operations/PeerToWebvhMigration.ts +62 -0
  100. package/src/migration/operations/WebvhToBtcoMigration.ts +105 -0
  101. package/src/migration/rollback/RollbackManager.ts +170 -0
  102. package/src/migration/state/StateMachine.ts +92 -0
  103. package/src/migration/state/StateTracker.ts +156 -0
  104. package/src/migration/types.ts +356 -0
  105. package/src/migration/validation/BitcoinValidator.ts +107 -0
  106. package/src/migration/validation/CredentialValidator.ts +62 -0
  107. package/src/migration/validation/DIDCompatibilityValidator.ts +151 -0
  108. package/src/migration/validation/LifecycleValidator.ts +64 -0
  109. package/src/migration/validation/StorageValidator.ts +79 -0
  110. package/src/migration/validation/ValidationPipeline.ts +213 -0
  111. package/src/resources/ResourceManager.ts +655 -0
  112. package/src/resources/index.ts +21 -0
  113. package/src/resources/types.ts +202 -0
  114. package/src/storage/LocalStorageAdapter.ts +64 -0
  115. package/src/storage/MemoryStorageAdapter.ts +29 -0
  116. package/src/storage/StorageAdapter.ts +25 -0
  117. package/src/storage/index.ts +3 -0
  118. package/src/types/bitcoin.ts +98 -0
  119. package/src/types/common.ts +92 -0
  120. package/src/types/credentials.ts +89 -0
  121. package/src/types/did.ts +31 -0
  122. package/src/types/external-shims.d.ts +53 -0
  123. package/src/types/index.ts +7 -0
  124. package/src/types/network.ts +178 -0
  125. package/src/utils/EventLogger.ts +298 -0
  126. package/src/utils/Logger.ts +324 -0
  127. package/src/utils/MetricsCollector.ts +358 -0
  128. package/src/utils/bitcoin-address.ts +132 -0
  129. package/src/utils/cbor.ts +31 -0
  130. package/src/utils/encoding.ts +135 -0
  131. package/src/utils/hash.ts +12 -0
  132. package/src/utils/retry.ts +46 -0
  133. package/src/utils/satoshi-validation.ts +196 -0
  134. package/src/utils/serialization.ts +102 -0
  135. package/src/utils/telemetry.ts +44 -0
  136. package/src/utils/validation.ts +123 -0
  137. package/src/vc/CredentialManager.ts +955 -0
  138. package/src/vc/Issuer.ts +105 -0
  139. package/src/vc/Verifier.ts +54 -0
  140. package/src/vc/cryptosuites/bbs.ts +253 -0
  141. package/src/vc/cryptosuites/bbsSimple.ts +21 -0
  142. package/src/vc/cryptosuites/eddsa.ts +99 -0
  143. package/src/vc/documentLoader.ts +81 -0
  144. package/src/vc/proofs/data-integrity.ts +33 -0
  145. package/src/vc/utils/jsonld.ts +18 -0
@@ -0,0 +1,361 @@
1
+ /**
2
+ * WebVHCelManager - CEL Manager for did:webvh Layer
3
+ *
4
+ * Manages migration of Originals assets to the did:webvh layer (Layer 1).
5
+ * This layer provides HTTP-based witnessing for verifiable history.
6
+ *
7
+ * The did:webvh (Web Verifiable History) method combines domain-based
8
+ * resolution with cryptographic event logging for discoverable assets.
9
+ *
10
+ * @see https://identity.foundation/didwebvh/
11
+ */
12
+
13
+ import type { EventLog, ExternalReference, DataIntegrityProof, UpdateOptions, AssetState } from '../types';
14
+ import { updateEventLog } from '../algorithms/updateEventLog';
15
+ import { witnessEvent } from '../algorithms/witnessEvent';
16
+ import type { WitnessService } from '../witnesses/WitnessService';
17
+ import type { CelSigner } from './PeerCelManager';
18
+
19
+ /**
20
+ * Configuration options for WebVHCelManager
21
+ */
22
+ export interface WebVHCelConfig {
23
+ /** The DID URL of the verification method for signing */
24
+ verificationMethod?: string;
25
+ /** The purpose of proofs (defaults to 'assertionMethod') */
26
+ proofPurpose?: string;
27
+ }
28
+
29
+ /**
30
+ * Migration data stored in the update event when migrating to webvh
31
+ */
32
+ export interface WebVHMigrationData {
33
+ /** The source DID from the previous layer */
34
+ sourceDid: string;
35
+ /** The new did:webvh DID */
36
+ targetDid: string;
37
+ /** The target layer */
38
+ layer: 'webvh';
39
+ /** The domain hosting the did:webvh */
40
+ domain: string;
41
+ /** ISO 8601 timestamp of migration */
42
+ migratedAt: string;
43
+ }
44
+
45
+ /**
46
+ * WebVHCelManager - Manages CEL-based asset migration to did:webvh layer
47
+ *
48
+ * The webvh layer is the first publication layer for Originals assets.
49
+ * Assets at this layer:
50
+ * - Have a did:webvh identifier based on a domain
51
+ * - Can have HTTP-based witness attestations
52
+ * - Are discoverable via web-based DID resolution
53
+ * - Can be further migrated to did:btco layer
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const httpWitness = new HttpWitness('https://witness.example.com/api/attest');
58
+ * const manager = new WebVHCelManager(
59
+ * async (data) => createEdDsaProof(data, privateKey),
60
+ * 'example.com',
61
+ * [httpWitness]
62
+ * );
63
+ *
64
+ * const webvhLog = await manager.migrate(peerLog);
65
+ * ```
66
+ */
67
+ export class WebVHCelManager {
68
+ private signer: CelSigner;
69
+ private domain: string;
70
+ private witnesses: WitnessService[];
71
+ private config: WebVHCelConfig;
72
+
73
+ /**
74
+ * Creates a new WebVHCelManager instance
75
+ *
76
+ * @param signer - Function that signs data and returns a DataIntegrityProof
77
+ * @param domain - The domain for the did:webvh DID (e.g., 'example.com')
78
+ * @param witnesses - Optional array of witness services for attestations
79
+ * @param config - Optional configuration options
80
+ */
81
+ constructor(
82
+ signer: CelSigner,
83
+ domain: string,
84
+ witnesses: WitnessService[] = [],
85
+ config: WebVHCelConfig = {}
86
+ ) {
87
+ if (typeof signer !== 'function') {
88
+ throw new Error('WebVHCelManager requires a signer function');
89
+ }
90
+ if (!domain || typeof domain !== 'string') {
91
+ throw new Error('WebVHCelManager requires a valid domain string');
92
+ }
93
+ // Basic domain validation
94
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$/.test(domain) && !/^[a-zA-Z0-9]$/.test(domain)) {
95
+ throw new Error(`Invalid domain format: ${domain}`);
96
+ }
97
+ if (!Array.isArray(witnesses)) {
98
+ throw new Error('witnesses must be an array');
99
+ }
100
+
101
+ this.signer = signer;
102
+ this.domain = domain;
103
+ this.witnesses = witnesses;
104
+ this.config = {
105
+ proofPurpose: 'assertionMethod',
106
+ ...config,
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Migrates a peer layer event log to the did:webvh layer
112
+ *
113
+ * This method:
114
+ * 1. Validates the input log (must have a create event with peer layer data)
115
+ * 2. Generates a new did:webvh DID based on the domain
116
+ * 3. Creates an update event with migration data
117
+ * 4. Optionally adds witness proofs from configured witnesses
118
+ * 5. Returns the updated EventLog
119
+ *
120
+ * @param peerLog - The event log from the peer layer to migrate
121
+ * @returns Promise resolving to an EventLog with the migration event appended
122
+ *
123
+ * @throws Error if the log is empty, deactivated, or not from peer layer
124
+ * @throws Error if signer produces invalid proof
125
+ * @throws Error if witness service fails (if witnesses configured)
126
+ */
127
+ async migrate(peerLog: EventLog): Promise<EventLog> {
128
+ // Validate input log
129
+ if (!peerLog || !peerLog.events || peerLog.events.length === 0) {
130
+ throw new Error('Cannot migrate an empty event log');
131
+ }
132
+
133
+ // Get the create event to extract source DID
134
+ const createEvent = peerLog.events[0];
135
+ if (createEvent.type !== 'create') {
136
+ throw new Error('First event must be a create event');
137
+ }
138
+
139
+ // Extract source data
140
+ const createData = createEvent.data as Record<string, unknown>;
141
+ const sourceDid = createData.did as string;
142
+
143
+ if (!sourceDid) {
144
+ throw new Error('Create event must have a did field');
145
+ }
146
+
147
+ // Validate source is from peer layer
148
+ const sourceLayer = createData.layer as string;
149
+ if (sourceLayer && sourceLayer !== 'peer') {
150
+ throw new Error(`Cannot migrate from ${sourceLayer} layer to webvh layer. Expected peer layer.`);
151
+ }
152
+
153
+ // Check if log is already deactivated
154
+ const lastEvent = peerLog.events[peerLog.events.length - 1];
155
+ if (lastEvent.type === 'deactivate') {
156
+ throw new Error('Cannot migrate a deactivated event log');
157
+ }
158
+
159
+ // Generate did:webvh DID
160
+ const targetDid = this.generateWebVHDid(sourceDid);
161
+
162
+ // Prepare migration data
163
+ const migrationData: WebVHMigrationData = {
164
+ sourceDid,
165
+ targetDid,
166
+ layer: 'webvh',
167
+ domain: this.domain,
168
+ migratedAt: new Date().toISOString(),
169
+ };
170
+
171
+ // Build update options
172
+ const updateOptions: UpdateOptions = {
173
+ signer: this.signer,
174
+ verificationMethod: this.config.verificationMethod || `${targetDid}#key-0`,
175
+ proofPurpose: this.config.proofPurpose,
176
+ };
177
+
178
+ // Create the update event with migration data
179
+ let updatedLog = await updateEventLog(peerLog, migrationData, updateOptions);
180
+
181
+ // Add witness proofs if witnesses are configured
182
+ if (this.witnesses.length > 0) {
183
+ // Get the last event (the migration event we just added)
184
+ const lastEventIndex = updatedLog.events.length - 1;
185
+ let witnessedEvent = updatedLog.events[lastEventIndex];
186
+
187
+ // Add witness proofs from each configured witness
188
+ for (const witness of this.witnesses) {
189
+ witnessedEvent = await witnessEvent(witnessedEvent, witness);
190
+ }
191
+
192
+ // Replace the last event with the witnessed version
193
+ updatedLog = {
194
+ ...updatedLog,
195
+ events: [
196
+ ...updatedLog.events.slice(0, lastEventIndex),
197
+ witnessedEvent,
198
+ ],
199
+ };
200
+ }
201
+
202
+ return updatedLog;
203
+ }
204
+
205
+ /**
206
+ * Generates a did:webvh DID for this domain
207
+ *
208
+ * The did:webvh format is: did:webvh:{domain}:{id}
209
+ * where {id} is derived from the source DID to maintain linkage.
210
+ *
211
+ * @param sourceDid - The source DID to derive the webvh DID from
212
+ * @returns A did:webvh string
213
+ */
214
+ private generateWebVHDid(sourceDid: string): string {
215
+ // Extract a stable identifier from the source DID
216
+ // For did:peer, extract the key portion after the method
217
+ let idPart: string;
218
+
219
+ if (sourceDid.startsWith('did:peer:')) {
220
+ // For peer DIDs, use a hash-derived portion
221
+ // did:peer:4zQm... -> use the multibase portion
222
+ const peerPart = sourceDid.replace('did:peer:', '');
223
+ // Take first 32 chars of the peer DID identifier for brevity
224
+ idPart = peerPart.substring(0, Math.min(32, peerPart.length));
225
+ } else if (sourceDid.startsWith('did:key:')) {
226
+ // For key DIDs, extract the key portion
227
+ idPart = sourceDid.replace('did:key:', '').substring(0, 32);
228
+ } else {
229
+ // For other DIDs, create a hash-based identifier
230
+ idPart = this.hashIdentifier(sourceDid);
231
+ }
232
+
233
+ // Sanitize for URL safety (replace invalid chars)
234
+ idPart = idPart.replace(/[^a-zA-Z0-9]/g, '');
235
+
236
+ return `did:webvh:${this.domain}:${idPart}`;
237
+ }
238
+
239
+ /**
240
+ * Creates a URL-safe hash-based identifier from a string
241
+ */
242
+ private hashIdentifier(input: string): string {
243
+ // Simple hash for identifier generation (not cryptographic)
244
+ let hash = 0;
245
+ for (let i = 0; i < input.length; i++) {
246
+ const char = input.charCodeAt(i);
247
+ hash = ((hash << 5) - hash) + char;
248
+ hash = hash & hash; // Convert to 32bit integer
249
+ }
250
+ return Math.abs(hash).toString(36);
251
+ }
252
+
253
+ /**
254
+ * Derives the current asset state by replaying all events in the log.
255
+ *
256
+ * @param log - The event log to derive state from
257
+ * @returns The current AssetState derived from replaying events
258
+ */
259
+ getCurrentState(log: EventLog): AssetState {
260
+ // Validate input log
261
+ if (!log || !log.events || log.events.length === 0) {
262
+ throw new Error('Cannot get state from an empty event log');
263
+ }
264
+
265
+ // First event must be a create event
266
+ const createEvent = log.events[0];
267
+ if (createEvent.type !== 'create') {
268
+ throw new Error('First event must be a create event');
269
+ }
270
+
271
+ // Extract initial state from create event
272
+ const createData = createEvent.data as Record<string, unknown>;
273
+
274
+ // Initialize state from create event
275
+ const state: AssetState = {
276
+ did: createData.did as string,
277
+ name: createData.name as string | undefined,
278
+ layer: (createData.layer as 'peer' | 'webvh' | 'btco') || 'peer',
279
+ resources: (createData.resources as ExternalReference[]) || [],
280
+ creator: createData.creator as string | undefined,
281
+ createdAt: createData.createdAt as string | undefined,
282
+ updatedAt: undefined,
283
+ deactivated: false,
284
+ metadata: {},
285
+ };
286
+
287
+ // Apply subsequent events
288
+ for (let i = 1; i < log.events.length; i++) {
289
+ const event = log.events[i];
290
+
291
+ if (event.type === 'update') {
292
+ const updateData = event.data as Record<string, unknown>;
293
+
294
+ // Check if this is a migration event
295
+ if (updateData.targetDid && updateData.layer) {
296
+ // Migration event - update DID and layer
297
+ state.did = updateData.targetDid as string;
298
+ state.layer = updateData.layer as 'peer' | 'webvh' | 'btco';
299
+ state.updatedAt = updateData.migratedAt as string;
300
+
301
+ // Store migration info in metadata
302
+ state.metadata = state.metadata || {};
303
+ state.metadata.sourceDid = updateData.sourceDid;
304
+ state.metadata.domain = updateData.domain;
305
+ } else {
306
+ // Regular update event
307
+ if (updateData.name !== undefined) {
308
+ state.name = updateData.name as string;
309
+ }
310
+ if (updateData.resources !== undefined) {
311
+ state.resources = updateData.resources as ExternalReference[];
312
+ }
313
+ if (updateData.updatedAt !== undefined) {
314
+ state.updatedAt = updateData.updatedAt as string;
315
+ }
316
+ if (updateData.did !== undefined) {
317
+ state.did = updateData.did as string;
318
+ }
319
+ if (updateData.layer !== undefined) {
320
+ state.layer = updateData.layer as 'peer' | 'webvh' | 'btco';
321
+ }
322
+ }
323
+
324
+ // Store other fields in metadata
325
+ for (const [key, value] of Object.entries(updateData)) {
326
+ if (!['name', 'resources', 'updatedAt', 'did', 'layer', 'creator', 'createdAt', 'sourceDid', 'targetDid', 'domain', 'migratedAt'].includes(key)) {
327
+ state.metadata = state.metadata || {};
328
+ state.metadata[key] = value;
329
+ }
330
+ }
331
+ } else if (event.type === 'deactivate') {
332
+ state.deactivated = true;
333
+
334
+ const deactivateData = event.data as Record<string, unknown>;
335
+ if (deactivateData.deactivatedAt !== undefined) {
336
+ state.updatedAt = deactivateData.deactivatedAt as string;
337
+ }
338
+ if (deactivateData.reason !== undefined) {
339
+ state.metadata = state.metadata || {};
340
+ state.metadata.deactivationReason = deactivateData.reason;
341
+ }
342
+ }
343
+ }
344
+
345
+ return state;
346
+ }
347
+
348
+ /**
349
+ * Gets the domain this manager is configured for
350
+ */
351
+ get domainName(): string {
352
+ return this.domain;
353
+ }
354
+
355
+ /**
356
+ * Gets the number of configured witnesses
357
+ */
358
+ get witnessCount(): number {
359
+ return this.witnesses.length;
360
+ }
361
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * CEL Layer Managers
3
+ *
4
+ * Layer managers handle CEL-based asset creation and management at different
5
+ * trust/persistence layers of the Originals Protocol:
6
+ *
7
+ * - PeerCelManager: Layer 0 (did:peer) - Local control, no witnesses
8
+ * - WebVHCelManager: Layer 1 (did:webvh) - HTTP-based witnessing
9
+ * - BtcoCelManager: Layer 2 (did:btco) - Bitcoin-based witnessing
10
+ */
11
+
12
+ // Export everything from PeerCelManager including CelSigner type
13
+ export * from './PeerCelManager';
14
+
15
+ // Export specific items from WebVHCelManager (CelSigner is imported from PeerCelManager)
16
+ export {
17
+ WebVHCelManager,
18
+ type WebVHCelConfig,
19
+ type WebVHMigrationData
20
+ } from './WebVHCelManager';
21
+
22
+ // Export specific items from BtcoCelManager (CelSigner is imported from PeerCelManager)
23
+ export {
24
+ BtcoCelManager,
25
+ type BtcoCelConfig,
26
+ type BtcoMigrationData
27
+ } from './BtcoCelManager';
@@ -0,0 +1,189 @@
1
+ /**
2
+ * CBOR Serialization for CEL Event Logs
3
+ *
4
+ * Provides compact binary serialization and parsing of EventLog objects using CBOR format.
5
+ * CBOR provides ~50% size reduction compared to JSON for bandwidth-sensitive applications.
6
+ */
7
+
8
+ import type { EventLog, LogEntry, DataIntegrityProof, WitnessProof } from '../types';
9
+ import { encode, decode } from '../../utils/cbor';
10
+
11
+ /**
12
+ * Check if a proof object is a WitnessProof (has witnessedAt field)
13
+ */
14
+ function isWitnessProof(proof: DataIntegrityProof | WitnessProof): proof is WitnessProof {
15
+ return 'witnessedAt' in proof && typeof (proof as WitnessProof).witnessedAt === 'string';
16
+ }
17
+
18
+ /**
19
+ * Validate and reconstruct a DataIntegrityProof or WitnessProof
20
+ */
21
+ function parseProof(proof: unknown): DataIntegrityProof | WitnessProof {
22
+ if (!proof || typeof proof !== 'object') {
23
+ throw new Error('Invalid proof: must be an object');
24
+ }
25
+
26
+ const p = proof as Record<string, unknown>;
27
+
28
+ // Validate required DataIntegrityProof fields
29
+ if (typeof p.type !== 'string') {
30
+ throw new Error('Invalid proof: missing or invalid type');
31
+ }
32
+ if (typeof p.cryptosuite !== 'string') {
33
+ throw new Error('Invalid proof: missing or invalid cryptosuite');
34
+ }
35
+ if (typeof p.created !== 'string') {
36
+ throw new Error('Invalid proof: missing or invalid created');
37
+ }
38
+ if (typeof p.verificationMethod !== 'string') {
39
+ throw new Error('Invalid proof: missing or invalid verificationMethod');
40
+ }
41
+ if (typeof p.proofPurpose !== 'string') {
42
+ throw new Error('Invalid proof: missing or invalid proofPurpose');
43
+ }
44
+ if (typeof p.proofValue !== 'string') {
45
+ throw new Error('Invalid proof: missing or invalid proofValue');
46
+ }
47
+
48
+ const baseProof: DataIntegrityProof = {
49
+ type: p.type,
50
+ cryptosuite: p.cryptosuite,
51
+ created: p.created,
52
+ verificationMethod: p.verificationMethod,
53
+ proofPurpose: p.proofPurpose,
54
+ proofValue: p.proofValue,
55
+ };
56
+
57
+ // Check for WitnessProof
58
+ if ('witnessedAt' in p) {
59
+ if (typeof p.witnessedAt !== 'string') {
60
+ throw new Error('Invalid witness proof: witnessedAt must be a string');
61
+ }
62
+ return {
63
+ ...baseProof,
64
+ witnessedAt: p.witnessedAt,
65
+ } as WitnessProof;
66
+ }
67
+
68
+ return baseProof;
69
+ }
70
+
71
+ /**
72
+ * Validate and reconstruct a LogEntry
73
+ */
74
+ function parseEntry(entry: unknown): LogEntry {
75
+ if (!entry || typeof entry !== 'object') {
76
+ throw new Error('Invalid entry: must be an object');
77
+ }
78
+
79
+ const e = entry as Record<string, unknown>;
80
+
81
+ // Validate type
82
+ if (e.type !== 'create' && e.type !== 'update' && e.type !== 'deactivate') {
83
+ throw new Error(`Invalid entry type: ${e.type}`);
84
+ }
85
+
86
+ // Validate proof array
87
+ if (!Array.isArray(e.proof)) {
88
+ throw new Error('Invalid entry: proof must be an array');
89
+ }
90
+
91
+ const parsedEntry: LogEntry = {
92
+ type: e.type,
93
+ data: e.data,
94
+ proof: e.proof.map(parseProof),
95
+ };
96
+
97
+ // Optional previousEvent
98
+ if (e.previousEvent !== undefined) {
99
+ if (typeof e.previousEvent !== 'string') {
100
+ throw new Error('Invalid entry: previousEvent must be a string');
101
+ }
102
+ parsedEntry.previousEvent = e.previousEvent;
103
+ }
104
+
105
+ return parsedEntry;
106
+ }
107
+
108
+ /**
109
+ * Serialize an EventLog to CBOR binary format.
110
+ *
111
+ * CBOR provides a compact binary representation that is typically
112
+ * 30-50% smaller than JSON for event logs.
113
+ *
114
+ * @param log - The EventLog to serialize
115
+ * @returns Uint8Array containing the CBOR-encoded EventLog
116
+ * @throws Error if log is null or undefined
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const log = await createEventLog(data, options);
121
+ * const cbor = serializeEventLogCbor(log);
122
+ * console.log(cbor.length); // compact binary size
123
+ * ```
124
+ */
125
+ export function serializeEventLogCbor(log: EventLog): Uint8Array {
126
+ if (!log) {
127
+ throw new Error('Cannot serialize null or undefined EventLog');
128
+ }
129
+
130
+ return encode(log);
131
+ }
132
+
133
+ /**
134
+ * Parse a CBOR binary into an EventLog.
135
+ *
136
+ * Validates the structure and types of the parsed object.
137
+ *
138
+ * @param cbor - CBOR binary data to parse
139
+ * @returns Parsed and validated EventLog
140
+ * @throws Error if CBOR is invalid or doesn't match EventLog structure
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * const cbor = readFileSync('asset.cel.cbor');
145
+ * const log = parseEventLogCbor(cbor);
146
+ * console.log(log.events.length);
147
+ * ```
148
+ */
149
+ export function parseEventLogCbor(cbor: Uint8Array): EventLog {
150
+ if (!cbor || !(cbor instanceof Uint8Array)) {
151
+ throw new Error('Cannot parse null, undefined, or non-Uint8Array value');
152
+ }
153
+
154
+ if (cbor.length === 0) {
155
+ throw new Error('Cannot parse empty CBOR data');
156
+ }
157
+
158
+ let parsed: unknown;
159
+ try {
160
+ parsed = decode(cbor);
161
+ } catch (e) {
162
+ throw new Error(`Invalid CBOR: ${(e as Error).message}`);
163
+ }
164
+
165
+ if (!parsed || typeof parsed !== 'object') {
166
+ throw new Error('Invalid EventLog: must be an object');
167
+ }
168
+
169
+ const obj = parsed as Record<string, unknown>;
170
+
171
+ // Validate events array
172
+ if (!Array.isArray(obj.events)) {
173
+ throw new Error('Invalid EventLog: events must be an array');
174
+ }
175
+
176
+ const eventLog: EventLog = {
177
+ events: obj.events.map(parseEntry),
178
+ };
179
+
180
+ // Optional previousLog
181
+ if (obj.previousLog !== undefined) {
182
+ if (typeof obj.previousLog !== 'string') {
183
+ throw new Error('Invalid EventLog: previousLog must be a string');
184
+ }
185
+ eventLog.previousLog = obj.previousLog;
186
+ }
187
+
188
+ return eventLog;
189
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * CEL Serialization Module
3
+ *
4
+ * Provides serialization formats for EventLog:
5
+ * - JSON: Human-readable, standard format
6
+ * - CBOR: Compact binary format for bandwidth-sensitive applications
7
+ */
8
+
9
+ export * from './json';
10
+ export * from './cbor';