@orieljs/vercel 0.1.0-alpha.11 → 0.1.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +27 -2
- package/dist/index.js +91 -6
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,11 @@ interface VercelBuildOptions {
|
|
|
10
10
|
maxFunctionBytes?: number;
|
|
11
11
|
/** The machine the build runs on, which decides the native packages copied. Default: `process.platform`. */
|
|
12
12
|
platform?: string;
|
|
13
|
+
/**
|
|
14
|
+
* When Vercel calls the app's scheduler, as a cron expression (UTC), or `false` for never. Default: `0 3 * * *`,
|
|
15
|
+
* daily at 03:00: Vercel's Hobby plan runs a cron at most once a day. Written only when the app schedules tasks.
|
|
16
|
+
*/
|
|
17
|
+
cron?: string | false;
|
|
13
18
|
}
|
|
14
19
|
interface VercelBuildResult {
|
|
15
20
|
/** The function's size, unzipped. */
|
|
@@ -28,6 +33,26 @@ declare const VERCEL_FUNCTION_LIMIT: number;
|
|
|
28
33
|
*/
|
|
29
34
|
declare function buildVercelOutput(app: Application, options?: VercelBuildOptions): Promise<VercelBuildResult>;
|
|
30
35
|
|
|
36
|
+
/** Where Vercel's cron calls the app; `oriel vercel:build` puts it in the cron entry. */
|
|
37
|
+
declare const SCHEDULE_PATH = "/_oriel/schedule";
|
|
38
|
+
/** The cron `vercel:build` writes by default: daily at 03:00 UTC (Hobby runs a cron at most once a day). */
|
|
39
|
+
declare const DEFAULT_CRON = "0 3 * * *";
|
|
40
|
+
/**
|
|
41
|
+
* Where a trigger with no record starts: the trigger's fire time before the current one, so the first trigger covers
|
|
42
|
+
* one full period, as every later one does (for a daily cron, yesterday's run time to now).
|
|
43
|
+
*/
|
|
44
|
+
declare function previousTrigger(expression: string, now: Date): Date;
|
|
45
|
+
/**
|
|
46
|
+
* What Vercel's cron calls. Off (404) without `CRON_SECRET`; 401 for any other bearer token. Otherwise, holding a lock
|
|
47
|
+
* so a doubled call runs nothing twice, it loads the app's schedule (a web request never has: routes/console.ts is the
|
|
48
|
+
* console's) and runs every task due since the previous trigger, once each. A failing task is reported and counted,
|
|
49
|
+
* and never stops the rest; the trigger is recorded either way. 200 `{ ran, failed: 0 }`, or 500 when any failed.
|
|
50
|
+
*/
|
|
51
|
+
declare function runVercelSchedule(authorization: string | undefined, options?: {
|
|
52
|
+
now?: () => Date;
|
|
53
|
+
trigger?: string;
|
|
54
|
+
}): Promise<Response>;
|
|
55
|
+
|
|
31
56
|
/**
|
|
32
57
|
* The function's entry (`index.mjs` in `.vercel/output/functions/index.func`): registers tsx as the app's `oriel`
|
|
33
58
|
* file does, boots the app once per function instance, and hands each request to the Node request handler `serve`
|
|
@@ -37,9 +62,9 @@ declare function buildVercelOutput(app: Application, options?: VercelBuildOption
|
|
|
37
62
|
*/
|
|
38
63
|
declare const LAUNCHER = "// Oriel on Vercel: boots the app once per function instance and hands it each request.\nimport { fileURLToPath } from 'node:url'\n\nlet ready\n\nasync function boot() {\n // Vercel's disk is read-only, so a log file can't open; stderr is what Vercel's log view shows.\n process.env.LOG_CHANNEL ??= 'stderr'\n if (!process.versions.bun) {\n const { register } = await import('tsx/esm/api')\n register({ tsconfig: fileURLToPath(new URL('./tsconfig.json', import.meta.url)) })\n }\n const { createApplication } = await import('./bootstrap/app.ts')\n const { nodeRequestHandler } = await import('@orieljs/framework')\n const { ConfigRepository } = await import('@orieljs/framework/contracts')\n const app = await createApplication()\n await app.boot()\n // Every request comes through Vercel's proxy, which sets the X-Forwarded-* headers itself: trust it, unless the app\n // lists its own proxies, so that URLs keep the browser's https and ip() is the visitor's.\n const config = app.make(ConfigRepository)\n const proxies = config.get('http.trusted_proxies', [])\n if (proxies === undefined || proxies === null || proxies.length === 0) config.set('http.trusted_proxies', '*')\n return nodeRequestHandler(app, { fallbackHost: 'localhost' })\n}\n\nexport default async function handler(req, res) {\n let handle\n try {\n ready ??= boot()\n handle = await ready\n } catch (error) {\n ready = undefined\n console.error(error)\n if (!res.headersSent) {\n res.statusCode = 500\n res.setHeader('content-type', 'text/plain; charset=utf-8')\n }\n res.end('Internal Server Error')\n return\n }\n await handle(req, res)\n}\n";
|
|
39
64
|
|
|
40
|
-
/** Registers `oriel vercel:build`, which writes Vercel's build output for the app. */
|
|
65
|
+
/** Registers `oriel vercel:build`, which writes Vercel's build output for the app, and the route Vercel Cron calls. */
|
|
41
66
|
declare class VercelServiceProvider extends ServiceProvider {
|
|
42
67
|
boot(): void;
|
|
43
68
|
}
|
|
44
69
|
|
|
45
|
-
export { LAUNCHER, VERCEL_FUNCTION_LIMIT, type VercelBuildOptions, type VercelBuildResult, VercelServiceProvider, buildVercelOutput };
|
|
70
|
+
export { DEFAULT_CRON, LAUNCHER, SCHEDULE_PATH, VERCEL_FUNCTION_LIMIT, type VercelBuildOptions, type VercelBuildResult, VercelServiceProvider, buildVercelOutput, previousTrigger, runVercelSchedule };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,57 @@
|
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "fs";
|
|
3
3
|
import { cp, mkdir, rm, writeFile } from "fs/promises";
|
|
4
4
|
import { basename, dirname, join, relative, resolve } from "path";
|
|
5
|
+
import { ConsoleKernel as ConsoleKernel2, Schedule as Schedule2 } from "@orieljs/contracts";
|
|
6
|
+
import { CronExpressionParser as CronExpressionParser2 } from "cron-parser";
|
|
7
|
+
|
|
8
|
+
// cron.ts
|
|
9
|
+
import { timingSafeEqual } from "crypto";
|
|
10
|
+
import { inject } from "@orieljs/container";
|
|
11
|
+
import { CacheManager, ConsoleKernel, ExceptionHandler, Schedule } from "@orieljs/contracts";
|
|
12
|
+
import { CronExpressionParser } from "cron-parser";
|
|
13
|
+
var SCHEDULE_PATH = "/_oriel/schedule";
|
|
14
|
+
var DEFAULT_CRON = "0 3 * * *";
|
|
15
|
+
var LAST_RUN = "oriel:vercel:schedule:last";
|
|
16
|
+
var LOCK = "oriel:vercel:schedule";
|
|
17
|
+
var json = (body, status = 200) => Response.json(body, { status, headers: { "cache-control": "no-store" } });
|
|
18
|
+
function authorized(header, secret) {
|
|
19
|
+
const expected = Buffer.from(`Bearer ${secret}`);
|
|
20
|
+
const given = Buffer.from(header ?? "");
|
|
21
|
+
return given.length === expected.length && timingSafeEqual(given, expected);
|
|
22
|
+
}
|
|
23
|
+
function previousTrigger(expression, now) {
|
|
24
|
+
const parsed = CronExpressionParser.parse(expression, { currentDate: new Date(now.getTime() + 6e4), tz: "UTC" });
|
|
25
|
+
parsed.prev();
|
|
26
|
+
return parsed.prev().toDate();
|
|
27
|
+
}
|
|
28
|
+
async function runVercelSchedule(authorization, options = {}) {
|
|
29
|
+
const secret = process.env.CRON_SECRET;
|
|
30
|
+
if (!secret) return new Response("Not Found", { status: 404 });
|
|
31
|
+
if (!authorized(authorization, secret)) return json({ error: "Unauthorized" }, 401);
|
|
32
|
+
const cache = inject(CacheManager);
|
|
33
|
+
const lock = cache.lock(LOCK, 900);
|
|
34
|
+
if (!await lock.get()) return json({ ran: 0, failed: 0, skipped: "busy" });
|
|
35
|
+
try {
|
|
36
|
+
await inject(ConsoleKernel).bootstrap();
|
|
37
|
+
const now = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
38
|
+
const last = await cache.get(LAST_RUN);
|
|
39
|
+
const from = last === void 0 ? previousTrigger(options.trigger ?? process.env.ORIEL_VERCEL_CRON ?? DEFAULT_CRON, now) : new Date(last);
|
|
40
|
+
const due = inject(Schedule).dueBetween(from, now);
|
|
41
|
+
let failed = 0;
|
|
42
|
+
for (const event of due) {
|
|
43
|
+
try {
|
|
44
|
+
await event.run();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
failed++;
|
|
47
|
+
await inject(ExceptionHandler).report(error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
await cache.forever(LAST_RUN, now.toISOString());
|
|
51
|
+
return json({ ran: due.length, failed }, failed > 0 ? 500 : 200);
|
|
52
|
+
} finally {
|
|
53
|
+
await lock.release();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
5
56
|
|
|
6
57
|
// launcher.ts
|
|
7
58
|
var LAUNCHER = `// Oriel on Vercel: boots the app once per function instance and hands it each request.
|
|
@@ -88,8 +139,10 @@ async function buildVercelOutput(app, options = {}) {
|
|
|
88
139
|
const vcConfig = { runtime: options.runtime ?? "nodejs22.x", handler: "index.mjs", launcherType: "Nodejs", shouldAddHelpers: false, supportsResponseStreaming: true };
|
|
89
140
|
await writeFile(join(fn, ".vc-config.json"), `${JSON.stringify(vcConfig, null, 2)}
|
|
90
141
|
`);
|
|
142
|
+
const { crons, cronWarnings } = await scheduleCron(app, options.cron);
|
|
91
143
|
const config = {
|
|
92
144
|
version: 3,
|
|
145
|
+
...crons === void 0 ? {} : { crons },
|
|
93
146
|
routes: [
|
|
94
147
|
// The built assets' names change with their content, so they're cached for a year, as serve() does.
|
|
95
148
|
{ src: "^/build/assets/(.*)$", headers: { "cache-control": "public, max-age=31536000, immutable" }, continue: true },
|
|
@@ -108,7 +161,26 @@ async function buildVercelOutput(app, options = {}) {
|
|
|
108
161
|
const warnings = platform === "linux" ? [] : [
|
|
109
162
|
`Built on ${PLATFORM_NAMES[platform] ?? platform}: the function's native packages (such as esbuild, which tsx needs) only run on this kind of machine, and Vercel's functions run on Linux. Let Vercel run the build, or build on Linux.`
|
|
110
163
|
];
|
|
111
|
-
return { functionBytes, staticFiles: countFiles(staticFolder), warnings };
|
|
164
|
+
return { functionBytes, staticFiles: countFiles(staticFolder), warnings: [...warnings, ...cronWarnings] };
|
|
165
|
+
}
|
|
166
|
+
async function scheduleCron(app, cron) {
|
|
167
|
+
if (cron === false) return { cronWarnings: [] };
|
|
168
|
+
if (app.bound(ConsoleKernel2)) await app.make(ConsoleKernel2).bootstrap();
|
|
169
|
+
if (!app.bound(Schedule2)) return { cronWarnings: [] };
|
|
170
|
+
const schedule = app.make(Schedule2);
|
|
171
|
+
if (schedule.events().length === 0) return { cronWarnings: [] };
|
|
172
|
+
const expression = cron ?? DEFAULT_CRON;
|
|
173
|
+
const fires = CronExpressionParser2.parse(expression, { tz: "UTC" });
|
|
174
|
+
const start = fires.next().toDate();
|
|
175
|
+
const end = fires.next().toDate();
|
|
176
|
+
const middle = new Date((start.getTime() + end.getTime()) / 2);
|
|
177
|
+
const later = new Set(schedule.dueBetween(middle, end));
|
|
178
|
+
const often = schedule.dueBetween(start, middle).filter((event) => later.has(event));
|
|
179
|
+
const plan = cron === void 0 ? " (once a day on Vercel's Hobby plan)" : "";
|
|
180
|
+
return {
|
|
181
|
+
crons: [{ path: SCHEDULE_PATH, schedule: expression }],
|
|
182
|
+
cronWarnings: often.map((event) => `"${event.getSummaryForDisplay()}" (${event.expression}) is due more often than the cron "${expression}", so it runs once per trigger${plan}.`)
|
|
183
|
+
};
|
|
112
184
|
}
|
|
113
185
|
async function copyDependencies(base, fn) {
|
|
114
186
|
const manifest = JSON.parse(readFileSync(join(base, "package.json"), "utf8"));
|
|
@@ -152,16 +224,25 @@ function countFiles(folder) {
|
|
|
152
224
|
var megabytes = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
153
225
|
|
|
154
226
|
// VercelServiceProvider.ts
|
|
155
|
-
import {
|
|
227
|
+
import { inject as inject2 } from "@orieljs/container";
|
|
228
|
+
import { ConsoleKernel as ConsoleKernel3, HttpRequest, Router } from "@orieljs/contracts";
|
|
156
229
|
import { ServiceProvider } from "@orieljs/support";
|
|
157
230
|
var megabytes2 = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
158
231
|
var VercelServiceProvider = class extends ServiceProvider {
|
|
159
232
|
boot() {
|
|
160
|
-
if (
|
|
233
|
+
if (this.app.bound(Router)) {
|
|
234
|
+
this.app.make(Router).get(SCHEDULE_PATH, () => {
|
|
235
|
+
const request = inject2(HttpRequest);
|
|
236
|
+
return runVercelSchedule(request.header("authorization"), { trigger: request.header("x-vercel-cron-schedule") });
|
|
237
|
+
}).name("vercel.schedule");
|
|
238
|
+
}
|
|
239
|
+
if (!this.app.bound(ConsoleKernel3)) return;
|
|
161
240
|
const app = this.app;
|
|
162
|
-
this.app.make(
|
|
241
|
+
this.app.make(ConsoleKernel3).command("vercel:build {--cron= : When Vercel calls the scheduler, as a cron expression (UTC), or false for never}", async function() {
|
|
163
242
|
try {
|
|
164
|
-
const
|
|
243
|
+
const option = this.option("cron");
|
|
244
|
+
const cron = typeof option === "string" && option !== "" ? option === "false" ? false : option : void 0;
|
|
245
|
+
const { staticFiles, functionBytes, warnings } = await buildVercelOutput(app, { cron });
|
|
165
246
|
const files = staticFiles === 1 ? "1 static file" : `${staticFiles} static files`;
|
|
166
247
|
this.info(`Wrote .vercel/output: ${files}, a function of ${megabytes2(functionBytes)}.`);
|
|
167
248
|
for (const warning of warnings) this.warn(warning);
|
|
@@ -174,9 +255,13 @@ var VercelServiceProvider = class extends ServiceProvider {
|
|
|
174
255
|
}
|
|
175
256
|
};
|
|
176
257
|
export {
|
|
258
|
+
DEFAULT_CRON,
|
|
177
259
|
LAUNCHER,
|
|
260
|
+
SCHEDULE_PATH,
|
|
178
261
|
VERCEL_FUNCTION_LIMIT,
|
|
179
262
|
VercelServiceProvider,
|
|
180
|
-
buildVercelOutput
|
|
263
|
+
buildVercelOutput,
|
|
264
|
+
previousTrigger,
|
|
265
|
+
runVercelSchedule
|
|
181
266
|
};
|
|
182
267
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../build.ts","../launcher.ts","../VercelServiceProvider.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs'\nimport { cp, mkdir, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join, relative, resolve } from 'node:path'\nimport type { Application } from '@orieljs/contracts'\nimport { LAUNCHER } from './launcher'\n\nexport interface VercelBuildOptions {\n /** Where the build output goes, relative to the app. Default: `.vercel/output` (where Vercel looks for it). */\n output?: string\n /** The function's Node runtime. Default: `nodejs22.x`. */\n runtime?: string\n /** The largest function Vercel accepts, unzipped. Default: 250 MB. */\n maxFunctionBytes?: number\n /** The machine the build runs on, which decides the native packages copied. Default: `process.platform`. */\n platform?: string\n}\n\nexport interface VercelBuildResult {\n /** The function's size, unzipped. */\n functionBytes: number\n /** The files Vercel serves without the function. */\n staticFiles: number\n /** What the build got right but the deploy may not: a build off Linux. */\n warnings: string[]\n}\n\n/** Vercel's limit on a function's unzipped size. */\nexport const VERCEL_FUNCTION_LIMIT = 250 * 1024 * 1024\n\n/** The app's own code, copied whole: Oriel finds routes, providers and Live components by reading these folders. */\nconst APP_FOLDERS = ['app', 'bootstrap', 'config', 'routes', 'resources', 'lang', 'database']\nconst APP_FILES = ['package.json', 'tsconfig.json']\n/** What the app reads from public/build at run time: the Vite manifest (a scrambled build's class map is in bootstrap/cache). */\nconst BUILD_FILES = ['manifest.json']\nconst VITE_CONFIGS = ['vite.config.ts', 'vite.config.mts', 'vite.config.js', 'vite.config.mjs']\n\nconst isEnvFile = (path: string) => /^\\.env(\\..*)?$/.test(basename(path))\n/** A SQLite file in `database/`: local data, which the function couldn't write to anyway. */\nconst isSqliteFile = (path: string) => /\\.(sqlite3?|db)(-wal|-shm|-journal)?$/.test(basename(path))\nconst PLATFORM_NAMES: Record<string, string> = { darwin: 'macOS', win32: 'Windows' }\n\n/**\n * Writes Vercel's Build Output API v3 folder for an app: `static/` from `public/`, and one Node function holding the\n * app's code, the build files it reads at run time, its production dependencies and the launcher. `.env` files are\n * never copied: the secrets come from Vercel's environment variables.\n */\nexport async function buildVercelOutput(app: Application, options: VercelBuildOptions = {}): Promise<VercelBuildResult> {\n const base = app.basePath()\n const publicPath = app.publicPath()\n if (existsSync(join(publicPath, 'hot'))) {\n throw new Error('The Vite dev server is running (public/hot exists): stop `npm run dev` and build the assets with `npm run build` first.')\n }\n if (VITE_CONFIGS.some((file) => existsSync(join(base, file))) && !existsSync(join(publicPath, 'build/manifest.json'))) {\n throw new Error(\"The assets aren't built (public/build/manifest.json is missing): run `npm run build` first.\")\n }\n\n const output = resolve(base, options.output ?? '.vercel/output')\n await rm(output, { recursive: true, force: true })\n\n const staticFolder = join(output, 'static')\n await mkdir(staticFolder, { recursive: true })\n if (existsSync(publicPath)) {\n await cp(publicPath, staticFolder, { recursive: true, filter: (source) => basename(source) !== 'hot' && !isEnvFile(source) })\n }\n\n const fn = join(output, 'functions/index.func')\n for (const folder of APP_FOLDERS) {\n const filter = (source: string) => !isEnvFile(source) && !(folder === 'database' && isSqliteFile(source))\n if (existsSync(join(base, folder))) await cp(join(base, folder), join(fn, folder), { recursive: true, filter })\n }\n for (const file of APP_FILES) if (existsSync(join(base, file))) await cp(join(base, file), join(fn, file))\n for (const file of BUILD_FILES) {\n const source = join(publicPath, 'build', file)\n if (existsSync(source)) await cp(source, join(fn, relative(base, publicPath), 'build', file))\n }\n await copyDependencies(base, fn)\n await writeFile(join(fn, 'index.mjs'), LAUNCHER)\n const vcConfig = { runtime: options.runtime ?? 'nodejs22.x', handler: 'index.mjs', launcherType: 'Nodejs', shouldAddHelpers: false, supportsResponseStreaming: true }\n await writeFile(join(fn, '.vc-config.json'), `${JSON.stringify(vcConfig, null, 2)}\\n`)\n const config = {\n version: 3,\n routes: [\n // The built assets' names change with their content, so they're cached for a year, as serve() does.\n { src: '^/build/assets/(.*)$', headers: { 'cache-control': 'public, max-age=31536000, immutable' }, continue: true },\n { handle: 'filesystem' },\n { src: '/(.*)', dest: '/index' },\n ],\n }\n await writeFile(join(output, 'config.json'), `${JSON.stringify(config, null, 2)}\\n`)\n\n const functionBytes = sizeOf(fn)\n const limit = options.maxFunctionBytes ?? VERCEL_FUNCTION_LIMIT\n if (functionBytes > limit) {\n throw new Error(`The function is ${megabytes(functionBytes)}, larger than Vercel allows (${megabytes(limit)}): move what it doesn't need at run time to devDependencies.`)\n }\n const platform = options.platform ?? process.platform\n const warnings =\n platform === 'linux'\n ? []\n : [\n `Built on ${PLATFORM_NAMES[platform] ?? platform}: the function's native packages (such as esbuild, which tsx needs) only run on this kind of machine, and Vercel's functions run on Linux. Let Vercel run the build, or build on Linux.`,\n ]\n return { functionBytes, staticFiles: countFiles(staticFolder), warnings }\n}\n\n/**\n * Copies the app's production dependencies (its `dependencies`, and `tsx`, which the launcher uses) with everything\n * they depend on, found as Node finds them. A package needed in two versions keeps the first at the top and nests the\n * other under the package that needs it.\n */\nasync function copyDependencies(base: string, fn: string): Promise<void> {\n const manifest = JSON.parse(readFileSync(join(base, 'package.json'), 'utf8')) as { dependencies?: Record<string, string> }\n const topLevel = new Map<string, string>()\n const copied = new Set<string>()\n\n const visit = async (name: string, from: string, into: string): Promise<void> => {\n const found = locate(name, from)\n if (found === undefined) return\n const real = realpathSync(found)\n let destination = join(fn, 'node_modules', name)\n const top = topLevel.get(name)\n if (top === undefined) topLevel.set(name, real)\n else if (top !== real) destination = join(into, name)\n if (copied.has(destination)) return\n copied.add(destination)\n await cp(real, destination, {\n recursive: true,\n dereference: true,\n filter: (source) => source === real || !['node_modules', '.bin', '.git'].includes(basename(source)),\n })\n const pkg = JSON.parse(readFileSync(join(real, 'package.json'), 'utf8')) as { dependencies?: Record<string, string>; optionalDependencies?: Record<string, string> }\n for (const dependency of [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.optionalDependencies ?? {})]) {\n await visit(dependency, real, join(destination, 'node_modules'))\n }\n }\n\n for (const name of [...Object.keys(manifest.dependencies ?? {}), 'tsx']) await visit(name, base, join(fn, 'node_modules'))\n}\n\n/** Where Node finds a package from a folder: `node_modules/<name>` in it or any folder above it. */\nfunction locate(name: string, from: string): string | undefined {\n for (let dir = from; ; dir = dirname(dir)) {\n const candidate = basename(dir) === 'node_modules' ? join(dir, name) : join(dir, 'node_modules', name)\n if (existsSync(join(candidate, 'package.json'))) return candidate\n if (dirname(dir) === dir) return undefined\n }\n}\n\nfunction sizeOf(folder: string): number {\n return readdirSync(folder, { withFileTypes: true, recursive: true })\n .filter((entry) => entry.isFile())\n .reduce((total, entry) => total + statSync(join(entry.parentPath, entry.name)).size, 0)\n}\n\nfunction countFiles(folder: string): number {\n return readdirSync(folder, { withFileTypes: true, recursive: true }).filter((entry) => entry.isFile()).length\n}\n\nconst megabytes = (bytes: number) => `${(bytes / 1024 / 1024).toFixed(1)} MB`\n","/**\n * The function's entry (`index.mjs` in `.vercel/output/functions/index.func`): registers tsx as the app's `oriel`\n * file does, boots the app once per function instance, and hands each request to the Node request handler `serve`\n * uses. Public files never reach it: Vercel serves them from `static/`. A boot that fails answers a plain 500, logs\n * the error, and is tried again on the next request. Logs go to stderr unless `LOG_CHANNEL` is set, since the\n * function can't write files, and Vercel's proxy is trusted unless the app lists its own.\n */\nexport const LAUNCHER = `// Oriel on Vercel: boots the app once per function instance and hands it each request.\nimport { fileURLToPath } from 'node:url'\n\nlet ready\n\nasync function boot() {\n // Vercel's disk is read-only, so a log file can't open; stderr is what Vercel's log view shows.\n process.env.LOG_CHANNEL ??= 'stderr'\n if (!process.versions.bun) {\n const { register } = await import('tsx/esm/api')\n register({ tsconfig: fileURLToPath(new URL('./tsconfig.json', import.meta.url)) })\n }\n const { createApplication } = await import('./bootstrap/app.ts')\n const { nodeRequestHandler } = await import('@orieljs/framework')\n const { ConfigRepository } = await import('@orieljs/framework/contracts')\n const app = await createApplication()\n await app.boot()\n // Every request comes through Vercel's proxy, which sets the X-Forwarded-* headers itself: trust it, unless the app\n // lists its own proxies, so that URLs keep the browser's https and ip() is the visitor's.\n const config = app.make(ConfigRepository)\n const proxies = config.get('http.trusted_proxies', [])\n if (proxies === undefined || proxies === null || proxies.length === 0) config.set('http.trusted_proxies', '*')\n return nodeRequestHandler(app, { fallbackHost: 'localhost' })\n}\n\nexport default async function handler(req, res) {\n let handle\n try {\n ready ??= boot()\n handle = await ready\n } catch (error) {\n ready = undefined\n console.error(error)\n if (!res.headersSent) {\n res.statusCode = 500\n res.setHeader('content-type', 'text/plain; charset=utf-8')\n }\n res.end('Internal Server Error')\n return\n }\n await handle(req, res)\n}\n`\n","import { ConsoleKernel, type CommandContext } from '@orieljs/contracts'\nimport { ServiceProvider } from '@orieljs/support'\nimport { buildVercelOutput } from './build'\n\nconst megabytes = (bytes: number) => `${(bytes / 1024 / 1024).toFixed(1)} MB`\n\n/** Registers `oriel vercel:build`, which writes Vercel's build output for the app. */\nexport class VercelServiceProvider extends ServiceProvider {\n override boot(): void {\n if (!this.app.bound(ConsoleKernel)) return\n const app = this.app\n this.app\n .make(ConsoleKernel)\n .command('vercel:build', async function (this: CommandContext) {\n try {\n const { staticFiles, functionBytes, warnings } = await buildVercelOutput(app)\n const files = staticFiles === 1 ? '1 static file' : `${staticFiles} static files`\n this.info(`Wrote .vercel/output: ${files}, a function of ${megabytes(functionBytes)}.`)\n for (const warning of warnings) this.warn(warning)\n return 0\n } catch (error) {\n this.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n })\n .purpose(\"Write Vercel's build output (.vercel/output) for the application\")\n }\n}\n"],"mappings":"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","names":["megabytes"]}
|
|
1
|
+
{"version":3,"sources":["../build.ts","../cron.ts","../launcher.ts","../VercelServiceProvider.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs'\nimport { cp, mkdir, rm, writeFile } from 'node:fs/promises'\nimport { basename, dirname, join, relative, resolve } from 'node:path'\nimport { ConsoleKernel, Schedule, type Application } from '@orieljs/contracts'\nimport { CronExpressionParser } from 'cron-parser'\nimport { DEFAULT_CRON, SCHEDULE_PATH } from './cron'\nimport { LAUNCHER } from './launcher'\n\nexport interface VercelBuildOptions {\n /** Where the build output goes, relative to the app. Default: `.vercel/output` (where Vercel looks for it). */\n output?: string\n /** The function's Node runtime. Default: `nodejs22.x`. */\n runtime?: string\n /** The largest function Vercel accepts, unzipped. Default: 250 MB. */\n maxFunctionBytes?: number\n /** The machine the build runs on, which decides the native packages copied. Default: `process.platform`. */\n platform?: string\n /**\n * When Vercel calls the app's scheduler, as a cron expression (UTC), or `false` for never. Default: `0 3 * * *`,\n * daily at 03:00: Vercel's Hobby plan runs a cron at most once a day. Written only when the app schedules tasks.\n */\n cron?: string | false\n}\n\nexport interface VercelBuildResult {\n /** The function's size, unzipped. */\n functionBytes: number\n /** The files Vercel serves without the function. */\n staticFiles: number\n /** What the build got right but the deploy may not: a build off Linux. */\n warnings: string[]\n}\n\n/** Vercel's limit on a function's unzipped size. */\nexport const VERCEL_FUNCTION_LIMIT = 250 * 1024 * 1024\n\n/** The app's own code, copied whole: Oriel finds routes, providers and Live components by reading these folders. */\nconst APP_FOLDERS = ['app', 'bootstrap', 'config', 'routes', 'resources', 'lang', 'database']\nconst APP_FILES = ['package.json', 'tsconfig.json']\n/** What the app reads from public/build at run time: the Vite manifest (a scrambled build's class map is in bootstrap/cache). */\nconst BUILD_FILES = ['manifest.json']\nconst VITE_CONFIGS = ['vite.config.ts', 'vite.config.mts', 'vite.config.js', 'vite.config.mjs']\n\nconst isEnvFile = (path: string) => /^\\.env(\\..*)?$/.test(basename(path))\n/** A SQLite file in `database/`: local data, which the function couldn't write to anyway. */\nconst isSqliteFile = (path: string) => /\\.(sqlite3?|db)(-wal|-shm|-journal)?$/.test(basename(path))\nconst PLATFORM_NAMES: Record<string, string> = { darwin: 'macOS', win32: 'Windows' }\n\n/**\n * Writes Vercel's Build Output API v3 folder for an app: `static/` from `public/`, and one Node function holding the\n * app's code, the build files it reads at run time, its production dependencies and the launcher. `.env` files are\n * never copied: the secrets come from Vercel's environment variables.\n */\nexport async function buildVercelOutput(app: Application, options: VercelBuildOptions = {}): Promise<VercelBuildResult> {\n const base = app.basePath()\n const publicPath = app.publicPath()\n if (existsSync(join(publicPath, 'hot'))) {\n throw new Error('The Vite dev server is running (public/hot exists): stop `npm run dev` and build the assets with `npm run build` first.')\n }\n if (VITE_CONFIGS.some((file) => existsSync(join(base, file))) && !existsSync(join(publicPath, 'build/manifest.json'))) {\n throw new Error(\"The assets aren't built (public/build/manifest.json is missing): run `npm run build` first.\")\n }\n\n const output = resolve(base, options.output ?? '.vercel/output')\n await rm(output, { recursive: true, force: true })\n\n const staticFolder = join(output, 'static')\n await mkdir(staticFolder, { recursive: true })\n if (existsSync(publicPath)) {\n await cp(publicPath, staticFolder, { recursive: true, filter: (source) => basename(source) !== 'hot' && !isEnvFile(source) })\n }\n\n const fn = join(output, 'functions/index.func')\n for (const folder of APP_FOLDERS) {\n const filter = (source: string) => !isEnvFile(source) && !(folder === 'database' && isSqliteFile(source))\n if (existsSync(join(base, folder))) await cp(join(base, folder), join(fn, folder), { recursive: true, filter })\n }\n for (const file of APP_FILES) if (existsSync(join(base, file))) await cp(join(base, file), join(fn, file))\n for (const file of BUILD_FILES) {\n const source = join(publicPath, 'build', file)\n if (existsSync(source)) await cp(source, join(fn, relative(base, publicPath), 'build', file))\n }\n await copyDependencies(base, fn)\n await writeFile(join(fn, 'index.mjs'), LAUNCHER)\n const vcConfig = { runtime: options.runtime ?? 'nodejs22.x', handler: 'index.mjs', launcherType: 'Nodejs', shouldAddHelpers: false, supportsResponseStreaming: true }\n await writeFile(join(fn, '.vc-config.json'), `${JSON.stringify(vcConfig, null, 2)}\\n`)\n const { crons, cronWarnings } = await scheduleCron(app, options.cron)\n const config = {\n version: 3,\n ...(crons === undefined ? {} : { crons }),\n routes: [\n // The built assets' names change with their content, so they're cached for a year, as serve() does.\n { src: '^/build/assets/(.*)$', headers: { 'cache-control': 'public, max-age=31536000, immutable' }, continue: true },\n { handle: 'filesystem' },\n { src: '/(.*)', dest: '/index' },\n ],\n }\n await writeFile(join(output, 'config.json'), `${JSON.stringify(config, null, 2)}\\n`)\n\n const functionBytes = sizeOf(fn)\n const limit = options.maxFunctionBytes ?? VERCEL_FUNCTION_LIMIT\n if (functionBytes > limit) {\n throw new Error(`The function is ${megabytes(functionBytes)}, larger than Vercel allows (${megabytes(limit)}): move what it doesn't need at run time to devDependencies.`)\n }\n const platform = options.platform ?? process.platform\n const warnings =\n platform === 'linux'\n ? []\n : [\n `Built on ${PLATFORM_NAMES[platform] ?? platform}: the function's native packages (such as esbuild, which tsx needs) only run on this kind of machine, and Vercel's functions run on Linux. Let Vercel run the build, or build on Linux.`,\n ]\n return { functionBytes, staticFiles: countFiles(staticFolder), warnings: [...warnings, ...cronWarnings] }\n}\n\n/**\n * The cron entry Vercel calls the scheduler with, when the app schedules anything, and a warning for each task due more\n * often than it: such a task runs once per trigger. A task counts as more frequent when it falls due in both halves of\n * one trigger period.\n */\nasync function scheduleCron(app: Application, cron: string | false | undefined): Promise<{ crons?: { path: string; schedule: string }[]; cronWarnings: string[] }> {\n if (cron === false) return { cronWarnings: [] }\n if (app.bound(ConsoleKernel)) await app.make(ConsoleKernel).bootstrap()\n if (!app.bound(Schedule)) return { cronWarnings: [] }\n const schedule = app.make(Schedule)\n if (schedule.events().length === 0) return { cronWarnings: [] }\n const expression = cron ?? DEFAULT_CRON\n const fires = CronExpressionParser.parse(expression, { tz: 'UTC' })\n const start = fires.next().toDate()\n const end = fires.next().toDate()\n const middle = new Date((start.getTime() + end.getTime()) / 2)\n const later = new Set(schedule.dueBetween(middle, end))\n const often = schedule.dueBetween(start, middle).filter((event) => later.has(event))\n const plan = cron === undefined ? \" (once a day on Vercel's Hobby plan)\" : ''\n return {\n crons: [{ path: SCHEDULE_PATH, schedule: expression }],\n cronWarnings: often.map((event) => `\"${event.getSummaryForDisplay()}\" (${event.expression}) is due more often than the cron \"${expression}\", so it runs once per trigger${plan}.`),\n }\n}\n\n/**\n * Copies the app's production dependencies (its `dependencies`, and `tsx`, which the launcher uses) with everything\n * they depend on, found as Node finds them. A package needed in two versions keeps the first at the top and nests the\n * other under the package that needs it.\n */\nasync function copyDependencies(base: string, fn: string): Promise<void> {\n const manifest = JSON.parse(readFileSync(join(base, 'package.json'), 'utf8')) as { dependencies?: Record<string, string> }\n const topLevel = new Map<string, string>()\n const copied = new Set<string>()\n\n const visit = async (name: string, from: string, into: string): Promise<void> => {\n const found = locate(name, from)\n if (found === undefined) return\n const real = realpathSync(found)\n let destination = join(fn, 'node_modules', name)\n const top = topLevel.get(name)\n if (top === undefined) topLevel.set(name, real)\n else if (top !== real) destination = join(into, name)\n if (copied.has(destination)) return\n copied.add(destination)\n await cp(real, destination, {\n recursive: true,\n dereference: true,\n filter: (source) => source === real || !['node_modules', '.bin', '.git'].includes(basename(source)),\n })\n const pkg = JSON.parse(readFileSync(join(real, 'package.json'), 'utf8')) as { dependencies?: Record<string, string>; optionalDependencies?: Record<string, string> }\n for (const dependency of [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.optionalDependencies ?? {})]) {\n await visit(dependency, real, join(destination, 'node_modules'))\n }\n }\n\n for (const name of [...Object.keys(manifest.dependencies ?? {}), 'tsx']) await visit(name, base, join(fn, 'node_modules'))\n}\n\n/** Where Node finds a package from a folder: `node_modules/<name>` in it or any folder above it. */\nfunction locate(name: string, from: string): string | undefined {\n for (let dir = from; ; dir = dirname(dir)) {\n const candidate = basename(dir) === 'node_modules' ? join(dir, name) : join(dir, 'node_modules', name)\n if (existsSync(join(candidate, 'package.json'))) return candidate\n if (dirname(dir) === dir) return undefined\n }\n}\n\nfunction sizeOf(folder: string): number {\n return readdirSync(folder, { withFileTypes: true, recursive: true })\n .filter((entry) => entry.isFile())\n .reduce((total, entry) => total + statSync(join(entry.parentPath, entry.name)).size, 0)\n}\n\nfunction countFiles(folder: string): number {\n return readdirSync(folder, { withFileTypes: true, recursive: true }).filter((entry) => entry.isFile()).length\n}\n\nconst megabytes = (bytes: number) => `${(bytes / 1024 / 1024).toFixed(1)} MB`\n","import { timingSafeEqual } from 'node:crypto'\nimport { inject } from '@orieljs/container'\nimport { CacheManager, ConsoleKernel, ExceptionHandler, Schedule } from '@orieljs/contracts'\nimport { CronExpressionParser } from 'cron-parser'\n\n/** Where Vercel's cron calls the app; `oriel vercel:build` puts it in the cron entry. */\nexport const SCHEDULE_PATH = '/_oriel/schedule'\n\n/** The cron `vercel:build` writes by default: daily at 03:00 UTC (Hobby runs a cron at most once a day). */\nexport const DEFAULT_CRON = '0 3 * * *'\n\nconst LAST_RUN = 'oriel:vercel:schedule:last'\nconst LOCK = 'oriel:vercel:schedule'\n\nconst json = (body: unknown, status = 200) => Response.json(body, { status, headers: { 'cache-control': 'no-store' } })\n\n/** Vercel sends `Authorization: Bearer <CRON_SECRET>`; compared in constant time. */\nfunction authorized(header: string | undefined, secret: string): boolean {\n const expected = Buffer.from(`Bearer ${secret}`)\n const given = Buffer.from(header ?? '')\n return given.length === expected.length && timingSafeEqual(given, expected)\n}\n\n/**\n * Where a trigger with no record starts: the trigger's fire time before the current one, so the first trigger covers\n * one full period, as every later one does (for a daily cron, yesterday's run time to now).\n */\nexport function previousTrigger(expression: string, now: Date): Date {\n const parsed = CronExpressionParser.parse(expression, { currentDate: new Date(now.getTime() + 60_000), tz: 'UTC' })\n parsed.prev()\n return parsed.prev().toDate()\n}\n\n/**\n * What Vercel's cron calls. Off (404) without `CRON_SECRET`; 401 for any other bearer token. Otherwise, holding a lock\n * so a doubled call runs nothing twice, it loads the app's schedule (a web request never has: routes/console.ts is the\n * console's) and runs every task due since the previous trigger, once each. A failing task is reported and counted,\n * and never stops the rest; the trigger is recorded either way. 200 `{ ran, failed: 0 }`, or 500 when any failed.\n */\nexport async function runVercelSchedule(authorization: string | undefined, options: { now?: () => Date; trigger?: string } = {}): Promise<Response> {\n const secret = process.env.CRON_SECRET\n if (!secret) return new Response('Not Found', { status: 404 })\n if (!authorized(authorization, secret)) return json({ error: 'Unauthorized' }, 401)\n\n const cache = inject(CacheManager)\n const lock = cache.lock(LOCK, 900)\n if (!(await lock.get())) return json({ ran: 0, failed: 0, skipped: 'busy' })\n try {\n await inject(ConsoleKernel).bootstrap()\n const now = options.now?.() ?? new Date()\n const last = await cache.get<string>(LAST_RUN)\n const from = last === undefined ? previousTrigger(options.trigger ?? process.env.ORIEL_VERCEL_CRON ?? DEFAULT_CRON, now) : new Date(last)\n const due = inject(Schedule).dueBetween(from, now)\n let failed = 0\n for (const event of due) {\n try {\n await event.run()\n } catch (error) {\n failed++\n await inject(ExceptionHandler).report(error)\n }\n }\n await cache.forever(LAST_RUN, now.toISOString())\n return json({ ran: due.length, failed }, failed > 0 ? 500 : 200)\n } finally {\n await lock.release()\n }\n}\n","/**\n * The function's entry (`index.mjs` in `.vercel/output/functions/index.func`): registers tsx as the app's `oriel`\n * file does, boots the app once per function instance, and hands each request to the Node request handler `serve`\n * uses. Public files never reach it: Vercel serves them from `static/`. A boot that fails answers a plain 500, logs\n * the error, and is tried again on the next request. Logs go to stderr unless `LOG_CHANNEL` is set, since the\n * function can't write files, and Vercel's proxy is trusted unless the app lists its own.\n */\nexport const LAUNCHER = `// Oriel on Vercel: boots the app once per function instance and hands it each request.\nimport { fileURLToPath } from 'node:url'\n\nlet ready\n\nasync function boot() {\n // Vercel's disk is read-only, so a log file can't open; stderr is what Vercel's log view shows.\n process.env.LOG_CHANNEL ??= 'stderr'\n if (!process.versions.bun) {\n const { register } = await import('tsx/esm/api')\n register({ tsconfig: fileURLToPath(new URL('./tsconfig.json', import.meta.url)) })\n }\n const { createApplication } = await import('./bootstrap/app.ts')\n const { nodeRequestHandler } = await import('@orieljs/framework')\n const { ConfigRepository } = await import('@orieljs/framework/contracts')\n const app = await createApplication()\n await app.boot()\n // Every request comes through Vercel's proxy, which sets the X-Forwarded-* headers itself: trust it, unless the app\n // lists its own proxies, so that URLs keep the browser's https and ip() is the visitor's.\n const config = app.make(ConfigRepository)\n const proxies = config.get('http.trusted_proxies', [])\n if (proxies === undefined || proxies === null || proxies.length === 0) config.set('http.trusted_proxies', '*')\n return nodeRequestHandler(app, { fallbackHost: 'localhost' })\n}\n\nexport default async function handler(req, res) {\n let handle\n try {\n ready ??= boot()\n handle = await ready\n } catch (error) {\n ready = undefined\n console.error(error)\n if (!res.headersSent) {\n res.statusCode = 500\n res.setHeader('content-type', 'text/plain; charset=utf-8')\n }\n res.end('Internal Server Error')\n return\n }\n await handle(req, res)\n}\n`\n","import { inject } from '@orieljs/container'\nimport { ConsoleKernel, HttpRequest, Router, type CommandContext } from '@orieljs/contracts'\nimport { ServiceProvider } from '@orieljs/support'\nimport { buildVercelOutput } from './build'\nimport { runVercelSchedule, SCHEDULE_PATH } from './cron'\n\nconst megabytes = (bytes: number) => `${(bytes / 1024 / 1024).toFixed(1)} MB`\n\n/** Registers `oriel vercel:build`, which writes Vercel's build output for the app, and the route Vercel Cron calls. */\nexport class VercelServiceProvider extends ServiceProvider {\n override boot(): void {\n // No `web` middleware: a cron call has no session or CSRF token; CRON_SECRET guards it.\n if (this.app.bound(Router)) {\n this.app\n .make(Router)\n .get(SCHEDULE_PATH, () => {\n const request = inject(HttpRequest)\n return runVercelSchedule(request.header('authorization'), { trigger: request.header('x-vercel-cron-schedule') })\n })\n .name('vercel.schedule')\n }\n if (!this.app.bound(ConsoleKernel)) return\n const app = this.app\n this.app\n .make(ConsoleKernel)\n .command('vercel:build {--cron= : When Vercel calls the scheduler, as a cron expression (UTC), or false for never}', async function (this: CommandContext) {\n try {\n const option = this.option('cron')\n const cron = typeof option === 'string' && option !== '' ? (option === 'false' ? false : option) : undefined\n const { staticFiles, functionBytes, warnings } = await buildVercelOutput(app, { cron })\n const files = staticFiles === 1 ? '1 static file' : `${staticFiles} static files`\n this.info(`Wrote .vercel/output: ${files}, a function of ${megabytes(functionBytes)}.`)\n for (const warning of warnings) this.warn(warning)\n return 0\n } catch (error) {\n this.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n })\n .purpose(\"Write Vercel's build output (.vercel/output) for the application\")\n }\n}\n"],"mappings":"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","names":["ConsoleKernel","Schedule","CronExpressionParser","ConsoleKernel","Schedule","CronExpressionParser","inject","ConsoleKernel","megabytes","inject","ConsoleKernel"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orieljs/vercel",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.13",
|
|
4
4
|
"description": "Deploy an Oriel app to Vercel: oriel vercel:build writes Vercel's build output, with the app as a Node function.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -31,8 +31,10 @@
|
|
|
31
31
|
"TRADEMARKS.md"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"
|
|
35
|
-
"@orieljs/
|
|
34
|
+
"cron-parser": "5.10.1",
|
|
35
|
+
"@orieljs/contracts": "0.1.0-alpha.13",
|
|
36
|
+
"@orieljs/support": "0.1.0-alpha.13",
|
|
37
|
+
"@orieljs/container": "0.1.0-alpha.13"
|
|
36
38
|
},
|
|
37
39
|
"scripts": {
|
|
38
40
|
"build": "tsup && node ../../../scripts/copy-license.mjs"
|