@sentinel-atl/registry 0.3.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/server.js ADDED
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Trust Registry HTTP API — REST endpoints for publishing and querying STCs.
3
+ *
4
+ * Endpoints:
5
+ * POST /api/v1/certificates Register a new STC
6
+ * GET /api/v1/certificates/:id Get certificate by ID
7
+ * GET /api/v1/certificates Query certificates
8
+ * DELETE /api/v1/certificates/:id Remove a certificate
9
+ *
10
+ * GET /api/v1/packages/:name Get latest certificate for a package
11
+ * GET /api/v1/packages/:name/history Get all certificates for a package
12
+ * GET /api/v1/packages/:name/badge SVG badge for a package
13
+ * GET /api/v1/packages/:name/badge/score Score badge
14
+ *
15
+ * GET /api/v1/stats Registry stats
16
+ * GET /health Health check
17
+ */
18
+ import { CertificateStore } from './store.js';
19
+ import { gradeBadge, scoreBadge, verifiedBadge, notFoundBadge } from './badge.js';
20
+ import { authenticate, hasScope, sendUnauthorized, sendForbidden, applyCors, defaultCorsConfig, createSecureServer, applySecurityHeaders, } from '@sentinel-atl/hardening';
21
+ // ─── Server ──────────────────────────────────────────────────────────
22
+ export class RegistryServer {
23
+ server = null;
24
+ store;
25
+ port;
26
+ authConfig;
27
+ corsConfig;
28
+ tlsConfig;
29
+ constructor(options) {
30
+ this.port = options?.port ?? 3200;
31
+ this.store = options?.store ?? new CertificateStore();
32
+ this.authConfig = options?.auth ?? { enabled: false, keys: [] };
33
+ this.corsConfig = options?.cors ?? defaultCorsConfig();
34
+ this.tlsConfig = options?.tls;
35
+ // Badge endpoints are always public (for README embeds)
36
+ if (this.authConfig.enabled && !this.authConfig.publicPaths) {
37
+ this.authConfig.publicPaths = ['/health'];
38
+ }
39
+ }
40
+ getStore() {
41
+ return this.store;
42
+ }
43
+ async start() {
44
+ // Load persisted certificates from backend (no-op if in-memory only)
45
+ await this.store.load();
46
+ return new Promise((resolve, reject) => {
47
+ this.server = createSecureServer((req, res) => this.handleRequest(req, res), this.tlsConfig);
48
+ this.server.on('error', reject);
49
+ this.server.listen(this.port, () => {
50
+ resolve({ port: this.port });
51
+ });
52
+ });
53
+ }
54
+ async stop() {
55
+ return new Promise((resolve) => {
56
+ if (this.server) {
57
+ this.server.close(() => resolve());
58
+ }
59
+ else {
60
+ resolve();
61
+ }
62
+ });
63
+ }
64
+ isTLS() {
65
+ return !!this.tlsConfig?.certPath || !!this.tlsConfig?.cert;
66
+ }
67
+ // ─── Router ────────────────────────────────────────────────────
68
+ async handleRequest(req, res) {
69
+ const url = new URL(req.url ?? '/', `http://localhost`);
70
+ const path = url.pathname;
71
+ const method = req.method ?? 'GET';
72
+ // CORS (configurable origins)
73
+ if (applyCors(req, res, this.corsConfig))
74
+ return; // preflight handled
75
+ // Security headers
76
+ applySecurityHeaders(res, { hsts: this.isTLS() });
77
+ // Authentication
78
+ const authResult = authenticate(req, this.authConfig);
79
+ // Badge endpoints are public even when auth is enabled
80
+ const isBadgePath = path.includes('/badge');
81
+ if (!authResult.authenticated && !isBadgePath) {
82
+ return sendUnauthorized(res, this.authConfig, authResult.error);
83
+ }
84
+ try {
85
+ // Health
86
+ if (path === '/health' && method === 'GET') {
87
+ return this.sendJson(res, 200, { status: 'ok', certificates: this.store.count() });
88
+ }
89
+ // Stats
90
+ if (path === '/api/v1/stats' && method === 'GET') {
91
+ return this.sendJson(res, 200, this.store.getStats());
92
+ }
93
+ // POST /api/v1/certificates — register (requires write scope)
94
+ if (path === '/api/v1/certificates' && method === 'POST') {
95
+ if (!hasScope(authResult, 'write'))
96
+ return sendForbidden(res, 'Write scope required');
97
+ return await this.handleRegister(req, res);
98
+ }
99
+ // GET /api/v1/certificates — query
100
+ if (path === '/api/v1/certificates' && method === 'GET') {
101
+ return this.handleQuery(url, res);
102
+ }
103
+ // GET/DELETE /api/v1/certificates/:id
104
+ const certMatch = path.match(/^\/api\/v1\/certificates\/(.+)$/);
105
+ if (certMatch) {
106
+ const id = decodeURIComponent(certMatch[1]);
107
+ if (method === 'GET')
108
+ return this.handleGetById(id, res);
109
+ if (method === 'DELETE') {
110
+ if (!hasScope(authResult, 'admin'))
111
+ return sendForbidden(res, 'Admin scope required');
112
+ return this.handleDelete(id, res);
113
+ }
114
+ }
115
+ // Package routes: /api/v1/packages/:name[/history|/badge|/badge/score]
116
+ const pkgMatch = path.match(/^\/api\/v1\/packages\/(@[^/]+\/[^/]+|[^/]+)(\/.*)?$/);
117
+ if (pkgMatch) {
118
+ const packageName = decodeURIComponent(pkgMatch[1]);
119
+ const suffix = pkgMatch[2] ?? '';
120
+ if (suffix === '' && method === 'GET')
121
+ return this.handleGetPackage(packageName, res);
122
+ if (suffix === '/history' && method === 'GET')
123
+ return this.handlePackageHistory(packageName, res);
124
+ if (suffix === '/badge' && method === 'GET')
125
+ return this.handleBadge(packageName, url, res);
126
+ if (suffix === '/badge/score' && method === 'GET')
127
+ return this.handleScoreBadge(packageName, url, res);
128
+ }
129
+ this.sendJson(res, 404, { error: 'Not found' });
130
+ }
131
+ catch (err) {
132
+ this.sendJson(res, 500, { error: 'Internal server error' });
133
+ }
134
+ }
135
+ // ─── Handlers ──────────────────────────────────────────────────
136
+ async handleRegister(req, res) {
137
+ // Content-Type enforcement
138
+ const contentType = req.headers['content-type'];
139
+ if (!contentType || !contentType.includes('application/json')) {
140
+ return this.sendJson(res, 415, { error: 'Content-Type must be application/json' });
141
+ }
142
+ const body = await readBody(req);
143
+ let certificate;
144
+ try {
145
+ certificate = JSON.parse(body);
146
+ }
147
+ catch {
148
+ return this.sendJson(res, 400, { error: 'Invalid JSON' });
149
+ }
150
+ // Comprehensive STC validation
151
+ const errors = validateSTC(certificate);
152
+ if (errors.length > 0) {
153
+ return this.sendJson(res, 400, { error: 'Invalid STC', details: errors });
154
+ }
155
+ // Check for duplicates
156
+ if (this.store.get(certificate.id)) {
157
+ return this.sendJson(res, 409, { error: 'Certificate already registered', id: certificate.id });
158
+ }
159
+ const entry = await this.store.register(certificate);
160
+ this.sendJson(res, 201, {
161
+ id: entry.id,
162
+ packageName: entry.packageName,
163
+ trustScore: entry.trustScore,
164
+ grade: entry.grade,
165
+ verified: entry.verified,
166
+ registeredAt: entry.registeredAt,
167
+ });
168
+ }
169
+ handleGetById(id, res) {
170
+ const entry = this.store.get(id);
171
+ if (!entry) {
172
+ return this.sendJson(res, 404, { error: 'Certificate not found' });
173
+ }
174
+ this.sendJson(res, 200, this.formatEntry(entry));
175
+ }
176
+ async handleDelete(id, res) {
177
+ const removed = await this.store.remove(id);
178
+ if (!removed) {
179
+ return this.sendJson(res, 404, { error: 'Certificate not found' });
180
+ }
181
+ this.sendJson(res, 200, { deleted: true, id });
182
+ }
183
+ handleQuery(url, res) {
184
+ const parseIntSafe = (val) => {
185
+ if (val === null)
186
+ return undefined;
187
+ const n = parseInt(val, 10);
188
+ return Number.isNaN(n) ? undefined : n;
189
+ };
190
+ const q = {
191
+ packageName: url.searchParams.get('package') ?? undefined,
192
+ minScore: parseIntSafe(url.searchParams.get('minScore')),
193
+ minGrade: url.searchParams.get('minGrade') ?? undefined,
194
+ verified: url.searchParams.has('verified') ? url.searchParams.get('verified') === 'true' : undefined,
195
+ limit: parseIntSafe(url.searchParams.get('limit')),
196
+ offset: parseIntSafe(url.searchParams.get('offset')),
197
+ };
198
+ const results = this.store.query(q);
199
+ this.sendJson(res, 200, {
200
+ total: this.store.count(),
201
+ count: results.length,
202
+ certificates: results.map(e => this.formatEntry(e)),
203
+ });
204
+ }
205
+ handleGetPackage(packageName, res) {
206
+ const entry = this.store.getLatestForPackage(packageName);
207
+ if (!entry) {
208
+ return this.sendJson(res, 404, { error: 'No certificates found for package', packageName });
209
+ }
210
+ this.sendJson(res, 200, this.formatEntry(entry));
211
+ }
212
+ handlePackageHistory(packageName, res) {
213
+ const entries = this.store.getForPackage(packageName);
214
+ this.sendJson(res, 200, {
215
+ packageName,
216
+ count: entries.length,
217
+ certificates: entries.map(e => this.formatEntry(e)),
218
+ });
219
+ }
220
+ handleBadge(packageName, url, res) {
221
+ const style = (url.searchParams.get('style') ?? 'flat');
222
+ const entry = this.store.getLatestForPackage(packageName);
223
+ let svg;
224
+ if (!entry) {
225
+ svg = notFoundBadge(style);
226
+ }
227
+ else if (entry.verified) {
228
+ svg = gradeBadge(entry.grade, style);
229
+ }
230
+ else {
231
+ svg = verifiedBadge(false, style);
232
+ }
233
+ res.writeHead(200, {
234
+ 'Content-Type': 'image/svg+xml',
235
+ 'Cache-Control': 'max-age=300',
236
+ });
237
+ res.end(svg);
238
+ }
239
+ handleScoreBadge(packageName, url, res) {
240
+ const style = (url.searchParams.get('style') ?? 'flat');
241
+ const entry = this.store.getLatestForPackage(packageName);
242
+ let svg;
243
+ if (!entry) {
244
+ svg = notFoundBadge(style);
245
+ }
246
+ else {
247
+ svg = scoreBadge(entry.trustScore, style);
248
+ }
249
+ res.writeHead(200, {
250
+ 'Content-Type': 'image/svg+xml',
251
+ 'Cache-Control': 'max-age=300',
252
+ });
253
+ res.end(svg);
254
+ }
255
+ // ─── Helpers ───────────────────────────────────────────────────
256
+ formatEntry(entry) {
257
+ return {
258
+ id: entry.id,
259
+ packageName: entry.packageName,
260
+ packageVersion: entry.packageVersion,
261
+ trustScore: entry.trustScore,
262
+ grade: entry.grade,
263
+ verified: entry.verified,
264
+ registeredAt: entry.registeredAt,
265
+ issuerDid: entry.issuerDid,
266
+ certificate: entry.certificate,
267
+ };
268
+ }
269
+ sendJson(res, status, data) {
270
+ res.writeHead(status, { 'Content-Type': 'application/json' });
271
+ res.end(JSON.stringify(data));
272
+ }
273
+ }
274
+ // ─── STC Validation ───────────────────────────────────────────────────
275
+ function validateSTC(cert) {
276
+ const errors = [];
277
+ if (!cert || typeof cert !== 'object') {
278
+ return ['Certificate must be a JSON object'];
279
+ }
280
+ // Required fields
281
+ if (!cert.id || typeof cert.id !== 'string')
282
+ errors.push('Missing or invalid "id" (string)');
283
+ if (cert.type !== 'SentinelTrustCertificate')
284
+ errors.push('"type" must be "SentinelTrustCertificate"');
285
+ if (cert['@context'] !== 'https://sentinel.trust/stc/v1')
286
+ errors.push('"@context" must be "https://sentinel.trust/stc/v1"');
287
+ // Timestamps
288
+ if (!cert.issuedAt || typeof cert.issuedAt !== 'string')
289
+ errors.push('Missing "issuedAt" (ISO date)');
290
+ if (!cert.expiresAt || typeof cert.expiresAt !== 'string')
291
+ errors.push('Missing "expiresAt" (ISO date)');
292
+ // Issuer
293
+ if (!cert.issuer || typeof cert.issuer !== 'object') {
294
+ errors.push('Missing "issuer" object');
295
+ }
296
+ else {
297
+ if (!cert.issuer.did || typeof cert.issuer.did !== 'string')
298
+ errors.push('Missing "issuer.did"');
299
+ }
300
+ // Subject
301
+ if (!cert.subject || typeof cert.subject !== 'object') {
302
+ errors.push('Missing "subject" object');
303
+ }
304
+ else {
305
+ if (!cert.subject.packageName || typeof cert.subject.packageName !== 'string')
306
+ errors.push('Missing "subject.packageName"');
307
+ if (!cert.subject.packageVersion || typeof cert.subject.packageVersion !== 'string')
308
+ errors.push('Missing "subject.packageVersion"');
309
+ }
310
+ // Trust score
311
+ if (!cert.trustScore || typeof cert.trustScore !== 'object') {
312
+ errors.push('Missing "trustScore" object');
313
+ }
314
+ else {
315
+ if (typeof cert.trustScore.overall !== 'number' || cert.trustScore.overall < 0 || cert.trustScore.overall > 100) {
316
+ errors.push('"trustScore.overall" must be a number 0-100');
317
+ }
318
+ if (!cert.trustScore.grade || typeof cert.trustScore.grade !== 'string') {
319
+ errors.push('Missing "trustScore.grade"');
320
+ }
321
+ }
322
+ // Proof
323
+ if (!cert.proof || typeof cert.proof !== 'object') {
324
+ errors.push('Missing "proof" object');
325
+ }
326
+ return errors;
327
+ }
328
+ // ─── Body Reader ──────────────────────────────────────────────────────
329
+ function readBody(req) {
330
+ return new Promise((resolve, reject) => {
331
+ const chunks = [];
332
+ let size = 0;
333
+ const MAX_BODY = 2_097_152; // 2 MB (STC max)
334
+ req.on('data', (chunk) => {
335
+ size += chunk.length;
336
+ if (size > MAX_BODY) {
337
+ reject(new Error('Request body too large'));
338
+ req.destroy();
339
+ return;
340
+ }
341
+ chunks.push(chunk);
342
+ });
343
+ req.on('end', () => resolve(Buffer.concat(chunks).toString()));
344
+ req.on('error', reject);
345
+ });
346
+ }
347
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Certificate Store — persistent storage for Sentinel Trust Certificates.
3
+ *
4
+ * Backed by SentinelStore interface — supports in-memory, Redis, Postgres, SQLite.
5
+ * By default uses in-memory Map for zero-config development.
6
+ */
7
+ import { type SentinelTrustCertificate } from '@sentinel-atl/scanner';
8
+ import type { SentinelStore } from '@sentinel-atl/store';
9
+ export interface RegistryEntry {
10
+ /** STC ID */
11
+ id: string;
12
+ /** Full certificate */
13
+ certificate: SentinelTrustCertificate;
14
+ /** Package name (for lookup) */
15
+ packageName: string;
16
+ /** Package version */
17
+ packageVersion: string;
18
+ /** Trust score */
19
+ trustScore: number;
20
+ /** Grade */
21
+ grade: string;
22
+ /** Whether signature is verified */
23
+ verified: boolean;
24
+ /** When the entry was registered */
25
+ registeredAt: string;
26
+ /** Issuer DID */
27
+ issuerDid: string;
28
+ }
29
+ export interface RegistryQuery {
30
+ /** Filter by package name */
31
+ packageName?: string;
32
+ /** Filter by minimum trust score */
33
+ minScore?: number;
34
+ /** Filter by minimum grade */
35
+ minGrade?: string;
36
+ /** Filter by verified status */
37
+ verified?: boolean;
38
+ /** Maximum results */
39
+ limit?: number;
40
+ /** Offset for pagination */
41
+ offset?: number;
42
+ }
43
+ export interface RegistryStats {
44
+ totalCertificates: number;
45
+ verifiedCertificates: number;
46
+ uniquePackages: number;
47
+ averageScore: number;
48
+ gradeDistribution: Record<string, number>;
49
+ }
50
+ /**
51
+ * Options for creating a CertificateStore.
52
+ * If `backend` is provided, certificates are persisted via SentinelStore.
53
+ * Otherwise, an in-memory Map is used (development only).
54
+ */
55
+ export interface CertificateStoreOptions {
56
+ /** Persistent backend — data survives restarts */
57
+ backend?: SentinelStore;
58
+ /** Key prefix for the backend (default: 'registry:') */
59
+ prefix?: string;
60
+ }
61
+ export declare class CertificateStore {
62
+ private cache;
63
+ /** Index: packageName → entry IDs (newest first) */
64
+ private pkgIndex;
65
+ private backend?;
66
+ private prefix;
67
+ private loaded;
68
+ constructor(options?: CertificateStoreOptions);
69
+ /** Load all entries from the backend into the in-memory cache. Call once on startup. */
70
+ load(): Promise<void>;
71
+ /**
72
+ * Register a new certificate. Verifies signature before storing.
73
+ */
74
+ register(certificate: SentinelTrustCertificate): Promise<RegistryEntry>;
75
+ /**
76
+ * Get a certificate by ID.
77
+ */
78
+ get(id: string): RegistryEntry | undefined;
79
+ /**
80
+ * Get the latest certificate for a package.
81
+ */
82
+ getLatestForPackage(packageName: string): RegistryEntry | undefined;
83
+ /**
84
+ * Get all certificates for a package.
85
+ */
86
+ getForPackage(packageName: string): RegistryEntry[];
87
+ /**
88
+ * Query certificates with filters.
89
+ */
90
+ query(q: RegistryQuery): RegistryEntry[];
91
+ /**
92
+ * Remove a certificate by ID.
93
+ */
94
+ remove(id: string): Promise<boolean>;
95
+ /**
96
+ * Get registry statistics.
97
+ */
98
+ getStats(): RegistryStats;
99
+ /**
100
+ * Get total count (for pagination).
101
+ */
102
+ count(): number;
103
+ }
104
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAa,KAAK,wBAAwB,EAAwB,MAAM,uBAAuB,CAAC;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,MAAM,WAAW,aAAa;IAC5B,aAAa;IACb,EAAE,EAAE,MAAM,CAAC;IACX,uBAAuB;IACvB,WAAW,EAAE,wBAAwB,CAAC;IACtC,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,QAAQ,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAYD;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,kDAAkD;IAClD,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAoC;IACjD,oDAAoD;IACpD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAAC,CAAgB;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,CAAC,EAAE,uBAAuB;IAK7C,wFAAwF;IAClF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B;;OAEG;IACG,QAAQ,CAAC,WAAW,EAAE,wBAAwB,GAAG,OAAO,CAAC,aAAa,CAAC;IAkC7E;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI1C;;OAEG;IACH,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAMnE;;OAEG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE;IAKnD;;OAEG;IACH,KAAK,CAAC,CAAC,EAAE,aAAa,GAAG,aAAa,EAAE;IAwBxC;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA6B1C;;OAEG;IACH,QAAQ,IAAI,aAAa;IAmBzB;;OAEG;IACH,KAAK,IAAI,MAAM;CAGhB"}
package/dist/store.js ADDED
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Certificate Store — persistent storage for Sentinel Trust Certificates.
3
+ *
4
+ * Backed by SentinelStore interface — supports in-memory, Redis, Postgres, SQLite.
5
+ * By default uses in-memory Map for zero-config development.
6
+ */
7
+ import { verifySTC } from '@sentinel-atl/scanner';
8
+ // ─── Grade Helpers ───────────────────────────────────────────────────
9
+ const GRADE_ORDER = { A: 4, B: 3, C: 2, D: 1, F: 0 };
10
+ function gradeAtLeast(actual, required) {
11
+ return (GRADE_ORDER[actual] ?? 0) >= (GRADE_ORDER[required] ?? 0);
12
+ }
13
+ const KEY_PREFIX_DEFAULT = 'registry:';
14
+ const CERT_PREFIX = 'cert:';
15
+ const PKG_INDEX_PREFIX = 'pkg:';
16
+ export class CertificateStore {
17
+ cache = new Map();
18
+ /** Index: packageName → entry IDs (newest first) */
19
+ pkgIndex = new Map();
20
+ backend;
21
+ prefix;
22
+ loaded = false;
23
+ constructor(options) {
24
+ this.backend = options?.backend;
25
+ this.prefix = options?.prefix ?? KEY_PREFIX_DEFAULT;
26
+ }
27
+ /** Load all entries from the backend into the in-memory cache. Call once on startup. */
28
+ async load() {
29
+ if (this.loaded || !this.backend)
30
+ return;
31
+ const keys = await this.backend.keys(`${this.prefix}${CERT_PREFIX}`);
32
+ const values = await this.backend.getMany(keys);
33
+ for (const [, json] of values) {
34
+ const entry = JSON.parse(json);
35
+ this.cache.set(entry.id, entry);
36
+ const existing = this.pkgIndex.get(entry.packageName) ?? [];
37
+ existing.push(entry.id);
38
+ this.pkgIndex.set(entry.packageName, existing);
39
+ }
40
+ // Sort each package's IDs by registeredAt descending
41
+ for (const [pkg, ids] of this.pkgIndex) {
42
+ ids.sort((a, b) => {
43
+ const ea = this.cache.get(a);
44
+ const eb = this.cache.get(b);
45
+ return eb.registeredAt.localeCompare(ea.registeredAt);
46
+ });
47
+ }
48
+ this.loaded = true;
49
+ }
50
+ /**
51
+ * Register a new certificate. Verifies signature before storing.
52
+ */
53
+ async register(certificate) {
54
+ const result = await verifySTC(certificate);
55
+ const entry = {
56
+ id: certificate.id,
57
+ certificate,
58
+ packageName: certificate.subject.packageName,
59
+ packageVersion: certificate.subject.packageVersion,
60
+ trustScore: certificate.trustScore.overall,
61
+ grade: certificate.trustScore.grade,
62
+ verified: result.valid,
63
+ registeredAt: new Date().toISOString(),
64
+ issuerDid: certificate.issuer.did,
65
+ };
66
+ this.cache.set(entry.id, entry);
67
+ // Update package index
68
+ const existing = this.pkgIndex.get(entry.packageName) ?? [];
69
+ existing.unshift(entry.id); // newest first
70
+ this.pkgIndex.set(entry.packageName, existing);
71
+ // Persist to backend
72
+ if (this.backend) {
73
+ await this.backend.set(`${this.prefix}${CERT_PREFIX}${entry.id}`, JSON.stringify(entry));
74
+ await this.backend.set(`${this.prefix}${PKG_INDEX_PREFIX}${entry.packageName}`, JSON.stringify(existing));
75
+ }
76
+ return entry;
77
+ }
78
+ /**
79
+ * Get a certificate by ID.
80
+ */
81
+ get(id) {
82
+ return this.cache.get(id);
83
+ }
84
+ /**
85
+ * Get the latest certificate for a package.
86
+ */
87
+ getLatestForPackage(packageName) {
88
+ const ids = this.pkgIndex.get(packageName);
89
+ if (!ids || ids.length === 0)
90
+ return undefined;
91
+ return this.cache.get(ids[0]);
92
+ }
93
+ /**
94
+ * Get all certificates for a package.
95
+ */
96
+ getForPackage(packageName) {
97
+ const ids = this.pkgIndex.get(packageName) ?? [];
98
+ return ids.map(id => this.cache.get(id)).filter(Boolean);
99
+ }
100
+ /**
101
+ * Query certificates with filters.
102
+ */
103
+ query(q) {
104
+ let results = Array.from(this.cache.values());
105
+ if (q.packageName) {
106
+ results = results.filter(e => e.packageName === q.packageName);
107
+ }
108
+ if (q.minScore !== undefined) {
109
+ results = results.filter(e => e.trustScore >= q.minScore);
110
+ }
111
+ if (q.minGrade) {
112
+ results = results.filter(e => gradeAtLeast(e.grade, q.minGrade));
113
+ }
114
+ if (q.verified !== undefined) {
115
+ results = results.filter(e => e.verified === q.verified);
116
+ }
117
+ // Sort by registeredAt descending
118
+ results.sort((a, b) => b.registeredAt.localeCompare(a.registeredAt));
119
+ const offset = q.offset ?? 0;
120
+ const limit = q.limit ?? 50;
121
+ return results.slice(offset, offset + limit);
122
+ }
123
+ /**
124
+ * Remove a certificate by ID.
125
+ */
126
+ async remove(id) {
127
+ const entry = this.cache.get(id);
128
+ if (!entry)
129
+ return false;
130
+ this.cache.delete(id);
131
+ const ids = this.pkgIndex.get(entry.packageName);
132
+ if (ids) {
133
+ const idx = ids.indexOf(id);
134
+ if (idx !== -1)
135
+ ids.splice(idx, 1);
136
+ if (ids.length === 0)
137
+ this.pkgIndex.delete(entry.packageName);
138
+ }
139
+ // Persist deletion to backend
140
+ if (this.backend) {
141
+ await this.backend.delete(`${this.prefix}${CERT_PREFIX}${id}`);
142
+ if (ids && ids.length > 0) {
143
+ await this.backend.set(`${this.prefix}${PKG_INDEX_PREFIX}${entry.packageName}`, JSON.stringify(ids));
144
+ }
145
+ else {
146
+ await this.backend.delete(`${this.prefix}${PKG_INDEX_PREFIX}${entry.packageName}`);
147
+ }
148
+ }
149
+ return true;
150
+ }
151
+ /**
152
+ * Get registry statistics.
153
+ */
154
+ getStats() {
155
+ const all = Array.from(this.cache.values());
156
+ const gradeDistribution = { A: 0, B: 0, C: 0, D: 0, F: 0 };
157
+ let totalScore = 0;
158
+ for (const entry of all) {
159
+ totalScore += entry.trustScore;
160
+ gradeDistribution[entry.grade] = (gradeDistribution[entry.grade] ?? 0) + 1;
161
+ }
162
+ return {
163
+ totalCertificates: all.length,
164
+ verifiedCertificates: all.filter(e => e.verified).length,
165
+ uniquePackages: this.pkgIndex.size,
166
+ averageScore: all.length > 0 ? Math.round(totalScore / all.length) : 0,
167
+ gradeDistribution,
168
+ };
169
+ }
170
+ /**
171
+ * Get total count (for pagination).
172
+ */
173
+ count() {
174
+ return this.cache.size;
175
+ }
176
+ }
177
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}