@mybase-dev/sdk 1.0.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.
package/dist/cache.js ADDED
@@ -0,0 +1,277 @@
1
+ // src/config.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+ var DEFAULT_CRON_PATH = "/api/cron";
6
+
7
+ // src/cron.ts
8
+ var SIGNATURE_HEADER = "X-Mybase-Signature";
9
+ var JOB_HEADER = "X-Mybase-Job";
10
+ var RUN_HEADER = "X-Mybase-Run";
11
+
12
+ class CronVerificationError extends Error {
13
+ name = "CronVerificationError";
14
+ }
15
+ var encoder = new TextEncoder;
16
+ async function hmacHex(secret, message) {
17
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
18
+ const mac = new Uint8Array(await crypto.subtle.sign("HMAC", key, encoder.encode(message)));
19
+ return Array.from(mac, (b) => b.toString(16).padStart(2, "0")).join("");
20
+ }
21
+ function safeEqual(a, b) {
22
+ if (a.length !== b.length)
23
+ return false;
24
+ let diff = 0;
25
+ for (let i = 0;i < a.length; i++)
26
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
27
+ return diff === 0;
28
+ }
29
+ async function signCronRequest(secret, body, t = Math.floor(Date.now() / 1000)) {
30
+ return `t=${t},v1=${await hmacHex(secret, `${t}.${body}`)}`;
31
+ }
32
+ async function verifySigned(signature, secret, toleranceSec, message) {
33
+ if (!signature)
34
+ throw new CronVerificationError(`missing ${SIGNATURE_HEADER} header`);
35
+ let t;
36
+ const sigs = [];
37
+ for (const part of signature.split(",")) {
38
+ const [k, v] = part.trim().split("=", 2);
39
+ if (k === "t" && v && /^\d+$/.test(v))
40
+ t = Number(v);
41
+ else if (k === "v1" && v)
42
+ sigs.push(v);
43
+ }
44
+ if (t === undefined || sigs.length === 0)
45
+ throw new CronVerificationError(`malformed ${SIGNATURE_HEADER} header`);
46
+ if (Math.abs(Date.now() / 1000 - t) > toleranceSec)
47
+ throw new CronVerificationError("signature timestamp outside tolerance");
48
+ const secrets = typeof secret === "string" ? [secret] : secret;
49
+ if (secrets.length === 0 || secrets.some((s) => !s))
50
+ throw new CronVerificationError("no cron secret configured");
51
+ for (const s of secrets) {
52
+ const expected = await hmacHex(s, message(t));
53
+ if (sigs.some((sig) => safeEqual(sig, expected)))
54
+ return;
55
+ }
56
+ throw new CronVerificationError("signature does not match");
57
+ }
58
+ async function verifyCronSignature({ body, signature, secret, toleranceSec = 300 }) {
59
+ await verifySigned(signature, secret, toleranceSec, (t) => `${t}.${body}`);
60
+ }
61
+ async function verifyCronRequest(req, secret, options = {}) {
62
+ const body = await req.text();
63
+ await verifyCronSignature({ body, signature: req.headers.get(SIGNATURE_HEADER), secret, ...options });
64
+ let run;
65
+ try {
66
+ run = JSON.parse(body);
67
+ } catch {
68
+ throw new CronVerificationError("body is not JSON");
69
+ }
70
+ if (typeof run?.job !== "string" || typeof run.runId !== "string")
71
+ throw new CronVerificationError("body is not a cron run");
72
+ return run;
73
+ }
74
+ var reply = (body, status) => new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } });
75
+ function cronHandler(options) {
76
+ const jobs = options.jobs;
77
+ return async (req) => {
78
+ if (req.method !== "POST")
79
+ return reply({ error: "method not allowed" }, 405);
80
+ let run;
81
+ try {
82
+ run = await verifyCronRequest(req, options.secret, { toleranceSec: options.toleranceSec });
83
+ } catch (err) {
84
+ if (err instanceof CronVerificationError)
85
+ return reply({ error: err.message }, 401);
86
+ throw err;
87
+ }
88
+ const job = Object.hasOwn(jobs, run.job) ? jobs[run.job] : undefined;
89
+ if (!job)
90
+ return reply({ error: `no handler for job "${run.job}"` }, 404);
91
+ try {
92
+ await job(run);
93
+ } catch (err) {
94
+ if (options.onError)
95
+ options.onError(err, run);
96
+ else
97
+ console.error(`mybase cron job "${run.job}" failed`, err);
98
+ return reply({ error: "job failed" }, 500);
99
+ }
100
+ return reply({ ok: true }, 200);
101
+ };
102
+ }
103
+
104
+ class MybaseApiError extends Error {
105
+ status;
106
+ name = "MybaseApiError";
107
+ constructor(message, status) {
108
+ super(message);
109
+ this.status = status;
110
+ }
111
+ }
112
+ var UNITS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
113
+ function parseDelay(value) {
114
+ if (typeof value === "number") {
115
+ if (!Number.isFinite(value) || value < 0)
116
+ throw new RangeError("delay must be a non-negative number of ms");
117
+ return Math.round(value);
118
+ }
119
+ const m = /^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)\s*$/.exec(value);
120
+ if (!m)
121
+ throw new RangeError(`invalid delay "${value}". Use ms, or a number with ms, s, m, h, or d (e.g. "15m")`);
122
+ return Math.round(Number(m[1]) * UNITS[m[2]]);
123
+ }
124
+ function createCronClient(options) {
125
+ const doFetch = options.fetch ?? globalThis.fetch;
126
+ const apiUrl = options.apiUrl.replace(/\/+$/, "");
127
+ const base = `/cron/${encodeURIComponent(options.project)}/tasks`;
128
+ async function call(method, path, body) {
129
+ const raw = body === undefined ? "" : JSON.stringify(body);
130
+ const res = await doFetch(`${apiUrl}${path}`, {
131
+ method,
132
+ body: body === undefined ? undefined : raw,
133
+ headers: {
134
+ "Content-Type": "application/json",
135
+ Authorization: `Bearer ${options.apiKey}`
136
+ }
137
+ });
138
+ if (res.status === 404 && method !== "POST")
139
+ return null;
140
+ const data = await res.json().catch(() => ({}));
141
+ if (!res.ok)
142
+ throw new MybaseApiError(data.error ?? `mybase API answered ${res.status}`, res.status);
143
+ return data;
144
+ }
145
+ const byId = (id) => `${base}/${encodeURIComponent(id)}`;
146
+ const byKey = (key) => `${base}/by-key/${encodeURIComponent(key)}`;
147
+ return {
148
+ async schedule(job, opts = {}) {
149
+ if (opts.at !== undefined && opts.in !== undefined)
150
+ throw new TypeError("give `at` or `in`, not both");
151
+ let url = opts.url;
152
+ if (!url) {
153
+ if (!options.baseUrl)
154
+ throw new TypeError("set `baseUrl` on the client, or `url` on the task");
155
+ url = `${options.baseUrl.replace(/\/+$/, "")}/${(opts.path ?? DEFAULT_CRON_PATH).replace(/^\/+/, "")}`;
156
+ }
157
+ const at = opts.at === undefined ? undefined : new Date(opts.at);
158
+ if (at && Number.isNaN(at.getTime()))
159
+ throw new RangeError(`invalid date "${String(opts.at)}"`);
160
+ const req = {
161
+ job,
162
+ url,
163
+ runAt: at?.toISOString(),
164
+ delayMs: opts.in === undefined ? undefined : parseDelay(opts.in),
165
+ key: opts.key,
166
+ payload: opts.payload,
167
+ retries: opts.retries,
168
+ timeoutMs: opts.timeoutMs
169
+ };
170
+ return await call("POST", base, req);
171
+ },
172
+ get: (id) => call("GET", byId(id)),
173
+ getByKey: (key) => call("GET", byKey(key)),
174
+ cancel: (id) => call("DELETE", byId(id)),
175
+ cancelByKey: (key) => call("DELETE", byKey(key))
176
+ };
177
+ }
178
+
179
+ // src/cache.ts
180
+ function setArgs(key, value, o = {}) {
181
+ const args = ["SET", key, value];
182
+ if (o.ex !== undefined)
183
+ args.push("EX", o.ex);
184
+ if (o.px !== undefined)
185
+ args.push("PX", o.px);
186
+ if (o.nx)
187
+ args.push("NX");
188
+ if (o.xx)
189
+ args.push("XX");
190
+ return args;
191
+ }
192
+ function createCache(options) {
193
+ const doFetch = options.fetch ?? globalThis.fetch;
194
+ const base = `${options.apiUrl.replace(/\/+$/, "")}/cache/${encodeURIComponent(options.project)}`;
195
+ const prefix = `${options.project}:`;
196
+ const k = (key) => key.startsWith(prefix) ? key : prefix + key;
197
+ async function post(path, body) {
198
+ const res = await doFetch(base + path, {
199
+ method: "POST",
200
+ body: JSON.stringify(body),
201
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${options.apiKey}` }
202
+ });
203
+ const data = await res.json().catch(() => ({}));
204
+ if (!res.ok)
205
+ throw new MybaseApiError(data.error ?? `mybase API answered ${res.status}`, res.status);
206
+ return data;
207
+ }
208
+ async function command(args) {
209
+ const data = await post("", args);
210
+ if (data.error !== undefined)
211
+ throw new MybaseApiError(data.error, 400);
212
+ return data.result;
213
+ }
214
+ function pipeline() {
215
+ const commands = [];
216
+ const add = (args) => {
217
+ commands.push(args);
218
+ return p;
219
+ };
220
+ const p = {
221
+ command: add,
222
+ get: (key) => add(["GET", k(key)]),
223
+ set: (key, value, o) => add(setArgs(k(key), value, o)),
224
+ del: (...keys) => add(["DEL", ...keys.map(k)]),
225
+ incr: (key, by = 1) => add(by === 1 ? ["INCR", k(key)] : ["INCRBY", k(key), by]),
226
+ expire: (key, seconds) => add(["EXPIRE", k(key), seconds]),
227
+ exec: ({ atomic = false } = {}) => commands.length === 0 ? Promise.resolve([]) : post(atomic ? "/multi-exec" : "/pipeline", commands)
228
+ };
229
+ return p;
230
+ }
231
+ const pairs = (flat) => {
232
+ const out = {};
233
+ for (let i = 0;i + 1 < flat.length; i += 2)
234
+ out[flat[i]] = flat[i + 1];
235
+ return out;
236
+ };
237
+ return {
238
+ key: k,
239
+ command,
240
+ get: (key) => command(["GET", k(key)]),
241
+ async getJson(key) {
242
+ const raw = await command(["GET", k(key)]);
243
+ return raw === null ? null : JSON.parse(raw);
244
+ },
245
+ set: async (key, value, o) => await command(setArgs(k(key), value, o)) === "OK",
246
+ setJson: async (key, value, o) => await command(setArgs(k(key), JSON.stringify(value), o)) === "OK",
247
+ del: (...keys) => command(["DEL", ...keys.map(k)]),
248
+ exists: (...keys) => command(["EXISTS", ...keys.map(k)]),
249
+ incr: (key, by = 1) => command(by === 1 ? ["INCR", k(key)] : ["INCRBY", k(key), by]),
250
+ expire: async (key, seconds) => await command(["EXPIRE", k(key), seconds]) === 1,
251
+ ttl: (key) => command(["TTL", k(key)]),
252
+ mget: (...keys) => command(["MGET", ...keys.map(k)]),
253
+ hget: (key, field) => command(["HGET", k(key), field]),
254
+ hset: (key, fields) => command(["HSET", k(key), ...Object.entries(fields).flat()]),
255
+ hgetall: async (key) => {
256
+ const raw = await command(["HGETALL", k(key)]);
257
+ return Array.isArray(raw) ? pairs(raw) : raw;
258
+ },
259
+ hdel: (key, ...fields) => command(["HDEL", k(key), ...fields]),
260
+ lpush: (key, ...values) => command(["LPUSH", k(key), ...values]),
261
+ rpush: (key, ...values) => command(["RPUSH", k(key), ...values]),
262
+ lrange: (key, start, stop) => command(["LRANGE", k(key), start, stop]),
263
+ sadd: (key, ...members) => command(["SADD", k(key), ...members]),
264
+ srem: (key, ...members) => command(["SREM", k(key), ...members]),
265
+ smembers: (key) => command(["SMEMBERS", k(key)]),
266
+ sismember: async (key, member) => await command(["SISMEMBER", k(key), member]) === 1,
267
+ zadd: (key, ...entries) => command(["ZADD", k(key), ...entries.flatMap((e) => [e.score, e.member])]),
268
+ zrange: (key, start, stop, o = {}) => command(["ZRANGE", k(key), start, stop, ...o.rev ? ["REV"] : [], ...o.withScores ? ["WITHSCORES"] : []]),
269
+ zincrby: async (key, by, member) => Number(await command(["ZINCRBY", k(key), by, member])),
270
+ publish: (channel, message) => command(["PUBLISH", k(channel), message]),
271
+ pipeline
272
+ };
273
+ }
274
+ export {
275
+ MybaseApiError,
276
+ createCache
277
+ };
@@ -0,0 +1,65 @@
1
+ import type { DeadLetterSettings } from "./queue";
2
+ import type { RateLimitAlgorithm } from "./ratelimit";
3
+ /** One job mybase calls on a schedule. */
4
+ export interface CronJobConfig {
5
+ /** Standard 5-field cron expression (minute precision), e.g. `0 3 * * *`. */
6
+ schedule: string;
7
+ /** Appended to `cron.baseUrl`. Default `/api/cron`. */
8
+ path?: string;
9
+ /** IANA time zone the schedule runs in. Default `UTC`. */
10
+ timezone?: string;
11
+ /** JSON sent to your handler as `run.payload`. */
12
+ payload?: unknown;
13
+ /** Extra tries after a failed call (network error, timeout, 429, 5xx). 0–5, default 2. */
14
+ retries?: number;
15
+ /** How long to wait for your endpoint, in ms. 1000–120000, default 30000. */
16
+ timeoutMs?: number;
17
+ }
18
+ /** One named rate limit. Apps use it with `limiter.limit("<name>", id)`. */
19
+ export interface RateLimitRuleConfig {
20
+ /** Default `sliding`. */
21
+ algorithm?: RateLimitAlgorithm;
22
+ /** Requests (units) allowed per window. */
23
+ limit: number;
24
+ /** Milliseconds, or `"10s"`, `"1m"`, `"1h"`, `"1d"`. 1s–30d. */
25
+ window: number | string;
26
+ /** Token bucket only: capacity. Default `limit`. */
27
+ burst?: number;
28
+ }
29
+ /** One queue. Durations are ms or `"30s"`, `"5m"`, `"4d"`. */
30
+ export interface QueueConfig {
31
+ /** Strict order per message group, deduplicated sends. Can't be changed after creation. Default false. */
32
+ fifo?: boolean;
33
+ /** How long a received message stays hidden. 0–12h, default 30s. */
34
+ visibilityTimeout?: number | string;
35
+ /** How long new messages wait before they can be received. 0–15m, default 0. */
36
+ delay?: number | string;
37
+ /** How long unreceived messages are kept. 1m–14d, default 4d. */
38
+ retention?: number | string;
39
+ /** Default long-poll wait for receives. 0–20s, default 0. */
40
+ receiveWait?: number | string;
41
+ /** 1024–262144, default 262144. */
42
+ maxMessageBytes?: number;
43
+ /** FIFO only: deduplicate by body when a send has no `dedupId`. */
44
+ contentDedup?: boolean;
45
+ /** After `maxReceives` failed deliveries, move messages to this queue. */
46
+ deadLetter?: DeadLetterSettings;
47
+ }
48
+ export interface MybaseConfig {
49
+ /** The mybase project these jobs belong to. */
50
+ project: string;
51
+ cron?: {
52
+ /** Where your app is reachable from the mybase server, e.g. `https://shop.example.com`. */
53
+ baseUrl: string;
54
+ jobs: Record<string, CronJobConfig>;
55
+ };
56
+ ratelimit?: {
57
+ rules: Record<string, RateLimitRuleConfig>;
58
+ };
59
+ queue?: {
60
+ queues: Record<string, QueueConfig>;
61
+ };
62
+ }
63
+ /** Identity function that types `mybase.config.ts`. */
64
+ export declare function defineConfig<const C extends MybaseConfig>(config: C): C;
65
+ export declare const DEFAULT_CRON_PATH = "/api/cron";
package/dist/config.js ADDED
@@ -0,0 +1,9 @@
1
+ // src/config.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+ var DEFAULT_CRON_PATH = "/api/cron";
6
+ export {
7
+ DEFAULT_CRON_PATH,
8
+ defineConfig
9
+ };
package/dist/cron.d.ts ADDED
@@ -0,0 +1,166 @@
1
+ import { type MybaseConfig } from "./config";
2
+ /** `schedule`: a cron tick. `manual`: "Run now". `task`: a one-off task scheduled with `createCronClient`. */
3
+ export type CronTrigger = "schedule" | "manual" | "task";
4
+ /** The body of every cron call. */
5
+ export interface CronRun<Job extends string = string> {
6
+ job: Job;
7
+ /** Same across retries of one run, so you can dedupe. */
8
+ runId: string;
9
+ /** 1 for the first try, then 2, 3, … on retries. */
10
+ attempt: number;
11
+ trigger: CronTrigger;
12
+ /** When the schedule said to run (ISO). For manual runs, when it was requested. For tasks, the task's run time. */
13
+ scheduledAt: string;
14
+ /** When this attempt was sent (ISO). */
15
+ firedAt: string;
16
+ payload: unknown;
17
+ /** Tasks only: the key the task was scheduled with, if any. `runId` is the task id. */
18
+ key?: string | null;
19
+ }
20
+ export declare const SIGNATURE_HEADER = "X-Mybase-Signature";
21
+ export declare const JOB_HEADER = "X-Mybase-Job";
22
+ export declare const RUN_HEADER = "X-Mybase-Run";
23
+ /** Thrown when a request's signature is missing, malformed, stale, or wrong. */
24
+ export declare class CronVerificationError extends Error {
25
+ name: string;
26
+ }
27
+ /** The `X-Mybase-Signature` value for `body`. `t` is unix seconds (default: now). */
28
+ export declare function signCronRequest(secret: string, body: string, t?: number): Promise<string>;
29
+ export interface VerifyOptions {
30
+ /** The raw request body, exactly as received. */
31
+ body: string;
32
+ /** The `X-Mybase-Signature` header. */
33
+ signature: string | null | undefined;
34
+ /** The project's cron secret. Pass several while rotating (new first). */
35
+ secret: string | readonly string[];
36
+ /** Max age of the signature, in seconds. Default 300. */
37
+ toleranceSec?: number;
38
+ }
39
+ /** Throws CronVerificationError unless `signature` signs `body` with one of the secrets, recently. */
40
+ export declare function verifyCronSignature({ body, signature, secret, toleranceSec }: VerifyOptions): Promise<void>;
41
+ /** Verifies a Fetch API request and returns its parsed body. Consumes the body. */
42
+ export declare function verifyCronRequest(req: Request, secret: string | readonly string[], options?: {
43
+ toleranceSec?: number;
44
+ }): Promise<CronRun>;
45
+ /** Job names from a config type, or any string without one. */
46
+ type JobName<C extends MybaseConfig> = C extends {
47
+ cron: {
48
+ jobs: infer J;
49
+ };
50
+ } ? Extract<keyof J, string> : string;
51
+ export type CronJobHandler<Job extends string = string> = (run: CronRun<Job>) => unknown | Promise<unknown>;
52
+ export interface CronHandlerOptions<C extends MybaseConfig> {
53
+ /** The project's cron secret. Pass several while rotating (new first). */
54
+ secret: string | readonly string[];
55
+ /** A function per job name. */
56
+ jobs: {
57
+ [J in JobName<C>]: CronJobHandler<J>;
58
+ };
59
+ /** Called when a job throws, before the 500 goes back (mybase then retries). */
60
+ onError?: (err: unknown, run: CronRun) => void;
61
+ toleranceSec?: number;
62
+ }
63
+ /**
64
+ * A Fetch API handler (Bun.serve, Next.js route handlers, Hono, Deno, Workers) that verifies
65
+ * the call and runs the named job. 401 bad signature, 404 unknown job, 500 job threw, 200 done.
66
+ * Pass your config type for typed job names: `cronHandler<typeof config>({ ... })`.
67
+ */
68
+ export declare function cronHandler<C extends MybaseConfig = MybaseConfig>(options: CronHandlerOptions<C>): (req: Request) => Promise<Response>;
69
+ export type CronTaskStatus = "pending" | "running" | "success" | "failed" | "cancelled";
70
+ /** A one-off call: mybase POSTs a CronRun (trigger `task`) to `url` once, at `runAt`. */
71
+ export interface CronTask {
72
+ id: string;
73
+ /** The handler name sent as `run.job`. */
74
+ job: string;
75
+ key: string | null;
76
+ url: string;
77
+ payload: unknown;
78
+ runAt: string;
79
+ timeoutMs: number;
80
+ retries: number;
81
+ status: CronTaskStatus;
82
+ attempts: number;
83
+ /** Last attempt's status; `null` before the call or when it got no response. */
84
+ httpStatus: number | null;
85
+ durationMs: number | null;
86
+ error: string | null;
87
+ /** Start of the last response body, truncated. */
88
+ response: string | null;
89
+ createdAt: string;
90
+ startedAt: string | null;
91
+ finishedAt: string | null;
92
+ }
93
+ /** What the API takes to create a task. Give `runAt` or `delayMs`, or neither to run as soon as possible. */
94
+ export interface CreateCronTaskRequest {
95
+ job: string;
96
+ url: string;
97
+ runAt?: string;
98
+ delayMs?: number;
99
+ key?: string;
100
+ payload?: unknown;
101
+ timeoutMs?: number;
102
+ retries?: number;
103
+ }
104
+ export interface CronClientOptions {
105
+ /** Where the mybase API is reachable from your app, e.g. `https://mybase.example.com`. */
106
+ apiUrl: string;
107
+ /** The mybase project. */
108
+ project: string;
109
+ /** A mybase API key (`mb_…`) with `cron:write` for this project. Server-side only. */
110
+ apiKey: string;
111
+ /** Your app, as reachable from the mybase server. Tasks POST to `baseUrl` + `path` unless they set `url`. */
112
+ baseUrl?: string;
113
+ /** Default `globalThis.fetch`. */
114
+ fetch?: typeof fetch;
115
+ }
116
+ export interface ScheduleOptions {
117
+ /** When to run. */
118
+ at?: Date | string;
119
+ /** Or how long from now: milliseconds, or `"30s"`, `"15m"`, `"2h"`, `"7d"`. Neither `at` nor `in`: as soon as possible. */
120
+ in?: number | string;
121
+ /** JSON sent to your handler as `run.payload` (up to 16 KB). */
122
+ payload?: unknown;
123
+ /**
124
+ * Dedupe key. While a task with this key is pending or running, scheduling the key again
125
+ * returns that task instead of making a new one. Also lets you `get`/`cancel` by key.
126
+ */
127
+ key?: string;
128
+ /** Appended to the client's `baseUrl`. Default `/api/cron`. */
129
+ path?: string;
130
+ /** Full URL to call instead of `baseUrl` + `path`. */
131
+ url?: string;
132
+ /** Extra tries after a failed call. 0–5, default 2. */
133
+ retries?: number;
134
+ /** 1000–120000, default 30000. */
135
+ timeoutMs?: number;
136
+ }
137
+ /** A non-2xx answer from the mybase API. */
138
+ export declare class MybaseApiError extends Error {
139
+ readonly status: number;
140
+ name: string;
141
+ constructor(message: string, status: number);
142
+ }
143
+ /** `1500`, `"30s"`, `"15m"`, `"2h"`, `"7d"` → milliseconds. */
144
+ export declare function parseDelay(value: number | string): number;
145
+ export interface CronClient {
146
+ /** Schedules `job` to be called once. Resolves to the new task (or the pending one with the same `key`). */
147
+ schedule(job: string, options?: ScheduleOptions): Promise<CronTask>;
148
+ /** A task by id, or `null` once it's gone (finished tasks are kept 7 days). */
149
+ get(id: string): Promise<CronTask | null>;
150
+ /** The newest task with this key, or `null`. */
151
+ getByKey(key: string): Promise<CronTask | null>;
152
+ /** Cancels a pending task. Resolves to the task, or `null` when there's no such task. Throws 409 if it already ran. */
153
+ cancel(id: string): Promise<CronTask | null>;
154
+ /** Cancels the pending task with this key. */
155
+ cancelByKey(key: string): Promise<CronTask | null>;
156
+ }
157
+ /**
158
+ * Schedules one-off calls to your cron handler from app code:
159
+ *
160
+ * const cron = createCronClient({ apiUrl, project: "shop", apiKey: process.env.MYBASE_API_KEY!, baseUrl: "https://shop.example.com" });
161
+ * await cron.schedule("send-reminder", { in: "24h", payload: { orderId }, key: `reminder-${orderId}` });
162
+ *
163
+ * Keep the API key server-side.
164
+ */
165
+ export declare function createCronClient(options: CronClientOptions): CronClient;
166
+ export {};