@helmr/sdk 0.1.1-rc.4 → 0.1.1-rc.6

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,3 +1,7 @@
1
+ import { type PayloadSchemaInput, type PayloadSchemaOutput, type PayloadSchema } from "./schema/payload.js";
2
+ import type { IdempotencyKeyInput } from "./idempotency.js";
3
+ import type { RunHandle } from "./runtime/run.js";
4
+ export { parsePayloadWithSchema } from "./schema/payload.js";
1
5
  export interface CacheMount {
2
6
  readonly id: string;
3
7
  }
@@ -13,33 +17,6 @@ export interface SecretMountBinding {
13
17
  export interface SourceDirectoryOptions {
14
18
  readonly ignore?: readonly string[];
15
19
  }
16
- export interface WorkspaceSpec {
17
- readonly kind: "github";
18
- readonly repository: string;
19
- readonly ref: string;
20
- readonly subpath?: string;
21
- }
22
- export type GitHubRefKind = "branch" | "tag" | "sha" | "pull_request" | "unknown";
23
- export interface GitHubPullRequestMetadata {
24
- readonly number: number;
25
- readonly baseRef: string;
26
- readonly baseSha: string;
27
- readonly headRef: string;
28
- readonly headSha: string;
29
- }
30
- export interface GitHubTaskSource {
31
- readonly kind: "github";
32
- readonly repository: string;
33
- readonly requestedRef: string;
34
- readonly resolvedSha: string;
35
- readonly refKind?: GitHubRefKind;
36
- readonly refName?: string;
37
- readonly fullRef?: string;
38
- readonly subpath?: string;
39
- readonly defaultBranch?: string;
40
- readonly pullRequest?: GitHubPullRequestMetadata;
41
- }
42
- export type TaskSource = GitHubTaskSource;
43
20
  export interface TaskWorkspace {
44
21
  readonly path: string;
45
22
  readonly projectPath: string;
@@ -55,19 +32,38 @@ export interface SourceCapabilities {
55
32
  file(path: string): SourceFileRef;
56
33
  directory(path: string, opts?: SourceDirectoryOptions): SourceDirRef;
57
34
  }
58
- export interface WorkspaceCapabilities {
59
- github(repo: string, opts: {
60
- readonly ref: string;
61
- readonly subpath?: string;
62
- }): WorkspaceSpec;
35
+ export type WaitJson = null | boolean | number | string | readonly WaitJson[] | {
36
+ readonly [key: string]: WaitJson;
37
+ };
38
+ export interface WaitOptions {
39
+ readonly timeout?: number;
40
+ readonly policy?: string;
41
+ readonly displayText?: string;
63
42
  }
64
- export declare class ApprovalTimeoutError extends Error {
65
- constructor(message: string);
66
- static [Symbol.hasInstance](value: unknown): boolean;
43
+ export interface WaitManualOptions<TSchema extends PayloadSchema<any, any> = PayloadSchema<any, any>> extends WaitOptions {
44
+ readonly schema?: TSchema;
67
45
  }
68
- export declare class MessageTimeoutError extends Error {
69
- constructor(message: string);
70
- static [Symbol.hasInstance](value: unknown): boolean;
46
+ export interface WaitResolution<TPayload = unknown> {
47
+ readonly kind: string;
48
+ readonly payload: TPayload;
49
+ readonly at: Date;
50
+ readonly principal?: string;
51
+ }
52
+ export type WaitForInput = string | number | {
53
+ readonly seconds?: number;
54
+ readonly milliseconds?: number;
55
+ readonly duration?: string;
56
+ readonly [key: string]: WaitJson | undefined;
57
+ };
58
+ export type WaitUntilInput = string | Date | {
59
+ readonly date?: string | Date;
60
+ readonly [key: string]: WaitJson | Date | undefined;
61
+ };
62
+ export interface WaitCapabilities {
63
+ for(input: WaitForInput, opts?: Omit<WaitOptions, "timeout" | "policy">): Promise<void>;
64
+ until(input: WaitUntilInput, opts?: Omit<WaitOptions, "timeout" | "policy">): Promise<void>;
65
+ manual<TSchema extends PayloadSchema<any, any>>(opts: WaitManualOptions<TSchema>): Promise<PayloadSchemaOutput<TSchema>>;
66
+ manual<TPayload = unknown>(opts?: WaitManualOptions): Promise<TPayload>;
71
67
  }
72
68
  export declare class ConcurrentWaitError extends Error {
73
69
  constructor(message: string);
@@ -87,25 +83,7 @@ export type Placement = {
87
83
  export type SecretDecls = Record<string, Placement>;
88
84
  export declare function validateSecretName(name: string, label?: string): void;
89
85
  export interface TaskContext {
90
- readonly wait: {
91
- approval(message: string, opts?: {
92
- readonly timeout?: number;
93
- readonly policy?: string;
94
- }): Promise<{
95
- readonly approved: boolean;
96
- readonly approvedBy: string;
97
- readonly at: Date;
98
- }>;
99
- message(prompt?: string, opts?: {
100
- readonly timeout?: number;
101
- readonly policy?: string;
102
- }): Promise<{
103
- readonly text: string;
104
- readonly sentBy: string;
105
- readonly at: Date;
106
- readonly attachments: readonly unknown[];
107
- }>;
108
- };
86
+ readonly wait: WaitCapabilities;
109
87
  emit(event: EmitEvent): void;
110
88
  readonly log: {
111
89
  info(...args: unknown[]): void;
@@ -119,7 +97,6 @@ export interface TaskContext {
119
97
  readonly task: {
120
98
  readonly id: string;
121
99
  };
122
- readonly source: TaskSource;
123
100
  readonly workspace: TaskWorkspace;
124
101
  }
125
102
  export type EmitContent = {
@@ -146,17 +123,96 @@ export interface EmitEvent {
146
123
  readonly content: readonly EmitContent[];
147
124
  }
148
125
  export type MaybePromise<T> = T | Promise<T>;
149
- export interface TaskConfig<TPayload = unknown, TOutput = unknown, TSecrets extends SecretDecls = Record<never, never>> {
126
+ declare const noPayloadBrand: unique symbol;
127
+ export interface NoPayload {
128
+ readonly [noPayloadBrand]: "NoPayload";
129
+ }
130
+ export interface TaskConfigBase<TSecrets extends SecretDecls = Record<never, never>> {
150
131
  readonly id: string;
151
132
  readonly sandbox: SandboxBuilder;
152
133
  readonly maxDuration?: number;
134
+ readonly queue?: TaskQueueConfig;
135
+ readonly ttl?: string;
153
136
  readonly secrets?: TSecrets;
154
- readonly run: (payload: TPayload, ctx: TaskContext) => MaybePromise<TOutput>;
155
137
  }
156
- export type Task<TPayload = unknown, TOutput = unknown, TSecrets extends SecretDecls = Record<never, never>> = TaskConfig<TPayload, TOutput, TSecrets>;
157
- export type AnyTask = Task<any, any, SecretDecls>;
158
- export type TaskPayload<TTask> = TTask extends Task<infer TPayload, any, any> ? TPayload : never;
159
- export type TaskOutput<TTask> = TTask extends Task<any, infer TOutput, any> ? Awaited<TOutput> : never;
138
+ export interface TaskQueueConfig {
139
+ readonly name?: string;
140
+ readonly concurrencyLimit?: number | null;
141
+ }
142
+ export interface InternalTaskScheduleConfig {
143
+ readonly cron: string;
144
+ readonly timezone?: string;
145
+ readonly secretBindings?: Record<string, string>;
146
+ }
147
+ export type TaskRunOptions<TSecrets extends SecretDecls> = {
148
+ readonly deploymentId?: string;
149
+ readonly version?: string;
150
+ readonly queue?: string;
151
+ readonly concurrencyKey?: string;
152
+ readonly priority?: number;
153
+ readonly ttl?: string;
154
+ readonly idempotencyKey?: IdempotencyKeyInput;
155
+ readonly idempotencyKeyTTL?: string;
156
+ } & ([keyof TSecrets] extends [never] ? {
157
+ readonly secrets?: Record<never, never>;
158
+ } : {
159
+ readonly secrets: {
160
+ readonly [K in keyof TSecrets]: string;
161
+ };
162
+ });
163
+ export type TaskDirectTrigger<TPayloadInput, TOutput, TSecrets extends SecretDecls> = [TPayloadInput] extends [NoPayload] ? (opts: TaskRunOptions<TSecrets>) => Promise<RunHandle<Awaited<TOutput>>> : (payload: TPayloadInput, opts: TaskRunOptions<TSecrets>) => Promise<RunHandle<Awaited<TOutput>>>;
164
+ export type TaskConfigWithPayload<TPayloadSchema extends PayloadSchema<any, any>, TOutput = unknown, TSecrets extends SecretDecls = Record<never, never>> = TaskConfigBase<TSecrets> & {
165
+ readonly payload: TPayloadSchema;
166
+ readonly run: (payload: PayloadSchemaOutput<TPayloadSchema>, ctx: TaskContext) => MaybePromise<TOutput>;
167
+ };
168
+ export type TaskConfigWithoutPayload<TOutput = unknown, TSecrets extends SecretDecls = Record<never, never>> = TaskConfigBase<TSecrets> & {
169
+ readonly payload?: undefined;
170
+ readonly run: (ctx: TaskContext) => MaybePromise<TOutput>;
171
+ };
172
+ export type TaskConfig<TPayload = NoPayload, TOutput = unknown, TSecrets extends SecretDecls = Record<never, never>, TPayloadSchema extends PayloadSchema<any, any> | undefined = undefined> = TPayloadSchema extends PayloadSchema<any, any> ? TaskConfigWithPayload<TPayloadSchema, TOutput, TSecrets> : TaskConfigWithoutPayload<TOutput, TSecrets>;
173
+ export type Task<TPayload = NoPayload, TOutput = unknown, TSecrets extends SecretDecls = Record<never, never>, TPayloadInput = TPayload> = TaskConfigBase<TSecrets> & {
174
+ readonly "~types"?: {
175
+ readonly payloadInput: TPayloadInput;
176
+ readonly payload: TPayload;
177
+ readonly output: TOutput;
178
+ readonly secrets: TSecrets;
179
+ };
180
+ readonly payload?: PayloadSchema<TPayloadInput, TPayload>;
181
+ readonly run: [TPayloadInput] extends [NoPayload] ? (ctx: TaskContext) => MaybePromise<TOutput> : (payload: TPayload, ctx: TaskContext) => MaybePromise<TOutput>;
182
+ readonly trigger: TaskDirectTrigger<TPayloadInput, TOutput, TSecrets>;
183
+ };
184
+ export type AnyTask = TaskConfigBase<SecretDecls> & {
185
+ readonly schedule?: InternalTaskScheduleConfig;
186
+ readonly "~types"?: {
187
+ readonly payloadInput: any;
188
+ readonly payload: any;
189
+ readonly output: any;
190
+ readonly secrets: SecretDecls;
191
+ };
192
+ readonly payload?: PayloadSchema<any, any>;
193
+ readonly run: (...args: any[]) => MaybePromise<any>;
194
+ readonly trigger: (...args: any[]) => Promise<RunHandle<any>>;
195
+ };
196
+ export type TaskPayload<TTask> = TTask extends {
197
+ readonly "~types"?: {
198
+ readonly payload: infer TPayload;
199
+ };
200
+ } ? [TPayload] extends [NoPayload] ? never : TPayload : never;
201
+ export type TaskTriggerPayload<TTask> = TTask extends {
202
+ readonly "~types"?: {
203
+ readonly payloadInput: infer TPayloadInput;
204
+ };
205
+ } ? TPayloadInput : never;
206
+ export type TaskOutput<TTask> = TTask extends {
207
+ readonly "~types"?: {
208
+ readonly output: infer TOutput;
209
+ };
210
+ } ? Awaited<TOutput> : never;
211
+ export type TaskSecrets<TTask> = TTask extends {
212
+ readonly "~types"?: {
213
+ readonly secrets: infer TSecrets;
214
+ };
215
+ } ? TSecrets : never;
160
216
  export declare const taskBrand: unique symbol;
161
217
  export declare const taskOriginBrand: unique symbol;
162
218
  export declare const configBrand: unique symbol;
@@ -180,6 +236,7 @@ export interface SandboxBuilder {
180
236
  resources(opts: {
181
237
  readonly cpu?: number;
182
238
  readonly memory?: string;
239
+ readonly disk?: string;
183
240
  }): SandboxBuilder;
184
241
  }
185
242
  export type ImageBuildStep = {
@@ -216,8 +273,16 @@ export interface SandboxWorkspace {
216
273
  export interface SandboxResources {
217
274
  readonly cpu?: number;
218
275
  readonly memory?: string;
276
+ readonly disk?: string;
219
277
  }
220
- export declare function markTask<TPayload, TOutput, TSecrets extends SecretDecls>(config: TaskConfig<TPayload, TOutput, TSecrets>): Task<TPayload, Awaited<TOutput>, TSecrets>;
278
+ export type MarkedTask<TPayload, TOutput, TSecrets extends SecretDecls, TPayloadSchema extends PayloadSchema<any, any> | undefined> = TPayloadSchema extends PayloadSchema<any, any> ? Task<PayloadSchemaOutput<TPayloadSchema>, Awaited<TOutput>, TSecrets, PayloadSchemaInput<TPayloadSchema>> : Task<NoPayload, Awaited<TOutput>, TSecrets, NoPayload>;
279
+ export declare function markTask<TPayload, TOutput, TSecrets extends SecretDecls, TPayloadSchema extends PayloadSchema<any, any> | undefined>(config: TaskConfig<TPayload, TOutput, TSecrets, TPayloadSchema>): MarkedTask<TPayload, TOutput, TSecrets, TPayloadSchema>;
280
+ export declare function markScheduledTask<TPayload, TOutput, TSecrets extends SecretDecls, TPayloadSchema extends PayloadSchema<any, any> | undefined>(config: TaskConfig<TPayload, TOutput, TSecrets, TPayloadSchema>, schedule: InternalTaskScheduleConfig | undefined): MarkedTask<TPayload, TOutput, TSecrets, TPayloadSchema>;
281
+ export declare function validateTaskSchedule(taskId: string, value: InternalTaskScheduleConfig | undefined): void;
282
+ export declare function validateTaskQueue(taskId: string, value: TaskQueueConfig | undefined): void;
283
+ export declare function defaultTaskQueueName(taskId: string): string;
284
+ export declare function validateOptionalTTL(value: unknown, label?: string): void;
285
+ export declare function parseTaskPayload<TTask extends AnyTask>(task: TTask, payload: unknown): Promise<TaskPayload<TTask>>;
221
286
  export declare function isTaskDefinition(value: unknown): value is AnyTask & {
222
287
  readonly [taskBrand]: true;
223
288
  };
@@ -254,6 +319,7 @@ export declare class SandboxBuilderImpl implements SandboxBuilder {
254
319
  resources(opts: {
255
320
  readonly cpu?: number;
256
321
  readonly memory?: string;
322
+ readonly disk?: string;
257
323
  }): SandboxBuilder;
258
324
  }
259
325
  export declare class SourceFileRefImpl implements SourceFileRef {
package/dist/internal.js CHANGED
@@ -1,9 +1,92 @@
1
+ // sdk/typescript/src/schema/payload.ts
2
+ var payloadSchemaValidationErrorBrand = Symbol.for("helmr.sdk.PayloadSchemaValidationError");
3
+
4
+ class PayloadSchemaValidationError extends Error {
5
+ issues;
6
+ constructor(label, issues) {
7
+ super(formatPayloadSchemaValidationMessage(label, issues));
8
+ this.name = "PayloadSchemaValidationError";
9
+ this.issues = issues;
10
+ Object.defineProperty(this, payloadSchemaValidationErrorBrand, { value: true });
11
+ }
12
+ static [Symbol.hasInstance](value) {
13
+ return this === PayloadSchemaValidationError && typeof value === "object" && value !== null && payloadSchemaValidationErrorBrand in value;
14
+ }
15
+ }
16
+ function assertPayloadSchema(value, label = "payload") {
17
+ if (value === undefined) {
18
+ return;
19
+ }
20
+ assertStandardSchema(value, label);
21
+ }
22
+ function assertStandardSchema(value, label = "schema") {
23
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
24
+ throw new Error(`${label} must implement the Standard Schema v1 interface`);
25
+ }
26
+ const standard = value["~standard"];
27
+ if (standard === null || typeof standard !== "object") {
28
+ throw new Error(`${label} must implement the Standard Schema v1 interface`);
29
+ }
30
+ const record = standard;
31
+ if (record["version"] !== 1 || typeof record["validate"] !== "function") {
32
+ throw new Error(`${label} must implement the Standard Schema v1 interface`);
33
+ }
34
+ }
35
+ async function parsePayloadWithSchema(schema, payload, label) {
36
+ assertStandardSchema(schema, label);
37
+ const result = await schema["~standard"].validate(payload);
38
+ if ("issues" in result && result.issues !== undefined) {
39
+ throw new PayloadSchemaValidationError(label, result.issues);
40
+ }
41
+ return result.value;
42
+ }
43
+ function formatPayloadSchemaValidationMessage(label, issues) {
44
+ if (issues.length === 0) {
45
+ return `${label} failed validation`;
46
+ }
47
+ const formattedIssues = issues.slice(0, 5).map(formatPayloadSchemaIssue);
48
+ const suffix = issues.length > formattedIssues.length ? `; and ${issues.length - formattedIssues.length} more` : "";
49
+ return `${label} failed validation: ${formattedIssues.join("; ")}${suffix}`;
50
+ }
51
+ function formatPayloadSchemaIssue(issue) {
52
+ const path = formatPayloadSchemaIssuePath(issue.path);
53
+ return path === "" ? issue.message : `${path}: ${issue.message}`;
54
+ }
55
+ function formatPayloadSchemaIssuePath(path) {
56
+ if (path === undefined || path.length === 0) {
57
+ return "";
58
+ }
59
+ let formatted = "payload";
60
+ for (const segment of path) {
61
+ const key = typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment;
62
+ if (typeof key === "string" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
63
+ formatted += `.${key}`;
64
+ } else if (typeof key === "string" && isArrayIndexKey(key)) {
65
+ formatted += `[${key}]`;
66
+ } else if (typeof key === "number") {
67
+ formatted += `[${key}]`;
68
+ } else {
69
+ formatted += `[${JSON.stringify(String(key))}]`;
70
+ }
71
+ }
72
+ return formatted;
73
+ }
74
+ function isArrayIndexKey(value) {
75
+ if (!/^(0|[1-9]\d*)$/.test(value)) {
76
+ return false;
77
+ }
78
+ const parsed = Number(value);
79
+ return Number.isSafeInteger(parsed);
80
+ }
81
+
1
82
  // sdk/typescript/src/schema/task.ts
2
83
  var TASK_ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$";
3
84
  var TASK_ID_MAX_LENGTH = 128;
4
85
  var DEFAULT_MAX_DURATION_SECONDS = 900;
5
86
  var MIN_MAX_DURATION_SECONDS = 5;
6
87
  var MAX_DURATION_SECONDS = 86400;
88
+ var QUEUE_NAME_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._/-]{0,255}$";
89
+ var QUEUE_NAME_MAX_LENGTH = 256;
7
90
 
8
91
  class TaskIdError extends Error {
9
92
  name = "TaskIdError";
@@ -57,36 +140,60 @@ function readOptionalMaxDurationSeconds(value, label = "task maxDuration") {
57
140
  function validateOptionalMaxDurationSeconds(value, label = "task maxDuration") {
58
141
  readOptionalMaxDurationSeconds(value, label);
59
142
  }
60
- function isAsciiAlnum(code) {
61
- return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
62
- }
63
143
 
64
- // sdk/typescript/src/internal.ts
65
- var approvalTimeoutErrorBrand = Symbol.for("helmr.sdk.ApprovalTimeoutError");
66
- var messageTimeoutErrorBrand = Symbol.for("helmr.sdk.MessageTimeoutError");
67
- var concurrentWaitErrorBrand = Symbol.for("helmr.sdk.ConcurrentWaitError");
144
+ class TaskQueueNameError extends Error {
145
+ name = "TaskQueueNameError";
146
+ value;
147
+ constructor(value) {
148
+ super(`queue name must match ${QUEUE_NAME_PATTERN}: ${JSON.stringify(value)}`);
149
+ this.value = value;
150
+ }
151
+ }
68
152
 
69
- class ApprovalTimeoutError extends Error {
70
- constructor(message) {
71
- super(message);
72
- this.name = "ApprovalTimeoutError";
73
- Object.defineProperty(this, approvalTimeoutErrorBrand, { value: true });
153
+ class TaskQueueConcurrencyLimitError extends Error {
154
+ name = "TaskQueueConcurrencyLimitError";
155
+ value;
156
+ constructor(value) {
157
+ super("queue concurrencyLimit must be a positive integer");
158
+ this.value = value;
74
159
  }
75
- static [Symbol.hasInstance](value) {
76
- return this === ApprovalTimeoutError && typeof value === "object" && value !== null && approvalTimeoutErrorBrand in value;
160
+ }
161
+ function validateQueueName(value) {
162
+ if (!isValidQueueName(value)) {
163
+ throw new TaskQueueNameError(value);
77
164
  }
78
165
  }
79
-
80
- class MessageTimeoutError extends Error {
81
- constructor(message) {
82
- super(message);
83
- this.name = "MessageTimeoutError";
84
- Object.defineProperty(this, messageTimeoutErrorBrand, { value: true });
166
+ function isValidQueueName(value) {
167
+ if (value.length === 0 || value.length > QUEUE_NAME_MAX_LENGTH) {
168
+ return false;
85
169
  }
86
- static [Symbol.hasInstance](value) {
87
- return this === MessageTimeoutError && typeof value === "object" && value !== null && messageTimeoutErrorBrand in value;
170
+ const first = value.charCodeAt(0);
171
+ if (!isAsciiAlnum(first)) {
172
+ return false;
88
173
  }
174
+ for (let index = 1;index < value.length; index += 1) {
175
+ const code = value.charCodeAt(index);
176
+ if (!(isAsciiAlnum(code) || code === 46 || code === 95 || code === 45 || code === 47)) {
177
+ return false;
178
+ }
179
+ }
180
+ return true;
89
181
  }
182
+ function validateOptionalQueueConcurrencyLimit(value) {
183
+ if (value === undefined || value === null) {
184
+ return;
185
+ }
186
+ if (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value) && value > 0) {
187
+ return;
188
+ }
189
+ throw new TaskQueueConcurrencyLimitError(value);
190
+ }
191
+ function isAsciiAlnum(code) {
192
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
193
+ }
194
+
195
+ // sdk/typescript/src/internal.ts
196
+ var concurrentWaitErrorBrand = Symbol.for("helmr.sdk.ConcurrentWaitError");
90
197
 
91
198
  class ConcurrentWaitError extends Error {
92
199
  constructor(message) {
@@ -162,12 +269,136 @@ var sandboxBuilderBrand = Symbol.for("helmr.sdk.SandboxBuilder");
162
269
  var sourceFileRefBrand = Symbol.for("helmr.sdk.SourceFileRef");
163
270
  var sourceDirRefBrand = Symbol.for("helmr.sdk.SourceDirRef");
164
271
  function markTask(config) {
272
+ if ("schedule" in config) {
273
+ throw new Error(`task ${JSON.stringify(config.id)} must use schedules.task(...) for declarative schedules`);
274
+ }
275
+ return markTaskInternal(config, undefined);
276
+ }
277
+ function markScheduledTask(config, schedule) {
278
+ return markTaskInternal(config, schedule);
279
+ }
280
+ function markTaskInternal(config, schedule) {
165
281
  validateTaskId(config.id);
166
282
  validateOptionalMaxDurationSeconds(config.maxDuration);
283
+ validateTaskQueue(config.id, config.queue);
284
+ validateTaskSchedule(config.id, schedule);
285
+ validateOptionalTTL(config.ttl, `task ${JSON.stringify(config.id)} ttl`);
286
+ assertPayloadSchema(config.payload, `task ${JSON.stringify(config.id)} payload`);
287
+ if (schedule !== undefined) {
288
+ Object.defineProperty(config, "schedule", {
289
+ value: Object.freeze({ ...schedule }),
290
+ enumerable: true
291
+ });
292
+ }
167
293
  Object.defineProperty(config, taskBrand, { value: true });
168
294
  Object.defineProperty(config, taskOriginBrand, { value: captureTaskOrigin() });
169
295
  return config;
170
296
  }
297
+ function validateTaskSchedule(taskId, value) {
298
+ if (value === undefined) {
299
+ return;
300
+ }
301
+ if (value.cron.trim() === "") {
302
+ throw new Error(`task ${JSON.stringify(taskId)} schedule cron is required`);
303
+ }
304
+ if (value.timezone !== undefined && value.timezone.trim() === "") {
305
+ throw new Error(`task ${JSON.stringify(taskId)} schedule timezone must not be empty`);
306
+ }
307
+ if (value.secretBindings !== undefined) {
308
+ if (value.secretBindings === null || typeof value.secretBindings !== "object" || Array.isArray(value.secretBindings)) {
309
+ throw new Error(`task ${JSON.stringify(taskId)} schedule secretBindings must be an object`);
310
+ }
311
+ for (const [name, binding] of Object.entries(value.secretBindings)) {
312
+ validateSecretName(name, `task ${JSON.stringify(taskId)} schedule secretBindings.${name}`);
313
+ if (typeof binding !== "string" || binding.trim() === "") {
314
+ throw new Error(`task ${JSON.stringify(taskId)} schedule secretBindings.${name} must be a non-empty string`);
315
+ }
316
+ }
317
+ }
318
+ }
319
+ function validateTaskQueue(taskId, value) {
320
+ if (value === undefined) {
321
+ return;
322
+ }
323
+ if (value.name !== undefined) {
324
+ validateQueueName(value.name);
325
+ }
326
+ validateOptionalQueueConcurrencyLimit(value.concurrencyLimit);
327
+ if (value.name === undefined && value.concurrencyLimit === undefined) {
328
+ throw new Error(`task ${JSON.stringify(taskId)} queue must include name or concurrencyLimit`);
329
+ }
330
+ }
331
+ function defaultTaskQueueName(taskId) {
332
+ return `task/${taskId}`;
333
+ }
334
+ function validateOptionalTTL(value, label = "ttl") {
335
+ if (value === undefined) {
336
+ return;
337
+ }
338
+ if (typeof value === "string" && isPositiveDurationString(value)) {
339
+ return;
340
+ }
341
+ throw new Error(`${label} must be a positive duration string`);
342
+ }
343
+ function isPositiveDurationString(value) {
344
+ const raw = value.trim();
345
+ if (raw === "") {
346
+ return false;
347
+ }
348
+ if (/^[1-9][0-9]*d$/.test(raw)) {
349
+ return true;
350
+ }
351
+ const sign = raw.startsWith("+") || raw.startsWith("-") ? raw.slice(0, 1) : "";
352
+ if (sign === "-") {
353
+ return false;
354
+ }
355
+ const body = sign === "" ? raw : raw.slice(1);
356
+ const tokenPattern = /([0-9]+(?:\.[0-9]*)?|\.[0-9]+)(ns|us|µs|μs|ms|s|m|h)/gy;
357
+ let totalNanoseconds = 0;
358
+ let offset = 0;
359
+ for (;; ) {
360
+ tokenPattern.lastIndex = offset;
361
+ const match = tokenPattern.exec(body);
362
+ if (match === null) {
363
+ return offset === body.length && totalNanoseconds >= 1;
364
+ }
365
+ if (match.index !== offset) {
366
+ return false;
367
+ }
368
+ const amount = Number(match[1]);
369
+ if (!Number.isFinite(amount) || amount < 0) {
370
+ return false;
371
+ }
372
+ totalNanoseconds += amount * durationUnitNanoseconds(match[2] ?? "");
373
+ offset = tokenPattern.lastIndex;
374
+ }
375
+ }
376
+ function durationUnitNanoseconds(unit) {
377
+ switch (unit) {
378
+ case "ns":
379
+ return 1;
380
+ case "us":
381
+ case "µs":
382
+ case "μs":
383
+ return 1000;
384
+ case "ms":
385
+ return 1e6;
386
+ case "s":
387
+ return 1e9;
388
+ case "m":
389
+ return 60000000000;
390
+ case "h":
391
+ return 3600000000000;
392
+ default:
393
+ return 0;
394
+ }
395
+ }
396
+ async function parseTaskPayload(task, payload) {
397
+ if (task.payload === undefined) {
398
+ throw new Error(`task ${JSON.stringify(task.id)} does not accept payload`);
399
+ }
400
+ return await parsePayloadWithSchema(task.payload, payload, `task ${JSON.stringify(task.id)} payload`);
401
+ }
171
402
  function isTaskDefinition(value) {
172
403
  return hasBrand(value, taskBrand);
173
404
  }
@@ -283,7 +514,8 @@ class SandboxBuilderImpl {
283
514
  resources(opts) {
284
515
  const resourceSpec = {
285
516
  ...opts.cpu === undefined ? {} : { cpu: opts.cpu },
286
- ...opts.memory === undefined ? {} : { memory: opts.memory }
517
+ ...opts.memory === undefined ? {} : { memory: opts.memory },
518
+ ...opts.disk === undefined ? {} : { disk: opts.disk }
287
519
  };
288
520
  return new SandboxBuilderImpl(this.id, this.imageBuilder, this.workspaceBinding, resourceSpec);
289
521
  }
@@ -322,11 +554,17 @@ function hasBrand(value, brand) {
322
554
  return value !== null && typeof value === "object" && value[brand] === true;
323
555
  }
324
556
  export {
557
+ validateTaskSchedule,
558
+ validateTaskQueue,
325
559
  validateSecretName,
560
+ validateOptionalTTL,
326
561
  taskOriginFile,
327
562
  taskOriginBrand,
328
563
  taskBrand,
564
+ parseTaskPayload,
565
+ parsePayloadWithSchema,
329
566
  markTask,
567
+ markScheduledTask,
330
568
  markConfig,
331
569
  isTaskDefinition,
332
570
  isSourceFileRef,
@@ -334,12 +572,11 @@ export {
334
572
  isSandboxBuilder,
335
573
  isImageBuilder,
336
574
  isConfigDefinition,
575
+ defaultTaskQueueName,
337
576
  configBrand,
338
577
  SourceFileRefImpl,
339
578
  SourceDirRefImpl,
340
579
  SandboxBuilderImpl,
341
- MessageTimeoutError,
342
580
  ImageBuilderImpl,
343
- ConcurrentWaitError,
344
- ApprovalTimeoutError
581
+ ConcurrentWaitError
345
582
  };