@barekey/sdk 0.1.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.
Files changed (57) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +21 -0
  3. package/dist/client.d.ts +41 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +302 -0
  6. package/dist/errors.d.ts +461 -0
  7. package/dist/errors.d.ts.map +1 -0
  8. package/dist/errors.js +343 -0
  9. package/dist/handle.d.ts +20 -0
  10. package/dist/handle.d.ts.map +1 -0
  11. package/dist/handle.js +35 -0
  12. package/dist/index.d.ts +5 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +3 -0
  15. package/dist/internal/cache.d.ts +13 -0
  16. package/dist/internal/cache.d.ts.map +1 -0
  17. package/dist/internal/cache.js +24 -0
  18. package/dist/internal/evaluate.d.ts +7 -0
  19. package/dist/internal/evaluate.d.ts.map +1 -0
  20. package/dist/internal/evaluate.js +176 -0
  21. package/dist/internal/http.d.ts +19 -0
  22. package/dist/internal/http.d.ts.map +1 -0
  23. package/dist/internal/http.js +92 -0
  24. package/dist/internal/node-runtime.d.ts +19 -0
  25. package/dist/internal/node-runtime.d.ts.map +1 -0
  26. package/dist/internal/node-runtime.js +422 -0
  27. package/dist/internal/requirements.d.ts +3 -0
  28. package/dist/internal/requirements.d.ts.map +1 -0
  29. package/dist/internal/requirements.js +40 -0
  30. package/dist/internal/runtime.d.ts +15 -0
  31. package/dist/internal/runtime.d.ts.map +1 -0
  32. package/dist/internal/runtime.js +135 -0
  33. package/dist/internal/ttl.d.ts +4 -0
  34. package/dist/internal/ttl.d.ts.map +1 -0
  35. package/dist/internal/ttl.js +30 -0
  36. package/dist/internal/typegen.d.ts +25 -0
  37. package/dist/internal/typegen.d.ts.map +1 -0
  38. package/dist/internal/typegen.js +75 -0
  39. package/dist/types.d.ts +130 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +1 -0
  42. package/generated.d.ts +16 -0
  43. package/index.d.ts +2 -0
  44. package/package.json +42 -0
  45. package/src/client.ts +422 -0
  46. package/src/errors.ts +420 -0
  47. package/src/handle.ts +67 -0
  48. package/src/index.ts +60 -0
  49. package/src/internal/cache.ts +33 -0
  50. package/src/internal/evaluate.ts +232 -0
  51. package/src/internal/http.ts +134 -0
  52. package/src/internal/node-runtime.ts +581 -0
  53. package/src/internal/requirements.ts +57 -0
  54. package/src/internal/runtime.ts +199 -0
  55. package/src/internal/ttl.ts +41 -0
  56. package/src/internal/typegen.ts +124 -0
  57. package/src/types.ts +189 -0
@@ -0,0 +1,581 @@
1
+ import {
2
+ InvalidConfigurationProvidedError,
3
+ InvalidCredentialsProvidedError,
4
+ NoCredentialsProvidedError,
5
+ SdkModuleNotFoundError,
6
+ TypegenReadFailedError,
7
+ TypegenUnsupportedSdkError,
8
+ TypegenWriteFailedError,
9
+ } from "../errors.js";
10
+ import { postJson } from "./http.js";
11
+
12
+ type NodeRuntimeModules = {
13
+ childProcess: typeof import("node:child_process");
14
+ fs: typeof import("node:fs/promises");
15
+ moduleApi: {
16
+ createRequire(path: string | URL): NodeRequire;
17
+ };
18
+ os: typeof import("node:os");
19
+ path: typeof import("node:path");
20
+ };
21
+
22
+ type CliConfig = {
23
+ baseUrl: string;
24
+ activeAccountId: string;
25
+ };
26
+
27
+ type CliCredentials = {
28
+ accessToken: string;
29
+ refreshToken: string;
30
+ accessTokenExpiresAtMs: number;
31
+ refreshTokenExpiresAtMs: number;
32
+ clerkUserId: string;
33
+ orgId: string;
34
+ orgSlug: string;
35
+ };
36
+
37
+ type StoredCliSession = {
38
+ baseUrl: string;
39
+ activeAccountId: string;
40
+ credentials: CliCredentials;
41
+ };
42
+
43
+ type StoredCliSessionSource = "keychain" | "file";
44
+
45
+ type StoredCliSessionWithSource = StoredCliSession & {
46
+ source: StoredCliSessionSource;
47
+ };
48
+
49
+ const CLI_SERVICE_NAME = "barekey-cli";
50
+
51
+ function isNodeRuntime(): boolean {
52
+ return (
53
+ typeof process !== "undefined" &&
54
+ typeof process.versions === "object" &&
55
+ process.versions !== null &&
56
+ typeof process.versions.node === "string"
57
+ );
58
+ }
59
+
60
+ async function loadNodeRuntime(): Promise<NodeRuntimeModules | null> {
61
+ if (!isNodeRuntime()) {
62
+ return null;
63
+ }
64
+
65
+ try {
66
+ const [childProcess, fs, moduleApi, os, path] = await Promise.all([
67
+ import("node:child_process"),
68
+ import("node:fs/promises"),
69
+ import("node:module"),
70
+ import("node:os"),
71
+ import("node:path"),
72
+ ]);
73
+
74
+ return {
75
+ childProcess,
76
+ fs,
77
+ moduleApi,
78
+ os,
79
+ path,
80
+ };
81
+ } catch {
82
+ return null;
83
+ }
84
+ }
85
+
86
+ type CommandResult = {
87
+ stdout: string;
88
+ stderr: string;
89
+ code: number;
90
+ };
91
+
92
+ async function runCommand(
93
+ runtime: NodeRuntimeModules,
94
+ command: string,
95
+ args: Array<string>,
96
+ input?: string,
97
+ ): Promise<CommandResult> {
98
+ return await new Promise((resolve) => {
99
+ const child = runtime.childProcess.spawn(command, args, {
100
+ stdio: "pipe",
101
+ });
102
+
103
+ let stdout = "";
104
+ let stderr = "";
105
+
106
+ child.stdout.on("data", (chunk: Buffer) => {
107
+ stdout += chunk.toString("utf8");
108
+ });
109
+ child.stderr.on("data", (chunk: Buffer) => {
110
+ stderr += chunk.toString("utf8");
111
+ });
112
+ child.stdin.on("error", () => {
113
+ // Ignore EPIPE if the child exits before consuming stdin.
114
+ });
115
+
116
+ child.on("error", () => {
117
+ resolve({
118
+ stdout: "",
119
+ stderr: "",
120
+ code: 127,
121
+ });
122
+ });
123
+
124
+ child.on("close", (code) => {
125
+ resolve({
126
+ stdout,
127
+ stderr,
128
+ code: code ?? 1,
129
+ });
130
+ });
131
+
132
+ if (input !== undefined) {
133
+ child.stdin.write(input);
134
+ }
135
+ child.stdin.end();
136
+ });
137
+ }
138
+
139
+ async function readJsonFile<T>(runtime: NodeRuntimeModules, filePath: string): Promise<T | null> {
140
+ try {
141
+ const value = await runtime.fs.readFile(filePath, "utf8");
142
+ return JSON.parse(value) as T;
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+
148
+ function isCliConfig(value: unknown): value is CliConfig {
149
+ return (
150
+ typeof value === "object" &&
151
+ value !== null &&
152
+ typeof (value as { baseUrl?: unknown }).baseUrl === "string" &&
153
+ typeof (value as { activeAccountId?: unknown }).activeAccountId === "string"
154
+ );
155
+ }
156
+
157
+ function isCliCredentials(value: unknown): value is CliCredentials {
158
+ if (typeof value !== "object" || value === null) {
159
+ return false;
160
+ }
161
+
162
+ const candidate = value as Record<string, unknown>;
163
+ return (
164
+ typeof candidate.accessToken === "string" &&
165
+ typeof candidate.refreshToken === "string" &&
166
+ typeof candidate.accessTokenExpiresAtMs === "number" &&
167
+ typeof candidate.refreshTokenExpiresAtMs === "number" &&
168
+ typeof candidate.clerkUserId === "string" &&
169
+ typeof candidate.orgId === "string" &&
170
+ typeof candidate.orgSlug === "string"
171
+ );
172
+ }
173
+
174
+ async function getFromKeychain(
175
+ runtime: NodeRuntimeModules,
176
+ account: string,
177
+ ): Promise<string | null> {
178
+ if (process.platform === "darwin") {
179
+ const result = await runCommand(runtime, "security", [
180
+ "find-generic-password",
181
+ "-s",
182
+ CLI_SERVICE_NAME,
183
+ "-a",
184
+ account,
185
+ "-w",
186
+ ]);
187
+ if (result.code !== 0) {
188
+ return null;
189
+ }
190
+ const value = result.stdout.trim();
191
+ return value.length > 0 ? value : null;
192
+ }
193
+
194
+ if (process.platform === "linux") {
195
+ const result = await runCommand(runtime, "secret-tool", [
196
+ "lookup",
197
+ "service",
198
+ CLI_SERVICE_NAME,
199
+ "account",
200
+ account,
201
+ ]);
202
+ if (result.code !== 0) {
203
+ return null;
204
+ }
205
+ const value = result.stdout.trim();
206
+ return value.length > 0 ? value : null;
207
+ }
208
+
209
+ return null;
210
+ }
211
+
212
+ async function setInKeychain(
213
+ runtime: NodeRuntimeModules,
214
+ account: string,
215
+ value: string,
216
+ ): Promise<boolean> {
217
+ if (process.platform === "darwin") {
218
+ const result = await runCommand(runtime, "security", [
219
+ "add-generic-password",
220
+ "-U",
221
+ "-s",
222
+ CLI_SERVICE_NAME,
223
+ "-a",
224
+ account,
225
+ "-w",
226
+ value,
227
+ ]);
228
+ return result.code === 0;
229
+ }
230
+
231
+ if (process.platform === "linux") {
232
+ const result = await runCommand(
233
+ runtime,
234
+ "secret-tool",
235
+ ["store", `--label=${CLI_SERVICE_NAME}`, "service", CLI_SERVICE_NAME, "account", account],
236
+ value,
237
+ );
238
+ return result.code === 0;
239
+ }
240
+
241
+ return false;
242
+ }
243
+
244
+ function getConfigPaths(runtime: NodeRuntimeModules): {
245
+ configPath: string;
246
+ credentialsDir: string;
247
+ } {
248
+ const configDir = runtime.path.join(runtime.os.homedir(), ".config", "barekey");
249
+ return {
250
+ configPath: runtime.path.join(configDir, "config.json"),
251
+ credentialsDir: runtime.path.join(configDir, "credentials"),
252
+ };
253
+ }
254
+
255
+ function credentialsPathForAccount(
256
+ runtime: NodeRuntimeModules,
257
+ credentialsDir: string,
258
+ accountId: string,
259
+ ): string {
260
+ return runtime.path.join(credentialsDir, `${encodeURIComponent(accountId)}.json`);
261
+ }
262
+
263
+ async function saveCliCredentials(
264
+ runtime: NodeRuntimeModules,
265
+ accountId: string,
266
+ credentials: CliCredentials,
267
+ ): Promise<void> {
268
+ const serialized = JSON.stringify(credentials);
269
+ if (await setInKeychain(runtime, accountId, serialized)) {
270
+ return;
271
+ }
272
+
273
+ const { credentialsDir } = getConfigPaths(runtime);
274
+ await runtime.fs.mkdir(credentialsDir, {
275
+ recursive: true,
276
+ mode: 0o700,
277
+ });
278
+ await runtime.fs.writeFile(
279
+ credentialsPathForAccount(runtime, credentialsDir, accountId),
280
+ serialized,
281
+ {
282
+ encoding: "utf8",
283
+ mode: 0o600,
284
+ },
285
+ );
286
+ }
287
+
288
+ async function readCliSessionWithSource(
289
+ runtime: NodeRuntimeModules,
290
+ ): Promise<StoredCliSessionWithSource | null> {
291
+ const { configPath, credentialsDir } = getConfigPaths(runtime);
292
+ const rawConfig = await readJsonFile<unknown>(runtime, configPath);
293
+ if (rawConfig === null) {
294
+ return null;
295
+ }
296
+ if (!isCliConfig(rawConfig)) {
297
+ throw new InvalidCredentialsProvidedError({
298
+ message: "Stored Barekey CLI config is malformed.",
299
+ });
300
+ }
301
+
302
+ const fromKeychain = await getFromKeychain(runtime, rawConfig.activeAccountId);
303
+ if (fromKeychain !== null) {
304
+ try {
305
+ const parsed = JSON.parse(fromKeychain) as unknown;
306
+ if (!isCliCredentials(parsed)) {
307
+ throw new InvalidCredentialsProvidedError({
308
+ message: "Stored Barekey CLI credentials are malformed.",
309
+ });
310
+ }
311
+ return {
312
+ baseUrl: rawConfig.baseUrl,
313
+ activeAccountId: rawConfig.activeAccountId,
314
+ credentials: parsed,
315
+ source: "keychain",
316
+ };
317
+ } catch (error: unknown) {
318
+ if (error instanceof InvalidCredentialsProvidedError) {
319
+ throw error;
320
+ }
321
+ throw new InvalidCredentialsProvidedError({
322
+ message: "Stored Barekey CLI credentials are malformed.",
323
+ cause: error,
324
+ });
325
+ }
326
+ }
327
+
328
+ const fromFile = await readJsonFile<unknown>(
329
+ runtime,
330
+ credentialsPathForAccount(runtime, credentialsDir, rawConfig.activeAccountId),
331
+ );
332
+ if (fromFile === null) {
333
+ return null;
334
+ }
335
+ if (!isCliCredentials(fromFile)) {
336
+ throw new InvalidCredentialsProvidedError({
337
+ message: "Stored Barekey CLI credentials are malformed.",
338
+ });
339
+ }
340
+
341
+ return {
342
+ baseUrl: rawConfig.baseUrl,
343
+ activeAccountId: rawConfig.activeAccountId,
344
+ credentials: fromFile,
345
+ source: "file",
346
+ };
347
+ }
348
+
349
+ export async function isFilesystemAvailable(): Promise<boolean> {
350
+ return (await loadNodeRuntime()) !== null;
351
+ }
352
+
353
+ export async function loadBarekeyJsonConfig(): Promise<{
354
+ path: string;
355
+ json: Record<string, unknown>;
356
+ } | null> {
357
+ const runtime = await loadNodeRuntime();
358
+ if (runtime === null) {
359
+ return null;
360
+ }
361
+
362
+ let current = runtime.path.resolve(process.cwd());
363
+ while (true) {
364
+ const candidate = runtime.path.join(current, "barekey.json");
365
+ try {
366
+ const raw = await runtime.fs.readFile(candidate, "utf8");
367
+ try {
368
+ return {
369
+ path: candidate,
370
+ json: JSON.parse(raw) as Record<string, unknown>,
371
+ };
372
+ } catch (error: unknown) {
373
+ throw new InvalidConfigurationProvidedError({
374
+ message: `The barekey.json file at ${candidate} is not valid JSON.`,
375
+ cause: error,
376
+ });
377
+ }
378
+ } catch (error: unknown) {
379
+ if (error instanceof InvalidConfigurationProvidedError) {
380
+ throw error;
381
+ }
382
+ const nodeError = error as NodeJS.ErrnoException | null;
383
+ if (nodeError?.code && nodeError.code !== "ENOENT") {
384
+ throw new InvalidConfigurationProvidedError({
385
+ message: `The barekey.json file at ${candidate} could not be read.`,
386
+ cause: error,
387
+ });
388
+ }
389
+ const parent = runtime.path.dirname(current);
390
+ if (parent === current) {
391
+ return null;
392
+ }
393
+ current = parent;
394
+ }
395
+ }
396
+ }
397
+
398
+ export async function loadCliSessionAuthResolver(fetchFn: typeof globalThis.fetch): Promise<{
399
+ baseUrl: string;
400
+ getAccessToken(): Promise<string>;
401
+ onUnauthorized(): Promise<void>;
402
+ } | null> {
403
+ const runtime = await loadNodeRuntime();
404
+ if (runtime === null) {
405
+ return null;
406
+ }
407
+
408
+ let cachedSession = await readCliSessionWithSource(runtime);
409
+ if (cachedSession === null) {
410
+ return null;
411
+ }
412
+
413
+ let forceRefresh = false;
414
+
415
+ const refreshCredentials = async (): Promise<string> => {
416
+ const currentSession = cachedSession;
417
+ if (currentSession === null) {
418
+ throw new NoCredentialsProvidedError();
419
+ }
420
+
421
+ if (!forceRefresh && currentSession.credentials.accessTokenExpiresAtMs > Date.now() + 10_000) {
422
+ return currentSession.credentials.accessToken;
423
+ }
424
+
425
+ const refreshed = await postJson<{
426
+ accessToken: string;
427
+ refreshToken: string;
428
+ accessTokenExpiresAtMs: number;
429
+ refreshTokenExpiresAtMs: number;
430
+ clerkUserId: string;
431
+ orgId: string;
432
+ orgSlug: string;
433
+ }>({
434
+ fetchFn,
435
+ baseUrl: currentSession.baseUrl,
436
+ path: "/v1/cli/token/refresh",
437
+ payload: {
438
+ refreshToken: currentSession.credentials.refreshToken,
439
+ },
440
+ });
441
+
442
+ const nextCredentials: CliCredentials = {
443
+ accessToken: refreshed.accessToken,
444
+ refreshToken: refreshed.refreshToken,
445
+ accessTokenExpiresAtMs: refreshed.accessTokenExpiresAtMs,
446
+ refreshTokenExpiresAtMs: refreshed.refreshTokenExpiresAtMs,
447
+ clerkUserId: refreshed.clerkUserId,
448
+ orgId: refreshed.orgId,
449
+ orgSlug: refreshed.orgSlug,
450
+ };
451
+
452
+ await saveCliCredentials(runtime, currentSession.activeAccountId, nextCredentials);
453
+ cachedSession = {
454
+ ...currentSession,
455
+ credentials: nextCredentials,
456
+ };
457
+ forceRefresh = false;
458
+ return nextCredentials.accessToken;
459
+ };
460
+
461
+ return {
462
+ baseUrl: cachedSession.baseUrl,
463
+ async getAccessToken(): Promise<string> {
464
+ return await refreshCredentials();
465
+ },
466
+ async onUnauthorized(): Promise<void> {
467
+ forceRefresh = true;
468
+ },
469
+ };
470
+ }
471
+
472
+ function isMissingModuleError(error: unknown): boolean {
473
+ const nodeError = error as NodeJS.ErrnoException | null;
474
+ return nodeError?.code === "MODULE_NOT_FOUND" || nodeError?.code === "ERR_MODULE_NOT_FOUND";
475
+ }
476
+
477
+ type InstalledSdkTypegenTarget = {
478
+ packageRoot: string;
479
+ generatedTypesPath: string;
480
+ };
481
+
482
+ export async function resolveInstalledSdkTypegenTarget(): Promise<InstalledSdkTypegenTarget | null> {
483
+ const runtime = await loadNodeRuntime();
484
+ if (runtime === null) {
485
+ return null;
486
+ }
487
+
488
+ const require = runtime.moduleApi.createRequire(
489
+ runtime.path.join(process.cwd(), "__barekey_typegen__.cjs"),
490
+ );
491
+
492
+ let resolvedEntrypoint: string;
493
+ try {
494
+ resolvedEntrypoint = require.resolve("@barekey/sdk/package.json");
495
+ } catch (error: unknown) {
496
+ if (isMissingModuleError(error)) {
497
+ throw new SdkModuleNotFoundError();
498
+ }
499
+ throw new SdkModuleNotFoundError({
500
+ message: "The installed @barekey/sdk module could not be resolved from the current project.",
501
+ cause: error,
502
+ });
503
+ }
504
+
505
+ let current = runtime.path.dirname(await runtime.fs.realpath(resolvedEntrypoint));
506
+ while (true) {
507
+ const candidatePackageJson = runtime.path.join(current, "package.json");
508
+ const rawPackageJson = await readJsonFile<Record<string, unknown>>(runtime, candidatePackageJson);
509
+ if (rawPackageJson !== null && rawPackageJson.name === "@barekey/sdk") {
510
+ const generatedTypesPath = runtime.path.join(current, "generated.d.ts");
511
+ try {
512
+ await runtime.fs.access(generatedTypesPath);
513
+ } catch (error: unknown) {
514
+ throw new TypegenUnsupportedSdkError({
515
+ message: `The installed @barekey/sdk module at ${current} does not include generated.d.ts.`,
516
+ cause: error,
517
+ });
518
+ }
519
+
520
+ return {
521
+ packageRoot: current,
522
+ generatedTypesPath,
523
+ };
524
+ }
525
+
526
+ const parent = runtime.path.dirname(current);
527
+ if (parent === current) {
528
+ break;
529
+ }
530
+ current = parent;
531
+ }
532
+
533
+ throw new TypegenUnsupportedSdkError({
534
+ message: "The installed @barekey/sdk module could not be mapped to a supported package root.",
535
+ });
536
+ }
537
+
538
+ export async function readTextFile(filePath: string): Promise<string | null> {
539
+ const runtime = await loadNodeRuntime();
540
+ if (runtime === null) {
541
+ return null;
542
+ }
543
+
544
+ try {
545
+ return await runtime.fs.readFile(filePath, "utf8");
546
+ } catch (error: unknown) {
547
+ const nodeError = error as NodeJS.ErrnoException | null;
548
+ if (nodeError?.code === "ENOENT") {
549
+ return null;
550
+ }
551
+ throw new TypegenReadFailedError({
552
+ message: `Barekey could not read ${filePath} while updating generated SDK types.`,
553
+ cause: error,
554
+ });
555
+ }
556
+ }
557
+
558
+ export async function writeTextFileAtomic(filePath: string, value: string): Promise<void> {
559
+ const runtime = await loadNodeRuntime();
560
+ if (runtime === null) {
561
+ throw new TypegenWriteFailedError({
562
+ message: "Barekey could not update generated SDK types because filesystem access is unavailable.",
563
+ });
564
+ }
565
+
566
+ const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
567
+ try {
568
+ await runtime.fs.writeFile(temporaryPath, value, "utf8");
569
+ await runtime.fs.rename(temporaryPath, filePath);
570
+ } catch (error: unknown) {
571
+ try {
572
+ await runtime.fs.unlink(temporaryPath);
573
+ } catch {
574
+ // Best effort cleanup only.
575
+ }
576
+ throw new TypegenWriteFailedError({
577
+ message: `Barekey could not write ${filePath}.`,
578
+ cause: error,
579
+ });
580
+ }
581
+ }
@@ -0,0 +1,57 @@
1
+ import { InvalidConfigurationProvidedError, RequirementsValidationFailedError } from "../errors.js";
2
+ import type {
3
+ BarekeyStandardSchemaPathSegment,
4
+ BarekeyStandardSchemaResult,
5
+ BarekeyStandardSchemaV1,
6
+ } from "../types.js";
7
+
8
+ function isStandardSchemaResult(value: unknown): value is BarekeyStandardSchemaResult {
9
+ if (typeof value !== "object" || value === null) {
10
+ return false;
11
+ }
12
+
13
+ return "value" in value || "issues" in value;
14
+ }
15
+
16
+ function formatPathSegment(segment: BarekeyStandardSchemaPathSegment): string {
17
+ if (typeof segment === "object" && segment !== null && "key" in segment) {
18
+ return String(segment.key);
19
+ }
20
+ return String(segment);
21
+ }
22
+
23
+ export async function validateRequirements(
24
+ requirements: BarekeyStandardSchemaV1,
25
+ value: unknown,
26
+ ): Promise<void> {
27
+ const standard = requirements["~standard"];
28
+ if (
29
+ standard === undefined ||
30
+ typeof standard !== "object" ||
31
+ standard === null ||
32
+ typeof standard.validate !== "function"
33
+ ) {
34
+ throw new InvalidConfigurationProvidedError({
35
+ message: "requirements must implement Standard Schema v1.",
36
+ });
37
+ }
38
+
39
+ const result = await standard.validate(value);
40
+ if (!isStandardSchemaResult(result)) {
41
+ throw new InvalidConfigurationProvidedError({
42
+ message: "requirements returned an invalid Standard Schema result.",
43
+ });
44
+ }
45
+
46
+ if ("issues" in result && Array.isArray(result.issues) && result.issues.length > 0) {
47
+ const firstIssue = result.issues[0];
48
+ const issuePath =
49
+ firstIssue?.path && firstIssue.path.length > 0
50
+ ? ` at ${firstIssue.path.map(formatPathSegment).join(".")}`
51
+ : "";
52
+ const issueMessage = firstIssue?.message ?? "Validation failed.";
53
+ throw new RequirementsValidationFailedError({
54
+ message: `Barekey requirements validation failed${issuePath}: ${issueMessage}`,
55
+ });
56
+ }
57
+ }