Unified · Split
| @@ -46,16 +46,7 @@ export interface KeychainPluginConfig { · 45 unmodified lines | ||
| 46 | 46 | // Plugin extension — public API on executor.keychain |
| 47 | 47 | // --------------------------------------------------------------------------- |
| 48 | 48 | |
| 49 | −export interface KeychainExtension { | |
| 50 | − /** Human-readable name for the keychain on this platform */ | |
| 51 | − readonly displayName: string; | |
| 52 | − | |
| 53 | − /** Whether the current platform supports system keychain */ | |
| 54 | − readonly isSupported: boolean; | |
| 55 | − | |
| 56 | − /** Check if a secret exists in the system keychain */ | |
| 57 | − readonly has: (id: string) => Effect.Effect<boolean>; | |
| 58 | −} | |
| 49 | +export type KeychainExtension = ReturnType<typeof makeKeychainExtension>; | |
| 59 | 50 | |
| 60 | 51 | // --------------------------------------------------------------------------- |
| 61 | 52 | // Plugin definition |
| @@ -69,25 +60,35 @@ const scopedServiceName = ( · 7 unmodified lines | ||
| 69 | 60 | ctx: PluginCtx<unknown>, |
| 70 | 61 | options: KeychainPluginConfig | undefined, |
| 71 | 62 | ): string => |
| 72 | − `${resolveServiceName(options?.serviceName)}/${ctx.scopes[0]!.id as string}`; | |
| 63 | + `${resolveServiceName(options?.serviceName)}/${ctx.scopes[0]!.id}`; | |
| 64 | + | |
| 65 | +const makeKeychainExtension = ( | |
| 66 | + ctx: PluginCtx<unknown>, | |
| 67 | + options: KeychainPluginConfig | undefined, | |
| 68 | +) => { | |
| 69 | + const serviceName = scopedServiceName(ctx, options); | |
| 70 | + return { | |
| 71 | + /** Human-readable name for the keychain on this platform */ | |
| 72 | + displayName: displayName(), | |
| 73 | + | |
| 74 | + /** Whether the current platform supports system keychain */ | |
| 75 | + isSupported: isSupportedPlatform(), | |
| 76 | + | |
| 77 | + /** Check if a secret exists in the system keychain */ | |
| 78 | + has: (id: string) => | |
| 79 | + getPassword(serviceName, id).pipe( | |
| 80 | + Effect.map((v) => v !== null), | |
| 81 | + Effect.orElseSucceed(() => false), | |
| 82 | + ), | |
| 83 | + }; | |
| 84 | +}; | |
| 73 | 85 | |
| 74 | 86 | export const keychainPlugin = definePlugin( |
| 75 | 87 | (options?: KeychainPluginConfig) => ({ |
| 76 | 88 | id: "keychain" as const, |
| 77 | 89 | storage: () => ({}), |
| 78 | 90 | |
| 79 | − extension: (ctx): KeychainExtension => { | |
| 80 | − const serviceName = scopedServiceName(ctx, options); | |
| 81 | − return { | |
| 82 | − displayName: displayName(), | |
| 83 | − isSupported: isSupportedPlatform(), | |
| 84 | − has: (id) => | |
| 85 | − getPassword(serviceName, id).pipe( | |
| 86 | − Effect.map((v) => v !== null), | |
| 87 | − Effect.orElseSucceed(() => false), | |
| 88 | − ), | |
| 89 | − }; | |
| 90 | − }, | |
| 91 | + extension: (ctx): KeychainExtension => makeKeychainExtension(ctx, options), | |
| 91 | 92 | |
| 92 | 93 | secretProviders: (ctx): Effect.Effect<readonly SecretProvider[]> => |
| 93 | 94 | Effect.gen(function* () { |
| @@ -103,9 +104,9 @@ export const keychainPlugin = definePlugin( · 9 unmodified lines | ||
| 103 | 104 | ), |
| 104 | 105 | ), |
| 105 | 106 | Effect.as(true), |
| 106 | − Effect.catch((cause) => | |
| 107 | + Effect.catch(() => | |
| 107 | 108 | Effect.logWarning( |
| 108 | − `keychain unavailable, skipping provider registration: ${cause.message}`, | |
| 109 | + "keychain unavailable, skipping provider registration", | |
| 109 | 110 | ).pipe(Effect.as(false)), |
| 110 | 111 | ), |
| 111 | 112 | ); |