From 439de612c17e7e10e310536921adbf5c81c829b3 Mon Sep 17 00:00:00 2001 From: thelastbackspace <23155651+thelastbackspace@users.noreply.github.com> Date: Sat, 7 Nov 2026 09:56:30 +0100 Subject: [PATCH] feat: node-redis instrumentation; Prisma, Sequelize, Knex and node-redis in the driver matrix - instrumentNodeRedis for redis / @redis/client 4 and 5 (MULTI and pipeline commands tracked one by one), auto target 'redis' - sqlLabel: CREATE/DROP TABLE IF [NOT] EXISTS are labelled by table - test/compat: redis 4/5, Prisma 5/6 on SQLite (prisma-setup.js), Sequelize 6 and Knex 2/3 on pg and mysql2 --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 6 +- README.md | 7 +- package-lock.json | 217 ++++++++ package.json | 2 + src/__tests__/drivers.test.ts | 96 ++++ src/auto.ts | 9 +- src/drivers.ts | 91 +++- src/register.ts | 2 +- test/compat/.gitignore | 2 + test/compat/compat.js | 124 ++++- test/compat/package-lock.json | 727 +++++++++++++++++++++++++- test/compat/package.json | 26 +- test/compat/prisma-setup.js | 29 + test/compat/prisma5/package-lock.json | 110 ++++ test/compat/prisma5/package.json | 9 + test/compat/prisma6/package-lock.json | 409 +++++++++++++++ test/compat/prisma6/package.json | 9 + test/compat/schema.prisma | 14 + 19 files changed, 1868 insertions(+), 23 deletions(-) create mode 100644 test/compat/.gitignore create mode 100644 test/compat/prisma-setup.js create mode 100644 test/compat/prisma5/package-lock.json create mode 100644 test/compat/prisma5/package.json create mode 100644 test/compat/prisma6/package-lock.json create mode 100644 test/compat/prisma6/package.json create mode 100644 test/compat/schema.prisma diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e64bd27..0c25dae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -126,7 +126,7 @@ jobs: driver-compat: needs: build runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 20 services: postgres: image: postgres:16 diff --git a/CHANGELOG.md b/CHANGELOG.md index c91cd66..fd52f6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- **node-redis:** `instrumentNodeRedis(tp, require('redis'))` for `redis` / `@redis/client` 4 and 5: `redis GET`, `redis HSET`; MULTI and pipeline commands are tracked one by one. Auto-instrumented as the `redis` target. + ### Fixed +- `sqlLabel`: `CREATE TABLE IF NOT EXISTS "Users"` (Sequelize's `sync()`) and `DROP TABLE IF EXISTS` were labelled `CREATE IF`; they are now `CREATE Users` / `DROP Users`. - **MongoDB driver 3.x / Mongoose 5:** `aggregate()` was labelled `mongo find $cmd`; it is now `mongo aggregate users`. ### Changed -- CI runs every driver integration against old and new majors on real servers (pg 8.0 and 8.x, mysql2 2 and 3, mysql 2, mongodb 3–7, Mongoose 5–8, ioredis 4 and 5, better-sqlite3 11 and 12): `test/compat`. The first run found the bug above. +- CI runs every driver integration against old and new majors on real servers (pg 8.0 and 8.x, mysql2 2 and 3, mysql 2, mongodb 3–7, Mongoose 5–8, ioredis 4 and 5, node-redis 4 and 5, Prisma 5 and 6 on SQLite, Sequelize 6 and Knex 2 and 3 on pg and mysql2, better-sqlite3 11 and 12): `test/compat`. The first run found the bug above. - Timing tests that assert on synchronous durations use a virtual clock, so a loaded CI machine can't fail them. ## [1.8.0] - 2026-11-06 diff --git a/README.md b/README.md index 7dd8d39..67e2708 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ - **Honest numbers.** You get total, self and synchronous time for each call. Detailed mode flags calls that blocked the event loop. - **Production-ready.** Stats are bounded per label (p50/p95/p99), sampling is built in, and `enabled: false` makes tracking a pass-through costing ~40 ns. - **Gets data out.** Chrome/Perfetto traces, OpenTelemetry spans, JSON lines, or your own `onComplete` hook. -- **Zero-effort coverage.** One line tracks every `fetch`, HTTP request or query, for pg, mysql2, mysql, MongoDB/Mongoose, ioredis, Prisma, better-sqlite3 (and Drizzle on any of these drivers) and the AWS SDK. +- **Zero-effort coverage.** One line tracks every `fetch`, HTTP request or query, for pg, mysql2, mysql, MongoDB/Mongoose, ioredis, node-redis, Prisma, better-sqlite3 (and Drizzle, Sequelize and Knex on any of these drivers) and the AWS SDK. - **Built for teams.** It feeds Prometheus, catches regressions in CI (`traceperf compare --fail`), shows up in Chrome DevTools, and produces HTML flame-chart reports. - **Fits your stack.** Express, Fastify, Hono, Koa, NestJS, Next.js route handlers, decorators, CommonJS, ESM, TypeScript, browsers, Node.js 14+, Bun, Deno, Cloudflare Workers and Vercel Edge. There are no runtime dependencies. @@ -136,7 +136,7 @@ The same naming works without `run`: `TRACEPERF_PROFILE_BLOCKING=1 node --requir ## No code changes: `traceperf/auto` -Start your app with traceperf preloaded. Every incoming HTTP request becomes a tree, with the outgoing `http`/`fetch` calls and the `pg`, `mysql2`, `mysql`, MongoDB/Mongoose, `ioredis`, Prisma and `better-sqlite3` queries it made nested under it (Drizzle runs on these drivers, so its queries show up too): +Start your app with traceperf preloaded. Every incoming HTTP request becomes a tree, with the outgoing `http`/`fetch` calls and the `pg`, `mysql2`, `mysql`, MongoDB/Mongoose, `ioredis`, `redis`, Prisma and `better-sqlite3` queries it made nested under it (Drizzle, Sequelize and Knex run on these drivers, so their queries show up too): ```bash @@ -162,7 +162,7 @@ Database clients are only instrumented when your `package.json` lists them. Conf | `TRACEPERF_THRESHOLD=50` | Only report calls taking at least 50 ms | | `TRACEPERF_SAMPLE_RATE=0.1` | Track 10% of requests | | `TRACEPERF_IGNORE=^/health` | Regular expression of request paths not to track | -| `TRACEPERF_DISABLE=http,fetch` | Skip targets: `server`, `http`, `fetch`, `pg`, `mysql2`, `mongodb`, `ioredis`, `prisma`, `better-sqlite3`, `mysql` | +| `TRACEPERF_DISABLE=http,fetch` | Skip targets: `server`, `http`, `fetch`, `pg`, `mysql2`, `mongodb`, `ioredis`, `prisma`, `better-sqlite3`, `mysql`, `redis` | | `TRACEPERF_SILENT=1` / `TRACEPERF_SUMMARY=0` / `TRACEPERF_QUIET=1` | No per-request trees / no exit summary / no startup line | | `TRACEPERF_ENABLED=0` | Load nothing | @@ -248,6 +248,7 @@ instrumentMysql(tp, require('mysql')); // the classic driver: conne instrumentMongo(tp, require('mongodb')); // "mongo find users", "mongo insertOne users" instrumentMongo(tp, require('mongoose').mongo); // Mongoose uses its own copy of the driver instrumentRedis(tp, require('ioredis')); // "redis GET" +instrumentNodeRedis(tp, require('redis')); // "redis GET" (node-redis 4/5, MULTI/pipeline commands one by one) instrumentPrisma(tp, require('@prisma/client')); // "prisma.User.findMany" (every client, $extends included) instrumentBetterSqlite3(tp, require('better-sqlite3')); // "sqlite SELECT users" (synchronous: slow ones show as blocking) instrumentAwsClient(tp, s3Client); // "aws S3.GetObject" diff --git a/package-lock.json b/package-lock.json index e2a6f63..d5d801c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,8 @@ "pg": "^8.23.1", "prettier": "^3.5.3", "publint": "^0.3.25", + "redis": "^5.12.1", + "redis4": "npm:redis@^4.7.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2", "ts-jest": "^29.2.6", @@ -2578,6 +2580,93 @@ "url": "https://bjornlu.com/sponsor" } }, + "node_modules/@redis/bloom": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.12.1.tgz", + "integrity": "sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/@redis/client": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.12.1.tgz", + "integrity": "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@node-rs/xxhash": "^1.1.0", + "@opentelemetry/api": ">=1 <2" + }, + "peerDependenciesMeta": { + "@node-rs/xxhash": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@redis/client/node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@redis/json": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.12.1.tgz", + "integrity": "sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/@redis/search": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.12.1.tgz", + "integrity": "sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/@redis/time-series": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.12.1.tgz", + "integrity": "sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, "node_modules/@shikijs/engine-oniguruma": { "version": "3.23.0", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", @@ -5312,6 +5401,16 @@ "is-property": "^1.0.2" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -8386,6 +8485,23 @@ "dev": true, "license": "MIT" }, + "node_modules/redis": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.12.1.tgz", + "integrity": "sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redis/bloom": "5.12.1", + "@redis/client": "5.12.1", + "@redis/json": "5.12.1", + "@redis/search": "5.12.1", + "@redis/time-series": "5.12.1" + }, + "engines": { + "node": ">= 18.19.0" + } + }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -8396,6 +8512,107 @@ "node": ">=4" } }, + "node_modules/redis4": { + "name": "redis", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", + "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.1", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, + "node_modules/redis4/node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/redis4/node_modules/@redis/client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", + "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/redis4/node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/redis4/node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/redis4/node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/redis4/node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/redis4/node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redis4/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", diff --git a/package.json b/package.json index ff472d7..6dae132 100644 --- a/package.json +++ b/package.json @@ -148,6 +148,8 @@ "pg": "^8.23.1", "prettier": "^3.5.3", "publint": "^0.3.25", + "redis": "^5.12.1", + "redis4": "npm:redis@^4.7.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2", "ts-jest": "^29.2.6", diff --git a/src/__tests__/drivers.test.ts b/src/__tests__/drivers.test.ts index 22b4af2..f297fd7 100644 --- a/src/__tests__/drivers.test.ts +++ b/src/__tests__/drivers.test.ts @@ -21,6 +21,7 @@ import { instrumentMysql, instrumentMysql2, instrumentPg, + instrumentNodeRedis, instrumentRedis, sqlLabel, } from '../index'; @@ -45,6 +46,12 @@ describe('sqlLabel', () => { [{ sql: 'select a from b join c' }, 'SELECT b'], ['SELECT "public"."Post"."id" FROM "public"."Post" WHERE 1', 'SELECT public.Post'], ['select * from `shop`.`orders`', 'SELECT shop.orders'], + ['CREATE TABLE IF NOT EXISTS "Users" ("id" SERIAL)', 'CREATE Users'], + ['DROP TABLE IF EXISTS `Users`', 'DROP Users'], + ['SELECT "id", "name" FROM "Users" AS "User" WHERE "User"."id" = 1', 'SELECT Users'], + ['INSERT INTO "Users" ("id","name") VALUES (DEFAULT,$1) RETURNING "id","name"', 'INSERT Users'], + ['select * from "users" where "name" = $1', 'SELECT users'], + ['create table `users` (`id` int unsigned not null auto_increment primary key)', 'CREATE users'], ['BEGIN', 'BEGIN'], [undefined, 'QUERY'], ])('%p -> %p', (sql, label) => { @@ -170,6 +177,95 @@ describe('instrumentRedis (real ioredis client, local RESP server)', () => { }); }); +/** A RESP2 server for SET/GET/INCR/MULTI/EXEC; other commands reply with an error */ +function fakeRedis(): Promise<{ server: net.Server; port: number }> { + return new Promise(resolve => { + const server = net.createServer(socket => { + const store = new Map(); + let queued: string[] | undefined; + let buffer = ''; + const bulk = (v: string | undefined) => (v === undefined ? '$-1\r\n' : `$${Buffer.byteLength(v)}\r\n${v}\r\n`); + const reply = (cmd: string, key: string, value: string): string => { + switch (cmd) { + case 'SET': store.set(key, value); return '+OK\r\n'; + case 'GET': return bulk(store.get(key)); + case 'INCR': { + const n = Number(store.get(key) || 0) + 1; + store.set(key, String(n)); + return `:${n}\r\n`; + } + default: return '-ERR unknown command\r\n'; + } + }; + socket.on('data', chunk => { + buffer += chunk.toString(); + for (;;) { + const m = /^\*(\d+)\r\n/.exec(buffer); + if (!m) return; + let pos = m[0].length; + const parts: string[] = []; + for (let i = 0; i < Number(m[1]); i++) { + const len = /^\$(\d+)\r\n/.exec(buffer.slice(pos)); + if (!len) return; + pos += len[0].length; + if (buffer.length < pos + Number(len[1]) + 2) return; + parts.push(buffer.slice(pos, pos + Number(len[1]))); + pos += Number(len[1]) + 2; + } + buffer = buffer.slice(pos); + const [name, key, value] = parts; + const cmd = name.toUpperCase(); + if (cmd === 'MULTI') { queued = []; socket.write('+OK\r\n'); continue; } + if (cmd === 'EXEC' && queued) { + socket.write(`*${queued.length}\r\n${queued.join('')}`); + queued = undefined; + continue; + } + const r = reply(cmd, key, value); + if (queued) { queued.push(r); socket.write('+QUEUED\r\n'); } else socket.write(r); + } + }); + }).listen(0, '127.0.0.1', () => resolve({ server, port: (server.address() as AddressInfo).port })); + }); +} + +describe.each([['redis@5', 'redis'], ['redis@4', 'redis4']])('instrumentNodeRedis (real %s client, local RESP server)', (_name, pkg) => { + let server: net.Server; + let port: number; + beforeAll(async () => { ({ server, port } = await fakeRedis()); }); + afterAll(done => { server.close(() => done()); }); + + test('commands, MULTI and pipeline commands are tracked individually, errors included', async () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const redis = require(pkg); + const { tp, roots } = setup(); + const uninstall = instrumentNodeRedis(tp, redis); + const client = redis.createClient({ url: `redis://127.0.0.1:${port}`, socket: { reconnectStrategy: false } }); + client.on('error', () => undefined); + try { + await client.connect(); + await tp.track(async () => { + await client.set('k', 'v'); + expect(await client.get('k')).toBe('v'); + expect(await client.multi().incr('n').incr('n').exec()).toEqual([1, 2]); + await client.multi().get('k').execAsPipeline(); + await expect(client.sendCommand(['NOPE'])).rejects.toThrow(); + }, { label: 'job' }); + } finally { + uninstall(); + await client.disconnect(); + } + const job = roots.find(r => r.label === 'job')!; + expect(labels(job)).toEqual(['redis SET', 'redis GET', 'redis INCR', 'redis INCR', 'redis GET', 'redis NOPE']); + expect(job.children[5].error).toBeDefined(); + }); + + test('a module without createClient is ignored', () => { + const { tp } = setup(); + expect(() => instrumentNodeRedis(tp, {})()).not.toThrow(); + }); +}); + describe('instrumentMysql2 (real mysql2 client)', () => { test('connection failures are tracked as failed queries (callback and promise APIs)', async () => { // eslint-disable-next-line @typescript-eslint/no-require-imports diff --git a/src/auto.ts b/src/auto.ts index 9b13f10..c642822 100644 --- a/src/auto.ts +++ b/src/auto.ts @@ -12,11 +12,11 @@ import type { OptimizedPerformanceMonitor } from './core/optimized-performance-monitor'; import { loadNodeBuiltin } from './core/env'; import { HttpServerInstrumentOptions, instrumentFetch, instrumentHttp, instrumentHttpServer, labelKoaRoutes, Uninstall, wrapFastifyFactory } from './instrument'; -import { instrumentBetterSqlite3, instrumentMongo, instrumentMysql, instrumentMysql2, instrumentPg, instrumentPrisma, instrumentRedis } from './drivers'; +import { instrumentBetterSqlite3, instrumentMongo, instrumentMysql, instrumentMysql2, instrumentPg, instrumentNodeRedis, instrumentPrisma, instrumentRedis } from './drivers'; -export type AutoTarget = 'server' | 'http' | 'fetch' | 'pg' | 'mysql2' | 'mongodb' | 'ioredis' | 'prisma' | 'better-sqlite3' | 'mysql'; +export type AutoTarget = 'server' | 'http' | 'fetch' | 'pg' | 'mysql2' | 'mongodb' | 'ioredis' | 'prisma' | 'better-sqlite3' | 'mysql' | 'redis'; -export const AUTO_TARGETS: AutoTarget[] = ['server', 'http', 'fetch', 'pg', 'mysql2', 'mongodb', 'ioredis', 'prisma', 'better-sqlite3', 'mysql']; +export const AUTO_TARGETS: AutoTarget[] = ['server', 'http', 'fetch', 'pg', 'mysql2', 'mongodb', 'ioredis', 'prisma', 'better-sqlite3', 'mysql', 'redis']; export interface AutoInstrumentOptions { /** Targets to skip, e.g. ['http'] */ @@ -133,6 +133,9 @@ export function autoInstrument(tp: OptimizedPerformanceMonitor, options: AutoIns return instrumentPrisma(tp, mod); }); if (deps.names.has('ioredis')) add('ioredis', () => instrumentRedis(tp, load('ioredis'))); + // node-redis: the `redis` package, or its client used directly + if (deps.names.has('redis')) add('redis', () => instrumentNodeRedis(tp, load('redis'))); + else if (deps.names.has('@redis/client')) add('redis', () => instrumentNodeRedis(tp, load('@redis/client'))); if (deps.names.has('mongodb')) add('mongodb', () => instrumentMongo(tp, load('mongodb'))); else if (deps.names.has('mongoose')) { // Mongoose's own copy of the driver, without loading Mongoose itself diff --git a/src/drivers.ts b/src/drivers.ts index 034ddf8..08c8e42 100644 --- a/src/drivers.ts +++ b/src/drivers.ts @@ -112,7 +112,7 @@ export function sqlLabel(sql: unknown): string { const cleaned = text.replace(/--[^\n]*|\/\*[\s\S]*?\*\//g, ' ').trim(); const verb = (cleaned.split(/\s+/)[0] || 'query').toUpperCase(); // users, public.accounts, "public"."Post", `shop`.`orders` - const table = /\b(?:from|into|update|join|table)\s+((?:"[^"]+"|`[^`]+`|[\w$]+)(?:\.(?:"[^"]+"|`[^`]+`|[\w$]+))*)/i.exec(cleaned); + const table = /\b(?:from|into|update|join|table(?:\s+if(?:\s+not)?\s+exists)?)\s+((?:"[^"]+"|`[^`]+`|[\w$]+)(?:\.(?:"[^"]+"|`[^`]+`|[\w$]+))*)/i.exec(cleaned); return table ? `${verb} ${table[1].replace(/["`]/g, '')}` : verb; } @@ -246,6 +246,95 @@ export function instrumentRedis(tp: OptimizedPerformanceMonitor, Redis: any, opt return () => restores.forEach(r => r()); } +const redisName = (args: unknown): string => { + const first = Array.isArray(args) ? args[0] : undefined; + return first !== undefined && first !== null && String(first) ? String(first).toUpperCase() : 'COMMAND'; +}; + +/** The prototype that defines a node-redis client's sendCommand (RedisClient) */ +function nodeRedisPrototype(redis: any): any { + if (!redis || typeof redis.createClient !== 'function') return undefined; + let client: any; + try { + client = redis.createClient(); // not connected: no socket is opened + } catch { + return undefined; + } + for (let p = Object.getPrototypeOf(client); p && p !== Object.prototype; p = Object.getPrototypeOf(p)) { + if (Object.prototype.hasOwnProperty.call(p, 'sendCommand')) return p; + } + return undefined; +} + +/** + * Track a batch of queued commands (MULTI or pipeline) as one span per + * command, all ending when the batch replies (they share one round trip). + */ +function trackBatch(tp: OptimizedPerformanceMonitor, options: DriverOptions, commands: unknown, run: () => unknown): unknown { + const list = Array.isArray(commands) ? commands : []; + const spans = list.map((c: any) => tp.start(`redis ${redisName(c && c.args)}`, options)); + const end = (error?: unknown) => spans.forEach(s => s.end(error)); + let result: unknown; + try { + result = run(); + } catch (error) { + end(error); + throw error; + } + if (isPromiseLike(result)) result.then(() => end(), error => end(error)); + else end(); + return result; +} + +/** + * node-redis (`redis` / `@redis/client`) 4 and 5: every command, "redis GET", + * "redis HSET"; MULTI and pipeline commands are tracked individually. + * (v4 legacyMode clients are not covered.) + * + * instrumentNodeRedis(tp, require('redis')); + */ +export function instrumentNodeRedis(tp: OptimizedPerformanceMonitor, redis: any, options: DriverOptions = {}): Uninstall { + const restores: Uninstall[] = []; + const proto = nodeRedisPrototype(redis); + if (!proto) return () => undefined; + // v5: every command (and v4: raw client.sendCommand) goes through sendCommand + patchMethod(proto, 'sendCommand', original => function (this: unknown, args: unknown, opts: any, ...rest: unknown[]) { + // v4 isolated commands are re-sent on an isolated client: tracked there + if (opts && opts.isolated) return original.call(this, args, opts, ...rest); + return trackCall(tp, `redis ${redisName(args)}`, options, this, original, [args, opts, ...rest], -1); + }, restores); + // v4: generated commands (client.get, client.GET, client.clientSetName ...) + // call a privately captured executor, so each command method is wrapped. + // Their names give the command: CLIENT_SETNAME -> "CLIENT SETNAME". + if (typeof proto.commandsExecutor === 'function') { + const names = Object.getOwnPropertyNames(proto); + const commands = new Map(); + for (const n of names) if (/^[A-Z][A-Z0-9_]*$/.test(n)) commands.set(n.replace(/_/g, '').toLowerCase(), n.replace(/_/g, ' ')); + for (const n of names) { + const command = commands.get(n.replace(/_/g, '').toLowerCase()); + if (!command || command === 'MULTI') continue; + const descriptor = Object.getOwnPropertyDescriptor(proto, n); + if (!descriptor || typeof descriptor.value !== 'function') continue; + patchMethod(proto, n, original => function (this: unknown, ...args: unknown[]) { + return trackCall(tp, `redis ${command}`, options, this, original, args, -1); + }, restores); + } + } + patchMethod(proto, 'executeScript', original => function (this: unknown, ...args: unknown[]) { + return trackCall(tp, 'redis EVALSHA', options, this, original, args, -1); + }, restores); + patchMethod(proto, 'executeFunction', original => function (this: unknown, ...args: unknown[]) { + return trackCall(tp, 'redis FCALL', options, this, original, args, -1); + }, restores); + // MULTI / pipeline: v4 multiExecutor, v5 _executeMulti and _executePipeline + for (const method of ['multiExecutor', '_executeMulti', '_executePipeline']) { + patchMethod(proto, method, original => function (this: unknown, commands: unknown, ...rest: unknown[]) { + return trackBatch(tp, options, commands, () => original.call(this, commands, ...rest)); + }, restores); + } + return () => restores.forEach(r => r()); +} + /** * AWS SDK v3 clients: "aws S3.GetObject", "aws DynamoDB.Query". * Adds a middleware to the given client instance. diff --git a/src/register.ts b/src/register.ts index fded59f..f55a7bf 100644 --- a/src/register.ts +++ b/src/register.ts @@ -13,7 +13,7 @@ * TRACEPERF_SILENT=1 no per-call console trees * TRACEPERF_SUMMARY=0 no stats + explanations on exit (default on) * TRACEPERF_DISABLE=http,fetch skip targets (server, http, fetch, pg, mysql2, mongodb, ioredis, - * prisma, better-sqlite3, mysql) + * prisma, better-sqlite3, mysql, redis) * TRACEPERF_IGNORE=^/health regular expression of request paths not to track * TRACEPERF_QUIET=1 no "traceperf: tracking ..." line at startup * TRACEPERF_PROFILE_BLOCKING=1 sample the CPU (V8 profiler) to name the diff --git a/test/compat/.gitignore b/test/compat/.gitignore new file mode 100644 index 0000000..5f68133 --- /dev/null +++ b/test/compat/.gitignore @@ -0,0 +1,2 @@ +prisma*/schema.prisma +prisma*/compat.db* diff --git a/test/compat/compat.js b/test/compat/compat.js index 94b3512..3e1037e 100644 --- a/test/compat/compat.js +++ b/test/compat/compat.js @@ -10,8 +10,10 @@ // TRACEPERF_COMPAT_STRICT=1 a skip is a failure). const assert = require('assert'); const { - createTracePerf, instrumentPg, instrumentMysql2, instrumentMysql, instrumentMongo, instrumentRedis, instrumentBetterSqlite3, + createTracePerf, instrumentPg, instrumentMysql2, instrumentMysql, instrumentMongo, instrumentRedis, instrumentNodeRedis, + instrumentBetterSqlite3, instrumentPrisma, } = require('../../dist'); +const path = require('path'); const env = { pg: process.env.TRACEPERF_PG_URL, @@ -178,6 +180,110 @@ const redisCheck = pkg => async () => { } }; +const nodeRedisCheck = pkg => async () => { + const redis = require(pkg); + const client = redis.createClient({ url: env.redis, socket: { reconnectStrategy: false } }); + client.on('error', () => undefined); + await client.connect(); + const key = `traceperf:compat:${process.pid}`; + try { + const { labels, job } = await trace(tp => instrumentNodeRedis(tp, redis), async () => { + await client.set(key, 'v'); + assert.strictEqual(await client.get(key), 'v'); + assert.deepStrictEqual(await client.multi().incr(`${key}:n`).incr(`${key}:n`).exec(), [1, 2]); + await client.multi().get(key).execAsPipeline(); + await client.sendCommand(['NOPE']).catch(() => undefined); + await client.del([key, `${key}:n`]); + }); + // MULTI and pipeline commands are tracked one by one + assert.deepStrictEqual(labels, ['redis SET', 'redis GET', 'redis INCR', 'redis INCR', 'redis GET', 'redis NOPE', 'redis DEL']); + assert.ok(job.children[5].error, 'failed command is marked as an error'); + } finally { + await client.disconnect(); + } +}; + +// Each Prisma major lives in its own prisma/ directory (see prisma-setup.js) +const prismaCheck = dir => async () => { + const mod = require(path.join(__dirname, dir, 'node_modules', '@prisma/client')); + const prisma = new mod.PrismaClient({ datasources: { db: { url: `file:${path.join(__dirname, dir, 'compat.db')}` } } }); + await prisma.$connect(); + try { + await prisma.user.deleteMany(); + const { labels, job } = await trace(tp => instrumentPrisma(tp, mod), async () => { + await prisma.user.create({ data: { name: 'ada' } }); + assert.strictEqual((await prisma.user.findMany()).length, 1); + await prisma.user.findFirst({ where: { name: 'ada' } }); + await prisma.user.update({ where: { id: -1 }, data: { name: 'x' } }).catch(() => undefined); + await prisma.$queryRaw`SELECT 1`; + await prisma.user.deleteMany(); + }); + assert.deepStrictEqual(labels, [ + 'prisma.User.create', 'prisma.User.findMany', 'prisma.User.findFirst', 'prisma.User.update', + 'prisma.raw.queryRaw', 'prisma.User.deleteMany', + ]); + assert.ok(job.children[3].error, 'failed call is marked as an error'); + } finally { + await prisma.$disconnect(); + } +}; + +// Sequelize and Knex run on the instrumented drivers: their queries carry the driver's labels +const SQL_SERVERS = { + pg: { url: () => env.pg, driver: 'pg8', instrument: instrumentPg, prefix: 'pg', knexDialect: 'postgres' }, + mysql: { url: () => env.mysql, driver: 'mysql2-3', instrument: instrumentMysql2, prefix: 'mysql', knexDialect: 'mysql2' }, +}; + +const sequelizeCheck = (pkg, server) => async () => { + const { Sequelize, DataTypes } = require(pkg); + const s = SQL_SERVERS[server]; + const driver = require(s.driver); + const sequelize = new Sequelize(s.url(), { dialectModule: driver, logging: false, pool: { max: 1 } }); + const table = `Users${process.pid}`; + const User = sequelize.define('User', { name: DataTypes.STRING }, { tableName: table }); + try { + const { labels } = await trace(tp => s.instrument(tp, driver), async () => { + await User.sync({ force: true }); + }); + assert.ok(labels.includes(`${s.prefix} CREATE ${table}`), `CREATE TABLE IF NOT EXISTS is labelled by table: ${labels}`); + const { labels: crud } = await trace(tp => s.instrument(tp, driver), async () => { + const ada = await User.create({ name: 'ada' }); + assert.strictEqual((await User.findAll({ where: { name: 'ada' } })).length, 1); + await User.update({ name: 'grace' }, { where: { id: ada.id } }); + await User.destroy({ where: { id: ada.id } }); + }); + assert.deepStrictEqual(crud, ['INSERT', 'SELECT', 'UPDATE', 'DELETE'].map(v => `${s.prefix} ${v} ${table}`)); + } finally { + await User.drop().catch(() => undefined); + await sequelize.close(); + } +}; + +const knexCheck = (pkg, server) => async () => { + const s = SQL_SERVERS[server]; + const driver = require(s.driver); + // Knex requires 'pg' / 'mysql2' by name; hand it the aliased driver instead + const Dialect = require(`${pkg}/lib/dialects/${s.knexDialect}`); + class Client extends Dialect { _driver() { return driver; } } + const knex = require(pkg)({ client: Client, connection: s.url(), pool: { min: 0, max: 1 } }); + const table = `users${process.pid}`; + try { + await knex.raw('SELECT 1'); // connect first: pg runs "select version()" on a new connection + const { labels } = await trace(tp => s.instrument(tp, driver), async () => { + await knex.schema.createTable(table, t => { t.increments('id'); t.string('name'); }); + await knex(table).insert({ name: 'ada' }); + assert.strictEqual((await knex(table).where({ name: 'ada' })).length, 1); + await knex(table).where({ name: 'ada' }).update({ name: 'grace' }); + await knex(table).del(); + await knex.schema.dropTable(table); + }); + assert.deepStrictEqual(labels, ['CREATE', 'INSERT', 'SELECT', 'UPDATE', 'DELETE', 'DROP'].map(v => `${s.prefix} ${v} ${table}`)); + } finally { + await knex.schema.dropTableIfExists(table).catch(() => undefined); + await knex.destroy(); + } +}; + const sqliteCheck = pkg => async () => { const Database = require(pkg); const sqlite = new Database(':memory:'); @@ -212,6 +318,17 @@ const checks = [ ['mongoose@8', 'mongo', mongooseCheck('mongoose8')], ['ioredis@4', 'redis', redisCheck('ioredis4')], ['ioredis@5', 'redis', redisCheck('ioredis5')], + ['redis@4', 'redis', nodeRedisCheck('redis4')], + ['redis@5', 'redis', nodeRedisCheck('redis5')], + ['prisma@5', null, prismaCheck('prisma5')], + ['prisma@6', null, prismaCheck('prisma6')], + // prisma@7: needs a driver adapter and the TypeScript-only generator for SQLite, see prisma-setup.js + ['sequelize@6 pg', 'pg', sequelizeCheck('sequelize6', 'pg')], + ['sequelize@6 mysql2', 'mysql', sequelizeCheck('sequelize6', 'mysql')], + ['knex@2 pg', 'pg', knexCheck('knex2', 'pg')], + ['knex@2 mysql2', 'mysql', knexCheck('knex2', 'mysql')], + ['knex@3 pg', 'pg', knexCheck('knex3', 'pg')], + ['knex@3 mysql2', 'mysql', knexCheck('knex3', 'mysql')], ['better-sqlite3@11', null, sqliteCheck('better-sqlite3-11')], ['better-sqlite3@12', null, sqliteCheck('better-sqlite3-12')], ]; @@ -246,7 +363,8 @@ process.on('exit', code => { if (!finished && code === 0) { console.log('FAIL a })(); function aliasOf(name) { - const [pkg, major] = name.split('@'); + const [pkg, major] = name.split(' ')[0].split('@'); if (name === 'pg@8.0') return 'pg8-0'; - return ['pg', 'mongodb', 'mongoose', 'ioredis'].includes(pkg) ? `${pkg}${major}` : `${pkg}-${major}`; + if (pkg === 'prisma') return path.join(`..`, `prisma${major}`, 'node_modules', '@prisma', 'client'); + return ['pg', 'mongodb', 'mongoose', 'ioredis', 'redis', 'sequelize', 'knex'].includes(pkg) ? `${pkg}${major}` : `${pkg}-${major}`; } diff --git a/test/compat/package-lock.json b/test/compat/package-lock.json index 6f3e439..f9806f3 100644 --- a/test/compat/package-lock.json +++ b/test/compat/package-lock.json @@ -10,6 +10,8 @@ "better-sqlite3-12": "npm:better-sqlite3@12", "ioredis4": "npm:ioredis@4", "ioredis5": "npm:ioredis@5", + "knex2": "npm:knex@^2.5.1", + "knex3": "npm:knex@^3.3.0", "mongodb3": "npm:mongodb@3", "mongodb4": "npm:mongodb@4", "mongodb5": "npm:mongodb@5", @@ -23,7 +25,10 @@ "mysql2-2": "npm:mysql2@2", "mysql2-3": "npm:mysql2@3", "pg8": "npm:pg@8", - "pg8-0": "npm:pg@8.0.3" + "pg8-0": "npm:pg@8.0.3", + "redis4": "npm:redis@^4.7.1", + "redis5": "npm:redis@^5.12.1", + "sequelize6": "npm:sequelize@^6.37.8" } }, "node_modules/@aws-sdk/core": { @@ -354,6 +359,65 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", + "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@smithy/core": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.39.0.tgz", @@ -457,6 +521,15 @@ "@types/node": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/mongodb": { "version": "3.6.20", "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", @@ -467,6 +540,12 @@ "@types/node": "*" } }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "26.6.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.6.tgz", @@ -476,6 +555,12 @@ "undici-types": "~8.9.0" } }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -646,6 +731,21 @@ "node": ">=0.10.0" } }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -711,6 +811,13 @@ "node": ">=8" } }, + "node_modules/dottie": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.7.tgz", + "integrity": "sha512-7lAK2A0b3zZr3UC5aE69CPdCFR4RHW1o2Dr74TqFykxkUCBXSRJum/yPc7g8zRHJqWKomPLHwFLLoUnn8PXXRg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -720,6 +827,33 @@ "once": "^1.4.0" } }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -741,6 +875,15 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -750,12 +893,48 @@ "is-property": "^1.0.2" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==", + "license": "MIT" + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", "license": "MIT" }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -788,6 +967,15 @@ ], "license": "BSD-3-Clause" }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ], + "license": "MIT" + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -800,6 +988,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/ioredis4": { "name": "ioredis", "version": "4.31.0", @@ -883,6 +1080,21 @@ "node": ">= 12" } }, + "node_modules/is-core-module": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.18.0.tgz", + "integrity": "sha512-qyax9fgaKOEC4qK8WZn7Q4IcoZC9RC1O1fFxRcRTq/oMMkWT/uXpqpXa12kzWR409zAt1drYN9N9BO26UmBCmw==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -901,6 +1113,183 @@ "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==", "license": "Apache-2.0" }, + "node_modules/knex2": { + "name": "knex", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz", + "integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==", + "license": "MIT", + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.1", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=12" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/knex2/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/knex2/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/knex2/node_modules/pg-connection-string": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", + "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==", + "license": "MIT" + }, + "node_modules/knex3": { + "name": "knex", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.3.0.tgz", + "integrity": "sha512-LgWl031hNuLv9Lhxdd9093zULa2aNcoP04Bk29e5NidgRcEr/T0rAr2WYi4BhjTiCDoQiRU56meUE5rppLKZzQ==", + "license": "MIT", + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.18.1", + "pg-connection-string": "2.6.2", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.1.0", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "pg-query-stream": "^4.14.0" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/knex3/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/knex3/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/knex3/node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -985,6 +1374,27 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "license": "MIT" }, + "node_modules/moment": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.31.0.tgz", + "integrity": "sha512-0acOTfMiWOheYS4eoWb80yYMb/JLvVv9SHbs2PehaDzfUG0Bw855SKyk0IKTnPGa5+U2bmi3W68l1+sGLX/pvw==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/mongodb": { "version": "3.7.4", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.4.tgz", @@ -2186,6 +2596,12 @@ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", "license": "MIT" }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, "node_modules/pg": { "version": "8.23.1", "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.1.tgz", @@ -2499,6 +2915,18 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -2520,6 +2948,113 @@ "node": ">=4" } }, + "node_modules/redis4": { + "name": "redis", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", + "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.1", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, + "node_modules/redis5": { + "name": "redis", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.12.1.tgz", + "integrity": "sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g==", + "license": "MIT", + "dependencies": { + "@redis/bloom": "5.12.1", + "@redis/client": "5.12.1", + "@redis/json": "5.12.1", + "@redis/search": "5.12.1", + "@redis/time-series": "5.12.1" + }, + "engines": { + "node": ">= 18.19.0" + } + }, + "node_modules/redis5/node_modules/@redis/bloom": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.12.1.tgz", + "integrity": "sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA==", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/redis5/node_modules/@redis/client": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.12.1.tgz", + "integrity": "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@node-rs/xxhash": "^1.1.0", + "@opentelemetry/api": ">=1 <2" + }, + "peerDependenciesMeta": { + "@node-rs/xxhash": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/redis5/node_modules/@redis/json": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.12.1.tgz", + "integrity": "sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg==", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/redis5/node_modules/@redis/search": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.12.1.tgz", + "integrity": "sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w==", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/redis5/node_modules/@redis/time-series": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.12.1.tgz", + "integrity": "sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ==", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, "node_modules/regexp-clone": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", @@ -2535,6 +3070,42 @@ "node": ">=4" } }, + "node_modules/resolve": { + "version": "1.22.14", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.14.tgz", + "integrity": "sha512-1ZjYvR9sbkiwC5+9QHetqW5HmA2pHRMaoupD01t2HlYnV/vydC2zPwLixhsMieSWVZt0JJRg5LVIk0Ow1D/TrA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.18.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2588,6 +3159,96 @@ "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize6": { + "name": "sequelize", + "version": "6.37.8", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.8.tgz", + "integrity": "sha512-HJ0IQFqcTsTiqbEgiuioYFMSD00TP6Cz7zoTti+zVVBwVe9fEhev9cH6WnM3XU31+ABS356durAb99ZuOthnKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize6/node_modules/pg-connection-string": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.1.tgz", + "integrity": "sha512-qR3kGNPBLpCNtz0evbKA0Y/MRFXwSSdT+pTJvYp/bXTcReZbvX1kzF0IyTc1QnxqF7AZbOeBhNL8R5mYQZV/MA==", + "license": "MIT" + }, + "node_modules/sequelize6/node_modules/semver": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.6.tgz", + "integrity": "sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/sift": { "version": "13.5.2", "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", @@ -2742,6 +3403,18 @@ "node": ">=0.10.0" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tar-fs": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", @@ -2795,6 +3468,30 @@ "node": ">= 6" } }, + "node_modules/tarn": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.1.2.tgz", + "integrity": "sha512-3RTvqKZcK/17jnJ8rMKFXbyNogywTs1z0gVPPwFsJGX46rkmUHOdIaSQ/aVO1rS7nH+soiXiWk7rvUXxndm8Dg==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" + }, "node_modules/tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", @@ -2838,6 +3535,25 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -2860,6 +3576,15 @@ "node": ">=12" } }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/test/compat/package.json b/test/compat/package.json index cc45373..515e088 100644 --- a/test/compat/package.json +++ b/test/compat/package.json @@ -2,12 +2,16 @@ "name": "traceperf-driver-compat", "private": true, "description": "Old and new majors of every instrumented driver, installed side by side under aliases. Run by compat.js against real servers.", + "scripts": { + "postinstall": "node prisma-setup.js" + }, "dependencies": { - "pg8-0": "npm:pg@8.0.3", - "pg8": "npm:pg@8", - "mysql2-2": "npm:mysql2@2", - "mysql2-3": "npm:mysql2@3", - "mysql-2": "npm:mysql@2", + "better-sqlite3-11": "npm:better-sqlite3@11", + "better-sqlite3-12": "npm:better-sqlite3@12", + "ioredis4": "npm:ioredis@4", + "ioredis5": "npm:ioredis@5", + "knex2": "npm:knex@2", + "knex3": "npm:knex@3", "mongodb3": "npm:mongodb@3", "mongodb4": "npm:mongodb@4", "mongodb5": "npm:mongodb@5", @@ -17,9 +21,13 @@ "mongoose6": "npm:mongoose@6", "mongoose7": "npm:mongoose@7", "mongoose8": "npm:mongoose@8", - "ioredis4": "npm:ioredis@4", - "ioredis5": "npm:ioredis@5", - "better-sqlite3-11": "npm:better-sqlite3@11", - "better-sqlite3-12": "npm:better-sqlite3@12" + "mysql-2": "npm:mysql@2", + "mysql2-2": "npm:mysql2@2", + "mysql2-3": "npm:mysql2@3", + "pg8": "npm:pg@8", + "pg8-0": "npm:pg@8.0.3", + "redis4": "npm:redis@4", + "redis5": "npm:redis@5", + "sequelize6": "npm:sequelize@6" } } diff --git a/test/compat/prisma-setup.js b/test/compat/prisma-setup.js new file mode 100644 index 0000000..4bcd2a5 --- /dev/null +++ b/test/compat/prisma-setup.js @@ -0,0 +1,29 @@ +// Each Prisma major needs its own @prisma/client next to the client it +// generates, so they cannot share ./node_modules under aliases: every +// prisma/ directory is installed on its own, its client generated and +// its SQLite database created from ./schema.prisma. Run by `npm install` +// (postinstall) here; set TRACEPERF_SKIP_PRISMA=1 to skip. +// +// Prisma 7 is not included: it no longer ships a query engine, so SQLite +// needs a driver adapter (@prisma/adapter-better-sqlite3), a prisma.config.ts +// and the new TypeScript-only "prisma-client" generator. instrumentPrisma's +// Prisma 7 path (the tracing hook) is covered by the unit tests. +const { execFileSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +if (process.env.TRACEPERF_SKIP_PRISMA === '1') process.exit(0); + +const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; +const schema = fs.readFileSync(path.join(__dirname, 'schema.prisma'), 'utf8'); + +for (const dir of ['prisma5', 'prisma6']) { + const cwd = path.join(__dirname, dir); + const run = (cmd, args) => execFileSync(cmd, args, { cwd, stdio: 'inherit', env: { ...process.env, DATABASE_URL: 'file:./compat.db' } }); + fs.writeFileSync(path.join(cwd, 'schema.prisma'), schema); + run(npm, [fs.existsSync(path.join(cwd, 'package-lock.json')) ? 'ci' : 'install', '--no-audit', '--no-fund']); + const prisma = path.join(cwd, 'node_modules', 'prisma', 'build', 'index.js'); + run(process.execPath, [prisma, 'generate', '--schema', 'schema.prisma']); + fs.rmSync(path.join(cwd, 'compat.db'), { force: true }); + run(process.execPath, [prisma, 'db', 'push', '--skip-generate', '--schema', 'schema.prisma']); +} diff --git a/test/compat/prisma5/package-lock.json b/test/compat/prisma5/package-lock.json new file mode 100644 index 0000000..7a0e830 --- /dev/null +++ b/test/compat/prisma5/package-lock.json @@ -0,0 +1,110 @@ +{ + "name": "traceperf-compat-prisma5", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "traceperf-compat-prisma5", + "dependencies": { + "@prisma/client": "5", + "prisma": "5" + } + }, + "node_modules/@prisma/client": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/fetch-engine": "5.22.0", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/prisma": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines": "5.22.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + } + } + } +} diff --git a/test/compat/prisma5/package.json b/test/compat/prisma5/package.json new file mode 100644 index 0000000..2016d4d --- /dev/null +++ b/test/compat/prisma5/package.json @@ -0,0 +1,9 @@ +{ + "name": "traceperf-compat-prisma5", + "private": true, + "description": "Prisma 5 with a generated client and a SQLite database (installed by ../prisma-setup.js).", + "dependencies": { + "@prisma/client": "5", + "prisma": "5" + } +} diff --git a/test/compat/prisma6/package-lock.json b/test/compat/prisma6/package-lock.json new file mode 100644 index 0000000..7e871cc --- /dev/null +++ b/test/compat/prisma6/package-lock.json @@ -0,0 +1,409 @@ +{ + "name": "traceperf-compat-prisma6", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "traceperf-compat-prisma6", + "dependencies": { + "@prisma/client": "6", + "prisma": "6" + } + }, + "node_modules/@prisma/client": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz", + "integrity": "sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.3.tgz", + "integrity": "sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.21.0", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.3.tgz", + "integrity": "sha512-ljkJ+SgpXNktLG0Q/n4JGYCkKf0f8oYLyjImS2I8e2q2WCfdRRtWER062ZV/ixaNP2M2VKlWXVJiGzZaUgbKZw==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.3.tgz", + "integrity": "sha512-RSYxtlYFl5pJ8ZePgMv0lZ9IzVCOdTPOegrs2qcbAEFrBI1G33h6wyC9kjQvo0DnYEhEVY0X4LsuFHXLKQk88g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.3", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.3", + "@prisma/get-platform": "6.19.3" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.3.tgz", + "integrity": "sha512-tKtl/qco9Nt7LU5iKhpultD8O4vMCZcU2CHjNTnRrL1QvSUr5W/GcyFPjNL87GtRrwBc7ubXXD9xy4EvLvt8JA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.3", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.3" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.3.tgz", + "integrity": "sha512-xFj1VcJ1N3MKooOQAGO0W5tsd0W2QzIvW7DD7c/8H14Zmp4jseeWAITm+w2LLoLrlhoHdPPh0NMZ8mfL6puoHA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.3" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/effect": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.21.0.tgz", + "integrity": "sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/exsolve": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.2.0.tgz", + "integrity": "sha512-h1pQPR9XoeSMdntRNCjyTznaume4UpfXrULBnlkZ2CUM/G5eSa+pBByGldO5uR8dxbPC+OuJXsPPNf+JZ81ADg==", + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/nypm": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.12.tgz", + "integrity": "sha512-4AhWntz+ds/P5kz1rN25yGegTPYsO5ffOy+8S0TeuXKZrgjlmgcEBLA1xubzcP/dWX4zYfNybv4TQERlL9Z0LQ==", + "license": "MIT", + "dependencies": { + "citty": "^0.2.2", + "pathe": "^2.0.3", + "tinyexec": "^1.3.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", + "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", + "license": "MIT" + }, + "node_modules/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/pkg-types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.4.0.tgz", + "integrity": "sha512-07Fys7iVx0X6TcviMOgogM1Lk33ZyqOezMnU79S+kffWqwolG/5W9V8EgQSeYCIfFTYVgssRbnH630Oa2+soBA==", + "license": "MIT", + "dependencies": { + "confbox": "^0.3.1", + "exsolve": "^1.1.3", + "pathe": "^2.0.3" + } + }, + "node_modules/pkg-types/node_modules/confbox": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.2.tgz", + "integrity": "sha512-ZUAKhKlkCOrIGKc8cj7zek9s9z1mveHif/25nUshI2BzCRzF7KH7KHa+Lnh2ubq+e1tIlaFuP763mzW4qVoehg==", + "license": "MIT" + }, + "node_modules/prisma": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.3.tgz", + "integrity": "sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.19.3", + "@prisma/engines": "6.19.3" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/test/compat/prisma6/package.json b/test/compat/prisma6/package.json new file mode 100644 index 0000000..704a8dc --- /dev/null +++ b/test/compat/prisma6/package.json @@ -0,0 +1,9 @@ +{ + "name": "traceperf-compat-prisma6", + "private": true, + "description": "Prisma 6 with a generated client and a SQLite database (installed by ../prisma-setup.js).", + "dependencies": { + "@prisma/client": "6", + "prisma": "6" + } +} diff --git a/test/compat/schema.prisma b/test/compat/schema.prisma new file mode 100644 index 0000000..0fee0d7 --- /dev/null +++ b/test/compat/schema.prisma @@ -0,0 +1,14 @@ +// Copied into each prisma/ directory by prisma-setup.js +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +model User { + id Int @id @default(autoincrement()) + name String +}