( ctx: PluginCtx<OnePasswordStore>, serviceFor: ServiceForAccount, resolveRef: CachedRefResolver, )
| 473 | // --------------------------------------------------------------------------- |
| 474 | |
| 475 | const makeProvider = ( |
| 476 | ctx: PluginCtx<OnePasswordStore>, |
| 477 | serviceFor: ServiceForAccount, |
| 478 | resolveRef: CachedRefResolver, |
| 479 | ): CredentialProvider => { |
| 480 | return { |
| 481 | key: PROVIDER_KEY, |
| 482 | writable: false, |
| 483 | |
| 484 | get: (id: ProviderItemId): Effect.Effect<string | null, StorageFailure> => |
| 485 | ctx.storage.getConfig().pipe( |
| 486 | // An undecodable stored config reads as "not configured" here; the |
| 487 | // settings surface reports the decode problem. |
| 488 | Effect.catchTag("OnePasswordError", () => Effect.succeed(null)), |
| 489 | Effect.flatMap((config) => { |
| 490 | if (!config) return Effect.succeed(null as string | null); |
| 491 | |
| 492 | return resolveRef(config, id).pipe( |
| 493 | // Backend unreachability degrades to "no value", matching the other |
| 494 | // providers. Ambiguity does NOT: silently picking a vault (or |
| 495 | // silently failing) hides a real conflict, so it surfaces as a |
| 496 | // typed failure with the full explanation. |
| 497 | Effect.catch(() => Effect.succeed({ kind: "not-found" } as RefResolution)), |
| 498 | Effect.flatMap((resolution): Effect.Effect<string | null, StorageError> => { |
| 499 | if (resolution.kind === "ambiguous") { |
| 500 | return Effect.fail( |
| 501 | new StorageError({ |
| 502 | message: ambiguityMessage(id, resolution.matches), |
| 503 | cause: undefined, |
| 504 | }), |
| 505 | ); |
| 506 | } |
| 507 | if (resolution.kind === "ambiguous-vault") { |
| 508 | return Effect.fail( |
| 509 | new StorageError({ |
| 510 | message: vaultAmbiguityMessage(resolution), |
| 511 | cause: undefined, |
| 512 | }), |
| 513 | ); |
| 514 | } |
| 515 | return Effect.succeed(resolution.kind === "resolved" ? resolution.value : null); |
| 516 | }), |
| 517 | ); |
| 518 | }), |
| 519 | ), |
| 520 | |
| 521 | list: (): Effect.Effect<readonly ProviderEntry[], StorageFailure> => |
| 522 | ctx.storage.getConfig().pipe( |
| 523 | Effect.flatMap((config) => { |
| 524 | if (!config) return Effect.succeed([] as readonly ProviderEntry[]); |
| 525 | const multipleAccounts = config.accounts.length > 1; |
| 526 | return Effect.forEach(config.accounts, (account) => |
| 527 | serviceFor(account).pipe( |
| 528 | Effect.flatMap((svc) => |
| 529 | Effect.forEach(account.vaults, (vault) => |
| 530 | svc.listItems(vault.id).pipe( |
| 531 | Effect.map((items) => |
| 532 | items.map( |
no test coverage detected