From f824ec5fab410e3d2b9425c6f0b8658e3a22ea89 Mon Sep 17 00:00:00 2001 From: Alexis Rico Date: Fri, 1 Sep 2023 09:40:50 +0200 Subject: [PATCH 1/4] Add integration tests Signed-off-by: Alexis Rico --- packages/plugin-client-drizzle/package.json | 13 +- packages/plugin-client-drizzle/src/session.ts | 16 +- .../test/integration.ava.ts | 2187 +++++++++++++++++ packages/plugin-client-drizzle/test/utils.ts | 5 + .../test/xata.codegen.ts | 149 ++ pnpm-lock.yaml | 661 ++++- 6 files changed, 3000 insertions(+), 31 deletions(-) create mode 100644 packages/plugin-client-drizzle/test/integration.ava.ts create mode 100644 packages/plugin-client-drizzle/test/utils.ts create mode 100644 packages/plugin-client-drizzle/test/xata.codegen.ts diff --git a/packages/plugin-client-drizzle/package.json b/packages/plugin-client-drizzle/package.json index 562d38237..f57a7e06a 100644 --- a/packages/plugin-client-drizzle/package.json +++ b/packages/plugin-client-drizzle/package.json @@ -14,7 +14,8 @@ }, "scripts": { "build": "rimraf dist && rollup -c", - "tsc": "tsc --noEmit" + "tsc": "tsc --noEmit", + "test": "NODE_OPTIONS='--loader=tsx --no-warnings' ava --serial test/*.ava.ts" }, "author": "", "license": "Apache-2.0", @@ -25,9 +26,17 @@ "@xata.io/client": "workspace:*" }, "devDependencies": { - "drizzle-orm": "^0.28.5" + "ava": "^5.3.1", + "drizzle-orm": "^0.28.5", + "tsx": "^3.12.7", + "uuid": "^9.0.0" }, "peerDependencies": { "drizzle-orm": "^0.28.5" + }, + "ava": { + "extensions": { + "ts": "module" + } } } diff --git a/packages/plugin-client-drizzle/src/session.ts b/packages/plugin-client-drizzle/src/session.ts index 529ad19dd..5ace436eb 100644 --- a/packages/plugin-client-drizzle/src/session.ts +++ b/packages/plugin-client-drizzle/src/session.ts @@ -21,7 +21,7 @@ import { XataClient } from './driver'; import { mapResultRow } from './utils'; type QueryResult> = { - records: T[]; + rows: T[]; warning?: string; }; @@ -146,11 +146,21 @@ export class XataSession< async query(query: string, params: unknown[]): Promise { this.logger.logQuery(query, params); - return await this.client.sql({ statement: query, params }); + const result = await this.client.sql>({ statement: query, params }); + + return { + rows: result.records, + warning: result.warning + }; } async queryObjects>(query: string, params: unknown[]): Promise> { - return this.client.sql({ statement: query, params }); + const result = await this.client.sql({ statement: query, params }); + + return { + rows: result.records, + warning: result.warning + }; } override async transaction( diff --git a/packages/plugin-client-drizzle/test/integration.ava.ts b/packages/plugin-client-drizzle/test/integration.ava.ts new file mode 100644 index 000000000..52d6d3cd2 --- /dev/null +++ b/packages/plugin-client-drizzle/test/integration.ava.ts @@ -0,0 +1,2187 @@ +import { getHostUrl, parseProviderString, XataApiClient } from '@xata.io/client'; +import type { TestFn } from 'ava'; +import anyTest from 'ava'; +import dotenv from 'dotenv'; +import { + and, + asc, + eq, + gt, + gte, + inArray, + lt, + name, + placeholder, + sql, + TransactionRollbackError, + type SQL, + type SQLWrapper +} from 'drizzle-orm'; +import { + alias, + boolean, + char, + cidr, + getMaterializedViewConfig, + getViewConfig, + inet, + integer, + jsonb, + macaddr, + macaddr8, + pgEnum, + pgMaterializedView, + pgTable, + pgTableCreator, + uuid as pgUuid, + pgView, + serial, + text, + timestamp, + varchar, + type PgColumn +} from 'drizzle-orm/pg-core'; +import { migrate } from 'drizzle-orm/vercel-postgres/migrator'; +import fetch from 'node-fetch'; +import { join } from 'path'; +import { v4 as uuid } from 'uuid'; +import { drizzle, type XataDatabase } from '../src'; +import { Expect, type Equal } from './utils'; +import { tables, XataClient } from './xata.codegen'; + +const ENABLE_LOGGING = false; + +// Get environment variables before reading them +dotenv.config({ path: join(process.cwd(), '.env') }); + +const apiKey = process.env.XATA_API_KEY ?? ''; +if (apiKey === '') throw new Error('XATA_API_KEY environment variable is not set'); + +const workspace = process.env.XATA_WORKSPACE ?? ''; +if (workspace === '') throw new Error('XATA_WORKSPACE environment variable is not set'); + +const region = process.env.XATA_REGION || 'eu-west-1'; + +const host = parseProviderString(process.env.XATA_API_PROVIDER); +if (host === null) { + throw new Error( + `Invalid XATA_API_PROVIDER environment variable, expected either "production", "staging" or "apiUrl,workspacesUrl"` + ); +} + +const usersTable = pgTable('users', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + verified: boolean('verified').notNull().default(false), + jsonb: jsonb('jsonb').$type(), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow() +}); + +const citiesTable = pgTable('cities', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + state: char('state', { length: 2 }) +}); + +const users2Table = pgTable('users2', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + cityId: integer('city_id').references(() => citiesTable.id) +}); + +const coursesTable = pgTable('courses', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + categoryId: integer('category_id').references(() => courseCategoriesTable.id) +}); + +const courseCategoriesTable = pgTable('course_categories', { + id: serial('id').primaryKey(), + name: text('name').notNull() +}); + +const orders = pgTable('orders', { + id: serial('id').primaryKey(), + region: text('region').notNull(), + product: text('product').notNull(), + amount: integer('amount').notNull(), + quantity: integer('quantity').notNull() +}); + +const network = pgTable('network_table', { + inet: inet('inet').notNull(), + cidr: cidr('cidr').notNull(), + macaddr: macaddr('macaddr').notNull(), + macaddr8: macaddr8('macaddr8').notNull() +}); + +const salEmp = pgTable('sal_emp', { + name: text('name'), + payByQuarter: integer('pay_by_quarter').array(), + schedule: text('schedule').array().array() +}); + +const _tictactoe = pgTable('tictactoe', { + squares: integer('squares').array(3).array(3) +}); + +const usersMigratorTable = pgTable('users12', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + email: text('email').notNull() +}); + +interface Context { + id: string; + db: XataDatabase; + client: XataClient; + api: XataApiClient; +} + +const test = anyTest as TestFn; + +test.before(async (t) => { + const ctx = t.context; + ctx.api = new XataApiClient({ apiKey, fetch, host, clientName: 'sdk-tests' }); +}); + +test.beforeEach(async (t) => { + const ctx = t.context; + ctx.id = Date.now().toString(36); + + const { databaseName: database } = await ctx.api.database.createDatabase({ + workspace, + database: `sdk-integration-test-drizzle-${ctx.id}`, + data: { region }, + headers: { 'X-Xata-Files': 'true' } + }); + + const { edits } = await ctx.api.migrations.compareBranchWithUserSchema({ + workspace, + region, + database, + branch: 'main', + schema: { tables: tables as any } + }); + + await ctx.api.migrations.applyBranchSchemaEdit({ workspace, region, database, branch: 'main', edits }); + + const workspaceUrl = getHostUrl(host, 'workspaces').replace('{workspaceId}', workspace).replace('{region}', region); + + ctx.client = new XataClient({ + databaseURL: `${workspaceUrl}/db/${database}`, + branch: 'main', + apiKey, + fetch, + clientName: 'sdk-tests' + }); + + ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); +}); + +test.afterEach.always(async (t) => { + const ctx = t.context; + + await ctx.api.database.deleteDatabase({ workspace, database: `sdk-integration-test-drizzle-${ctx.id}` }); +}); + +test.skip('select all fields', async (t) => { + const { db } = t.context; + + const now = Date.now(); + + await db.insert(usersTable).values({ name: 'John' }); + const result = await db.select().from(usersTable); + + t.assert(result[0]!.createdAt instanceof Date); + t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100); + t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); +}); + +test.skip('select sql', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const users = await db + .select({ + name: sql`upper(${usersTable.name})` + }) + .from(usersTable); + + t.deepEqual(users, [{ name: 'JOHN' }]); +}); + +test.skip('select typed sql', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + + const users = await db + .select({ + name: sql`upper(${usersTable.name})` + }) + .from(usersTable); + + t.deepEqual(users, [{ name: 'JOHN' }]); +}); + +test.skip('select distinct', async (t) => { + const { db } = t.context; + + const usersDistinctTable = pgTable('users_distinct', { + id: integer('id').notNull(), + name: text('name').notNull() + }); + + await db.execute(sql`drop table if exists ${usersDistinctTable}`); + await db.execute(sql`create table ${usersDistinctTable} (id integer, name text)`); + + await db.insert(usersDistinctTable).values([ + { id: 1, name: 'John' }, + { id: 1, name: 'John' }, + { id: 2, name: 'John' }, + { id: 1, name: 'Jane' } + ]); + const users1 = await db + .selectDistinct() + .from(usersDistinctTable) + .orderBy(usersDistinctTable.id, usersDistinctTable.name); + const users2 = await db + .selectDistinctOn([usersDistinctTable.id]) + .from(usersDistinctTable) + .orderBy(usersDistinctTable.id); + const users3 = await db + .selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }) + .from(usersDistinctTable) + .orderBy(usersDistinctTable.name); + + await db.execute(sql`drop table ${usersDistinctTable}`); + + t.deepEqual(users1, [ + { id: 1, name: 'Jane' }, + { id: 1, name: 'John' }, + { id: 2, name: 'John' } + ]); + + t.deepEqual(users2.length, 2); + t.deepEqual(users2[0]?.id, 1); + t.deepEqual(users2[1]?.id, 2); + + t.deepEqual(users3.length, 2); + t.deepEqual(users3[0]?.name, 'Jane'); + t.deepEqual(users3[1]?.name, 'John'); +}); + +test.skip('insert returning sql', async (t) => { + const { db } = t.context; + + const users = await db + .insert(usersTable) + .values({ name: 'John' }) + .returning({ + name: sql`upper(${usersTable.name})` + }); + + t.deepEqual(users, [{ name: 'JOHN' }]); +}); + +test.skip('delete returning sql', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const users = await db + .delete(usersTable) + .where(eq(usersTable.name, 'John')) + .returning({ + name: sql`upper(${usersTable.name})` + }); + + t.deepEqual(users, [{ name: 'JOHN' }]); +}); + +test.skip('update returning sql', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const users = await db + .update(usersTable) + .set({ name: 'Jane' }) + .where(eq(usersTable.name, 'John')) + .returning({ + name: sql`upper(${usersTable.name})` + }); + + t.deepEqual(users, [{ name: 'JANE' }]); +}); + +test.skip('update with returning all fields', async (t) => { + const { db } = t.context; + + const now = Date.now(); + + await db.insert(usersTable).values({ name: 'John' }); + const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning(); + + t.assert(users[0]!.createdAt instanceof Date); + t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); + t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); +}); + +test.skip('update with returning partial', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ + id: usersTable.id, + name: usersTable.name + }); + + t.deepEqual(users, [{ id: 1, name: 'Jane' }]); +}); + +test.skip('delete with returning all fields', async (t) => { + const { db } = t.context; + + const now = Date.now(); + + await db.insert(usersTable).values({ name: 'John' }); + const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); + + t.assert(users[0]!.createdAt instanceof Date); + t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); + t.deepEqual(users, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); +}); + +test.skip('delete with returning partial', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ + id: usersTable.id, + name: usersTable.name + }); + + t.deepEqual(users, [{ id: 1, name: 'John' }]); +}); + +test.skip('insert + select', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const result = await db.select().from(usersTable); + t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); + + await db.insert(usersTable).values({ name: 'Jane' }); + const result2 = await db.select().from(usersTable); + t.deepEqual(result2, [ + { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, + { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt } + ]); +}); + +test.skip('json insert', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); + const result = await db + .select({ + id: usersTable.id, + name: usersTable.name, + jsonb: usersTable.jsonb + }) + .from(usersTable); + + t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); +}); + +test.skip('char insert', async (t) => { + const { db } = t.context; + + await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); + const result = await db + .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) + .from(citiesTable); + + t.deepEqual(result, [{ id: 1, name: 'Austin', state: 'TX' }]); +}); + +test.skip('char update', async (t) => { + const { db } = t.context; + + await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); + await db.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1)); + const result = await db + .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) + .from(citiesTable); + + t.deepEqual(result, [{ id: 1, name: 'Atlanta', state: 'GA' }]); +}); + +test.skip('char delete', async (t) => { + const { db } = t.context; + + await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); + await db.delete(citiesTable).where(eq(citiesTable.state, 'TX')); + const result = await db + .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) + .from(citiesTable); + + t.deepEqual(result, []); +}); + +test.skip('insert with overridden default values', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John', verified: true }); + const result = await db.select().from(usersTable); + + t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); +}); + +test.skip('insert many', async (t) => { + const { db } = t.context; + + await db + .insert(usersTable) + .values([ + { name: 'John' }, + { name: 'Bruce', jsonb: ['foo', 'bar'] }, + { name: 'Jane' }, + { name: 'Austin', verified: true } + ]); + const result = await db + .select({ + id: usersTable.id, + name: usersTable.name, + jsonb: usersTable.jsonb, + verified: usersTable.verified + }) + .from(usersTable); + + t.deepEqual(result, [ + { id: 1, name: 'John', jsonb: null, verified: false }, + { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, + { id: 3, name: 'Jane', jsonb: null, verified: false }, + { id: 4, name: 'Austin', jsonb: null, verified: true } + ]); +}); + +test.skip('insert many with returning', async (t) => { + const { db } = t.context; + + const result = await db + .insert(usersTable) + .values([ + { name: 'John' }, + { name: 'Bruce', jsonb: ['foo', 'bar'] }, + { name: 'Jane' }, + { name: 'Austin', verified: true } + ]) + .returning({ + id: usersTable.id, + name: usersTable.name, + jsonb: usersTable.jsonb, + verified: usersTable.verified + }); + + t.deepEqual(result, [ + { id: 1, name: 'John', jsonb: null, verified: false }, + { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, + { id: 3, name: 'Jane', jsonb: null, verified: false }, + { id: 4, name: 'Austin', jsonb: null, verified: true } + ]); +}); + +test.skip('select with group by as field', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); + + const result = await db.select({ name: usersTable.name }).from(usersTable).groupBy(usersTable.name); + + t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); +}); + +test.skip('select with group by as sql', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); + + const result = await db + .select({ name: usersTable.name }) + .from(usersTable) + .groupBy(sql`${usersTable.name}`); + + t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); +}); + +test.skip('select with group by as sql + column', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); + + const result = await db + .select({ name: usersTable.name }) + .from(usersTable) + .groupBy(sql`${usersTable.name}`, usersTable.id); + + t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); +}); + +test.skip('select with group by as column + sql', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); + + const result = await db + .select({ name: usersTable.name }) + .from(usersTable) + .groupBy(usersTable.id, sql`${usersTable.name}`); + + t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); +}); + +test.skip('select with group by complex query', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); + + const result = await db + .select({ name: usersTable.name }) + .from(usersTable) + .groupBy(usersTable.id, sql`${usersTable.name}`) + .orderBy(asc(usersTable.name)) + .limit(1); + + t.deepEqual(result, [{ name: 'Jane' }]); +}); + +test('build query', async (t) => { + const { db } = t.context; + + const query = db + .select({ id: usersTable.id, name: usersTable.name }) + .from(usersTable) + .groupBy(usersTable.id, usersTable.name) + .toSQL(); + + t.deepEqual(query, { + sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', + params: [] + }); +}); + +test.skip('insert sql', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: sql`${'John'}` }); + const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); + t.deepEqual(result, [{ id: 1, name: 'John' }]); +}); + +test.skip('partial join with alias', async (t) => { + const { db } = t.context; + const customerAlias = alias(usersTable, 'customer'); + + await db.insert(usersTable).values([ + { id: 10, name: 'Ivan' }, + { id: 11, name: 'Hans' } + ]); + const result = await db + .select({ + user: { + id: usersTable.id, + name: usersTable.name + }, + customer: { + id: customerAlias.id, + name: customerAlias.name + } + }) + .from(usersTable) + .leftJoin(customerAlias, eq(customerAlias.id, 11)) + .where(eq(usersTable.id, 10)); + + t.deepEqual(result, [ + { + user: { id: 10, name: 'Ivan' }, + customer: { id: 11, name: 'Hans' } + } + ]); +}); + +test.skip('full join with alias', async (t) => { + const { db } = t.context; + + const pgTable = pgTableCreator((name) => `prefixed_${name}`); + + const users = pgTable('users', { + id: serial('id').primaryKey(), + name: text('name').notNull() + }); + + await db.execute(sql`drop table if exists ${users}`); + await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); + + const customers = alias(users, 'customer'); + + await db.insert(users).values([ + { id: 10, name: 'Ivan' }, + { id: 11, name: 'Hans' } + ]); + const result = await db.select().from(users).leftJoin(customers, eq(customers.id, 11)).where(eq(users.id, 10)); + + t.deepEqual(result, [ + { + users: { + id: 10, + name: 'Ivan' + }, + customer: { + id: 11, + name: 'Hans' + } + } + ]); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('select from alias', async (t) => { + const { db } = t.context; + + const pgTable = pgTableCreator((name) => `prefixed_${name}`); + + const users = pgTable('users', { + id: serial('id').primaryKey(), + name: text('name').notNull() + }); + + await db.execute(sql`drop table if exists ${users}`); + await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); + + const user = alias(users, 'user'); + const customers = alias(users, 'customer'); + + await db.insert(users).values([ + { id: 10, name: 'Ivan' }, + { id: 11, name: 'Hans' } + ]); + const result = await db.select().from(user).leftJoin(customers, eq(customers.id, 11)).where(eq(user.id, 10)); + + t.deepEqual(result, [ + { + user: { + id: 10, + name: 'Ivan' + }, + customer: { + id: 11, + name: 'Hans' + } + } + ]); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('insert with spaces', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: sql`'Jo h n'` }); + const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); + + t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); +}); + +test.skip('prepared statement', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const statement = db + .select({ + id: usersTable.id, + name: usersTable.name + }) + .from(usersTable) + .prepare('statement1'); + const result = await statement.execute(); + + t.deepEqual(result, [{ id: 1, name: 'John' }]); +}); + +test.skip('prepared statement reuse', async (t) => { + const { db } = t.context; + + const stmt = db + .insert(usersTable) + .values({ + verified: true, + name: placeholder('name') + }) + .prepare('stmt2'); + + for (let i = 0; i < 10; i++) { + await stmt.execute({ name: `John ${i}` }); + } + + const result = await db + .select({ + id: usersTable.id, + name: usersTable.name, + verified: usersTable.verified + }) + .from(usersTable); + + t.deepEqual(result, [ + { id: 1, name: 'John 0', verified: true }, + { id: 2, name: 'John 1', verified: true }, + { id: 3, name: 'John 2', verified: true }, + { id: 4, name: 'John 3', verified: true }, + { id: 5, name: 'John 4', verified: true }, + { id: 6, name: 'John 5', verified: true }, + { id: 7, name: 'John 6', verified: true }, + { id: 8, name: 'John 7', verified: true }, + { id: 9, name: 'John 8', verified: true }, + { id: 10, name: 'John 9', verified: true } + ]); +}); + +test.skip('prepared statement with placeholder in .where', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const stmt = db + .select({ + id: usersTable.id, + name: usersTable.name + }) + .from(usersTable) + .where(eq(usersTable.id, placeholder('id'))) + .prepare('stmt3'); + const result = await stmt.execute({ id: 1 }); + + t.deepEqual(result, [{ id: 1, name: 'John' }]); +}); + +test.skip('prepared statement with placeholder in .limit', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + const stmt = db + .select({ + id: usersTable.id, + name: usersTable.name + }) + .from(usersTable) + .where(eq(usersTable.id, placeholder('id'))) + .limit(placeholder('limit')) + .prepare('stmt_limit'); + + const result = await stmt.execute({ id: 1, limit: 1 }); + + t.deepEqual(result, [{ id: 1, name: 'John' }]); + t.is(result.length, 1); +}); + +test.skip('prepared statement with placeholder in .offset', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); + const stmt = db + .select({ + id: usersTable.id, + name: usersTable.name + }) + .from(usersTable) + .offset(placeholder('offset')) + .prepare('stmt_offset'); + + const result = await stmt.execute({ offset: 1 }); + + t.deepEqual(result, [{ id: 2, name: 'John1' }]); +}); + +// TODO change tests to new structure +test.skip('migrator', async (t) => { + const { db } = t.context; + + await db.execute(sql`drop table if exists all_columns`); + await db.execute(sql`drop table if exists users12`); + await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); + + await migrate(db, { migrationsFolder: './drizzle2/pg' }); + + await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); + + const result = await db.select().from(usersMigratorTable); + + t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); + + await db.execute(sql`drop table all_columns`); + await db.execute(sql`drop table users12`); + await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); +}); + +test.skip('insert via db.execute + select via db.execute', async (t) => { + const { db } = t.context; + + await db.execute(sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`); + + const result = await db.execute<{ id: number; name: string }>(sql`select id, name from "users"`); + t.deepEqual(result.rows, [{ id: 1, name: 'John' }]); +}); + +test.skip('insert via db.execute + returning', async (t) => { + const { db } = t.context; + + const inserted = await db.execute<{ id: number; name: string }>( + sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'}) returning ${usersTable.id}, ${ + usersTable.name + }` + ); + t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); +}); + +test.skip('insert via db.execute w/ query builder', async (t) => { + const { db } = t.context; + + const inserted = await db.execute>( + db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }) + ); + t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); +}); + +test.skip('build query insert with onConflict do update', async (t) => { + const { db } = t.context; + + const query = db + .insert(usersTable) + .values({ name: 'John', jsonb: ['foo', 'bar'] }) + .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) + .toSQL(); + + t.deepEqual(query, { + sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id") do update set "name" = $3', + params: ['John', '["foo","bar"]', 'John1'] + }); +}); + +test.skip('build query insert with onConflict do update / multiple columns', async (t) => { + const { db } = t.context; + + const query = db + .insert(usersTable) + .values({ name: 'John', jsonb: ['foo', 'bar'] }) + .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) + .toSQL(); + + t.deepEqual(query, { + sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id","name") do update set "name" = $3', + params: ['John', '["foo","bar"]', 'John1'] + }); +}); + +test.skip('build query insert with onConflict do nothing', async (t) => { + const { db } = t.context; + + const query = db + .insert(usersTable) + .values({ name: 'John', jsonb: ['foo', 'bar'] }) + .onConflictDoNothing() + .toSQL(); + + t.deepEqual(query, { + sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict do nothing', + params: ['John', '["foo","bar"]'] + }); +}); + +test.skip('build query insert with onConflict do nothing + target', async (t) => { + const { db } = t.context; + + const query = db + .insert(usersTable) + .values({ name: 'John', jsonb: ['foo', 'bar'] }) + .onConflictDoNothing({ target: usersTable.id }) + .toSQL(); + + t.deepEqual(query, { + sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id") do nothing', + params: ['John', '["foo","bar"]'] + }); +}); + +test.skip('insert with onConflict do update', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + + await db + .insert(usersTable) + .values({ id: 1, name: 'John' }) + .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); + + const res = await db + .select({ id: usersTable.id, name: usersTable.name }) + .from(usersTable) + .where(eq(usersTable.id, 1)); + + t.deepEqual(res, [{ id: 1, name: 'John1' }]); +}); + +test.skip('insert with onConflict do nothing', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + + await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing(); + + const res = await db + .select({ id: usersTable.id, name: usersTable.name }) + .from(usersTable) + .where(eq(usersTable.id, 1)); + + t.deepEqual(res, [{ id: 1, name: 'John' }]); +}); + +test.skip('insert with onConflict do nothing + target', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values({ name: 'John' }); + + await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing({ target: usersTable.id }); + + const res = await db + .select({ id: usersTable.id, name: usersTable.name }) + .from(usersTable) + .where(eq(usersTable.id, 1)); + + t.deepEqual(res, [{ id: 1, name: 'John' }]); +}); + +test.skip('left join (flat object fields)', async (t) => { + const { db } = t.context; + + const { id: cityId } = await db + .insert(citiesTable) + .values([{ name: 'Paris' }, { name: 'London' }]) + .returning({ id: citiesTable.id }) + .then((rows) => rows[0]!); + + await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); + + const res = await db + .select({ + userId: users2Table.id, + userName: users2Table.name, + cityId: citiesTable.id, + cityName: citiesTable.name + }) + .from(users2Table) + .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); + + t.deepEqual(res, [ + { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, + { userId: 2, userName: 'Jane', cityId: null, cityName: null } + ]); +}); + +test.skip('left join (grouped fields)', async (t) => { + const { db } = t.context; + + const { id: cityId } = await db + .insert(citiesTable) + .values([{ name: 'Paris' }, { name: 'London' }]) + .returning({ id: citiesTable.id }) + .then((rows) => rows[0]!); + + await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); + + const res = await db + .select({ + id: users2Table.id, + user: { + name: users2Table.name, + nameUpper: sql`upper(${users2Table.name})` + }, + city: { + id: citiesTable.id, + name: citiesTable.name, + nameUpper: sql`upper(${citiesTable.name})` + } + }) + .from(users2Table) + .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); + + t.deepEqual(res, [ + { + id: 1, + user: { name: 'John', nameUpper: 'JOHN' }, + city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' } + }, + { + id: 2, + user: { name: 'Jane', nameUpper: 'JANE' }, + city: null + } + ]); +}); + +test.skip('left join (all fields)', async (t) => { + const { db } = t.context; + + const { id: cityId } = await db + .insert(citiesTable) + .values([{ name: 'Paris' }, { name: 'London' }]) + .returning({ id: citiesTable.id }) + .then((rows) => rows[0]!); + + await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); + + const res = await db.select().from(users2Table).leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); + + t.deepEqual(res, [ + { + users2: { + id: 1, + name: 'John', + cityId + }, + cities: { + id: cityId, + name: 'Paris', + state: null + } + }, + { + users2: { + id: 2, + name: 'Jane', + cityId: null + }, + cities: null + } + ]); +}); + +test.skip('join subquery', async (t) => { + const { db } = t.context; + + await db + .insert(courseCategoriesTable) + .values([{ name: 'Category 1' }, { name: 'Category 2' }, { name: 'Category 3' }, { name: 'Category 4' }]); + + await db.insert(coursesTable).values([ + { name: 'Development', categoryId: 2 }, + { name: 'IT & Software', categoryId: 3 }, + { name: 'Marketing', categoryId: 4 }, + { name: 'Design', categoryId: 1 } + ]); + + const sq2 = db + .select({ + categoryId: courseCategoriesTable.id, + category: courseCategoriesTable.name, + total: sql`count(${courseCategoriesTable.id})` + }) + .from(courseCategoriesTable) + .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) + .as('sq2'); + + const res = await db + .select({ + courseName: coursesTable.name, + categoryId: sq2.categoryId + }) + .from(coursesTable) + .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) + .orderBy(coursesTable.name); + + t.deepEqual(res, [ + { courseName: 'Design', categoryId: 1 }, + { courseName: 'Development', categoryId: 2 }, + { courseName: 'IT & Software', categoryId: 3 }, + { courseName: 'Marketing', categoryId: 4 } + ]); +}); + +test.skip('with ... select', async (t) => { + const { db } = t.context; + + await db.insert(orders).values([ + { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, + { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, + { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, + { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, + { region: 'US', product: 'A', amount: 30, quantity: 3 }, + { region: 'US', product: 'A', amount: 40, quantity: 4 }, + { region: 'US', product: 'B', amount: 40, quantity: 4 }, + { region: 'US', product: 'B', amount: 50, quantity: 5 } + ]); + + const regionalSales = db.$with('regional_sales').as( + db + .select({ + region: orders.region, + totalSales: sql`sum(${orders.amount})`.as('total_sales') + }) + .from(orders) + .groupBy(orders.region) + ); + + const topRegions = db.$with('top_regions').as( + db + .select({ + region: regionalSales.region + }) + .from(regionalSales) + .where( + gt(regionalSales.totalSales, db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales)) + ) + ); + + const result = await db + .with(regionalSales, topRegions) + .select({ + region: orders.region, + product: orders.product, + productUnits: sql`sum(${orders.quantity})::int`, + productSales: sql`sum(${orders.amount})::int` + }) + .from(orders) + .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) + .groupBy(orders.region, orders.product) + .orderBy(orders.region, orders.product); + + t.deepEqual(result, [ + { + region: 'Europe', + product: 'A', + productUnits: 3, + productSales: 30 + }, + { + region: 'Europe', + product: 'B', + productUnits: 5, + productSales: 50 + }, + { + region: 'US', + product: 'A', + productUnits: 7, + productSales: 70 + }, + { + region: 'US', + product: 'B', + productUnits: 9, + productSales: 90 + } + ]); +}); + +test.skip('select from subquery sql', async (t) => { + const { db } = t.context; + + await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); + + const sq = db + .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) + .from(users2Table) + .as('sq'); + + const res = await db.select({ name: sq.name }).from(sq); + + t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); +}); + +test('select a field without joining its table', (t) => { + const { db } = t.context; + + t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query')); +}); + +test('select all fields from subquery without alias', (t) => { + const { db } = t.context; + + const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); + + t.throws(() => db.select().from(sq).prepare('query')); +}); + +test.skip('select count()', async (t) => { + const { db } = t.context; + + await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); + + const res = await db.select({ count: sql`count(*)` }).from(usersTable); + + t.deepEqual(res, [{ count: '2' }]); +}); + +test.skip('select count w/ custom mapper', async (t) => { + const { db } = t.context; + + function count(value: PgColumn | SQLWrapper): SQL; + function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased; + function count(value: PgColumn | SQLWrapper, alias?: string): SQL | SQL.Aliased { + const result = sql`count(${value})`.mapWith(Number); + if (!alias) { + return result; + } + return result.as(alias); + } + + await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); + + const res = await db.select({ count: count(sql`*`) }).from(usersTable); + + t.deepEqual(res, [{ count: 2 }]); +}); + +test.skip('network types', async (t) => { + const { db } = t.context; + + const value: typeof network.$inferSelect = { + inet: '127.0.0.1', + cidr: '192.168.100.128/25', + macaddr: '08:00:2b:01:02:03', + macaddr8: '08:00:2b:01:02:03:04:05' + }; + + await db.insert(network).values(value); + + const res = await db.select().from(network); + + t.deepEqual(res, [value]); +}); + +test.skip('array types', async (t) => { + const { db } = t.context; + + const values: (typeof salEmp.$inferSelect)[] = [ + { + name: 'John', + payByQuarter: [10000, 10000, 10000, 10000], + schedule: [ + ['meeting', 'lunch'], + ['training', 'presentation'] + ] + }, + { + name: 'Carol', + payByQuarter: [20000, 25000, 25000, 25000], + schedule: [ + ['breakfast', 'consulting'], + ['meeting', 'lunch'] + ] + } + ]; + + await db.insert(salEmp).values(values); + + const res = await db.select().from(salEmp); + + t.deepEqual(res, values); +}); + +test('select for ...', (t) => { + const { db } = t.context; + + const query = db + .select() + .from(users2Table) + .for('update') + .for('no key update', { of: users2Table }) + .for('no key update', { of: users2Table, skipLocked: true }) + .for('share', { of: users2Table, noWait: true }) + .toSQL(); + + t.regex( + query.sql, + / for update for no key update of "users2" for no key update of "users2" skip locked for share of "users2" no wait$/ + ); +}); + +test.skip('having', async (t) => { + const { db } = t.context; + + await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); + + await db.insert(users2Table).values([ + { name: 'John', cityId: 1 }, + { name: 'Jane', cityId: 1 }, + { + name: 'Jack', + cityId: 2 + } + ]); + + const result = await db + .select({ + id: citiesTable.id, + name: sql`upper(${citiesTable.name})`.as('upper_name'), + usersCount: sql`count(${users2Table.id})::int`.as('users_count') + }) + .from(citiesTable) + .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) + .where(({ name }) => sql`length(${name}) >= 3`) + .groupBy(citiesTable.id) + .having(({ usersCount }) => sql`${usersCount} > 0`) + .orderBy(({ name }) => name); + + t.deepEqual(result, [ + { + id: 1, + name: 'LONDON', + usersCount: 2 + }, + { + id: 2, + name: 'PARIS', + usersCount: 1 + } + ]); +}); + +test.skip('view', async (t) => { + const { db } = t.context; + + const newYorkers1 = pgView('new_yorkers').as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); + + const newYorkers2 = pgView('new_yorkers', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + cityId: integer('city_id').notNull() + }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); + + const newYorkers3 = pgView('new_yorkers', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + cityId: integer('city_id').notNull() + }).existing(); + + await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); + + await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); + + await db.insert(users2Table).values([ + { name: 'John', cityId: 1 }, + { name: 'Jane', cityId: 1 }, + { name: 'Jack', cityId: 2 } + ]); + + { + const result = await db.select().from(newYorkers1); + t.deepEqual(result, [ + { id: 1, name: 'John', cityId: 1 }, + { id: 2, name: 'Jane', cityId: 1 } + ]); + } + + { + const result = await db.select().from(newYorkers2); + t.deepEqual(result, [ + { id: 1, name: 'John', cityId: 1 }, + { id: 2, name: 'Jane', cityId: 1 } + ]); + } + + { + const result = await db.select().from(newYorkers3); + t.deepEqual(result, [ + { id: 1, name: 'John', cityId: 1 }, + { id: 2, name: 'Jane', cityId: 1 } + ]); + } + + { + const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); + t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); + } + + await db.execute(sql`drop view ${newYorkers1}`); +}); + +test.skip('materialized view', async (t) => { + const { db } = t.context; + + const newYorkers1 = pgMaterializedView('new_yorkers').as((qb) => + qb.select().from(users2Table).where(eq(users2Table.cityId, 1)) + ); + + const newYorkers2 = pgMaterializedView('new_yorkers', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + cityId: integer('city_id').notNull() + }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); + + const newYorkers3 = pgMaterializedView('new_yorkers', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + cityId: integer('city_id').notNull() + }).existing(); + + await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); + + await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); + + await db.insert(users2Table).values([ + { name: 'John', cityId: 1 }, + { name: 'Jane', cityId: 1 }, + { name: 'Jack', cityId: 2 } + ]); + + { + const result = await db.select().from(newYorkers1); + t.deepEqual(result, []); + } + + await db.refreshMaterializedView(newYorkers1); + + { + const result = await db.select().from(newYorkers1); + t.deepEqual(result, [ + { id: 1, name: 'John', cityId: 1 }, + { id: 2, name: 'Jane', cityId: 1 } + ]); + } + + { + const result = await db.select().from(newYorkers2); + t.deepEqual(result, [ + { id: 1, name: 'John', cityId: 1 }, + { id: 2, name: 'Jane', cityId: 1 } + ]); + } + + { + const result = await db.select().from(newYorkers3); + t.deepEqual(result, [ + { id: 1, name: 'John', cityId: 1 }, + { id: 2, name: 'Jane', cityId: 1 } + ]); + } + + { + const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); + t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); + } + + await db.execute(sql`drop materialized view ${newYorkers1}`); +}); + +// TODO: copy to SQLite and MySQL, add to docs +test('select from raw sql', async (t) => { + const { db } = t.context; + + const result = await db + .select({ + id: sql`id`, + name: sql`name` + }) + .from(sql`(select 1 as id, 'John' as name) as users`); + + Expect>; + + t.deepEqual(result, [{ id: 1, name: 'John' }]); +}); + +test.skip('select from raw sql with joins', async (t) => { + const { db } = t.context; + + const result = await db + .select({ + id: sql`users.id`, + name: sql`users.name`, + userCity: sql`users.city`, + cityName: sql`cities.name` + }) + .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) + .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); + + Expect>; + + t.deepEqual(result, [{ id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }]); +}); + +test.skip('join on aliased sql from select', async (t) => { + const { db } = t.context; + + const result = await db + .select({ + userId: sql`users.id`.as('userId'), + name: sql`users.name`, + userCity: sql`users.city`, + cityId: sql`cities.id`.as('cityId'), + cityName: sql`cities.name` + }) + .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) + .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); + + Expect>; + + t.deepEqual(result, [{ userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }]); +}); + +test.skip('join on aliased sql from with clause', async (t) => { + const { db } = t.context; + + const users = db.$with('users').as( + db + .select({ + id: sql`id`.as('userId'), + name: sql`name`.as('userName'), + city: sql`city`.as('city') + }) + .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) + ); + + const cities = db.$with('cities').as( + db + .select({ + id: sql`id`.as('cityId'), + name: sql`name`.as('cityName') + }) + .from(sql`(select 1 as id, 'Paris' as name) as cities`) + ); + + const result = await db + .with(users, cities) + .select({ + userId: users.id, + name: users.name, + userCity: users.city, + cityId: cities.id, + cityName: cities.name + }) + .from(users) + .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); + + Expect>; + + t.deepEqual(result, [{ userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }]); +}); + +test.skip('prefixed table', async (t) => { + const { db } = t.context; + + const pgTable = pgTableCreator((name) => `myprefix_${name}`); + + const users = pgTable('test_prefixed_table_with_unique_name', { + id: integer('id').primaryKey(), + name: text('name').notNull() + }); + + await db.execute(sql`drop table if exists ${users}`); + + await db.execute( + sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)` + ); + + await db.insert(users).values({ id: 1, name: 'John' }); + + const result = await db.select().from(users); + + t.deepEqual(result, [{ id: 1, name: 'John' }]); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('select from enum', async (t) => { + const { db } = t.context; + + const muscleEnum = pgEnum('muscle', [ + 'abdominals', + 'hamstrings', + 'adductors', + 'quadriceps', + 'biceps', + 'shoulders', + 'chest', + 'middle_back', + 'calves', + 'glutes', + 'lower_back', + 'lats', + 'triceps', + 'traps', + 'forearms', + 'neck', + 'abductors' + ]); + + const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']); + + const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']); + + const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']); + + const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']); + + const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']); + + const exercises = pgTable('exercises', { + id: serial('id').primaryKey(), + name: varchar('name').notNull(), + force: forceEnum('force'), + level: levelEnum('level'), + mechanic: mechanicEnum('mechanic'), + equipment: equipmentEnum('equipment'), + instructions: text('instructions'), + category: categoryEnum('category'), + primaryMuscles: muscleEnum('primary_muscles').array(), + secondaryMuscles: muscleEnum('secondary_muscles').array(), + createdAt: timestamp('created_at') + .notNull() + .default(sql`now()`), + updatedAt: timestamp('updated_at') + .notNull() + .default(sql`now()`) + }); + + await db.execute(sql`drop table if exists ${exercises}`); + await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`); + await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`); + await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`); + await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`); + await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); + await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); + + await db.execute( + sql`create type ${name( + muscleEnum.enumName + )} as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')` + ); + await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); + await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); + await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); + await db.execute( + sql`create type ${name( + equipmentEnum.enumName + )} as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')` + ); + await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); + await db.execute(sql` + create table ${exercises} ( + id serial primary key, + name varchar not null, + force force, + level level, + mechanic mechanic, + equipment equipment, + instructions text, + category category, + primary_muscles muscle[], + secondary_muscles muscle[], + created_at timestamp not null default now(), + updated_at timestamp not null default now() + ) + `); + + await db.insert(exercises).values({ + name: 'Bench Press', + force: 'isotonic', + level: 'beginner', + mechanic: 'compound', + equipment: 'barbell', + instructions: + 'Lie on your back on a flat bench. Grasp the barbell with an overhand grip, slightly wider than shoulder width. Unrack the barbell and hold it over you with your arms locked. Lower the barbell to your chest. Press the barbell back to the starting position.', + category: 'upper_body', + primaryMuscles: ['chest', 'triceps'], + secondaryMuscles: ['shoulders', 'traps'] + }); + + const result = await db.select().from(exercises); + + t.deepEqual(result, [ + { + id: 1, + name: 'Bench Press', + force: 'isotonic', + level: 'beginner', + mechanic: 'compound', + equipment: 'barbell', + instructions: + 'Lie on your back on a flat bench. Grasp the barbell with an overhand grip, slightly wider than shoulder width. Unrack the barbell and hold it over you with your arms locked. Lower the barbell to your chest. Press the barbell back to the starting position.', + category: 'upper_body', + primaryMuscles: ['chest', 'triceps'], + secondaryMuscles: ['shoulders', 'traps'], + createdAt: result[0]!.createdAt, + updatedAt: result[0]!.updatedAt + } + ]); + + await db.execute(sql`drop table ${exercises}`); + await db.execute(sql`drop type ${name(muscleEnum.enumName)}`); + await db.execute(sql`drop type ${name(forceEnum.enumName)}`); + await db.execute(sql`drop type ${name(levelEnum.enumName)}`); + await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`); + await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`); + await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); +}); + +test('orderBy with aliased column', (t) => { + const { db } = t.context; + + const query = db + .select({ + test: sql`something`.as('test') + }) + .from(users2Table) + .orderBy((fields) => fields.test) + .toSQL(); + + t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); +}); + +test.skip('select from sql', async (t) => { + const { db } = t.context; + + const metricEntry = pgTable('metric_entry', { + id: pgUuid('id').notNull(), + createdAt: timestamp('created_at').notNull() + }); + + await db.execute(sql`drop table if exists ${metricEntry}`); + await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`); + + const metricId = uuid(); + + const intervals = db.$with('intervals').as( + db + .select({ + startTime: sql`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'), + endTime: sql`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time') + }) + .from(sql`generate_series(0, 29, 1) as t(x)`) + ); + + await t.notThrowsAsync(() => + db + .with(intervals) + .select({ + startTime: intervals.startTime, + endTime: intervals.endTime, + count: sql`count(${metricEntry})` + }) + .from(metricEntry) + .rightJoin( + intervals, + and( + eq(metricEntry.id, metricId), + gte(metricEntry.createdAt, intervals.startTime), + lt(metricEntry.createdAt, intervals.endTime) + ) + ) + .groupBy(intervals.startTime, intervals.endTime) + .orderBy(asc(intervals.startTime)) + ); +}); + +test.skip('timestamp timezone', async (t) => { + const { db } = t.context; + + const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow() + }); + + await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); + + await db.execute( + sql` + create table users_test_with_and_without_timezone ( + id serial not null primary key, + name text not null, + created_at timestamptz not null default now(), + updated_at timestamp not null default now() + ) + ` + ); + + const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); + + await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); + await db.insert(usersTableWithAndWithoutTimezone).values({ + name: 'Without default times', + createdAt: date, + updatedAt: date + }); + const users = await db.select().from(usersTableWithAndWithoutTimezone); + + // check that the timestamps are set correctly for default times + t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000); + t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); + + // check that the timestamps are set correctly for non default times + t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000); + t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); +}); + +test.skip('transaction', async (t) => { + const { db } = t.context; + + const users = pgTable('users_transactions', { + id: serial('id').primaryKey(), + balance: integer('balance').notNull() + }); + const products = pgTable('products_transactions', { + id: serial('id').primaryKey(), + price: integer('price').notNull(), + stock: integer('stock').notNull() + }); + + await db.execute(sql`drop table if exists ${users}`); + await db.execute(sql`drop table if exists ${products}`); + + await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`); + await db.execute( + sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)` + ); + + const user = await db + .insert(users) + .values({ balance: 100 }) + .returning() + .then((rows) => rows[0]!); + const product = await db + .insert(products) + .values({ price: 10, stock: 10 }) + .returning() + .then((rows) => rows[0]!); + + await db.transaction(async (tx) => { + await tx + .update(users) + .set({ balance: user.balance - product.price }) + .where(eq(users.id, user.id)); + await tx + .update(products) + .set({ stock: product.stock - 1 }) + .where(eq(products.id, product.id)); + }); + + const result = await db.select().from(users); + + t.deepEqual(result, [{ id: 1, balance: 90 }]); + + await db.execute(sql`drop table ${users}`); + await db.execute(sql`drop table ${products}`); +}); + +test.skip('transaction rollback', async (t) => { + const { db } = t.context; + + const users = pgTable('users_transactions_rollback', { + id: serial('id').primaryKey(), + balance: integer('balance').notNull() + }); + + await db.execute(sql`drop table if exists ${users}`); + + await db.execute( + sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)` + ); + + await t.throwsAsync( + async () => + await db.transaction(async (tx) => { + await tx.insert(users).values({ balance: 100 }); + tx.rollback(); + }), + new TransactionRollbackError() + ); + + const result = await db.select().from(users); + + t.deepEqual(result, []); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('nested transaction', async (t) => { + const { db } = t.context; + + const users = pgTable('users_nested_transactions', { + id: serial('id').primaryKey(), + balance: integer('balance').notNull() + }); + + await db.execute(sql`drop table if exists ${users}`); + + await db.execute( + sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)` + ); + + await db.transaction(async (tx) => { + await tx.insert(users).values({ balance: 100 }); + + await tx.transaction(async (tx) => { + await tx.update(users).set({ balance: 200 }); + }); + }); + + const result = await db.select().from(users); + + t.deepEqual(result, [{ id: 1, balance: 200 }]); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('nested transaction rollback', async (t) => { + const { db } = t.context; + + const users = pgTable('users_nested_transactions_rollback', { + id: serial('id').primaryKey(), + balance: integer('balance').notNull() + }); + + await db.execute(sql`drop table if exists ${users}`); + + await db.execute( + sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)` + ); + + await db.transaction(async (tx) => { + await tx.insert(users).values({ balance: 100 }); + + await t.throwsAsync( + async () => + await tx.transaction(async (tx) => { + await tx.update(users).set({ balance: 200 }); + tx.rollback(); + }), + new TransactionRollbackError() + ); + }); + + const result = await db.select().from(users); + + t.deepEqual(result, [{ id: 1, balance: 100 }]); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('join subquery with join', async (t) => { + const { db } = t.context; + + const internalStaff = pgTable('internal_staff', { + userId: integer('user_id').notNull() + }); + + const customUser = pgTable('custom_user', { + id: integer('id').notNull() + }); + + const ticket = pgTable('ticket', { + staffId: integer('staff_id').notNull() + }); + + await db.execute(sql`drop table if exists ${internalStaff}`); + await db.execute(sql`drop table if exists ${customUser}`); + await db.execute(sql`drop table if exists ${ticket}`); + + await db.execute(sql`create table internal_staff (user_id integer not null)`); + await db.execute(sql`create table custom_user (id integer not null)`); + await db.execute(sql`create table ticket (staff_id integer not null)`); + + await db.insert(internalStaff).values({ userId: 1 }); + await db.insert(customUser).values({ id: 1 }); + await db.insert(ticket).values({ staffId: 1 }); + + const subq = db + .select() + .from(internalStaff) + .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) + .as('internal_staff'); + + const mainQuery = await db.select().from(ticket).leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); + + t.deepEqual(mainQuery, [ + { + ticket: { staffId: 1 }, + internal_staff: { + internal_staff: { userId: 1 }, + custom_user: { id: 1 } + } + } + ]); + + await db.execute(sql`drop table ${internalStaff}`); + await db.execute(sql`drop table ${customUser}`); + await db.execute(sql`drop table ${ticket}`); +}); + +test.skip('subquery with view', async (t) => { + const { db } = t.context; + + const users = pgTable('users_subquery_view', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + cityId: integer('city_id').notNull() + }); + + const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); + + await db.execute(sql`drop table if exists ${users}`); + await db.execute(sql`drop view if exists ${newYorkers}`); + + await db.execute( + sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)` + ); + await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); + + await db.insert(users).values([ + { name: 'John', cityId: 1 }, + { name: 'Jane', cityId: 2 }, + { name: 'Jack', cityId: 1 }, + { name: 'Jill', cityId: 2 } + ]); + + const sq = db.$with('sq').as(db.select().from(newYorkers)); + const result = await db.with(sq).select().from(sq); + + t.deepEqual(result, [ + { id: 1, name: 'John', cityId: 1 }, + { id: 3, name: 'Jack', cityId: 1 } + ]); + + await db.execute(sql`drop view ${newYorkers}`); + await db.execute(sql`drop table ${users}`); +}); + +test.skip('join view as subquery', async (t) => { + const { db } = t.context; + + const users = pgTable('users_join_view', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + cityId: integer('city_id').notNull() + }); + + const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); + + await db.execute(sql`drop table if exists ${users}`); + await db.execute(sql`drop view if exists ${newYorkers}`); + + await db.execute( + sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)` + ); + await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); + + await db.insert(users).values([ + { name: 'John', cityId: 1 }, + { name: 'Jane', cityId: 2 }, + { name: 'Jack', cityId: 1 }, + { name: 'Jill', cityId: 2 } + ]); + + const sq = db.select().from(newYorkers).as('new_yorkers_sq'); + + const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); + + t.deepEqual(result, [ + { + users_join_view: { id: 1, name: 'John', cityId: 1 }, + new_yorkers_sq: { id: 1, name: 'John', cityId: 1 } + }, + { + users_join_view: { id: 2, name: 'Jane', cityId: 2 }, + new_yorkers_sq: null + }, + { + users_join_view: { id: 3, name: 'Jack', cityId: 1 }, + new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 } + }, + { + users_join_view: { id: 4, name: 'Jill', cityId: 2 }, + new_yorkers_sq: null + } + ]); + + await db.execute(sql`drop view ${newYorkers}`); + await db.execute(sql`drop table ${users}`); +}); + +test.skip('table selection with single table', async (t) => { + const { db } = t.context; + + const users = pgTable('users', { + id: serial('id').primaryKey(), + name: text('name').notNull(), + cityId: integer('city_id').notNull() + }); + + await db.execute(sql`drop table if exists ${users}`); + + await db.execute( + sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)` + ); + + await db.insert(users).values({ name: 'John', cityId: 1 }); + + const result = await db.select({ users }).from(users); + + t.deepEqual(result, [{ users: { id: 1, name: 'John', cityId: 1 } }]); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('set null to jsonb field', async (t) => { + const { db } = t.context; + + const users = pgTable('users', { + id: serial('id').primaryKey(), + jsonb: jsonb('jsonb') + }); + + await db.execute(sql`drop table if exists ${users}`); + + await db.execute(sql`create table ${users} (id serial not null primary key, jsonb jsonb)`); + + const result = await db.insert(users).values({ jsonb: null }).returning(); + + t.deepEqual(result, [{ id: 1, jsonb: null }]); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('insert undefined', async (t) => { + const { db } = t.context; + + const users = pgTable('users', { + id: serial('id').primaryKey(), + name: text('name') + }); + + await db.execute(sql`drop table if exists ${users}`); + + await db.execute(sql`create table ${users} (id serial not null primary key, name text)`); + + await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); + + await db.execute(sql`drop table ${users}`); +}); + +test.skip('update undefined', async (t) => { + const { db } = t.context; + + const users = pgTable('users', { + id: serial('id').primaryKey(), + name: text('name') + }); + + await db.execute(sql`drop table if exists ${users}`); + + await db.execute(sql`create table ${users} (id serial not null primary key, name text)`); + + await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); + await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); + + await db.execute(sql`drop table ${users}`); +}); diff --git a/packages/plugin-client-drizzle/test/utils.ts b/packages/plugin-client-drizzle/test/utils.ts new file mode 100644 index 000000000..ecf7cc029 --- /dev/null +++ b/packages/plugin-client-drizzle/test/utils.ts @@ -0,0 +1,5 @@ +// shut up eslint you cannot possibly comprehend what's happening here +// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars +export function Expect() {} + +export type Equal = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false; diff --git a/packages/plugin-client-drizzle/test/xata.codegen.ts b/packages/plugin-client-drizzle/test/xata.codegen.ts new file mode 100644 index 000000000..e3e77cc88 --- /dev/null +++ b/packages/plugin-client-drizzle/test/xata.codegen.ts @@ -0,0 +1,149 @@ +import { buildClient } from '@xata.io/client'; +import type { BaseClientOptions, SchemaInference, XataRecord } from '@xata.io/client'; + +export const tables = [ + { + name: 'users', + columns: [ + { name: 'name', type: 'string', notNull: true, defaultValue: '' }, + { name: 'verified', type: 'bool', notNull: true, defaultValue: 'false' }, + { name: 'jsonb', type: 'json' }, + { + name: 'created_at', + type: 'datetime', + notNull: true, + defaultValue: 'now' + } + ] + }, + { + name: 'cities', + columns: [ + { name: 'name', type: 'string', notNull: true, defaultValue: '' }, + { name: 'state', type: 'string' } + ], + revLinks: [{ column: 'city_id', table: 'users2' }] + }, + { + name: 'users2', + columns: [ + { name: 'name', type: 'string', notNull: true, defaultValue: '' }, + { name: 'city_id', type: 'link', link: { table: 'cities' } } + ] + }, + { + name: 'courses', + columns: [ + { name: 'name', type: 'string', notNull: true, defaultValue: '' }, + { + name: 'category_id', + type: 'link', + link: { table: 'course_categories' } + } + ] + }, + { + name: 'course_categories', + columns: [{ name: 'name', type: 'string', defaultValue: '' }], + revLinks: [{ column: 'category_id', table: 'courses' }] + }, + { + name: 'orders', + columns: [ + { name: 'region', type: 'string', notNull: true, defaultValue: '' }, + { name: 'product', type: 'string', notNull: true, defaultValue: '' }, + { name: 'amount', type: 'int', notNull: true, defaultValue: '0' }, + { name: 'quantity', type: 'int', notNull: true, defaultValue: '0' } + ] + }, + { + name: 'network_table', + columns: [ + { name: 'inet', type: 'string', notNull: true, defaultValue: '' }, + { name: 'cidr', type: 'string', notNull: true, defaultValue: '' }, + { name: 'macaddr', type: 'string', notNull: true, defaultValue: '' }, + { name: 'macaddr8', type: 'string', notNull: true, defaultValue: '' } + ] + }, + { + name: 'sal_emp', + columns: [ + { name: 'name', type: 'string' }, + { name: 'pay_by_quarter', type: 'multiple' }, + { name: 'schedule', type: 'multiple' } + ] + }, + { name: 'tictactoe', columns: [{ name: 'squares', type: 'json' }] }, + { + name: 'users12', + columns: [ + { name: 'name', type: 'string', notNull: true, defaultValue: '' }, + { name: 'email', type: 'email' } + ] + } +] as const; + +export type SchemaTables = typeof tables; +export type InferredTypes = SchemaInference; + +export type Users = InferredTypes['users']; +export type UsersRecord = Users & XataRecord; + +export type Cities = InferredTypes['cities']; +export type CitiesRecord = Cities & XataRecord; + +export type Users2 = InferredTypes['users2']; +export type Users2Record = Users2 & XataRecord; + +export type Courses = InferredTypes['courses']; +export type CoursesRecord = Courses & XataRecord; + +export type CourseCategories = InferredTypes['course_categories']; +export type CourseCategoriesRecord = CourseCategories & XataRecord; + +export type Orders = InferredTypes['orders']; +export type OrdersRecord = Orders & XataRecord; + +export type NetworkTable = InferredTypes['network_table']; +export type NetworkTableRecord = NetworkTable & XataRecord; + +export type SalEmp = InferredTypes['sal_emp']; +export type SalEmpRecord = SalEmp & XataRecord; + +export type Tictactoe = InferredTypes['tictactoe']; +export type TictactoeRecord = Tictactoe & XataRecord; + +export type Users12 = InferredTypes['users12']; +export type Users12Record = Users12 & XataRecord; + +export type DatabaseSchema = { + users: UsersRecord; + cities: CitiesRecord; + users2: Users2Record; + courses: CoursesRecord; + course_categories: CourseCategoriesRecord; + orders: OrdersRecord; + network_table: NetworkTableRecord; + sal_emp: SalEmpRecord; + tictactoe: TictactoeRecord; + users12: Users12Record; +}; + +const DatabaseClient = buildClient(); + +const defaultOptions = {}; + +export class XataClient extends DatabaseClient { + constructor(options?: BaseClientOptions) { + super({ ...defaultOptions, ...options }, tables); + } +} + +let instance: XataClient | undefined = undefined; + +export const getXataClient = () => { + if (instance) return instance; + + instance = new XataClient(); + return instance; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c9a7af28..96a4cd204 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -438,9 +438,18 @@ importers: specifier: workspace:* version: link:../client devDependencies: + ava: + specifier: ^5.3.1 + version: 5.3.1 drizzle-orm: specifier: ^0.28.5 version: 0.28.5(@opentelemetry/api@1.4.1) + tsx: + specifier: ^3.12.7 + version: 3.12.7 + uuid: + specifier: ^9.0.0 + version: 9.0.0 packages/plugin-client-kysely: dependencies: @@ -2304,6 +2313,30 @@ packages: '@edge-runtime/primitives': 3.1.0 dev: false + /@esbuild-kit/cjs-loader@2.4.2: + resolution: + { integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg== } + dependencies: + '@esbuild-kit/core-utils': 3.2.2 + get-tsconfig: 4.7.0 + dev: true + + /@esbuild-kit/core-utils@3.2.2: + resolution: + { integrity: sha512-Ub6LaRaAgF80dTSzUdXpFLM1pVDdmEVB9qb5iAzSpyDlX/mfJTFGOnZ516O05p5uWWteNviMKi4PAyEuRxI5gA== } + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + dev: true + + /@esbuild-kit/esm-loader@2.5.5: + resolution: + { integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw== } + dependencies: + '@esbuild-kit/core-utils': 3.2.2 + get-tsconfig: 4.7.0 + dev: true + /@esbuild/android-arm64@0.18.19: resolution: { integrity: sha512-4+jkUFQxZkQfQOOxfGVZB38YUWHMJX2ihZwF+2nh8m7bHdWXpixiurgGRN3c/KMSwlltbYI0/i929jwBRMFzbA== } @@ -2314,6 +2347,16 @@ packages: dev: true optional: true + /@esbuild/android-arm64@0.18.20: + resolution: + { integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== } + engines: { node: '>=12' } + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm64@0.19.2: resolution: { integrity: sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw== } @@ -2333,6 +2376,16 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.18.20: + resolution: + { integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== } + engines: { node: '>=12' } + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm@0.19.2: resolution: { integrity: sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q== } @@ -2352,6 +2405,16 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.18.20: + resolution: + { integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== } + engines: { node: '>=12' } + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64@0.19.2: resolution: { integrity: sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w== } @@ -2371,6 +2434,16 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.18.20: + resolution: + { integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== } + engines: { node: '>=12' } + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64@0.19.2: resolution: { integrity: sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA== } @@ -2390,6 +2463,16 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.18.20: + resolution: + { integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== } + engines: { node: '>=12' } + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64@0.19.2: resolution: { integrity: sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw== } @@ -2409,6 +2492,16 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.18.20: + resolution: + { integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== } + engines: { node: '>=12' } + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64@0.19.2: resolution: { integrity: sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ== } @@ -2428,6 +2521,16 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.18.20: + resolution: + { integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== } + engines: { node: '>=12' } + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64@0.19.2: resolution: { integrity: sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw== } @@ -2447,6 +2550,16 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.18.20: + resolution: + { integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== } + engines: { node: '>=12' } + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64@0.19.2: resolution: { integrity: sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg== } @@ -2466,6 +2579,16 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.18.20: + resolution: + { integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== } + engines: { node: '>=12' } + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm@0.19.2: resolution: { integrity: sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg== } @@ -2485,6 +2608,16 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.18.20: + resolution: + { integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== } + engines: { node: '>=12' } + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32@0.19.2: resolution: { integrity: sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ== } @@ -2504,6 +2637,16 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.18.20: + resolution: + { integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== } + engines: { node: '>=12' } + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64@0.19.2: resolution: { integrity: sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw== } @@ -2523,6 +2666,16 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.18.20: + resolution: + { integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== } + engines: { node: '>=12' } + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el@0.19.2: resolution: { integrity: sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg== } @@ -2542,6 +2695,16 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.18.20: + resolution: + { integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== } + engines: { node: '>=12' } + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64@0.19.2: resolution: { integrity: sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw== } @@ -2561,6 +2724,16 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.18.20: + resolution: + { integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== } + engines: { node: '>=12' } + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64@0.19.2: resolution: { integrity: sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw== } @@ -2580,6 +2753,16 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.18.20: + resolution: + { integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== } + engines: { node: '>=12' } + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x@0.19.2: resolution: { integrity: sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g== } @@ -2599,6 +2782,16 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.18.20: + resolution: + { integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== } + engines: { node: '>=12' } + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64@0.19.2: resolution: { integrity: sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ== } @@ -2618,6 +2811,16 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.18.20: + resolution: + { integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== } + engines: { node: '>=12' } + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64@0.19.2: resolution: { integrity: sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ== } @@ -2637,6 +2840,16 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.18.20: + resolution: + { integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== } + engines: { node: '>=12' } + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64@0.19.2: resolution: { integrity: sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw== } @@ -2656,6 +2869,16 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.18.20: + resolution: + { integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== } + engines: { node: '>=12' } + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64@0.19.2: resolution: { integrity: sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw== } @@ -2675,6 +2898,16 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.18.20: + resolution: + { integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== } + engines: { node: '>=12' } + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64@0.19.2: resolution: { integrity: sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg== } @@ -2694,6 +2927,16 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.18.20: + resolution: + { integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== } + engines: { node: '>=12' } + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32@0.19.2: resolution: { integrity: sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA== } @@ -2713,6 +2956,16 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.18.20: + resolution: + { integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== } + engines: { node: '>=12' } + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64@0.19.2: resolution: { integrity: sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw== } @@ -3748,7 +4001,7 @@ packages: toml: 3.0.0 unixify: 1.0.0 urlpattern-polyfill: 8.0.2 - yargs: 17.6.2 + yargs: 17.7.2 transitivePeerDependencies: - encoding - supports-color @@ -5990,7 +6243,6 @@ packages: dependencies: clean-stack: 4.2.0 indent-string: 5.0.0 - dev: false /ajv-errors@3.0.0(ajv@8.12.0): resolution: @@ -6202,6 +6454,12 @@ packages: engines: { node: '>=8' } dev: true + /array-find-index@1.0.2: + resolution: + { integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== } + engines: { node: '>=0.10.0' } + dev: true + /array-includes@3.1.6: resolution: { integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== } @@ -6266,6 +6524,12 @@ packages: is-shared-array-buffer: 1.0.2 dev: true + /arrgv@1.0.2: + resolution: + { integrity: sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw== } + engines: { node: '>=8.0.0' } + dev: true + /arrify@1.0.1: resolution: { integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== } @@ -6282,7 +6546,6 @@ packages: resolution: { integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw== } engines: { node: '>=12' } - dev: false /asap@2.0.6: resolution: @@ -6346,6 +6609,64 @@ packages: engines: { node: '>=8' } dev: true + /ava@5.3.1: + resolution: + { integrity: sha512-Scv9a4gMOXB6+ni4toLuhAm9KYWEjsgBglJl+kMGI5+IVDt120CCDZyB5HNU9DjmLI2t4I0GbnxGLmmRfGTJGg== } + engines: { node: '>=14.19 <15 || >=16.15 <17 || >=18' } + hasBin: true + peerDependencies: + '@ava/typescript': '*' + peerDependenciesMeta: + '@ava/typescript': + optional: true + dependencies: + acorn: 8.9.0 + acorn-walk: 8.2.0 + ansi-styles: 6.2.1 + arrgv: 1.0.2 + arrify: 3.0.0 + callsites: 4.1.0 + cbor: 8.1.0 + chalk: 5.3.0 + chokidar: 3.5.3 + chunkd: 2.0.1 + ci-info: 3.8.0 + ci-parallel-vars: 1.0.1 + clean-yaml-object: 0.1.0 + cli-truncate: 3.1.0 + code-excerpt: 4.0.0 + common-path-prefix: 3.0.0 + concordance: 5.0.4 + currently-unhandled: 0.4.1 + debug: 4.3.4(supports-color@9.3.1) + emittery: 1.0.1 + figures: 5.0.0 + globby: 13.2.2 + ignore-by-default: 2.1.0 + indent-string: 5.0.0 + is-error: 2.2.2 + is-plain-object: 5.0.0 + is-promise: 4.0.0 + matcher: 5.0.0 + mem: 9.0.2 + ms: 2.1.3 + p-event: 5.0.1 + p-map: 5.5.0 + picomatch: 2.3.1 + pkg-conf: 4.0.0 + plur: 5.1.0 + pretty-ms: 8.0.0 + resolve-cwd: 3.0.0 + stack-utils: 2.0.6 + strip-ansi: 7.0.1 + supertap: 3.0.1 + temp-dir: 3.0.0 + write-file-atomic: 5.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + dev: true + /available-typed-arrays@1.0.5: resolution: { integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== } @@ -6518,6 +6839,11 @@ packages: inherits: 2.0.4 readable-stream: 3.6.0 + /blueimp-md5@2.19.0: + resolution: + { integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== } + dev: true + /bn.js@4.12.0: resolution: { integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== } @@ -6854,6 +7180,12 @@ packages: { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== } engines: { node: '>=6' } + /callsites@4.1.0: + resolution: + { integrity: sha512-aBMbD1Xxay75ViYezwT40aQONfr+pSXTHwNKvIXhXD6+LY3F1dLIcceoC5OZKBVHbXcysz1hL9D2w0JJIMXpUw== } + engines: { node: '>=12.20' } + dev: true + /camelcase-keys@6.2.2: resolution: { integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== } @@ -6893,6 +7225,14 @@ packages: { integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== } engines: { node: '>= 0.8.0' } + /cbor@8.1.0: + resolution: + { integrity: sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== } + engines: { node: '>=12.19' } + dependencies: + nofilter: 3.1.0 + dev: true + /ccount@1.1.0: resolution: { integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== } @@ -6985,6 +7325,11 @@ packages: { integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== } engines: { node: '>=10' } + /chunkd@2.0.1: + resolution: + { integrity: sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ== } + dev: true + /ci-info@2.0.0: resolution: { integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== } @@ -6996,6 +7341,11 @@ packages: engines: { node: '>=8' } dev: true + /ci-parallel-vars@1.0.1: + resolution: + { integrity: sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg== } + dev: true + /cipher-base@1.0.4: resolution: { integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== } @@ -7036,7 +7386,12 @@ packages: engines: { node: '>=12' } dependencies: escape-string-regexp: 5.0.0 - dev: false + + /clean-yaml-object@0.1.0: + resolution: + { integrity: sha512-3yONmlN9CSAkzNwnRCiJQ7Q2xK5mWuEfL3PuTZcAUzhObbXsfsnMptJzXwz93nc5zn9V9TwCVMmV7w4xsm43dw== } + engines: { node: '>=0.10.0' } + dev: true /cli-boxes@2.2.1: resolution: @@ -7219,6 +7574,14 @@ packages: convert-to-spaces: 1.0.2 dev: true + /code-excerpt@4.0.0: + resolution: + { integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dependencies: + convert-to-spaces: 2.0.1 + dev: true + /color-convert@1.9.3: resolution: { integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== } @@ -7306,7 +7669,6 @@ packages: /common-path-prefix@3.0.0: resolution: { integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== } - dev: false /commondir@1.0.1: resolution: @@ -7338,6 +7700,21 @@ packages: typedarray: 0.0.6 dev: true + /concordance@5.0.4: + resolution: + { integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw== } + engines: { node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14' } + dependencies: + date-time: 3.1.0 + esutils: 2.0.3 + fast-diff: 1.3.0 + js-string-escape: 1.0.1 + lodash: 4.17.21 + md5-hex: 3.0.1 + semver: 7.5.4 + well-known-symbols: 2.0.0 + dev: true + /concurrently@7.6.0: resolution: { integrity: sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw== } @@ -7385,6 +7762,12 @@ packages: engines: { node: '>= 4' } dev: true + /convert-to-spaces@2.0.1: + resolution: + { integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dev: true + /cookie@0.4.2: resolution: { integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== } @@ -7440,7 +7823,7 @@ packages: dependencies: arrify: 3.0.0 cp-file: 9.1.0 - globby: 13.1.3 + globby: 13.2.2 junk: 4.0.0 micromatch: 4.0.5 nested-error-stacks: 2.1.1 @@ -7584,6 +7967,14 @@ packages: stream-transform: 2.1.3 dev: true + /currently-unhandled@0.4.1: + resolution: + { integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng== } + engines: { node: '>=0.10.0' } + dependencies: + array-find-index: 1.0.2 + dev: true + /dargs@7.0.0: resolution: { integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== } @@ -7606,6 +7997,14 @@ packages: engines: { node: '>=0.11' } dev: true + /date-time@3.1.0: + resolution: + { integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg== } + engines: { node: '>=6' } + dependencies: + time-zone: 1.0.0 + dev: true + /dateformat@4.6.3: resolution: { integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== } @@ -8118,6 +8517,12 @@ packages: minimalistic-crypto-utils: 1.0.1 dev: true + /emittery@1.0.1: + resolution: + { integrity: sha512-2ID6FdrMD9KDLldGesP6317G78K7km/kMcwItRtVFva7I/cSEOIaLpewaUb+YLXVwdAp3Ctfxh/V5zIl1sj7dQ== } + engines: { node: '>=14.16' } + dev: true + /emoji-regex@10.1.0: resolution: { integrity: sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg== } @@ -8358,6 +8763,37 @@ packages: '@esbuild/win32-x64': 0.18.19 dev: true + /esbuild@0.18.20: + resolution: + { integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== } + engines: { node: '>=12' } + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + /esbuild@0.19.2: resolution: { integrity: sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg== } @@ -8413,7 +8849,6 @@ packages: resolution: { integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== } engines: { node: '>=12' } - dev: false /escodegen@2.0.0: resolution: @@ -8947,6 +9382,11 @@ packages: resolution: { integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== } + /fast-diff@1.3.0: + resolution: + { integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== } + dev: true + /fast-equals@3.0.3: resolution: { integrity: sha512-NCe8qxnZFARSHGztGMZOO/PC1qa5MIFB5Hp66WdzbCRAz8U8US3bx1UTgLS49efBQPcUtO9gf5oVEY8o7y/7Kg== } @@ -9042,7 +9482,6 @@ packages: dependencies: escape-string-regexp: 5.0.0 is-unicode-supported: 1.3.0 - dev: false /file-entry-cache@6.0.1: resolution: @@ -9107,7 +9546,6 @@ packages: dependencies: locate-path: 7.2.0 path-exists: 5.0.0 - dev: false /find-yarn-workspace-root2@1.2.16: resolution: @@ -9408,7 +9846,6 @@ packages: { integrity: sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw== } dependencies: resolve-pkg-maps: 1.0.0 - dev: false /github-slugger@1.5.0: resolution: @@ -9525,6 +9962,17 @@ packages: slash: 4.0.0 dev: false + /globby@13.2.2: + resolution: + { integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 4.0.0 + /gonzales-pe@4.3.0: resolution: { integrity: sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== } @@ -9922,6 +10370,12 @@ packages: resolution: { integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== } + /ignore-by-default@2.1.0: + resolution: + { integrity: sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw== } + engines: { node: '>=10 <11 || >=12 <13 || >=14' } + dev: true + /ignore-walk@4.0.1: resolution: { integrity: sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw== } @@ -9968,7 +10422,6 @@ packages: resolution: { integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== } engines: { node: '>=12' } - dev: false /indexof@0.0.1: resolution: @@ -10079,6 +10532,12 @@ packages: { integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== } dev: true + /irregular-plurals@3.5.0: + resolution: + { integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ== } + engines: { node: '>=8' } + dev: true + /is-alphabetical@1.0.4: resolution: { integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== } @@ -10204,6 +10663,11 @@ packages: hasBin: true dev: false + /is-error@2.2.2: + resolution: + { integrity: sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg== } + dev: true + /is-extglob@2.1.1: resolution: { integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== } @@ -10329,6 +10793,11 @@ packages: engines: { node: '>=0.10.0' } dev: true + /is-promise@4.0.0: + resolution: + { integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== } + dev: true + /is-reference@1.2.1: resolution: { integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== } @@ -10421,7 +10890,6 @@ packages: resolution: { integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== } engines: { node: '>=12' } - dev: false /is-url-superb@4.0.0: resolution: @@ -10567,6 +11035,12 @@ packages: engines: { node: '>=0.10.0' } dev: true + /js-string-escape@1.0.1: + resolution: + { integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== } + engines: { node: '>= 0.8' } + dev: true + /js-tokens@4.0.0: resolution: { integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== } @@ -10917,6 +11391,12 @@ packages: type-fest: 0.3.1 dev: false + /load-json-file@7.0.1: + resolution: + { integrity: sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dev: true + /load-yaml-file@0.2.0: resolution: { integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== } @@ -10956,7 +11436,6 @@ packages: engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } dependencies: p-locate: 6.0.0 - dev: false /lodash-es@4.17.21: resolution: @@ -11273,6 +11752,14 @@ packages: - supports-color dev: true + /map-age-cleaner@0.1.3: + resolution: + { integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== } + engines: { node: '>=6' } + dependencies: + p-defer: 1.0.0 + dev: true + /map-obj@1.0.1: resolution: { integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== } @@ -11298,6 +11785,22 @@ packages: repeat-string: 1.6.1 dev: true + /matcher@5.0.0: + resolution: + { integrity: sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dependencies: + escape-string-regexp: 5.0.0 + dev: true + + /md5-hex@3.0.1: + resolution: + { integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw== } + engines: { node: '>=8' } + dependencies: + blueimp-md5: 2.19.0 + dev: true + /md5.js@1.3.5: resolution: { integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== } @@ -11443,6 +11946,15 @@ packages: vinyl-file: 3.0.0 dev: true + /mem@9.0.2: + resolution: + { integrity: sha512-F2t4YIv9XQUBHt6AOJ0y7lSmP1+cY7Fm1DRh9GClTGzKST7UWLMx6ly9WZdLH/G/ppM5RL4MlQfRT71ri9t19A== } + engines: { node: '>=12.20' } + dependencies: + map-age-cleaner: 0.1.3 + mimic-fn: 4.0.0 + dev: true + /memoize-one@6.0.0: resolution: { integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== } @@ -12182,6 +12694,12 @@ packages: engines: { node: '>=0.12.0' } dev: false + /nofilter@3.1.0: + resolution: + { integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== } + engines: { node: '>=12.19' } + dev: true + /nopt@5.0.0: resolution: { integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== } @@ -12451,7 +12969,7 @@ packages: oas-kit-common: 1.0.8 reftools: 1.1.9 yaml: 1.10.2 - yargs: 17.6.2 + yargs: 17.7.2 dev: true /oas-schema-walker@1.1.5: @@ -12725,6 +13243,12 @@ packages: { integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== } engines: { node: '>=12.20' } + /p-defer@1.0.0: + resolution: + { integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== } + engines: { node: '>=4' } + dev: true + /p-event@4.2.0: resolution: { integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== } @@ -12739,7 +13263,6 @@ packages: engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } dependencies: p-timeout: 5.1.0 - dev: false /p-every@2.0.0: resolution: @@ -12815,7 +13338,6 @@ packages: engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } dependencies: p-limit: 4.0.0 - dev: false /p-map@2.1.0: resolution: @@ -12836,7 +13358,6 @@ packages: engines: { node: '>=12' } dependencies: aggregate-error: 4.0.1 - dev: false /p-queue@6.6.2: resolution: @@ -12882,7 +13403,6 @@ packages: resolution: { integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew== } engines: { node: '>=12' } - dev: false /p-transform@1.3.0: resolution: @@ -13012,7 +13532,6 @@ packages: resolution: { integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw== } engines: { node: '>=12' } - dev: false /parse-package-name@1.0.0: resolution: @@ -13064,7 +13583,6 @@ packages: resolution: { integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dev: false /path-is-absolute@1.0.1: resolution: @@ -13178,6 +13696,15 @@ packages: { integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== } engines: { node: '>=6' } + /pkg-conf@4.0.0: + resolution: + { integrity: sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dependencies: + find-up: 6.3.0 + load-json-file: 7.0.1 + dev: true + /pkg-dir@4.2.0: resolution: { integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== } @@ -13203,6 +13730,14 @@ packages: pathe: 1.1.1 dev: true + /plur@5.1.0: + resolution: + { integrity: sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dependencies: + irregular-plurals: 3.5.0 + dev: true + /pluralize@8.0.0: resolution: { integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== } @@ -13321,7 +13856,6 @@ packages: engines: { node: '>=14.16' } dependencies: parse-ms: 3.0.0 - dev: false /proc-log@1.0.0: resolution: @@ -13923,6 +14457,14 @@ packages: resolution: { integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== } + /resolve-cwd@3.0.0: + resolution: + { integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== } + engines: { node: '>=8' } + dependencies: + resolve-from: 5.0.0 + dev: true + /resolve-from@4.0.0: resolution: { integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== } @@ -13936,7 +14478,6 @@ packages: /resolve-pkg-maps@1.0.0: resolution: { integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== } - dev: false /resolve@1.22.4: resolution: @@ -14310,6 +14851,14 @@ packages: dependencies: lru-cache: 6.0.0 + /serialize-error@7.0.1: + resolution: + { integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== } + engines: { node: '>=10' } + dependencies: + type-fest: 0.13.1 + dev: true + /set-blocking@2.0.0: resolution: { integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== } @@ -14575,13 +15124,11 @@ packages: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: false /source-map@0.6.1: resolution: { integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== } engines: { node: '>=0.10.0' } - dev: false /sourcemap-codec@1.4.8: resolution: @@ -14858,6 +15405,17 @@ packages: acorn: 8.9.0 dev: true + /supertap@3.0.1: + resolution: + { integrity: sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw== } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dependencies: + indent-string: 5.0.0 + js-yaml: 3.14.1 + serialize-error: 7.0.1 + strip-ansi: 7.0.1 + dev: true + /supports-color@5.5.0: resolution: { integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== } @@ -14912,7 +15470,7 @@ packages: oas-validator: 5.0.8 reftools: 1.1.9 yaml: 1.10.2 - yargs: 17.6.2 + yargs: 17.7.2 transitivePeerDependencies: - encoding dev: true @@ -14953,6 +15511,12 @@ packages: mkdirp: 1.0.4 yallist: 4.0.0 + /temp-dir@3.0.0: + resolution: + { integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw== } + engines: { node: '>=14.16' } + dev: true + /term-size@2.2.1: resolution: { integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== } @@ -15017,6 +15581,12 @@ packages: convert-hrtime: 3.0.0 dev: false + /time-zone@1.0.0: + resolution: + { integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== } + engines: { node: '>=4' } + dev: true + /tinybench@2.5.0: resolution: { integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA== } @@ -15222,6 +15792,18 @@ packages: tslib: 1.14.1 typescript: 5.2.2 + /tsx@3.12.7: + resolution: + { integrity: sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw== } + hasBin: true + dependencies: + '@esbuild-kit/cjs-loader': 2.4.2 + '@esbuild-kit/core-utils': 3.2.2 + '@esbuild-kit/esm-loader': 2.5.5 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /tty-table@4.1.6: resolution: { integrity: sha512-kRj5CBzOrakV4VRRY5kUWbNYvo/FpOsz65DzI5op9P+cHov3+IqPbo1JE1ZnQGkHdZgNFDsrEjrfqqy/Ply9fw== } @@ -15695,7 +16277,6 @@ packages: resolution: { integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== } hasBin: true - dev: false /v8-compile-cache-lib@3.0.1: resolution: @@ -15955,6 +16536,12 @@ packages: resolution: { integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== } + /well-known-symbols@2.0.0: + resolution: + { integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== } + engines: { node: '>=6' } + dev: true + /whatwg-url@5.0.0: resolution: { integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== } @@ -16106,6 +16693,15 @@ packages: signal-exit: 3.0.7 dev: true + /write-file-atomic@5.0.1: + resolution: + { integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.0.2 + dev: true + /ws@7.5.9: resolution: { integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== } @@ -16286,6 +16882,19 @@ packages: y18n: 5.0.8 yargs-parser: 21.1.1 + /yargs@17.7.2: + resolution: + { integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== } + engines: { node: '>=12' } + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + /yarn@1.22.19: resolution: { integrity: sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ== } From 930d0e084abc8253c95e563744df65b6f0a81707 Mon Sep 17 00:00:00 2001 From: Alexis Rico Date: Wed, 6 Sep 2023 14:56:14 +0200 Subject: [PATCH 2/4] Fix empty check Signed-off-by: Alexis Rico --- packages/plugin-client-drizzle/src/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-client-drizzle/src/session.ts b/packages/plugin-client-drizzle/src/session.ts index 5ace436eb..20314014d 100644 --- a/packages/plugin-client-drizzle/src/session.ts +++ b/packages/plugin-client-drizzle/src/session.ts @@ -63,7 +63,7 @@ export class XataPreparedQuery extends PreparedQu // FIXME: This is a hack, we should be able to get the fields from the query but SELECT * fails const fields = this.fields ?? - Object.keys(records[0]!).map( + Object.keys(records[0] ?? {}).map( (key) => ({ path: [key], From bced69195c806e52484f1514bb815eb04b475891 Mon Sep 17 00:00:00 2001 From: Alexis Rico Date: Thu, 5 Oct 2023 12:08:38 +0200 Subject: [PATCH 3/4] Add traces to debug Signed-off-by: Alexis Rico --- packages/plugin-client-drizzle/package.json | 2 +- packages/plugin-client-drizzle/src/session.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/plugin-client-drizzle/package.json b/packages/plugin-client-drizzle/package.json index f57a7e06a..94cc35c9e 100644 --- a/packages/plugin-client-drizzle/package.json +++ b/packages/plugin-client-drizzle/package.json @@ -15,7 +15,7 @@ "scripts": { "build": "rimraf dist && rollup -c", "tsc": "tsc --noEmit", - "test": "NODE_OPTIONS='--loader=tsx --no-warnings' ava --serial test/*.ava.ts" + "test": "NODE_OPTIONS='--loader=tsx --no-warnings' ava --verbose --serial test/*.ava.ts" }, "author": "", "license": "Apache-2.0", diff --git a/packages/plugin-client-drizzle/src/session.ts b/packages/plugin-client-drizzle/src/session.ts index 20314014d..0a96e1eb1 100644 --- a/packages/plugin-client-drizzle/src/session.ts +++ b/packages/plugin-client-drizzle/src/session.ts @@ -60,6 +60,8 @@ export class XataPreparedQuery extends PreparedQu params }); + console.debug('execute', { statement: this.query.statement, params, result: records.length }); + // FIXME: This is a hack, we should be able to get the fields from the query but SELECT * fails const fields = this.fields ?? From de857bcc4b9c9d769c0d7eb202f6e43116063841 Mon Sep 17 00:00:00 2001 From: Alexis Rico Date: Thu, 5 Oct 2023 12:09:57 +0200 Subject: [PATCH 4/4] Show warning Signed-off-by: Alexis Rico --- packages/plugin-client-drizzle/src/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-client-drizzle/src/session.ts b/packages/plugin-client-drizzle/src/session.ts index 0a96e1eb1..ee0391e18 100644 --- a/packages/plugin-client-drizzle/src/session.ts +++ b/packages/plugin-client-drizzle/src/session.ts @@ -60,7 +60,7 @@ export class XataPreparedQuery extends PreparedQu params }); - console.debug('execute', { statement: this.query.statement, params, result: records.length }); + console.debug('execute', { statement: this.query.statement, params, result: records.length, warning }); // FIXME: This is a hack, we should be able to get the fields from the query but SELECT * fails const fields =