@clivly/core 0.10.2 → 0.10.3

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.
@@ -103,6 +103,19 @@ function deriveLiveness(facts) {
103
103
  } } : {}
104
104
  };
105
105
  if (facts.presence && facts.presence.transport === "websocket") return websocketLiveness(facts, facts.presence, base);
106
+ const probe = facts.reachabilityProbe;
107
+ if (probe?.reachable && facts.now - probe.checkedAt < staleMs) {
108
+ const probeAgeMs = facts.now - probe.checkedAt;
109
+ const issues = livenessIssues(facts);
110
+ return {
111
+ ...base,
112
+ ageMs: probeAgeMs,
113
+ ageLabel: formatAge(probeAgeMs),
114
+ health: issues.length > 0 ? "live_with_issues" : "live",
115
+ issues,
116
+ reachable: true
117
+ };
118
+ }
106
119
  if (ageMs === null || ageMs >= staleMs) return {
107
120
  ...base,
108
121
  health: "offline",
@@ -55,6 +55,19 @@ interface IntegrationFacts {
55
55
  * transport keeps the cadence-derived behaviour every SDK in the field uses.
56
56
  */
57
57
  presence?: DeploymentPresence | null;
58
+ /**
59
+ * The result of Clivly asking the app directly whether it is up.
60
+ *
61
+ * Every other signal here is *pushed* by the app. On a serverless host there
62
+ * is no process between requests to push anything: the SDK's heartbeat timer
63
+ * fires once at boot and is then frozen, so cadence alone reports a perfectly
64
+ * healthy deployment as offline forever. A probe inverts that — Clivly asks,
65
+ * and a host that answers is up by definition.
66
+ *
67
+ * Absent keeps the pure cadence behaviour, so nothing changes for apps that
68
+ * do hold a long-lived process.
69
+ */
70
+ reachabilityProbe?: ReachabilityProbe | null;
58
71
  /**
59
72
  * Faults the running app diagnosed about itself on its last heartbeat — an
60
73
  * unreadable database, a secret missing from the process environment. These
@@ -84,6 +97,21 @@ declare function formatAge(ms: number): string;
84
97
  /** The cadence `createClivlySDK` uses when the host app configures none. */
85
98
  declare const DEFAULT_HEARTBEAT_INTERVAL_MS = 60000;
86
99
  type ConnectionHealth = "live" | "live_with_issues" | "stale" | "offline";
100
+ /**
101
+ * Clivly's own answer to "is this app up", obtained by asking it rather than
102
+ * waiting to be told.
103
+ *
104
+ * `reachable` is deliberately about the *connection*, not the response body: an
105
+ * app that answers `401` has proved it is running just as conclusively as one
106
+ * that answers `200`. Only silence — a timeout, a refused connection, DNS that
107
+ * does not resolve — means offline.
108
+ */
109
+ interface ReachabilityProbe {
110
+ /** When Clivly performed the check. Probe results are cached, so this ages. */
111
+ checkedAt: number;
112
+ /** Did anything answer at the other end? */
113
+ reachable: boolean;
114
+ }
87
115
  /** Which algorithm decides whether this deployment is up. */
88
116
  type PresenceTransport = "websocket" | "http";
89
117
  type PresenceConnectionState = "connected" | "reconnecting" | "disconnected";
@@ -172,4 +200,4 @@ declare const INSTALL_COMMAND = "npx clivly init";
172
200
  declare function nextAction(steps: IntegrationStep[], status: IntegrationStatus, liveness: ConnectionLiveness): NextAction | null;
173
201
  declare function computeIntegrationState(facts: IntegrationFacts): IntegrationState;
174
202
  //#endregion
175
- export { ConnectionHealth, ConnectionLiveness, DEFAULT_HEARTBEAT_INTERVAL_MS, DeploymentPresence, HostEnvironment, INSTALL_COMMAND, INTEGRATION_STEPS, INTEGRATION_STEP_LABELS, IntegrationFacts, IntegrationState, IntegrationStatus, IntegrationStep, NextAction, PresenceConnectionState, PresenceTransport, RunFact, STEP_PREREQUISITES, StepId, StepProblem, StepState, computeIntegrationState, deriveLiveness, deriveStatus, formatAge, livenessWindows, nextAction, resolveSteps, stepIndex };
203
+ export { ConnectionHealth, ConnectionLiveness, DEFAULT_HEARTBEAT_INTERVAL_MS, DeploymentPresence, HostEnvironment, INSTALL_COMMAND, INTEGRATION_STEPS, INTEGRATION_STEP_LABELS, IntegrationFacts, IntegrationState, IntegrationStatus, IntegrationStep, NextAction, PresenceConnectionState, PresenceTransport, ReachabilityProbe, RunFact, STEP_PREREQUISITES, StepId, StepProblem, StepState, computeIntegrationState, deriveLiveness, deriveStatus, formatAge, livenessWindows, nextAction, resolveSteps, stepIndex };
@@ -55,6 +55,19 @@ interface IntegrationFacts {
55
55
  * transport keeps the cadence-derived behaviour every SDK in the field uses.
56
56
  */
57
57
  presence?: DeploymentPresence | null;
58
+ /**
59
+ * The result of Clivly asking the app directly whether it is up.
60
+ *
61
+ * Every other signal here is *pushed* by the app. On a serverless host there
62
+ * is no process between requests to push anything: the SDK's heartbeat timer
63
+ * fires once at boot and is then frozen, so cadence alone reports a perfectly
64
+ * healthy deployment as offline forever. A probe inverts that — Clivly asks,
65
+ * and a host that answers is up by definition.
66
+ *
67
+ * Absent keeps the pure cadence behaviour, so nothing changes for apps that
68
+ * do hold a long-lived process.
69
+ */
70
+ reachabilityProbe?: ReachabilityProbe | null;
58
71
  /**
59
72
  * Faults the running app diagnosed about itself on its last heartbeat — an
60
73
  * unreadable database, a secret missing from the process environment. These
@@ -84,6 +97,21 @@ declare function formatAge(ms: number): string;
84
97
  /** The cadence `createClivlySDK` uses when the host app configures none. */
85
98
  declare const DEFAULT_HEARTBEAT_INTERVAL_MS = 60000;
86
99
  type ConnectionHealth = "live" | "live_with_issues" | "stale" | "offline";
100
+ /**
101
+ * Clivly's own answer to "is this app up", obtained by asking it rather than
102
+ * waiting to be told.
103
+ *
104
+ * `reachable` is deliberately about the *connection*, not the response body: an
105
+ * app that answers `401` has proved it is running just as conclusively as one
106
+ * that answers `200`. Only silence — a timeout, a refused connection, DNS that
107
+ * does not resolve — means offline.
108
+ */
109
+ interface ReachabilityProbe {
110
+ /** When Clivly performed the check. Probe results are cached, so this ages. */
111
+ checkedAt: number;
112
+ /** Did anything answer at the other end? */
113
+ reachable: boolean;
114
+ }
87
115
  /** Which algorithm decides whether this deployment is up. */
88
116
  type PresenceTransport = "websocket" | "http";
89
117
  type PresenceConnectionState = "connected" | "reconnecting" | "disconnected";
@@ -172,4 +200,4 @@ declare const INSTALL_COMMAND = "npx clivly init";
172
200
  declare function nextAction(steps: IntegrationStep[], status: IntegrationStatus, liveness: ConnectionLiveness): NextAction | null;
173
201
  declare function computeIntegrationState(facts: IntegrationFacts): IntegrationState;
174
202
  //#endregion
175
- export { ConnectionHealth, ConnectionLiveness, DEFAULT_HEARTBEAT_INTERVAL_MS, DeploymentPresence, HostEnvironment, INSTALL_COMMAND, INTEGRATION_STEPS, INTEGRATION_STEP_LABELS, IntegrationFacts, IntegrationState, IntegrationStatus, IntegrationStep, NextAction, PresenceConnectionState, PresenceTransport, RunFact, STEP_PREREQUISITES, StepId, StepProblem, StepState, computeIntegrationState, deriveLiveness, deriveStatus, formatAge, livenessWindows, nextAction, resolveSteps, stepIndex };
203
+ export { ConnectionHealth, ConnectionLiveness, DEFAULT_HEARTBEAT_INTERVAL_MS, DeploymentPresence, HostEnvironment, INSTALL_COMMAND, INTEGRATION_STEPS, INTEGRATION_STEP_LABELS, IntegrationFacts, IntegrationState, IntegrationStatus, IntegrationStep, NextAction, PresenceConnectionState, PresenceTransport, ReachabilityProbe, RunFact, STEP_PREREQUISITES, StepId, StepProblem, StepState, computeIntegrationState, deriveLiveness, deriveStatus, formatAge, livenessWindows, nextAction, resolveSteps, stepIndex };
@@ -102,6 +102,19 @@ function deriveLiveness(facts) {
102
102
  } } : {}
103
103
  };
104
104
  if (facts.presence && facts.presence.transport === "websocket") return websocketLiveness(facts, facts.presence, base);
105
+ const probe = facts.reachabilityProbe;
106
+ if (probe?.reachable && facts.now - probe.checkedAt < staleMs) {
107
+ const probeAgeMs = facts.now - probe.checkedAt;
108
+ const issues = livenessIssues(facts);
109
+ return {
110
+ ...base,
111
+ ageMs: probeAgeMs,
112
+ ageLabel: formatAge(probeAgeMs),
113
+ health: issues.length > 0 ? "live_with_issues" : "live",
114
+ issues,
115
+ reachable: true
116
+ };
117
+ }
105
118
  if (ageMs === null || ageMs >= staleMs) return {
106
119
  ...base,
107
120
  health: "offline",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clivly/core",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "ORM/auth-agnostic CRM adapter contracts and shared domain types for Clivly.",