@attest-it/core 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,629 @@
1
+ export { checkOpenSSL, generateKeyPair, getDefaultPrivateKeyPath, getDefaultPublicKeyPath, setKeyPermissions, sign, verify } from './chunk-UWYR7JNE.js';
2
+ import * as fs from 'fs';
3
+ import { readFileSync } from 'fs';
4
+ import { readFile } from 'fs/promises';
5
+ import * as path from 'path';
6
+ import { join, resolve } from 'path';
7
+ import { parse } from 'yaml';
8
+ import { z } from 'zod';
9
+ import * as crypto from 'crypto';
10
+ import { glob, globSync } from 'tinyglobby';
11
+ import * as os from 'os';
12
+ import * as canonicalizeNamespace from 'canonicalize';
13
+
14
+ var settingsSchema = z.object({
15
+ maxAgeDays: z.number().int().positive().default(30),
16
+ publicKeyPath: z.string().default(".attest-it/pubkey.pem"),
17
+ attestationsPath: z.string().default(".attest-it/attestations.json"),
18
+ defaultCommand: z.string().optional(),
19
+ algorithm: z.enum(["ed25519", "rsa"]).default("ed25519")
20
+ }).strict();
21
+ var suiteSchema = z.object({
22
+ description: z.string().optional(),
23
+ packages: z.array(z.string().min(1, "Package path cannot be empty")).min(1, "At least one package pattern is required"),
24
+ files: z.array(z.string().min(1, "File path cannot be empty")).optional(),
25
+ ignore: z.array(z.string().min(1, "Ignore pattern cannot be empty")).optional(),
26
+ command: z.string().optional(),
27
+ invalidates: z.array(z.string().min(1, "Invalidated suite name cannot be empty")).optional()
28
+ }).strict();
29
+ var configSchema = z.object({
30
+ version: z.literal(1),
31
+ settings: settingsSchema.default({}),
32
+ suites: z.record(z.string(), suiteSchema).refine((suites) => Object.keys(suites).length >= 1, {
33
+ message: "At least one suite must be defined"
34
+ })
35
+ }).strict();
36
+ var ConfigValidationError = class extends Error {
37
+ constructor(message, issues) {
38
+ super(message);
39
+ this.issues = issues;
40
+ this.name = "ConfigValidationError";
41
+ }
42
+ };
43
+ var ConfigNotFoundError = class extends Error {
44
+ constructor(message) {
45
+ super(message);
46
+ this.name = "ConfigNotFoundError";
47
+ }
48
+ };
49
+ function parseConfigContent(content, format) {
50
+ let rawConfig;
51
+ try {
52
+ if (format === "yaml") {
53
+ rawConfig = parse(content);
54
+ } else {
55
+ rawConfig = JSON.parse(content);
56
+ }
57
+ } catch (error) {
58
+ throw new ConfigValidationError(
59
+ `Failed to parse ${format.toUpperCase()}: ${error instanceof Error ? error.message : String(error)}`,
60
+ []
61
+ );
62
+ }
63
+ const result = configSchema.safeParse(rawConfig);
64
+ if (!result.success) {
65
+ throw new ConfigValidationError(
66
+ "Configuration validation failed:\n" + result.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n"),
67
+ result.error.issues
68
+ );
69
+ }
70
+ return result.data;
71
+ }
72
+ function getConfigFormat(filePath) {
73
+ const ext = filePath.toLowerCase();
74
+ if (ext.endsWith(".yaml") || ext.endsWith(".yml")) {
75
+ return "yaml";
76
+ }
77
+ if (ext.endsWith(".json")) {
78
+ return "json";
79
+ }
80
+ return "yaml";
81
+ }
82
+ function findConfigPath(startDir = process.cwd()) {
83
+ const configDir = join(startDir, ".attest-it");
84
+ const candidates = ["config.yaml", "config.yml", "config.json"];
85
+ for (const candidate of candidates) {
86
+ const configPath = join(configDir, candidate);
87
+ try {
88
+ readFileSync(configPath, "utf8");
89
+ return configPath;
90
+ } catch {
91
+ continue;
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+ async function loadConfig(configPath) {
97
+ const resolvedPath = configPath ?? findConfigPath();
98
+ if (!resolvedPath) {
99
+ throw new ConfigNotFoundError(
100
+ "Configuration file not found. Expected .attest-it/config.yaml, .attest-it/config.yml, or .attest-it/config.json"
101
+ );
102
+ }
103
+ try {
104
+ const content = await readFile(resolvedPath, "utf8");
105
+ const format = getConfigFormat(resolvedPath);
106
+ return parseConfigContent(content, format);
107
+ } catch (error) {
108
+ if (error instanceof ConfigValidationError) {
109
+ throw error;
110
+ }
111
+ throw new ConfigNotFoundError(
112
+ `Failed to read configuration file at ${resolvedPath}: ${String(error)}`
113
+ );
114
+ }
115
+ }
116
+ function loadConfigSync(configPath) {
117
+ const resolvedPath = configPath ?? findConfigPath();
118
+ if (!resolvedPath) {
119
+ throw new ConfigNotFoundError(
120
+ "Configuration file not found. Expected .attest-it/config.yaml, .attest-it/config.yml, or .attest-it/config.json"
121
+ );
122
+ }
123
+ try {
124
+ const content = readFileSync(resolvedPath, "utf8");
125
+ const format = getConfigFormat(resolvedPath);
126
+ return parseConfigContent(content, format);
127
+ } catch (error) {
128
+ if (error instanceof ConfigValidationError) {
129
+ throw error;
130
+ }
131
+ throw new ConfigNotFoundError(
132
+ `Failed to read configuration file at ${resolvedPath}: ${String(error)}`
133
+ );
134
+ }
135
+ }
136
+ function resolveConfigPaths(config, repoRoot) {
137
+ return {
138
+ ...config,
139
+ settings: {
140
+ ...config.settings,
141
+ publicKeyPath: resolve(repoRoot, config.settings.publicKeyPath),
142
+ attestationsPath: resolve(repoRoot, config.settings.attestationsPath)
143
+ }
144
+ };
145
+ }
146
+ function toAttestItConfig(config) {
147
+ return {
148
+ version: config.version,
149
+ settings: {
150
+ maxAgeDays: config.settings.maxAgeDays,
151
+ publicKeyPath: config.settings.publicKeyPath,
152
+ attestationsPath: config.settings.attestationsPath,
153
+ algorithm: config.settings.algorithm,
154
+ ...config.settings.defaultCommand !== void 0 && {
155
+ defaultCommand: config.settings.defaultCommand
156
+ }
157
+ },
158
+ suites: Object.fromEntries(
159
+ Object.entries(config.suites).map(([name, suite]) => [
160
+ name,
161
+ {
162
+ packages: suite.packages,
163
+ ...suite.description !== void 0 && { description: suite.description },
164
+ ...suite.files !== void 0 && { files: suite.files },
165
+ ...suite.ignore !== void 0 && { ignore: suite.ignore },
166
+ ...suite.command !== void 0 && { command: suite.command },
167
+ ...suite.invalidates !== void 0 && { invalidates: suite.invalidates }
168
+ }
169
+ ])
170
+ )
171
+ };
172
+ }
173
+ var LARGE_FILE_THRESHOLD = 50 * 1024 * 1024;
174
+ function sortFiles(files) {
175
+ return [...files].sort((a, b) => {
176
+ if (a < b) return -1;
177
+ if (a > b) return 1;
178
+ return 0;
179
+ });
180
+ }
181
+ function normalizePath(filePath) {
182
+ return filePath.split(path.sep).join("/");
183
+ }
184
+ function computeFinalFingerprint(fileHashes) {
185
+ const sorted = [...fileHashes].sort((a, b) => {
186
+ if (a.relativePath < b.relativePath) return -1;
187
+ if (a.relativePath > b.relativePath) return 1;
188
+ return 0;
189
+ });
190
+ const hashes = sorted.map((input) => input.hash);
191
+ const concatenated = Buffer.concat(hashes);
192
+ const finalHash = crypto.createHash("sha256").update(concatenated).digest();
193
+ return `sha256:${finalHash.toString("hex")}`;
194
+ }
195
+ async function hashFileAsync(realPath, normalizedPath, stats) {
196
+ if (stats.size > LARGE_FILE_THRESHOLD) {
197
+ return new Promise((resolve3, reject) => {
198
+ const hash2 = crypto.createHash("sha256");
199
+ hash2.update(normalizedPath);
200
+ hash2.update("\0");
201
+ const stream = fs.createReadStream(realPath);
202
+ stream.on("data", (chunk) => {
203
+ hash2.update(chunk);
204
+ });
205
+ stream.on("end", () => {
206
+ resolve3(hash2.digest());
207
+ });
208
+ stream.on("error", reject);
209
+ });
210
+ }
211
+ const content = await fs.promises.readFile(realPath);
212
+ const hash = crypto.createHash("sha256");
213
+ hash.update(normalizedPath);
214
+ hash.update("\0");
215
+ hash.update(content);
216
+ return hash.digest();
217
+ }
218
+ function hashFileSync(realPath, normalizedPath) {
219
+ const content = fs.readFileSync(realPath);
220
+ const hash = crypto.createHash("sha256");
221
+ hash.update(normalizedPath);
222
+ hash.update("\0");
223
+ hash.update(content);
224
+ return hash.digest();
225
+ }
226
+ function validateOptions(options) {
227
+ if (options.packages.length === 0) {
228
+ throw new Error("packages array must not be empty");
229
+ }
230
+ const baseDir = options.baseDir ?? process.cwd();
231
+ for (const pkg of options.packages) {
232
+ const pkgPath = path.resolve(baseDir, pkg);
233
+ if (!fs.existsSync(pkgPath)) {
234
+ throw new Error(`Package path does not exist: ${pkgPath}`);
235
+ }
236
+ }
237
+ return baseDir;
238
+ }
239
+ async function computeFingerprint(options) {
240
+ const baseDir = validateOptions(options);
241
+ const files = await listPackageFiles(options.packages, options.ignore, baseDir);
242
+ const sortedFiles = sortFiles(files);
243
+ const fileHashCache = /* @__PURE__ */ new Map();
244
+ const fileHashInputs = [];
245
+ for (const file of sortedFiles) {
246
+ const filePath = path.resolve(baseDir, file);
247
+ let realPath = filePath;
248
+ let stats = await fs.promises.lstat(filePath);
249
+ if (stats.isSymbolicLink()) {
250
+ try {
251
+ realPath = await fs.promises.realpath(filePath);
252
+ } catch {
253
+ continue;
254
+ }
255
+ try {
256
+ stats = await fs.promises.stat(realPath);
257
+ } catch {
258
+ continue;
259
+ }
260
+ }
261
+ if (!stats.isFile()) {
262
+ continue;
263
+ }
264
+ const normalizedPath = normalizePath(file);
265
+ let hash;
266
+ const cachedHash = fileHashCache.get(realPath);
267
+ if (cachedHash !== void 0) {
268
+ hash = cachedHash;
269
+ } else {
270
+ hash = await hashFileAsync(realPath, normalizedPath, stats);
271
+ fileHashCache.set(realPath, hash);
272
+ }
273
+ fileHashInputs.push({ relativePath: normalizedPath, hash });
274
+ }
275
+ const fingerprint = computeFinalFingerprint(fileHashInputs);
276
+ return {
277
+ fingerprint,
278
+ files: sortedFiles,
279
+ fileCount: sortedFiles.length
280
+ };
281
+ }
282
+ function computeFingerprintSync(options) {
283
+ const baseDir = validateOptions(options);
284
+ const files = listPackageFilesSync(options.packages, options.ignore, baseDir);
285
+ const sortedFiles = sortFiles(files);
286
+ const fileHashCache = /* @__PURE__ */ new Map();
287
+ const fileHashInputs = [];
288
+ for (const file of sortedFiles) {
289
+ const filePath = path.resolve(baseDir, file);
290
+ let realPath = filePath;
291
+ let stats = fs.lstatSync(filePath);
292
+ if (stats.isSymbolicLink()) {
293
+ try {
294
+ realPath = fs.realpathSync(filePath);
295
+ } catch {
296
+ continue;
297
+ }
298
+ try {
299
+ stats = fs.statSync(realPath);
300
+ } catch {
301
+ continue;
302
+ }
303
+ }
304
+ if (!stats.isFile()) {
305
+ continue;
306
+ }
307
+ const normalizedPath = normalizePath(file);
308
+ let hash;
309
+ const cachedHash = fileHashCache.get(realPath);
310
+ if (cachedHash !== void 0) {
311
+ hash = cachedHash;
312
+ } else {
313
+ hash = hashFileSync(realPath, normalizedPath);
314
+ fileHashCache.set(realPath, hash);
315
+ }
316
+ fileHashInputs.push({ relativePath: normalizedPath, hash });
317
+ }
318
+ const fingerprint = computeFinalFingerprint(fileHashInputs);
319
+ return {
320
+ fingerprint,
321
+ files: sortedFiles,
322
+ fileCount: sortedFiles.length
323
+ };
324
+ }
325
+ async function listPackageFiles(packages, ignore = [], baseDir = process.cwd()) {
326
+ const allFiles = [];
327
+ for (const pkg of packages) {
328
+ const patterns = [`${pkg}/**/*`];
329
+ const files = await glob(patterns, {
330
+ cwd: baseDir,
331
+ ignore,
332
+ onlyFiles: true,
333
+ dot: true,
334
+ // Include dotfiles
335
+ absolute: false
336
+ // Return relative paths
337
+ });
338
+ allFiles.push(...files);
339
+ }
340
+ return allFiles;
341
+ }
342
+ function listPackageFilesSync(packages, ignore = [], baseDir = process.cwd()) {
343
+ const allFiles = [];
344
+ for (const pkg of packages) {
345
+ const patterns = [`${pkg}/**/*`];
346
+ const files = globSync(patterns, {
347
+ cwd: baseDir,
348
+ ignore,
349
+ onlyFiles: true,
350
+ dot: true,
351
+ // Include dotfiles
352
+ absolute: false
353
+ // Return relative paths
354
+ });
355
+ allFiles.push(...files);
356
+ }
357
+ return allFiles;
358
+ }
359
+ var canonicalize = canonicalizeNamespace;
360
+ var serialize = canonicalize.default;
361
+ var attestationSchema = z.object({
362
+ suite: z.string().min(1),
363
+ fingerprint: z.string().regex(/^sha256:[a-f0-9]{64}$/),
364
+ attestedAt: z.string().datetime(),
365
+ attestedBy: z.string().min(1),
366
+ command: z.string().min(1),
367
+ exitCode: z.literal(0)
368
+ });
369
+ var attestationsFileSchema = z.object({
370
+ schemaVersion: z.literal("1"),
371
+ attestations: z.array(attestationSchema),
372
+ signature: z.string()
373
+ // Will be validated by crypto module
374
+ });
375
+ function isNodeError(error) {
376
+ if (error === null || typeof error !== "object") {
377
+ return false;
378
+ }
379
+ if (!("code" in error)) {
380
+ return false;
381
+ }
382
+ const errorObj = error;
383
+ return typeof errorObj.code === "string";
384
+ }
385
+ async function readAttestations(filePath) {
386
+ try {
387
+ const content = await fs.promises.readFile(filePath, "utf-8");
388
+ const parsed = JSON.parse(content);
389
+ return attestationsFileSchema.parse(parsed);
390
+ } catch (error) {
391
+ if (isNodeError(error) && error.code === "ENOENT") {
392
+ return null;
393
+ }
394
+ throw error;
395
+ }
396
+ }
397
+ function readAttestationsSync(filePath) {
398
+ try {
399
+ const content = fs.readFileSync(filePath, "utf-8");
400
+ const parsed = JSON.parse(content);
401
+ return attestationsFileSchema.parse(parsed);
402
+ } catch (error) {
403
+ if (isNodeError(error) && error.code === "ENOENT") {
404
+ return null;
405
+ }
406
+ throw error;
407
+ }
408
+ }
409
+ async function writeAttestations(filePath, attestations, signature) {
410
+ const fileContent = {
411
+ schemaVersion: "1",
412
+ attestations,
413
+ signature
414
+ };
415
+ attestationsFileSchema.parse(fileContent);
416
+ const dir = path.dirname(filePath);
417
+ await fs.promises.mkdir(dir, { recursive: true });
418
+ const json = JSON.stringify(fileContent, null, 2);
419
+ await fs.promises.writeFile(filePath, json, "utf-8");
420
+ }
421
+ function writeAttestationsSync(filePath, attestations, signature) {
422
+ const fileContent = {
423
+ schemaVersion: "1",
424
+ attestations,
425
+ signature
426
+ };
427
+ attestationsFileSchema.parse(fileContent);
428
+ const dir = path.dirname(filePath);
429
+ fs.mkdirSync(dir, { recursive: true });
430
+ const json = JSON.stringify(fileContent, null, 2);
431
+ fs.writeFileSync(filePath, json, "utf-8");
432
+ }
433
+ function findAttestation(attestations, suite) {
434
+ return attestations.attestations.find((a) => a.suite === suite);
435
+ }
436
+ function upsertAttestation(attestations, newAttestation) {
437
+ attestationSchema.parse(newAttestation);
438
+ const existingIndex = attestations.findIndex((a) => a.suite === newAttestation.suite);
439
+ if (existingIndex === -1) {
440
+ return [...attestations, newAttestation];
441
+ } else {
442
+ const updated = [...attestations];
443
+ updated[existingIndex] = newAttestation;
444
+ return updated;
445
+ }
446
+ }
447
+ function removeAttestation(attestations, suite) {
448
+ return attestations.filter((a) => a.suite !== suite);
449
+ }
450
+ function canonicalizeAttestations(attestations) {
451
+ const canonical = serialize(attestations);
452
+ if (canonical === void 0) {
453
+ throw new Error("Failed to canonicalize attestations");
454
+ }
455
+ return canonical;
456
+ }
457
+ function createAttestation(params) {
458
+ const attestation = {
459
+ suite: params.suite,
460
+ fingerprint: params.fingerprint,
461
+ attestedAt: (/* @__PURE__ */ new Date()).toISOString(),
462
+ attestedBy: params.attestedBy ?? os.userInfo().username,
463
+ command: params.command,
464
+ exitCode: 0
465
+ };
466
+ attestationSchema.parse(attestation);
467
+ return attestation;
468
+ }
469
+ async function writeSignedAttestations(options) {
470
+ const { sign: sign2 } = await import('./crypto-ITLMIMRJ.js');
471
+ const canonical = canonicalizeAttestations(options.attestations);
472
+ const signature = await sign2({
473
+ privateKeyPath: options.privateKeyPath,
474
+ data: canonical
475
+ });
476
+ await writeAttestations(options.filePath, options.attestations, signature);
477
+ }
478
+ async function readAndVerifyAttestations(options) {
479
+ const { verify: verify2 } = await import('./crypto-ITLMIMRJ.js');
480
+ const file = await readAttestations(options.filePath);
481
+ if (!file) {
482
+ throw new Error(`Attestations file not found: ${options.filePath}`);
483
+ }
484
+ const canonical = canonicalizeAttestations(file.attestations);
485
+ const isValid = await verify2({
486
+ publicKeyPath: options.publicKeyPath,
487
+ data: canonical,
488
+ signature: file.signature
489
+ });
490
+ if (!isValid) {
491
+ throw new SignatureInvalidError(options.filePath);
492
+ }
493
+ return file;
494
+ }
495
+ var SignatureInvalidError = class extends Error {
496
+ /**
497
+ * Create a new SignatureInvalidError.
498
+ * @param filePath - Path to the file that failed verification
499
+ */
500
+ constructor(filePath) {
501
+ super(`Signature verification failed for: ${filePath}`);
502
+ this.name = "SignatureInvalidError";
503
+ }
504
+ };
505
+ async function verifyAttestations(options) {
506
+ const { config, repoRoot = process.cwd() } = options;
507
+ const errors = [];
508
+ const suiteResults = [];
509
+ let signatureValid = true;
510
+ let attestationsFile = null;
511
+ const attestationsPath = resolvePath(config.settings.attestationsPath, repoRoot);
512
+ const publicKeyPath = resolvePath(config.settings.publicKeyPath, repoRoot);
513
+ try {
514
+ if (!fs.existsSync(attestationsPath)) {
515
+ attestationsFile = null;
516
+ } else if (!fs.existsSync(publicKeyPath)) {
517
+ errors.push(`Public key not found: ${publicKeyPath}`);
518
+ signatureValid = false;
519
+ } else {
520
+ attestationsFile = await readAndVerifyAttestations({
521
+ filePath: attestationsPath,
522
+ publicKeyPath
523
+ });
524
+ }
525
+ } catch (err) {
526
+ if (err instanceof SignatureInvalidError) {
527
+ signatureValid = false;
528
+ errors.push(err.message);
529
+ } else if (err instanceof Error) {
530
+ errors.push(err.message);
531
+ }
532
+ }
533
+ const attestations = attestationsFile?.attestations ?? [];
534
+ for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {
535
+ const result = await verifySuite({
536
+ suiteName,
537
+ suiteConfig,
538
+ attestations,
539
+ maxAgeDays: config.settings.maxAgeDays,
540
+ repoRoot
541
+ });
542
+ suiteResults.push(result);
543
+ }
544
+ checkInvalidationChains(config, suiteResults);
545
+ const allValid = signatureValid && suiteResults.every((r) => r.status === "VALID") && errors.length === 0;
546
+ return {
547
+ success: allValid,
548
+ signatureValid,
549
+ suites: suiteResults,
550
+ errors
551
+ };
552
+ }
553
+ async function verifySuite(options) {
554
+ const { suiteName, suiteConfig, attestations, maxAgeDays, repoRoot } = options;
555
+ const fingerprintOptions = {
556
+ packages: suiteConfig.packages.map((p) => resolvePath(p, repoRoot)),
557
+ baseDir: repoRoot,
558
+ ...suiteConfig.ignore && { ignore: suiteConfig.ignore }
559
+ };
560
+ const fingerprintResult = await computeFingerprint(fingerprintOptions);
561
+ const attestation = attestations.find((a) => a.suite === suiteName);
562
+ if (!attestation) {
563
+ return {
564
+ suite: suiteName,
565
+ status: "NEEDS_ATTESTATION",
566
+ fingerprint: fingerprintResult.fingerprint,
567
+ message: "No attestation found for this suite"
568
+ };
569
+ }
570
+ if (attestation.fingerprint !== fingerprintResult.fingerprint) {
571
+ return {
572
+ suite: suiteName,
573
+ status: "FINGERPRINT_CHANGED",
574
+ fingerprint: fingerprintResult.fingerprint,
575
+ attestation,
576
+ message: `Fingerprint changed from ${attestation.fingerprint.slice(0, 20)}... to ${fingerprintResult.fingerprint.slice(0, 20)}...`
577
+ };
578
+ }
579
+ const attestedAt = new Date(attestation.attestedAt);
580
+ const ageMs = Date.now() - attestedAt.getTime();
581
+ const ageDays = Math.floor(ageMs / (1e3 * 60 * 60 * 24));
582
+ if (ageDays > maxAgeDays) {
583
+ return {
584
+ suite: suiteName,
585
+ status: "EXPIRED",
586
+ fingerprint: fingerprintResult.fingerprint,
587
+ attestation,
588
+ age: ageDays,
589
+ message: `Attestation expired (${String(ageDays)} days old, max ${String(maxAgeDays)} days)`
590
+ };
591
+ }
592
+ return {
593
+ suite: suiteName,
594
+ status: "VALID",
595
+ fingerprint: fingerprintResult.fingerprint,
596
+ attestation,
597
+ age: ageDays
598
+ };
599
+ }
600
+ function checkInvalidationChains(config, results) {
601
+ for (const [parentName, parentConfig] of Object.entries(config.suites)) {
602
+ const invalidates = parentConfig.invalidates ?? [];
603
+ const parentResult = results.find((r) => r.suite === parentName);
604
+ if (!parentResult?.attestation) continue;
605
+ const parentTime = new Date(parentResult.attestation.attestedAt).getTime();
606
+ for (const childName of invalidates) {
607
+ const childResult = results.find((r) => r.suite === childName);
608
+ if (!childResult?.attestation) continue;
609
+ const childTime = new Date(childResult.attestation.attestedAt).getTime();
610
+ if (parentTime > childTime && childResult.status === "VALID") {
611
+ childResult.status = "INVALIDATED_BY_PARENT";
612
+ childResult.message = `Invalidated by ${parentName} (attested later)`;
613
+ }
614
+ }
615
+ }
616
+ }
617
+ function resolvePath(relativePath, baseDir) {
618
+ if (path.isAbsolute(relativePath)) {
619
+ return relativePath;
620
+ }
621
+ return path.join(baseDir, relativePath);
622
+ }
623
+
624
+ // src/index.ts
625
+ var version = "0.0.0";
626
+
627
+ export { ConfigNotFoundError, ConfigValidationError, SignatureInvalidError, canonicalizeAttestations, computeFingerprint, computeFingerprintSync, createAttestation, findAttestation, findConfigPath, listPackageFiles, loadConfig, loadConfigSync, readAndVerifyAttestations, readAttestations, readAttestationsSync, removeAttestation, resolveConfigPaths, toAttestItConfig, upsertAttestation, verifyAttestations, version, writeAttestations, writeAttestationsSync, writeSignedAttestations };
628
+ //# sourceMappingURL=index.js.map
629
+ //# sourceMappingURL=index.js.map