diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1e3a9ca8b..e465fdb01 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -21,6 +21,23 @@ Read more: ## v0.18.0 +### Breaking changes + +- Graphile Worker is now published as an ESM package (`"type": "module"`). Use + `import` syntax from ESM consumers. Since Graphile Worker requires Node + 22.18+, CommonJS consumers can continue to load Worker with `require()` via + Node's `require(esm)` support. +- Example `.js` task and configuration files now use ESM syntax. In projects + with `"type": "module"`, `.js` task files must use `export default`; rename + CommonJS tasks/configuration to `.cjs` if you need to keep using + `module.exports`. +- TypeScript task files with `.ts` and `.mts` extensions are now recognized by + default and loaded through Node's native type stripping. Only erasable, + verbatim TypeScript syntax is supported without a custom loader or + precompilation. + +### Changes + - Since Node 20 is EOL, Node 22 is now the minimum supported version, per our [requirements documentation](https://worker.graphile.org/docs/requirements). - `Runner` gains `[Symbol.asyncDispose]()` method, so you can diff --git a/__tests__/batchJobs.test.ts b/__tests__/batchJobs.test.ts index 83c7d5f0b..3f5ecc9eb 100644 --- a/__tests__/batchJobs.test.ts +++ b/__tests__/batchJobs.test.ts @@ -1,6 +1,6 @@ import { jest } from "@jest/globals"; -import { Task, TaskList, WorkerSharedOptions } from "../src/interfaces.ts"; +import type { Task, TaskList, WorkerSharedOptions } from "../src/interfaces.ts"; import { runTaskListOnce } from "../src/main.ts"; import { ESCAPED_GRAPHILE_WORKER_SCHEMA, diff --git a/__tests__/crontab.test.ts b/__tests__/crontab.test.ts index a4c4aa8e0..817dc7a46 100644 --- a/__tests__/crontab.test.ts +++ b/__tests__/crontab.test.ts @@ -1,5 +1,5 @@ import { parseCronItem, parseCrontab } from "../src/crontab.ts"; -import { CronItemOptions, ParsedCronMatch } from "../src/index.ts"; +import type { CronItemOptions, ParsedCronMatch } from "../src/index.ts"; // 0...59 const ALL_MINUTES = Array.from(Array(60).keys()); diff --git a/__tests__/events.test.ts b/__tests__/events.test.ts index ac81fa2ea..e6e37ff4e 100644 --- a/__tests__/events.test.ts +++ b/__tests__/events.test.ts @@ -2,9 +2,11 @@ import { jest } from "@jest/globals"; import { EventEmitter } from "events"; import type { Pool } from "pg"; -import deferred, { Deferred } from "../src/deferred.ts"; -import { run, Runner } from "../src/index.ts"; -import { Task, TaskList, WorkerSharedOptions } from "../src/interfaces.ts"; +import type { Deferred } from "../src/deferred.ts"; +import deferred from "../src/deferred.ts"; +import type { Runner } from "../src/index.ts"; +import { run } from "../src/index.ts"; +import type { Task, TaskList, WorkerSharedOptions } from "../src/interfaces.ts"; import { ESCAPED_GRAPHILE_WORKER_SCHEMA, expectJobCount, diff --git a/__tests__/fixtures-esm/tasks/module-typescript.mts b/__tests__/fixtures-esm/tasks/module-typescript.mts new file mode 100644 index 000000000..4a7dafae6 --- /dev/null +++ b/__tests__/fixtures-esm/tasks/module-typescript.mts @@ -0,0 +1,3 @@ +export default function moduleTypescriptTask() { + return "some module typescript"; +} diff --git a/__tests__/fixtures-esm/tasks/typescript.ts b/__tests__/fixtures-esm/tasks/typescript.ts new file mode 100644 index 000000000..6a70ffe88 --- /dev/null +++ b/__tests__/fixtures-esm/tasks/typescript.ts @@ -0,0 +1,6 @@ +import type { JobHelpers } from "../../../src/interfaces.ts"; + +export default function typescriptTask(_payload: unknown, helpers: JobHelpers) { + helpers.logger.debug("typescript task"); + return "some typescript"; +} diff --git a/__tests__/forbiddenFlags.test.ts b/__tests__/forbiddenFlags.test.ts index 5ec167d23..4a3752247 100644 --- a/__tests__/forbiddenFlags.test.ts +++ b/__tests__/forbiddenFlags.test.ts @@ -1,12 +1,7 @@ import { jest } from "@jest/globals"; -import { - makeWorkerUtils, - runTaskListOnce, - Task, - TaskList, - WorkerSharedOptions, -} from "../src/index.ts"; +import type { Task, TaskList, WorkerSharedOptions } from "../src/index.ts"; +import { makeWorkerUtils, runTaskListOnce } from "../src/index.ts"; import { getJobs, reset, withPgClient, withPgPool } from "./helpers.ts"; const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/__tests__/getTasks.test.ts b/__tests__/getTasks.test.ts index ee9d37aa1..d928afa52 100644 --- a/__tests__/getTasks.test.ts +++ b/__tests__/getTasks.test.ts @@ -1,6 +1,6 @@ import { getTasks } from "../src/getTasks.ts"; import { makeJobHelpers, makeWithPgClientFromClient } from "../src/helpers.ts"; -import { +import type { CompiledSharedOptions, WatchedTaskList, WorkerSharedOptions, @@ -48,6 +48,12 @@ describe("commonjs", () => { expect( await tasks.wouldyoulike_default!(helpers.job.payload, helpers), ).toEqual("some more sausages"); + expect(await tasks.typescript!(helpers.job.payload, helpers)).toEqual( + "some typescript", + ); + expect( + await tasks["module-typescript"]!(helpers.job.payload, helpers), + ).toEqual("some module typescript"); expect( await tasks.wouldyoulike_ts!(helpers.job.payload, helpers), ).toEqual("some TS sausages"); @@ -191,6 +197,8 @@ describe("esm", () => { expect(tasks).toBeTruthy(); expect(Object.keys(tasks).sort()).toMatchInlineSnapshot(` [ + "module-typescript", + "typescript", "wouldyoulike", "wouldyoulike_default", ] @@ -212,6 +220,12 @@ describe("esm", () => { expect( await tasks.wouldyoulike_default!(helpers.job.payload, helpers), ).toEqual("some more sausages"); + expect(await tasks.typescript!(helpers.job.payload, helpers)).toEqual( + "some typescript", + ); + expect( + await tasks["module-typescript"]!(helpers.job.payload, helpers), + ).toEqual("some module typescript"); await release(); })); diff --git a/__tests__/helpers.ts b/__tests__/helpers.ts index 1217f8076..b50ac6c6b 100644 --- a/__tests__/helpers.ts +++ b/__tests__/helpers.ts @@ -8,7 +8,7 @@ import { import pg from "pg"; import defer from "../src/deferred.ts"; -import { +import type { DbJob, Job, KnownCrontab, diff --git a/__tests__/jobsView.test.ts b/__tests__/jobsView.test.ts index 3a9f4ccef..130258e80 100644 --- a/__tests__/jobsView.test.ts +++ b/__tests__/jobsView.test.ts @@ -1,4 +1,5 @@ -import { Job, makeWorkerUtils, WorkerSharedOptions } from "../src/index.ts"; +import type { Job, WorkerSharedOptions } from "../src/index.ts"; +import { makeWorkerUtils } from "../src/index.ts"; import { ESCAPED_GRAPHILE_WORKER_SCHEMA, makeSelectionOfJobs, diff --git a/__tests__/main.runTaskList.test.ts b/__tests__/main.runTaskList.test.ts index 773a538dd..c02ff2d23 100644 --- a/__tests__/main.runTaskList.test.ts +++ b/__tests__/main.runTaskList.test.ts @@ -2,8 +2,14 @@ import { jest } from "@jest/globals"; import type { Pool } from "pg"; -import deferred, { Deferred } from "../src/deferred.ts"; -import { Job, Task, TaskList, WorkerSharedOptions } from "../src/interfaces.ts"; +import type { Deferred } from "../src/deferred.ts"; +import deferred from "../src/deferred.ts"; +import type { + Job, + Task, + TaskList, + WorkerSharedOptions, +} from "../src/interfaces.ts"; import { runTaskList } from "../src/main.ts"; import { ESCAPED_GRAPHILE_WORKER_SCHEMA, diff --git a/__tests__/main.runTaskListOnce.test.ts b/__tests__/main.runTaskListOnce.test.ts index efe388642..549a0b248 100644 --- a/__tests__/main.runTaskListOnce.test.ts +++ b/__tests__/main.runTaskListOnce.test.ts @@ -1,7 +1,8 @@ import { jest } from "@jest/globals"; -import defer, { Deferred } from "../src/deferred.ts"; -import { +import type { Deferred } from "../src/deferred.ts"; +import defer from "../src/deferred.ts"; +import type { DbJob, Task, TaskList, diff --git a/__tests__/migrate.test.ts b/__tests__/migrate.test.ts index 10c2d974a..8a211dbb3 100644 --- a/__tests__/migrate.test.ts +++ b/__tests__/migrate.test.ts @@ -1,7 +1,7 @@ import type { PoolClient } from "pg"; import { migrations } from "../src/generated/sql.ts"; -import { WorkerSharedOptions } from "../src/index.ts"; +import type { WorkerSharedOptions } from "../src/index.ts"; import { processSharedOptions } from "../src/lib.ts"; import { installSchema, migrate, runMigration } from "../src/migrate.ts"; import { diff --git a/__tests__/nodeTime.test.ts b/__tests__/nodeTime.test.ts index 039b002f0..68584d4b0 100644 --- a/__tests__/nodeTime.test.ts +++ b/__tests__/nodeTime.test.ts @@ -1,7 +1,7 @@ import { jest } from "@jest/globals"; import { run, runTaskListOnce } from "../src/index.ts"; -import { WorkerSharedOptions } from "../src/interfaces.ts"; +import type { WorkerSharedOptions } from "../src/interfaces.ts"; import { ESCAPED_GRAPHILE_WORKER_SCHEMA, EventMonitor, diff --git a/__tests__/resetLockedAt.test.ts b/__tests__/resetLockedAt.test.ts index 630c13ae6..dd5381574 100644 --- a/__tests__/resetLockedAt.test.ts +++ b/__tests__/resetLockedAt.test.ts @@ -1,7 +1,7 @@ import { jest } from "@jest/globals"; import { EventEmitter } from "events"; -import { +import type { Task, TaskList, WorkerEvents, diff --git a/__tests__/runner.helpers.getTaskName.test.ts b/__tests__/runner.helpers.getTaskName.test.ts index 8a23df8b0..3c8ef9bc4 100644 --- a/__tests__/runner.helpers.getTaskName.test.ts +++ b/__tests__/runner.helpers.getTaskName.test.ts @@ -1,7 +1,7 @@ import type { Pool, PoolClient } from "pg"; import pg from "pg"; -import { DbJobSpec, RunnerOptions } from "../src/interfaces.ts"; +import type { DbJobSpec, RunnerOptions } from "../src/interfaces.ts"; import { run } from "../src/runner.ts"; import { databaseDetails, diff --git a/__tests__/runner.runOnce.test.ts b/__tests__/runner.runOnce.test.ts index 54cc0dca7..fba90a1c9 100644 --- a/__tests__/runner.runOnce.test.ts +++ b/__tests__/runner.runOnce.test.ts @@ -1,7 +1,7 @@ import pg from "pg"; import { makeWorkerPresetWorkerOptions } from "../src/config.ts"; -import { Job, RunnerOptions, WorkerUtils } from "../src/interfaces.ts"; +import type { Job, RunnerOptions, WorkerUtils } from "../src/interfaces.ts"; import { coerceError } from "../src/lib.ts"; import { _allWorkerPools } from "../src/main.ts"; import { WorkerPreset } from "../src/preset.ts"; diff --git a/__tests__/workerUtils.addJob.test.ts b/__tests__/workerUtils.addJob.test.ts index efe5e0fa9..ae244e0e2 100644 --- a/__tests__/workerUtils.addJob.test.ts +++ b/__tests__/workerUtils.addJob.test.ts @@ -1,13 +1,7 @@ import { jest } from "@jest/globals"; -import { - addJobAdhoc, - makeWorkerUtils, - runTaskListOnce, - Task, - WorkerSharedOptions, - WorkerUtils, -} from "../src/index.ts"; +import type { Task, WorkerSharedOptions, WorkerUtils } from "../src/index.ts"; +import { addJobAdhoc, makeWorkerUtils, runTaskListOnce } from "../src/index.ts"; import { getJobs, HOUR, diff --git a/__tests__/workerUtils.addJobs.test.ts b/__tests__/workerUtils.addJobs.test.ts index c6ee74de6..de3932237 100644 --- a/__tests__/workerUtils.addJobs.test.ts +++ b/__tests__/workerUtils.addJobs.test.ts @@ -1,14 +1,12 @@ import { jest } from "@jest/globals"; -import type { Job } from "../src/index.ts"; -import { - addJobAdhoc, - makeWorkerUtils, - runTaskListOnce, +import type { + Job, Task, WorkerSharedOptions, WorkerUtils, } from "../src/index.ts"; +import { addJobAdhoc, makeWorkerUtils, runTaskListOnce } from "../src/index.ts"; import { getJobs, HOUR, diff --git a/__tests__/workerUtils.cleanup.test.ts b/__tests__/workerUtils.cleanup.test.ts index ab0733312..723f78f85 100644 --- a/__tests__/workerUtils.cleanup.test.ts +++ b/__tests__/workerUtils.cleanup.test.ts @@ -1,10 +1,10 @@ -import { +import type { DbJob, Job, - makeWorkerUtils, WorkerSharedOptions, WorkerUtils, } from "../src/index.ts"; +import { makeWorkerUtils } from "../src/index.ts"; import { ESCAPED_GRAPHILE_WORKER_SCHEMA, makeSelectionOfJobs, diff --git a/__tests__/workerUtils.completeJobs.test.ts b/__tests__/workerUtils.completeJobs.test.ts index ca018c578..26b237584 100644 --- a/__tests__/workerUtils.completeJobs.test.ts +++ b/__tests__/workerUtils.completeJobs.test.ts @@ -1,8 +1,5 @@ -import { - makeWorkerUtils, - WorkerSharedOptions, - WorkerUtils, -} from "../src/index.ts"; +import type { WorkerSharedOptions, WorkerUtils } from "../src/index.ts"; +import { makeWorkerUtils } from "../src/index.ts"; import { getJobs, makeSelectionOfJobs, diff --git a/__tests__/workerUtils.forceUnlockWorkers.test.ts b/__tests__/workerUtils.forceUnlockWorkers.test.ts index 90fa7b1d5..c6e97f5eb 100644 --- a/__tests__/workerUtils.forceUnlockWorkers.test.ts +++ b/__tests__/workerUtils.forceUnlockWorkers.test.ts @@ -1,9 +1,5 @@ -import { - Job, - makeWorkerUtils, - WorkerSharedOptions, - WorkerUtils, -} from "../src/index.ts"; +import type { Job, WorkerSharedOptions, WorkerUtils } from "../src/index.ts"; +import { makeWorkerUtils } from "../src/index.ts"; import { ESCAPED_GRAPHILE_WORKER_SCHEMA, getJobs, diff --git a/__tests__/workerUtils.permanentlyFailJobs.test.ts b/__tests__/workerUtils.permanentlyFailJobs.test.ts index bf14eda70..61ac5debb 100644 --- a/__tests__/workerUtils.permanentlyFailJobs.test.ts +++ b/__tests__/workerUtils.permanentlyFailJobs.test.ts @@ -1,8 +1,5 @@ -import { - makeWorkerUtils, - WorkerSharedOptions, - WorkerUtils, -} from "../src/index.ts"; +import type { WorkerSharedOptions, WorkerUtils } from "../src/index.ts"; +import { makeWorkerUtils } from "../src/index.ts"; import { getJobs, makeSelectionOfJobs, diff --git a/__tests__/workerUtils.rescheduleJobs.test.ts b/__tests__/workerUtils.rescheduleJobs.test.ts index ae29662dd..7f612d238 100644 --- a/__tests__/workerUtils.rescheduleJobs.test.ts +++ b/__tests__/workerUtils.rescheduleJobs.test.ts @@ -1,8 +1,5 @@ -import { - makeWorkerUtils, - WorkerSharedOptions, - WorkerUtils, -} from "../src/index.ts"; +import type { WorkerSharedOptions, WorkerUtils } from "../src/index.ts"; +import { makeWorkerUtils } from "../src/index.ts"; import { getJobs, makeSelectionOfJobs, diff --git a/examples/readme/events.js b/examples/readme/events.js index e2e4b1a9a..e1c6f3606 100644 --- a/examples/readme/events.js +++ b/examples/readme/events.js @@ -1,6 +1,5 @@ -const { run, addJobAdhoc } = require( - /* "graphile-worker" */ "../../dist/index.js", -); +/* eslint-disable import-x/no-unresolved */ +import { addJobAdhoc, run } from /* "graphile-worker" */ "../../dist/index.js"; async function main() { // Run a worker to execute jobs: diff --git a/examples/readme/tasks/task_2.js b/examples/readme/tasks/task_2.js index ee64cdb4a..c02b97d2c 100644 --- a/examples/readme/tasks/task_2.js +++ b/examples/readme/tasks/task_2.js @@ -1,4 +1,4 @@ -module.exports = async (payload, helpers) => { +export default async function task2(payload, helpers) { // async is optional, but best practice helpers.logger.debug(`Received ${JSON.stringify(payload)}`); -}; +} diff --git a/examples/worker-bullmq-exporter/tasks/bullmq-exporter.js b/examples/worker-bullmq-exporter/tasks/bullmq-exporter.js index 4475c78e2..3d64ae0c1 100644 --- a/examples/worker-bullmq-exporter/tasks/bullmq-exporter.js +++ b/examples/worker-bullmq-exporter/tasks/bullmq-exporter.js @@ -1,4 +1,5 @@ -const { Queue } = require("bullmq"); +/* eslint-disable import-x/no-unresolved */ +import { Queue } from "bullmq"; const defaultQueueName = "database-events"; const queueName = process.env.QUEUE_NAME || defaultQueueName; diff --git a/examples/worker-cloud-tasks-exporter/README.md b/examples/worker-cloud-tasks-exporter/README.md index b20dab492..b2eb0279a 100644 --- a/examples/worker-cloud-tasks-exporter/README.md +++ b/examples/worker-cloud-tasks-exporter/README.md @@ -47,7 +47,7 @@ To create tasks in Cloud Tasks ```js // tasks/cloud-tasks-exporter.js -const { CloudTasksClient } = require("@google-cloud/tasks"); +import { CloudTasksClient } from "@google-cloud/tasks"; const client = new CloudTasksClient(); @@ -110,7 +110,7 @@ To run Graphile Worker task ```js // tasks/cloud-tasks-exporter.js -module.exports = async (payload, { logger }) => { +export default async function task(payload, { logger }) { logger.info( `Delegating task to Cloud Tasks with payload ${JSON.stringify(payload)}`, ); @@ -122,7 +122,7 @@ module.exports = async (payload, { logger }) => { }); logger.info("Done!"); -}; +} ``` ## Run the worker and add a job diff --git a/examples/worker-cloud-tasks-exporter/tasks/cloud-tasks-exporter.js b/examples/worker-cloud-tasks-exporter/tasks/cloud-tasks-exporter.js index 51a13ff40..6e956e97c 100644 --- a/examples/worker-cloud-tasks-exporter/tasks/cloud-tasks-exporter.js +++ b/examples/worker-cloud-tasks-exporter/tasks/cloud-tasks-exporter.js @@ -1,4 +1,5 @@ -const { CloudTasksClient } = require("@google-cloud/tasks"); +/* eslint-disable import-x/no-unresolved */ +import { CloudTasksClient } from "@google-cloud/tasks"; const client = new CloudTasksClient(); @@ -56,7 +57,7 @@ async function createTask({ } // Graphile Worker Task -module.exports = async (payload, { logger }) => { +export default async function cloudTasksExporter(payload, { logger }) { logger.info( `Delegating task to Cloud Tasks with payload ${JSON.stringify(payload)}`, ); @@ -68,4 +69,4 @@ module.exports = async (payload, { logger }) => { }); logger.info("Done!"); -}; +} diff --git a/examples/worker-faktory-exporter/README.md b/examples/worker-faktory-exporter/README.md index b58b04044..7cc25930a 100644 --- a/examples/worker-faktory-exporter/README.md +++ b/examples/worker-faktory-exporter/README.md @@ -22,9 +22,9 @@ job from a queue. ```JS // tasks/faktory-export.js -const faktory = require("faktory-worker"); +import faktory from "faktory-worker"; -module.exports = async (payload, helpers) => { +export default async function task(payload, helpers) { const { param } = payload; const { logger } = helpers; diff --git a/examples/worker-faktory-exporter/tasks/faktory-exporter.js b/examples/worker-faktory-exporter/tasks/faktory-exporter.js index 331204e77..546123070 100644 --- a/examples/worker-faktory-exporter/tasks/faktory-exporter.js +++ b/examples/worker-faktory-exporter/tasks/faktory-exporter.js @@ -1,6 +1,7 @@ -const faktory = require("faktory-worker"); +/* eslint-disable import-x/no-unresolved */ +import faktory from "faktory-worker"; -module.exports = async (payload, helpers) => { +export default async function faktoryExporter(payload, helpers) { const { param } = payload; const { logger } = helpers; @@ -17,4 +18,4 @@ module.exports = async (payload, helpers) => { logger.info(`Received jid from Faktory: ${jid}. Thanks Faktory!`); await faktoryClient.close(); -}; +} diff --git a/jest.config.js b/jest.config.cjs similarity index 91% rename from jest.config.js rename to jest.config.cjs index d004e069b..b1f37b9a3 100644 --- a/jest.config.js +++ b/jest.config.cjs @@ -8,7 +8,7 @@ module.exports = { }, testRegex: "(/__tests__/.*\\.(test|spec))\\.[tj]sx?$", moduleFileExtensions: ["ts", "mjs", "js", "json"], - extensionsToTreatAsEsm: [], + extensionsToTreatAsEsm: [".ts"], moduleNameMapper: { "^(\\.{1,2}/.*)\\.js$": "$1", }, diff --git a/src/config.ts b/src/config.ts index 56ab11c31..07c5c7dda 100644 --- a/src/config.ts +++ b/src/config.ts @@ -28,7 +28,7 @@ export const makeWorkerPresetWorkerOptions = () => preparedStatements: true as boolean, crontabFile: `${process.cwd()}/crontab`, taskDirectory: `${process.cwd()}/tasks`, - fileExtensions: [".js", ".cjs", ".mjs"], + fileExtensions: [".js", ".cjs", ".mjs", ".ts", ".mts"], logger: defaultLogger, minResetLockedInterval: 8 * MINUTE, maxResetLockedInterval: 10 * MINUTE, diff --git a/src/index.ts b/src/index.ts index 37a537f68..2ea1a4551 100644 --- a/src/index.ts +++ b/src/index.ts @@ -186,7 +186,7 @@ declare global { * should attempt to import as Node modules when loading task executors from * the file system. * - * @defaultValue `[".js", ".cjs", ".mjs"]` + * @defaultValue `[".js", ".cjs", ".mjs", ".ts", ".mts"]` */ fileExtensions?: string[]; diff --git a/src/plugins/LoadTaskFromJsPlugin.ts b/src/plugins/LoadTaskFromJsPlugin.ts index 3a15dbfd4..b3c24280d 100644 --- a/src/plugins/LoadTaskFromJsPlugin.ts +++ b/src/plugins/LoadTaskFromJsPlugin.ts @@ -6,7 +6,7 @@ import { isValidTask } from "../index.ts"; import { coerceError } from "../lib.ts"; import { version } from "../version.ts"; -const DEFAULT_EXTENSIONS = [".js", ".mjs", ".cjs"]; +const DEFAULT_EXTENSIONS = [".js", ".cjs", ".mjs", ".ts", ".mts"]; export const LoadTaskFromJsPlugin: GraphileConfig.Plugin = { name: "LoadTaskFromJsPlugin", diff --git a/tsconfig.json b/tsconfig.json index fbf770bdf..0ed5908cb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,10 +6,8 @@ "compilerOptions": { "declarationDir": "./dist", "outDir": "./dist", - - "verbatimModuleSyntax": false, + "verbatimModuleSyntax": true, "isolatedModules": true, - "declaration": true, "sourceMap": true, "noFallthroughCasesInSwitch": true, diff --git a/website/docs/cli/index.md b/website/docs/cli/index.md index 62154b15c..7854408a2 100644 --- a/website/docs/cli/index.md +++ b/website/docs/cli/index.md @@ -24,10 +24,10 @@ Create a `tasks/` folder, and place in it JS files containing your task specs. The names of these files will be the task identifiers, e.g. `hello` below: ```js title="tasks/hello.js" -module.exports = async (payload, helpers) => { +export default async function hello(payload, helpers) { const { name } = payload; helpers.logger.info(`Hello, ${name}`); -}; +} ``` ### Run the worker diff --git a/website/docs/config.md b/website/docs/config.md index 03420ecb9..950c6ee23 100644 --- a/website/docs/config.md +++ b/website/docs/config.md @@ -28,10 +28,10 @@ We therefore recommend that the preset be the default export of a Here's an example in JavaScript: -```ts title="graphile.config.js" -const { WorkerPreset } = require("graphile-worker"); +```js title="graphile.config.js" +import { WorkerPreset } from "graphile-worker"; -module.exports = { +export default { extends: [WorkerPreset], worker: { connectionString: process.env.DATABASE_URL, @@ -41,7 +41,7 @@ module.exports = { schema: "graphile_worker", crontabFile: "crontab", concurrentJobs: 1, - fileExtensions: [".js", ".cjs", ".mjs"], + fileExtensions: [".js", ".cjs", ".mjs", ".ts", ".mts"], }, }; ``` @@ -61,7 +61,7 @@ const preset: GraphileConfig.Preset = { schema: "graphile_worker", crontabFile: "crontab", concurrentJobs: 1, - fileExtensions: [".js", ".cjs", ".mjs"], + fileExtensions: [".js", ".cjs", ".mjs", ".ts", ".mts"], }, }; diff --git a/website/docs/contributing.md b/website/docs/contributing.md index 9e54c3676..7f00f6169 100644 --- a/website/docs/contributing.md +++ b/website/docs/contributing.md @@ -95,7 +95,7 @@ you should create a tasks folder first (but not in the root!): ```sh yarn prepack mkdir -p _LOCAL/tasks -echo 'module.exports = () => {}' > _LOCAL/tasks/hello.js +echo 'export default function hello() {}' > _LOCAL/tasks/hello.js cd _LOCAL node ../dist/cli.js -c "postgres:///my_db" ``` diff --git a/website/docs/library/index.md b/website/docs/library/index.md index 97c189e3c..8db387e85 100644 --- a/website/docs/library/index.md +++ b/website/docs/library/index.md @@ -26,7 +26,7 @@ The following is equivalent to the setup in [the CLI quickstart](/docs/cli#quickstart): ```js -const { run } = require("graphile-worker"); +import { run } from "graphile-worker"; async function main() { // Run a worker to execute jobs: @@ -62,12 +62,20 @@ main().catch((err) => { }); ``` +:::tip CommonJS + +Graphile Worker is published as ESM, but the minimum supported Node.js version +supports `require(esm)`. If your application is still CommonJS, you may continue +to load the library with `const { run } = require("graphile-worker")`. + +::: + ### Add a job via the library You can also use the library to quickly add a job: ```js -const { addJobAdhoc } = require("graphile-worker"); +import { addJobAdhoc } from "graphile-worker"; addJobAdhoc( // makeWorkerUtils options diff --git a/website/docs/library/logger.md b/website/docs/library/logger.md index e8b5953df..672931973 100644 --- a/website/docs/library/logger.md +++ b/website/docs/library/logger.md @@ -24,7 +24,7 @@ You may customize where log messages from `graphile-worker` (and your tasks) go by supplying a custom `Logger` instance using your own `logFactory`. ```js -const { Logger, run } = require("graphile-worker"); +import { Logger, run } from "graphile-worker"; /* Replace this function with your own implementation */ function logFactory(scope) { diff --git a/website/docs/library/queue.md b/website/docs/library/queue.md index c1e12fd6f..e81edef13 100644 --- a/website/docs/library/queue.md +++ b/website/docs/library/queue.md @@ -33,7 +33,7 @@ Useful for adding jobs from within JavaScript in an efficient way. Runnable example: ```js -const { makeWorkerUtils } = require("graphile-worker"); +import { makeWorkerUtils } from "graphile-worker"; async function main() { const workerUtils = await makeWorkerUtils({ @@ -143,7 +143,7 @@ one-off scripts this convenience method may be enough. Runnable example: ```js -const { addJobAdhoc } = require("graphile-worker"); +import { addJobAdhoc } from "graphile-worker"; async function main() { await addJobAdhoc( diff --git a/website/docs/performance.md b/website/docs/performance.md index 47b1a0931..8c751f343 100644 --- a/website/docs/performance.md +++ b/website/docs/performance.md @@ -18,7 +18,7 @@ The above stats were achieved with this configuration: const preset = { worker: { connectionString: "postgres:///graphile_worker_perftest", - fileExtensions: [".js", ".cjs", ".mjs"], + fileExtensions: [".js", ".cjs", ".mjs", ".ts", ".mts"], concurrentJobs: 24, maxPoolSize: 25, @@ -157,7 +157,7 @@ apps, and what not). const preset = { worker: { connectionString: "postgres:///graphile_worker_perftest", - fileExtensions: [".js", ".cjs", ".mjs"], + fileExtensions: [".js", ".cjs", ".mjs", ".ts", ".mts"], concurrentJobs: 24, maxPoolSize: 25, @@ -199,7 +199,7 @@ Latencies - min: 3.24ms, max: 18.18ms, avg: 4.28ms const preset = { worker: { connectionString: "postgres:///graphile_worker_perftest", - fileExtensions: [".js", ".cjs", ".mjs"], + fileExtensions: [".js", ".cjs", ".mjs", ".ts", ".mts"], concurrentJobs: 24, maxPoolSize: 25, diff --git a/website/docs/requirements.md b/website/docs/requirements.md index 91ed1c98a..4109244db 100644 --- a/website/docs/requirements.md +++ b/website/docs/requirements.md @@ -3,18 +3,13 @@ title: Requirements sidebar_position: 30 --- -The current version of Graphile Worker requires PostgreSQL 12+ and Node 18+[^1]. +The current version of Graphile Worker requires PostgreSQL 12+ and Node 22.18+. Once a version of PostgreSQL or Node.js reaches end of life, we also no longer support it and may drop support in a minor update. Should you require support for an end-of-life version of one of these projects, please [get in touch about our commercial support options](https://graphile.org/support/). -[^1]: - Might work with older versions, but has not been tested. Node 18 won't run - our jest tests due to segfault, fixed in Node 20.8.1, so CI cannot run - against Node 18. - :::note `graphile-worker` versions before 0.13.0 installed the `pgcrypto` extension into @@ -29,8 +24,9 @@ for instructions. :::note Postgres 12 is required for the `generated always as (expression)` feature; if -you need to use earlier versions of Postgres or Node, please use version 0.13.x -or earlier. +you need to use earlier versions of Postgres, please use version 0.13.x or +earlier. For earlier versions of Node.js, use a Graphile Worker release that +supports your Node.js runtime. ::: diff --git a/website/docs/tasks.md b/website/docs/tasks.md index e04489ca7..d8a2c53e0 100644 --- a/website/docs/tasks.md +++ b/website/docs/tasks.md @@ -54,16 +54,16 @@ extra side effects) — for example sending emails. ## Example JS task executors ```js title="tasks/task_1.js" -module.exports = async (payload) => { +export default async function task1(payload) { await doMyLogicWith(payload); -}; +} ``` ```js title="tasks/task_2.js" -module.exports = async (payload, helpers) => { +export default async function task2(payload, helpers) { // async is optional, but best practice helpers.logger.debug(`Received ${JSON.stringify(payload)}`); -}; +} ``` ## The task directory @@ -89,11 +89,12 @@ the plugins you have loaded. ## Loading JavaScript files -With the default preset, Graphile Worker will load `.js`, `.cjs` and `.mjs` -files as task executors using the `import()` function. If the file is a CommonJS -module, then Worker will expect `module.exports` to be the task executor -function; if the file is an ECMAScript module (ESM) then Worker will expect the -default export to be the task executor function. +With the default preset, Graphile Worker will load `.js`, `.cjs`, `.mjs`, `.ts`, +and `.mts` files as task executors using the `import()` function. Graphile +Worker is now an ESM package, so `.js` task files in a project with +`"type": "module"` should use ESM syntax and export the task executor as the +default export. If you need a CommonJS task, name it with the `.cjs` extension +and export the executor with `module.exports`. You can add support for other ways of loading task executors via plugins; look at the source code of @@ -102,47 +103,25 @@ for inspiration. ### Loading TypeScript files -:::info - -You might not need to use ts-node anymore since Node.js has native type -stripping support. These docs need to be updated. +Graphile Worker includes `.ts` and `.mts` in its default task file extensions. +On the supported Node.js versions, these files are loaded by Node's native type +stripping, so no custom loader is required for erasable TypeScript syntax. -::: +```ts title="tasks/send_email.ts" +import type { JobHelpers } from "graphile-worker"; -:::tip - -For performance and memory usage reasons, we recommend that you compile -TypeScript files to JS and then have Graphile Worker load the JS files. - -::: - -To load TypeScript files directly as task executors (without precompilation), -one way is to do the following: - -1. Install `ts-node`. -2. Add `".ts"` to the `worker.fileExtensions` list in your preset. -3. Run Graphile Worker with the environment variable - `NODE_OPTIONS="--loader ts-node/esm"` set. - -```ts title="Example graphile.config.ts" -import { WorkerPreset } from "graphile-worker"; - -const preset: GraphileConfig.Preset = { - extends: [WorkerPreset], - worker: { - connectionString: process.env.DATABASE_URL, - concurrentJobs: 5, - fileExtensions: [".js", ".cjs", ".mjs", ".ts", ".cts", ".mts"], - }, -}; - -export default preset; +export default async function sendEmail( + payload: { to: string }, + helpers: JobHelpers, +) { + helpers.logger.info(`Sending email to ${payload.to}`); +} ``` -```bash title="Running graphile-worker with '--loader ts-node/esm'" -NODE_OPTIONS="--loader ts-node/esm" graphile-worker -c ... -# OR: node --loader ts-node/esm node_modules/.bin/graphile-worker -c ... -``` +Native type stripping only supports TypeScript syntax that can be erased without +code generation. If your tasks use TypeScript features that require transforms, +compile them to JavaScript before running Worker or configure your own loader +and `worker.fileExtensions` list. ## Loading executable files