@longarc/mdash 3.1.2 → 3.1.3
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/README.md +86 -23
- package/SECURITY.md +254 -0
- package/dist/accountability/engine.d.ts +27 -0
- package/dist/accountability/engine.d.ts.map +1 -0
- package/dist/accountability/engine.js +148 -0
- package/dist/accountability/engine.js.map +1 -0
- package/dist/accountability/types.d.ts +46 -0
- package/dist/accountability/types.d.ts.map +1 -0
- package/dist/accountability/types.js +8 -0
- package/dist/accountability/types.js.map +1 -0
- package/dist/checkpoint/engine.d.ts.map +1 -1
- package/dist/checkpoint/engine.js +4 -0
- package/dist/checkpoint/engine.js.map +1 -1
- package/dist/context/compose.d.ts +62 -0
- package/dist/context/compose.d.ts.map +1 -0
- package/dist/context/compose.js +286 -0
- package/dist/context/compose.js.map +1 -0
- package/dist/context/crypto/hash.d.ts +100 -0
- package/dist/context/crypto/hash.d.ts.map +1 -0
- package/dist/context/crypto/hash.js +248 -0
- package/dist/context/crypto/hash.js.map +1 -0
- package/dist/context/crypto/hmac.d.ts +80 -0
- package/dist/context/crypto/hmac.d.ts.map +1 -0
- package/dist/context/crypto/hmac.js +192 -0
- package/dist/context/crypto/hmac.js.map +1 -0
- package/dist/context/crypto/index.d.ts +7 -0
- package/dist/context/crypto/index.d.ts.map +1 -0
- package/dist/context/crypto/index.js +7 -0
- package/dist/context/crypto/index.js.map +1 -0
- package/dist/context/engine-v3.0-backup.d.ts +197 -0
- package/dist/context/engine-v3.0-backup.d.ts.map +1 -0
- package/dist/context/engine-v3.0-backup.js +392 -0
- package/dist/context/engine-v3.0-backup.js.map +1 -0
- package/dist/context/fragment.d.ts +99 -0
- package/dist/context/fragment.d.ts.map +1 -0
- package/dist/context/fragment.js +316 -0
- package/dist/context/fragment.js.map +1 -0
- package/dist/context/index.d.ts +99 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +180 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/provenance.d.ts +80 -0
- package/dist/context/provenance.d.ts.map +1 -0
- package/dist/context/provenance.js +294 -0
- package/dist/context/provenance.js.map +1 -0
- package/dist/context/resolve.d.ts +106 -0
- package/dist/context/resolve.d.ts.map +1 -0
- package/dist/context/resolve.js +440 -0
- package/dist/context/resolve.js.map +1 -0
- package/dist/context/store.d.ts +156 -0
- package/dist/context/store.d.ts.map +1 -0
- package/dist/context/store.js +396 -0
- package/dist/context/store.js.map +1 -0
- package/dist/context/types.d.ts +463 -0
- package/dist/context/types.d.ts.map +1 -0
- package/dist/context/types.js +94 -0
- package/dist/context/types.js.map +1 -0
- package/dist/context/utils/atomic.d.ts +76 -0
- package/dist/context/utils/atomic.d.ts.map +1 -0
- package/dist/context/utils/atomic.js +159 -0
- package/dist/context/utils/atomic.js.map +1 -0
- package/dist/context/utils/credit.d.ts +65 -0
- package/dist/context/utils/credit.d.ts.map +1 -0
- package/dist/context/utils/credit.js +164 -0
- package/dist/context/utils/credit.js.map +1 -0
- package/dist/context/utils/index.d.ts +13 -0
- package/dist/context/utils/index.d.ts.map +1 -0
- package/dist/context/utils/index.js +13 -0
- package/dist/context/utils/index.js.map +1 -0
- package/dist/context/utils/utility.d.ts +63 -0
- package/dist/context/utils/utility.d.ts.map +1 -0
- package/dist/context/utils/utility.js +141 -0
- package/dist/context/utils/utility.js.map +1 -0
- package/dist/core/commitment.d.ts +25 -2
- package/dist/core/commitment.d.ts.map +1 -1
- package/dist/core/commitment.js +44 -6
- package/dist/core/commitment.js.map +1 -1
- package/dist/core/crypto.d.ts +2 -0
- package/dist/core/crypto.d.ts.map +1 -1
- package/dist/core/crypto.js +12 -0
- package/dist/core/crypto.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -10
- package/dist/index.js.map +1 -1
- package/dist/mcca/engine.d.ts.map +1 -1
- package/dist/mcca/engine.js +5 -4
- package/dist/mcca/engine.js.map +1 -1
- package/dist/physics/engine.d.ts +1 -0
- package/dist/physics/engine.d.ts.map +1 -1
- package/dist/physics/engine.js +36 -2
- package/dist/physics/engine.js.map +1 -1
- package/dist/provenance/api-handler.d.ts +45 -0
- package/dist/provenance/api-handler.d.ts.map +1 -0
- package/dist/provenance/api-handler.js +223 -0
- package/dist/provenance/api-handler.js.map +1 -0
- package/dist/provenance/api-types.d.ts +108 -0
- package/dist/provenance/api-types.d.ts.map +1 -0
- package/dist/provenance/api-types.js +9 -0
- package/dist/provenance/api-types.js.map +1 -0
- package/dist/provenance/index.d.ts +6 -0
- package/dist/provenance/index.d.ts.map +1 -0
- package/dist/provenance/index.js +3 -0
- package/dist/provenance/index.js.map +1 -0
- package/dist/provenance/provenance-engine.d.ts +63 -0
- package/dist/provenance/provenance-engine.d.ts.map +1 -0
- package/dist/provenance/provenance-engine.js +311 -0
- package/dist/provenance/provenance-engine.js.map +1 -0
- package/dist/provenance/types.d.ts +193 -0
- package/dist/provenance/types.d.ts.map +1 -0
- package/dist/provenance/types.js +9 -0
- package/dist/provenance/types.js.map +1 -0
- package/dist/tee/engine.d.ts.map +1 -1
- package/dist/tee/engine.js +14 -0
- package/dist/tee/engine.js.map +1 -1
- package/dist/warrant/engine.d.ts +24 -1
- package/dist/warrant/engine.d.ts.map +1 -1
- package/dist/warrant/engine.js +76 -1
- package/dist/warrant/engine.js.map +1 -1
- package/dist/zk/engine.d.ts.map +1 -1
- package/dist/zk/engine.js +7 -4
- package/dist/zk/engine.js.map +1 -1
- package/docs/SECURITY-PATCHES.md +170 -0
- package/package.json +17 -5
- package/src/__tests__/accountability.test.ts +308 -0
- package/src/__tests__/l1-verification-modes.test.ts +424 -0
- package/src/__tests__/phase1.benchmark.test.ts +94 -0
- package/src/__tests__/phase1.test.ts +0 -77
- package/src/__tests__/phase2-4.benchmark.test.ts +60 -0
- package/src/__tests__/phase2-4.test.ts +1 -52
- package/src/__tests__/provenance/api-handler.test.ts +356 -0
- package/src/__tests__/provenance/provenance-engine.test.ts +628 -0
- package/src/__tests__/sa-2026-008.test.ts +45 -0
- package/src/__tests__/sa-2026-009.test.ts +86 -0
- package/src/__tests__/sa-2026-010.test.ts +72 -0
- package/src/__tests__/sa-2026-012.test.ts +65 -0
- package/src/__tests__/sa-2026-nfc.test.ts +40 -0
- package/src/__tests__/security.test.ts +786 -0
- package/src/accountability/engine.ts +230 -0
- package/src/accountability/types.ts +58 -0
- package/src/checkpoint/engine.ts +4 -0
- package/src/context/__tests__/caret-v0.2.0.test.ts +860 -0
- package/src/context/__tests__/integration.test.ts +356 -0
- package/src/context/compose.ts +388 -0
- package/src/context/crypto/hash.ts +277 -0
- package/src/context/crypto/hmac.ts +253 -0
- package/src/context/crypto/index.ts +29 -0
- package/src/context/engine-v3.0-backup.ts +598 -0
- package/src/context/fragment.ts +454 -0
- package/src/context/index.ts +427 -0
- package/src/context/provenance.ts +380 -0
- package/src/context/resolve.ts +581 -0
- package/src/context/store.ts +503 -0
- package/src/context/types.ts +679 -0
- package/src/context/utils/atomic.ts +207 -0
- package/src/context/utils/credit.ts +224 -0
- package/src/context/utils/index.ts +13 -0
- package/src/context/utils/utility.ts +200 -0
- package/src/core/commitment.ts +129 -67
- package/src/core/crypto.ts +13 -0
- package/src/index.ts +62 -10
- package/src/mcca/engine.ts +5 -4
- package/src/physics/engine.ts +40 -3
- package/src/provenance/api-handler.ts +248 -0
- package/src/provenance/api-types.ts +112 -0
- package/src/provenance/index.ts +19 -0
- package/src/provenance/provenance-engine.ts +387 -0
- package/src/provenance/types.ts +211 -0
- package/src/tee/engine.ts +16 -0
- package/src/warrant/engine.ts +89 -1
- package/src/zk/engine.ts +8 -4
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caret — Provenance Chain Implementation
|
|
3
|
+
* @module @longarcstudios/caret/provenance
|
|
4
|
+
*
|
|
5
|
+
* Immutable provenance tracking for context fragments.
|
|
6
|
+
* Every fragment knows exactly where it came from.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
ProvenanceRecord,
|
|
11
|
+
ProvenanceChain,
|
|
12
|
+
SourceURI,
|
|
13
|
+
Attribution,
|
|
14
|
+
TrustLevel,
|
|
15
|
+
Timestamp,
|
|
16
|
+
Hash,
|
|
17
|
+
} from './types.js';
|
|
18
|
+
import { sha256Object, rollingHash } from './crypto/hash.js';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// FACTORY FUNCTIONS
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a new ProvenanceRecord.
|
|
26
|
+
*/
|
|
27
|
+
export async function createProvenanceRecord(options: {
|
|
28
|
+
source: SourceURI | string;
|
|
29
|
+
attribution: Attribution;
|
|
30
|
+
trust_level?: number;
|
|
31
|
+
parent_hash?: Hash | null;
|
|
32
|
+
signature?: string;
|
|
33
|
+
}): Promise<ProvenanceRecord> {
|
|
34
|
+
const trust = validateTrustLevel(options.trust_level ?? getDefaultTrust(options.attribution));
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
source: options.source as SourceURI,
|
|
38
|
+
attribution: options.attribution,
|
|
39
|
+
trust_level: trust,
|
|
40
|
+
timestamp: new Date().toISOString() as Timestamp,
|
|
41
|
+
parent_hash: options.parent_hash ?? null,
|
|
42
|
+
signature: options.signature,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create a new ProvenanceChain with a single record.
|
|
48
|
+
*/
|
|
49
|
+
export async function createProvenanceChain(
|
|
50
|
+
record: ProvenanceRecord
|
|
51
|
+
): Promise<ProvenanceChain> {
|
|
52
|
+
const chain_hash = await sha256Object(record);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
head: record,
|
|
56
|
+
tail: null,
|
|
57
|
+
length: 1,
|
|
58
|
+
chain_hash,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Extend an existing ProvenanceChain with a new record.
|
|
64
|
+
* Returns a new chain (immutable).
|
|
65
|
+
*/
|
|
66
|
+
export async function extendProvenanceChain(
|
|
67
|
+
chain: ProvenanceChain,
|
|
68
|
+
record: ProvenanceRecord
|
|
69
|
+
): Promise<ProvenanceChain> {
|
|
70
|
+
// Update record's parent_hash to point to current chain
|
|
71
|
+
const linkedRecord: ProvenanceRecord = {
|
|
72
|
+
...record,
|
|
73
|
+
parent_hash: chain.chain_hash,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Compute new chain hash
|
|
77
|
+
const recordHash = await sha256Object(linkedRecord);
|
|
78
|
+
const chain_hash = await rollingHash([chain.chain_hash, recordHash]);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
head: linkedRecord,
|
|
82
|
+
tail: chain,
|
|
83
|
+
length: chain.length + 1,
|
|
84
|
+
chain_hash,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// CHAIN OPERATIONS
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get all records in the chain as an array (head to tail).
|
|
94
|
+
*/
|
|
95
|
+
export function flattenChain(chain: ProvenanceChain): ProvenanceRecord[] {
|
|
96
|
+
const records: ProvenanceRecord[] = [];
|
|
97
|
+
let current: ProvenanceChain | null = chain;
|
|
98
|
+
|
|
99
|
+
while (current !== null) {
|
|
100
|
+
records.push(current.head);
|
|
101
|
+
current = current.tail;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return records;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Find a record in the chain by source URI.
|
|
109
|
+
*/
|
|
110
|
+
export function findBySource(
|
|
111
|
+
chain: ProvenanceChain,
|
|
112
|
+
source: SourceURI | string
|
|
113
|
+
): ProvenanceRecord | null {
|
|
114
|
+
let current: ProvenanceChain | null = chain;
|
|
115
|
+
|
|
116
|
+
while (current !== null) {
|
|
117
|
+
if (current.head.source === source) {
|
|
118
|
+
return current.head;
|
|
119
|
+
}
|
|
120
|
+
current = current.tail;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get the minimum trust level in the chain.
|
|
128
|
+
*/
|
|
129
|
+
export function getMinTrust(chain: ProvenanceChain): TrustLevel {
|
|
130
|
+
let min = chain.head.trust_level;
|
|
131
|
+
let current: ProvenanceChain | null = chain.tail;
|
|
132
|
+
|
|
133
|
+
while (current !== null) {
|
|
134
|
+
if (current.head.trust_level < min) {
|
|
135
|
+
min = current.head.trust_level;
|
|
136
|
+
}
|
|
137
|
+
current = current.tail;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return min;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get the maximum trust level in the chain.
|
|
145
|
+
*/
|
|
146
|
+
export function getMaxTrust(chain: ProvenanceChain): TrustLevel {
|
|
147
|
+
let max = chain.head.trust_level;
|
|
148
|
+
let current: ProvenanceChain | null = chain.tail;
|
|
149
|
+
|
|
150
|
+
while (current !== null) {
|
|
151
|
+
if (current.head.trust_level > max) {
|
|
152
|
+
max = current.head.trust_level;
|
|
153
|
+
}
|
|
154
|
+
current = current.tail;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return max;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if chain contains a specific attribution type.
|
|
162
|
+
*/
|
|
163
|
+
export function hasAttribution(
|
|
164
|
+
chain: ProvenanceChain,
|
|
165
|
+
attribution: Attribution
|
|
166
|
+
): boolean {
|
|
167
|
+
let current: ProvenanceChain | null = chain;
|
|
168
|
+
|
|
169
|
+
while (current !== null) {
|
|
170
|
+
if (current.head.attribution === attribution) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
current = current.tail;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get all unique attributions in the chain.
|
|
181
|
+
*/
|
|
182
|
+
export function getAttributions(chain: ProvenanceChain): Attribution[] {
|
|
183
|
+
const attributions = new Set<Attribution>();
|
|
184
|
+
let current: ProvenanceChain | null = chain;
|
|
185
|
+
|
|
186
|
+
while (current !== null) {
|
|
187
|
+
attributions.add(current.head.attribution);
|
|
188
|
+
current = current.tail;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return Array.from(attributions);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// CHAIN VERIFICATION
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Verify the integrity of a provenance chain.
|
|
200
|
+
* Checks that all hash links are valid.
|
|
201
|
+
*/
|
|
202
|
+
export async function verifyChain(chain: ProvenanceChain): Promise<{
|
|
203
|
+
valid: boolean;
|
|
204
|
+
errors: string[];
|
|
205
|
+
}> {
|
|
206
|
+
const errors: string[] = [];
|
|
207
|
+
|
|
208
|
+
// Single-record chain
|
|
209
|
+
if (chain.length === 1) {
|
|
210
|
+
const expectedHash = await sha256Object(chain.head);
|
|
211
|
+
if (expectedHash !== chain.chain_hash) {
|
|
212
|
+
errors.push('Chain hash mismatch for single-record chain');
|
|
213
|
+
}
|
|
214
|
+
return { valid: errors.length === 0, errors };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Multi-record chain
|
|
218
|
+
let current: ProvenanceChain | null = chain;
|
|
219
|
+
const recordHashes: Hash[] = [];
|
|
220
|
+
|
|
221
|
+
while (current !== null) {
|
|
222
|
+
const recordHash = await sha256Object(current.head);
|
|
223
|
+
recordHashes.unshift(recordHash); // Add to front (we're traversing head to tail)
|
|
224
|
+
|
|
225
|
+
// Check parent_hash linkage
|
|
226
|
+
if (current.tail !== null && current.head.parent_hash !== current.tail.chain_hash) {
|
|
227
|
+
errors.push(`Parent hash mismatch at depth ${recordHashes.length}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
current = current.tail;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Verify rolling hash
|
|
234
|
+
const expectedChainHash = await computeChainHash(chain);
|
|
235
|
+
if (expectedChainHash !== chain.chain_hash) {
|
|
236
|
+
errors.push('Rolling chain hash verification failed');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { valid: errors.length === 0, errors };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Recompute the chain hash from records.
|
|
244
|
+
*/
|
|
245
|
+
async function computeChainHash(chain: ProvenanceChain): Promise<Hash> {
|
|
246
|
+
if (chain.tail === null) {
|
|
247
|
+
return sha256Object(chain.head);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const tailHash = await computeChainHash(chain.tail);
|
|
251
|
+
const headHash = await sha256Object(chain.head);
|
|
252
|
+
return rollingHash([tailHash, headHash]);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// CHAIN MERGING
|
|
257
|
+
// ============================================================================
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Merge multiple provenance chains into one.
|
|
261
|
+
* Used when composing multiple fragments into a manifold.
|
|
262
|
+
*
|
|
263
|
+
* Strategy: Creates a new chain with all records, ordered by timestamp.
|
|
264
|
+
*/
|
|
265
|
+
export async function mergeChains(
|
|
266
|
+
chains: readonly ProvenanceChain[]
|
|
267
|
+
): Promise<ProvenanceChain> {
|
|
268
|
+
if (chains.length === 0) {
|
|
269
|
+
throw new Error('Cannot merge empty chain array');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (chains.length === 1) {
|
|
273
|
+
const first = chains[0];
|
|
274
|
+
if (!first) throw new Error('Invalid chain');
|
|
275
|
+
return first;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Collect all records
|
|
279
|
+
const allRecords: ProvenanceRecord[] = [];
|
|
280
|
+
for (const chain of chains) {
|
|
281
|
+
allRecords.push(...flattenChain(chain));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Sort by timestamp (oldest first)
|
|
285
|
+
allRecords.sort((a, b) =>
|
|
286
|
+
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// Deduplicate by source + timestamp
|
|
290
|
+
const seen = new Set<string>();
|
|
291
|
+
const uniqueRecords = allRecords.filter(record => {
|
|
292
|
+
const key = `${record.source}:${record.timestamp}`;
|
|
293
|
+
if (seen.has(key)) return false;
|
|
294
|
+
seen.add(key);
|
|
295
|
+
return true;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Build new chain
|
|
299
|
+
const firstRecord = uniqueRecords[0];
|
|
300
|
+
if (!firstRecord) {
|
|
301
|
+
throw new Error('No records to merge');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let result = await createProvenanceChain(firstRecord);
|
|
305
|
+
|
|
306
|
+
for (let i = 1; i < uniqueRecords.length; i++) {
|
|
307
|
+
const record = uniqueRecords[i];
|
|
308
|
+
if (record) {
|
|
309
|
+
result = await extendProvenanceChain(result, record);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// UTILITY FUNCTIONS
|
|
318
|
+
// ============================================================================
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Get default trust level for an attribution type.
|
|
322
|
+
*/
|
|
323
|
+
function getDefaultTrust(attribution: Attribution): number {
|
|
324
|
+
switch (attribution) {
|
|
325
|
+
case 'system':
|
|
326
|
+
return 100;
|
|
327
|
+
case 'operator':
|
|
328
|
+
return 90;
|
|
329
|
+
case 'user':
|
|
330
|
+
return 70;
|
|
331
|
+
case 'derived':
|
|
332
|
+
return 60;
|
|
333
|
+
case 'agent':
|
|
334
|
+
return 50;
|
|
335
|
+
case 'external':
|
|
336
|
+
return 30;
|
|
337
|
+
default:
|
|
338
|
+
return 50;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Validate and clamp trust level to 0-100.
|
|
344
|
+
*/
|
|
345
|
+
function validateTrustLevel(value: number): TrustLevel {
|
|
346
|
+
const clamped = Math.max(0, Math.min(100, Math.round(value)));
|
|
347
|
+
return clamped as TrustLevel;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Parse a source string into a SourceURI.
|
|
352
|
+
* Validates the format if strict mode is enabled.
|
|
353
|
+
*/
|
|
354
|
+
export function parseSourceURI(source: string, strict = false): SourceURI {
|
|
355
|
+
if (strict) {
|
|
356
|
+
// Validate URI format: scheme://path
|
|
357
|
+
const uriPattern = /^[a-z][a-z0-9+.-]*:\/\/.+$/i;
|
|
358
|
+
if (!uriPattern.test(source)) {
|
|
359
|
+
throw new Error('Invalid source URI format');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return source as SourceURI;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Create a timestamp for the current moment.
|
|
368
|
+
*/
|
|
369
|
+
export function now(): Timestamp {
|
|
370
|
+
return new Date().toISOString() as Timestamp;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Check if a timestamp is expired given a max age.
|
|
375
|
+
*/
|
|
376
|
+
export function isExpired(timestamp: Timestamp, maxAgeMs: number): boolean {
|
|
377
|
+
const then = new Date(timestamp).getTime();
|
|
378
|
+
const now = Date.now();
|
|
379
|
+
return (now - then) > maxAgeMs;
|
|
380
|
+
}
|