(
ref: ConnectionRef,
options?: {
/** Skip the probe and return the persisted verdict when it is younger
* than this. The server owns the freshness decision so N open tabs
* revalidating on load cannot stampede an upstream. Omit = always
* probe (the manual "Check now"). */
readonly ifStaleMs?: number;
},
)
| 2621 | }); |
| 2622 | |
| 2623 | const connectionCheckHealth = ( |
| 2624 | ref: ConnectionRef, |
| 2625 | options?: { |
| 2626 | /** Skip the probe and return the persisted verdict when it is younger |
| 2627 | * than this. The server owns the freshness decision so N open tabs |
| 2628 | * revalidating on load cannot stampede an upstream. Omit = always |
| 2629 | * probe (the manual "Check now"). */ |
| 2630 | readonly ifStaleMs?: number; |
| 2631 | }, |
| 2632 | ): Effect.Effect< |
| 2633 | HealthCheckResult, |
| 2634 | ConnectionNotFoundError | IntegrationNotFoundError | StorageFailure |
| 2635 | > => |
| 2636 | Effect.gen(function* () { |
| 2637 | const connectionRow = yield* findConnectionRow(ref); |
| 2638 | if (!connectionRow) { |
| 2639 | return yield* new ConnectionNotFoundError({ |
| 2640 | owner: ref.owner, |
| 2641 | integration: ref.integration, |
| 2642 | name: ref.name, |
| 2643 | }); |
| 2644 | } |
| 2645 | if (options?.ifStaleMs !== undefined) { |
| 2646 | const cached = Option.getOrNull(decodeLastHealth(connectionRow.last_health)); |
| 2647 | if (cached && Date.now() - cached.checkedAt < options.ifStaleMs) return cached; |
| 2648 | } |
| 2649 | const integrationRow = yield* findIntegrationRow(ref.integration); |
| 2650 | if (!integrationRow) { |
| 2651 | return yield* new IntegrationNotFoundError({ slug: ref.integration }); |
| 2652 | } |
| 2653 | const runtime = runtimes.get(integrationRow.plugin_id); |
| 2654 | const check = runtime?.plugin.checkHealth; |
| 2655 | if (!runtime || !check) return unknownHealth(); |
| 2656 | |
| 2657 | const values = yield* foldResolutionFailure(resolveConnectionValues(connectionRow)); |
| 2658 | const record = rowToIntegrationRecord( |
| 2659 | integrationRow, |
| 2660 | describeAuthMethodsForRow(integrationRow), |
| 2661 | ); |
| 2662 | const credential: ToolInvocationCredential = { |
| 2663 | owner: connectionRow.owner as Owner, |
| 2664 | integration: ref.integration, |
| 2665 | connection: ConnectionName.make(connectionRow.name), |
| 2666 | template: AuthTemplateSlug.make(connectionRow.template), |
| 2667 | value: values[PRIMARY_INPUT_VARIABLE] ?? null, |
| 2668 | values, |
| 2669 | config: record.config, |
| 2670 | }; |
| 2671 | // Core resolves the declared spec (its own column) and hands it to the |
| 2672 | // plugin; plugins no longer read it out of their config. |
| 2673 | const spec = describeHealthCheckForRow(integrationRow) ?? undefined; |
| 2674 | const result = yield* foldPluginFailure( |
| 2675 | check({ ctx: runtime.ctx, integration: record, credential, spec }), |
| 2676 | `Health check for connection "${ref.name}" failed.`, |
| 2677 | ); |
| 2678 | // Persist the verdict on the connection row so the accounts list shows |
| 2679 | // alive/expired at a glance. Best-effort: a write failure must not turn |
| 2680 | // a successful probe into an error. |
nothing calls this directly
no test coverage detected