@vercel/schedules 0.0.0-alpha.5 → 0.0.0-alpha.7
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 +13 -0
- package/dist/index.cjs +33 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -14
- package/dist/index.d.ts +20 -14
- package/dist/index.js +33 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,11 +8,24 @@ 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
|
+
Update schedule configuration by name and optional namespace:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
await Schedules.update({
|
|
21
|
+
name: 'cleanup',
|
|
22
|
+
expression: { type: 'cron', cron: '0 2 * * *' },
|
|
23
|
+
payload: { maxAgeDays: 30 },
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Use `Schedules.enable` and `Schedules.disable` to change schedule state.
|
|
28
|
+
|
|
16
29
|
## Handle a scheduled function
|
|
17
30
|
|
|
18
31
|
Use `handleSchedule` in a function targeted by a schedule:
|
package/dist/index.cjs
CHANGED
|
@@ -70,6 +70,15 @@ Cause: ${cause}`
|
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
// src/schedules.ts
|
|
73
|
+
function schedulePath(name, namespace, suffix = "") {
|
|
74
|
+
const searchParams = new URLSearchParams();
|
|
75
|
+
if (namespace !== void 0) {
|
|
76
|
+
searchParams.set("namespace", namespace);
|
|
77
|
+
}
|
|
78
|
+
const query = searchParams.toString();
|
|
79
|
+
const path = `/v1/schedules/${encodeURIComponent(name)}${suffix}`;
|
|
80
|
+
return query ? `${path}?${query}` : path;
|
|
81
|
+
}
|
|
73
82
|
var SchedulesClient = class extends SchedulesApiClient {
|
|
74
83
|
create({
|
|
75
84
|
target,
|
|
@@ -98,29 +107,33 @@ var SchedulesClient = class extends SchedulesApiClient {
|
|
|
98
107
|
const path = query ? `/v1/schedules?${query}` : "/v1/schedules";
|
|
99
108
|
return this.request("GET", path);
|
|
100
109
|
}
|
|
101
|
-
get(
|
|
102
|
-
return this.request(
|
|
103
|
-
"GET",
|
|
104
|
-
`/v1/schedules/${encodeURIComponent(scheduleId)}`
|
|
105
|
-
);
|
|
110
|
+
get({ name, namespace }) {
|
|
111
|
+
return this.request("GET", schedulePath(name, namespace));
|
|
106
112
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
update({
|
|
114
|
+
name,
|
|
115
|
+
namespace,
|
|
116
|
+
target,
|
|
117
|
+
...params
|
|
118
|
+
}) {
|
|
119
|
+
return this.request("PATCH", schedulePath(name, namespace), {
|
|
120
|
+
...params,
|
|
121
|
+
...target === void 0 ? {} : {
|
|
122
|
+
target: {
|
|
123
|
+
type: "queue",
|
|
124
|
+
topic: target.topic
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
112
128
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"POST",
|
|
116
|
-
`/v1/schedules/${encodeURIComponent(scheduleId)}/enable`
|
|
117
|
-
);
|
|
129
|
+
async delete({ name, namespace }) {
|
|
130
|
+
await this.request("DELETE", schedulePath(name, namespace));
|
|
118
131
|
}
|
|
119
|
-
|
|
120
|
-
return this.request(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
);
|
|
132
|
+
enable({ name, namespace }) {
|
|
133
|
+
return this.request("POST", schedulePath(name, namespace, "/enable"));
|
|
134
|
+
}
|
|
135
|
+
disable({ name, namespace }) {
|
|
136
|
+
return this.request("POST", schedulePath(name, namespace, "/disable"));
|
|
124
137
|
}
|
|
125
138
|
};
|
|
126
139
|
|
package/dist/index.cjs.map
CHANGED
|
@@ -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","../src/schedule-event.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;AG7DF;AACJ,EAAA;AACL,IAAA;AACG,IAAA;AACqD,EAAA;AACjD,IAAA;AACF,MAAA;AACK,MAAA;AACA,QAAA;AACC,QAAA;AACT,MAAA;AACD,IAAA;AACH,EAAA;AAEK,EAAA;AACG,IAAA;AAEK,IAAA;AACT,MAAA;AACF,IAAA;AACW,IAAA;AACT,MAAA;AACF,IAAA;AACW,IAAA;AACT,MAAA;AACF,IAAA;AAEM,IAAA;AACA,IAAA;AAEC,IAAA;AACT,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;AAEO,EAAA;AACE,IAAA;AACL,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEQ,EAAA;AACC,IAAA;AACL,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACF;AHuDe;AACA;AI5HF;AJ8HE;AACA;AKhIF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAyBA;AAGC,EAAA;AACJ,IAAA;AACD,IAAA;AACD,IAAA;AACG,MAAA;AACP,IAAA;AACF,EAAA;AACF;AAES;AACD,EAAA;AACM,EAAA;AACA,IAAA;AACR,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAGsB;AAGR,EAAA;AACA,IAAA;AACR,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACQ,IAAA;AACR,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AACQ,IAAA;AACR,MAAA;AACF,IAAA;AACF,EAAA;AAEA,EAAA;AACA,EAAA;AACM,EAAA;AACA,EAAA;AACJ,IAAA;AACM,IAAA;AACK,IAAA;AACF,IAAA;AACD,IAAA;AACV,EAAA;AAEW,EAAA;AACC,IAAA;AACR,MAAA;AACF,IAAA;AACF,EAAA;AAEY,EAAA;AACD,IAAA;AACX,EAAA;AAEM,EAAA;AACD,EAAA;AACG,IAAA;AACF,IAAA;AACO,MAAA;AACX,IAAA;AACF,EAAA;AAEE,EAAA;AAEU,IAAA;AACR,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACO,IAAA;AACF,EAAA;AACG,IAAA;AACR,MAAA;AACQ,MAAA;AACV,IAAA;AACF,EAAA;AACF;AAWgB;AAGP,EAAA;AACD,IAAA;AACA,IAAA;AACM,MAAA;AACD,IAAA;AACH,MAAA;AACK,QAAA;AACT,MAAA;AACO,MAAA;AACH,QAAA;AACA,QAAA;AACJ,MAAA;AACF,IAAA;AAEI,IAAA;AACI,MAAA;AACC,MAAA;AACD,IAAA;AACC,MAAA;AACH,QAAA;AACA,QAAA;AACJ,MAAA;AACF,IAAA;AACF,EAAA;AACF;AL6Ee;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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 ListSchedulesParams,\n ListSchedulesResponse,\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 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(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 enable(scheduleId: string): Promise<Schedule> {\n return this.request(\n 'POST',\n `/v1/schedules/${encodeURIComponent(scheduleId)}/enable`,\n );\n }\n\n disable(scheduleId: string): Promise<Schedule> {\n return this.request(\n 'POST',\n `/v1/schedules/${encodeURIComponent(scheduleId)}/disable`,\n );\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_FIRED_AT_HEADER = 'ce-time';\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 /** Schedule name, unique within its project and namespace. */\n name: string;\n /** Namespace the schedule belongs to. */\n namespace: string;\n /** When the schedule fired. */\n firedAt: Date;\n /** How the schedule was defined, such as `static`. */\n source: string;\n}\n\nexport interface ParsedScheduleEvent<T = unknown> {\n payload: T | undefined;\n metadata: ScheduleMetadata;\n}\n\nexport type ScheduleHandler<T = unknown> = (\n payload: T | undefined,\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 a binary-mode schedule CloudEvent. */\nexport async function parseScheduleEvent<T = unknown>(\n request: Request,\n): Promise<ParsedScheduleEvent<T>> {\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 requiredHeader(request, CLOUD_EVENT_ID_HEADER);\n const firedAtValue = requiredHeader(request, SCHEDULE_FIRED_AT_HEADER);\n const metadata: ScheduleMetadata = {\n scheduleId: requiredHeader(request, SCHEDULE_ID_HEADER),\n name: requiredHeader(request, SCHEDULE_NAME_HEADER),\n namespace: requiredHeader(request, SCHEDULE_NAMESPACE_HEADER),\n firedAt: new Date(firedAtValue),\n source: requiredHeader(request, SCHEDULE_SOURCE_HEADER),\n };\n\n if (Number.isNaN(metadata.firedAt.getTime())) {\n throw new ScheduleEventParseError(\n `Schedule dispatch time is not a valid timestamp: ${firedAtValue}`,\n );\n }\n\n if (request.body === null) {\n return { payload: undefined, metadata };\n }\n\n const contentType = request.headers.get('content-type');\n if (!contentType) {\n const body = await request.text();\n if (body === '') {\n return { payload: undefined, metadata };\n }\n }\n if (\n contentType?.split(';', 1)[0].trim().toLowerCase() !== 'application/json'\n ) {\n throw new ScheduleEventParseError(\n 'Schedule dispatch payload must use the application/json content type',\n );\n }\n\n try {\n return { payload: (await request.json()) as T, metadata };\n } catch (cause) {\n throw new ScheduleEventParseError(\n 'Schedule dispatch payload is not valid JSON',\n { cause },\n );\n }\n}\n\n/**\n * Creates a web-standard route handler for schedule dispatches.\n *\n * ```ts\n * export const POST = handleSchedule(async (payload, metadata) => {\n * console.log(metadata.name, payload);\n * });\n * ```\n */\nexport function handleSchedule<T = unknown>(\n handler: ScheduleHandler<T>,\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n let event: ParsedScheduleEvent<T>;\n try {\n event = await parseScheduleEvent<T>(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(event.payload, event.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"]}
|
|
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\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 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","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_FIRED_AT_HEADER = 'ce-time';\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 /** Schedule name, unique within its project and namespace. */\n name: string;\n /** Namespace the schedule belongs to. */\n namespace: string;\n /** When the schedule fired. */\n firedAt: Date;\n /** How the schedule was defined, such as `static`. */\n source: string;\n}\n\nexport interface ParsedScheduleEvent<T = unknown> {\n payload: T | undefined;\n metadata: ScheduleMetadata;\n}\n\nexport type ScheduleHandler<T = unknown> = (\n payload: T | undefined,\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 a binary-mode schedule CloudEvent. */\nexport async function parseScheduleEvent<T = unknown>(\n request: Request,\n): Promise<ParsedScheduleEvent<T>> {\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 requiredHeader(request, CLOUD_EVENT_ID_HEADER);\n const firedAtValue = requiredHeader(request, SCHEDULE_FIRED_AT_HEADER);\n const metadata: ScheduleMetadata = {\n scheduleId: requiredHeader(request, SCHEDULE_ID_HEADER),\n name: requiredHeader(request, SCHEDULE_NAME_HEADER),\n namespace: requiredHeader(request, SCHEDULE_NAMESPACE_HEADER),\n firedAt: new Date(firedAtValue),\n source: requiredHeader(request, SCHEDULE_SOURCE_HEADER),\n };\n\n if (Number.isNaN(metadata.firedAt.getTime())) {\n throw new ScheduleEventParseError(\n `Schedule dispatch time is not a valid timestamp: ${firedAtValue}`,\n );\n }\n\n if (request.body === null) {\n return { payload: undefined, metadata };\n }\n\n const contentType = request.headers.get('content-type');\n if (!contentType) {\n const body = await request.text();\n if (body === '') {\n return { payload: undefined, metadata };\n }\n }\n if (\n contentType?.split(';', 1)[0].trim().toLowerCase() !== 'application/json'\n ) {\n throw new ScheduleEventParseError(\n 'Schedule dispatch payload must use the application/json content type',\n );\n }\n\n try {\n return { payload: (await request.json()) as T, metadata };\n } catch (cause) {\n throw new ScheduleEventParseError(\n 'Schedule dispatch payload is not valid JSON',\n { cause },\n );\n }\n}\n\n/**\n * Creates a web-standard route handler for schedule dispatches.\n *\n * ```ts\n * export const POST = handleSchedule(async (payload, metadata) => {\n * console.log(metadata.name, payload);\n * });\n * ```\n */\nexport function handleSchedule<T = unknown>(\n handler: ScheduleHandler<T>,\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n let event: ParsedScheduleEvent<T>;\n try {\n event = await parseScheduleEvent<T>(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(event.payload, event.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
|
@@ -41,32 +41,37 @@ interface Schedule {
|
|
|
41
41
|
trackId: string;
|
|
42
42
|
name: string;
|
|
43
43
|
namespace: string;
|
|
44
|
+
timezone: string;
|
|
44
45
|
expression: ScheduleExpression;
|
|
45
46
|
jitter?: number;
|
|
46
47
|
target: ScheduleTarget;
|
|
47
48
|
state: ScheduleState;
|
|
48
|
-
stateOverride?: {
|
|
49
|
-
state: ScheduleState;
|
|
50
|
-
until: number;
|
|
51
|
-
};
|
|
52
49
|
source: ScheduleSource;
|
|
53
50
|
createdAt: number;
|
|
54
51
|
updatedAt: number;
|
|
55
52
|
}
|
|
56
53
|
interface CreateScheduleParams {
|
|
57
|
-
name
|
|
54
|
+
name: string;
|
|
58
55
|
namespace?: string;
|
|
59
56
|
expression: ScheduleExpression;
|
|
57
|
+
timezone?: string;
|
|
60
58
|
jitter?: number;
|
|
61
59
|
target: CreateScheduleTarget;
|
|
62
60
|
payload?: unknown;
|
|
63
61
|
}
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
type CreateScheduleResponse = Schedule;
|
|
63
|
+
interface ScheduleIdentifier {
|
|
64
|
+
name: string;
|
|
65
|
+
namespace?: string;
|
|
66
66
|
}
|
|
67
|
-
interface
|
|
68
|
-
|
|
67
|
+
interface UpdateScheduleParams extends ScheduleIdentifier {
|
|
68
|
+
expression?: ScheduleExpression;
|
|
69
|
+
timezone?: string;
|
|
70
|
+
jitter?: number | null;
|
|
71
|
+
target?: CreateScheduleTarget;
|
|
72
|
+
payload?: unknown;
|
|
69
73
|
}
|
|
74
|
+
type UpdateScheduleResponse = Schedule;
|
|
70
75
|
interface ListSchedulesParams {
|
|
71
76
|
namespace?: string;
|
|
72
77
|
cursor?: string;
|
|
@@ -80,10 +85,11 @@ interface ListSchedulesResponse {
|
|
|
80
85
|
declare class SchedulesClient extends SchedulesApiClient {
|
|
81
86
|
create({ target, ...params }: CreateScheduleParams): Promise<CreateScheduleResponse>;
|
|
82
87
|
list(params?: ListSchedulesParams): Promise<ListSchedulesResponse>;
|
|
83
|
-
get(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
get({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
|
|
89
|
+
update({ name, namespace, target, ...params }: UpdateScheduleParams): Promise<UpdateScheduleResponse>;
|
|
90
|
+
delete({ name, namespace }: ScheduleIdentifier): Promise<void>;
|
|
91
|
+
enable({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
|
|
92
|
+
disable({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
|
|
87
93
|
}
|
|
88
94
|
|
|
89
95
|
declare const schedules: SchedulesClient;
|
|
@@ -135,4 +141,4 @@ declare function parseScheduleEvent<T = unknown>(request: Request): Promise<Pars
|
|
|
135
141
|
*/
|
|
136
142
|
declare function handleSchedule<T = unknown>(handler: ScheduleHandler<T>): (request: Request) => Promise<Response>;
|
|
137
143
|
|
|
138
|
-
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
|
|
144
|
+
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 ListSchedulesParams, type ListSchedulesResponse, type ParsedScheduleEvent, SCHEDULE_CLOUD_EVENT_TYPE, SCHEDULE_FIRED_AT_HEADER, SCHEDULE_ID_HEADER, SCHEDULE_NAMESPACE_HEADER, SCHEDULE_NAME_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
|
@@ -41,32 +41,37 @@ interface Schedule {
|
|
|
41
41
|
trackId: string;
|
|
42
42
|
name: string;
|
|
43
43
|
namespace: string;
|
|
44
|
+
timezone: string;
|
|
44
45
|
expression: ScheduleExpression;
|
|
45
46
|
jitter?: number;
|
|
46
47
|
target: ScheduleTarget;
|
|
47
48
|
state: ScheduleState;
|
|
48
|
-
stateOverride?: {
|
|
49
|
-
state: ScheduleState;
|
|
50
|
-
until: number;
|
|
51
|
-
};
|
|
52
49
|
source: ScheduleSource;
|
|
53
50
|
createdAt: number;
|
|
54
51
|
updatedAt: number;
|
|
55
52
|
}
|
|
56
53
|
interface CreateScheduleParams {
|
|
57
|
-
name
|
|
54
|
+
name: string;
|
|
58
55
|
namespace?: string;
|
|
59
56
|
expression: ScheduleExpression;
|
|
57
|
+
timezone?: string;
|
|
60
58
|
jitter?: number;
|
|
61
59
|
target: CreateScheduleTarget;
|
|
62
60
|
payload?: unknown;
|
|
63
61
|
}
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
type CreateScheduleResponse = Schedule;
|
|
63
|
+
interface ScheduleIdentifier {
|
|
64
|
+
name: string;
|
|
65
|
+
namespace?: string;
|
|
66
66
|
}
|
|
67
|
-
interface
|
|
68
|
-
|
|
67
|
+
interface UpdateScheduleParams extends ScheduleIdentifier {
|
|
68
|
+
expression?: ScheduleExpression;
|
|
69
|
+
timezone?: string;
|
|
70
|
+
jitter?: number | null;
|
|
71
|
+
target?: CreateScheduleTarget;
|
|
72
|
+
payload?: unknown;
|
|
69
73
|
}
|
|
74
|
+
type UpdateScheduleResponse = Schedule;
|
|
70
75
|
interface ListSchedulesParams {
|
|
71
76
|
namespace?: string;
|
|
72
77
|
cursor?: string;
|
|
@@ -80,10 +85,11 @@ interface ListSchedulesResponse {
|
|
|
80
85
|
declare class SchedulesClient extends SchedulesApiClient {
|
|
81
86
|
create({ target, ...params }: CreateScheduleParams): Promise<CreateScheduleResponse>;
|
|
82
87
|
list(params?: ListSchedulesParams): Promise<ListSchedulesResponse>;
|
|
83
|
-
get(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
get({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
|
|
89
|
+
update({ name, namespace, target, ...params }: UpdateScheduleParams): Promise<UpdateScheduleResponse>;
|
|
90
|
+
delete({ name, namespace }: ScheduleIdentifier): Promise<void>;
|
|
91
|
+
enable({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
|
|
92
|
+
disable({ name, namespace }: ScheduleIdentifier): Promise<Schedule>;
|
|
87
93
|
}
|
|
88
94
|
|
|
89
95
|
declare const schedules: SchedulesClient;
|
|
@@ -135,4 +141,4 @@ declare function parseScheduleEvent<T = unknown>(request: Request): Promise<Pars
|
|
|
135
141
|
*/
|
|
136
142
|
declare function handleSchedule<T = unknown>(handler: ScheduleHandler<T>): (request: Request) => Promise<Response>;
|
|
137
143
|
|
|
138
|
-
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
|
|
144
|
+
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 ListSchedulesParams, type ListSchedulesResponse, type ParsedScheduleEvent, SCHEDULE_CLOUD_EVENT_TYPE, SCHEDULE_FIRED_AT_HEADER, SCHEDULE_ID_HEADER, SCHEDULE_NAMESPACE_HEADER, SCHEDULE_NAME_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
|
@@ -70,6 +70,15 @@ Cause: ${cause}`
|
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
// src/schedules.ts
|
|
73
|
+
function schedulePath(name, namespace, suffix = "") {
|
|
74
|
+
const searchParams = new URLSearchParams();
|
|
75
|
+
if (namespace !== void 0) {
|
|
76
|
+
searchParams.set("namespace", namespace);
|
|
77
|
+
}
|
|
78
|
+
const query = searchParams.toString();
|
|
79
|
+
const path = `/v1/schedules/${encodeURIComponent(name)}${suffix}`;
|
|
80
|
+
return query ? `${path}?${query}` : path;
|
|
81
|
+
}
|
|
73
82
|
var SchedulesClient = class extends SchedulesApiClient {
|
|
74
83
|
create({
|
|
75
84
|
target,
|
|
@@ -98,29 +107,33 @@ var SchedulesClient = class extends SchedulesApiClient {
|
|
|
98
107
|
const path = query ? `/v1/schedules?${query}` : "/v1/schedules";
|
|
99
108
|
return this.request("GET", path);
|
|
100
109
|
}
|
|
101
|
-
get(
|
|
102
|
-
return this.request(
|
|
103
|
-
"GET",
|
|
104
|
-
`/v1/schedules/${encodeURIComponent(scheduleId)}`
|
|
105
|
-
);
|
|
110
|
+
get({ name, namespace }) {
|
|
111
|
+
return this.request("GET", schedulePath(name, namespace));
|
|
106
112
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
update({
|
|
114
|
+
name,
|
|
115
|
+
namespace,
|
|
116
|
+
target,
|
|
117
|
+
...params
|
|
118
|
+
}) {
|
|
119
|
+
return this.request("PATCH", schedulePath(name, namespace), {
|
|
120
|
+
...params,
|
|
121
|
+
...target === void 0 ? {} : {
|
|
122
|
+
target: {
|
|
123
|
+
type: "queue",
|
|
124
|
+
topic: target.topic
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
112
128
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"POST",
|
|
116
|
-
`/v1/schedules/${encodeURIComponent(scheduleId)}/enable`
|
|
117
|
-
);
|
|
129
|
+
async delete({ name, namespace }) {
|
|
130
|
+
await this.request("DELETE", schedulePath(name, namespace));
|
|
118
131
|
}
|
|
119
|
-
|
|
120
|
-
return this.request(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
);
|
|
132
|
+
enable({ name, namespace }) {
|
|
133
|
+
return this.request("POST", schedulePath(name, namespace, "/enable"));
|
|
134
|
+
}
|
|
135
|
+
disable({ name, namespace }) {
|
|
136
|
+
return this.request("POST", schedulePath(name, namespace, "/disable"));
|
|
124
137
|
}
|
|
125
138
|
};
|
|
126
139
|
|
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","../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\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 ListSchedulesParams,\n ListSchedulesResponse,\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 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(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 enable(scheduleId: string): Promise<Schedule> {\n return this.request(\n 'POST',\n `/v1/schedules/${encodeURIComponent(scheduleId)}/enable`,\n );\n }\n\n disable(scheduleId: string): Promise<Schedule> {\n return this.request(\n 'POST',\n `/v1/schedules/${encodeURIComponent(scheduleId)}/disable`,\n );\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_FIRED_AT_HEADER = 'ce-time';\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 /** Schedule name, unique within its project and namespace. */\n name: string;\n /** Namespace the schedule belongs to. */\n namespace: string;\n /** When the schedule fired. */\n firedAt: Date;\n /** How the schedule was defined, such as `static`. */\n source: string;\n}\n\nexport interface ParsedScheduleEvent<T = unknown> {\n payload: T | undefined;\n metadata: ScheduleMetadata;\n}\n\nexport type ScheduleHandler<T = unknown> = (\n payload: T | undefined,\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 a binary-mode schedule CloudEvent. */\nexport async function parseScheduleEvent<T = unknown>(\n request: Request,\n): Promise<ParsedScheduleEvent<T>> {\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 requiredHeader(request, CLOUD_EVENT_ID_HEADER);\n const firedAtValue = requiredHeader(request, SCHEDULE_FIRED_AT_HEADER);\n const metadata: ScheduleMetadata = {\n scheduleId: requiredHeader(request, SCHEDULE_ID_HEADER),\n name: requiredHeader(request, SCHEDULE_NAME_HEADER),\n namespace: requiredHeader(request, SCHEDULE_NAMESPACE_HEADER),\n firedAt: new Date(firedAtValue),\n source: requiredHeader(request, SCHEDULE_SOURCE_HEADER),\n };\n\n if (Number.isNaN(metadata.firedAt.getTime())) {\n throw new ScheduleEventParseError(\n `Schedule dispatch time is not a valid timestamp: ${firedAtValue}`,\n );\n }\n\n if (request.body === null) {\n return { payload: undefined, metadata };\n }\n\n const contentType = request.headers.get('content-type');\n if (!contentType) {\n const body = await request.text();\n if (body === '') {\n return { payload: undefined, metadata };\n }\n }\n if (\n contentType?.split(';', 1)[0].trim().toLowerCase() !== 'application/json'\n ) {\n throw new ScheduleEventParseError(\n 'Schedule dispatch payload must use the application/json content type',\n );\n }\n\n try {\n return { payload: (await request.json()) as T, metadata };\n } catch (cause) {\n throw new ScheduleEventParseError(\n 'Schedule dispatch payload is not valid JSON',\n { cause },\n );\n }\n}\n\n/**\n * Creates a web-standard route handler for schedule dispatches.\n *\n * ```ts\n * export const POST = handleSchedule(async (payload, metadata) => {\n * console.log(metadata.name, payload);\n * });\n * ```\n */\nexport function handleSchedule<T = unknown>(\n handler: ScheduleHandler<T>,\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n let event: ParsedScheduleEvent<T>;\n try {\n event = await parseScheduleEvent<T>(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(event.payload, event.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":[]}
|
|
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\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 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","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_FIRED_AT_HEADER = 'ce-time';\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 /** Schedule name, unique within its project and namespace. */\n name: string;\n /** Namespace the schedule belongs to. */\n namespace: string;\n /** When the schedule fired. */\n firedAt: Date;\n /** How the schedule was defined, such as `static`. */\n source: string;\n}\n\nexport interface ParsedScheduleEvent<T = unknown> {\n payload: T | undefined;\n metadata: ScheduleMetadata;\n}\n\nexport type ScheduleHandler<T = unknown> = (\n payload: T | undefined,\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 a binary-mode schedule CloudEvent. */\nexport async function parseScheduleEvent<T = unknown>(\n request: Request,\n): Promise<ParsedScheduleEvent<T>> {\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 requiredHeader(request, CLOUD_EVENT_ID_HEADER);\n const firedAtValue = requiredHeader(request, SCHEDULE_FIRED_AT_HEADER);\n const metadata: ScheduleMetadata = {\n scheduleId: requiredHeader(request, SCHEDULE_ID_HEADER),\n name: requiredHeader(request, SCHEDULE_NAME_HEADER),\n namespace: requiredHeader(request, SCHEDULE_NAMESPACE_HEADER),\n firedAt: new Date(firedAtValue),\n source: requiredHeader(request, SCHEDULE_SOURCE_HEADER),\n };\n\n if (Number.isNaN(metadata.firedAt.getTime())) {\n throw new ScheduleEventParseError(\n `Schedule dispatch time is not a valid timestamp: ${firedAtValue}`,\n );\n }\n\n if (request.body === null) {\n return { payload: undefined, metadata };\n }\n\n const contentType = request.headers.get('content-type');\n if (!contentType) {\n const body = await request.text();\n if (body === '') {\n return { payload: undefined, metadata };\n }\n }\n if (\n contentType?.split(';', 1)[0].trim().toLowerCase() !== 'application/json'\n ) {\n throw new ScheduleEventParseError(\n 'Schedule dispatch payload must use the application/json content type',\n );\n }\n\n try {\n return { payload: (await request.json()) as T, metadata };\n } catch (cause) {\n throw new ScheduleEventParseError(\n 'Schedule dispatch payload is not valid JSON',\n { cause },\n );\n }\n}\n\n/**\n * Creates a web-standard route handler for schedule dispatches.\n *\n * ```ts\n * export const POST = handleSchedule(async (payload, metadata) => {\n * console.log(metadata.name, payload);\n * });\n * ```\n */\nexport function handleSchedule<T = unknown>(\n handler: ScheduleHandler<T>,\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n let event: ParsedScheduleEvent<T>;\n try {\n event = await parseScheduleEvent<T>(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(event.payload, event.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":[]}
|