@oatscenter/trellis 0.100.0-rc.1

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 (264) hide show
  1. package/README.md +59 -0
  2. package/auth/authorization/client_context.d.ts +61 -0
  3. package/auth/authorization/client_context.js +266 -0
  4. package/auth/authorization/nats_registry.d.ts +41 -0
  5. package/auth/authorization/nats_registry.js +283 -0
  6. package/auth/authorization/provider_cache.d.ts +90 -0
  7. package/auth/authorization/provider_cache.js +783 -0
  8. package/auth/authorization/refresh.d.ts +46 -0
  9. package/auth/authorization/refresh.js +246 -0
  10. package/auth/authorization/types.d.ts +207 -0
  11. package/auth/authorization/types.js +60 -0
  12. package/auth/authorization_context.d.ts +11 -0
  13. package/auth/authorization_context.js +10 -0
  14. package/auth/browser/flow_types.d.ts +84 -0
  15. package/auth/browser/flow_types.js +70 -0
  16. package/auth/browser/portal.d.ts +21 -0
  17. package/auth/browser/portal.js +307 -0
  18. package/auth/browser/storage.d.ts +26 -0
  19. package/auth/browser/storage.js +224 -0
  20. package/auth/browser.d.ts +11 -0
  21. package/auth/browser.js +10 -0
  22. package/auth/browser_recovery.d.ts +13 -0
  23. package/auth/browser_recovery.js +38 -0
  24. package/auth/device_activation.d.ts +134 -0
  25. package/auth/device_activation.js +376 -0
  26. package/auth/http_error.d.ts +12 -0
  27. package/auth/http_error.js +32 -0
  28. package/auth/keys.d.ts +5 -0
  29. package/auth/keys.js +63 -0
  30. package/auth/mod.d.ts +16 -0
  31. package/auth/mod.js +16 -0
  32. package/auth/proof.d.ts +31 -0
  33. package/auth/proof.js +95 -0
  34. package/auth/protocol_wasm/trellis_protocol_wasm.d.ts +138 -0
  35. package/auth/protocol_wasm/trellis_protocol_wasm.js +605 -0
  36. package/auth/protocol_wasm/trellis_protocol_wasm_bg.wasm +0 -0
  37. package/auth/protocol_wasm/trellis_protocol_wasm_bytes.d.ts +1 -0
  38. package/auth/protocol_wasm/trellis_protocol_wasm_bytes.js +2 -0
  39. package/auth/protocol_wasm.d.ts +179 -0
  40. package/auth/protocol_wasm.js +133 -0
  41. package/auth/session_auth.d.ts +30 -0
  42. package/auth/session_auth.js +77 -0
  43. package/auth/session_proof.d.ts +53 -0
  44. package/auth/session_proof.js +43 -0
  45. package/auth/time.d.ts +6 -0
  46. package/auth/time.js +7 -0
  47. package/auth/trellis_id.d.ts +1 -0
  48. package/auth/trellis_id.js +5 -0
  49. package/auth/utils.d.ts +10 -0
  50. package/auth/utils.js +69 -0
  51. package/auth.d.ts +1 -0
  52. package/auth.js +1 -0
  53. package/caller.d.ts +111 -0
  54. package/caller.js +85 -0
  55. package/client.d.ts +14 -0
  56. package/client.js +1 -0
  57. package/client_connect.d.ts +143 -0
  58. package/client_connect.js +1097 -0
  59. package/codec.d.ts +13 -0
  60. package/codec.js +192 -0
  61. package/connection.d.ts +107 -0
  62. package/connection.js +396 -0
  63. package/device/activation.d.ts +42 -0
  64. package/device/activation.js +319 -0
  65. package/device.d.ts +80 -0
  66. package/device.js +687 -0
  67. package/env.d.ts +2 -0
  68. package/env.js +10 -0
  69. package/errors/AuthError.d.ts +30 -0
  70. package/errors/AuthError.js +34 -0
  71. package/errors/KVError.d.ts +30 -0
  72. package/errors/KVError.js +36 -0
  73. package/errors/OperationLifecycleError.d.ts +110 -0
  74. package/errors/OperationLifecycleError.js +142 -0
  75. package/errors/RemoteError.d.ts +61 -0
  76. package/errors/RemoteError.js +72 -0
  77. package/errors/SchemaValidationError.d.ts +48 -0
  78. package/errors/SchemaValidationError.js +49 -0
  79. package/errors/StoreError.d.ts +21 -0
  80. package/errors/StoreError.js +31 -0
  81. package/errors/TransferError.d.ts +21 -0
  82. package/errors/TransferError.js +31 -0
  83. package/errors/TransportError.d.ts +29 -0
  84. package/errors/TransportError.js +33 -0
  85. package/errors/TrellisError.d.ts +15 -0
  86. package/errors/TrellisError.js +15 -0
  87. package/errors/ValidationError.d.ts +50 -0
  88. package/errors/ValidationError.js +59 -0
  89. package/errors/index.d.ts +254 -0
  90. package/errors/index.js +238 -0
  91. package/generated.d.ts +4 -0
  92. package/generated.js +3 -0
  93. package/generated_support.d.ts +120 -0
  94. package/generated_support.js +359 -0
  95. package/globals.d.ts +9 -0
  96. package/globals.js +16 -0
  97. package/health_transport.d.ts +18 -0
  98. package/health_transport.js +32 -0
  99. package/helpers.d.ts +11 -0
  100. package/helpers.js +53 -0
  101. package/index.d.ts +2 -0
  102. package/index.js +2 -0
  103. package/internal_sdk/generated/apis/auth/mod.d.ts +805 -0
  104. package/internal_sdk/generated/apis/auth/mod.js +958 -0
  105. package/internal_sdk/generated/apis/core/mod.d.ts +79 -0
  106. package/internal_sdk/generated/apis/core/mod.js +93 -0
  107. package/internal_sdk/generated/apis/events/mod.d.ts +224 -0
  108. package/internal_sdk/generated/apis/events/mod.js +313 -0
  109. package/internal_sdk/generated/apis/health/mod.d.ts +106 -0
  110. package/internal_sdk/generated/apis/health/mod.js +132 -0
  111. package/internal_sdk/generated/apis/index.d.ts +7 -0
  112. package/internal_sdk/generated/apis/index.js +8 -0
  113. package/internal_sdk/generated/apis/jobs/mod.d.ts +176 -0
  114. package/internal_sdk/generated/apis/jobs/mod.js +209 -0
  115. package/internal_sdk/generated/apis/state/mod.d.ts +104 -0
  116. package/internal_sdk/generated/apis/state/mod.js +139 -0
  117. package/internal_sdk/generated/index.d.ts +4 -0
  118. package/internal_sdk/generated/index.js +5 -0
  119. package/internal_sdk/generated/package.json +18 -0
  120. package/internal_sdk/generated/participants/cli/mod.d.ts +402 -0
  121. package/internal_sdk/generated/participants/cli/mod.js +460 -0
  122. package/internal_sdk/generated/participants/cli/types.d.ts +2 -0
  123. package/internal_sdk/generated/participants/cli/types.js +3 -0
  124. package/internal_sdk/generated/participants/console/mod.d.ts +489 -0
  125. package/internal_sdk/generated/participants/console/mod.js +579 -0
  126. package/internal_sdk/generated/participants/console/types.d.ts +2 -0
  127. package/internal_sdk/generated/participants/console/types.js +3 -0
  128. package/internal_sdk/generated/participants/eventsRuntime/mod.d.ts +64 -0
  129. package/internal_sdk/generated/participants/eventsRuntime/mod.js +42 -0
  130. package/internal_sdk/generated/participants/eventsRuntime/types.d.ts +2 -0
  131. package/internal_sdk/generated/participants/eventsRuntime/types.js +3 -0
  132. package/internal_sdk/generated/participants/healthRuntime/mod.d.ts +58 -0
  133. package/internal_sdk/generated/participants/healthRuntime/mod.js +36 -0
  134. package/internal_sdk/generated/participants/healthRuntime/types.d.ts +2 -0
  135. package/internal_sdk/generated/participants/healthRuntime/types.js +3 -0
  136. package/internal_sdk/generated/participants/index.d.ts +8 -0
  137. package/internal_sdk/generated/participants/index.js +9 -0
  138. package/internal_sdk/generated/participants/jobsRuntime/mod.d.ts +64 -0
  139. package/internal_sdk/generated/participants/jobsRuntime/mod.js +42 -0
  140. package/internal_sdk/generated/participants/jobsRuntime/types.d.ts +2 -0
  141. package/internal_sdk/generated/participants/jobsRuntime/types.js +3 -0
  142. package/internal_sdk/generated/participants/platform/mod.d.ts +164 -0
  143. package/internal_sdk/generated/participants/platform/mod.js +149 -0
  144. package/internal_sdk/generated/participants/platform/types.d.ts +7 -0
  145. package/internal_sdk/generated/participants/platform/types.js +5 -0
  146. package/internal_sdk/generated/participants/portal/mod.d.ts +131 -0
  147. package/internal_sdk/generated/participants/portal/mod.js +109 -0
  148. package/internal_sdk/generated/participants/portal/types.d.ts +2 -0
  149. package/internal_sdk/generated/participants/portal/types.js +3 -0
  150. package/internal_sdk/generated/types/_internal/p0.d.ts +13511 -0
  151. package/internal_sdk/generated/types/_internal/p0.js +6105 -0
  152. package/internal_sdk/generated/types/index.d.ts +2951 -0
  153. package/internal_sdk/generated/types/index.js +1477 -0
  154. package/jobs.d.ts +309 -0
  155. package/jobs.js +389 -0
  156. package/kv.d.ts +85 -0
  157. package/kv.js +330 -0
  158. package/operations.d.ts +189 -0
  159. package/operations.js +847 -0
  160. package/package.json +73 -0
  161. package/pagination.d.ts +25 -0
  162. package/pagination.js +117 -0
  163. package/participant.d.ts +6 -0
  164. package/participant.js +6 -0
  165. package/participant_runtime/api.d.ts +131 -0
  166. package/participant_runtime/api.js +42 -0
  167. package/participant_runtime/json.d.ts +16 -0
  168. package/participant_runtime/json.js +193 -0
  169. package/participant_runtime/metadata.d.ts +34 -0
  170. package/participant_runtime/metadata.js +4 -0
  171. package/participant_runtime/participant.d.ts +200 -0
  172. package/participant_runtime/participant.js +330 -0
  173. package/participant_runtime/schemas.d.ts +347 -0
  174. package/participant_runtime/schemas.js +211 -0
  175. package/participant_runtime/surface_names.d.ts +16 -0
  176. package/participant_runtime/surface_names.js +34 -0
  177. package/portable.d.ts +30 -0
  178. package/portable.js +14 -0
  179. package/provider.d.ts +69 -0
  180. package/provider.js +68 -0
  181. package/runtime_transport.d.ts +25 -0
  182. package/runtime_transport.js +48 -0
  183. package/service/mod.d.ts +14 -0
  184. package/service/mod.js +13 -0
  185. package/service/outbox_inbox.d.ts +259 -0
  186. package/service/outbox_inbox.js +908 -0
  187. package/service/runtime/bootstrap.d.ts +56 -0
  188. package/service/runtime/bootstrap.js +226 -0
  189. package/service/runtime/core.d.ts +46 -0
  190. package/service/runtime/core.js +1747 -0
  191. package/service/runtime/health.d.ts +97 -0
  192. package/service/runtime/health.js +153 -0
  193. package/service/runtime/health_schemas.d.ts +23 -0
  194. package/service/runtime/health_schemas.js +20 -0
  195. package/service/runtime/internal_jobs/active-job.d.ts +40 -0
  196. package/service/runtime/internal_jobs/active-job.js +91 -0
  197. package/service/runtime/internal_jobs/bindings.d.ts +40 -0
  198. package/service/runtime/internal_jobs/bindings.js +1 -0
  199. package/service/runtime/internal_jobs/cancellation-registry.d.ts +13 -0
  200. package/service/runtime/internal_jobs/cancellation-registry.js +55 -0
  201. package/service/runtime/internal_jobs/heartbeat.d.ts +33 -0
  202. package/service/runtime/internal_jobs/heartbeat.js +65 -0
  203. package/service/runtime/internal_jobs/job-manager.d.ts +126 -0
  204. package/service/runtime/internal_jobs/job-manager.js +821 -0
  205. package/service/runtime/internal_jobs/key-coordinator.d.ts +280 -0
  206. package/service/runtime/internal_jobs/key-coordinator.js +747 -0
  207. package/service/runtime/internal_jobs/projection.d.ts +3 -0
  208. package/service/runtime/internal_jobs/projection.js +42 -0
  209. package/service/runtime/internal_jobs/runtime-worker.d.ts +126 -0
  210. package/service/runtime/internal_jobs/runtime-worker.js +454 -0
  211. package/service/runtime/internal_jobs/types.d.ts +246 -0
  212. package/service/runtime/internal_jobs/types.js +163 -0
  213. package/service/runtime/logger.d.ts +2 -0
  214. package/service/runtime/logger.js +7 -0
  215. package/service/runtime/runtime.d.ts +16 -0
  216. package/service/runtime/runtime.js +1 -0
  217. package/service/runtime/service.d.ts +372 -0
  218. package/service/runtime/service.js +1896 -0
  219. package/service/runtime/transfer/download.d.ts +27 -0
  220. package/service/runtime/transfer/download.js +1 -0
  221. package/service/runtime/transfer/protocol.d.ts +15 -0
  222. package/service/runtime/transfer/protocol.js +46 -0
  223. package/service/runtime/transfer/queue.d.ts +18 -0
  224. package/service/runtime/transfer/queue.js +136 -0
  225. package/service/runtime/transfer/upload.d.ts +40 -0
  226. package/service/runtime/transfer/upload.js +27 -0
  227. package/service/runtime/transfer.d.ts +62 -0
  228. package/service/runtime/transfer.js +763 -0
  229. package/session.d.ts +862 -0
  230. package/session.js +3407 -0
  231. package/store.d.ts +71 -0
  232. package/store.js +412 -0
  233. package/store_entry.d.ts +20 -0
  234. package/store_entry.js +75 -0
  235. package/tasks.d.ts +10 -0
  236. package/tasks.js +28 -0
  237. package/telemetry/carrier.d.ts +8 -0
  238. package/telemetry/carrier.js +20 -0
  239. package/telemetry/core.d.ts +7 -0
  240. package/telemetry/core.js +14 -0
  241. package/telemetry/env.d.ts +2 -0
  242. package/telemetry/env.js +10 -0
  243. package/telemetry/init.d.ts +2 -0
  244. package/telemetry/init.js +7 -0
  245. package/telemetry/metrics.d.ts +81 -0
  246. package/telemetry/metrics.js +267 -0
  247. package/telemetry/mod.d.ts +13 -0
  248. package/telemetry/mod.js +8 -0
  249. package/telemetry/nats.d.ts +6 -0
  250. package/telemetry/nats.js +6 -0
  251. package/telemetry/result.d.ts +1 -0
  252. package/telemetry/result.js +18 -0
  253. package/telemetry/runtime.d.ts +1 -0
  254. package/telemetry/runtime.js +141 -0
  255. package/telemetry/trace.d.ts +4 -0
  256. package/telemetry/trace.js +26 -0
  257. package/telemetry/trellis.d.ts +1 -0
  258. package/telemetry/trellis.js +1 -0
  259. package/telemetry.d.ts +1 -0
  260. package/telemetry.js +1 -0
  261. package/transfer.d.ts +112 -0
  262. package/transfer.js +525 -0
  263. package/transfer_protocol.d.ts +1 -0
  264. package/transfer_protocol.js +13 -0
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Health check types and utility functions for Trellis services.
3
+ *
4
+ * This module provides a standardized way to implement health checks
5
+ * for services, with support for individual check results and
6
+ * aggregated health status (healthy, degraded, unhealthy).
7
+ *
8
+ * @module
9
+ */
10
+ import type { JsonValue } from "../../participant.ts";
11
+ import { type HealthHeartbeatSample } from "../../internal_sdk/generated/types/_internal/p0.js";
12
+ type MaybePromise<T> = T | Promise<T>;
13
+ /**
14
+ * Result of a single health check.
15
+ */
16
+ export type HealthCheckResult = {
17
+ /** Name of the health check */
18
+ name: string;
19
+ /** Status of the check: "ok" if passed, "failed" if not */
20
+ status: "ok" | "failed";
21
+ /** Error message if the check failed with an error */
22
+ error?: string;
23
+ /** Optional short human-readable summary */
24
+ summary?: string;
25
+ /** Optional structured metadata for the check */
26
+ info?: Record<string, JsonValue>;
27
+ /** Time in milliseconds the check took to execute */
28
+ latencyMs: number;
29
+ };
30
+ export type ServiceHealthInfo = {
31
+ version?: string;
32
+ info?: Record<string, JsonValue>;
33
+ };
34
+ export type ServiceHealthCheck = {
35
+ status: "ok" | "failed";
36
+ summary?: string;
37
+ info?: Record<string, JsonValue>;
38
+ error?: string;
39
+ };
40
+ export type ServiceHealthCheckFn = () => MaybePromise<ServiceHealthCheck>;
41
+ export type ServiceHealthInfoFn = () => MaybePromise<ServiceHealthInfo | undefined>;
42
+ /**
43
+ * Aggregated health response for a service.
44
+ */
45
+ export type HealthResponse = {
46
+ /** Overall status: "healthy" if all pass, "unhealthy" if all fail, "degraded" if mixed */
47
+ status: "healthy" | "unhealthy" | "degraded";
48
+ /** Name of the service */
49
+ service: string;
50
+ /** ISO timestamp of when the health check was performed */
51
+ timestamp: string;
52
+ /** Individual health check results */
53
+ checks: HealthCheckResult[];
54
+ };
55
+ export declare function runServiceHealthCheck(name: string, check: ServiceHealthCheckFn): Promise<HealthCheckResult>;
56
+ export declare function runAllServiceHealthChecks(service: string, checks: Record<string, ServiceHealthCheckFn>): Promise<HealthResponse>;
57
+ export declare function createHealthHeartbeatSample(args: {
58
+ serviceName: string;
59
+ kind?: HealthHeartbeatSample["participant"]["kind"];
60
+ instanceId: string;
61
+ contractId: string;
62
+ contractDigest: string;
63
+ startedAt: string;
64
+ publishIntervalMs: number;
65
+ checks: HealthCheckResult[];
66
+ info?: ServiceHealthInfo;
67
+ }): HealthHeartbeatSample;
68
+ /** Public health enrichment surface exposed by connected participants. */
69
+ export type ServiceHealth = Readonly<{
70
+ setInfo(info: ServiceHealthInfo | ServiceHealthInfoFn): void;
71
+ add(name: string, check: ServiceHealthCheckFn): () => void;
72
+ }>;
73
+ /** Internal health runtime used by automatic heartbeat publishers. */
74
+ export declare class ServiceHealthRuntime implements ServiceHealth {
75
+ #private;
76
+ readonly serviceName: string;
77
+ readonly kind: HealthHeartbeatSample["participant"]["kind"];
78
+ readonly instanceId: string;
79
+ readonly contractId: string;
80
+ readonly contractDigest: string;
81
+ readonly startedAt: string;
82
+ readonly publishIntervalMs: number;
83
+ constructor(args: {
84
+ serviceName: string;
85
+ kind?: HealthHeartbeatSample["participant"]["kind"];
86
+ instanceId?: string;
87
+ contractId: string;
88
+ contractDigest: string;
89
+ publishIntervalMs: number;
90
+ });
91
+ setInfo(info: ServiceHealthInfo | ServiceHealthInfoFn): void;
92
+ add(name: string, check: ServiceHealthCheckFn): () => void;
93
+ checks(): Promise<HealthCheckResult[]>;
94
+ response(): Promise<HealthResponse>;
95
+ sample(): Promise<HealthHeartbeatSample>;
96
+ }
97
+ export {};
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Health check types and utility functions for Trellis services.
3
+ *
4
+ * This module provides a standardized way to implement health checks
5
+ * for services, with support for individual check results and
6
+ * aggregated health status (healthy, degraded, unhealthy).
7
+ *
8
+ * @module
9
+ */
10
+ import { ulid } from "ulid";
11
+ import { HealthHeartbeatSampleCodec, } from "../../internal_sdk/generated/types/_internal/p0.js";
12
+ function summarizeHealthStatus(results) {
13
+ const allOk = results.every((r) => r.status === "ok");
14
+ const anyOk = results.some((r) => r.status === "ok");
15
+ return allOk ? "healthy" : anyOk ? "degraded" : "unhealthy";
16
+ }
17
+ function summarizeHealthChecks(results) {
18
+ const failedCount = results.filter((r) => r.status === "failed").length;
19
+ if (failedCount === 0) {
20
+ return undefined;
21
+ }
22
+ return `${failedCount} check${failedCount === 1 ? "" : "s"} failing`;
23
+ }
24
+ function detectRuntime() {
25
+ const maybeDeno = Reflect.get(globalThis, "Deno");
26
+ if (maybeDeno?.version?.deno) {
27
+ return { runtime: "deno", runtimeVersion: maybeDeno.version.deno };
28
+ }
29
+ const maybeProcess = Reflect.get(globalThis, "process");
30
+ if (typeof maybeProcess?.version === "string") {
31
+ return { runtime: "node", runtimeVersion: maybeProcess.version };
32
+ }
33
+ return { runtime: "unknown" };
34
+ }
35
+ export async function runServiceHealthCheck(name, check) {
36
+ const start = performance.now();
37
+ try {
38
+ const result = await check();
39
+ const latencyMs = performance.now() - start;
40
+ return {
41
+ name,
42
+ status: result.status,
43
+ error: result.error,
44
+ summary: result.summary,
45
+ info: result.info,
46
+ latencyMs,
47
+ };
48
+ }
49
+ catch (error) {
50
+ const latencyMs = performance.now() - start;
51
+ const message = error instanceof Error ? error.message : String(error);
52
+ return {
53
+ name,
54
+ status: "failed",
55
+ error: message,
56
+ summary: message,
57
+ latencyMs,
58
+ };
59
+ }
60
+ }
61
+ export async function runAllServiceHealthChecks(service, checks) {
62
+ const results = await Promise.all(Object.entries(checks).map(([name, fn]) => runServiceHealthCheck(name, fn)));
63
+ return {
64
+ status: summarizeHealthStatus(results),
65
+ service,
66
+ timestamp: new Date().toISOString(),
67
+ checks: results,
68
+ };
69
+ }
70
+ export function createHealthHeartbeatSample(args) {
71
+ const runtime = detectRuntime();
72
+ const summary = summarizeHealthChecks(args.checks);
73
+ return HealthHeartbeatSampleCodec.decode({
74
+ sample: {
75
+ id: ulid(),
76
+ time: new Date().toISOString(),
77
+ },
78
+ participant: {
79
+ name: args.serviceName,
80
+ kind: args.kind ?? "service",
81
+ instanceId: args.instanceId,
82
+ contractId: args.contractId,
83
+ contractDigest: args.contractDigest,
84
+ startedAt: args.startedAt,
85
+ publishIntervalMs: String(args.publishIntervalMs),
86
+ runtime: runtime.runtime,
87
+ ...(runtime.runtimeVersion
88
+ ? { runtimeVersion: runtime.runtimeVersion }
89
+ : {}),
90
+ ...(args.info?.version ? { version: args.info.version } : {}),
91
+ ...(args.info?.info ? { info: args.info.info } : {}),
92
+ },
93
+ reportedStatus: summarizeHealthStatus(args.checks),
94
+ ...(summary ? { summary } : {}),
95
+ checks: args.checks,
96
+ });
97
+ }
98
+ /** Internal health runtime used by automatic heartbeat publishers. */
99
+ export class ServiceHealthRuntime {
100
+ serviceName;
101
+ kind;
102
+ instanceId;
103
+ contractId;
104
+ contractDigest;
105
+ startedAt;
106
+ publishIntervalMs;
107
+ #checks = new Map();
108
+ #info;
109
+ constructor(args) {
110
+ this.serviceName = args.serviceName;
111
+ this.kind = args.kind ?? "service";
112
+ this.instanceId = args.instanceId ?? ulid();
113
+ this.contractId = args.contractId;
114
+ this.contractDigest = args.contractDigest;
115
+ this.startedAt = new Date().toISOString();
116
+ this.publishIntervalMs = args.publishIntervalMs;
117
+ }
118
+ setInfo(info) {
119
+ if (typeof info === "function") {
120
+ this.#info = info;
121
+ return;
122
+ }
123
+ this.#info = () => info;
124
+ }
125
+ add(name, check) {
126
+ this.#checks.set(name, check);
127
+ return () => {
128
+ this.#checks.delete(name);
129
+ };
130
+ }
131
+ async checks() {
132
+ return await Promise.all(Array.from(this.#checks.entries()).map(([name, check]) => runServiceHealthCheck(name, check)));
133
+ }
134
+ async response() {
135
+ const checks = Object.fromEntries(this.#checks.entries());
136
+ return await runAllServiceHealthChecks(this.serviceName, checks);
137
+ }
138
+ async sample() {
139
+ const checks = await this.checks();
140
+ const info = this.#info ? await this.#info() : undefined;
141
+ return createHealthHeartbeatSample({
142
+ serviceName: this.serviceName,
143
+ kind: this.kind,
144
+ instanceId: this.instanceId,
145
+ contractId: this.contractId,
146
+ contractDigest: this.contractDigest,
147
+ startedAt: this.startedAt,
148
+ publishIntervalMs: this.publishIntervalMs,
149
+ checks,
150
+ info,
151
+ });
152
+ }
153
+ }
@@ -0,0 +1,23 @@
1
+ import { Type } from "typebox";
2
+ export declare const HealthInfoSchema: Type.TRecord<"^.*$", Type.TUnknown>;
3
+ export declare const HealthCheckResultSchema: Type.TObject<{
4
+ name: Type.TString;
5
+ status: Type.TUnion<[Type.TLiteral<"ok">, Type.TLiteral<"failed">]>;
6
+ error: Type.TOptional<Type.TString>;
7
+ summary: Type.TOptional<Type.TString>;
8
+ info: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
9
+ latencyMs: Type.TNumber;
10
+ }>;
11
+ export declare const HealthResponseSchema: Type.TObject<{
12
+ status: Type.TUnion<[Type.TLiteral<"healthy">, Type.TLiteral<"unhealthy">, Type.TLiteral<"degraded">]>;
13
+ service: Type.TString;
14
+ timestamp: Type.TString;
15
+ checks: Type.TArray<Type.TObject<{
16
+ name: Type.TString;
17
+ status: Type.TUnion<[Type.TLiteral<"ok">, Type.TLiteral<"failed">]>;
18
+ error: Type.TOptional<Type.TString>;
19
+ summary: Type.TOptional<Type.TString>;
20
+ info: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
21
+ latencyMs: Type.TNumber;
22
+ }>>;
23
+ }>;
@@ -0,0 +1,20 @@
1
+ import { Type } from "typebox";
2
+ export const HealthInfoSchema = Type.Record(Type.String(), Type.Unknown());
3
+ export const HealthCheckResultSchema = Type.Object({
4
+ name: Type.String(),
5
+ status: Type.Union([Type.Literal("ok"), Type.Literal("failed")]),
6
+ error: Type.Optional(Type.String()),
7
+ summary: Type.Optional(Type.String()),
8
+ info: Type.Optional(HealthInfoSchema),
9
+ latencyMs: Type.Number(),
10
+ });
11
+ export const HealthResponseSchema = Type.Object({
12
+ status: Type.Union([
13
+ Type.Literal("healthy"),
14
+ Type.Literal("unhealthy"),
15
+ Type.Literal("degraded"),
16
+ ]),
17
+ service: Type.String(),
18
+ timestamp: Type.String({ format: "date-time" }),
19
+ checks: Type.Array(HealthCheckResultSchema),
20
+ });
@@ -0,0 +1,40 @@
1
+ import type { Job, JobContext, JobLogEntry, JobProgress, JobWaitTarget } from "./types.ts";
2
+ export declare class ActiveJobRuntimeError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export type ActiveJobRuntimeMetadata = {
6
+ redeliveryCount: number;
7
+ };
8
+ export declare class JobCancellationToken {
9
+ #private;
10
+ cancel(): void;
11
+ cancelForShutdown(): void;
12
+ cancelForLeaseLoss(): void;
13
+ get signal(): AbortSignal;
14
+ isCancelled(): boolean;
15
+ isJobCancelled(): boolean;
16
+ isHostShutdown(): boolean;
17
+ isLeaseLost(): boolean;
18
+ }
19
+ type ActiveJobHooks = {
20
+ updateProgress: (progress: JobProgress) => Promise<void>;
21
+ log: (entry: JobLogEntry) => Promise<void>;
22
+ heartbeat: () => Promise<void>;
23
+ waitFor: <T>(target: JobWaitTarget, fn: () => Promise<T>) => Promise<T>;
24
+ };
25
+ export declare class ActiveJob<TPayload = unknown, TResult = unknown> {
26
+ #private;
27
+ constructor(job: Job<TPayload, TResult>, cancellation: JobCancellationToken, hooks: ActiveJobHooks, metadata: ActiveJobRuntimeMetadata);
28
+ job(): Readonly<Job<TPayload, TResult>>;
29
+ context(): Readonly<JobContext>;
30
+ get signal(): AbortSignal;
31
+ isCancelled(): boolean;
32
+ cancellationToken(): JobCancellationToken;
33
+ redeliveryCount(): number;
34
+ isRedelivery(): boolean;
35
+ heartbeat(): Promise<void>;
36
+ updateProgress(progress: JobProgress): Promise<void>;
37
+ log(level: JobLogEntry["level"], message: string): Promise<void>;
38
+ waitFor<T>(target: JobWaitTarget, fn: () => Promise<T> | T): Promise<T>;
39
+ }
40
+ export {};
@@ -0,0 +1,91 @@
1
+ export class ActiveJobRuntimeError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "ActiveJobRuntimeError";
5
+ }
6
+ }
7
+ export class JobCancellationToken {
8
+ #controller = new AbortController();
9
+ #reason = "none";
10
+ cancel() {
11
+ if (this.#reason === "shutdown") {
12
+ return;
13
+ }
14
+ this.#reason = "job";
15
+ this.#controller.abort("job");
16
+ }
17
+ cancelForShutdown() {
18
+ this.#reason = "shutdown";
19
+ this.#controller.abort("shutdown");
20
+ }
21
+ cancelForLeaseLoss() {
22
+ if (this.#reason !== "none")
23
+ return;
24
+ this.#reason = "lease-lost";
25
+ this.#controller.abort("lease-lost");
26
+ }
27
+ get signal() {
28
+ return this.#controller.signal;
29
+ }
30
+ isCancelled() {
31
+ return this.#reason !== "none";
32
+ }
33
+ isJobCancelled() {
34
+ return this.#reason === "job";
35
+ }
36
+ isHostShutdown() {
37
+ return this.#reason === "shutdown";
38
+ }
39
+ isLeaseLost() {
40
+ return this.#reason === "lease-lost";
41
+ }
42
+ }
43
+ export class ActiveJob {
44
+ #job;
45
+ #cancellation;
46
+ #hooks;
47
+ #metadata;
48
+ constructor(job, cancellation, hooks, metadata) {
49
+ this.#job = job;
50
+ this.#cancellation = cancellation;
51
+ this.#hooks = hooks;
52
+ this.#metadata = metadata;
53
+ }
54
+ job() {
55
+ return this.#job;
56
+ }
57
+ context() {
58
+ return this.#job.context;
59
+ }
60
+ get signal() {
61
+ return this.#cancellation.signal;
62
+ }
63
+ isCancelled() {
64
+ return this.#cancellation.isCancelled();
65
+ }
66
+ cancellationToken() {
67
+ return this.#cancellation;
68
+ }
69
+ redeliveryCount() {
70
+ return this.#metadata.redeliveryCount;
71
+ }
72
+ isRedelivery() {
73
+ return this.#metadata.redeliveryCount > 0;
74
+ }
75
+ heartbeat() {
76
+ return this.#hooks.heartbeat();
77
+ }
78
+ updateProgress(progress) {
79
+ return this.#hooks.updateProgress(progress);
80
+ }
81
+ log(level, message) {
82
+ return this.#hooks.log({
83
+ timestamp: new Date().toISOString(),
84
+ level,
85
+ message,
86
+ });
87
+ }
88
+ waitFor(target, fn) {
89
+ return this.#hooks.waitFor(target, async () => await fn());
90
+ }
91
+ }
@@ -0,0 +1,40 @@
1
+ export type JobsQueueBinding = {
2
+ queueType: string;
3
+ publishPrefix: string;
4
+ workSubject: string;
5
+ consumerName: string;
6
+ payload: {
7
+ schema: string;
8
+ };
9
+ update?: {
10
+ schema: string;
11
+ };
12
+ updatesPrefix?: string;
13
+ result?: {
14
+ schema: string;
15
+ };
16
+ maxDeliver: number;
17
+ backoffMs: number[];
18
+ ackWaitMs: number;
19
+ defaultDeadlineMs?: number;
20
+ keyConcurrency?: {
21
+ key: string[];
22
+ maxActive: number;
23
+ heartbeatIntervalMs: number;
24
+ heartbeatTtlMs: number;
25
+ stalePolicy: "fail-stale" | "block";
26
+ };
27
+ queue?: {
28
+ maxQueuedPerKey: number;
29
+ whenFull: "reject" | "coalesce" | "replace-oldest";
30
+ };
31
+ };
32
+ export type JobsBinding = {
33
+ serviceName: string;
34
+ namespace: string;
35
+ queues: Record<string, JobsQueueBinding>;
36
+ };
37
+ export type JobsRuntimeBinding = {
38
+ jobs: JobsBinding;
39
+ workStream: string;
40
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { JobCancellationToken } from "./active-job.ts";
2
+ export declare class ActiveJobCancellationGuard {
3
+ #private;
4
+ constructor(key: string, token: JobCancellationToken, registry: ActiveJobCancellationRegistry);
5
+ dispose(): void;
6
+ }
7
+ export declare class ActiveJobCancellationRegistry {
8
+ #private;
9
+ register(key: string, token: JobCancellationToken): ActiveJobCancellationGuard;
10
+ cancel(key: string): boolean;
11
+ clearPending(key: string): void;
12
+ unregister(key: string, token: JobCancellationToken): void;
13
+ }
@@ -0,0 +1,55 @@
1
+ export class ActiveJobCancellationGuard {
2
+ #key;
3
+ #token;
4
+ #registry;
5
+ #disposed = false;
6
+ constructor(key, token, registry) {
7
+ this.#key = key;
8
+ this.#token = token;
9
+ this.#registry = registry;
10
+ }
11
+ dispose() {
12
+ if (this.#disposed) {
13
+ return;
14
+ }
15
+ this.#disposed = true;
16
+ this.#registry.unregister(this.#key, this.#token);
17
+ }
18
+ }
19
+ export class ActiveJobCancellationRegistry {
20
+ #tokens = new Map();
21
+ #pending = new Set();
22
+ register(key, token) {
23
+ const tokens = this.#tokens.get(key) ?? new Set();
24
+ tokens.add(token);
25
+ this.#tokens.set(key, tokens);
26
+ if (this.#pending.delete(key)) {
27
+ token.cancel();
28
+ }
29
+ return new ActiveJobCancellationGuard(key, token, this);
30
+ }
31
+ cancel(key) {
32
+ const tokens = this.#tokens.get(key);
33
+ if (!tokens || tokens.size === 0) {
34
+ this.#pending.add(key);
35
+ return false;
36
+ }
37
+ for (const token of tokens) {
38
+ token.cancel();
39
+ }
40
+ return true;
41
+ }
42
+ clearPending(key) {
43
+ this.#pending.delete(key);
44
+ }
45
+ unregister(key, token) {
46
+ const tokens = this.#tokens.get(key);
47
+ if (!tokens) {
48
+ return;
49
+ }
50
+ tokens.delete(token);
51
+ if (tokens.size === 0) {
52
+ this.#tokens.delete(key);
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,33 @@
1
+ import type { WorkerHeartbeat } from "./types.ts";
2
+ type HeartbeatPublisher = {
3
+ publish(subject: string, payload: Uint8Array): void | Promise<void>;
4
+ };
5
+ type WorkerHeartbeatOptions = {
6
+ service: string;
7
+ jobType: string;
8
+ instanceId: string;
9
+ concurrency?: number;
10
+ version?: string;
11
+ timestamp: string;
12
+ };
13
+ type WorkerHeartbeatLoopOptions = {
14
+ publisher: HeartbeatPublisher;
15
+ service: string;
16
+ subjectService?: string;
17
+ jobType: string;
18
+ instanceId: string;
19
+ concurrency?: number;
20
+ version?: string;
21
+ intervalMs?: number;
22
+ nowIso?: () => string;
23
+ };
24
+ export declare class WorkerHeartbeatLoopError extends AggregateError {
25
+ constructor(errors: unknown[]);
26
+ }
27
+ export declare function workerHeartbeatSubject(service: string, jobType: string, instanceId: string): string;
28
+ export declare function newWorkerHeartbeat(options: WorkerHeartbeatOptions): WorkerHeartbeat;
29
+ export declare function publishWorkerHeartbeat(publisher: HeartbeatPublisher, heartbeat: WorkerHeartbeat, subjectService?: string): Promise<void>;
30
+ export declare function startWorkerHeartbeatLoop(options: WorkerHeartbeatLoopOptions): Promise<{
31
+ stop(): Promise<void>;
32
+ }>;
33
+ export {};
@@ -0,0 +1,65 @@
1
+ export class WorkerHeartbeatLoopError extends AggregateError {
2
+ constructor(errors) {
3
+ super(errors, "worker heartbeat loop failed");
4
+ this.name = "WorkerHeartbeatLoopError";
5
+ }
6
+ }
7
+ export function workerHeartbeatSubject(service, jobType, instanceId) {
8
+ return `trellis.jobs.workers.${service}.${jobType}.${instanceId}.heartbeat`;
9
+ }
10
+ export function newWorkerHeartbeat(options) {
11
+ return {
12
+ service: options.service,
13
+ jobType: options.jobType,
14
+ instanceId: options.instanceId,
15
+ ...(options.concurrency !== undefined
16
+ ? { concurrency: options.concurrency }
17
+ : {}),
18
+ ...(options.version !== undefined ? { version: options.version } : {}),
19
+ timestamp: options.timestamp,
20
+ };
21
+ }
22
+ export async function publishWorkerHeartbeat(publisher, heartbeat, subjectService = heartbeat.service) {
23
+ await publisher.publish(workerHeartbeatSubject(subjectService, heartbeat.jobType, heartbeat.instanceId), new TextEncoder().encode(JSON.stringify(heartbeat)));
24
+ }
25
+ export async function startWorkerHeartbeatLoop(options) {
26
+ const nowIso = options.nowIso ?? (() => new Date().toISOString());
27
+ const intervalMs = options.intervalMs ?? 30000;
28
+ const errors = [];
29
+ const inFlight = new Set();
30
+ const publish = async () => {
31
+ await publishWorkerHeartbeat(options.publisher, newWorkerHeartbeat({
32
+ service: options.service,
33
+ jobType: options.jobType,
34
+ instanceId: options.instanceId,
35
+ ...(options.concurrency !== undefined
36
+ ? { concurrency: options.concurrency }
37
+ : {}),
38
+ ...(options.version !== undefined ? { version: options.version } : {}),
39
+ timestamp: nowIso(),
40
+ }), options.subjectService);
41
+ };
42
+ const publishSafely = () => {
43
+ const task = publish()
44
+ .catch((error) => {
45
+ errors.push(error);
46
+ })
47
+ .finally(() => {
48
+ inFlight.delete(task);
49
+ });
50
+ inFlight.add(task);
51
+ };
52
+ await publish();
53
+ const timer = setInterval(() => {
54
+ publishSafely();
55
+ }, intervalMs);
56
+ return {
57
+ async stop() {
58
+ clearInterval(timer);
59
+ await Promise.all(inFlight);
60
+ if (errors.length > 0) {
61
+ throw new WorkerHeartbeatLoopError(errors);
62
+ }
63
+ },
64
+ };
65
+ }