@vercel/schedules 0.0.0-alpha.1 → 0.0.0-alpha.11

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/README.md CHANGED
@@ -8,19 +8,114 @@ npm install @vercel/schedules
8
8
  import { Schedules } from '@vercel/schedules';
9
9
 
10
10
  await Schedules.create({
11
+ name: 'cleanup',
11
12
  target: { topic: 'scheduled-cleanup' },
12
13
  expression: { type: 'cron', cron: '0 * * * *' },
13
14
  });
14
15
  ```
15
16
 
17
+ ## Schedule in a timezone
18
+
19
+ Set `timezone` to an IANA timezone to evaluate a cron expression in local time. For example, run a schedule at 9 AM Pacific time each day:
20
+
21
+ ```ts
22
+ import { Schedules } from '@vercel/schedules';
23
+
24
+ await Schedules.create({
25
+ name: 'daily-cleanup',
26
+ target: { topic: 'scheduled-cleanup' },
27
+ expression: { type: 'cron', cron: '0 9 * * *' },
28
+ timezone: 'America/Los_Angeles',
29
+ });
30
+ ```
31
+
32
+ Omitting `timezone` uses UTC. Named timezones follow daylight-saving changes.
33
+
34
+ For a one-time schedule, pass `at` in `YYYY-MM-DDTHH:mm` or `YYYY-MM-DDTHH:mm:00` format. The datetime has no timezone information; set `timezone` separately or omit it to use UTC:
35
+
36
+ ```ts
37
+ await Schedules.create({
38
+ name: 'one-time-cleanup',
39
+ target: { topic: 'scheduled-cleanup' },
40
+ expression: { type: 'single', at: '2026-09-16T09:00' },
41
+ timezone: 'America/Los_Angeles',
42
+ });
43
+ ```
44
+
45
+ This schedules one invocation at 9 AM Pacific on September 16. The `at` field does not accept `Z`, numeric offsets, fractional seconds, or non-zero seconds. Omitted seconds are stored as `:00`.
46
+
47
+ Schedules returned by `get()` and `list()` include the IANA timezone.
48
+
49
+ Returned targets can be queues (`{ type: 'queue', topic: string }`) or statically configured functions (`{ type: 'function', function: string }`). Check `target.type` before reading the destination. Both the local and production APIs can return function targets. The `create()` and `update()` target inputs remain queue-only (`{ topic: string }`); the SDK does not support creating dynamic function schedules.
50
+
51
+ Update schedule configuration by name and optional namespace:
52
+
53
+ ```ts
54
+ await Schedules.update({
55
+ name: 'cleanup',
56
+ expression: { type: 'cron', cron: '0 2 * * *' },
57
+ payload: { maxAgeDays: 30 },
58
+ });
59
+ ```
60
+
61
+ Use `Schedules.enable` and `Schedules.disable` to change schedule state.
62
+
63
+ Invoke a schedule immediately:
64
+
65
+ ```ts
66
+ await Schedules.invoke({ name: 'cleanup' });
67
+ ```
68
+
69
+ The method returns once the asynchronous invocation has been accepted.
70
+
71
+ ## Handle a scheduled function
72
+
73
+ Use `handleSchedule` in a function targeted by a schedule:
74
+
75
+ ```ts
76
+ // api/cleanup.ts
77
+ import { handleSchedule } from '@vercel/schedules';
78
+
79
+ export const POST = handleSchedule(async (metadata) => {
80
+ console.log(`Running ${metadata.name}, scheduled at ${metadata.scheduledAt}`);
81
+ await deleteExpiredRecords();
82
+ });
83
+ ```
84
+
85
+ The handler receives only metadata about the current firing, including `executionId` from the CloudEvent `ce-id` header and `scheduledAt` from the required `ce-vssscheduledat` header. `scheduledAt` is a `Date` representing the scheduled run time; the parser does not use `ce-time` as a fallback. The handler returns a `200` response when the callback completes successfully. Static schedules do not accept a configured payload; dynamic Queue schedules created or updated through the SDK still support `payload`.
86
+
16
87
  For a custom client:
17
88
 
18
89
  ```ts
19
90
  import { SchedulesClient } from '@vercel/schedules';
20
91
 
21
- const schedules = new SchedulesClient({ baseUrl: 'https://vss-server.vercel.sh' });
92
+ const schedules = new SchedulesClient();
22
93
  ```
23
94
 
24
- The default client uses `https://vss-server.vercel.sh`, overridable via `VERCEL_SCHEDULE_BASE_URL`.
95
+ The default client connects to the Vercel Schedules API at `https://vercel-schedules.com`. Override the endpoint with `baseUrl` or `VERCEL_SCHEDULE_BASE_URL`.
25
96
 
26
97
  Requests are authenticated with the Vercel OIDC token from `@vercel/oidc`, or an explicit `token` option.
98
+
99
+ ## Connect to the local Schedules API
100
+
101
+ The SDK uses CLI-injected local authentication only when `NODE_ENV` is `development`, `VERCEL_DEPLOYMENT_ID` is unset, and `VERCEL_SCHEDULE_DEV_API_VERSION` is `1`. An empty `VERCEL_DEPLOYMENT_ID` still counts as set and disables automatic local authentication.
102
+
103
+ A compatible CLI-hosted local API supplies these environment variables before starting your app:
104
+
105
+ | Variable | Value |
106
+ | --- | --- |
107
+ | `VERCEL_SCHEDULE_BASE_URL` | Absolute HTTP(S) URL of the local API, optionally including a proxy path prefix. The URL must not include credentials, a query, or a fragment. |
108
+ | `VERCEL_SCHEDULE_TOKEN` | Bearer token for the local API. |
109
+ | `VERCEL_SCHEDULE_DEV_API_VERSION` | `1`, declaring support for the SDK's `/v1/schedules` routes. |
110
+
111
+ The endpoint precedence remains `baseUrl`, then `VERCEL_SCHEDULE_BASE_URL`, then `https://vercel-schedules.com`. Without the version marker, the existing environment URL override continues to use OIDC or an explicit token, even for a loopback URL.
112
+
113
+ An explicit `token` takes precedence over `VERCEL_SCHEDULE_TOKEN`. The injected token applies only when the effective endpoint has the same origin and normalized base path as `VERCEL_SCHEDULE_BASE_URL`. URL normalization includes default ports and trailing slashes; a different path on the same origin is a separate endpoint. A separate endpoint uses its explicit token or OIDC and ignores broken injected local configuration.
114
+
115
+ The SDK preserves the base path when appending API routes. For example, `http://localhost:3000/_vercel/schedules` sends list requests to `http://localhost:3000/_vercel/schedules/v1/schedules`. The local API supports the same `create`, `list`, `get`, `update`, `delete`, `enable`, `disable`, and `invoke` methods.
116
+
117
+ The exported `Schedules` singleton is created eagerly when the SDK module is imported. Set the environment variables **before importing** `@vercel/schedules`. Each client captures its endpoint, local token, and local configuration errors at construction; changing the environment afterward does not reconfigure that client. Create a new `SchedulesClient` to capture changed configuration.
118
+
119
+ When local mode is selected, a missing token, missing or invalid URL, or unsupported version causes requests to fail before fetching or acquiring OIDC credentials. An explicit token can replace a missing injected token. Only selected-local configuration errors are deferred until a method is called, so importing the default singleton still allows you to create a client for an independent endpoint. An invalid explicit `baseUrl` always throws during construction, as does an invalid environment base URL outside selected local mode. Local request failures and API errors do not fall back to OIDC or the cloud API.
120
+
121
+ The SDK does not probe local capabilities, start a scheduler, or create timers on import or client construction. The version marker declares API compatibility; the CLI manages the local service.
package/dist/index.cjs CHANGED
@@ -2,9 +2,16 @@
2
2
  var _oidc = require('@vercel/oidc');
3
3
 
4
4
  // src/constants.ts
5
- var DEFAULT_SCHEDULES_BASE_URL = "https://vss-server.vercel.sh";
5
+ var DEFAULT_SCHEDULES_BASE_URL = "https://vercel-schedules.com";
6
6
 
7
7
  // src/api-client.ts
8
+ function trimTrailingSlashes(path) {
9
+ let end = path.length;
10
+ while (end > 0 && path[end - 1] === "/") {
11
+ end--;
12
+ }
13
+ return path.slice(0, end);
14
+ }
8
15
  var SchedulesApiError = class extends Error {
9
16
  constructor(status, message) {
10
17
  super(message);
@@ -14,14 +21,50 @@ var SchedulesApiError = class extends Error {
14
21
  };
15
22
  var SchedulesApiClient = class {
16
23
  constructor(options = {}) {
17
- var _a, _b, _c;
24
+ var _a, _b, _c, _d, _e;
25
+ this.token = options.token;
26
+ this.fetchImpl = (_a = options.fetch) != null ? _a : fetch;
27
+ const envBaseUrl = process.env.VERCEL_SCHEDULE_BASE_URL;
28
+ const devApiVersion = process.env.VERCEL_SCHEDULE_DEV_API_VERSION;
29
+ let advertisedBaseUrl;
30
+ try {
31
+ advertisedBaseUrl = new URL(envBaseUrl != null ? envBaseUrl : "");
32
+ } catch (e) {
33
+ }
34
+ const explicitBaseUrl = options.baseUrl === void 0 ? void 0 : new URL(options.baseUrl);
35
+ const localSelected = process.env.NODE_ENV === "development" && process.env.VERCEL_DEPLOYMENT_ID === void 0 && devApiVersion !== void 0 && (explicitBaseUrl === void 0 || advertisedBaseUrl !== void 0 && explicitBaseUrl.origin === advertisedBaseUrl.origin && trimTrailingSlashes(explicitBaseUrl.pathname) === trimTrailingSlashes(advertisedBaseUrl.pathname));
36
+ if (localSelected) {
37
+ try {
38
+ if (devApiVersion !== "1") {
39
+ throw new Error(
40
+ 'Unsupported VERCEL_SCHEDULE_DEV_API_VERSION. This SDK supports local Schedules API version "1".'
41
+ );
42
+ }
43
+ if (!advertisedBaseUrl || !["http:", "https:"].includes(advertisedBaseUrl.protocol) || advertisedBaseUrl.username || advertisedBaseUrl.password || advertisedBaseUrl.search || advertisedBaseUrl.hash) {
44
+ throw new Error(
45
+ "Local Schedules requires VERCEL_SCHEDULE_BASE_URL to be an absolute HTTP(S) URL without credentials, a query, or a fragment."
46
+ );
47
+ }
48
+ this.token = (_b = options.token) != null ? _b : process.env.VERCEL_SCHEDULE_TOKEN;
49
+ if (!((_c = this.token) == null ? void 0 : _c.trim())) {
50
+ throw new Error(
51
+ "Local Schedules requires VERCEL_SCHEDULE_TOKEN or an explicit token option."
52
+ );
53
+ }
54
+ } catch (err) {
55
+ this.configurationError = err instanceof Error ? err : new Error(String(err));
56
+ return;
57
+ }
58
+ }
18
59
  this.baseUrl = new URL(
19
- (_b = (_a = options.baseUrl) != null ? _a : process.env.VERCEL_SCHEDULE_BASE_URL) != null ? _b : DEFAULT_SCHEDULES_BASE_URL
60
+ (_e = (_d = options.baseUrl) != null ? _d : envBaseUrl) != null ? _e : DEFAULT_SCHEDULES_BASE_URL
20
61
  );
21
- this.token = options.token;
22
- this.fetchImpl = (_c = options.fetch) != null ? _c : fetch;
62
+ this.baseUrl.pathname = `${trimTrailingSlashes(this.baseUrl.pathname)}/`;
23
63
  }
24
64
  async getToken() {
65
+ if (this.configurationError) {
66
+ throw this.configurationError;
67
+ }
25
68
  if (this.token) {
26
69
  return this.token;
27
70
  }
@@ -53,11 +96,14 @@ Cause: ${cause}`
53
96
  if (body !== void 0) {
54
97
  headers.set("Content-Type", "application/json");
55
98
  }
56
- const response = await this.fetchImpl(new URL(path, this.baseUrl), {
57
- method,
58
- headers,
59
- body: body === void 0 ? void 0 : JSON.stringify(body)
60
- });
99
+ const response = await this.fetchImpl(
100
+ new URL(path.replace(/^\/+/, ""), this.baseUrl),
101
+ {
102
+ method,
103
+ headers,
104
+ body: body === void 0 ? void 0 : JSON.stringify(body)
105
+ }
106
+ );
61
107
  if (!response.ok) {
62
108
  const text = await response.text();
63
109
  throw new SchedulesApiError(response.status, text || response.statusText);
@@ -70,6 +116,15 @@ Cause: ${cause}`
70
116
  };
71
117
 
72
118
  // src/schedules.ts
119
+ function schedulePath(name, namespace, suffix = "") {
120
+ const searchParams = new URLSearchParams();
121
+ if (namespace !== void 0) {
122
+ searchParams.set("namespace", namespace);
123
+ }
124
+ const query = searchParams.toString();
125
+ const path = `/v1/schedules/${encodeURIComponent(name)}${suffix}`;
126
+ return query ? `${path}?${query}` : path;
127
+ }
73
128
  var SchedulesClient = class extends SchedulesApiClient {
74
129
  create({
75
130
  target,
@@ -83,26 +138,167 @@ var SchedulesClient = class extends SchedulesApiClient {
83
138
  }
84
139
  });
85
140
  }
86
- get(scheduleId) {
87
- return this.request(
88
- "GET",
89
- `/v1/schedules/${encodeURIComponent(scheduleId)}`
90
- );
141
+ list(params = {}) {
142
+ const searchParams = new URLSearchParams();
143
+ if (params.namespace !== void 0) {
144
+ searchParams.set("namespace", params.namespace);
145
+ }
146
+ if (params.cursor !== void 0) {
147
+ searchParams.set("cursor", params.cursor);
148
+ }
149
+ if (params.limit !== void 0) {
150
+ searchParams.set("limit", String(params.limit));
151
+ }
152
+ const query = searchParams.toString();
153
+ const path = query ? `/v1/schedules?${query}` : "/v1/schedules";
154
+ return this.request("GET", path);
91
155
  }
92
- delete(scheduleId) {
93
- return this.request(
94
- "DELETE",
95
- `/v1/schedules/${encodeURIComponent(scheduleId)}`
96
- );
156
+ get({ name, namespace }) {
157
+ return this.request("GET", schedulePath(name, namespace));
158
+ }
159
+ update({
160
+ name,
161
+ namespace,
162
+ target,
163
+ ...params
164
+ }) {
165
+ return this.request("PATCH", schedulePath(name, namespace), {
166
+ ...params,
167
+ ...target === void 0 ? {} : {
168
+ target: {
169
+ type: "queue",
170
+ topic: target.topic
171
+ }
172
+ }
173
+ });
174
+ }
175
+ async delete({ name, namespace }) {
176
+ await this.request("DELETE", schedulePath(name, namespace));
177
+ }
178
+ enable({ name, namespace }) {
179
+ return this.request("POST", schedulePath(name, namespace, "/enable"));
180
+ }
181
+ disable({ name, namespace }) {
182
+ return this.request("POST", schedulePath(name, namespace, "/disable"));
183
+ }
184
+ async invoke({ name, namespace }) {
185
+ await this.request("POST", schedulePath(name, namespace, "/invoke"));
97
186
  }
98
187
  };
99
188
 
100
189
  // src/default-client.ts
101
190
  var schedules = new SchedulesClient();
102
191
 
192
+ // src/schedule-event.ts
193
+ var SCHEDULE_CLOUD_EVENT_TYPE = "com.vercel.schedule.v1beta";
194
+ var CLOUD_EVENT_SPEC_VERSION_HEADER = "ce-specversion";
195
+ var CLOUD_EVENT_TYPE_HEADER = "ce-type";
196
+ var CLOUD_EVENT_SOURCE_HEADER = "ce-source";
197
+ var CLOUD_EVENT_ID_HEADER = "ce-id";
198
+ var SCHEDULE_SCHEDULED_AT_HEADER = "ce-vssscheduledat";
199
+ var SCHEDULE_ID_HEADER = "ce-vssscheduleid";
200
+ var SCHEDULE_NAME_HEADER = "ce-vssschedulename";
201
+ var SCHEDULE_NAMESPACE_HEADER = "ce-vssnamespace";
202
+ var SCHEDULE_SOURCE_HEADER = "ce-vssschedulesource";
203
+ var ScheduleEventParseError = class extends Error {
204
+ constructor(message, options) {
205
+ super(message);
206
+ this.name = "ScheduleEventParseError";
207
+ if (options && "cause" in options) {
208
+ this.cause = options.cause;
209
+ }
210
+ }
211
+ };
212
+ function requiredHeader(request, name) {
213
+ const value = request.headers.get(name);
214
+ if (!value) {
215
+ throw new ScheduleEventParseError(
216
+ `Request is missing the ${name} header; it does not look like a schedule dispatch`
217
+ );
218
+ }
219
+ return value;
220
+ }
221
+ async function parseScheduleEvent(request) {
222
+ if (request.method !== "POST") {
223
+ throw new ScheduleEventParseError(
224
+ `Schedule dispatches are POST requests, received ${request.method}`
225
+ );
226
+ }
227
+ const specVersion = requiredHeader(request, CLOUD_EVENT_SPEC_VERSION_HEADER);
228
+ if (specVersion !== "1.0") {
229
+ throw new ScheduleEventParseError(
230
+ `Unsupported CloudEvent spec version: ${specVersion}`
231
+ );
232
+ }
233
+ const eventType = requiredHeader(request, CLOUD_EVENT_TYPE_HEADER);
234
+ if (eventType !== SCHEDULE_CLOUD_EVENT_TYPE) {
235
+ throw new ScheduleEventParseError(
236
+ `Unsupported schedule CloudEvent type: ${eventType}`
237
+ );
238
+ }
239
+ requiredHeader(request, CLOUD_EVENT_SOURCE_HEADER);
240
+ const executionId = requiredHeader(request, CLOUD_EVENT_ID_HEADER);
241
+ const scheduledAtValue = requiredHeader(
242
+ request,
243
+ SCHEDULE_SCHEDULED_AT_HEADER
244
+ );
245
+ const metadata = {
246
+ scheduleId: requiredHeader(request, SCHEDULE_ID_HEADER),
247
+ executionId,
248
+ name: requiredHeader(request, SCHEDULE_NAME_HEADER),
249
+ namespace: requiredHeader(request, SCHEDULE_NAMESPACE_HEADER),
250
+ scheduledAt: new Date(scheduledAtValue),
251
+ source: requiredHeader(request, SCHEDULE_SOURCE_HEADER)
252
+ };
253
+ if (Number.isNaN(metadata.scheduledAt.getTime())) {
254
+ throw new ScheduleEventParseError(
255
+ `Schedule scheduledAt is not a valid timestamp: ${scheduledAtValue}`
256
+ );
257
+ }
258
+ return metadata;
259
+ }
260
+ function handleSchedule(handler) {
261
+ return async (request) => {
262
+ let metadata;
263
+ try {
264
+ metadata = await parseScheduleEvent(request);
265
+ } catch (error) {
266
+ if (error instanceof ScheduleEventParseError) {
267
+ return Response.json({ error: error.message }, { status: 400 });
268
+ }
269
+ return Response.json(
270
+ { error: "Failed to parse schedule dispatch" },
271
+ { status: 500 }
272
+ );
273
+ }
274
+ try {
275
+ await handler(metadata);
276
+ return new Response(null, { status: 200 });
277
+ } catch (e2) {
278
+ return Response.json(
279
+ { error: "Schedule handler failed" },
280
+ { status: 500 }
281
+ );
282
+ }
283
+ };
284
+ }
285
+
286
+
287
+
288
+
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
103
299
 
104
300
 
105
301
 
106
302
 
107
- exports.DEFAULT_SCHEDULES_BASE_URL = DEFAULT_SCHEDULES_BASE_URL; exports.Schedules = schedules; exports.SchedulesApiError = SchedulesApiError; exports.SchedulesClient = SchedulesClient;
303
+ exports.CLOUD_EVENT_ID_HEADER = CLOUD_EVENT_ID_HEADER; exports.CLOUD_EVENT_SOURCE_HEADER = CLOUD_EVENT_SOURCE_HEADER; exports.CLOUD_EVENT_SPEC_VERSION_HEADER = CLOUD_EVENT_SPEC_VERSION_HEADER; exports.CLOUD_EVENT_TYPE_HEADER = CLOUD_EVENT_TYPE_HEADER; exports.DEFAULT_SCHEDULES_BASE_URL = DEFAULT_SCHEDULES_BASE_URL; exports.SCHEDULE_CLOUD_EVENT_TYPE = SCHEDULE_CLOUD_EVENT_TYPE; exports.SCHEDULE_ID_HEADER = SCHEDULE_ID_HEADER; exports.SCHEDULE_NAMESPACE_HEADER = SCHEDULE_NAMESPACE_HEADER; exports.SCHEDULE_NAME_HEADER = SCHEDULE_NAME_HEADER; exports.SCHEDULE_SCHEDULED_AT_HEADER = SCHEDULE_SCHEDULED_AT_HEADER; exports.SCHEDULE_SOURCE_HEADER = SCHEDULE_SOURCE_HEADER; exports.ScheduleEventParseError = ScheduleEventParseError; exports.Schedules = schedules; exports.SchedulesApiError = SchedulesApiError; exports.SchedulesClient = SchedulesClient; exports.handleSchedule = handleSchedule; exports.parseScheduleEvent = parseScheduleEvent;
108
304
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/schedules/schedules/packages/schedules/dist/index.cjs","../src/api-client.ts","../src/constants.ts","../src/schedules.ts","../src/default-client.ts"],"names":[],"mappings":"AAAA;ACAA,oCAAmC;ADEnC;AACA;AEHO,IAAM,2BAAA,EAA6B,8BAAA;AFK1C;AACA;ACGO,IAAM,kBAAA,EAAN,MAAA,QAAgC,MAAM;AAAA,EAG3C,WAAA,CAAY,MAAA,EAAgB,OAAA,EAAiB;AAC3C,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,KAAA,EAAO,mBAAA;AACZ,IAAA,IAAA,CAAK,OAAA,EAAS,MAAA;AAAA,EAChB;AACF,CAAA;AAEO,IAAM,mBAAA,EAAN,MAAyB;AAAA,EAK9B,WAAA,CAAY,QAAA,EAAqC,CAAC,CAAA,EAAG;AAxBvD,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAyBI,IAAA,IAAA,CAAK,QAAA,EAAU,IAAI,GAAA;AAAA,MAAA,CACjB,GAAA,EAAA,CAAA,GAAA,EAAA,OAAA,CAAQ,OAAA,EAAA,GAAR,KAAA,EAAA,GAAA,EACE,OAAA,CAAQ,GAAA,CAAI,wBAAA,EAAA,GADd,KAAA,EAAA,GAAA,EAEE;AAAA,IACJ,CAAA;AACA,IAAA,IAAA,CAAK,MAAA,EAAQ,OAAA,CAAQ,KAAA;AACrB,IAAA,IAAA,CAAK,UAAA,EAAA,CAAY,GAAA,EAAA,OAAA,CAAQ,KAAA,EAAA,GAAR,KAAA,EAAA,GAAA,EAAiB,KAAA;AAAA,EACpC;AAAA,EAEA,MAAgB,QAAA,CAAA,EAA4B;AAC1C,IAAA,GAAA,CAAI,IAAA,CAAK,KAAA,EAAO;AACd,MAAA,OAAO,IAAA,CAAK,KAAA;AAAA,IACd;AAEA,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,sCAAA,CAAmB;AAAA,IAClC,EAAA,MAAA,CAAS,GAAA,EAAK;AACZ,MAAA,MAAM,MAAA,EAAQ,IAAA,WAAe,MAAA,EAAQ,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,GAAG,CAAA;AAC7D,MAAA,MAAM,MAAA,EAAQ,OAAA,CAAQ,GAAA,CAAI,SAAA,IAAa,aAAA;AAEvC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,MAAA,EACI,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAA,EAGY,KAAK,CAAA,EAAA;AACjB;AAAA;AAAA;AAAA;AAAA;AAKiB,OAAA;AACvB,MAAA;AACF,IAAA;AACF,EAAA;AAEgB,EAAA;AAKR,IAAA;AACJ,MAAA;AACD,IAAA;AAEG,IAAA;AACM,MAAA;AACV,IAAA;AAEM,IAAA;AACJ,MAAA;AACA,MAAA;AACM,MAAA;AACP,IAAA;AAEI,IAAA;AACG,MAAA;AACA,MAAA;AACR,IAAA;AAEI,IAAA;AACK,MAAA;AACT,IAAA;AAEQ,IAAA;AACV,EAAA;AACF;ADrBe;AACA;AG/DF;AACJ,EAAA;AACL,IAAA;AACG,IAAA;AACqD,EAAA;AACjD,IAAA;AACF,MAAA;AACK,MAAA;AACA,QAAA;AACC,QAAA;AACT,MAAA;AACD,IAAA;AACH,EAAA;AAEI,EAAA;AACK,IAAA;AACL,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACE,IAAA;AACL,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;AH+De;AACA;AIjGF;AJmGE;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/schedules/schedules/packages/schedules/dist/index.cjs","sourcesContent":[null,"import { getVercelOidcToken } from '@vercel/oidc';\nimport { DEFAULT_SCHEDULES_BASE_URL } from './constants';\n\nexport interface SchedulesApiClientOptions {\n baseUrl?: string;\n token?: string;\n fetch?: typeof fetch;\n}\n\nexport class SchedulesApiError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = 'SchedulesApiError';\n this.status = status;\n }\n}\n\nexport class SchedulesApiClient {\n private readonly baseUrl: URL;\n private readonly token?: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: SchedulesApiClientOptions = {}) {\n this.baseUrl = new URL(\n options.baseUrl ??\n process.env.VERCEL_SCHEDULE_BASE_URL ??\n DEFAULT_SCHEDULES_BASE_URL,\n );\n this.token = options.token;\n this.fetchImpl = options.fetch ?? fetch;\n }\n\n protected async getToken(): Promise<string> {\n if (this.token) {\n return this.token;\n }\n\n try {\n return await getVercelOidcToken();\n } catch (err) {\n const cause = err instanceof Error ? err.message : String(err);\n const isDev = process.env.NODE_ENV === 'development';\n\n throw new Error(\n isDev\n ? 'Failed to get OIDC token for local development.\\n\\n' +\n 'To fix this, pull your environment variables with Vercel CLI:\\n' +\n ' `vercel env pull`\\n\\n' +\n `Cause: ${cause}`\n : 'Failed to get OIDC token. This usually means the function is running ' +\n 'outside of a Vercel Function environment.\\n\\n' +\n 'To fix this, either:\\n' +\n ' - Deploy to Vercel (OIDC tokens are provisioned automatically)\\n' +\n ' - Provide a token explicitly: `new Schedules({ token: \"...\" })`\\n\\n' +\n `Cause: ${cause}`,\n );\n }\n }\n\n protected async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const headers = new Headers({\n Authorization: `Bearer ${await this.getToken()}`,\n });\n\n if (body !== undefined) {\n headers.set('Content-Type', 'application/json');\n }\n\n const response = await this.fetchImpl(new URL(path, this.baseUrl), {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new SchedulesApiError(response.status, text || response.statusText);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n }\n}\n","export const DEFAULT_SCHEDULES_BASE_URL = 'https://vss-server.vercel.sh';\n","import { SchedulesApiClient } from './api-client';\nimport type {\n CreateScheduleParams,\n CreateScheduleResponse,\n DeleteScheduleResponse,\n Schedule,\n} from './types';\n\nexport class SchedulesClient extends SchedulesApiClient {\n create({\n target,\n ...params\n }: CreateScheduleParams): Promise<CreateScheduleResponse> {\n return this.request('POST', '/v1/schedules', {\n ...params,\n target: {\n type: 'queue',\n topic: target.topic,\n },\n });\n }\n\n get(scheduleId: string): Promise<Schedule> {\n return this.request(\n 'GET',\n `/v1/schedules/${encodeURIComponent(scheduleId)}`,\n );\n }\n\n delete(scheduleId: string): Promise<DeleteScheduleResponse> {\n return this.request(\n 'DELETE',\n `/v1/schedules/${encodeURIComponent(scheduleId)}`,\n );\n }\n}\n","import { SchedulesClient } from './schedules';\n\nexport const schedules = new SchedulesClient();\n"]}
1
+ {"version":3,"sources":["/home/runner/work/schedules/schedules/packages/schedules/dist/index.cjs","../src/api-client.ts","../src/constants.ts","../src/schedules.ts","../src/default-client.ts","../src/schedule-event.ts"],"names":[],"mappings":"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","file":"/home/runner/work/schedules/schedules/packages/schedules/dist/index.cjs","sourcesContent":[null,"import { getVercelOidcToken } from '@vercel/oidc';\nimport { DEFAULT_SCHEDULES_BASE_URL } from './constants';\n\nexport interface SchedulesApiClientOptions {\n baseUrl?: string;\n token?: string;\n fetch?: typeof fetch;\n}\n\nfunction trimTrailingSlashes(path: string): string {\n let end = path.length;\n while (end > 0 && path[end - 1] === '/') {\n end--;\n }\n return path.slice(0, end);\n}\n\nexport class SchedulesApiError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = 'SchedulesApiError';\n this.status = status;\n }\n}\n\nexport class SchedulesApiClient {\n private readonly baseUrl?: URL;\n private readonly token?: string;\n private readonly fetchImpl: typeof fetch;\n private readonly configurationError?: Error;\n\n constructor(options: SchedulesApiClientOptions = {}) {\n this.token = options.token;\n this.fetchImpl = options.fetch ?? fetch;\n\n const envBaseUrl = process.env.VERCEL_SCHEDULE_BASE_URL;\n const devApiVersion = process.env.VERCEL_SCHEDULE_DEV_API_VERSION;\n let advertisedBaseUrl: URL | undefined;\n try {\n advertisedBaseUrl = new URL(envBaseUrl ?? '');\n } catch {\n // An independent explicit endpoint does not need the injected URL.\n }\n\n const explicitBaseUrl =\n options.baseUrl === undefined ? undefined : new URL(options.baseUrl);\n const localSelected =\n process.env.NODE_ENV === 'development' &&\n process.env.VERCEL_DEPLOYMENT_ID === undefined &&\n devApiVersion !== undefined &&\n (explicitBaseUrl === undefined ||\n (advertisedBaseUrl !== undefined &&\n explicitBaseUrl.origin === advertisedBaseUrl.origin &&\n trimTrailingSlashes(explicitBaseUrl.pathname) ===\n trimTrailingSlashes(advertisedBaseUrl.pathname)));\n\n if (localSelected) {\n try {\n if (devApiVersion !== '1') {\n throw new Error(\n 'Unsupported VERCEL_SCHEDULE_DEV_API_VERSION. This SDK supports local Schedules API version \"1\".',\n );\n }\n if (\n !advertisedBaseUrl ||\n !['http:', 'https:'].includes(advertisedBaseUrl.protocol) ||\n advertisedBaseUrl.username ||\n advertisedBaseUrl.password ||\n advertisedBaseUrl.search ||\n advertisedBaseUrl.hash\n ) {\n throw new Error(\n 'Local Schedules requires VERCEL_SCHEDULE_BASE_URL to be an absolute HTTP(S) URL without credentials, a query, or a fragment.',\n );\n }\n this.token = options.token ?? process.env.VERCEL_SCHEDULE_TOKEN;\n if (!this.token?.trim()) {\n throw new Error(\n 'Local Schedules requires VERCEL_SCHEDULE_TOKEN or an explicit token option.',\n );\n }\n } catch (err) {\n // Defer only selected-local configuration errors so the default\n // singleton does not prevent importing independent clients.\n this.configurationError =\n err instanceof Error ? err : new Error(String(err));\n return;\n }\n }\n\n this.baseUrl = new URL(\n options.baseUrl ?? envBaseUrl ?? DEFAULT_SCHEDULES_BASE_URL,\n );\n this.baseUrl.pathname = `${trimTrailingSlashes(this.baseUrl.pathname)}/`;\n }\n\n protected async getToken(): Promise<string> {\n if (this.configurationError) {\n throw this.configurationError;\n }\n if (this.token) {\n return this.token;\n }\n\n try {\n return await getVercelOidcToken();\n } catch (err) {\n const cause = err instanceof Error ? err.message : String(err);\n const isDev = process.env.NODE_ENV === 'development';\n\n throw new Error(\n isDev\n ? 'Failed to get OIDC token for local development.\\n\\n' +\n 'To fix this, pull your environment variables with Vercel CLI:\\n' +\n ' `vercel env pull`\\n\\n' +\n `Cause: ${cause}`\n : 'Failed to get OIDC token. This usually means the function is running ' +\n 'outside of a Vercel Function environment.\\n\\n' +\n 'To fix this, either:\\n' +\n ' - Deploy to Vercel (OIDC tokens are provisioned automatically)\\n' +\n ' - Provide a token explicitly: `new Schedules({ token: \"...\" })`\\n\\n' +\n `Cause: ${cause}`,\n );\n }\n }\n\n protected async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const headers = new Headers({\n Authorization: `Bearer ${await this.getToken()}`,\n });\n\n if (body !== undefined) {\n headers.set('Content-Type', 'application/json');\n }\n\n const response = await this.fetchImpl(\n new URL(path.replace(/^\\/+/, ''), this.baseUrl),\n {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new SchedulesApiError(response.status, text || response.statusText);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n }\n}\n","export const DEFAULT_SCHEDULES_BASE_URL = 'https://vercel-schedules.com';\n","import { SchedulesApiClient } from './api-client';\nimport type {\n CreateScheduleParams,\n CreateScheduleResponse,\n InvokeScheduleParams,\n ListSchedulesParams,\n ListSchedulesResponse,\n Schedule,\n ScheduleIdentifier,\n UpdateScheduleParams,\n UpdateScheduleResponse,\n} from './types';\n\nfunction schedulePath(name: string, namespace?: string, suffix = ''): string {\n const searchParams = new URLSearchParams();\n\n if (namespace !== undefined) {\n searchParams.set('namespace', namespace);\n }\n\n const query = searchParams.toString();\n const path = `/v1/schedules/${encodeURIComponent(name)}${suffix}`;\n\n return query ? `${path}?${query}` : path;\n}\n\nexport class SchedulesClient extends SchedulesApiClient {\n create({\n target,\n ...params\n }: CreateScheduleParams): Promise<CreateScheduleResponse> {\n return this.request('POST', '/v1/schedules', {\n ...params,\n target: {\n type: 'queue',\n topic: target.topic,\n },\n });\n }\n\n list(params: ListSchedulesParams = {}): Promise<ListSchedulesResponse> {\n const searchParams = new URLSearchParams();\n\n if (params.namespace !== undefined) {\n searchParams.set('namespace', params.namespace);\n }\n if (params.cursor !== undefined) {\n searchParams.set('cursor', params.cursor);\n }\n if (params.limit !== undefined) {\n searchParams.set('limit', String(params.limit));\n }\n\n const query = searchParams.toString();\n const path = query ? `/v1/schedules?${query}` : '/v1/schedules';\n\n return this.request('GET', path);\n }\n\n get({ name, namespace }: ScheduleIdentifier): Promise<Schedule> {\n return this.request('GET', schedulePath(name, namespace));\n }\n\n update({\n name,\n namespace,\n target,\n ...params\n }: UpdateScheduleParams): Promise<UpdateScheduleResponse> {\n return this.request('PATCH', schedulePath(name, namespace), {\n ...params,\n ...(target === undefined\n ? {}\n : {\n target: {\n type: 'queue',\n topic: target.topic,\n },\n }),\n });\n }\n\n async delete({ name, namespace }: ScheduleIdentifier): Promise<void> {\n await this.request('DELETE', schedulePath(name, namespace));\n }\n\n enable({ name, namespace }: ScheduleIdentifier): Promise<Schedule> {\n return this.request('POST', schedulePath(name, namespace, '/enable'));\n }\n\n disable({ name, namespace }: ScheduleIdentifier): Promise<Schedule> {\n return this.request('POST', schedulePath(name, namespace, '/disable'));\n }\n\n async invoke({ name, namespace }: InvokeScheduleParams): Promise<void> {\n await this.request('POST', schedulePath(name, namespace, '/invoke'));\n }\n}\n","import { SchedulesClient } from './schedules';\n\nexport const schedules = new SchedulesClient();\n","/** CloudEvent type used for schedule function dispatches. */\nexport const SCHEDULE_CLOUD_EVENT_TYPE = 'com.vercel.schedule.v1beta';\nexport const CLOUD_EVENT_SPEC_VERSION_HEADER = 'ce-specversion';\nexport const CLOUD_EVENT_TYPE_HEADER = 'ce-type';\nexport const CLOUD_EVENT_SOURCE_HEADER = 'ce-source';\nexport const CLOUD_EVENT_ID_HEADER = 'ce-id';\nexport const SCHEDULE_SCHEDULED_AT_HEADER = 'ce-vssscheduledat';\nexport const SCHEDULE_ID_HEADER = 'ce-vssscheduleid';\nexport const SCHEDULE_NAME_HEADER = 'ce-vssschedulename';\nexport const SCHEDULE_NAMESPACE_HEADER = 'ce-vssnamespace';\nexport const SCHEDULE_SOURCE_HEADER = 'ce-vssschedulesource';\n\nexport interface ScheduleMetadata {\n /** Unique identifier of the schedule. */\n scheduleId: string;\n /** Unique identifier for this schedule occurrence. */\n executionId: string;\n /** Schedule name, unique within its project and namespace. */\n name: string;\n /** Namespace the schedule belongs to. */\n namespace: string;\n /**\n * Nominal scheduled time for this occurrence, based on the schedule expression.\n * Does not account for jitter. The dispatch may arrive later within the jitter window.\n */\n scheduledAt: Date;\n /** How the schedule was defined, such as `static`. */\n source: string;\n}\n\nexport type ScheduleHandler = (\n metadata: ScheduleMetadata,\n) => void | Promise<void>;\n\nexport class ScheduleEventParseError extends Error {\n cause?: unknown;\n\n constructor(message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = 'ScheduleEventParseError';\n if (options && 'cause' in options) {\n this.cause = options.cause;\n }\n }\n}\n\nfunction requiredHeader(request: Request, name: string): string {\n const value = request.headers.get(name);\n if (!value) {\n throw new ScheduleEventParseError(\n `Request is missing the ${name} header; it does not look like a schedule dispatch`,\n );\n }\n return value;\n}\n\n/** Parses schedule metadata from a binary-mode schedule CloudEvent. */\nexport async function parseScheduleEvent(\n request: Request,\n): Promise<ScheduleMetadata> {\n if (request.method !== 'POST') {\n throw new ScheduleEventParseError(\n `Schedule dispatches are POST requests, received ${request.method}`,\n );\n }\n\n const specVersion = requiredHeader(request, CLOUD_EVENT_SPEC_VERSION_HEADER);\n if (specVersion !== '1.0') {\n throw new ScheduleEventParseError(\n `Unsupported CloudEvent spec version: ${specVersion}`,\n );\n }\n\n const eventType = requiredHeader(request, CLOUD_EVENT_TYPE_HEADER);\n if (eventType !== SCHEDULE_CLOUD_EVENT_TYPE) {\n throw new ScheduleEventParseError(\n `Unsupported schedule CloudEvent type: ${eventType}`,\n );\n }\n\n requiredHeader(request, CLOUD_EVENT_SOURCE_HEADER);\n const executionId = requiredHeader(request, CLOUD_EVENT_ID_HEADER);\n const scheduledAtValue = requiredHeader(\n request,\n SCHEDULE_SCHEDULED_AT_HEADER,\n );\n const metadata: ScheduleMetadata = {\n scheduleId: requiredHeader(request, SCHEDULE_ID_HEADER),\n executionId,\n name: requiredHeader(request, SCHEDULE_NAME_HEADER),\n namespace: requiredHeader(request, SCHEDULE_NAMESPACE_HEADER),\n scheduledAt: new Date(scheduledAtValue),\n source: requiredHeader(request, SCHEDULE_SOURCE_HEADER),\n };\n\n if (Number.isNaN(metadata.scheduledAt.getTime())) {\n throw new ScheduleEventParseError(\n `Schedule scheduledAt is not a valid timestamp: ${scheduledAtValue}`,\n );\n }\n\n return metadata;\n}\n\n/**\n * Creates a web-standard route handler for schedule dispatches.\n *\n * ```ts\n * export const POST = handleSchedule(async (metadata) => {\n * console.log(metadata.name, metadata.scheduledAt);\n * });\n * ```\n */\nexport function handleSchedule(\n handler: ScheduleHandler,\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n let metadata: ScheduleMetadata;\n try {\n metadata = await parseScheduleEvent(request);\n } catch (error) {\n if (error instanceof ScheduleEventParseError) {\n return Response.json({ error: error.message }, { status: 400 });\n }\n return Response.json(\n { error: 'Failed to parse schedule dispatch' },\n { status: 500 },\n );\n }\n\n try {\n await handler(metadata);\n return new Response(null, { status: 200 });\n } catch {\n return Response.json(\n { error: 'Schedule handler failed' },\n { status: 500 },\n );\n }\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -8,26 +8,31 @@ declare class SchedulesApiError extends Error {
8
8
  constructor(status: number, message: string);
9
9
  }
10
10
  declare class SchedulesApiClient {
11
- private readonly baseUrl;
11
+ private readonly baseUrl?;
12
12
  private readonly token?;
13
13
  private readonly fetchImpl;
14
+ private readonly configurationError?;
14
15
  constructor(options?: SchedulesApiClientOptions);
15
16
  protected getToken(): Promise<string>;
16
17
  protected request<T>(method: string, path: string, body?: unknown): Promise<T>;
17
18
  }
18
19
 
19
- declare const DEFAULT_SCHEDULES_BASE_URL = "https://vss-server.vercel.sh";
20
+ declare const DEFAULT_SCHEDULES_BASE_URL = "https://vercel-schedules.com";
20
21
 
21
22
  type ScheduleExpression = {
22
23
  type: 'cron';
23
24
  cron: string;
24
25
  } | {
25
26
  type: 'single';
27
+ /** Datetime in YYYY-MM-DDTHH:mm or YYYY-MM-DDTHH:mm:00 format, with no timezone suffix. */
26
28
  at: string;
27
29
  };
28
30
  type ScheduleTarget = {
29
31
  type: 'queue';
30
32
  topic: string;
33
+ } | {
34
+ type: 'function';
35
+ function: string;
31
36
  };
32
37
  type CreateScheduleTarget = {
33
38
  topic: string;
@@ -42,37 +47,112 @@ interface Schedule {
42
47
  name: string;
43
48
  namespace: string;
44
49
  expression: ScheduleExpression;
50
+ /** IANA timezone used for cron expressions and one-time schedules. */
51
+ timezone: string;
45
52
  jitter?: number;
46
53
  target: ScheduleTarget;
47
54
  state: ScheduleState;
48
- stateOverride?: {
49
- state: ScheduleState;
50
- until: number;
51
- };
52
55
  source: ScheduleSource;
53
56
  createdAt: number;
54
57
  updatedAt: number;
55
58
  }
56
59
  interface CreateScheduleParams {
57
- name?: string;
60
+ name: string;
58
61
  namespace?: string;
59
62
  expression: ScheduleExpression;
63
+ /** IANA timezone for cron expressions and local one-time schedules. Defaults to UTC. */
64
+ timezone?: string;
60
65
  jitter?: number;
61
66
  target: CreateScheduleTarget;
67
+ payload?: unknown;
62
68
  }
63
- interface CreateScheduleResponse {
64
- scheduleId: string;
69
+ type CreateScheduleResponse = Schedule & {
70
+ target: Extract<ScheduleTarget, {
71
+ type: 'queue';
72
+ }>;
73
+ };
74
+ interface ScheduleIdentifier {
75
+ name: string;
76
+ namespace?: string;
65
77
  }
66
- interface DeleteScheduleResponse {
67
- scheduleId: string;
78
+ interface UpdateScheduleParams extends ScheduleIdentifier {
79
+ expression?: ScheduleExpression;
80
+ timezone?: string;
81
+ jitter?: number | null;
82
+ target?: CreateScheduleTarget;
83
+ payload?: unknown;
84
+ }
85
+ type UpdateScheduleResponse = Schedule;
86
+ type InvokeScheduleParams = ScheduleIdentifier;
87
+ interface ListSchedulesParams {
88
+ namespace?: string;
89
+ cursor?: string;
90
+ limit?: number;
91
+ }
92
+ interface ListSchedulesResponse {
93
+ data: Schedule[];
94
+ cursor: string | null;
68
95
  }
69
96
 
70
97
  declare class SchedulesClient extends SchedulesApiClient {
71
98
  create({ target, ...params }: CreateScheduleParams): Promise<CreateScheduleResponse>;
72
- get(scheduleId: string): Promise<Schedule>;
73
- delete(scheduleId: string): Promise<DeleteScheduleResponse>;
99
+ list(params?: ListSchedulesParams): Promise<ListSchedulesResponse>;
100
+ get({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
101
+ update({ name, namespace, target, ...params }: UpdateScheduleParams): Promise<UpdateScheduleResponse>;
102
+ delete({ name, namespace }: ScheduleIdentifier): Promise<void>;
103
+ enable({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
104
+ disable({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
105
+ invoke({ name, namespace }: InvokeScheduleParams): Promise<void>;
74
106
  }
75
107
 
76
108
  declare const schedules: SchedulesClient;
77
109
 
78
- export { type CreateScheduleParams, type CreateScheduleResponse, type CreateScheduleTarget, DEFAULT_SCHEDULES_BASE_URL, type DeleteScheduleResponse, type Schedule, type ScheduleExpression, type ScheduleSource, type ScheduleState, type ScheduleTarget, schedules as Schedules, type SchedulesApiClientOptions, SchedulesApiError, SchedulesClient };
110
+ /** CloudEvent type used for schedule function dispatches. */
111
+ declare const SCHEDULE_CLOUD_EVENT_TYPE = "com.vercel.schedule.v1beta";
112
+ declare const CLOUD_EVENT_SPEC_VERSION_HEADER = "ce-specversion";
113
+ declare const CLOUD_EVENT_TYPE_HEADER = "ce-type";
114
+ declare const CLOUD_EVENT_SOURCE_HEADER = "ce-source";
115
+ declare const CLOUD_EVENT_ID_HEADER = "ce-id";
116
+ declare const SCHEDULE_SCHEDULED_AT_HEADER = "ce-vssscheduledat";
117
+ declare const SCHEDULE_ID_HEADER = "ce-vssscheduleid";
118
+ declare const SCHEDULE_NAME_HEADER = "ce-vssschedulename";
119
+ declare const SCHEDULE_NAMESPACE_HEADER = "ce-vssnamespace";
120
+ declare const SCHEDULE_SOURCE_HEADER = "ce-vssschedulesource";
121
+ interface ScheduleMetadata {
122
+ /** Unique identifier of the schedule. */
123
+ scheduleId: string;
124
+ /** Unique identifier for this schedule occurrence. */
125
+ executionId: string;
126
+ /** Schedule name, unique within its project and namespace. */
127
+ name: string;
128
+ /** Namespace the schedule belongs to. */
129
+ namespace: string;
130
+ /**
131
+ * Nominal scheduled time for this occurrence, based on the schedule expression.
132
+ * Does not account for jitter. The dispatch may arrive later within the jitter window.
133
+ */
134
+ scheduledAt: Date;
135
+ /** How the schedule was defined, such as `static`. */
136
+ source: string;
137
+ }
138
+ type ScheduleHandler = (metadata: ScheduleMetadata) => void | Promise<void>;
139
+ declare class ScheduleEventParseError extends Error {
140
+ cause?: unknown;
141
+ constructor(message: string, options?: {
142
+ cause?: unknown;
143
+ });
144
+ }
145
+ /** Parses schedule metadata from a binary-mode schedule CloudEvent. */
146
+ declare function parseScheduleEvent(request: Request): Promise<ScheduleMetadata>;
147
+ /**
148
+ * Creates a web-standard route handler for schedule dispatches.
149
+ *
150
+ * ```ts
151
+ * export const POST = handleSchedule(async (metadata) => {
152
+ * console.log(metadata.name, metadata.scheduledAt);
153
+ * });
154
+ * ```
155
+ */
156
+ declare function handleSchedule(handler: ScheduleHandler): (request: Request) => Promise<Response>;
157
+
158
+ export { CLOUD_EVENT_ID_HEADER, CLOUD_EVENT_SOURCE_HEADER, CLOUD_EVENT_SPEC_VERSION_HEADER, CLOUD_EVENT_TYPE_HEADER, type CreateScheduleParams, type CreateScheduleResponse, type CreateScheduleTarget, DEFAULT_SCHEDULES_BASE_URL, type InvokeScheduleParams, type ListSchedulesParams, type ListSchedulesResponse, SCHEDULE_CLOUD_EVENT_TYPE, SCHEDULE_ID_HEADER, SCHEDULE_NAMESPACE_HEADER, SCHEDULE_NAME_HEADER, SCHEDULE_SCHEDULED_AT_HEADER, SCHEDULE_SOURCE_HEADER, type Schedule, ScheduleEventParseError, type ScheduleExpression, type ScheduleHandler, type ScheduleIdentifier, type ScheduleMetadata, type ScheduleSource, type ScheduleState, type ScheduleTarget, schedules as Schedules, type SchedulesApiClientOptions, SchedulesApiError, SchedulesClient, type UpdateScheduleParams, type UpdateScheduleResponse, handleSchedule, parseScheduleEvent };
package/dist/index.d.ts CHANGED
@@ -8,26 +8,31 @@ declare class SchedulesApiError extends Error {
8
8
  constructor(status: number, message: string);
9
9
  }
10
10
  declare class SchedulesApiClient {
11
- private readonly baseUrl;
11
+ private readonly baseUrl?;
12
12
  private readonly token?;
13
13
  private readonly fetchImpl;
14
+ private readonly configurationError?;
14
15
  constructor(options?: SchedulesApiClientOptions);
15
16
  protected getToken(): Promise<string>;
16
17
  protected request<T>(method: string, path: string, body?: unknown): Promise<T>;
17
18
  }
18
19
 
19
- declare const DEFAULT_SCHEDULES_BASE_URL = "https://vss-server.vercel.sh";
20
+ declare const DEFAULT_SCHEDULES_BASE_URL = "https://vercel-schedules.com";
20
21
 
21
22
  type ScheduleExpression = {
22
23
  type: 'cron';
23
24
  cron: string;
24
25
  } | {
25
26
  type: 'single';
27
+ /** Datetime in YYYY-MM-DDTHH:mm or YYYY-MM-DDTHH:mm:00 format, with no timezone suffix. */
26
28
  at: string;
27
29
  };
28
30
  type ScheduleTarget = {
29
31
  type: 'queue';
30
32
  topic: string;
33
+ } | {
34
+ type: 'function';
35
+ function: string;
31
36
  };
32
37
  type CreateScheduleTarget = {
33
38
  topic: string;
@@ -42,37 +47,112 @@ interface Schedule {
42
47
  name: string;
43
48
  namespace: string;
44
49
  expression: ScheduleExpression;
50
+ /** IANA timezone used for cron expressions and one-time schedules. */
51
+ timezone: string;
45
52
  jitter?: number;
46
53
  target: ScheduleTarget;
47
54
  state: ScheduleState;
48
- stateOverride?: {
49
- state: ScheduleState;
50
- until: number;
51
- };
52
55
  source: ScheduleSource;
53
56
  createdAt: number;
54
57
  updatedAt: number;
55
58
  }
56
59
  interface CreateScheduleParams {
57
- name?: string;
60
+ name: string;
58
61
  namespace?: string;
59
62
  expression: ScheduleExpression;
63
+ /** IANA timezone for cron expressions and local one-time schedules. Defaults to UTC. */
64
+ timezone?: string;
60
65
  jitter?: number;
61
66
  target: CreateScheduleTarget;
67
+ payload?: unknown;
62
68
  }
63
- interface CreateScheduleResponse {
64
- scheduleId: string;
69
+ type CreateScheduleResponse = Schedule & {
70
+ target: Extract<ScheduleTarget, {
71
+ type: 'queue';
72
+ }>;
73
+ };
74
+ interface ScheduleIdentifier {
75
+ name: string;
76
+ namespace?: string;
65
77
  }
66
- interface DeleteScheduleResponse {
67
- scheduleId: string;
78
+ interface UpdateScheduleParams extends ScheduleIdentifier {
79
+ expression?: ScheduleExpression;
80
+ timezone?: string;
81
+ jitter?: number | null;
82
+ target?: CreateScheduleTarget;
83
+ payload?: unknown;
84
+ }
85
+ type UpdateScheduleResponse = Schedule;
86
+ type InvokeScheduleParams = ScheduleIdentifier;
87
+ interface ListSchedulesParams {
88
+ namespace?: string;
89
+ cursor?: string;
90
+ limit?: number;
91
+ }
92
+ interface ListSchedulesResponse {
93
+ data: Schedule[];
94
+ cursor: string | null;
68
95
  }
69
96
 
70
97
  declare class SchedulesClient extends SchedulesApiClient {
71
98
  create({ target, ...params }: CreateScheduleParams): Promise<CreateScheduleResponse>;
72
- get(scheduleId: string): Promise<Schedule>;
73
- delete(scheduleId: string): Promise<DeleteScheduleResponse>;
99
+ list(params?: ListSchedulesParams): Promise<ListSchedulesResponse>;
100
+ get({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
101
+ update({ name, namespace, target, ...params }: UpdateScheduleParams): Promise<UpdateScheduleResponse>;
102
+ delete({ name, namespace }: ScheduleIdentifier): Promise<void>;
103
+ enable({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
104
+ disable({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
105
+ invoke({ name, namespace }: InvokeScheduleParams): Promise<void>;
74
106
  }
75
107
 
76
108
  declare const schedules: SchedulesClient;
77
109
 
78
- export { type CreateScheduleParams, type CreateScheduleResponse, type CreateScheduleTarget, DEFAULT_SCHEDULES_BASE_URL, type DeleteScheduleResponse, type Schedule, type ScheduleExpression, type ScheduleSource, type ScheduleState, type ScheduleTarget, schedules as Schedules, type SchedulesApiClientOptions, SchedulesApiError, SchedulesClient };
110
+ /** CloudEvent type used for schedule function dispatches. */
111
+ declare const SCHEDULE_CLOUD_EVENT_TYPE = "com.vercel.schedule.v1beta";
112
+ declare const CLOUD_EVENT_SPEC_VERSION_HEADER = "ce-specversion";
113
+ declare const CLOUD_EVENT_TYPE_HEADER = "ce-type";
114
+ declare const CLOUD_EVENT_SOURCE_HEADER = "ce-source";
115
+ declare const CLOUD_EVENT_ID_HEADER = "ce-id";
116
+ declare const SCHEDULE_SCHEDULED_AT_HEADER = "ce-vssscheduledat";
117
+ declare const SCHEDULE_ID_HEADER = "ce-vssscheduleid";
118
+ declare const SCHEDULE_NAME_HEADER = "ce-vssschedulename";
119
+ declare const SCHEDULE_NAMESPACE_HEADER = "ce-vssnamespace";
120
+ declare const SCHEDULE_SOURCE_HEADER = "ce-vssschedulesource";
121
+ interface ScheduleMetadata {
122
+ /** Unique identifier of the schedule. */
123
+ scheduleId: string;
124
+ /** Unique identifier for this schedule occurrence. */
125
+ executionId: string;
126
+ /** Schedule name, unique within its project and namespace. */
127
+ name: string;
128
+ /** Namespace the schedule belongs to. */
129
+ namespace: string;
130
+ /**
131
+ * Nominal scheduled time for this occurrence, based on the schedule expression.
132
+ * Does not account for jitter. The dispatch may arrive later within the jitter window.
133
+ */
134
+ scheduledAt: Date;
135
+ /** How the schedule was defined, such as `static`. */
136
+ source: string;
137
+ }
138
+ type ScheduleHandler = (metadata: ScheduleMetadata) => void | Promise<void>;
139
+ declare class ScheduleEventParseError extends Error {
140
+ cause?: unknown;
141
+ constructor(message: string, options?: {
142
+ cause?: unknown;
143
+ });
144
+ }
145
+ /** Parses schedule metadata from a binary-mode schedule CloudEvent. */
146
+ declare function parseScheduleEvent(request: Request): Promise<ScheduleMetadata>;
147
+ /**
148
+ * Creates a web-standard route handler for schedule dispatches.
149
+ *
150
+ * ```ts
151
+ * export const POST = handleSchedule(async (metadata) => {
152
+ * console.log(metadata.name, metadata.scheduledAt);
153
+ * });
154
+ * ```
155
+ */
156
+ declare function handleSchedule(handler: ScheduleHandler): (request: Request) => Promise<Response>;
157
+
158
+ export { CLOUD_EVENT_ID_HEADER, CLOUD_EVENT_SOURCE_HEADER, CLOUD_EVENT_SPEC_VERSION_HEADER, CLOUD_EVENT_TYPE_HEADER, type CreateScheduleParams, type CreateScheduleResponse, type CreateScheduleTarget, DEFAULT_SCHEDULES_BASE_URL, type InvokeScheduleParams, type ListSchedulesParams, type ListSchedulesResponse, SCHEDULE_CLOUD_EVENT_TYPE, SCHEDULE_ID_HEADER, SCHEDULE_NAMESPACE_HEADER, SCHEDULE_NAME_HEADER, SCHEDULE_SCHEDULED_AT_HEADER, SCHEDULE_SOURCE_HEADER, type Schedule, ScheduleEventParseError, type ScheduleExpression, type ScheduleHandler, type ScheduleIdentifier, type ScheduleMetadata, type ScheduleSource, type ScheduleState, type ScheduleTarget, schedules as Schedules, type SchedulesApiClientOptions, SchedulesApiError, SchedulesClient, type UpdateScheduleParams, type UpdateScheduleResponse, handleSchedule, parseScheduleEvent };
package/dist/index.js CHANGED
@@ -2,9 +2,16 @@
2
2
  import { getVercelOidcToken } from "@vercel/oidc";
3
3
 
4
4
  // src/constants.ts
5
- var DEFAULT_SCHEDULES_BASE_URL = "https://vss-server.vercel.sh";
5
+ var DEFAULT_SCHEDULES_BASE_URL = "https://vercel-schedules.com";
6
6
 
7
7
  // src/api-client.ts
8
+ function trimTrailingSlashes(path) {
9
+ let end = path.length;
10
+ while (end > 0 && path[end - 1] === "/") {
11
+ end--;
12
+ }
13
+ return path.slice(0, end);
14
+ }
8
15
  var SchedulesApiError = class extends Error {
9
16
  constructor(status, message) {
10
17
  super(message);
@@ -14,14 +21,50 @@ var SchedulesApiError = class extends Error {
14
21
  };
15
22
  var SchedulesApiClient = class {
16
23
  constructor(options = {}) {
17
- var _a, _b, _c;
24
+ var _a, _b, _c, _d, _e;
25
+ this.token = options.token;
26
+ this.fetchImpl = (_a = options.fetch) != null ? _a : fetch;
27
+ const envBaseUrl = process.env.VERCEL_SCHEDULE_BASE_URL;
28
+ const devApiVersion = process.env.VERCEL_SCHEDULE_DEV_API_VERSION;
29
+ let advertisedBaseUrl;
30
+ try {
31
+ advertisedBaseUrl = new URL(envBaseUrl != null ? envBaseUrl : "");
32
+ } catch {
33
+ }
34
+ const explicitBaseUrl = options.baseUrl === void 0 ? void 0 : new URL(options.baseUrl);
35
+ const localSelected = process.env.NODE_ENV === "development" && process.env.VERCEL_DEPLOYMENT_ID === void 0 && devApiVersion !== void 0 && (explicitBaseUrl === void 0 || advertisedBaseUrl !== void 0 && explicitBaseUrl.origin === advertisedBaseUrl.origin && trimTrailingSlashes(explicitBaseUrl.pathname) === trimTrailingSlashes(advertisedBaseUrl.pathname));
36
+ if (localSelected) {
37
+ try {
38
+ if (devApiVersion !== "1") {
39
+ throw new Error(
40
+ 'Unsupported VERCEL_SCHEDULE_DEV_API_VERSION. This SDK supports local Schedules API version "1".'
41
+ );
42
+ }
43
+ if (!advertisedBaseUrl || !["http:", "https:"].includes(advertisedBaseUrl.protocol) || advertisedBaseUrl.username || advertisedBaseUrl.password || advertisedBaseUrl.search || advertisedBaseUrl.hash) {
44
+ throw new Error(
45
+ "Local Schedules requires VERCEL_SCHEDULE_BASE_URL to be an absolute HTTP(S) URL without credentials, a query, or a fragment."
46
+ );
47
+ }
48
+ this.token = (_b = options.token) != null ? _b : process.env.VERCEL_SCHEDULE_TOKEN;
49
+ if (!((_c = this.token) == null ? void 0 : _c.trim())) {
50
+ throw new Error(
51
+ "Local Schedules requires VERCEL_SCHEDULE_TOKEN or an explicit token option."
52
+ );
53
+ }
54
+ } catch (err) {
55
+ this.configurationError = err instanceof Error ? err : new Error(String(err));
56
+ return;
57
+ }
58
+ }
18
59
  this.baseUrl = new URL(
19
- (_b = (_a = options.baseUrl) != null ? _a : process.env.VERCEL_SCHEDULE_BASE_URL) != null ? _b : DEFAULT_SCHEDULES_BASE_URL
60
+ (_e = (_d = options.baseUrl) != null ? _d : envBaseUrl) != null ? _e : DEFAULT_SCHEDULES_BASE_URL
20
61
  );
21
- this.token = options.token;
22
- this.fetchImpl = (_c = options.fetch) != null ? _c : fetch;
62
+ this.baseUrl.pathname = `${trimTrailingSlashes(this.baseUrl.pathname)}/`;
23
63
  }
24
64
  async getToken() {
65
+ if (this.configurationError) {
66
+ throw this.configurationError;
67
+ }
25
68
  if (this.token) {
26
69
  return this.token;
27
70
  }
@@ -53,11 +96,14 @@ Cause: ${cause}`
53
96
  if (body !== void 0) {
54
97
  headers.set("Content-Type", "application/json");
55
98
  }
56
- const response = await this.fetchImpl(new URL(path, this.baseUrl), {
57
- method,
58
- headers,
59
- body: body === void 0 ? void 0 : JSON.stringify(body)
60
- });
99
+ const response = await this.fetchImpl(
100
+ new URL(path.replace(/^\/+/, ""), this.baseUrl),
101
+ {
102
+ method,
103
+ headers,
104
+ body: body === void 0 ? void 0 : JSON.stringify(body)
105
+ }
106
+ );
61
107
  if (!response.ok) {
62
108
  const text = await response.text();
63
109
  throw new SchedulesApiError(response.status, text || response.statusText);
@@ -70,6 +116,15 @@ Cause: ${cause}`
70
116
  };
71
117
 
72
118
  // src/schedules.ts
119
+ function schedulePath(name, namespace, suffix = "") {
120
+ const searchParams = new URLSearchParams();
121
+ if (namespace !== void 0) {
122
+ searchParams.set("namespace", namespace);
123
+ }
124
+ const query = searchParams.toString();
125
+ const path = `/v1/schedules/${encodeURIComponent(name)}${suffix}`;
126
+ return query ? `${path}?${query}` : path;
127
+ }
73
128
  var SchedulesClient = class extends SchedulesApiClient {
74
129
  create({
75
130
  target,
@@ -83,26 +138,167 @@ var SchedulesClient = class extends SchedulesApiClient {
83
138
  }
84
139
  });
85
140
  }
86
- get(scheduleId) {
87
- return this.request(
88
- "GET",
89
- `/v1/schedules/${encodeURIComponent(scheduleId)}`
90
- );
141
+ list(params = {}) {
142
+ const searchParams = new URLSearchParams();
143
+ if (params.namespace !== void 0) {
144
+ searchParams.set("namespace", params.namespace);
145
+ }
146
+ if (params.cursor !== void 0) {
147
+ searchParams.set("cursor", params.cursor);
148
+ }
149
+ if (params.limit !== void 0) {
150
+ searchParams.set("limit", String(params.limit));
151
+ }
152
+ const query = searchParams.toString();
153
+ const path = query ? `/v1/schedules?${query}` : "/v1/schedules";
154
+ return this.request("GET", path);
91
155
  }
92
- delete(scheduleId) {
93
- return this.request(
94
- "DELETE",
95
- `/v1/schedules/${encodeURIComponent(scheduleId)}`
96
- );
156
+ get({ name, namespace }) {
157
+ return this.request("GET", schedulePath(name, namespace));
158
+ }
159
+ update({
160
+ name,
161
+ namespace,
162
+ target,
163
+ ...params
164
+ }) {
165
+ return this.request("PATCH", schedulePath(name, namespace), {
166
+ ...params,
167
+ ...target === void 0 ? {} : {
168
+ target: {
169
+ type: "queue",
170
+ topic: target.topic
171
+ }
172
+ }
173
+ });
174
+ }
175
+ async delete({ name, namespace }) {
176
+ await this.request("DELETE", schedulePath(name, namespace));
177
+ }
178
+ enable({ name, namespace }) {
179
+ return this.request("POST", schedulePath(name, namespace, "/enable"));
180
+ }
181
+ disable({ name, namespace }) {
182
+ return this.request("POST", schedulePath(name, namespace, "/disable"));
183
+ }
184
+ async invoke({ name, namespace }) {
185
+ await this.request("POST", schedulePath(name, namespace, "/invoke"));
97
186
  }
98
187
  };
99
188
 
100
189
  // src/default-client.ts
101
190
  var schedules = new SchedulesClient();
191
+
192
+ // src/schedule-event.ts
193
+ var SCHEDULE_CLOUD_EVENT_TYPE = "com.vercel.schedule.v1beta";
194
+ var CLOUD_EVENT_SPEC_VERSION_HEADER = "ce-specversion";
195
+ var CLOUD_EVENT_TYPE_HEADER = "ce-type";
196
+ var CLOUD_EVENT_SOURCE_HEADER = "ce-source";
197
+ var CLOUD_EVENT_ID_HEADER = "ce-id";
198
+ var SCHEDULE_SCHEDULED_AT_HEADER = "ce-vssscheduledat";
199
+ var SCHEDULE_ID_HEADER = "ce-vssscheduleid";
200
+ var SCHEDULE_NAME_HEADER = "ce-vssschedulename";
201
+ var SCHEDULE_NAMESPACE_HEADER = "ce-vssnamespace";
202
+ var SCHEDULE_SOURCE_HEADER = "ce-vssschedulesource";
203
+ var ScheduleEventParseError = class extends Error {
204
+ constructor(message, options) {
205
+ super(message);
206
+ this.name = "ScheduleEventParseError";
207
+ if (options && "cause" in options) {
208
+ this.cause = options.cause;
209
+ }
210
+ }
211
+ };
212
+ function requiredHeader(request, name) {
213
+ const value = request.headers.get(name);
214
+ if (!value) {
215
+ throw new ScheduleEventParseError(
216
+ `Request is missing the ${name} header; it does not look like a schedule dispatch`
217
+ );
218
+ }
219
+ return value;
220
+ }
221
+ async function parseScheduleEvent(request) {
222
+ if (request.method !== "POST") {
223
+ throw new ScheduleEventParseError(
224
+ `Schedule dispatches are POST requests, received ${request.method}`
225
+ );
226
+ }
227
+ const specVersion = requiredHeader(request, CLOUD_EVENT_SPEC_VERSION_HEADER);
228
+ if (specVersion !== "1.0") {
229
+ throw new ScheduleEventParseError(
230
+ `Unsupported CloudEvent spec version: ${specVersion}`
231
+ );
232
+ }
233
+ const eventType = requiredHeader(request, CLOUD_EVENT_TYPE_HEADER);
234
+ if (eventType !== SCHEDULE_CLOUD_EVENT_TYPE) {
235
+ throw new ScheduleEventParseError(
236
+ `Unsupported schedule CloudEvent type: ${eventType}`
237
+ );
238
+ }
239
+ requiredHeader(request, CLOUD_EVENT_SOURCE_HEADER);
240
+ const executionId = requiredHeader(request, CLOUD_EVENT_ID_HEADER);
241
+ const scheduledAtValue = requiredHeader(
242
+ request,
243
+ SCHEDULE_SCHEDULED_AT_HEADER
244
+ );
245
+ const metadata = {
246
+ scheduleId: requiredHeader(request, SCHEDULE_ID_HEADER),
247
+ executionId,
248
+ name: requiredHeader(request, SCHEDULE_NAME_HEADER),
249
+ namespace: requiredHeader(request, SCHEDULE_NAMESPACE_HEADER),
250
+ scheduledAt: new Date(scheduledAtValue),
251
+ source: requiredHeader(request, SCHEDULE_SOURCE_HEADER)
252
+ };
253
+ if (Number.isNaN(metadata.scheduledAt.getTime())) {
254
+ throw new ScheduleEventParseError(
255
+ `Schedule scheduledAt is not a valid timestamp: ${scheduledAtValue}`
256
+ );
257
+ }
258
+ return metadata;
259
+ }
260
+ function handleSchedule(handler) {
261
+ return async (request) => {
262
+ let metadata;
263
+ try {
264
+ metadata = await parseScheduleEvent(request);
265
+ } catch (error) {
266
+ if (error instanceof ScheduleEventParseError) {
267
+ return Response.json({ error: error.message }, { status: 400 });
268
+ }
269
+ return Response.json(
270
+ { error: "Failed to parse schedule dispatch" },
271
+ { status: 500 }
272
+ );
273
+ }
274
+ try {
275
+ await handler(metadata);
276
+ return new Response(null, { status: 200 });
277
+ } catch {
278
+ return Response.json(
279
+ { error: "Schedule handler failed" },
280
+ { status: 500 }
281
+ );
282
+ }
283
+ };
284
+ }
102
285
  export {
286
+ CLOUD_EVENT_ID_HEADER,
287
+ CLOUD_EVENT_SOURCE_HEADER,
288
+ CLOUD_EVENT_SPEC_VERSION_HEADER,
289
+ CLOUD_EVENT_TYPE_HEADER,
103
290
  DEFAULT_SCHEDULES_BASE_URL,
291
+ SCHEDULE_CLOUD_EVENT_TYPE,
292
+ SCHEDULE_ID_HEADER,
293
+ SCHEDULE_NAMESPACE_HEADER,
294
+ SCHEDULE_NAME_HEADER,
295
+ SCHEDULE_SCHEDULED_AT_HEADER,
296
+ SCHEDULE_SOURCE_HEADER,
297
+ ScheduleEventParseError,
104
298
  schedules as Schedules,
105
299
  SchedulesApiError,
106
- SchedulesClient
300
+ SchedulesClient,
301
+ handleSchedule,
302
+ parseScheduleEvent
107
303
  };
108
304
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api-client.ts","../src/constants.ts","../src/schedules.ts","../src/default-client.ts"],"sourcesContent":["import { getVercelOidcToken } from '@vercel/oidc';\nimport { DEFAULT_SCHEDULES_BASE_URL } from './constants';\n\nexport interface SchedulesApiClientOptions {\n baseUrl?: string;\n token?: string;\n fetch?: typeof fetch;\n}\n\nexport class SchedulesApiError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = 'SchedulesApiError';\n this.status = status;\n }\n}\n\nexport class SchedulesApiClient {\n private readonly baseUrl: URL;\n private readonly token?: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: SchedulesApiClientOptions = {}) {\n this.baseUrl = new URL(\n options.baseUrl ??\n process.env.VERCEL_SCHEDULE_BASE_URL ??\n DEFAULT_SCHEDULES_BASE_URL,\n );\n this.token = options.token;\n this.fetchImpl = options.fetch ?? fetch;\n }\n\n protected async getToken(): Promise<string> {\n if (this.token) {\n return this.token;\n }\n\n try {\n return await getVercelOidcToken();\n } catch (err) {\n const cause = err instanceof Error ? err.message : String(err);\n const isDev = process.env.NODE_ENV === 'development';\n\n throw new Error(\n isDev\n ? 'Failed to get OIDC token for local development.\\n\\n' +\n 'To fix this, pull your environment variables with Vercel CLI:\\n' +\n ' `vercel env pull`\\n\\n' +\n `Cause: ${cause}`\n : 'Failed to get OIDC token. This usually means the function is running ' +\n 'outside of a Vercel Function environment.\\n\\n' +\n 'To fix this, either:\\n' +\n ' - Deploy to Vercel (OIDC tokens are provisioned automatically)\\n' +\n ' - Provide a token explicitly: `new Schedules({ token: \"...\" })`\\n\\n' +\n `Cause: ${cause}`,\n );\n }\n }\n\n protected async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const headers = new Headers({\n Authorization: `Bearer ${await this.getToken()}`,\n });\n\n if (body !== undefined) {\n headers.set('Content-Type', 'application/json');\n }\n\n const response = await this.fetchImpl(new URL(path, this.baseUrl), {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new SchedulesApiError(response.status, text || response.statusText);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n }\n}\n","export const DEFAULT_SCHEDULES_BASE_URL = 'https://vss-server.vercel.sh';\n","import { SchedulesApiClient } from './api-client';\nimport type {\n CreateScheduleParams,\n CreateScheduleResponse,\n DeleteScheduleResponse,\n Schedule,\n} from './types';\n\nexport class SchedulesClient extends SchedulesApiClient {\n create({\n target,\n ...params\n }: CreateScheduleParams): Promise<CreateScheduleResponse> {\n return this.request('POST', '/v1/schedules', {\n ...params,\n target: {\n type: 'queue',\n topic: target.topic,\n },\n });\n }\n\n get(scheduleId: string): Promise<Schedule> {\n return this.request(\n 'GET',\n `/v1/schedules/${encodeURIComponent(scheduleId)}`,\n );\n }\n\n delete(scheduleId: string): Promise<DeleteScheduleResponse> {\n return this.request(\n 'DELETE',\n `/v1/schedules/${encodeURIComponent(scheduleId)}`,\n );\n }\n}\n","import { SchedulesClient } from './schedules';\n\nexport const schedules = new SchedulesClient();\n"],"mappings":";AAAA,SAAS,0BAA0B;;;ACA5B,IAAM,6BAA6B;;;ADSnC,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAG3C,YAAY,QAAgB,SAAiB;AAC3C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAEO,IAAM,qBAAN,MAAyB;AAAA,EAK9B,YAAY,UAAqC,CAAC,GAAG;AAxBvD;AAyBI,SAAK,UAAU,IAAI;AAAA,OACjB,mBAAQ,YAAR,YACE,QAAQ,IAAI,6BADd,YAEE;AAAA,IACJ;AACA,SAAK,QAAQ,QAAQ;AACrB,SAAK,aAAY,aAAQ,UAAR,YAAiB;AAAA,EACpC;AAAA,EAEA,MAAgB,WAA4B;AAC1C,QAAI,KAAK,OAAO;AACd,aAAO,KAAK;AAAA,IACd;AAEA,QAAI;AACF,aAAO,MAAM,mBAAmB;AAAA,IAClC,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC7D,YAAM,QAAQ,QAAQ,IAAI,aAAa;AAEvC,YAAM,IAAI;AAAA,QACR,QACI;AAAA;AAAA;AAAA;AAAA;AAAA,SAGY,KAAK,KACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAKY,KAAK;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAgB,QACd,QACA,MACA,MACY;AACZ,UAAM,UAAU,IAAI,QAAQ;AAAA,MAC1B,eAAe,UAAU,MAAM,KAAK,SAAS,CAAC;AAAA,IAChD,CAAC;AAED,QAAI,SAAS,QAAW;AACtB,cAAQ,IAAI,gBAAgB,kBAAkB;AAAA,IAChD;AAEA,UAAM,WAAW,MAAM,KAAK,UAAU,IAAI,IAAI,MAAM,KAAK,OAAO,GAAG;AAAA,MACjE;AAAA,MACA;AAAA,MACA,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,IAC5D,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,kBAAkB,SAAS,QAAQ,QAAQ,SAAS,UAAU;AAAA,IAC1E;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AACF;;;AEnFO,IAAM,kBAAN,cAA8B,mBAAmB;AAAA,EACtD,OAAO;AAAA,IACL;AAAA,IACA,GAAG;AAAA,EACL,GAA0D;AACxD,WAAO,KAAK,QAAQ,QAAQ,iBAAiB;AAAA,MAC3C,GAAG;AAAA,MACH,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,OAAO,OAAO;AAAA,MAChB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,YAAuC;AACzC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,mBAAmB,UAAU,CAAC;AAAA,IACjD;AAAA,EACF;AAAA,EAEA,OAAO,YAAqD;AAC1D,WAAO,KAAK;AAAA,MACV;AAAA,MACA,iBAAiB,mBAAmB,UAAU,CAAC;AAAA,IACjD;AAAA,EACF;AACF;;;ACjCO,IAAM,YAAY,IAAI,gBAAgB;","names":[]}
1
+ {"version":3,"sources":["../src/api-client.ts","../src/constants.ts","../src/schedules.ts","../src/default-client.ts","../src/schedule-event.ts"],"sourcesContent":["import { getVercelOidcToken } from '@vercel/oidc';\nimport { DEFAULT_SCHEDULES_BASE_URL } from './constants';\n\nexport interface SchedulesApiClientOptions {\n baseUrl?: string;\n token?: string;\n fetch?: typeof fetch;\n}\n\nfunction trimTrailingSlashes(path: string): string {\n let end = path.length;\n while (end > 0 && path[end - 1] === '/') {\n end--;\n }\n return path.slice(0, end);\n}\n\nexport class SchedulesApiError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.name = 'SchedulesApiError';\n this.status = status;\n }\n}\n\nexport class SchedulesApiClient {\n private readonly baseUrl?: URL;\n private readonly token?: string;\n private readonly fetchImpl: typeof fetch;\n private readonly configurationError?: Error;\n\n constructor(options: SchedulesApiClientOptions = {}) {\n this.token = options.token;\n this.fetchImpl = options.fetch ?? fetch;\n\n const envBaseUrl = process.env.VERCEL_SCHEDULE_BASE_URL;\n const devApiVersion = process.env.VERCEL_SCHEDULE_DEV_API_VERSION;\n let advertisedBaseUrl: URL | undefined;\n try {\n advertisedBaseUrl = new URL(envBaseUrl ?? '');\n } catch {\n // An independent explicit endpoint does not need the injected URL.\n }\n\n const explicitBaseUrl =\n options.baseUrl === undefined ? undefined : new URL(options.baseUrl);\n const localSelected =\n process.env.NODE_ENV === 'development' &&\n process.env.VERCEL_DEPLOYMENT_ID === undefined &&\n devApiVersion !== undefined &&\n (explicitBaseUrl === undefined ||\n (advertisedBaseUrl !== undefined &&\n explicitBaseUrl.origin === advertisedBaseUrl.origin &&\n trimTrailingSlashes(explicitBaseUrl.pathname) ===\n trimTrailingSlashes(advertisedBaseUrl.pathname)));\n\n if (localSelected) {\n try {\n if (devApiVersion !== '1') {\n throw new Error(\n 'Unsupported VERCEL_SCHEDULE_DEV_API_VERSION. This SDK supports local Schedules API version \"1\".',\n );\n }\n if (\n !advertisedBaseUrl ||\n !['http:', 'https:'].includes(advertisedBaseUrl.protocol) ||\n advertisedBaseUrl.username ||\n advertisedBaseUrl.password ||\n advertisedBaseUrl.search ||\n advertisedBaseUrl.hash\n ) {\n throw new Error(\n 'Local Schedules requires VERCEL_SCHEDULE_BASE_URL to be an absolute HTTP(S) URL without credentials, a query, or a fragment.',\n );\n }\n this.token = options.token ?? process.env.VERCEL_SCHEDULE_TOKEN;\n if (!this.token?.trim()) {\n throw new Error(\n 'Local Schedules requires VERCEL_SCHEDULE_TOKEN or an explicit token option.',\n );\n }\n } catch (err) {\n // Defer only selected-local configuration errors so the default\n // singleton does not prevent importing independent clients.\n this.configurationError =\n err instanceof Error ? err : new Error(String(err));\n return;\n }\n }\n\n this.baseUrl = new URL(\n options.baseUrl ?? envBaseUrl ?? DEFAULT_SCHEDULES_BASE_URL,\n );\n this.baseUrl.pathname = `${trimTrailingSlashes(this.baseUrl.pathname)}/`;\n }\n\n protected async getToken(): Promise<string> {\n if (this.configurationError) {\n throw this.configurationError;\n }\n if (this.token) {\n return this.token;\n }\n\n try {\n return await getVercelOidcToken();\n } catch (err) {\n const cause = err instanceof Error ? err.message : String(err);\n const isDev = process.env.NODE_ENV === 'development';\n\n throw new Error(\n isDev\n ? 'Failed to get OIDC token for local development.\\n\\n' +\n 'To fix this, pull your environment variables with Vercel CLI:\\n' +\n ' `vercel env pull`\\n\\n' +\n `Cause: ${cause}`\n : 'Failed to get OIDC token. This usually means the function is running ' +\n 'outside of a Vercel Function environment.\\n\\n' +\n 'To fix this, either:\\n' +\n ' - Deploy to Vercel (OIDC tokens are provisioned automatically)\\n' +\n ' - Provide a token explicitly: `new Schedules({ token: \"...\" })`\\n\\n' +\n `Cause: ${cause}`,\n );\n }\n }\n\n protected async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const headers = new Headers({\n Authorization: `Bearer ${await this.getToken()}`,\n });\n\n if (body !== undefined) {\n headers.set('Content-Type', 'application/json');\n }\n\n const response = await this.fetchImpl(\n new URL(path.replace(/^\\/+/, ''), this.baseUrl),\n {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new SchedulesApiError(response.status, text || response.statusText);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n }\n}\n","export const DEFAULT_SCHEDULES_BASE_URL = 'https://vercel-schedules.com';\n","import { SchedulesApiClient } from './api-client';\nimport type {\n CreateScheduleParams,\n CreateScheduleResponse,\n InvokeScheduleParams,\n ListSchedulesParams,\n ListSchedulesResponse,\n Schedule,\n ScheduleIdentifier,\n UpdateScheduleParams,\n UpdateScheduleResponse,\n} from './types';\n\nfunction schedulePath(name: string, namespace?: string, suffix = ''): string {\n const searchParams = new URLSearchParams();\n\n if (namespace !== undefined) {\n searchParams.set('namespace', namespace);\n }\n\n const query = searchParams.toString();\n const path = `/v1/schedules/${encodeURIComponent(name)}${suffix}`;\n\n return query ? `${path}?${query}` : path;\n}\n\nexport class SchedulesClient extends SchedulesApiClient {\n create({\n target,\n ...params\n }: CreateScheduleParams): Promise<CreateScheduleResponse> {\n return this.request('POST', '/v1/schedules', {\n ...params,\n target: {\n type: 'queue',\n topic: target.topic,\n },\n });\n }\n\n list(params: ListSchedulesParams = {}): Promise<ListSchedulesResponse> {\n const searchParams = new URLSearchParams();\n\n if (params.namespace !== undefined) {\n searchParams.set('namespace', params.namespace);\n }\n if (params.cursor !== undefined) {\n searchParams.set('cursor', params.cursor);\n }\n if (params.limit !== undefined) {\n searchParams.set('limit', String(params.limit));\n }\n\n const query = searchParams.toString();\n const path = query ? `/v1/schedules?${query}` : '/v1/schedules';\n\n return this.request('GET', path);\n }\n\n get({ name, namespace }: ScheduleIdentifier): Promise<Schedule> {\n return this.request('GET', schedulePath(name, namespace));\n }\n\n update({\n name,\n namespace,\n target,\n ...params\n }: UpdateScheduleParams): Promise<UpdateScheduleResponse> {\n return this.request('PATCH', schedulePath(name, namespace), {\n ...params,\n ...(target === undefined\n ? {}\n : {\n target: {\n type: 'queue',\n topic: target.topic,\n },\n }),\n });\n }\n\n async delete({ name, namespace }: ScheduleIdentifier): Promise<void> {\n await this.request('DELETE', schedulePath(name, namespace));\n }\n\n enable({ name, namespace }: ScheduleIdentifier): Promise<Schedule> {\n return this.request('POST', schedulePath(name, namespace, '/enable'));\n }\n\n disable({ name, namespace }: ScheduleIdentifier): Promise<Schedule> {\n return this.request('POST', schedulePath(name, namespace, '/disable'));\n }\n\n async invoke({ name, namespace }: InvokeScheduleParams): Promise<void> {\n await this.request('POST', schedulePath(name, namespace, '/invoke'));\n }\n}\n","import { SchedulesClient } from './schedules';\n\nexport const schedules = new SchedulesClient();\n","/** CloudEvent type used for schedule function dispatches. */\nexport const SCHEDULE_CLOUD_EVENT_TYPE = 'com.vercel.schedule.v1beta';\nexport const CLOUD_EVENT_SPEC_VERSION_HEADER = 'ce-specversion';\nexport const CLOUD_EVENT_TYPE_HEADER = 'ce-type';\nexport const CLOUD_EVENT_SOURCE_HEADER = 'ce-source';\nexport const CLOUD_EVENT_ID_HEADER = 'ce-id';\nexport const SCHEDULE_SCHEDULED_AT_HEADER = 'ce-vssscheduledat';\nexport const SCHEDULE_ID_HEADER = 'ce-vssscheduleid';\nexport const SCHEDULE_NAME_HEADER = 'ce-vssschedulename';\nexport const SCHEDULE_NAMESPACE_HEADER = 'ce-vssnamespace';\nexport const SCHEDULE_SOURCE_HEADER = 'ce-vssschedulesource';\n\nexport interface ScheduleMetadata {\n /** Unique identifier of the schedule. */\n scheduleId: string;\n /** Unique identifier for this schedule occurrence. */\n executionId: string;\n /** Schedule name, unique within its project and namespace. */\n name: string;\n /** Namespace the schedule belongs to. */\n namespace: string;\n /**\n * Nominal scheduled time for this occurrence, based on the schedule expression.\n * Does not account for jitter. The dispatch may arrive later within the jitter window.\n */\n scheduledAt: Date;\n /** How the schedule was defined, such as `static`. */\n source: string;\n}\n\nexport type ScheduleHandler = (\n metadata: ScheduleMetadata,\n) => void | Promise<void>;\n\nexport class ScheduleEventParseError extends Error {\n cause?: unknown;\n\n constructor(message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = 'ScheduleEventParseError';\n if (options && 'cause' in options) {\n this.cause = options.cause;\n }\n }\n}\n\nfunction requiredHeader(request: Request, name: string): string {\n const value = request.headers.get(name);\n if (!value) {\n throw new ScheduleEventParseError(\n `Request is missing the ${name} header; it does not look like a schedule dispatch`,\n );\n }\n return value;\n}\n\n/** Parses schedule metadata from a binary-mode schedule CloudEvent. */\nexport async function parseScheduleEvent(\n request: Request,\n): Promise<ScheduleMetadata> {\n if (request.method !== 'POST') {\n throw new ScheduleEventParseError(\n `Schedule dispatches are POST requests, received ${request.method}`,\n );\n }\n\n const specVersion = requiredHeader(request, CLOUD_EVENT_SPEC_VERSION_HEADER);\n if (specVersion !== '1.0') {\n throw new ScheduleEventParseError(\n `Unsupported CloudEvent spec version: ${specVersion}`,\n );\n }\n\n const eventType = requiredHeader(request, CLOUD_EVENT_TYPE_HEADER);\n if (eventType !== SCHEDULE_CLOUD_EVENT_TYPE) {\n throw new ScheduleEventParseError(\n `Unsupported schedule CloudEvent type: ${eventType}`,\n );\n }\n\n requiredHeader(request, CLOUD_EVENT_SOURCE_HEADER);\n const executionId = requiredHeader(request, CLOUD_EVENT_ID_HEADER);\n const scheduledAtValue = requiredHeader(\n request,\n SCHEDULE_SCHEDULED_AT_HEADER,\n );\n const metadata: ScheduleMetadata = {\n scheduleId: requiredHeader(request, SCHEDULE_ID_HEADER),\n executionId,\n name: requiredHeader(request, SCHEDULE_NAME_HEADER),\n namespace: requiredHeader(request, SCHEDULE_NAMESPACE_HEADER),\n scheduledAt: new Date(scheduledAtValue),\n source: requiredHeader(request, SCHEDULE_SOURCE_HEADER),\n };\n\n if (Number.isNaN(metadata.scheduledAt.getTime())) {\n throw new ScheduleEventParseError(\n `Schedule scheduledAt is not a valid timestamp: ${scheduledAtValue}`,\n );\n }\n\n return metadata;\n}\n\n/**\n * Creates a web-standard route handler for schedule dispatches.\n *\n * ```ts\n * export const POST = handleSchedule(async (metadata) => {\n * console.log(metadata.name, metadata.scheduledAt);\n * });\n * ```\n */\nexport function handleSchedule(\n handler: ScheduleHandler,\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n let metadata: ScheduleMetadata;\n try {\n metadata = await parseScheduleEvent(request);\n } catch (error) {\n if (error instanceof ScheduleEventParseError) {\n return Response.json({ error: error.message }, { status: 400 });\n }\n return Response.json(\n { error: 'Failed to parse schedule dispatch' },\n { status: 500 },\n );\n }\n\n try {\n await handler(metadata);\n return new Response(null, { status: 200 });\n } catch {\n return Response.json(\n { error: 'Schedule handler failed' },\n { status: 500 },\n );\n }\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/schedules",
3
- "version": "0.0.0-alpha.1",
3
+ "version": "0.0.0-alpha.11",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },