@hatchet-dev/typescript-sdk 1.38.0 → 1.39.0

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.
@@ -1,4 +1,4 @@
1
- import { APIErrors, APIMeta, AcceptInviteRequest, BulkCreateEventRequest, CancelEventRequest, CreateAPITokenRequest, CreateAPITokenResponse, CreateCronWorkflowTriggerRequest, CreateEventRequest, CreateSNSIntegrationRequest, CreateTenantAlertEmailGroupRequest, CreateTenantInviteRequest, CreateTenantRequest, CronWorkflows, CronWorkflowsList, CronWorkflowsOrderByField, Event, EventData, EventKey, EventKeyList, EventList, EventOrderByDirection, EventOrderByField, EventSearch, Events, FeatureFlagEvaluationResult, FeatureFlagId, ListAPIMetaIntegration, ListAPITokensResponse, ListSNSIntegrations, ListSlackWebhooks, OtelSpanList, RateLimitList, RateLimitOrderByDirection, RateLimitOrderByField, RejectInviteRequest, ReplayEventRequest, ReplayWorkflowRunsRequest, ReplayWorkflowRunsResponse, RerunStepRunRequest, SNSIntegration, ScheduleWorkflowRunRequest, ScheduledRunStatus, ScheduledWorkflows, ScheduledWorkflowsBulkDeleteRequest, ScheduledWorkflowsBulkDeleteResponse, ScheduledWorkflowsBulkUpdateRequest, ScheduledWorkflowsBulkUpdateResponse, ScheduledWorkflowsList, ScheduledWorkflowsOrderByField, StepRun, StepRunArchiveList, StepRunEventList, TaskStats, Tenant, TenantAlertEmailGroup, TenantAlertEmailGroupList, TenantAlertingSettings, TenantInvite, TenantInviteList, TenantMember, TenantMemberList, TenantQueueMetrics, TenantResourcePolicy, TenantStepRunQueueMetrics, TriggerRunResult, TriggerWorkflowRunRequest, UpdateCronWorkflowTriggerRequest, UpdateScheduledWorkflowRunRequest, UpdateTenantAlertEmailGroupRequest, UpdateTenantInviteRequest, UpdateTenantMemberRequest, UpdateTenantRequest, UpdateWorkerRequest, User, UserChangePasswordRequest, UserLoginRequest, UserRegisterRequest, UserTenantMembershipsList, V1AdditionalMetadataOperator, V1BranchDurableTaskRequest, V1BranchDurableTaskResponse, V1CELDebugRequest, V1CELDebugResponse, V1CancelTaskRequest, V1CancelledTasks, V1CreateFilterRequest, V1CreateWebhookRequest, V1DagChildren, V1DurableEventLogList, V1Event, V1EventList, V1Filter, V1FilterList, V1LogLineLevel, V1LogLineList, V1LogLineOrderByDirection, V1LogsPointMetrics, V1ReplayTaskRequest, V1ReplayedTasks, V1RestoreTaskResponse, V1RunningFilter, V1StreamPayloadRef, V1TaskEventList, V1TaskPointMetrics, V1TaskRunMetrics, V1TaskStatus, V1TaskSummary, V1TaskSummaryList, V1TaskTimingList, V1TriggerWorkflowRunRequest, V1UpdateFilterRequest, V1UpdateWebhookRequest, V1Webhook, V1WebhookList, V1WebhookResponse, V1WebhookSourceName, V1WorkflowRunCount, V1WorkflowRunDetails, V1WorkflowRunDisplayNameList, V1WorkflowRunExternalIdList, WebhookWorkerCreateRequest, WebhookWorkerCreated, WebhookWorkerListResponse, WebhookWorkerRequestListResponse, Worker, WorkerList, WorkerStatus, Workflow, WorkflowID, WorkflowKindList, WorkflowList, WorkflowMetrics, WorkflowRun, WorkflowRunList, WorkflowRunOrderByDirection, WorkflowRunOrderByField, WorkflowRunShape, WorkflowRunStatus, WorkflowRunStatusList, WorkflowRunsCancelRequest, WorkflowRunsMetrics, WorkflowUpdateRequest, WorkflowVersion, WorkflowWorkersCount } from './data-contracts';
1
+ import { APIErrors, APIMeta, AcceptInviteRequest, BulkCreateEventRequest, CancelEventRequest, CreateAPITokenRequest, CreateAPITokenResponse, CreateCronWorkflowTriggerRequest, CreateEventRequest, CreateSNSIntegrationRequest, CreateTenantAlertEmailGroupRequest, CreateTenantInviteRequest, CreateTenantRequest, CronWorkflows, CronWorkflowsList, CronWorkflowsOrderByField, Event, EventData, EventKey, EventKeyList, EventList, EventOrderByDirection, EventOrderByField, EventSearch, Events, FeatureFlagEvaluationResult, FeatureFlagId, ListAPIMetaIntegration, ListAPITokensResponse, ListSNSIntegrations, ListSlackWebhooks, OtelSpanList, RateLimitList, RateLimitOrderByDirection, RateLimitOrderByField, RejectInviteRequest, ReplayEventRequest, ReplayWorkflowRunsRequest, ReplayWorkflowRunsResponse, RerunStepRunRequest, SNSIntegration, ScheduleWorkflowRunRequest, ScheduledRunStatus, ScheduledWorkflows, ScheduledWorkflowsBulkDeleteRequest, ScheduledWorkflowsBulkDeleteResponse, ScheduledWorkflowsBulkUpdateRequest, ScheduledWorkflowsBulkUpdateResponse, ScheduledWorkflowsList, ScheduledWorkflowsOrderByField, StepRun, StepRunArchiveList, StepRunEventList, TaskStats, Tenant, TenantAlertEmailGroup, TenantAlertEmailGroupList, TenantAlertingSettings, TenantInvite, TenantInviteList, TenantMember, TenantMemberList, TenantQueueMetrics, TenantResourcePolicy, TenantStepRunQueueMetrics, TriggerRunResult, TriggerWorkflowRunRequest, UpdateCronWorkflowTriggerRequest, UpdateScheduledWorkflowRunRequest, UpdateTenantAlertEmailGroupRequest, UpdateTenantInviteRequest, UpdateTenantMemberRequest, UpdateTenantRequest, UpdateWorkerRequest, User, UserChangePasswordRequest, UserLoginRequest, UserRegisterRequest, UserTenantMembershipsList, V1AdditionalMetadataOperator, V1BranchDurableTaskRequest, V1BranchDurableTaskResponse, V1CELDebugRequest, V1CELDebugResponse, V1CancelTaskRequest, V1CancelledTasks, V1CreateFilterRequest, V1CreateServerlessEndpointRequest, V1CreateWebhookRequest, V1DagChildren, V1DurableEventLogList, V1Event, V1EventList, V1Filter, V1FilterList, V1LogLineLevel, V1LogLineList, V1LogLineOrderByDirection, V1LogsPointMetrics, V1ReplayTaskRequest, V1ReplayedTasks, V1RestoreTaskResponse, V1RunningFilter, V1ServerlessEndpoint, V1ServerlessEndpointList, V1StreamPayloadRef, V1TaskEventList, V1TaskPointMetrics, V1TaskRunMetrics, V1TaskStatus, V1TaskSummary, V1TaskSummaryList, V1TaskTimingList, V1TriggerWorkflowRunRequest, V1UpdateFilterRequest, V1UpdateServerlessEndpointRequest, V1UpdateWebhookRequest, V1Webhook, V1WebhookList, V1WebhookResponse, V1WebhookSourceName, V1WorkflowRunCount, V1WorkflowRunDetails, V1WorkflowRunDisplayNameList, V1WorkflowRunExternalIdList, WebhookWorkerCreateRequest, WebhookWorkerCreated, WebhookWorkerListResponse, WebhookWorkerRequestListResponse, Worker, WorkerList, WorkerStatus, Workflow, WorkflowID, WorkflowKindList, WorkflowList, WorkflowMetrics, WorkflowRun, WorkflowRunList, WorkflowRunOrderByDirection, WorkflowRunOrderByField, WorkflowRunShape, WorkflowRunStatus, WorkflowRunStatusList, WorkflowRunsCancelRequest, WorkflowRunsMetrics, WorkflowUpdateRequest, WorkflowVersion, WorkflowWorkersCount } from './data-contracts';
2
2
  import { HttpClient, RequestParams } from './http-client';
3
3
  export declare class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
4
4
  /**
@@ -777,6 +777,67 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
777
777
  * @secure
778
778
  */
779
779
  v1WebhookUpdate: (tenant: string, v1Webhook: string, data: V1UpdateWebhookRequest, params?: RequestParams) => Promise<import("axios").AxiosResponse<V1Webhook, any, {}>>;
780
+ /**
781
+ * @description Lists the serverless endpoints of a tenant.
782
+ *
783
+ * @tags Serverless
784
+ * @name V1ServerlessEndpointList
785
+ * @summary List serverless endpoints
786
+ * @request GET:/api/v1/stable/tenants/{tenant}/serverless/endpoints
787
+ * @secure
788
+ */
789
+ v1ServerlessEndpointList: (tenant: string, query?: {
790
+ /**
791
+ * The number to skip
792
+ * @format int64
793
+ */
794
+ offset?: number;
795
+ /**
796
+ * The number to limit by
797
+ * @format int64
798
+ */
799
+ limit?: number;
800
+ }, params?: RequestParams) => Promise<import("axios").AxiosResponse<V1ServerlessEndpointList, any, {}>>;
801
+ /**
802
+ * @description Create a serverless endpoint
803
+ *
804
+ * @tags Serverless
805
+ * @name V1ServerlessEndpointCreate
806
+ * @summary Create a serverless endpoint
807
+ * @request POST:/api/v1/stable/tenants/{tenant}/serverless/endpoints
808
+ * @secure
809
+ */
810
+ v1ServerlessEndpointCreate: (tenant: string, data: V1CreateServerlessEndpointRequest, params?: RequestParams) => Promise<import("axios").AxiosResponse<V1ServerlessEndpoint, any, {}>>;
811
+ /**
812
+ * @description Get a serverless endpoint by its id
813
+ *
814
+ * @tags Serverless
815
+ * @name V1ServerlessEndpointGet
816
+ * @summary Get a serverless endpoint
817
+ * @request GET:/api/v1/stable/serverless/endpoints/{v1-serverless-endpoint}
818
+ * @secure
819
+ */
820
+ v1ServerlessEndpointGet: (v1ServerlessEndpoint: string, params?: RequestParams) => Promise<import("axios").AxiosResponse<V1ServerlessEndpoint, any, {}>>;
821
+ /**
822
+ * @description Update a serverless endpoint
823
+ *
824
+ * @tags Serverless
825
+ * @name V1ServerlessEndpointUpdate
826
+ * @summary Update a serverless endpoint
827
+ * @request PATCH:/api/v1/stable/serverless/endpoints/{v1-serverless-endpoint}
828
+ * @secure
829
+ */
830
+ v1ServerlessEndpointUpdate: (v1ServerlessEndpoint: string, data: V1UpdateServerlessEndpointRequest, params?: RequestParams) => Promise<import("axios").AxiosResponse<V1ServerlessEndpoint, any, {}>>;
831
+ /**
832
+ * @description Delete a serverless endpoint
833
+ *
834
+ * @tags Serverless
835
+ * @name V1ServerlessEndpointDelete
836
+ * @summary Delete a serverless endpoint
837
+ * @request DELETE:/api/v1/stable/serverless/endpoints/{v1-serverless-endpoint}
838
+ * @secure
839
+ */
840
+ v1ServerlessEndpointDelete: (v1ServerlessEndpoint: string, params?: RequestParams) => Promise<import("axios").AxiosResponse<V1ServerlessEndpoint, any, {}>>;
780
841
  /**
781
842
  * @description Evaluate a CEL expression against provided input data.
782
843
  *
@@ -405,6 +405,56 @@ class Api extends http_client_1.HttpClient {
405
405
  * @secure
406
406
  */
407
407
  this.v1WebhookUpdate = (tenant, v1Webhook, data, params = {}) => this.request(Object.assign({ path: `/api/v1/stable/tenants/${tenant}/webhooks/${v1Webhook}`, method: 'PATCH', body: data, secure: true, type: http_client_1.ContentType.Json, format: 'json' }, params));
408
+ /**
409
+ * @description Lists the serverless endpoints of a tenant.
410
+ *
411
+ * @tags Serverless
412
+ * @name V1ServerlessEndpointList
413
+ * @summary List serverless endpoints
414
+ * @request GET:/api/v1/stable/tenants/{tenant}/serverless/endpoints
415
+ * @secure
416
+ */
417
+ this.v1ServerlessEndpointList = (tenant, query, params = {}) => this.request(Object.assign({ path: `/api/v1/stable/tenants/${tenant}/serverless/endpoints`, method: 'GET', query: query, secure: true, format: 'json' }, params));
418
+ /**
419
+ * @description Create a serverless endpoint
420
+ *
421
+ * @tags Serverless
422
+ * @name V1ServerlessEndpointCreate
423
+ * @summary Create a serverless endpoint
424
+ * @request POST:/api/v1/stable/tenants/{tenant}/serverless/endpoints
425
+ * @secure
426
+ */
427
+ this.v1ServerlessEndpointCreate = (tenant, data, params = {}) => this.request(Object.assign({ path: `/api/v1/stable/tenants/${tenant}/serverless/endpoints`, method: 'POST', body: data, secure: true, type: http_client_1.ContentType.Json, format: 'json' }, params));
428
+ /**
429
+ * @description Get a serverless endpoint by its id
430
+ *
431
+ * @tags Serverless
432
+ * @name V1ServerlessEndpointGet
433
+ * @summary Get a serverless endpoint
434
+ * @request GET:/api/v1/stable/serverless/endpoints/{v1-serverless-endpoint}
435
+ * @secure
436
+ */
437
+ this.v1ServerlessEndpointGet = (v1ServerlessEndpoint, params = {}) => this.request(Object.assign({ path: `/api/v1/stable/serverless/endpoints/${v1ServerlessEndpoint}`, method: 'GET', secure: true, format: 'json' }, params));
438
+ /**
439
+ * @description Update a serverless endpoint
440
+ *
441
+ * @tags Serverless
442
+ * @name V1ServerlessEndpointUpdate
443
+ * @summary Update a serverless endpoint
444
+ * @request PATCH:/api/v1/stable/serverless/endpoints/{v1-serverless-endpoint}
445
+ * @secure
446
+ */
447
+ this.v1ServerlessEndpointUpdate = (v1ServerlessEndpoint, data, params = {}) => this.request(Object.assign({ path: `/api/v1/stable/serverless/endpoints/${v1ServerlessEndpoint}`, method: 'PATCH', body: data, secure: true, type: http_client_1.ContentType.Json, format: 'json' }, params));
448
+ /**
449
+ * @description Delete a serverless endpoint
450
+ *
451
+ * @tags Serverless
452
+ * @name V1ServerlessEndpointDelete
453
+ * @summary Delete a serverless endpoint
454
+ * @request DELETE:/api/v1/stable/serverless/endpoints/{v1-serverless-endpoint}
455
+ * @secure
456
+ */
457
+ this.v1ServerlessEndpointDelete = (v1ServerlessEndpoint, params = {}) => this.request(Object.assign({ path: `/api/v1/stable/serverless/endpoints/${v1ServerlessEndpoint}`, method: 'DELETE', secure: true, format: 'json' }, params));
408
458
  /**
409
459
  * @description Evaluate a CEL expression against provided input data.
410
460
  *
@@ -191,6 +191,11 @@ export declare enum V1CELDebugResponseStatus {
191
191
  SUCCESS = "SUCCESS",
192
192
  ERROR = "ERROR"
193
193
  }
194
+ /** The kind of serverless endpoint. GENERIC_HTTP is any HTTPS endpoint that implements the serverless endpoint contract; CLOUDFLARE_WORKERS applies the Cloudflare Workers runtime constraints on top of it. */
195
+ export declare enum V1ServerlessEndpointKind {
196
+ GENERIC_HTTP = "GENERIC_HTTP",
197
+ CLOUDFLARE_WORKERS = "CLOUDFLARE_WORKERS"
198
+ }
194
199
  export declare enum V1WebhookHMACEncoding {
195
200
  HEX = "HEX",
196
201
  BASE64 = "BASE64",
@@ -1129,6 +1134,130 @@ export interface V1UpdateWebhookRequest {
1129
1134
  /** Whether to return the triggered event as the response payload when this webhook is triggered */
1130
1135
  returnEventAsResponsePayload?: boolean;
1131
1136
  }
1137
+ /** The health of the endpoint as last observed by the serverless operator. Written on state transitions only, so changedAt is the time the endpoint last flipped between healthy and unhealthy. */
1138
+ export interface V1ServerlessEndpointStatus {
1139
+ /** Whether the last healthcheck succeeded. Absent until the operator has polled the endpoint at least once. */
1140
+ healthy?: boolean;
1141
+ /** The error from the last failed healthcheck, if the endpoint is unhealthy. */
1142
+ error?: string;
1143
+ /**
1144
+ * When the healthy flag last changed.
1145
+ * @format date-time
1146
+ */
1147
+ changedAt?: string;
1148
+ /** The action ids the operator registered for this endpoint from its last healthcheck, as the endpoint declared them. Several endpoints of a tenant may register the same action; each then serves it. */
1149
+ registeredActions: string[];
1150
+ }
1151
+ export interface V1ServerlessEndpoint {
1152
+ metadata: APIResourceMeta;
1153
+ /**
1154
+ * The ID of the tenant that owns this endpoint.
1155
+ * @format uuid
1156
+ */
1157
+ tenantId: string;
1158
+ /** The name of the endpoint. Unique within the tenant. */
1159
+ name: string;
1160
+ /** The kind of serverless endpoint. GENERIC_HTTP is any HTTPS endpoint that implements the serverless endpoint contract; CLOUDFLARE_WORKERS applies the Cloudflare Workers runtime constraints on top of it. */
1161
+ kind: V1ServerlessEndpointKind;
1162
+ /** The HTTPS URL (port 443) polled periodically to discover the workflows this endpoint serves. */
1163
+ healthcheckUrl: string;
1164
+ /** The HTTPS URL (port 443) that assigned tasks are delivered to. */
1165
+ triggerUrl: string;
1166
+ /**
1167
+ * The per-request timeout backstop for trigger requests, in seconds.
1168
+ * @format int32
1169
+ */
1170
+ requestTimeoutSeconds: number;
1171
+ /**
1172
+ * How often the healthcheck URL is polled, in seconds.
1173
+ * @format int32
1174
+ */
1175
+ pollIntervalSeconds: number;
1176
+ /**
1177
+ * How long a trigger request may block waiting for a durable result before the operator falls back to the websocket durable protocol, in milliseconds.
1178
+ * @format int32
1179
+ */
1180
+ inlineWaitBudgetMs: number;
1181
+ /** Worker labels applied to the connection-backed worker the operator creates for this endpoint. */
1182
+ labels: Record<string, any>;
1183
+ /** Whether the operator polls and dispatches to this endpoint. */
1184
+ enabled: boolean;
1185
+ /** The health of the endpoint as last observed by the serverless operator. Written on state transitions only, so changedAt is the time the endpoint last flipped between healthy and unhealthy. */
1186
+ status: V1ServerlessEndpointStatus;
1187
+ }
1188
+ export interface V1ServerlessEndpointList {
1189
+ pagination?: PaginationResponse;
1190
+ rows?: V1ServerlessEndpoint[];
1191
+ }
1192
+ export interface V1CreateServerlessEndpointRequest {
1193
+ /** The name of the endpoint. Unique within the tenant. */
1194
+ name: string;
1195
+ /** The kind of serverless endpoint. GENERIC_HTTP is any HTTPS endpoint that implements the serverless endpoint contract; CLOUDFLARE_WORKERS applies the Cloudflare Workers runtime constraints on top of it. */
1196
+ kind?: V1ServerlessEndpointKind;
1197
+ /** The HTTPS URL (port 443) polled periodically to discover the workflows this endpoint serves. */
1198
+ healthcheckUrl: string;
1199
+ /** The HTTPS URL (port 443) that assigned tasks are delivered to. */
1200
+ triggerUrl: string;
1201
+ /**
1202
+ * The secret used to HMAC-sign requests delivered to the endpoint. At least 32 characters. Write-only: it is stored encrypted and never returned in responses.
1203
+ * @minLength 32
1204
+ */
1205
+ signingSecret: string;
1206
+ /**
1207
+ * The per-request timeout backstop for trigger requests, in seconds. Defaults to 60.
1208
+ * @format int32
1209
+ */
1210
+ requestTimeoutSeconds?: number;
1211
+ /**
1212
+ * How often the healthcheck URL is polled, in seconds. Defaults to 30.
1213
+ * @format int32
1214
+ */
1215
+ pollIntervalSeconds?: number;
1216
+ /**
1217
+ * The inline wait budget for durable results, in milliseconds. Defaults to 5000.
1218
+ * @format int32
1219
+ */
1220
+ inlineWaitBudgetMs?: number;
1221
+ /** Worker labels applied to the worker the operator creates for this endpoint. */
1222
+ labels?: Record<string, any>;
1223
+ /** Whether the operator polls and dispatches to this endpoint. Defaults to true. */
1224
+ enabled?: boolean;
1225
+ }
1226
+ /** Fields to update on a serverless endpoint. Omitted fields are left unchanged. */
1227
+ export interface V1UpdateServerlessEndpointRequest {
1228
+ /** The name of the endpoint. Unique within the tenant. */
1229
+ name?: string;
1230
+ /** The kind of serverless endpoint. GENERIC_HTTP is any HTTPS endpoint that implements the serverless endpoint contract; CLOUDFLARE_WORKERS applies the Cloudflare Workers runtime constraints on top of it. */
1231
+ kind?: V1ServerlessEndpointKind;
1232
+ /** The HTTPS URL (port 443) polled periodically to discover the workflows this endpoint serves. */
1233
+ healthcheckUrl?: string;
1234
+ /** The HTTPS URL (port 443) that assigned tasks are delivered to. */
1235
+ triggerUrl?: string;
1236
+ /**
1237
+ * A new secret used to HMAC-sign requests delivered to the endpoint. At least 32 characters. Provide a value to rotate the secret; it is never returned in responses.
1238
+ * @minLength 32
1239
+ */
1240
+ signingSecret?: string;
1241
+ /**
1242
+ * The per-request timeout backstop for trigger requests, in seconds.
1243
+ * @format int32
1244
+ */
1245
+ requestTimeoutSeconds?: number;
1246
+ /**
1247
+ * How often the healthcheck URL is polled, in seconds.
1248
+ * @format int32
1249
+ */
1250
+ pollIntervalSeconds?: number;
1251
+ /**
1252
+ * The inline wait budget for durable results, in milliseconds.
1253
+ * @format int32
1254
+ */
1255
+ inlineWaitBudgetMs?: number;
1256
+ /** Worker labels applied to the worker the operator creates for this endpoint. Replaces the existing labels. */
1257
+ labels?: Record<string, any>;
1258
+ /** Whether the operator polls and dispatches to this endpoint. */
1259
+ enabled?: boolean;
1260
+ }
1132
1261
  export interface V1CELDebugRequest {
1133
1262
  /** The CEL expression to evaluate */
1134
1263
  expression: string;
@@ -1661,6 +1790,8 @@ export interface Workflow {
1661
1790
  jobs?: Job[];
1662
1791
  /** The tenant id of the workflow. */
1663
1792
  tenantId: string;
1793
+ /** The event and cron triggers declared on the latest workflow version. */
1794
+ triggers?: WorkflowTriggers;
1664
1795
  }
1665
1796
  export interface WorkflowVersionMeta {
1666
1797
  metadata: APIResourceMeta;
@@ -1704,6 +1835,21 @@ export interface Job {
1704
1835
  /** The timeout of the job. */
1705
1836
  timeout?: string;
1706
1837
  }
1838
+ export interface WorkflowTriggerEventRef {
1839
+ parent_id?: string;
1840
+ event_key?: string;
1841
+ }
1842
+ export interface WorkflowTriggerCronRef {
1843
+ parent_id?: string;
1844
+ cron?: string;
1845
+ }
1846
+ export interface WorkflowTriggers {
1847
+ metadata?: APIResourceMeta;
1848
+ workflow_version_id?: string;
1849
+ tenant_id?: string;
1850
+ events?: WorkflowTriggerEventRef[];
1851
+ crons?: WorkflowTriggerCronRef[];
1852
+ }
1707
1853
  export interface WorkflowList {
1708
1854
  metadata?: APIResourceMeta;
1709
1855
  rows?: Workflow[];
@@ -1904,21 +2050,6 @@ export interface WorkflowConcurrency {
1904
2050
  /** An action which gets the concurrency group for the WorkflowRun. */
1905
2051
  getConcurrencyGroup: string;
1906
2052
  }
1907
- export interface WorkflowTriggerEventRef {
1908
- parent_id?: string;
1909
- event_key?: string;
1910
- }
1911
- export interface WorkflowTriggerCronRef {
1912
- parent_id?: string;
1913
- cron?: string;
1914
- }
1915
- export interface WorkflowTriggers {
1916
- metadata?: APIResourceMeta;
1917
- workflow_version_id?: string;
1918
- tenant_id?: string;
1919
- events?: WorkflowTriggerEventRef[];
1920
- crons?: WorkflowTriggerCronRef[];
1921
- }
1922
2053
  export interface ConcurrencySetting {
1923
2054
  /**
1924
2055
  * The maximum number of concurrent workflow runs.
@@ -11,7 +11,8 @@
11
11
  * ---------------------------------------------------------------
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.V1TaskStatus = exports.V1WorkflowType = exports.V1TaskEventType = exports.V1LogLineLevel = exports.V1LogLineOrderByDirection = exports.V1AdditionalMetadataOperator = exports.V1RunningFilter = exports.V1DurableEventLogKind = exports.V1DurableWaitConditionKind = exports.OtelSpanKind = exports.OtelStatusCode = exports.TenantVersion = exports.TenantEnvironment = exports.V1WebhookSourceName = exports.V1WebhookAuthType = exports.V1WebhookHMACAlgorithm = exports.V1WebhookHMACEncoding = exports.V1CELDebugResponseStatus = exports.TenantResource = exports.TenantMemberRole = exports.WorkflowRunStatus = exports.EventOrderByField = exports.EventOrderByDirection = exports.RateLimitOrderByField = exports.RateLimitOrderByDirection = exports.WorkflowPauseScheduledCronRunQueueBehavior = exports.ScheduledWorkflowsMethod = exports.ScheduledWorkflowsOrderByField = exports.WorkflowRunOrderByDirection = exports.ScheduledRunStatus = exports.CronWorkflowsMethod = exports.CronWorkflowsOrderByField = exports.ConcurrencyLimitStrategy = exports.ConcurrencyScope = exports.StepRunStatus = exports.JobRunStatus = exports.StepRunEventReason = exports.StepRunEventSeverity = exports.WorkflowKind = exports.WorkflowRunOrderByField = exports.WorkerStatus = exports.WorkerType = exports.WorkerRuntimeSDKs = exports.WebhookWorkerRequestMethod = exports.FeatureFlagId = exports.PullRequestState = exports.LogLineLevel = exports.LogLineOrderByField = exports.LogLineOrderByDirection = exports.V1TaskRunStatus = void 0;
14
+ exports.V1WorkflowType = exports.V1TaskEventType = exports.V1LogLineLevel = exports.V1LogLineOrderByDirection = exports.V1AdditionalMetadataOperator = exports.V1RunningFilter = exports.V1DurableEventLogKind = exports.V1DurableWaitConditionKind = exports.OtelSpanKind = exports.OtelStatusCode = exports.TenantVersion = exports.TenantEnvironment = exports.V1WebhookSourceName = exports.V1WebhookAuthType = exports.V1WebhookHMACAlgorithm = exports.V1WebhookHMACEncoding = exports.V1ServerlessEndpointKind = exports.V1CELDebugResponseStatus = exports.TenantResource = exports.TenantMemberRole = exports.WorkflowRunStatus = exports.EventOrderByField = exports.EventOrderByDirection = exports.RateLimitOrderByField = exports.RateLimitOrderByDirection = exports.WorkflowPauseScheduledCronRunQueueBehavior = exports.ScheduledWorkflowsMethod = exports.ScheduledWorkflowsOrderByField = exports.WorkflowRunOrderByDirection = exports.ScheduledRunStatus = exports.CronWorkflowsMethod = exports.CronWorkflowsOrderByField = exports.ConcurrencyLimitStrategy = exports.ConcurrencyScope = exports.StepRunStatus = exports.JobRunStatus = exports.StepRunEventReason = exports.StepRunEventSeverity = exports.WorkflowKind = exports.WorkflowRunOrderByField = exports.WorkerStatus = exports.WorkerType = exports.WorkerRuntimeSDKs = exports.WebhookWorkerRequestMethod = exports.FeatureFlagId = exports.PullRequestState = exports.LogLineLevel = exports.LogLineOrderByField = exports.LogLineOrderByDirection = exports.V1TaskRunStatus = void 0;
15
+ exports.V1TaskStatus = void 0;
15
16
  var V1TaskRunStatus;
16
17
  (function (V1TaskRunStatus) {
17
18
  V1TaskRunStatus["PENDING"] = "PENDING";
@@ -238,6 +239,12 @@ var V1CELDebugResponseStatus;
238
239
  V1CELDebugResponseStatus["SUCCESS"] = "SUCCESS";
239
240
  V1CELDebugResponseStatus["ERROR"] = "ERROR";
240
241
  })(V1CELDebugResponseStatus || (exports.V1CELDebugResponseStatus = V1CELDebugResponseStatus = {}));
242
+ /** The kind of serverless endpoint. GENERIC_HTTP is any HTTPS endpoint that implements the serverless endpoint contract; CLOUDFLARE_WORKERS applies the Cloudflare Workers runtime constraints on top of it. */
243
+ var V1ServerlessEndpointKind;
244
+ (function (V1ServerlessEndpointKind) {
245
+ V1ServerlessEndpointKind["GENERIC_HTTP"] = "GENERIC_HTTP";
246
+ V1ServerlessEndpointKind["CLOUDFLARE_WORKERS"] = "CLOUDFLARE_WORKERS";
247
+ })(V1ServerlessEndpointKind || (exports.V1ServerlessEndpointKind = V1ServerlessEndpointKind = {}));
241
248
  var V1WebhookHMACEncoding;
242
249
  (function (V1WebhookHMACEncoding) {
243
250
  V1WebhookHMACEncoding["HEX"] = "HEX";
@@ -0,0 +1,8 @@
1
+ import { V1StreamsClient } from '../../protoc/v1/streams';
2
+ import type { Transport } from '../transport/transport';
3
+ /**
4
+ * The v1 `V1Streams` client, every RPC of the generated `V1StreamsClient` interface, served
5
+ * by a Connect client on the given transport. `Subscribe` is a server stream, which the unary
6
+ * transport does not serve: calling it throws.
7
+ */
8
+ export declare function createV1StreamsRpc(transport: Transport): V1StreamsClient;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createV1StreamsRpc = createV1StreamsRpc;
4
+ const streams_1 = require("../../protoc/v1/streams");
5
+ const streams_pb_1 = require("../../protoc-es/v1/streams_pb");
6
+ const ts_proto_client_1 = require("../transport/ts-proto-client");
7
+ /**
8
+ * The v1 `V1Streams` client, every RPC of the generated `V1StreamsClient` interface, served
9
+ * by a Connect client on the given transport. `Subscribe` is a server stream, which the unary
10
+ * transport does not serve: calling it throws.
11
+ */
12
+ function createV1StreamsRpc(transport) {
13
+ return (0, ts_proto_client_1.createTsProtoClient)(streams_1.V1StreamsDefinition, streams_pb_1.V1Streams, transport);
14
+ }
@@ -82,7 +82,24 @@ function createFetchTransport(options) {
82
82
  baseUrl: resolveServerUrl(options),
83
83
  useBinaryFormat: true,
84
84
  interceptors: [(0, transport_1.createAuthInterceptor)(options.token)],
85
- fetch: limitMessageSizes(send, limits),
85
+ fetch: limitMessageSizes(withoutRedirects(send), limits),
86
+ });
87
+ }
88
+ /**
89
+ * Wraps a `fetch` so a redirect fails the call instead of being followed. connect-web asks
90
+ * for this with `redirect: 'error'`, which Cloudflare Workers reject outright ("Invalid
91
+ * redirect value"), so the request goes out with `redirect: 'manual'` and a redirect
92
+ * response (a 3xx status, or the opaque redirect a browser hands back) is turned into the
93
+ * error connect-web would have raised. The engine never redirects; one means `serverUrl`
94
+ * points at something in front of it.
95
+ */
96
+ function withoutRedirects(send) {
97
+ return (input, init) => __awaiter(this, void 0, void 0, function* () {
98
+ const response = yield send(input, Object.assign(Object.assign({}, init), { redirect: 'manual' }));
99
+ if (response.type === 'opaqueredirect' || (response.status >= 300 && response.status < 400)) {
100
+ throw new connect_1.ConnectError(`the server answered with a redirect (${response.status || 'opaque'}); serverUrl must point at the engine itself`, connect_1.Code.Unavailable);
101
+ }
102
+ return response;
86
103
  });
87
104
  }
88
105
  /**
package/core/client.d.ts CHANGED
@@ -55,11 +55,12 @@ export declare class HatchetCore {
55
55
  readonly rateLimits: RateLimitsClient;
56
56
  /** Writes task run log lines. */
57
57
  readonly logs: LogsClient;
58
- /** Publishes task run stream chunks. */
58
+ /** Publishes task run stream chunks and durable stream topics. */
59
59
  readonly streams: StreamsClient;
60
60
  private readonly workflowsRpc;
61
61
  private readonly adminRpc;
62
62
  private readonly eventsRpc;
63
+ private readonly streamsRpc;
63
64
  constructor(config: CoreClientConfig);
64
65
  /** The same as `new HatchetCore(config)`, mirroring the Node client's `init`. */
65
66
  static init(config: CoreClientConfig): HatchetCore;
package/core/client.js CHANGED
@@ -20,6 +20,7 @@ const fetch_transport_1 = require("../clients/transport/fetch-transport");
20
20
  const rpc_1 = require("../clients/admin/rpc");
21
21
  const trigger_request_1 = require("../clients/admin/trigger-request");
22
22
  const rpc_2 = require("../clients/event/rpc");
23
+ const rpc_3 = require("../clients/streams/rpc");
23
24
  const workflows_1 = require("../protoc/workflows");
24
25
  const token_1 = require("../util/config-loader/token");
25
26
  const batch_1 = require("../util/batch");
@@ -81,13 +82,14 @@ class HatchetCore {
81
82
  this.workflowsRpc = (0, rpc_1.createWorkflowsRpc)(this.transport);
82
83
  this.adminRpc = (0, rpc_1.createV1AdminRpc)(this.transport);
83
84
  this.eventsRpc = (0, rpc_2.createEventsRpc)(this.transport);
85
+ this.streamsRpc = (0, rpc_3.createV1StreamsRpc)(this.transport);
84
86
  const shared = { logger: this.logger, namespace, retrier: config.retrier };
85
87
  this.events = new events_1.EventsClient(Object.assign({ rpc: this.eventsRpc }, shared));
86
88
  this.runs = new runs_1.RunsClient(this.adminRpc);
87
89
  this.workflows = new workflows_2.WorkflowsClient(Object.assign({ rpc: this.adminRpc }, shared));
88
90
  this.rateLimits = new rate_limits_1.RateLimitsClient(Object.assign({ rpc: this.workflowsRpc }, shared));
89
91
  this.logs = new logs_1.LogsClient(this.eventsRpc);
90
- this.streams = new streams_1.StreamsClient(this.eventsRpc);
92
+ this.streams = new streams_1.StreamsClient({ events: this.eventsRpc, streams: this.streamsRpc });
91
93
  }
92
94
  /** The same as `new HatchetCore(config)`, mirroring the Node client's `init`. */
93
95
  static init(config) {
@@ -1,11 +1,76 @@
1
1
  import type { EventsServiceClient, PutStreamEventResponse } from '../../protoc/events/events';
2
- /** Publishes stream chunks from a task run, the way `ctx.putStream` does from a worker. */
2
+ import type { V1StreamsClient } from '../../protoc/v1/streams';
3
+ import type { StreamCallOptions, StreamTopicMetadata } from '../types';
4
+ /**
5
+ * The largest payload one `publish` carries: the engine's limit for a payload sent inline
6
+ * (`MaxStreamMessagePayloadBytes`). The Node client uploads larger payloads over its REST
7
+ * client first; this client has none, so it rejects them.
8
+ */
9
+ export declare const MAX_STREAM_MESSAGE_BYTES: number;
10
+ export interface StreamsClientDeps {
11
+ events: EventsServiceClient;
12
+ streams: V1StreamsClient;
13
+ }
14
+ /**
15
+ * How many (namespace, topic) producers the client remembers. A topic past the cap starts
16
+ * over with a new producer id, which the engine orders after the old one, so correctness
17
+ * does not depend on the cap; it only bounds memory for clients that publish to many
18
+ * short-lived topics.
19
+ */
20
+ export declare const MAX_TRACKED_TOPICS = 1024;
21
+ /**
22
+ * Task run streams and durable stream topics.
23
+ *
24
+ * `put` publishes a chunk on a task run's stream, the way `ctx.putStream` does from a worker.
25
+ * `publish`, `reset` and `topicMetadata` are the Node client's durable topic calls
26
+ * (`hatchet.streams`), with the same names and options. `events()` is not here: it is a
27
+ * server stream, which the unary transport does not carry. Read a topic with the Node client
28
+ * (`hatchet.streams.events(topic)`), or from a serverless task over the operator socket once
29
+ * the operator relays the subscription.
30
+ */
3
31
  export declare class StreamsClient {
4
- private readonly rpc;
5
- constructor(rpc: EventsServiceClient);
32
+ /** Per (namespace, topic); the sequence number only advances after a publish succeeds. */
33
+ private readonly producers;
34
+ /**
35
+ * Serializes publishes per (namespace, topic) so two never share a sequence number. The
36
+ * stored promise always resolves, so one failure does not block later calls.
37
+ */
38
+ private readonly publishChains;
39
+ private readonly events;
40
+ private readonly streams;
41
+ /**
42
+ * Takes the events and durable streams services, or only the events service, which
43
+ * serves `put` and leaves the durable stream calls unavailable.
44
+ */
45
+ constructor(deps: StreamsClientDeps | EventsServiceClient);
46
+ private durable;
6
47
  /**
7
48
  * Publishes one chunk on a task run's stream. `index` orders chunks that are published
8
49
  * concurrently. Resolves once the engine has the chunk.
9
50
  */
10
51
  put(taskRunExternalId: string, data: string | Uint8Array, index?: number): Promise<PutStreamEventResponse>;
52
+ /**
53
+ * Publishes a message to a durable topic and resolves once it is stored. A client's
54
+ * messages to a topic are delivered in the order `publish` was called. A payload over
55
+ * `MAX_STREAM_MESSAGE_BYTES` rejects with a `HatchetError` before anything is sent.
56
+ * @param topic - the topic to publish to
57
+ * @param message - the message payload
58
+ * @param options - optional namespace override and abort signal
59
+ */
60
+ publish(topic: string, message: Uint8Array | string, options?: StreamCallOptions): Promise<void>;
61
+ /**
62
+ * Describes a topic's retained messages. Rejects with a `ConnectError` of code `NotFound`
63
+ * for a topic nothing was published to.
64
+ * @param topic - the topic to describe
65
+ * @param options - optional namespace override and abort signal
66
+ */
67
+ topicMetadata(topic: string, options?: Pick<StreamCallOptions, 'namespace' | 'signal'>): Promise<StreamTopicMetadata>;
68
+ /**
69
+ * Makes `events()` calls without a cursor start after the topic's latest message. Readers
70
+ * already iterating, and any call with a cursor, are unaffected.
71
+ * @param topic - the topic to reset
72
+ * @param options - optional namespace override and abort signal
73
+ */
74
+ reset(topic: string, options?: Pick<StreamCallOptions, 'namespace' | 'signal'>): Promise<void>;
75
+ private publishOrdered;
11
76
  }
@@ -1,10 +1,82 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
2
14
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StreamsClient = void 0;
4
- /** Publishes stream chunks from a task run, the way `ctx.putStream` does from a worker. */
15
+ exports.StreamsClient = exports.MAX_TRACKED_TOPICS = exports.MAX_STREAM_MESSAGE_BYTES = void 0;
16
+ const connect_1 = require("@connectrpc/connect");
17
+ const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error"));
18
+ const abort_error_1 = require("../../util/abort-error");
19
+ /**
20
+ * The largest payload one `publish` carries: the engine's limit for a payload sent inline
21
+ * (`MaxStreamMessagePayloadBytes`). The Node client uploads larger payloads over its REST
22
+ * client first; this client has none, so it rejects them.
23
+ */
24
+ exports.MAX_STREAM_MESSAGE_BYTES = 4 * 1024 * 1024 - 5 * 1024;
25
+ /**
26
+ * How many (namespace, topic) producers the client remembers. A topic past the cap starts
27
+ * over with a new producer id, which the engine orders after the old one, so correctness
28
+ * does not depend on the cap; it only bounds memory for clients that publish to many
29
+ * short-lived topics.
30
+ */
31
+ exports.MAX_TRACKED_TOPICS = 1024;
32
+ function isEventsService(deps) {
33
+ return typeof deps.putStreamEvent === 'function';
34
+ }
35
+ /** The engine stored nothing, so the producer's sequence number is still unused. */
36
+ function publishRejectedBeforeStoring(err) {
37
+ return (err instanceof connect_1.ConnectError &&
38
+ (err.code === connect_1.Code.InvalidArgument ||
39
+ err.code === connect_1.Code.ResourceExhausted ||
40
+ err.code === connect_1.Code.Unauthenticated ||
41
+ err.code === connect_1.Code.PermissionDenied));
42
+ }
43
+ /**
44
+ * Task run streams and durable stream topics.
45
+ *
46
+ * `put` publishes a chunk on a task run's stream, the way `ctx.putStream` does from a worker.
47
+ * `publish`, `reset` and `topicMetadata` are the Node client's durable topic calls
48
+ * (`hatchet.streams`), with the same names and options. `events()` is not here: it is a
49
+ * server stream, which the unary transport does not carry. Read a topic with the Node client
50
+ * (`hatchet.streams.events(topic)`), or from a serverless task over the operator socket once
51
+ * the operator relays the subscription.
52
+ */
5
53
  class StreamsClient {
6
- constructor(rpc) {
7
- this.rpc = rpc;
54
+ /**
55
+ * Takes the events and durable streams services, or only the events service, which
56
+ * serves `put` and leaves the durable stream calls unavailable.
57
+ */
58
+ constructor(deps) {
59
+ /** Per (namespace, topic); the sequence number only advances after a publish succeeds. */
60
+ this.producers = new Map();
61
+ /**
62
+ * Serializes publishes per (namespace, topic) so two never share a sequence number. The
63
+ * stored promise always resolves, so one failure does not block later calls.
64
+ */
65
+ this.publishChains = new Map();
66
+ if (isEventsService(deps)) {
67
+ this.events = deps;
68
+ this.streams = undefined;
69
+ }
70
+ else {
71
+ this.events = deps.events;
72
+ this.streams = deps.streams;
73
+ }
74
+ }
75
+ durable() {
76
+ if (!this.streams) {
77
+ throw new hatchet_error_1.default('durable streams are unavailable: this StreamsClient was built with the events service only; build it with { events, streams }');
78
+ }
79
+ return this.streams;
8
80
  }
9
81
  /**
10
82
  * Publishes one chunk on a task run's stream. `index` orders chunks that are published
@@ -12,12 +84,141 @@ class StreamsClient {
12
84
  */
13
85
  put(taskRunExternalId, data, index) {
14
86
  const message = typeof data === 'string' ? new TextEncoder().encode(data) : data;
15
- return this.rpc.putStreamEvent({
87
+ return this.events.putStreamEvent({
16
88
  taskRunExternalId,
17
89
  createdAt: new Date(),
18
90
  message,
19
91
  eventIndex: index,
20
92
  });
21
93
  }
94
+ /**
95
+ * Publishes a message to a durable topic and resolves once it is stored. A client's
96
+ * messages to a topic are delivered in the order `publish` was called. A payload over
97
+ * `MAX_STREAM_MESSAGE_BYTES` rejects with a `HatchetError` before anything is sent.
98
+ * @param topic - the topic to publish to
99
+ * @param message - the message payload
100
+ * @param options - optional namespace override and abort signal
101
+ */
102
+ publish(topic, message, options) {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ var _a, _b;
105
+ const payload = typeof message === 'string' ? new TextEncoder().encode(message) : message;
106
+ const namespace = (_a = options === null || options === void 0 ? void 0 : options.namespace) !== null && _a !== void 0 ? _a : '';
107
+ const key = `${namespace}\0${topic}`;
108
+ if (payload.byteLength > exports.MAX_STREAM_MESSAGE_BYTES) {
109
+ throw new hatchet_error_1.default(`stream payload is ${payload.byteLength} bytes, over the ${exports.MAX_STREAM_MESSAGE_BYTES}-byte limit of one publish; publish it from the Node client, which uploads larger payloads`);
110
+ }
111
+ const signal = options === null || options === void 0 ? void 0 : options.signal;
112
+ const previous = (_b = this.publishChains.get(key)) !== null && _b !== void 0 ? _b : Promise.resolve();
113
+ const publishPromise = previous.then(() => {
114
+ // An abort while this publish waited behind earlier ones sends nothing.
115
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
116
+ throw new abort_error_1.AbortError();
117
+ }
118
+ return this.publishOrdered(key, namespace, topic, payload, signal);
119
+ });
120
+ const chain = publishPromise
121
+ .catch(() => { })
122
+ .then(() => {
123
+ // The topic's queue is idle once its last publish settled.
124
+ if (this.publishChains.get(key) === chain) {
125
+ this.publishChains.delete(key);
126
+ }
127
+ });
128
+ this.publishChains.set(key, chain);
129
+ if (!signal) {
130
+ return publishPromise;
131
+ }
132
+ // The caller is released as soon as its signal aborts, while the queue keeps its order
133
+ // and the aborted publish is skipped when its turn comes.
134
+ return new Promise((resolve, reject) => {
135
+ const onAbort = () => reject(new abort_error_1.AbortError());
136
+ if (signal.aborted) {
137
+ onAbort();
138
+ return;
139
+ }
140
+ signal.addEventListener('abort', onAbort, { once: true });
141
+ publishPromise.then(resolve, reject).finally(() => {
142
+ signal.removeEventListener('abort', onAbort);
143
+ });
144
+ });
145
+ });
146
+ }
147
+ /**
148
+ * Describes a topic's retained messages. Rejects with a `ConnectError` of code `NotFound`
149
+ * for a topic nothing was published to.
150
+ * @param topic - the topic to describe
151
+ * @param options - optional namespace override and abort signal
152
+ */
153
+ topicMetadata(topic, options) {
154
+ return __awaiter(this, void 0, void 0, function* () {
155
+ var _a;
156
+ const res = yield this.durable().getTopicMetadata({ namespace: (_a = options === null || options === void 0 ? void 0 : options.namespace) !== null && _a !== void 0 ? _a : '', topic }, { signal: options === null || options === void 0 ? void 0 : options.signal });
157
+ return {
158
+ namespace: res.namespace,
159
+ topic: res.topic,
160
+ tenantId: res.tenantId,
161
+ messageCount: res.messageCount,
162
+ latestCursor: res.latestCursor,
163
+ lastPublishedAt: res.lastPublishedAt,
164
+ };
165
+ });
166
+ }
167
+ /**
168
+ * Makes `events()` calls without a cursor start after the topic's latest message. Readers
169
+ * already iterating, and any call with a cursor, are unaffected.
170
+ * @param topic - the topic to reset
171
+ * @param options - optional namespace override and abort signal
172
+ */
173
+ reset(topic, options) {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ var _a;
176
+ yield this.durable().reset({ namespace: (_a = options === null || options === void 0 ? void 0 : options.namespace) !== null && _a !== void 0 ? _a : '', topic }, { signal: options === null || options === void 0 ? void 0 : options.signal });
177
+ });
178
+ }
179
+ publishOrdered(key_1, namespace_1, topic_1, payload_1, signal_1) {
180
+ return __awaiter(this, arguments, void 0, function* (key, namespace, topic, payload, signal, retryGap = true) {
181
+ const streams = this.durable();
182
+ let producer = this.producers.get(key);
183
+ if (producer) {
184
+ // re-inserting keeps the map in last-use order for the eviction below
185
+ this.producers.delete(key);
186
+ }
187
+ else {
188
+ producer = { producerId: crypto.randomUUID(), seq: 0 };
189
+ }
190
+ this.producers.set(key, producer);
191
+ while (this.producers.size > exports.MAX_TRACKED_TOPICS) {
192
+ const oldest = this.producers.keys().next().value;
193
+ if (oldest === undefined) {
194
+ break;
195
+ }
196
+ this.producers.delete(oldest);
197
+ }
198
+ try {
199
+ yield streams.publish({
200
+ namespace,
201
+ topic,
202
+ payload,
203
+ producerId: producer.producerId,
204
+ producerSeq: producer.seq,
205
+ }, { signal });
206
+ }
207
+ catch (err) {
208
+ // The message may still land, and a different payload under its sequence number
209
+ // would be dropped as a duplicate, so the next publish comes from a new producer.
210
+ if (!publishRejectedBeforeStoring(err)) {
211
+ this.producers.set(key, { producerId: crypto.randomUUID(), seq: 0 });
212
+ }
213
+ // A gap stored nothing (the watermark passed cursor retention, say): resend as the
214
+ // new producer.
215
+ if (retryGap && err instanceof connect_1.ConnectError && err.code === connect_1.Code.FailedPrecondition) {
216
+ return this.publishOrdered(key, namespace, topic, payload, signal, false);
217
+ }
218
+ throw err;
219
+ }
220
+ producer.seq += 1;
221
+ });
222
+ }
22
223
  }
23
224
  exports.StreamsClient = StreamsClient;
package/core/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The core entry point: the application client for runtimes without gRPC. It triggers runs,
3
- * pushes events, waits for results by polling and manages workflows over unary Connect calls
4
- * on a fetch transport, and it is configured from an explicit object only.
3
+ * pushes events, waits for results by polling, publishes to durable streams and manages
4
+ * workflows over unary Connect calls on a fetch transport, and it is configured from an
5
+ * explicit object only.
5
6
  *
6
7
  * Nothing exported here imports from Node. `scripts/check-edge-entry.mjs` bundles this module
7
8
  * for a browser-like target and fails the build if a Node builtin, `axios` or a gRPC package
@@ -24,14 +25,15 @@ export { WorkflowRunRef, INITIAL_POLL_INTERVAL_MS, MAX_POLL_INTERVAL_MS } from '
24
25
  export type { RunRefClient } from './run-ref';
25
26
  export { toRunDetail, parseTaskOutput, runStatusToJSON } from './run-detail';
26
27
  export { ConsoleLogger, consoleLogger } from './logger';
27
- export type { CallOptions, CancelRunOpts, CoreClientConfig, CreateRateLimitOpts, DesiredWorkerLabelOpt, EventWithMetadata, LogConstructor, PushEventOptions, ReplayRunOpts, ResultOptions, RunDetail, RunFilter, RunFilterBase, TaskRunDetail, TriggerRun, TriggerRunOptions, } from './types';
28
+ export type { CallOptions, CancelRunOpts, CoreClientConfig, CreateRateLimitOpts, DesiredWorkerLabelOpt, EventWithMetadata, LogConstructor, PushEventOptions, ReplayRunOpts, ResultOptions, RunDetail, RunFilter, RunFilterBase, StreamCallOptions, StreamTopicMetadata, TaskRunDetail, TriggerRun, TriggerRunOptions, } from './types';
28
29
  export { EventsClient } from './features/events';
29
30
  export { RunsClient } from './features/runs';
30
31
  export { WorkflowsClient } from './features/workflows';
31
32
  export { RateLimitsClient } from './features/rate-limits';
32
33
  export { LogsClient, MAX_LOG_LINE_CHARS } from './features/logs';
33
34
  export type { PutLogOptions } from './features/logs';
34
- export { StreamsClient } from './features/streams';
35
+ export { StreamsClient, MAX_STREAM_MESSAGE_BYTES } from './features/streams';
36
+ export type { StreamsClientDeps } from './features/streams';
35
37
  export { createFetchTransport, limitMessageSizes, resolveServerUrl, type FetchTlsConfig, type FetchTransportOptions, type MessageSizeLimits, } from '../clients/transport/fetch-transport';
36
38
  export { createAuthInterceptor, DEFAULT_MAX_MESSAGE_BYTES, type Transport, } from '../clients/transport/transport';
37
39
  export { LogLevel } from '../clients/event/rpc';
@@ -40,7 +42,9 @@ export type { RunManyOpt, RunOpts } from '../v1/declaration';
40
42
  export { RateLimitDuration } from '../protoc/workflows/workflows';
41
43
  export { WorkerLabelComparator } from '../protoc/v1/shared/trigger';
42
44
  export { V1TaskStatus } from '../clients/rest/generated/data-contracts';
43
- export { default as HatchetError, getErrorMessage, toHatchetError, } from '../util/errors/hatchet-error';
45
+ import HatchetError from '../util/errors/hatchet-error';
46
+ export { HatchetError };
47
+ export { getErrorMessage, toHatchetError } from '../util/errors/hatchet-error';
44
48
  export { IdempotencyCollisionError } from '../util/errors/idempotency-collision-error';
45
49
  export { BulkTriggerIdempotencyCollisionError } from '../util/errors/bulk-trigger-idempotency-collision-error';
46
50
  export { BulkTriggerPartialError } from '../util/errors/bulk-trigger-partial-error';
package/core/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  /**
3
3
  * The core entry point: the application client for runtimes without gRPC. It triggers runs,
4
- * pushes events, waits for results by polling and manages workflows over unary Connect calls
5
- * on a fetch transport, and it is configured from an explicit object only.
4
+ * pushes events, waits for results by polling, publishes to durable streams and manages
5
+ * workflows over unary Connect calls on a fetch transport, and it is configured from an
6
+ * explicit object only.
6
7
  *
7
8
  * Nothing exported here imports from Node. `scripts/check-edge-entry.mjs` bundles this module
8
9
  * for a browser-like target and fails the build if a Node builtin, `axios` or a gRPC package
@@ -21,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
21
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
22
23
  };
23
24
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.isAbortError = exports.AbortError = exports.BulkTriggerPartialError = exports.BulkTriggerIdempotencyCollisionError = exports.IdempotencyCollisionError = exports.toHatchetError = exports.getErrorMessage = exports.HatchetError = exports.V1TaskStatus = exports.WorkerLabelComparator = exports.RateLimitDuration = exports.Priority = exports.LogLevel = exports.DEFAULT_MAX_MESSAGE_BYTES = exports.createAuthInterceptor = exports.resolveServerUrl = exports.limitMessageSizes = exports.createFetchTransport = exports.StreamsClient = exports.MAX_LOG_LINE_CHARS = exports.LogsClient = exports.RateLimitsClient = exports.WorkflowsClient = exports.RunsClient = exports.EventsClient = exports.consoleLogger = exports.ConsoleLogger = exports.runStatusToJSON = exports.parseTaskOutput = exports.toRunDetail = exports.MAX_POLL_INTERVAL_MS = exports.INITIAL_POLL_INTERVAL_MS = exports.WorkflowRunRef = exports.Hatchet = exports.HatchetCore = exports.CORE_ENTRY_VERSION = void 0;
25
+ exports.isAbortError = exports.AbortError = exports.BulkTriggerPartialError = exports.BulkTriggerIdempotencyCollisionError = exports.IdempotencyCollisionError = exports.toHatchetError = exports.getErrorMessage = exports.HatchetError = exports.V1TaskStatus = exports.WorkerLabelComparator = exports.RateLimitDuration = exports.Priority = exports.LogLevel = exports.DEFAULT_MAX_MESSAGE_BYTES = exports.createAuthInterceptor = exports.resolveServerUrl = exports.limitMessageSizes = exports.createFetchTransport = exports.MAX_STREAM_MESSAGE_BYTES = exports.StreamsClient = exports.MAX_LOG_LINE_CHARS = exports.LogsClient = exports.RateLimitsClient = exports.WorkflowsClient = exports.RunsClient = exports.EventsClient = exports.consoleLogger = exports.ConsoleLogger = exports.runStatusToJSON = exports.parseTaskOutput = exports.toRunDetail = exports.MAX_POLL_INTERVAL_MS = exports.INITIAL_POLL_INTERVAL_MS = exports.WorkflowRunRef = exports.Hatchet = exports.HatchetCore = exports.CORE_ENTRY_VERSION = void 0;
25
26
  /** Bumped whenever the surface exported from this entry changes incompatibly. */
26
27
  exports.CORE_ENTRY_VERSION = 1;
27
28
  var client_1 = require("./client");
@@ -52,6 +53,7 @@ Object.defineProperty(exports, "LogsClient", { enumerable: true, get: function (
52
53
  Object.defineProperty(exports, "MAX_LOG_LINE_CHARS", { enumerable: true, get: function () { return logs_1.MAX_LOG_LINE_CHARS; } });
53
54
  var streams_1 = require("./features/streams");
54
55
  Object.defineProperty(exports, "StreamsClient", { enumerable: true, get: function () { return streams_1.StreamsClient; } });
56
+ Object.defineProperty(exports, "MAX_STREAM_MESSAGE_BYTES", { enumerable: true, get: function () { return streams_1.MAX_STREAM_MESSAGE_BYTES; } });
55
57
  // Transport
56
58
  var fetch_transport_1 = require("../clients/transport/fetch-transport");
57
59
  Object.defineProperty(exports, "createFetchTransport", { enumerable: true, get: function () { return fetch_transport_1.createFetchTransport; } });
@@ -71,10 +73,14 @@ var trigger_1 = require("../protoc/v1/shared/trigger");
71
73
  Object.defineProperty(exports, "WorkerLabelComparator", { enumerable: true, get: function () { return trigger_1.WorkerLabelComparator; } });
72
74
  var data_contracts_1 = require("../clients/rest/generated/data-contracts");
73
75
  Object.defineProperty(exports, "V1TaskStatus", { enumerable: true, get: function () { return data_contracts_1.V1TaskStatus; } });
74
- var hatchet_error_1 = require("../util/errors/hatchet-error");
75
- Object.defineProperty(exports, "HatchetError", { enumerable: true, get: function () { return __importDefault(hatchet_error_1).default; } });
76
- Object.defineProperty(exports, "getErrorMessage", { enumerable: true, get: function () { return hatchet_error_1.getErrorMessage; } });
77
- Object.defineProperty(exports, "toHatchetError", { enumerable: true, get: function () { return hatchet_error_1.toHatchetError; } });
76
+ // HatchetError is imported and re-exported by name rather than as `default as`: tsc emits a
77
+ // getter for the latter that Node's CommonJS export detection cannot see, which hides the name
78
+ // from ESM importers of this entry.
79
+ const hatchet_error_1 = __importDefault(require("../util/errors/hatchet-error"));
80
+ exports.HatchetError = hatchet_error_1.default;
81
+ var hatchet_error_2 = require("../util/errors/hatchet-error");
82
+ Object.defineProperty(exports, "getErrorMessage", { enumerable: true, get: function () { return hatchet_error_2.getErrorMessage; } });
83
+ Object.defineProperty(exports, "toHatchetError", { enumerable: true, get: function () { return hatchet_error_2.toHatchetError; } });
78
84
  var idempotency_collision_error_1 = require("../util/errors/idempotency-collision-error");
79
85
  Object.defineProperty(exports, "IdempotencyCollisionError", { enumerable: true, get: function () { return idempotency_collision_error_1.IdempotencyCollisionError; } });
80
86
  var bulk_trigger_idempotency_collision_error_1 = require("../util/errors/bulk-trigger-idempotency-collision-error");
package/core/types.d.ts CHANGED
@@ -180,3 +180,23 @@ export interface ResultOptions {
180
180
  /** Rejects with an `AbortError` when the signal fires. */
181
181
  signal?: AbortSignal;
182
182
  }
183
+ /** Per-call options of the durable stream calls; the same shape on the core and Node clients. */
184
+ export type StreamCallOptions = {
185
+ /** The topic's namespace; the empty namespace when omitted. */
186
+ namespace?: string;
187
+ /** Resumes an `events()` iteration right after this message. */
188
+ cursor?: string;
189
+ /** Ends an `events()` iteration cleanly, without throwing; aborts a unary call. */
190
+ signal?: AbortSignal;
191
+ };
192
+ /** What a durable stream topic retains; counts and the latest message cover the tenant's retention. */
193
+ export type StreamTopicMetadata = {
194
+ namespace: string;
195
+ topic: string;
196
+ tenantId: string;
197
+ messageCount: number;
198
+ /** Resumes `events()` after the newest retained message; undefined when none is retained. */
199
+ latestCursor?: string;
200
+ /** When the newest retained message was stored; undefined when none is retained. */
201
+ lastPublishedAt?: Date;
202
+ };
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ // Verifies that every export of the built core entry is visible to an ESM importer under Node:
3
+ // the entry is CommonJS, and Node derives its named exports statically, so a re-export written
4
+ // in a form that detection misses (a getter over a default import) vanishes for `import { X }`.
5
+ import { createRequire } from 'node:module';
6
+ import { pathToFileURL } from 'node:url';
7
+ import { resolve } from 'node:path';
8
+
9
+ const entry = resolve('dist/core/index.js');
10
+ const cjs = createRequire(import.meta.url)(entry);
11
+ const esm = await import(pathToFileURL(entry).href);
12
+ const missing = Object.keys(cjs).filter((name) => name !== '__esModule' && !(name in esm));
13
+
14
+ if (missing.length > 0) {
15
+ console.error(`core entry exports hidden from ESM importers: ${missing.join(', ')}`);
16
+ process.exit(1);
17
+ }
18
+
19
+ console.log(`core entry: ${Object.keys(cjs).length} exports visible to ESM importers`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hatchet-dev/typescript-sdk",
3
- "version": "1.38.0",
3
+ "version": "1.39.0",
4
4
  "engines": {
5
5
  "node": ">=20"
6
6
  },
@@ -238,7 +238,7 @@
238
238
  "tsc:build": "pnpm run dump-version && tsc && resolve-tspaths",
239
239
  "test:unit": "jest --testMatch='**/*.test.ts'",
240
240
  "check:edge": "pnpm run tsc:build && node scripts/check-edge-entry.mjs",
241
- "check:core": "pnpm run tsc:build && node scripts/check-edge-entry.mjs dist/core/index.js --forbid axios,nice-grpc,@grpc/grpc-js,@connectrpc/connect-node,long,protobufjs,yaml",
241
+ "check:core": "pnpm run tsc:build && node scripts/check-edge-entry.mjs dist/core/index.js --forbid axios,nice-grpc,@grpc/grpc-js,@connectrpc/connect-node,long,protobufjs,yaml && node scripts/check-core-esm-exports.mjs",
242
242
  "check:exports": "pnpm run tsc:build && node scripts/check-exports.mjs",
243
243
  "test:e2e": "jest --config jest.e2e.config.ts --silent --forceExit --runInBand",
244
244
  "test:unit:watch": "jest --testMatch='**/*.test.ts' --watch",
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ // Verifies that every export of the built core entry is visible to an ESM importer under Node:
3
+ // the entry is CommonJS, and Node derives its named exports statically, so a re-export written
4
+ // in a form that detection misses (a getter over a default import) vanishes for `import { X }`.
5
+ import { createRequire } from 'node:module';
6
+ import { pathToFileURL } from 'node:url';
7
+ import { resolve } from 'node:path';
8
+
9
+ const entry = resolve('dist/core/index.js');
10
+ const cjs = createRequire(import.meta.url)(entry);
11
+ const esm = await import(pathToFileURL(entry).href);
12
+ const missing = Object.keys(cjs).filter((name) => name !== '__esModule' && !(name in esm));
13
+
14
+ if (missing.length > 0) {
15
+ console.error(`core entry exports hidden from ESM importers: ${missing.join(', ')}`);
16
+ process.exit(1);
17
+ }
18
+
19
+ console.log(`core entry: ${Object.keys(cjs).length} exports visible to ESM importers`);
@@ -1,3 +1,4 @@
1
+ import type { StreamCallOptions, StreamTopicMetadata } from '../../../core/types';
1
2
  import { HatchetClient } from '../client';
2
3
  export type StreamEvent = {
3
4
  /** the message payload */
@@ -7,23 +8,7 @@ export type StreamEvent = {
7
8
  /** when the message was stored */
8
9
  createdAt?: Date;
9
10
  };
10
- /** Covers only messages within the tenant's retention. */
11
- export type StreamTopicMetadata = {
12
- namespace: string;
13
- topic: string;
14
- tenantId: string;
15
- messageCount: number;
16
- /** resumes events() after the newest retained message; undefined when none is retained */
17
- latestCursor?: string;
18
- /** when the newest retained message was stored; undefined when none is retained */
19
- lastPublishedAt?: Date;
20
- };
21
- export type StreamCallOptions = {
22
- namespace?: string;
23
- cursor?: string;
24
- /** ends an events() iteration cleanly, without throwing */
25
- signal?: AbortSignal;
26
- };
11
+ export type { StreamCallOptions, StreamTopicMetadata } from '../../../core/types';
27
12
  /**
28
13
  * StreamsClient publishes to and reads from durable topics. Unlike
29
14
  * `runs.subscribeToStream`, topics are stored, independent of any run, and
@@ -0,0 +1 @@
1
+ export declare const DURABLE_WAIT_MARKED = "[e2e] durable wait marked";
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.DURABLE_WAIT_MARKED = void 0;
13
+ const hatchet_client_1 = require("../hatchet-client");
14
+ const workflow_1 = require("./workflow");
15
+ exports.DURABLE_WAIT_MARKED = '[e2e] durable wait marked';
16
+ // Worker for the graceful termination e2e test. The test must not terminate
17
+ // this worker before the SDK has recorded the run as waiting, and nothing
18
+ // observable outside the process marks that moment: the engine writes the
19
+ // WAIT_FOR entry before it acks, and the SDK only calls markWaiting once the
20
+ // ack arrives. The eviction manager is created lazily on the worker's
21
+ // internal state, so it is wrapped on assignment to print a line the test can
22
+ // wait for.
23
+ function main() {
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ const worker = yield hatchet_client_1.hatchet.worker('graceful-eviction-worker', {
26
+ workflows: [workflow_1.evictableSleepForGracefulTermination],
27
+ });
28
+ let manager;
29
+ Object.defineProperty(worker._internal, 'evictionManager', {
30
+ configurable: true,
31
+ enumerable: true,
32
+ get: () => manager,
33
+ set: (next) => {
34
+ manager = next;
35
+ if (!next)
36
+ return;
37
+ const markWaiting = next.markWaiting.bind(next);
38
+ next.markWaiting = (key, waitKind, resourceId) => {
39
+ markWaiting(key, waitKind, resourceId);
40
+ console.log(`${exports.DURABLE_WAIT_MARKED} key=${key} kind=${waitKind} resource=${resourceId}`);
41
+ };
42
+ },
43
+ });
44
+ yield worker.start();
45
+ });
46
+ }
47
+ if (require.main === module) {
48
+ main();
49
+ }
package/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const HATCHET_VERSION = "1.38.0";
1
+ export declare const HATCHET_VERSION = "1.39.0";
package/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HATCHET_VERSION = void 0;
4
- exports.HATCHET_VERSION = '1.38.0';
4
+ exports.HATCHET_VERSION = '1.39.0';