diff --git a/.changeset/light-cycles-repair.md b/.changeset/light-cycles-repair.md new file mode 100644 index 000000000..8ed66fc50 --- /dev/null +++ b/.changeset/light-cycles-repair.md @@ -0,0 +1,5 @@ +--- +'@xata.io/client': major +--- + +Make XataApiClient to use ES Proxies diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 000000000..09815f51e --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,15 @@ +{ + "mode": "pre", + "tag": "next", + "initialVersions": { + "@xata.io/cli": "0.15.3", + "@xata.io/client": "0.28.2", + "@xata.io/codegen": "0.28.2", + "@xata.io/importer": "1.1.3", + "@xata.io/drizzle": "0.0.13", + "@xata.io/kysely": "0.1.13", + "@xata.io/netlify": "0.1.23", + "@xata.io/plugin-client-opentelemetry": "0.2.37" + }, + "changesets": [] +} diff --git a/.changeset/violet-worms-develop.md b/.changeset/violet-worms-develop.md new file mode 100644 index 000000000..2d58b8624 --- /dev/null +++ b/.changeset/violet-worms-develop.md @@ -0,0 +1,12 @@ +--- +'@xata.io/cli': major +'@xata.io/client': major +'@xata.io/codegen': major +'@xata.io/importer': major +'@xata.io/drizzle': major +'@xata.io/kysely': major +'@xata.io/netlify': major +'@xata.io/plugin-client-opentelemetry': major +--- + +Version 1.0 diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index ef9f494b1..21b62c105 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -106,8 +106,6 @@ jobs: cat << EOF > .changeset/force-canary-build.md --- '@xata.io/plugin-client-opentelemetry': patch - '@xata.io/plugin-client-cloudflare': patch - '@xata.io/plugin-client-cache': patch '@xata.io/drizzle': patch '@xata.io/kysely': patch '@xata.io/pgroll': patch diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1cbc1c41d..2357a5aea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,8 +53,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GIT_TOKEN }} run: | - npx changeset version - npx changeset publish + npx changeset pre exit + npx changeset version --snapshot next + npx changeset publish --tag next --no-git-tag - name: Create Release Pull Request or Publish to npm uses: changesets/action@v1 diff --git a/cli/package.json b/cli/package.json index 508c71dc5..f633fd182 100644 --- a/cli/package.json +++ b/cli/package.json @@ -43,6 +43,7 @@ "lodash.keyby": "^4.6.0", "lodash.compact": "^3.0.1", "lodash.get": "^4.4.2", + "lodash.keyby": "^4.6.0", "lodash.set": "^4.3.2", "node-fetch": "^3.3.2", "open": "^10.1.0", @@ -50,9 +51,9 @@ "relaxed-json": "^1.0.3", "semver": "^7.6.2", "text-table": "^0.2.0", - "tslib": "^2.6.3", "tmp": "^0.2.3", - "type-fest": "^4.20.1", + "tslib": "^2.6.2", + "type-fest": "^4.18.1", "which": "^4.0.0", "zod": "^3.23.8" }, diff --git a/cli/src/base.ts b/cli/src/base.ts index cd5277a6d..fb340432f 100644 --- a/cli/src/base.ts +++ b/cli/src/base.ts @@ -1,13 +1,5 @@ import { Command, Flags, Interfaces } from '@oclif/core'; -import { - buildClient, - getAPIKey, - getBranch, - getHostUrl, - parseWorkspacesUrlParts, - Schemas, - XataApiPlugin -} from '@xata.io/client'; +import { buildClient, getHostUrl, parseWorkspacesUrlParts, Schemas, XataApiPlugin } from '@xata.io/client'; import { XataImportPlugin } from '@xata.io/importer'; import ansiRegex from 'ansi-regex'; import chalk from 'chalk'; @@ -36,7 +28,7 @@ import { reportBugURL } from './utils.js'; export class XataClient extends buildClient({ api: new XataApiPlugin(), import: new XataImportPlugin() -}) {} +}) {} export type APIKeyLocation = 'shell' | 'dotenv' | 'profile' | 'new'; @@ -192,7 +184,7 @@ export abstract class BaseCommand extends Command { const { flags } = await this.parseCommand(); const profileName = flags.profile || getEnvProfileName(); - const apiKey = getAPIKey(); + const apiKey = process.env.XATA_API_KEY; const useEnv = !ignoreEnv || profileName === 'default'; if (useEnv && apiKey) return buildProfile({ name: 'default', apiKey }); @@ -220,15 +212,18 @@ export abstract class BaseCommand extends Command { const databaseURL = flags.db ?? `${getHostUrl(host, 'workspaces')}/db/{database}`; const branch = flags.branch ?? this.getCurrentBranchName(); - this.#xataClient = new XataClient({ - databaseURL, - branch, - apiKey, - fetch, - host, - clientName: 'cli', - xataAgentExtra: { cliCommandId: this.id ?? 'unknown' } - }); + this.#xataClient = new XataClient( + { + databaseURL, + branch, + apiKey, + fetch, + host, + clientName: 'cli', + xataAgentExtra: { cliCommandId: this.id ?? 'unknown' } + }, + { tables: [] } + ); return this.#xataClient; } @@ -531,7 +526,7 @@ export abstract class BaseCommand extends Command { } getCurrentBranchName() { - return getBranch() ?? 'main'; + return process.env.XATA_BRANCH ?? 'main'; } async updateConfig() { diff --git a/cli/src/commands/codegen/index.ts b/cli/src/commands/codegen/index.ts index 1741efab6..7a0b2539f 100644 --- a/cli/src/commands/codegen/index.ts +++ b/cli/src/commands/codegen/index.ts @@ -1,11 +1,12 @@ import { Flags } from '@oclif/core'; import { generate, isValidJavascriptTarget, javascriptTargets } from '@xata.io/codegen'; import chalk from 'chalk'; -import { mkdir, readFile, writeFile } from 'fs/promises'; +import { mkdir, writeFile } from 'fs/promises'; import path, { dirname, extname, relative } from 'path'; import { BaseCommand } from '../../base.js'; import { ProjectConfig } from '../../config.js'; import { getBranchDetailsWithPgRoll } from '../../migrations/pgroll.js'; +import { safeReadFile } from '../../utils/files.js'; export const languages: Record = { '.js': 'javascript', @@ -46,9 +47,6 @@ export default class Codegen extends BaseCommand { }), 'worker-id': Flags.string({ description: 'Xata worker deployment id' - }), - 'experimental-incremental-build': Flags.boolean({ - description: 'Experimental: Keep the source code in the generated file and only update the parts that changed' }) }; @@ -84,27 +82,16 @@ export default class Codegen extends BaseCommand { } const xata = await this.getXataClient(); - const { workspace, region, database, branch, databaseURL } = await this.getParsedDatabaseURLWithBranch( - flags.db, - flags.branch - ); + const { workspace, region, database, branch } = await this.getParsedDatabaseURLWithBranch(flags.db, flags.branch); const { schema } = await getBranchDetailsWithPgRoll(xata, { workspace, region, database, branch }); - - const codegenBranch = flags['inject-branch'] ? branch : undefined; - - // Experimental: Keep the source code in the generated file and only update the parts that changed - const incrementalBuild = - flags['experimental-incremental-build'] ?? this.projectConfig?.experimental?.incrementalBuild ?? false; - const existingCode = incrementalBuild ? await readFile(output, 'utf8').catch(() => undefined) : undefined; + const existingCode = await safeReadFile(output); const result = await generate({ schema, - databaseURL, language, moduleType, javascriptTarget, - branch: codegenBranch, - existingCode + existingCode: existingCode ?? '' }); const { typescript, javascript, types } = result; diff --git a/cli/src/commands/diff/index.ts b/cli/src/commands/diff/index.ts deleted file mode 100644 index e3d6ca7d4..000000000 --- a/cli/src/commands/diff/index.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Args } from '@oclif/core'; -import { BaseCommand } from '../../base.js'; -import { getLocalMigrationFiles } from '../../migrations/files.js'; -import { buildMigrationDiff } from '../../utils/diff.js'; -import compact from 'lodash.compact'; - -export default class Diff extends BaseCommand { - static description = 'Compare two local or remote branches'; - - static examples = []; - - static flags = { - ...this.commonFlags, - ...this.databaseURLFlag - }; - - static args = { - branch: Args.string({ description: 'The branch to compare', required: false }), - base: Args.string({ description: 'The base branch to compare against', required: false }) - }; - - static hidden = true; - - static enableJsonFlag = true; - - async run() { - const { args, flags } = await this.parseCommand(); - - const xata = await this.getXataClient(); - const { workspace, region, database, branch } = await this.getParsedDatabaseURLWithBranch( - flags.db, - args.branch ?? 'main' - ); - - this.info(`Diff command is experimental, use with caution`); - - const localMigrationFiles = await getLocalMigrationFiles(); - const schemaOperations = compact(localMigrationFiles.flatMap((migrationFile) => migrationFile.operations)); - - const apiRequest = - args.branch && args.base - ? xata.api.migrations.compareBranchSchemas({ - pathParams: { workspace, region, dbBranchName: `${database}:${args.branch}`, branchName: args.base }, - body: {} - }) - : xata.api.migrations.compareBranchWithUserSchema({ - pathParams: { workspace, region, dbBranchName: `${database}:${branch}` }, - body: { schema: { tables: [] }, schemaOperations } - }); - - const { - edits: { operations } - } = await apiRequest; - - const diff = buildMigrationDiff(operations); - if (this.jsonEnabled()) return diff; - - if (operations.length === 0) { - this.log('No changes found'); - return; - } - - this.log(diff); - } -} diff --git a/cli/src/commands/import/csv.ts b/cli/src/commands/import/csv.ts index a61ba4574..87a85ef1f 100644 --- a/cli/src/commands/import/csv.ts +++ b/cli/src/commands/import/csv.ts @@ -158,30 +158,24 @@ export default class ImportCSV extends BaseCommand { if (!parseResults.success) { throw new Error('Failed to parse CSV file'); } - const batchRows = () => { - if (this.#pgrollEnabled) { - const res = parseResults.data.map(({ data }) => { - const formattedRow: { [k: string]: any } = {}; - const keys = Object.keys(data); - for (const key of keys) { - if (INTERNAL_COLUMNS_PGROLL.includes(key) && key !== 'xata_id') continue; - formattedRow[key] = data[key]; - } - return formattedRow; - }); - return res; - } else { - return parseResults.data.map(({ data }) => data); + const batchRows = parseResults.data.map(({ data }) => { + const formattedRow: { [k: string]: any } = {}; + const keys = Object.keys(data); + for (const key of keys) { + if (INTERNAL_COLUMNS_PGROLL.includes(key) && key !== 'xata_id') continue; + formattedRow[key] = data[key]; } - }; + return formattedRow; + }); + const importResult = await xata.import.importBatch( { workspace, region, database, branch }, { - columns: this.#pgrollEnabled - ? parseResults.columns.filter(({ name }) => name === 'xata_id' || !INTERNAL_COLUMNS_PGROLL.includes(name)) - : parseResults.columns, + columns: parseResults.columns.filter( + ({ name }) => name === 'xata_id' || !INTERNAL_COLUMNS_PGROLL.includes(name) + ), table, - batchRows: batchRows() + batchRows } ); await xata.import.importFiles( @@ -246,6 +240,7 @@ export default class ImportCSV extends BaseCommand { const xata = await this.getXataClient(); const { workspace, region, database, branch } = await this.parseDatabase(); const { schema: existingSchema } = await getBranchDetailsWithPgRoll(xata, { workspace, region, database, branch }); + const newSchema = { tables: [ ...existingSchema.tables.filter((t) => t.name !== table), diff --git a/cli/src/commands/init/index.test.ts b/cli/src/commands/init/index.test.ts index 8b2807a04..475369df4 100644 --- a/cli/src/commands/init/index.test.ts +++ b/cli/src/commands/init/index.test.ts @@ -157,19 +157,18 @@ describe('xata init', () => { }", "package.json": "{"name":"test","version":"1.0.0"}", "readme.md": "", - "xataCustom.ts": "// Generated by Xata Codegen 0.29.5. Please do not edit. - import { buildClient } from "@xata.io/client"; + "xataCustom.ts": "import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "@xata.io/client"; - const tables = [ - { name: "table1", columns: [{ name: "a", type: "string" }] }, - ] as const; + const schema = { + tables: [{ name: "table1", columns: [{ name: "a", type: "string" }] }], + } as const; - export type SchemaTables = typeof tables; + export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type Table1 = InferredTypes["table1"]; @@ -181,24 +180,23 @@ describe('xata init', () => { const DatabaseClient = buildClient(); - const defaultOptions = { - databaseURL: "https://test-1234.us-east-1.xata.sh/db/db1", - }; - - export class XataClient extends DatabaseClient { + export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } - - let instance: XataClient | undefined = undefined; - - export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }; ", } `); @@ -242,19 +240,18 @@ describe('xata init', () => { } }", "readme.md": "", - "xataCustom.ts": "// Generated by Xata Codegen 0.29.5. Please do not edit. - import { buildClient } from "@xata.io/client"; + "xataCustom.ts": "import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "@xata.io/client"; - const tables = [ - { name: "table1", columns: [{ name: "a", type: "string" }] }, - ] as const; + const schema = { + tables: [{ name: "table1", columns: [{ name: "a", type: "string" }] }], + } as const; - export type SchemaTables = typeof tables; + export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type Table1 = InferredTypes["table1"]; @@ -266,24 +263,23 @@ describe('xata init', () => { const DatabaseClient = buildClient(); - const defaultOptions = { - databaseURL: "https://test-1234.us-east-1.xata.sh/db/db1", - }; - - export class XataClient extends DatabaseClient { + export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } - - let instance: XataClient | undefined = undefined; - - export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }; ", } `); @@ -319,19 +315,21 @@ describe('xata init', () => { } }", "readme.md": "", - "xataCustom.ts": "// Generated by Xata Codegen 0.29.5. Please do not edit. - import { buildClient } from "npm:@xata.io/client@latest"; + "xataCustom.ts": "import { + buildClient, + getDeployPreviewBranch, + } from "npm:@xata.io/client@latest"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "npm:@xata.io/client@latest"; - const tables = [ - { name: "table1", columns: [{ name: "a", type: "string" }] }, - ] as const; + const schema = { + tables: [{ name: "table1", columns: [{ name: "a", type: "string" }] }], + } as const; - export type SchemaTables = typeof tables; + export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type Table1 = InferredTypes["table1"]; @@ -343,24 +341,23 @@ describe('xata init', () => { const DatabaseClient = buildClient(); - const defaultOptions = { - databaseURL: "https://test-1234.us-east-1.xata.sh/db/db1", - }; - - export class XataClient extends DatabaseClient { + export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: Deno.env.get("XATA_API_KEY"), + databaseURL: Deno.env.get("XATA_DATABASE_URL"), + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(Deno.env.get) ?? + Deno.env.get("XATA_BRANCH") ?? + "main", + ...options, + }, + schema + ); } } - - let instance: XataClient | undefined = undefined; - - export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }; ", } `); @@ -400,19 +397,18 @@ describe('xata init', () => { "package.json": "{"name":"test","version":"1.0.0"}", "pnpm-lock.yaml": "lockfileVersion: '6.0'", "readme.md": "", - "xataCustom.ts": "// Generated by Xata Codegen 0.29.5. Please do not edit. - import { buildClient } from "@xata.io/client"; + "xataCustom.ts": "import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "@xata.io/client"; - const tables = [ - { name: "table1", columns: [{ name: "a", type: "string" }] }, - ] as const; + const schema = { + tables: [{ name: "table1", columns: [{ name: "a", type: "string" }] }], + } as const; - export type SchemaTables = typeof tables; + export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type Table1 = InferredTypes["table1"]; @@ -424,24 +420,23 @@ describe('xata init', () => { const DatabaseClient = buildClient(); - const defaultOptions = { - databaseURL: "https://test-1234.us-east-1.xata.sh/db/db1", - }; - - export class XataClient extends DatabaseClient { + export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } - - let instance: XataClient | undefined = undefined; - - export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }; ", } `); diff --git a/cli/src/commands/init/index.ts b/cli/src/commands/init/index.ts index 74b2788f4..8c45afe26 100644 --- a/cli/src/commands/init/index.ts +++ b/cli/src/commands/init/index.ts @@ -201,8 +201,12 @@ export default class Init extends BaseCommand { this.log(); if (this.projectConfig?.codegen?.output) { - const hasTables = this.branchDetails.schema?.tables && this.branchDetails.schema?.tables.length > 0; - const hasColumns = this.branchDetails.schema?.tables.some((t) => t.columns.length > 0); + const { schema: currentSchema } = await ( + await this.getXataClient() + ).api.branch.getBranchDetails({ pathParams: { workspace, region, dbBranchName: `${database}:${branch}` } }); + + const hasTables = currentSchema?.tables && currentSchema?.tables.length > 0; + const hasColumns = currentSchema?.tables.some((t) => t.columns.length > 0); const isSchemaSetup = hasTables && hasColumns; if (shouldInstallPackage && !canInstallPackage) { this.warn( diff --git a/cli/src/commands/pull/index.ts b/cli/src/commands/pull/index.ts index f6fe778d6..b5819e924 100644 --- a/cli/src/commands/pull/index.ts +++ b/cli/src/commands/pull/index.ts @@ -66,11 +66,7 @@ export default class Pull extends BaseCommand { } else { const data = await xata.api.migrations.getBranchSchemaHistory({ pathParams: { workspace, region, dbBranchName: `${database}:${branch}` }, - body: { - // TODO: Fix pagination in the API to start from last known migration and not from the beginning - // Also paginate until we get all migrations - page: { size: 200 } - } + body: { page: { size: 200 } } }); logs = data.logs; } diff --git a/cli/src/commands/push/index.ts b/cli/src/commands/push/index.ts index 343012931..a3db0a089 100644 --- a/cli/src/commands/push/index.ts +++ b/cli/src/commands/push/index.ts @@ -63,11 +63,7 @@ export default class Push extends BaseCommand { } else { const data = await xata.api.migrations.getBranchSchemaHistory({ pathParams: { workspace, region, dbBranchName: `${database}:${branch}` }, - body: { - // TODO: Fix pagination in the API to start from last known migration and not from the beginning - // Also paginate until we get all migrations - page: { size: 200 } - } + body: { page: { size: 200 } } }); logs = data.logs; } diff --git a/cli/src/commands/schema/edit.test.ts b/cli/src/commands/schema/edit.test.ts index d13ac6953..90bd06747 100644 --- a/cli/src/commands/schema/edit.test.ts +++ b/cli/src/commands/schema/edit.test.ts @@ -1,5 +1,7 @@ import { beforeEach, expect, test, describe } from 'vitest'; -import { +import { PgRollMigration } from '@xata.io/pgroll'; +import EditSchema, { + editsToMigrations, AddColumnPayload, AddTablePayload, ColumnAdditions, @@ -9,8 +11,6 @@ import { DeleteTablePayload, EditTablePayload } from './edit'; -import { PgRollMigration } from '@xata.io/pgroll'; -import EditSchemaNew, { editsToMigrations } from './edit'; const column: AddColumnPayload['column'] = { name: 'col1', @@ -108,7 +108,7 @@ const createEdit = (column: ColumnData) => { const runTest = (name: string, setup: () => void, expectation: any) => { test(name, () => { setup(); - editCommand.currentMigration.operations = editsToMigrations(editCommand as EditSchemaNew); + editCommand.currentMigration.operations = editsToMigrations(editCommand as EditSchema); expect(editCommand.currentMigration.operations).toEqual(expectation); }); }; diff --git a/cli/src/commands/shell/index.ts b/cli/src/commands/shell/index.ts index 8e6dd3a13..a8afd38e8 100644 --- a/cli/src/commands/shell/index.ts +++ b/cli/src/commands/shell/index.ts @@ -49,7 +49,7 @@ export default class Shell extends BaseCommand { const branchDetails = await getBranchDetailsWithPgRoll(xata, { workspace, region, database, branch }); const { schema } = branchDetails; - const { javascript } = await generate({ language: 'javascript', databaseURL, schema }); + const { javascript } = await generate({ language: 'javascript', schema }); await fs.writeFile(tempFile, javascript); } catch (err) { const message = err instanceof Error ? err.message : String(err); diff --git a/cli/src/config.ts b/cli/src/config.ts index d8e2faed1..365ceb6e7 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -5,7 +5,7 @@ export const projectConfigSchema = z.object({ databaseURL: z.string(), codegen: z.object({ output: z.string(), - moduleType: z.enum(['cjs', 'esm', 'deno']), + moduleType: z.enum(['cjs', 'esm', 'deno', 'vite']), declarations: z.boolean(), javascriptTarget: z.enum([ 'es5', @@ -21,7 +21,6 @@ export const projectConfigSchema = z.object({ ]) }), experimental: z.object({ - incrementalBuild: z.boolean(), workflow: z.boolean() }) }); diff --git a/cli/src/migrations/pgroll.ts b/cli/src/migrations/pgroll.ts index 97f6e4b3e..d5d066942 100644 --- a/cli/src/migrations/pgroll.ts +++ b/cli/src/migrations/pgroll.ts @@ -1,13 +1,13 @@ import { Schemas, XataApiClient } from '@xata.io/client'; import { Column } from '@xata.io/codegen'; +import { OpRawSQL, OpRenameConstraint, PgRollOperation } from '@xata.io/pgroll'; import path from 'path'; import z from 'zod'; import { XataClient } from '../base.js'; +import { BranchSchemaFormatted } from '../commands/schema/edit.js'; import { safeJSONParse, safeReadFile } from '../utils/files.js'; import { migrationsDir, readMigrationsDir } from './files.js'; import { MigrationFilePgroll, migrationFilePgroll } from './schema.js'; -import { OpRawSQL, OpRenameConstraint, PgRollOperation } from '@xata.io/pgroll'; -import { BranchSchemaFormatted } from '../commands/schema/edit.js'; export const isBranchPgRollEnabled = (details: Schemas.DBBranch) => { // @ts-expect-error TODO: Fix this when api is finalized @@ -346,12 +346,7 @@ export async function waitForMigrationToFinish( jobId: string ): Promise { const { status, error } = await api.migrations.getMigrationJobStatus({ - pathParams: { - workspace: workspace, - region: region, - dbBranchName: `${database}:${branch}`, - jobId - } + pathParams: { workspace, region, dbBranchName: `${database}:${branch}`, jobId } }); if (status === 'failed') { throw new Error(`Migration failed, ${error}`); diff --git a/cli/src/utils/diff.ts b/cli/src/utils/diff.ts deleted file mode 100644 index 118f35ded..000000000 --- a/cli/src/utils/diff.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Schemas } from '@xata.io/client'; -import chalk from 'chalk'; - -export function buildMigrationDiff(ops: Schemas.MigrationOp[]): string { - const lines = ops.map((op) => { - if ('addTable' in op) { - return `${chalk.green('+')} ${chalk.bold(op.addTable.table)}`; - } else if ('removeTable' in op) { - return `${chalk.red('-')} ${chalk.bold(op.removeTable.table)}`; - } else if ('renameTable' in op) { - return `${chalk.yellow('~')} ${chalk.bold(op.renameTable.oldName)} -> ${chalk.bold(op.renameTable.newName)}`; - } else if ('addColumn' in op) { - return `${chalk.green('+')} ${chalk.bold(op.addColumn.table)}.${chalk.bold(op.addColumn.column.name)}`; - } else if ('removeColumn' in op) { - return `${chalk.red('-')} ${chalk.bold(op.removeColumn.table)}.${chalk.bold(op.removeColumn.column)}`; - } else if ('renameColumn' in op) { - return `${chalk.yellow('~')} ${chalk.bold(op.renameColumn.table)}.${chalk.bold( - op.renameColumn.oldName - )} -> ${chalk.bold(op.renameColumn.newName)}`; - } else { - throw new Error(`Unknown migration op: ${JSON.stringify(op)}`); - } - }); - - return lines.join('\n'); -} diff --git a/packages/client/package.json b/packages/client/package.json index 2f50128bb..56244b2b2 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -33,5 +33,10 @@ "bugs": { "url": "https://github.com/xataio/client-ts/issues" }, - "homepage": "https://xata.io/docs/sdk/getting-started" + "homepage": "https://xata.io/docs/sdk/getting-started", + "dependencies": { + "kysely": "^0.27.3", + "@xata.io/sql": "workspace:*", + "@xata.io/kysely": "workspace:*" + } } diff --git a/packages/client/src/api/client.ts b/packages/client/src/api/client.ts index 67eb2c989..079b73f31 100644 --- a/packages/client/src/api/client.ts +++ b/packages/client/src/api/client.ts @@ -1,5 +1,4 @@ import { defaultTrace, TraceFunction } from '../schema/tracing'; -import { getAPIKey } from '../util/environment'; import { FetchImpl, getFetchImplementation } from '../util/fetch'; import { RequiredKeys } from '../util/types'; import { generateUUID } from '../util/uuid'; @@ -40,7 +39,7 @@ const buildApiClient = () => class { constructor(options: XataApiClientOptions = {}) { const provider = options.host ?? 'production'; - const apiKey = options.apiKey ?? getAPIKey(); + const apiKey = options.apiKey; const trace = options.trace ?? defaultTrace; const clientID = generateUUID(); diff --git a/packages/client/src/api/fetcher.ts b/packages/client/src/api/fetcher.ts index 6971e5c02..94ca7e7b6 100644 --- a/packages/client/src/api/fetcher.ts +++ b/packages/client/src/api/fetcher.ts @@ -203,6 +203,8 @@ export async function fetch< 'X-Xata-Client-ID': clientID ?? defaultClientID, 'X-Xata-Session-ID': sessionID ?? generateUUID(), 'X-Xata-Agent': xataAgent, + // Force field rename to xata_ internal properties + 'X-Features': compact(['feat-internal-field-rename-api=1', customHeaders?.['X-Features']]).join(' '), ...customHeaders, ...hostHeader(fullUrl), Authorization: `Bearer ${apiKey}` diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index c9ff2c343..a4b723275 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -1,13 +1,10 @@ -import { ApiExtraProps, HostProvider, Schemas } from './api'; +import { ApiExtraProps, HostProvider } from './api'; import { FilesPlugin, FilesPluginResult } from './files'; import { XataPlugin, XataPluginOptions } from './plugins'; -import { BaseSchema, SchemaPlugin, SchemaPluginResult, XataRecord } from './schema'; -import { CacheImpl, SimpleCache } from './schema/cache'; -import { defaultTrace, TraceFunction } from './schema/tracing'; +import { DatabaseSchema, SchemaInference, SchemaPlugin, SchemaPluginResult } from './schema'; +import { TraceFunction, defaultTrace } from './schema/tracing'; import { SearchPlugin, SearchPluginResult } from './search'; import { SQLPlugin, SQLPluginResult } from './sql'; -import { TransactionPlugin, TransactionPluginResult } from './transaction'; -import { getAPIKey, getBranch, getDatabaseURL, getEnableBrowserVariable, getPreviewBranch } from './util/environment'; import { FetchImpl, getFetchImplementation } from './util/fetch'; import { AllRequired, StringKeys } from './util/types'; import { generateUUID } from './util/uuid'; @@ -18,7 +15,6 @@ export type BaseClientOptions = { apiKey?: string; databaseURL?: string; branch?: string; - cache?: CacheImpl; trace?: TraceFunction; enableBrowser?: boolean; clientName?: string; @@ -36,36 +32,32 @@ export const buildClient = = {}>(plu class { #options: SafeOptions; - schema: Schemas.Schema; + schema: DatabaseSchema; db: SchemaPluginResult; search: SearchPluginResult; - transactions: TransactionPluginResult; sql: SQLPluginResult; files: FilesPluginResult; - constructor(options: BaseClientOptions = {}, tables: Schemas.Table[]) { + constructor(options: BaseClientOptions = {}, schema: DatabaseSchema) { const safeOptions = this.#parseOptions(options); this.#options = safeOptions; const pluginOptions: XataPluginOptions = { ...this.#getFetchProps(safeOptions), - cache: safeOptions.cache, host: safeOptions.host, - tables, + schema, branch: safeOptions.branch }; const db = new SchemaPlugin().build(pluginOptions); const search = new SearchPlugin(db).build(pluginOptions); - const transactions = new TransactionPlugin().build(pluginOptions); const sql = new SQLPlugin().build(pluginOptions); const files = new FilesPlugin().build(pluginOptions); // We assign the namespaces after creating in case the user overrides the db plugin - this.schema = { tables }; + this.schema = schema; this.db = db; this.search = search; - this.transactions = transactions; this.sql = sql; this.files = files; @@ -86,7 +78,7 @@ export const buildClient = = {}>(plu #parseOptions(options?: BaseClientOptions): SafeOptions { // If is running from the browser and the user didn't pass `enableBrowser` we throw an error - const enableBrowser = options?.enableBrowser ?? getEnableBrowserVariable() ?? false; + const enableBrowser = options?.enableBrowser ?? false; // @ts-ignore Window, Deno are not globals const isBrowser = typeof window !== 'undefined' && typeof Deno === 'undefined'; if (isBrowser && !enableBrowser) { @@ -96,9 +88,9 @@ export const buildClient = = {}>(plu } const fetch = getFetchImplementation(options?.fetch); - const databaseURL = options?.databaseURL || getDatabaseURL(); - const apiKey = options?.apiKey || getAPIKey(); - const cache = options?.cache ?? new SimpleCache({ defaultQueryTTL: 0 }); + const databaseURL = options?.databaseURL; + const apiKey = options?.apiKey; + const branch = options?.branch; const trace = options?.trace ?? defaultTrace; const clientName = options?.clientName; const host = options?.host ?? 'production'; @@ -112,25 +104,8 @@ export const buildClient = = {}>(plu throw new Error('Option databaseURL is required'); } - const envBranch = getBranch(); - const previewBranch = getPreviewBranch(); - const branch = options?.branch || previewBranch || envBranch || 'main'; - if (!!previewBranch && branch !== previewBranch) { - console.warn( - `Ignoring preview branch ${previewBranch} because branch option was passed to the client constructor with value ${branch}` - ); - } else if (!!envBranch && branch !== envBranch) { - console.warn( - `Ignoring branch ${envBranch} because branch option was passed to the client constructor with value ${branch}` - ); - } else if (!!previewBranch && !!envBranch && previewBranch !== envBranch) { - console.warn( - `Ignoring preview branch ${previewBranch} and branch ${envBranch} because branch option was passed to the client constructor with value ${branch}` - ); - } else if (!previewBranch && !envBranch && options?.branch === undefined) { - console.warn( - `No branch was passed to the client constructor. Using default branch ${branch}. You can set the branch with the environment variable XATA_BRANCH or by passing the branch option to the client constructor.` - ); + if (!branch) { + throw new Error('Option branch is required'); } return { @@ -138,7 +113,6 @@ export const buildClient = = {}>(plu databaseURL, apiKey, branch, - cache, trace, host, clientID: generateUUID(), @@ -177,16 +151,13 @@ export const buildClient = = {}>(plu } as unknown as ClientConstructor; export interface ClientConstructor> { - new = {}>( - options?: Partial, - schemaTables?: readonly BaseSchema[] - ): Omit< + new (options: BaseClientOptions, schema: Schema): Omit< { - db: Awaited['build']>>; - search: Awaited['build']>>; - transactions: Awaited['build']>>; + db: Awaited['build']>>; + search: Awaited['build']>>; sql: Awaited>; - files: Awaited['build']>>; + files: Awaited>['build']>>; + schema: Schema; }, keyof Plugins > & { @@ -199,4 +170,4 @@ export interface ClientConstructor> { }; } -export class BaseClient extends buildClient()> {} +export class BaseClient extends buildClient() {} diff --git a/packages/client/src/files/index.ts b/packages/client/src/files/index.ts index ae956c162..2e9585eab 100644 --- a/packages/client/src/files/index.ts +++ b/packages/client/src/files/index.ts @@ -2,7 +2,7 @@ import { deleteFileItem, getFileItem, putFileItem } from '../api'; import { FileResponse } from '../api/dataPlaneSchemas'; import { XataPlugin, XataPluginOptions } from '../plugins'; import { ColumnsByValue, XataArrayFile, XataFile } from '../schema'; -import { BaseData, XataRecord } from '../schema/record'; +import { BaseData } from '../schema/record'; import { isBlob } from '../util/lang'; import { GetArrayInnerType, StringKeys, Values } from '../util/types'; @@ -48,7 +48,7 @@ export type DownloadDestination, Tables }; }>; -export class FilesPlugin> extends XataPlugin { +export class FilesPlugin> extends XataPlugin { build(pluginOptions: XataPluginOptions): FilesPluginResult { return { download: async (location: Record) => { diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 26dd96081..3a1b8af02 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -15,8 +15,7 @@ export * from './search'; export * from './sql'; export * from './serializer'; export * from './files'; -export * from './transaction'; export { transformImage } from './files/transformations'; export type { ImageTransformations } from './files/transformations'; -export { getAPIKey, getBranch, getDatabaseURL, getPreviewBranch, buildPreviewBranchName } from './util/environment'; +export { buildPreviewBranchName, getDeployPreviewBranch } from './util/environment'; export { Buffer } from './util/buffer'; diff --git a/packages/client/src/kysely/index.ts b/packages/client/src/kysely/index.ts new file mode 100644 index 000000000..2395d2554 --- /dev/null +++ b/packages/client/src/kysely/index.ts @@ -0,0 +1,17 @@ +import { Kysely } from 'kysely'; +import { XataPlugin, XataPluginOptions } from '../plugins'; +import { XataRecord } from '../schema'; +import { SQLPlugin } from '../sql'; +import { Model, XataDialect } from '@xata.io/kysely'; + +export type KyselyPluginResult> = Kysely>; + +export class KyselyPlugin> extends XataPlugin { + build(pluginOptions: XataPluginOptions): KyselyPluginResult { + const xata = { sql: new SQLPlugin().build(pluginOptions) }; + + return new Kysely>({ + dialect: new XataDialect({ xata }) + }); + } +} diff --git a/packages/client/src/plugins.ts b/packages/client/src/plugins.ts index 72a3f8cdc..183261755 100644 --- a/packages/client/src/plugins.ts +++ b/packages/client/src/plugins.ts @@ -1,13 +1,12 @@ -import { ApiExtraProps, HostProvider, Schemas } from './api'; -import { CacheImpl } from './schema/cache'; +import { ApiExtraProps, HostProvider } from './api'; +import { DatabaseSchema } from './schema'; export abstract class XataPlugin { abstract build(options: XataPluginOptions): unknown; } export type XataPluginOptions = ApiExtraProps & { - cache: CacheImpl; host: HostProvider; - tables: Schemas.Table[]; + schema: DatabaseSchema; branch: string; }; diff --git a/packages/client/src/schema/aggregate.ts b/packages/client/src/schema/aggregate.ts index 6ac7cf294..fb1dcaff1 100644 --- a/packages/client/src/schema/aggregate.ts +++ b/packages/client/src/schema/aggregate.ts @@ -1,38 +1,34 @@ import { Dictionary, ExactlyOne } from '../util/types'; import { Filter } from './filters'; -import { XataRecord } from './record'; import { ColumnsByValue } from './selection'; /** * The description of a single aggregation operation. The key represents the */ -export type AggregationExpression = ExactlyOne<{ - count: CountAggregation; - sum: SumAggregation; - max: MaxAggregation; - min: MinAggregation; - average: AverageAggregation; - percentiles: PercentilesAggregation; - uniqueCount: UniqueCountAggregation; - dateHistogram: DateHistogramAggregation; - topValues: TopValuesAggregation; - numericHistogram: NumericHistogramAggregation; +export type AggregationExpression = ExactlyOne<{ + count: CountAggregation; + sum: SumAggregation; + max: MaxAggregation; + min: MinAggregation; + average: AverageAggregation; + percentiles: PercentilesAggregation; + uniqueCount: UniqueCountAggregation; + dateHistogram: DateHistogramAggregation; + topValues: TopValuesAggregation; + numericHistogram: NumericHistogramAggregation; }>; -export type AggregationResult< - Record extends XataRecord, - Expression extends Dictionary> -> = { +export type AggregationResult>> = { aggs: { - [K in keyof Expression]: AggregationResultItem; + [K in keyof Expression]: AggregationResultItem; }; }; type AggregationExpressionType> = keyof T; type AggregationResultItem< - Record extends XataRecord, - Expression extends AggregationExpression + ObjectType, + Expression extends AggregationExpression > = AggregationExpressionType extends infer Type ? Type extends keyof AggregationExpressionResultTypes ? AggregationExpressionResultTypes[Type] @@ -42,71 +38,71 @@ type AggregationResultItem< /** * Count the number of records with an optional filter. */ -export type CountAggregation = +export type CountAggregation = | { - filter?: Filter; + filter?: Filter; } | '*'; /** * The sum of the numeric values in a particular column. */ -export type SumAggregation = { +export type SumAggregation = { /** * The column on which to compute the sum. Must be a numeric type. */ - column: ColumnsByValue; + column: ColumnsByValue; }; /** * The max of the numeric values in a particular column. */ -export type MaxAggregation = { +export type MaxAggregation = { /** * The column on which to compute the max. Must be a numeric type. */ - column: ColumnsByValue; + column: ColumnsByValue; }; /** * The min of the numeric values in a particular column. */ -export type MinAggregation = { +export type MinAggregation = { /** * The column on which to compute the min. Must be a numeric type. */ - column: ColumnsByValue; + column: ColumnsByValue; }; /** * The average of the numeric values in a particular column. */ -export type AverageAggregation = { +export type AverageAggregation = { /** * The column on which to compute the average. Must be a numeric type. */ - column: ColumnsByValue; + column: ColumnsByValue; }; /** * Calculate given percentiles of the numeric values in a particular column. */ -export type PercentilesAggregation = { +export type PercentilesAggregation = { /** * The column on which to compute the average. Must be a numeric type. */ - column: ColumnsByValue; + column: ColumnsByValue; percentiles: number[]; }; /** * Count the number of distinct values in a particular column. */ -export type UniqueCountAggregation = { +export type UniqueCountAggregation = { /** * The column from where to count the unique values. */ - column: ColumnsByValue; + column: ColumnsByValue; /** * The threshold under which the unique count is exact. If the number of unique * values in the column is higher than this threshold, the results are approximative. @@ -118,11 +114,11 @@ export type UniqueCountAggregation = { /** * Split data into buckets by a datetime column. Accepts sub-aggregations for each bucket. */ -export type DateHistogramAggregation = { +export type DateHistogramAggregation = { /** * The column to use for bucketing. Must be of type datetime. */ - column: ColumnsByValue; + column: ColumnsByValue; /** * The fixed interval to use when bucketing. * It is fromatted as number + units, for example: `5d`, `20m`, `10s`. @@ -143,19 +139,19 @@ export type DateHistogramAggregation = { * @pattern ^[+-][01]\d:[0-5]\d$ */ timezone?: string; - aggs?: Dictionary>; + aggs?: Dictionary>; }; /** * Split data into buckets by the unique values in a column. Accepts sub-aggregations for each bucket. * The top values as ordered by the number of records (`$count``) are returned. */ -export type TopValuesAggregation = { +export type TopValuesAggregation = { /** * The column to use for bucketing. Accepted types are `string`, `email`, `int`, `float`, or `bool`. */ - column: ColumnsByValue; - aggs?: Dictionary>; + column: ColumnsByValue; + aggs?: Dictionary>; /** * The maximum number of unique values to return. * @@ -168,11 +164,11 @@ export type TopValuesAggregation = { /** * Split data into buckets by dynamic numeric ranges. Accepts sub-aggregations for each bucket. */ -export type NumericHistogramAggregation = { +export type NumericHistogramAggregation = { /** * The column to use for bucketing. Must be of numeric type. */ - column: ColumnsByValue; + column: ColumnsByValue; /** * The numeric interval to use for bucketing. The resulting buckets will be ranges * with this value as size. @@ -189,7 +185,7 @@ export type NumericHistogramAggregation = { * @default 0 */ offset?: number; - aggs?: Dictionary>; + aggs?: Dictionary>; }; type AggregationExpressionResultTypes = { diff --git a/packages/client/src/schema/ask.ts b/packages/client/src/schema/ask.ts index d8600e711..e0f2b3450 100644 --- a/packages/client/src/schema/ask.ts +++ b/packages/client/src/schema/ask.ts @@ -2,9 +2,8 @@ import { FuzzinessExpression, PrefixExpression } from '../api/dataPlaneSchemas'; import { Boosters } from '../search/boosters'; import { TargetColumn } from '../search/target'; import { Filter } from './filters'; -import { XataRecord } from './record'; -export type KeywordAskOptions = { +export type KeywordAskOptions = { searchType?: 'keyword'; search?: { fuzziness?: FuzzinessExpression; @@ -15,7 +14,7 @@ export type KeywordAskOptions = { }; }; -export type VectorAskOptions = { +export type VectorAskOptions = { searchType?: 'vector'; vectorSearch?: { /** @@ -30,13 +29,13 @@ export type VectorAskOptions = { }; }; -type TypeAskOptions = KeywordAskOptions | VectorAskOptions; +type TypeAskOptions = KeywordAskOptions | VectorAskOptions; type BaseAskOptions = { rules?: string[]; sessionId?: string; }; -export type AskOptions = TypeAskOptions & BaseAskOptions; +export type AskOptions = TypeAskOptions & BaseAskOptions; export type AskResult = { answer?: string; records?: string[]; sessionId?: string }; diff --git a/packages/client/src/schema/cache.test.ts b/packages/client/src/schema/cache.test.ts deleted file mode 100644 index 62b1f93c1..000000000 --- a/packages/client/src/schema/cache.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { describe, expect, test } from 'vitest'; -import { SimpleCache } from './cache'; - -const cache = new SimpleCache({ max: 5 }); - -describe('simple cache', () => { - test('no cache', async () => { - const noCache = new SimpleCache({ max: 0 }); - - await noCache.set('foo', 'bar'); - expect(await noCache.get('foo')).toBe(null); - }); - - test('useless cache', async () => { - const uselessCache = new SimpleCache({ max: 1 }); - - await uselessCache.set('foo', 'bar'); - expect(await uselessCache.get('foo')).toBe('bar'); - }); - - test('cache', async () => { - await cache.set('foo', 'bar'); - expect(await cache.get('foo')).toBe('bar'); - }); - - test('cache with delete', async () => { - await cache.set('foo', 'bar'); - await cache.delete('foo'); - expect(await cache.get('foo')).toBe(null); - }); - - test('cache with clear', async () => { - await cache.set('foo', 'bar'); - await cache.clear(); - expect(await cache.get('foo')).toBe(null); - }); - - test('cache with getAll', async () => { - await cache.set('foo', 'bar'); - await cache.set('bar', 'foo'); - expect(await cache.getAll()).toEqual({ foo: 'bar', bar: 'foo' }); - }); - - test('cache with getAll and delete', async () => { - await cache.set('foo', 'bar'); - await cache.set('bar', 'foo'); - await cache.delete('foo'); - expect(await cache.getAll()).toEqual({ bar: 'foo' }); - }); - - test('cache with getAll and clear', async () => { - await cache.set('foo', 'bar'); - await cache.set('bar', 'foo'); - await cache.clear(); - expect(await cache.getAll()).toEqual({}); - }); - - test('cache with max size', async () => { - await cache.set('foo', 'bar'); - await cache.set('bar', 'foo'); - await cache.set('baz', 'foo'); - await cache.set('qux', 'foo'); - await cache.set('quux', 'foo'); - await cache.set('corge', 'foo'); - await cache.set('grault', 'foo'); - expect(await cache.getAll()).toMatchInlineSnapshot(` - { - "baz": "foo", - "corge": "foo", - "grault": "foo", - "quux": "foo", - "qux": "foo", - } - `); - }); - - test('cache with max size, least recently used', async () => { - await cache.set('foo', 'bar'); - await cache.set('bar', 'foo'); - await cache.set('baz', 'foo'); - await cache.set('qux', 'foo'); - await cache.set('foo', 'bar'); - await cache.set('quux', 'foo'); - await cache.set('corge', 'foo'); - await cache.set('grault', 'foo'); - expect(await cache.getAll()).toMatchInlineSnapshot(` - { - "corge": "foo", - "foo": "bar", - "grault": "foo", - "quux": "foo", - "qux": "foo", - } - `); - }); -}); diff --git a/packages/client/src/schema/cache.ts b/packages/client/src/schema/cache.ts deleted file mode 100644 index 9dd098928..000000000 --- a/packages/client/src/schema/cache.ts +++ /dev/null @@ -1,53 +0,0 @@ -export interface CacheImpl { - defaultQueryTTL: number; - - getAll(): Promise>; - get: (key: string) => Promise; - set: (key: string, value: T) => Promise; - delete: (key: string) => Promise; - clear: () => Promise; -} - -export interface SimpleCacheOptions { - max?: number; - defaultQueryTTL?: number; -} - -export class SimpleCache implements CacheImpl { - #map: Map; - - capacity: number; - defaultQueryTTL: number; - - constructor(options: SimpleCacheOptions = {}) { - this.#map = new Map(); - this.capacity = options.max ?? 500; - this.defaultQueryTTL = options.defaultQueryTTL ?? 60 * 1000; - } - - async getAll(): Promise> { - return Object.fromEntries(this.#map); - } - - async get(key: string): Promise { - return (this.#map.get(key) ?? null) as T | null; - } - - async set(key: string, value: T): Promise { - await this.delete(key); - this.#map.set(key, value); - - if (this.#map.size > this.capacity) { - const leastRecentlyUsed = this.#map.keys().next().value; - await this.delete(leastRecentlyUsed); - } - } - - async delete(key: string): Promise { - this.#map.delete(key); - } - - async clear(): Promise { - return this.#map.clear(); - } -} diff --git a/packages/client/src/schema/filters.test.ts b/packages/client/src/schema/filters.test.ts index 79725497c..c8273b372 100644 --- a/packages/client/src/schema/filters.test.ts +++ b/packages/client/src/schema/filters.test.ts @@ -5,6 +5,10 @@ import { XataRecord } from './record'; import { FilterExpression } from '../api/schemas'; type Record = XataRecord & { + xata_id: string; + xata_version: number; + xata_createdat: Date; + xata_updatedat: Date; name: string; string: string; number: number; @@ -230,7 +234,7 @@ const filterWithWildcardIsNotAllowed: Filter = { '*': { $is: 'foo' } }; const filterWithLinkWildcardIsNotAllowed: Filter = { 'owner.*': { $is: 'foo' } }; // Filter on internal column is allowed -const filterOnInternalColumnIsAllowed: Filter = { 'xata.version': { $is: 4 } }; +const filterOnInternalColumnIsAllowed: Filter = { xata_version: { $is: 4 } }; test('fake test', () => { // This is a fake test to make sure that the type definitions in this file are working diff --git a/packages/client/src/schema/filters.ts b/packages/client/src/schema/filters.ts index e5c65032a..d39140fba 100644 --- a/packages/client/src/schema/filters.ts +++ b/packages/client/src/schema/filters.ts @@ -1,9 +1,11 @@ import { FilterExpression, FilterPredicate } from '../api/schemas'; -import { isDefined, isObject } from '../util/lang'; +import { isDefined, isNumber, isObject, isString } from '../util/lang'; import { SingleOrArray, Values } from '../util/types'; import { JSONValue } from './json'; -import { XataRecordMetadata } from './record'; import { ColumnsByValue, ValueAtColumn } from './selection'; +import { ExpressionBuilder, ExpressionOrFactory, ReferenceExpression, sql } from 'kysely'; +import { ExpressionFactory } from 'kysely/dist/cjs/parser/expression-parser'; +import { Schemas } from '../api'; export type JSONFilterColumns = Values<{ [K in keyof Record]: NonNullable extends JSONValue @@ -13,7 +15,7 @@ export type JSONFilterColumns = Values<{ : never; }>; -export type FilterColumns = ColumnsByValue | `xata.${keyof XataRecordMetadata}`; +export type FilterColumns = ColumnsByValue; export type FilterValueAtColumn = NonNullable> extends JSONValue ? PropertyFilter @@ -160,3 +162,291 @@ export function cleanFilter(filter?: FilterExpression | FilterPredicate): any { return Object.keys(values).length > 0 ? values : undefined; } + +const isJsonColumnFilter = (key?: string): boolean => (key && key?.includes('->') ? true : false); + +const parseJsonPath = (path: string) => { + const token = 'texttoremove'; + const pathToUse = path.replace(/->/g, token).split(token); + return { + firstCol: pathToUse.shift(), + pathToUse: pathToUse.map((i) => `'${i}'`).join(',') + }; +}; + +const buildStatement = ({ + column, + value, + eb, + operator, + castToText = true +}: { + column: ReferenceExpression; + value: string | number | Date; + eb: ExpressionBuilder; + operator?: FilterPredicate; + castToText?: boolean; +}) => { + switch (operator) { + case '$contains': { + return sql`(position(${value} IN ${column}::text)>0)`; + } + case '$iContains': { + return sql`(position(lower(${value}) IN lower(${column}::text))>0)`; + } + case '$includes': { + return sql`(true = ANY(SELECT "tmp"=${value} FROM unnest(${column}) as tmp))`; + } + case '$includesNone': { + return sql`(false = ALL(SELECT "tmp"=${value} FROM unnest(${column}) as tmp))`; + } + case '$includesAll': { + return sql`(true = ANY(SELECT "tmp"=${value} FROM unnest(${column}) as tmp))`; + } + case '$includesAny': { + return sql`(true = ANY(SELECT "tmp"=${value} FROM unnest(${column}) as tmp))`; + } + case '$startsWith': { + return sql`(starts_with(${column}::text, ${value}))`; + } + case '$endsWith': { + return sql`(${column}::text LIKE ${eb.val('%' + buildPattern(value, false))})`; + } + case '$lt': { + return sql`${column} < ${value}`; + } + case '$lte': { + return sql`${column} <= ${value}`; + } + case '$gt': { + return sql`${column} > ${value}`; + } + case '$gte': { + return sql`${column} >= ${value}`; + } + case '$is': { + return sql`${column} = ${value}`; + } + case '$isNot': { + return sql`${column} != ${value}`; + } + case '$pattern': { + return sql`${column} LIKE ${buildPattern(value, true)}`; + } + case '$iPattern': { + return sql`${column} ILIKE ${buildPattern(value, true)}`; + } + case '$exists': { + return sql`(${eb.ref(value as string)} IS NOT NULL)`; + } + case '$notExists': { + return sql`(${eb.ref(value as string)} IS NULL)`; + } + default: { + if (castToText) { + return sql`CAST (${column} AS text) = ${value}`; + } + return eb(column, '=', value); + } + } +}; + +export const filterToKysely = ({ + value, + columnName, + operation, + path = [] +}: { + value: Filter; + columnName?: string; + operation?: FilterPredicate; + path: string[]; +}): + | (( + eb: ExpressionBuilder, + columnData: Schemas.Column[], + tableName?: string + ) => ExpressionOrFactory) + | ExpressionFactory => { + return (eb, columnData, tableName) => { + if (isString(value) || typeof value === 'number' || value instanceof Date) { + const operator = operation ?? '='; + const column = eb.ref(columnName ?? 'unknown'); + + const isJsonColumnType = columnData.find((c) => c.name === columnName)?.type === 'json'; + const castToText = isJsonColumnType || value instanceof Date || isNumber(value) ? false : true; + if (isJsonColumnFilter(columnName)) { + const { firstCol, pathToUse } = parseJsonPath(columnName ?? ''); + return buildStatement({ + operator, + column: sql.raw(`jsonb_extract_path_text(${firstCol}::jsonb, ${pathToUse})::text`), + value, + eb, + castToText + }); + } + + const buildS = buildStatement({ + operator, + column, + value, + eb, + castToText + }); + return buildS; + } + + if (Array.isArray(value)) { + return eb.and( + value.map((f) => { + return filterToKysely({ value: f, columnName, operation, path: columnName ? [...path, columnName] : path })( + eb, + columnData, + tableName + ); + }) + ); + } + if (isObject(value)) { + const entries = Object.entries(value); + + const handleEntries = entries.map(([key, value]) => { + const valueToUse = Array.isArray(value) ? value : [value]; + + switch (key) { + case '$all': { + return eb.and( + valueToUse.map((v) => { + return filterToKysely({ + value: v, + columnName: key.startsWith('$') ? columnName : key, + operation: key.startsWith('$') ? key : operation, + path: columnName ? [...path, columnName] : path + })(eb, columnData, tableName); + }) + ); + } + case '$any': { + return eb.or( + valueToUse.map((v) => { + return filterToKysely({ + value: v, + columnName: key.startsWith('$') ? columnName : key, + operation: key.startsWith('$') ? key : operation, + path: columnName ? [...path, columnName] : path + })(eb, columnData, tableName); + }) + ); + } + case '$includesNone': { + return eb.and( + valueToUse.map((v) => + eb.not( + filterToKysely({ + value: v, + columnName: key.startsWith('$') ? columnName : key, + operation: '$includes', + path: columnName ? [...path, columnName] : path + })(eb, columnData, tableName) + ) + ) + ); + } + case '$none': + case '$not': { + return eb.and( + valueToUse.map((v) => + eb.not( + filterToKysely({ + value: v, + columnName: key.startsWith('$') ? columnName : key, + operation: '$not', + path: columnName ? [...path, columnName] : path + })(eb, columnData, tableName) + ) + ) + ); + } + + default: { + return eb.and( + valueToUse.map((v) => + filterToKysely({ + value: v, + columnName: key.startsWith('$') ? columnName : key, + operation: key.startsWith('$') ? key : operation, + path: key ? [...path, key] : path + })(eb, columnData, tableName) + ) + ); + } + } + }); + if (operation === '$any') { + return eb.or(handleEntries); + } + return eb.and(handleEntries); + } + throw new Error('Not implemented'); + }; +}; + +const buildPattern = (value: string | number | Date, translatePattern: boolean) => { + // if there are special chars like %,_,*, ?, \, in the value, we should escape them with single slash + if (translatePattern) { + return `${String(value) + .replace(/\\/g, '\\\\') + .replace(/%/g, '\\%') + .replace(/_/g, '\\_') + .replace('*', '%') + .replace('?', '_')}`; + } + return `${String(value).replace('*', '%').replace('?', '_')}`; +}; + +/** + * + * Separates the filter into relevant filters for regular fields and nested linked columns. + * Removes nested filters for link fields to avoid duplicate conditions. + * @param filter original filter + * @param firstLevelOnly boolean to only return filters for non link/foreign key fields + * @param linkFields a list of the tables linked fields to use for determining if a field is a link + * @returns null or object + */ + +export const relevantFilters = (filter: any, topLevelOnly: boolean, originalKey: string, visited: Set) => { + const copy = {}; + const traverse = (filter: any, path: string[]) => { + for (const key in filter) { + if (isObject(filter[key])) { + if (topLevelOnly && !key.startsWith('$') && path?.length > 0 && !path[path.length - 1]?.startsWith('$')) { + continue; + } + traverse(filter[key], [...path, key]); + } else if (!topLevelOnly && path.includes(originalKey)) { + path.push(key); + const stringifiedPaths = path.join('.'); + if (visited.has(stringifiedPaths)) { + continue; + } + visited.add(stringifiedPaths); + atPath(copy, path)[key] = filter[key]; + } else if (topLevelOnly && !path.includes(originalKey)) { + atPath(copy, path)[key] = filter[key]; + } + } + }; + + traverse(filter, []); + + return Object.keys(copy).length > 0 ? copy : null; +}; + +export const atPath = (obj: object, atPath: string[]) => { + return atPath.reduce((acc, key) => { + if (!acc[key]) { + acc[key] = {}; + } + return acc[key]; + }, obj as { [k: string]: any }); +}; diff --git a/packages/client/src/schema/identifiable.spec.ts b/packages/client/src/schema/identifiable.spec.ts new file mode 100644 index 000000000..b3c52714c --- /dev/null +++ b/packages/client/src/schema/identifiable.spec.ts @@ -0,0 +1,128 @@ +import { test } from 'vitest'; +import { NewIdentifiable, NewIdentifierKey, NewIndentifierValue } from './identifiable'; + +const tables = [ + { + name: 'teams', + primaryKey: ['xata_id'], + foreignKeys: { + pet_pet: { + name: 'pet_pet', + columns: ['pet'], + referencedTable: 'pets', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + } + }, + columns: [ + { name: 'xata_id', type: 'boolean', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'email', type: 'email', unique: true }, + { name: 'pet', type: 'link', link: { table: 'pets' } }, + { name: 'account_value', type: 'int' }, + { name: 'vector', type: 'vector', vector: { dimension: 4 } } + ] + }, + { + name: 'users', + primaryKey: ['userdefined'], + columns: [ + { name: 'userdefined', type: 'int', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'email', type: 'email', unique: true } + ] + }, + { + name: 'pets', + primaryKey: [], + columns: [ + { name: 'xata_id', type: 'text', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'name', type: 'string', notNull: true, unique: true } + ] + }, + { + name: 'datetime', + primaryKey: ['xata_id'], + columns: [ + { name: 'xata_id', type: 'datetime', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + }, + { + name: 'multiple', + primaryKey: ['xata_id'], + columns: [ + { name: 'xata_id', type: 'multiple', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + }, + { + name: 'vector', + primaryKey: ['xata_id'], + columns: [ + { name: 'xata_id', type: 'vector', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + }, + { + name: 'boolean[]', + primaryKey: ['xata_id'], + columns: [ + { name: 'xata_id', type: 'boolean[]', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + }, + { + name: 'jsonb', + primaryKey: ['xata_id'], + columns: [ + { name: 'xata_id', type: 'json', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + }, + { + name: 'unknown', + primaryKey: [], + columns: [ + { name: 'xata_id', type: 'text', notNull: true, unique: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + }, + { + name: 'neither', + primaryKey: [], + columns: [ + { name: 'xata_id', type: 'text' }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + } +] as const; + +type DbIndentifiable = NewIdentifiable['users']; +type DbIndentifiableKey = NewIdentifierKey; +type DbIndentifiableValue = NewIndentifierValue; + +test('fake test', () => { + // This is a fake test to make sure that the type definitions in this file are working +}); diff --git a/packages/client/src/schema/identifiable.ts b/packages/client/src/schema/identifiable.ts new file mode 100644 index 000000000..e0338c313 --- /dev/null +++ b/packages/client/src/schema/identifiable.ts @@ -0,0 +1,122 @@ +import { Values } from '../util/types'; +import { XataFile } from './files'; +import { TableSchema } from './inference'; +import { InputXataFile, NumericOperator } from './record'; + +/** + * Returns an object with a key and type of the column specified in the schema primary key array. + * + * If there is more than one column in the primary key array, it will return the name and type of the first column in the array. + * If empty, it will check for xata_id column and return that key and type provided it is not null. + * + * If neither found, never will be returned. + */ +export type NewIdentifiable = T extends never[] + ? never + : T extends readonly unknown[] + ? T[number] extends { name: string; columns: readonly unknown[] } + ? { + [K in T[number]['name']]: PrimaryKeyType; + } + : never + : never; + +type PrimaryKeyType = Tables & { name: TableName } extends infer Table + ? Table extends { name: string; columns: infer Columns } & { primaryKey: infer primaryKey } + ? Columns extends readonly unknown[] + ? Columns[number] extends { name: string; type: string } + ? primaryKey extends readonly string[] + ? Values<{ + [K in Columns[number]['name']]: K extends primaryKey[0] + ? PropertyType + : K extends 'xata_id' + ? PropertyType + : never; + }> + : never + : never + : never + : never + : never; + +type PropertyType = Properties & { + name: PropertyName; +} extends infer Property + ? Property extends { + name: string; + type: infer Type; + link?: { table: infer LinkedTable }; + notNull?: infer NotNull; + } + ? NotNull extends true + ? { + [K in PropertyName]: InnerType; + } + : never + : never + : never; + +type InnerType = Type extends + | 'string' + | 'text' + | 'email' + | 'character' + | 'varchar' + | 'character varying' + | `varchar(${number})` + | `character(${number})` + ? string + : Type extends + | 'int' + | 'float' + | 'bigint' + | 'int8' + | 'integer' + | 'int4' + | 'smallint' + | 'double precision' + | 'float8' + | 'real' + | 'numeric' + ? number + : never; + +export type NewIndentifierValue = { + [K in keyof T]: T[K] extends never ? never : T[K]; +}[keyof T]; + +export type NewIdentifierKey = { + [K in keyof T]: T[K] extends never ? never : K; +}[keyof T]; + +type NewEditableDataFields = T extends Date + ? string | Date + : NonNullable extends Date + ? string | Date | null | undefined + : T extends XataFile + ? InputXataFile + : T extends XataFile[] + ? InputXataFile[] + : T extends number + ? number | NumericOperator + : T; + +export type NewEditableData = Partial<{ + [K in keyof O]: NewEditableDataFields; +}>; + +type NewEditableDataFieldsWithoutNumeric = T extends Date + ? string | Date + : NonNullable extends Date + ? string | Date | null | undefined + : T extends XataFile + ? InputXataFile + : T extends XataFile[] + ? InputXataFile[] + : T extends number + ? number + : T; + +export type NewEditableDataWithoutNumeric = Partial<{ + [K in keyof O]: NewEditableDataFieldsWithoutNumeric; +}>; diff --git a/packages/client/src/schema/index.test.ts b/packages/client/src/schema/index.test.ts index f62a8c123..1e060accf 100644 --- a/packages/client/src/schema/index.test.ts +++ b/packages/client/src/schema/index.test.ts @@ -4,7 +4,7 @@ import { server } from '../../../../test/mock_server'; import { Response } from '../util/fetch'; interface User { - id: string; + xata_id: string; name: string; } @@ -19,15 +19,22 @@ const buildClient = (options: Partial = {}) => { // @ts-expect-error - Fetch doesn't appear in globalThis yet const fetch = vi.fn(globalThis.realFetch); - const client = new BaseClient({ fetch, apiKey, databaseURL, branch, clientName, xataAgentExtra }, [ + const client = new BaseClient( + { fetch, apiKey, databaseURL, branch, clientName, xataAgentExtra }, { - name: 'users', - columns: [ - { name: 'name', type: 'string' }, - { name: 'email', type: 'string' } + tables: [ + { + name: 'users', + primaryKey: [], + columns: [ + { name: 'xata_id', type: 'string', notNull: true }, + { name: 'name', type: 'string' }, + { name: 'email', type: 'string' } + ] + } ] } - ]); + ); const users = client.db.users; @@ -59,13 +66,15 @@ describe('client options', () => { test('provide branch as a string', async () => { const { fetch, users } = buildClient({ branch: 'branch' }); - fetch.mockImplementationOnce(async () => { + fetch.mockImplementation(async () => { return { ok: true, - json: async () => ({ - records: [], - meta: { page: { cursor: '', more: false } } - }) + json: async () => { + return { + records: [], + meta: { page: { cursor: '', more: false } } + }; + } } as Response; }); @@ -79,9 +88,9 @@ describe('client options', () => { expect(result).toMatchInlineSnapshot(` { - "body": "{"page":{"size":1},"columns":["*"]}", + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1","params":["1"]}", "method": "POST", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:branch/tables/users/query", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:branch/sql", } `); }); @@ -91,7 +100,7 @@ describe('request', () => { test('builds the right arguments for a GET request', async () => { const { fetch, users } = buildClient(); - fetch.mockImplementationOnce(async () => { + fetch.mockImplementation(async () => { return { ok: true, json: async () => ({ @@ -111,9 +120,9 @@ describe('request', () => { expect(result).toMatchInlineSnapshot(` { - "body": "{"page":{"size":1},"columns":["*"]}", + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1","params":["1"]}", "method": "POST", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/query", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", } `); }); @@ -121,7 +130,7 @@ describe('request', () => { test('builds the right arguments for a POST request', async () => { const { fetch, users } = buildClient(); - fetch.mockImplementationOnce(async () => { + fetch.mockImplementation(async () => { return { ok: true, json: async () => ({ @@ -141,9 +150,9 @@ describe('request', () => { expect(result).toMatchInlineSnapshot(` { - "body": "{"page":{"size":20},"columns":["*"]}", + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1","params":["20"]}", "method": "POST", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/query", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", } `); }); @@ -159,14 +168,14 @@ describe('request', () => { } as Response; }); - await expect(users.getFirst()).rejects.toMatchInlineSnapshot('[Error: Not Found]'); + await expect(users.getFirst()).rejects.toMatchInlineSnapshot(`[Error: Not Found]`); }); test('returns the json body if the response is ok', async () => { const { fetch, users } = buildClient(); const json = { a: 1 }; - fetch.mockImplementationOnce(async () => { + fetch.mockImplementation(async () => { return { ok: true, json: async () => ({ @@ -185,7 +194,7 @@ describe('request', () => { test('sets X-Xata-Agent header', async () => { const { fetch, users } = buildClient(); - fetch.mockImplementationOnce(async () => { + fetch.mockImplementation(async () => { return { ok: true, json: async () => ({ @@ -205,7 +214,7 @@ describe('request', () => { test('sets X-Xata-Agent header with service', async () => { const { fetch, users } = buildClient({ clientName: 'myService' }); - fetch.mockImplementationOnce(async () => { + fetch.mockImplementation(async () => { return { ok: true, json: async () => ({ @@ -227,7 +236,7 @@ describe('request', () => { test('sets X-Xata-Agent header with extras', async () => { const { fetch, users } = buildClient({ clientName: 'myService', xataAgentExtra: { hello: 'world' } }); - fetch.mockImplementationOnce(async () => { + fetch.mockImplementation(async () => { return { ok: true, json: async () => ({ @@ -258,7 +267,7 @@ async function expectRequest( callback: () => void, response?: any ): Promise { - fetch.mockImplementationOnce(() => { + fetch.mockImplementation(() => { return { ok: true, json: async () => response @@ -289,9 +298,14 @@ describe('query', () => { expect(result).toMatchInlineSnapshot(` [ { - "body": "{"page":{"size":20},"columns":["*"]}", + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1","params":["20"]}", + "method": "POST", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", + }, + { + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1 offset $2","params":["1","0"]}", "method": "POST", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/query", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); @@ -309,9 +323,14 @@ describe('query', () => { expect(result).toMatchInlineSnapshot(` [ { - "body": "{"filter":{"$all":[{"name":"foo"}]},"page":{"size":20},"columns":["*"]}", + "body": "{"statement":"select * from \\"users\\" where CAST (\\"name\\" AS text) = $1 order by \\"xata_id\\" asc limit $2","params":["foo","20"]}", + "method": "POST", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", + }, + { + "body": "{"statement":"select * from \\"users\\" where CAST (\\"name\\" AS text) = $1 order by \\"xata_id\\" asc limit $2 offset $3","params":["foo","1","0"]}", "method": "POST", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/query", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); @@ -322,14 +341,14 @@ describe('query', () => { test('returns a single object', async () => { const { fetch, users } = buildClient(); - const resultBody = { records: [{ id: '1234' }], meta: { page: { cursor: '', more: false } } }; + const resultBody = { records: [{ xata_id: '1234' }], meta: { page: { cursor: '', more: false } } }; const expected = { method: 'POST', path: '/tables/users/query', body: { page: { size: 1 } } }; const result = await expectRequest( fetch, expected, async () => { const first = await users.getFirst(); - expect(first?.id).toBe(resultBody.records[0].id); + expect(first?.xata_id).toBe(resultBody.records[0].xata_id); }, resultBody ); @@ -337,9 +356,14 @@ describe('query', () => { expect(result).toMatchInlineSnapshot(` [ { - "body": "{"page":{"size":1},"columns":["*"]}", + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1","params":["1"]}", + "method": "POST", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", + }, + { + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1 offset $2","params":["1","1"]}", "method": "POST", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/query", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); @@ -362,9 +386,14 @@ describe('query', () => { expect(result).toMatchInlineSnapshot(` [ { - "body": "{"page":{"size":1},"columns":["*"]}", + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1","params":["1"]}", + "method": "POST", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", + }, + { + "body": "{"statement":"select * from \\"users\\" order by \\"xata_id\\" asc limit $1 offset $2","params":["1","0"]}", "method": "POST", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/query", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); @@ -377,15 +406,17 @@ describe('read', () => { const { fetch, users } = buildClient(); const id = 'rec_1234'; - const expected = { method: 'GET', path: `/tables/users/data/${id}`, body: undefined }; - const result = await expectRequest(fetch, expected, () => users.read(id)); + const result = await expectRequest(fetch, [], () => users.read(id), { + records: [{ xata_id: id }], + meta: { page: { cursor: '', more: false } } + }); expect(result).toMatchInlineSnapshot(` [ { - "body": undefined, - "method": "GET", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/data/rec_1234?columns=*", + "body": "{"statement":"select * from \\"users\\" where \\"xata_id\\" = $1","params":["rec_1234"]}", + "method": "POST", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); @@ -395,15 +426,17 @@ describe('read', () => { const { fetch, users } = buildClient(); const id = 'rec_1234'; - const expected = { method: 'GET', path: `/tables/users/data/${id}`, body: undefined }; - const result = await expectRequest(fetch, expected, () => users.read(id, ['name', 'age'])); + const result = await expectRequest(fetch, [], () => users.read(id, ['name', 'age']), { + records: [{ xata_id: id }], + meta: { page: { cursor: '', more: false } } + }); expect(result).toMatchInlineSnapshot(` [ { - "body": undefined, - "method": "GET", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/data/rec_1234?columns=name%2Cage", + "body": "{"statement":"select \\"xata_id\\", \\"name\\", \\"age\\" from \\"users\\" where \\"xata_id\\" = $1","params":["rec_1234"]}", + "method": "POST", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); @@ -414,27 +447,26 @@ describe('Repository.update', () => { test('updates an object successfully', async () => { const { fetch, users } = buildClient(); - const object = { id: 'rec_1234', xata: { version: 1 }, name: 'Ada' }; - const expected = [ - { method: 'PUT', path: `/tables/users/data/${object.id}`, body: object }, - { method: 'GET', path: `/tables/users/data/${object.id}` } - ]; + const object = { xata_id: 'rec_1234', xata_version: 1, name: 'Ada' }; const result = await expectRequest( fetch, - expected, + [], async () => { - const result = await users.update(object.id, object); - expect(result?.id).toBe(object.id); + const result = await users.update(object.xata_id, object); + expect(result?.xata_id).toBe(object.xata_id); }, - { id: object.id } + { + records: [{ xata_id: object.xata_id }], + meta: { page: { cursor: '', more: false } } + } ); expect(result).toMatchInlineSnapshot(` [ { - "body": "{"name":"Ada"}", - "method": "PATCH", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/data/rec_1234?columns=*", + "body": "{"statement":"update \\"users\\" set \\"name\\" = $1 where \\"xata_id\\" = $2 returning *","params":["Ada","rec_1234"]}", + "method": "POST", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); @@ -446,17 +478,24 @@ describe('Repository.delete', () => { const { fetch, users } = buildClient(); const id = 'rec_1234'; - const expected = { method: 'DELETE', path: `/tables/users/data/${id}`, body: undefined }; - const result = await expectRequest(fetch, expected, async () => { - await users.delete(id); - }); + const result = await expectRequest( + fetch, + [], + async () => { + await users.delete(id); + }, + { + records: [], + meta: { page: { cursor: '', more: false } } + } + ); expect(result).toMatchInlineSnapshot(` [ { - "body": undefined, - "method": "DELETE", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/data/rec_1234?columns=*", + "body": "{"statement":"delete from \\"users\\" where \\"xata_id\\" = $1 returning *","params":["rec_1234"]}", + "method": "POST", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); @@ -467,33 +506,28 @@ describe('create', () => { test('successful', async () => { const { fetch, users } = buildClient(); - const created = { id: 'rec_1234', _version: 0 }; + const created = { xata_id: 'rec_1234', _version: 0 }; const object = { name: 'Ada' } as User; - const expected = [ - { method: 'POST', path: '/tables/users/data', body: object }, - { - method: 'GET', - path: '/tables/users/data/rec_1234', - body: undefined - } - ]; const result = await expectRequest( fetch, - expected, + [], async () => { const result = await users.create(object); - expect(result.id).toBe(created.id); + expect(result.xata_id).toBe(created.xata_id); }, - created + { + records: [created], + meta: { page: { cursor: '', more: false } } + } ); expect(result).toMatchInlineSnapshot(` [ { - "body": "{"name":"Ada"}", + "body": "{"statement":"insert into \\"users\\" (\\"name\\") values ($1) returning *","params":["Ada"]}", "method": "POST", - "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/tables/users/data?columns=*", + "url": "https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb:main/sql", }, ] `); diff --git a/packages/client/src/schema/index.ts b/packages/client/src/schema/index.ts index 2d0fe9102..7ddc77018 100644 --- a/packages/client/src/schema/index.ts +++ b/packages/client/src/schema/index.ts @@ -1,44 +1,43 @@ import { XataPlugin, XataPluginOptions } from '../plugins'; import { isString } from '../util/lang'; -import { XataRecord } from './record'; -import { Repository, RestRepository } from './repository'; +import { DatabaseSchema, SchemaInference } from './inference'; +import { Repository, KyselyRepository } from './repository'; export * from './ask'; -export * from './cache'; export { XataFile } from './files'; export type { XataArrayFile } from './files'; export * from './inference'; export * from './operators'; export * from './pagination'; export { Query } from './query'; -export { RecordColumnTypes, isIdentifiable, isXataRecord } from './record'; +export { RecordColumnTypes, isIdentifiable } from './record'; export type { BaseData, EditableData, Identifiable, JSONData, Link, XataRecord } from './record'; -export { Repository, RestRepository } from './repository'; +export { Repository, KyselyRepository } from './repository'; export * from './selection'; export type SchemaDefinition = { table: string; }; -export type SchemaPluginResult> = { - [Key in keyof Schemas]: Repository; +export type SchemaPluginResult = { + [Key in Schema['tables'][number]['name']]: Repository[Key]>; }; -export class SchemaPlugin> extends XataPlugin { - #tables: Record> = {}; +export class SchemaPlugin extends XataPlugin { + #tables: Record> = {}; constructor() { super(); } - build(pluginOptions: XataPluginOptions): SchemaPluginResult { + build(pluginOptions: XataPluginOptions): SchemaPluginResult { const db: any = new Proxy( {}, { get: (_target, table) => { if (!isString(table)) throw new Error('Invalid table name'); if (this.#tables[table] === undefined) { - this.#tables[table] = new RestRepository({ db, pluginOptions, table, schemaTables: pluginOptions.tables }); + this.#tables[table] = new KyselyRepository({ db, pluginOptions, table, schema: pluginOptions.schema }); } return this.#tables[table]; @@ -47,9 +46,9 @@ export class SchemaPlugin> extends Xa ); // Inject generated tables for shell to auto-complete - const tableNames = pluginOptions.tables?.map(({ name }) => name) ?? []; + const tableNames = pluginOptions.schema.tables.map(({ name }) => name); for (const table of tableNames) { - db[table] = new RestRepository({ db, pluginOptions, table, schemaTables: pluginOptions.tables }); + db[table] = new KyselyRepository({ db, pluginOptions, table, schema: pluginOptions.schema }); } return db; diff --git a/packages/client/src/schema/inference.spec.ts b/packages/client/src/schema/inference.spec.ts index 438c1752a..1aa717e2a 100644 --- a/packages/client/src/schema/inference.spec.ts +++ b/packages/client/src/schema/inference.spec.ts @@ -8,6 +8,10 @@ const tables = [ { name: 'teams', columns: [ + { name: 'xata_id', type: 'string' }, + { name: 'xata_version', type: 'int' }, + { name: 'xata_createdat', type: 'datetime' }, + { name: 'xata_updatedat', type: 'datetime' }, { name: 'name', type: 'string' }, { name: 'labels', type: 'multiple' }, { name: 'owner', type: 'link', link: { table: 'users' } } @@ -16,6 +20,10 @@ const tables = [ { name: 'users', columns: [ + { name: 'xata_id', type: 'string' }, + { name: 'xata_version', type: 'int' }, + { name: 'xata_createdat', type: 'datetime' }, + { name: 'xata_updatedat', type: 'datetime' }, { name: 'email', type: 'email' }, { name: 'full_name', type: 'string', notNull: true, defaultValue: 'John Doe' }, { name: 'team', type: 'link', link: { table: 'teams' } }, @@ -24,17 +32,9 @@ const tables = [ } ] as const; -function simpleTeam(team: SchemaInference['teams'] & XataRecord) { - team.getMetadata(); - team.owner?.getMetadata(); -} - function simpleUser(user: SchemaInference['users'] & XataRecord) { user.full_name.startsWith('a'); - user.getMetadata(); - user.team?.getMetadata(); - user.json?.foo; user.json?.[0]; } diff --git a/packages/client/src/schema/inference.ts b/packages/client/src/schema/inference.ts index 342d348fc..172e614af 100644 --- a/packages/client/src/schema/inference.ts +++ b/packages/client/src/schema/inference.ts @@ -1,22 +1,23 @@ -import { Schemas } from '../api'; import { UnionToIntersection, Values } from '../util/types'; import { XataArrayFile, XataFile } from './files'; import { JSONValue } from './json'; import { Identifiable, XataRecord } from './record'; -export type BaseSchema = { +export type DatabaseSchema = { + tables: readonly TableSchema[]; +}; + +export type TableSchema = { name: string; - columns: readonly ( - | { - name: string; - type: Schemas.Column['type']; - notNull?: boolean; - } - | { name: string; type: 'link'; link: { table: string } } - )[]; + columns: readonly { + name: string; + type: string; + notNull?: boolean; + }[]; + primaryKey?: readonly string[]; }; -export type SchemaInference = T extends never[] +export type SchemaInference = T extends never[] ? Record> : T extends readonly unknown[] ? T[number] extends { name: string; columns: readonly unknown[] } @@ -30,8 +31,7 @@ type TableType = Tables & { name: TableName } extends infer T ? Table extends { name: string; columns: infer Columns } ? Columns extends readonly unknown[] ? Columns[number] extends { name: string; type: string } - ? Identifiable & - UnionToIntersection }>> + ? UnionToIntersection }>> : never : never : never @@ -98,6 +98,6 @@ type InnerType = Type extends : Type extends 'json' | 'jsonb' ? JSONValue : Type extends 'link' - ? TableType & XataRecord + ? string : // This is a fallback for when the type is not recognized string; diff --git a/packages/client/src/schema/pagination.ts b/packages/client/src/schema/pagination.ts index 877d23632..1dd56eff2 100644 --- a/packages/client/src/schema/pagination.ts +++ b/packages/client/src/schema/pagination.ts @@ -1,17 +1,23 @@ import { isDefined, isObject } from '../util/lang'; -import { Query } from './query'; +import { DatabaseSchema } from './inference'; +import { Query, QueryOptions } from './query'; import { JSONData, XataRecord } from './record'; export type PaginationQueryMeta = { page: { cursor: string; more: boolean; size: number } }; -export interface Paginable { +export interface Paginable< + Schema extends DatabaseSchema, + TableName extends string, + ObjectType, + Result extends XataRecord = XataRecord +> { meta: PaginationQueryMeta; records: PageRecordArray; - nextPage(size?: number, offset?: number): Promise>; - previousPage(size?: number, offset?: number): Promise>; - startPage(size?: number, offset?: number): Promise>; - endPage(size?: number, offset?: number): Promise>; + nextPage(size?: number, offset?: number): Promise>; + previousPage(size?: number, offset?: number): Promise>; + startPage(size?: number, offset?: number): Promise>; + endPage(size?: number, offset?: number): Promise>; hasNextPage(): boolean; } @@ -20,8 +26,14 @@ export interface Paginable implements Paginable { - #query: Query; +export class Page< + Schema extends DatabaseSchema, + TableName extends string, + ObjectType, + Result extends XataRecord = XataRecord +> implements Paginable +{ + #query: Query; /** * Page metadata, required to retrieve additional records. */ @@ -31,7 +43,7 @@ export class Page */ readonly records: PageRecordArray; - constructor(query: Query, meta: PaginationQueryMeta, records: Result[] = []) { + constructor(query: Query, meta: PaginationQueryMeta, records: Result[] = []) { this.#query = query; this.meta = meta; this.records = new PageRecordArray(this, records); @@ -43,7 +55,7 @@ export class Page * @param offset Number of results to skip when retrieving the results. * @returns The next page or results. */ - async nextPage(size?: number, offset?: number): Promise> { + async nextPage(size?: number, offset?: number): Promise> { return this.#query.getPaginated({ pagination: { size, offset, after: this.meta.page.cursor } }); } @@ -53,7 +65,7 @@ export class Page * @param offset Number of results to skip when retrieving the results. * @returns The previous page or results. */ - async previousPage(size?: number, offset?: number): Promise> { + async previousPage(size?: number, offset?: number): Promise> { return this.#query.getPaginated({ pagination: { size, offset, before: this.meta.page.cursor } }); } @@ -63,7 +75,7 @@ export class Page * @param offset Number of results to skip when retrieving the results. * @returns The start page or results. */ - async startPage(size?: number, offset?: number): Promise> { + async startPage(size?: number, offset?: number): Promise> { return this.#query.getPaginated({ pagination: { size, offset, start: this.meta.page.cursor } }); } @@ -73,7 +85,7 @@ export class Page * @param offset Number of results to skip when retrieving the results. * @returns The end page or results. */ - async endPage(size?: number, offset?: number): Promise> { + async endPage(size?: number, offset?: number): Promise> { return this.#query.getPaginated({ pagination: { size, offset, end: this.meta.page.cursor } }); } @@ -88,6 +100,7 @@ export class Page export type CursorNavigationOptions = { start?: string } | { end?: string } | { after?: string; before?: string }; export type OffsetNavigationOptions = { size?: number; offset?: number }; +export type CursorNavigationDecoded = { lastSeenId: string; data: QueryOptions } | undefined; export const PAGINATION_MAX_SIZE = 1000; export const PAGINATION_DEFAULT_SIZE = 20; @@ -143,9 +156,9 @@ export class RecordArray extends Array { } export class PageRecordArray extends Array { - #page: Paginable; + #page: Paginable; - constructor(page: Paginable, overrideRecords?: Result[]); + constructor(page: Paginable, overrideRecords?: Result[]); constructor(...args: any[]) { super(...PageRecordArray.parseConstructorParams(...args)); diff --git a/packages/client/src/schema/query.ts b/packages/client/src/schema/query.ts index 75165d9ff..0471314f8 100644 --- a/packages/client/src/schema/query.ts +++ b/packages/client/src/schema/query.ts @@ -3,6 +3,7 @@ import { FilterExpression } from '../api/schemas'; import { compact, isDefined, isObject, isString, isStringArray, toBase64 } from '../util/lang'; import { Dictionary, OmitBy, RequiredBy, SingleOrArray } from '../util/types'; import { Filter, FilterColumns, FilterValueAtColumn, JSONFilterColumns } from './filters'; +import { DatabaseSchema, TableSchema } from './inference'; import { CursorNavigationOptions, OffsetNavigationOptions, @@ -16,15 +17,14 @@ import { RecordArray } from './pagination'; import { XataRecord } from './record'; -import { RestRepository } from './repository'; +import { KyselyRepository } from './repository'; import { SelectableColumn, SelectableColumnWithObjectNotation, SelectedPick } from './selection'; import { SortColumns, SortDirection, SortFilter } from './sorting'; import { SummarizeExpression, SummarizeParams, SummarizeResult } from './summarize'; -type BaseOptions = { +type BaseOptions = { columns?: SelectableColumnWithObjectNotation[]; consistency?: 'strong' | 'eventual'; - cache?: number; fetchOptions?: Record; }; @@ -34,13 +34,13 @@ type CursorQueryOptions = { sort?: never; }; -type OffsetQueryOptions = { +type OffsetQueryOptions = { pagination?: OffsetNavigationOptions; filter?: FilterExpression; sort?: SingleOrArray>; }; -export type QueryOptions = BaseOptions & (CursorQueryOptions | OffsetQueryOptions); +export type QueryOptions = BaseOptions & (CursorQueryOptions | OffsetQueryOptions); /** * Query objects contain the information of all filters, sorting, etc. to be included in the database query. @@ -48,20 +48,26 @@ export type QueryOptions = BaseOptions & (CursorQueryOp * Query objects are immutable. Any method that adds more constraints or options to the query will return * a new Query object containing the both the previous and the new constraints and options. */ -export class Query implements Paginable { - #table: { name: string; schema?: Schemas.Table }; - #repository: RestRepository; - #data: QueryOptions = { filter: {} }; +export class Query< + Schema extends DatabaseSchema, + TableName extends string, + ObjectType, + Result extends XataRecord = XataRecord +> implements Paginable +{ + #table: { name: string; schema?: TableSchema }; + #repository: KyselyRepository; + #data: QueryOptions = { filter: {} }; // Implements pagination readonly meta: PaginationQueryMeta = { page: { cursor: 'start', more: true, size: PAGINATION_DEFAULT_SIZE } }; readonly records: PageRecordArray = new PageRecordArray(this, []); constructor( - repository: RestRepository | null, - table: { name: string; schema?: Schemas.Table }, - data: Partial>, - rawParent?: Partial> + repository: KyselyRepository | null, + table: { name: string; schema?: TableSchema }, + data: Partial>, + rawParent?: Partial> ) { this.#table = table; @@ -79,11 +85,10 @@ export class Query['sort']; // Fix for TS =4.7 + this.#data.sort = (data.sort ?? parent?.sort) as QueryOptions['sort']; // Fix for TS =4.7 this.#data.columns = data.columns ?? parent?.columns; this.#data.consistency = data.consistency ?? parent?.consistency; this.#data.pagination = data.pagination ?? parent?.pagination; - this.#data.cache = data.cache ?? parent?.cache; this.#data.fetchOptions = data.fetchOptions ?? parent?.fetchOptions; this.any = this.any.bind(this); @@ -97,7 +102,7 @@ export class Query { + getQueryOptions(): QueryOptions { return this.#data; } @@ -112,9 +117,14 @@ export class Query[]): Query { + any(...queries: Query[]): Query { const $any = queries.map((query) => query.getQueryOptions().filter ?? {}); - return new Query(this.#repository, this.#table, { filter: { $any } }, this.#data); + return new Query( + this.#repository, + this.#table, + { filter: { $any } }, + this.#data + ); } /** @@ -122,9 +132,14 @@ export class Query[]): Query { + all(...queries: Query[]): Query { const $all = queries.map((query) => query.getQueryOptions().filter ?? {}); - return new Query(this.#repository, this.#table, { filter: { $all } }, this.#data); + return new Query( + this.#repository, + this.#table, + { filter: { $all } }, + this.#data + ); } /** @@ -132,9 +147,14 @@ export class Query[]): Query { + not(...queries: Query[]): Query { const $not = queries.map((query) => query.getQueryOptions().filter ?? {}); - return new Query(this.#repository, this.#table, { filter: { $not } }, this.#data); + return new Query( + this.#repository, + this.#table, + { filter: { $not } }, + this.#data + ); } /** @@ -142,9 +162,14 @@ export class Query[]): Query { + none(...queries: Query[]): Query { const $none = queries.map((query) => query.getQueryOptions().filter ?? {}); - return new Query(this.#repository, this.#table, { filter: { $none } }, this.#data); + return new Query( + this.#repository, + this.#table, + { filter: { $none } }, + this.#data + ); } /** @@ -159,10 +184,10 @@ export class Query | JSONFilterColumns>( + filter | JSONFilterColumns>( column: F, - value: FilterValueAtColumn - ): Query; + value: FilterValueAtColumn + ): Query; /** * Builds a new query object adding one or more constraints. Examples: @@ -177,21 +202,31 @@ export class Query): Query; + filter(filter?: Filter): Query; - filter(a: any, b?: any): Query { + filter(a: any, b?: any): Query { if (arguments.length === 1) { const constraints = Object.entries(a ?? {}).map(([column, constraint]) => ({ [column]: this.#cleanFilterConstraint(column, constraint) as any })); const $all = compact([this.#data.filter?.$all].flat().concat(constraints)); - return new Query(this.#repository, this.#table, { filter: { $all } }, this.#data); + return new Query( + this.#repository, + this.#table, + { filter: { $all } }, + this.#data + ); } else { const constraints = isDefined(a) && isDefined(b) ? [{ [a]: this.#cleanFilterConstraint(a, b) }] : undefined; const $all = compact([this.#data.filter?.$all].flat().concat(constraints)); - return new Query(this.#repository, this.#table, { filter: { $all } }, this.#data); + return new Query( + this.#repository, + this.#table, + { filter: { $all } }, + this.#data + ); } } @@ -203,8 +238,8 @@ export class Query>(column: F, direction: SortDirection): Query; - sort(column: '*', direction: 'random'): Query; - sort>(column: F): Query; - sort(column: string, direction = 'asc'): Query { - const originalSort = [this.#data.sort ?? []].flat() as SortFilter[]; + sort>( + column: F, + direction: SortDirection + ): Query; + sort(column: '*', direction: 'random'): Query; + sort>(column: F): Query; + sort(column: string, direction = 'asc'): Query { + const originalSort = [this.#data.sort ?? []].flat() as SortFilter[]; const sort = [...originalSort, { column, direction }]; - return new Query(this.#repository, this.#table, { sort }, this.#data); + return new Query(this.#repository, this.#table, { sort }, this.#data); } /** @@ -230,8 +268,8 @@ export class Query>(columns: K[]) { - return new Query>( + select>(columns: K[]) { + return new Query>( this.#repository, this.#table, { columns }, @@ -244,7 +282,7 @@ export class Query>; + getPaginated(): Promise>; /** * Get paginated results @@ -252,7 +290,9 @@ export class Query, 'columns'>): Promise>; + getPaginated( + options: OmitBy, 'columns'> + ): Promise>; /** * Get paginated results @@ -260,12 +300,14 @@ export class Query, 'columns'>>( + getPaginated, 'columns'>>( options: Options - ): Promise>>; + ): Promise>>; - getPaginated(options: QueryOptions = {}): Promise> { - const query = new Query(this.#repository, this.#table, options, this.#data); + getPaginated( + options: QueryOptions = {} + ): Promise> { + const query = new Query(this.#repository, this.#table, options, this.#data); return this.#repository.query(query); } @@ -295,7 +337,7 @@ export class Query, 'columns' | 'pagination'> & { batchSize?: number } + options: OmitBy, 'columns' | 'pagination'> & { batchSize?: number } ): AsyncGenerator; /** @@ -305,11 +347,11 @@ export class Query, 'pagination'>, 'columns'> & { batchSize?: number } - >(options: Options): AsyncGenerator[]>; + Options extends RequiredBy, 'pagination'>, 'columns'> & { batchSize?: number } + >(options: Options): AsyncGenerator[]>; async *getIterator( - options: QueryOptions & { batchSize?: number } = {} + options: QueryOptions & { batchSize?: number } = {} ): AsyncGenerator { const { batchSize = 1 } = options; @@ -337,18 +379,18 @@ export class Query, 'columns'>>( + getMany, 'columns'>>( options: Options - ): Promise>>; + ): Promise>>; /** * Performs the query in the database and returns a set of results. * @param options Additional options to be used when performing the query. * @returns An array of records from the database. */ - getMany(options: OmitBy, 'columns'>): Promise>; + getMany(options: OmitBy, 'columns'>): Promise>; - async getMany(options: QueryOptions = {}): Promise> { + async getMany(options: QueryOptions = {}): Promise> { const { pagination = {}, ...rest } = options; const { size = PAGINATION_DEFAULT_SIZE, offset } = pagination; const batchSize = size <= PAGINATION_MAX_SIZE ? size : PAGINATION_MAX_SIZE; @@ -384,9 +426,9 @@ export class Query, 'pagination'>, 'columns'> & { batchSize?: number }>( - options: Options - ): Promise>>; + getAll< + Options extends RequiredBy, 'pagination'>, 'columns'> & { batchSize?: number } + >(options: Options): Promise>>; /** * Performs the query in the database and returns all the results. @@ -395,11 +437,11 @@ export class Query, 'columns' | 'pagination'> & { batchSize?: number } + options: OmitBy, 'columns' | 'pagination'> & { batchSize?: number } ): Promise>; async getAll( - options: QueryOptions & { batchSize?: number } = {} + options: QueryOptions & { batchSize?: number } = {} ): Promise> { const { batchSize = PAGINATION_MAX_SIZE, ...rest } = options; const results = []; @@ -423,18 +465,18 @@ export class Query, 'pagination'>, 'columns'>>( + getFirst, 'pagination'>, 'columns'>>( options: Options - ): Promise | null>; + ): Promise | null>; /** * Performs the query in the database and returns the first result. * @param options Additional options to be used when performing the query. * @returns The first record that matches the query, or null if no record matched the query. */ - getFirst(options: OmitBy, 'columns' | 'pagination'>): Promise; + getFirst(options: OmitBy, 'columns' | 'pagination'>): Promise; - async getFirst(options: QueryOptions = {}): Promise { + async getFirst(options: QueryOptions = {}): Promise { const records = await this.getMany({ ...options, pagination: { size: 1 } }); // Method overloading does not provide type inference for the return type. @@ -454,9 +496,9 @@ export class Query, 'pagination'>, 'columns'>>( + getFirstOrThrow, 'pagination'>, 'columns'>>( options: Options - ): Promise>; + ): Promise>; /** * Performs the query in the database and returns the first result. @@ -464,9 +506,9 @@ export class Query, 'columns' | 'pagination'>): Promise; + getFirstOrThrow(options: OmitBy, 'columns' | 'pagination'>): Promise; - async getFirstOrThrow(options: QueryOptions = {}): Promise { + async getFirstOrThrow(options: QueryOptions = {}): Promise { const records = await this.getMany({ ...options, pagination: { size: 1 } }); if (records[0] === undefined) throw new Error('No results found.'); @@ -475,35 +517,28 @@ export class Query>, - Columns extends SelectableColumn[] - >(params: SummarizeParams = {}): Promise> { + Expression extends Dictionary>, + Columns extends SelectableColumn[] + >( + params: SummarizeParams = {} + ): Promise> { const { summaries, summariesFilter, ...options } = params; - const query = new Query( + const query = new Query( this.#repository, this.#table, - options as Partial>, + options as Partial>, this.#data ); return this.#repository.summarizeTable(query, summaries, summariesFilter as Schemas.FilterExpression) as any; } - /** - * Builds a new query object adding a cache TTL in milliseconds. - * @param ttl The cache TTL in milliseconds. - * @returns A new Query object. - */ - cache(ttl: number): Query { - return new Query(this.#repository, this.#table, { cache: ttl }, this.#data); - } - /** * Retrieve next page of records * * @returns A new page object. */ - nextPage(size?: number, offset?: number): Promise> { + nextPage(size?: number, offset?: number): Promise> { return this.startPage(size, offset); } @@ -512,7 +547,7 @@ export class Query> { + previousPage(size?: number, offset?: number): Promise> { return this.startPage(size, offset); } @@ -521,7 +556,7 @@ export class Query> { + startPage(size?: number, offset?: number): Promise> { return this.getPaginated({ pagination: { size, offset } }); } @@ -530,7 +565,7 @@ export class Query> { + endPage(size?: number, offset?: number): Promise> { return this.getPaginated({ pagination: { size, offset, before: 'end' } }); } @@ -544,10 +579,7 @@ export class Query( - data: Partial>, - parent?: Partial> -) { +function cleanParent(data: Partial>, parent?: Partial>) { if (isCursorPaginationOptions(data.pagination)) { return { ...parent, sort: undefined, filter: undefined }; } diff --git a/packages/client/src/schema/record.ts b/packages/client/src/schema/record.ts index b5ffa1cc8..316f7fab7 100644 --- a/packages/client/src/schema/record.ts +++ b/packages/client/src/schema/record.ts @@ -28,7 +28,7 @@ export interface Identifiable { /** * Unique id of this record. */ - id: Identifier; + xata_id: Identifier; } export interface BaseData { @@ -38,18 +38,7 @@ export interface BaseData { /** * Represents a persisted record from the database. */ -export interface XataRecord = XataRecord> extends Identifiable { - /** - * Metadata of this record. - */ - xata: XataRecordMetadata; - - /** - * Get metadata of this record. - * @deprecated Use `xata` property instead. - */ - getMetadata(): XataRecordMetadata; - +export interface XataRecord> extends Identifiable { /** * Get an object representation of this record. */ @@ -142,33 +131,11 @@ export interface XataRecord = XataRecord< export type Link = XataRecord; -export type XataRecordMetadata = { - /** - * Number that is increased every time the record is updated. - */ - version: number; - /** - * Timestamp when the record was created. - */ - createdAt: Date; - /** - * Timestamp when the record was last updated. - */ - updatedAt: Date; -}; - export function isIdentifiable(x: any): x is Identifiable & Record { - return isObject(x) && isString((x as Partial)?.id); + return isObject(x) && isString(x?.xata_id); } -export function isXataRecord(x: any): x is XataRecord & Record { - const record = x as XataRecord & Record; - const metadata = record?.getMetadata(); - - return isIdentifiable(x) && isObject(metadata) && typeof metadata.version === 'number'; -} - -type NumericOperator = ExclusiveOr< +export type NumericOperator = ExclusiveOr< { $increment: number }, ExclusiveOr<{ $decrement: number }, ExclusiveOr<{ $multiply: number }, { $divide: number }>> >; @@ -176,9 +143,9 @@ type NumericOperator = ExclusiveOr< export type InputXataFile = Partial | Promise>; type EditableDataFields = T extends XataRecord - ? { id: Identifier } | Identifier + ? { xata_id: Identifier } | Identifier : NonNullable extends XataRecord - ? { id: Identifier } | Identifier | null | undefined + ? { xata_id: Identifier } | Identifier | null | undefined : T extends Date ? string | Date : NonNullable extends Date @@ -191,7 +158,7 @@ type EditableDataFields = T extends XataRecord ? number | NumericOperator : T; -export type EditableData = Identifiable & +export type EditableData = Identifiable & Partial< Omit< { @@ -205,7 +172,9 @@ type JSONDataFile = { [K in keyof XataFile]: XataFile[K] extends Function ? never : XataFile[K]; }; -type JSONDataFields = T extends XataFile +type JSONDataFields = T extends null | undefined | void + ? null | undefined + : T extends XataFile ? JSONDataFile : NonNullable extends XataFile ? JSONDataFile | null | undefined @@ -221,22 +190,17 @@ type JSONDataFields = T extends XataFile type JSONDataBase = Identifiable & { /** - * Metadata about the record. + * Timestamp when the record was created. + */ + xata_createdat: string; + /** + * Timestamp when the record was last updated. + */ + xata_updatedat: string; + /** + * Number that is increased every time the record is updated. */ - xata: { - /** - * Timestamp when the record was created. - */ - createdAt: string; - /** - * Timestamp when the record was last updated. - */ - updatedAt: string; - /** - * Number that is increased every time the record is updated. - */ - version: number; - }; + xata_version: number; }; export type JSONData = JSONDataBase & diff --git a/packages/client/src/schema/repository.spec.ts b/packages/client/src/schema/repository.spec.ts new file mode 100644 index 000000000..388bd0d2b --- /dev/null +++ b/packages/client/src/schema/repository.spec.ts @@ -0,0 +1,123 @@ +import { expect, test } from 'vitest'; +import { KyselyPlugin } from '../kysely'; +import { columnSelectionObject, generateSelectStatement } from './repository'; +import { XataPluginOptions } from '../plugins'; + +const teamsForeignKeys = { + owner_owner: { + name: 'owner_owner', + columns: ['owner'], + referencedTable: 'users', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + } +}; + +const usersForeignKeys = { + pet_pet: { + name: 'pet_pet', + columns: ['pet'], + referencedTable: 'pets', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + } +}; + +const schema = { + tables: [ + { + name: 'teams', + foreignKeys: teamsForeignKeys + }, + { + name: 'users', + foreignKeys: usersForeignKeys + } + ] +}; + +const clientOptions = { + apiKey: 'dummyApiKey', + apiUrl: '', + branch: 'main', + fetch: {} as any, + host: 'staging', + schema: {} as any, + trace: {} as any, + workspacesApiUrl: 'https://dummy-workspace.eu-west-1.staging-xata.dev/db/dummy-database' +} as XataPluginOptions; + +test('link selection', () => { + const input = ['name', 'owner.*', 'owner.pet.*', 'owner.full_name', 'xata_id']; + const res = columnSelectionObject(input); + expect(res).toEqual({ + links: { + owner: { + links: { + pet: { + links: {}, + regular: ['*'] + } + }, + regular: ['*', 'full_name'] + } + }, + regular: ['name', 'xata_id'] + }); +}); + +test('link selection to kysely selects', () => { + const columns = ['name', 'owner.pet.*', 'owner.full_name', 'xata_id']; + + const db = new KyselyPlugin().build(clientOptions); + const tableName = 'teams'; + let statement = db.selectFrom(tableName); + + statement = generateSelectStatement({ + filter: {}, + columnData: [], + columns, + schema, + db: db, + primaryKey: 'xata_id', + stmt: statement, + tableName + }) as any; + + expect(statement.compile().sql).toBe( + `select "name", "xata_id", (select to_json(obj) from (select "xata_id", "full_name", (select to_json(obj) from (select * from "pets" where "xata_id" = "users"."pet") as obj) as "pet" from "users" where "xata_id" = "teams"."owner") as obj) as "owner" from "teams"` + ); +}); + +test('link selection to kysely selects with filter', () => { + const columns = ['owner.pet.*', 'owner.full_name']; + const db = new KyselyPlugin().build(clientOptions); + const tableName = 'teams'; + let statement = db.selectFrom(tableName); + + const filter = { + owner: { + full_name: { $isNot: 'John Doe' }, + pet: { + name: { + $isNot: 'thirdlevelPropagain' + } + } + }, + name: 'Team animals' + }; + statement = generateSelectStatement({ + filter, + columnData: [], + columns, + schema, + primaryKey: 'xata_id', + stmt: statement, + tableName, + db + }) as any; + + expect(statement.compile().sql).toBe( + `select * from (select "xata_id", (select to_json(obj) from (select "xata_id", "full_name", (select to_json(obj) from (select * from "pets" where "xata_id" = "users"."pet" and "name" != $1) as obj where obj is not null) as "pet" from "users" where "xata_id" = "teams"."owner" and "full_name" != $2) as obj where obj is not null) as "owner" from "teams" where CAST ("name" AS text) = $3) as "tmp" where "tmp"."owner" is not null` + ); +}); diff --git a/packages/client/src/schema/repository.ts b/packages/client/src/schema/repository.ts index 4c7db45ac..562a6e2d5 100644 --- a/packages/client/src/schema/repository.ts +++ b/packages/client/src/schema/repository.ts @@ -1,17 +1,18 @@ -import { SchemaPluginResult } from '.'; +import { DatabaseSchema, SchemaPluginResult } from '.'; import { ApiExtraProps, Schemas, + SqlBatchQueryRequestBody, aggregateTable, askTableSession, branchTransaction, deleteRecord, - getBranchDetails, getRecord, insertRecord, insertRecordWithID, queryTable, searchTable, + sqlBatchQuery, summarizeTable, updateRecordWithID, upsertRecordWithID, @@ -19,10 +20,11 @@ import { } from '../api'; import { fetchSSERequest } from '../api/fetcher'; import { + BranchSchema, FuzzinessExpression, HighlightExpression, PrefixExpression, - RecordsMetadata, + Schema, SearchPageConfig, TransactionOperation } from '../api/schemas'; @@ -30,38 +32,75 @@ import { XataPluginOptions } from '../plugins'; import { SearchXataRecord, TotalCount } from '../search'; import { Boosters } from '../search/boosters'; import { TargetColumn } from '../search/target'; -import { chunk, compact, isDefined, isNumber, isObject, isString, promiseMap } from '../util/lang'; +import { chunk, compact, isDefined, isNumber, isObject, isString, isStringOrNumber, promiseMap } from '../util/lang'; import { Dictionary } from '../util/types'; import { generateUUID } from '../util/uuid'; import { VERSION } from '../version'; import { AggregationExpression, AggregationResult } from './aggregate'; import { AskOptions, AskResult } from './ask'; -import { CacheImpl } from './cache'; import { XataArrayFile, XataFile, parseInputFileEntry } from './files'; -import { Filter, cleanFilter } from './filters'; +import { Filter, atPath, cleanFilter, filterToKysely, relevantFilters } from './filters'; import { parseJson, stringifyJson } from './json'; -import { Page } from './pagination'; +import { + CursorNavigationDecoded, + PAGINATION_DEFAULT_OFFSET, + PAGINATION_DEFAULT_SIZE, + PAGINATION_MAX_OFFSET, + PAGINATION_MAX_SIZE, + Page +} from './pagination'; import { Query } from './query'; import { EditableData, Identifiable, Identifier, InputXataFile, XataRecord, isIdentifiable } from './record'; import { + ColumnSelectionObject, ColumnsByValue, SelectableColumn, SelectableColumnWithObjectNotation, SelectedPick, isValidSelectableColumns } from './selection'; -import { buildSortFilter } from './sorting'; +import { ApiSortFilter, SortDirection, buildSortFilter, isSortFilterObject } from './sorting'; import { SummarizeExpression } from './summarize'; import { AttributeDictionary, TraceAttributes, TraceFunction, defaultTrace } from './tracing'; +import { + AliasedRawBuilder, + DeleteQueryBuilder, + Expression, + ExpressionBuilder, + InsertQueryBuilder, + RawBuilder, + SelectExpression, + SelectQueryBuilder, + Selection, + Simplify, + UpdateQueryBuilder, + sql +} from 'kysely'; +import { BinaryOperatorExpression } from 'kysely/dist/cjs/parser/binary-operation-parser'; +import { SQLBatchResponse } from '../api/dataPlaneResponses'; +import { Cursor, decode } from '@xata.io/sql'; +import { KyselyPlugin, KyselyPluginResult } from '../kysely'; +import { + NewEditableData, + NewEditableDataWithoutNumeric, + NewIdentifiable, + NewIdentifierKey, + NewIndentifierValue +} from './identifiable'; +import { Model } from '@xata.io/kysely'; +import { jsonObjectFrom } from 'kysely/helpers/postgres'; +import { ObjectType } from 'typescript'; const BULK_OPERATION_MAX_SIZE = 1000; /** * Common interface for performing operations on a table. */ -export abstract class Repository extends Query< - Record, - Readonly> +export abstract class Repository extends Query< + Schema, + TableName, + ObjectType, + Readonly> > { /* * Creates a single record in the table. @@ -69,11 +108,11 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The full persisted record. */ - abstract create>( - object: Omit, 'id'> & Partial, + abstract create>( + object: NewEditableDataWithoutNumeric, columns: K[], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /* * Creates a single record in the table. @@ -81,9 +120,9 @@ export abstract class Repository extends Query< * @returns The full persisted record. */ abstract create( - object: Omit, 'id'> & Partial, + object: NewEditableDataWithoutNumeric, options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates a single record in the table with a unique id. @@ -92,12 +131,15 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The full persisted record. */ - abstract create>( - id: Identifier, - object: Omit, 'id'>, + abstract create>( + id: NewIndentifierValue[TableName]>, + object: Omit< + NewEditableDataWithoutNumeric, + NewIdentifierKey[TableName]> + >, columns: K[], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates a single record in the table with a unique id. @@ -106,10 +148,13 @@ export abstract class Repository extends Query< * @returns The full persisted record. */ abstract create( - id: Identifier, - object: Omit, 'id'>, + id: NewIndentifierValue[TableName]>, + object: Omit< + NewEditableDataWithoutNumeric, + NewIdentifierKey[TableName]> + >, options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates multiple records in the table. @@ -117,10 +162,10 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns Array of the persisted records in order. */ - abstract create>( - objects: Array, 'id'> & Partial>, + abstract create>( + objects: Array>, columns: K[] - ): Promise>[]>; + ): Promise>[]>; /** * Creates multiple records in the table. @@ -128,8 +173,8 @@ export abstract class Repository extends Query< * @returns Array of the persisted records in order. */ abstract create( - objects: Array, 'id'> & Partial> - ): Promise>[]>; + objects: Array> + ): Promise>[]>; /** * Queries a single record from the table given its unique id. @@ -137,17 +182,19 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The persisted record for the given id or null if the record could not be found. */ - abstract read>( - id: Identifier, + abstract read>( + id: NewIndentifierValue[TableName]>, columns: K[] - ): Promise | null>>; + ): Promise | null>>; /** * Queries a single record from the table given its unique id. * @param id The unique id. * @returns The persisted record for the given id or null if the record could not be found. */ - abstract read(id: Identifier): Promise | null>>; + abstract read( + id: NewIndentifierValue[TableName]> + ): Promise | null>>; /** * Queries multiple records from the table given their unique id. @@ -155,17 +202,19 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The persisted records for the given ids in order (if a record could not be found null is returned). */ - abstract read>( - ids: ReadonlyArray, + abstract read>( + ids: ReadonlyArray[TableName]>>, columns: K[] - ): Promise> | null>>; + ): Promise> | null>>; /** * Queries multiple records from the table given their unique id. * @param ids The unique ids array. * @returns The persisted records for the given ids in order (if a record could not be found null is returned). */ - abstract read(ids: ReadonlyArray): Promise> | null>>; + abstract read( + ids: ReadonlyArray[TableName]>> + ): Promise> | null>>; /** * Queries a single record from the table by the id in the object. @@ -173,17 +222,19 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The persisted record for the given id or null if the record could not be found. */ - abstract read>( - object: Identifiable, + abstract read>( + object: NewIdentifiable[TableName], columns: K[] - ): Promise | null>>; + ): Promise | null>>; /** * Queries a single record from the table by the id in the object. * @param object Object containing the id of the record. * @returns The persisted record for the given id or null if the record could not be found. */ - abstract read(object: Identifiable): Promise | null>>; + abstract read( + object: NewIdentifiable[TableName] + ): Promise | null>>; /** * Queries multiple records from the table by the ids in the objects. @@ -191,17 +242,19 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The persisted records for the given ids in order (if a record could not be found null is returned). */ - abstract read>( - objects: Identifiable[], + abstract read>( + objects: NewIdentifiable[TableName][], columns: K[] - ): Promise> | null>>; + ): Promise> | null>>; /** * Queries multiple records from the table by the ids in the objects. * @param objects Array of objects containing the ids of the records. * @returns The persisted records for the given ids in order (if a record could not be found null is returned). */ - abstract read(objects: Identifiable[]): Promise> | null>>; + abstract read( + objects: NewIdentifiable[TableName][] + ): Promise> | null>>; /** * Queries a single record from the table given its unique id. @@ -210,10 +263,10 @@ export abstract class Repository extends Query< * @returns The persisted record for the given id. * @throws If the record could not be found. */ - abstract readOrThrow>( - id: Identifier, + abstract readOrThrow>( + id: NewIndentifierValue[TableName]>, columns: K[] - ): Promise>>; + ): Promise>>; /** * Queries a single record from the table given its unique id. @@ -221,7 +274,9 @@ export abstract class Repository extends Query< * @returns The persisted record for the given id. * @throws If the record could not be found. */ - abstract readOrThrow(id: Identifier): Promise>>; + abstract readOrThrow( + id: NewIndentifierValue[TableName]> + ): Promise>>; /** * Queries multiple records from the table given their unique id. @@ -230,10 +285,10 @@ export abstract class Repository extends Query< * @returns The persisted records for the given ids in order. * @throws If one or more records could not be found. */ - abstract readOrThrow>( - ids: ReadonlyArray, + abstract readOrThrow>( + ids: ReadonlyArray[TableName]>>, columns: K[] - ): Promise>>>; + ): Promise>>>; /** * Queries multiple records from the table given their unique id. @@ -241,7 +296,9 @@ export abstract class Repository extends Query< * @returns The persisted records for the given ids in order. * @throws If one or more records could not be found. */ - abstract readOrThrow(ids: ReadonlyArray): Promise>>>; + abstract readOrThrow( + ids: ReadonlyArray[TableName]>> + ): Promise>>>; /** * Queries a single record from the table by the id in the object. @@ -250,10 +307,10 @@ export abstract class Repository extends Query< * @returns The persisted record for the given id. * @throws If the record could not be found. */ - abstract readOrThrow>( - object: Identifiable, + abstract readOrThrow>( + object: NewIdentifiable[TableName], columns: K[] - ): Promise>>; + ): Promise>>; /** * Queries a single record from the table by the id in the object. @@ -261,7 +318,9 @@ export abstract class Repository extends Query< * @returns The persisted record for the given id. * @throws If the record could not be found. */ - abstract readOrThrow(object: Identifiable): Promise>>; + abstract readOrThrow( + object: NewIdentifiable[TableName] + ): Promise>>; /** * Queries multiple records from the table by the ids in the objects. @@ -270,10 +329,10 @@ export abstract class Repository extends Query< * @returns The persisted records for the given ids in order. * @throws If one or more records could not be found. */ - abstract readOrThrow>( - objects: Identifiable[], + abstract readOrThrow>( + objects: NewIdentifiable[TableName][], columns: K[] - ): Promise>>>; + ): Promise>>>; /** * Queries multiple records from the table by the ids in the objects. @@ -281,7 +340,9 @@ export abstract class Repository extends Query< * @returns The persisted records for the given ids in order. * @throws If one or more records could not be found. */ - abstract readOrThrow(objects: Identifiable[]): Promise>>>; + abstract readOrThrow( + objects: NewIdentifiable[TableName][] + ): Promise>>>; /** * Partially update a single record. @@ -289,11 +350,11 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The full persisted record, null if the record could not be found. */ - abstract update>( - object: Partial> & Identifiable, + abstract update>( + object: NewEditableData & NewIdentifiable[TableName], columns: K[], options?: { ifVersion?: number } - ): Promise> | null>; + ): Promise> | null>; /** * Partially update a single record. @@ -301,9 +362,9 @@ export abstract class Repository extends Query< * @returns The full persisted record, null if the record could not be found. */ abstract update( - object: Partial> & Identifiable, + object: NewEditableData & NewIdentifiable[TableName], options?: { ifVersion?: number } - ): Promise> | null>; + ): Promise> | null>; /** * Partially update a single record given its unique id. @@ -312,12 +373,12 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The full persisted record, null if the record could not be found. */ - abstract update>( - id: Identifier, - object: Partial>, + abstract update>( + id: NewIndentifierValue[TableName]>, + object: NewEditableData, columns: K[], options?: { ifVersion?: number } - ): Promise> | null>; + ): Promise> | null>; /** * Partially update a single record given its unique id. @@ -326,10 +387,10 @@ export abstract class Repository extends Query< * @returns The full persisted record, null if the record could not be found. */ abstract update( - id: Identifier, - object: Partial>, + id: NewIndentifierValue[TableName]>, + object: Omit, NewIdentifierKey[TableName]>>, options?: { ifVersion?: number } - ): Promise> | null>; + ): Promise> | null>; /** * Partially updates multiple records. @@ -337,10 +398,10 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns Array of the persisted records in order (if a record could not be found null is returned). */ - abstract update>( - objects: Array> & Identifiable>, + abstract update>( + objects: Array & NewIdentifiable[TableName]>, columns: K[] - ): Promise> | null>>; + ): Promise> | null>>; /** * Partially updates multiple records. @@ -348,8 +409,8 @@ export abstract class Repository extends Query< * @returns Array of the persisted records in order (if a record could not be found null is returned). */ abstract update( - objects: Array> & Identifiable> - ): Promise> | null>>; + objects: Array & NewIdentifiable[TableName]> + ): Promise> | null>>; /** * Partially update a single record. @@ -358,11 +419,11 @@ export abstract class Repository extends Query< * @returns The full persisted record. * @throws If the record could not be found. */ - abstract updateOrThrow>( - object: Partial> & Identifiable, + abstract updateOrThrow>( + object: NewEditableData & NewIdentifiable[TableName], columns: K[], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Partially update a single record. @@ -371,9 +432,9 @@ export abstract class Repository extends Query< * @throws If the record could not be found. */ abstract updateOrThrow( - object: Partial> & Identifiable, + object: NewEditableData & NewIdentifiable[TableName], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Partially update a single record given its unique id. @@ -383,12 +444,12 @@ export abstract class Repository extends Query< * @returns The full persisted record. * @throws If the record could not be found. */ - abstract updateOrThrow>( - id: Identifier, - object: Partial>, + abstract updateOrThrow>( + id: NewIndentifierValue[TableName]>, + object: NewEditableData, columns: K[], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Partially update a single record given its unique id. @@ -398,10 +459,10 @@ export abstract class Repository extends Query< * @throws If the record could not be found. */ abstract updateOrThrow( - id: Identifier, - object: Partial>, + id: NewIndentifierValue[TableName]>, + object: NewEditableData, options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Partially updates multiple records. @@ -410,10 +471,10 @@ export abstract class Repository extends Query< * @returns Array of the persisted records in order. * @throws If one or more records could not be found. */ - abstract updateOrThrow>( - objects: Array> & Identifiable>, + abstract updateOrThrow>( + objects: Array & NewIdentifiable[TableName]>, columns: K[] - ): Promise>[]>; + ): Promise>[]>; /** * Partially updates multiple records. @@ -422,8 +483,8 @@ export abstract class Repository extends Query< * @throws If one or more records could not be found. */ abstract updateOrThrow( - objects: Array> & Identifiable> - ): Promise>[]>; + objects: Array & NewIdentifiable[TableName]> + ): Promise>[]>; /** * Creates or updates a single record. If a record exists with the given id, @@ -432,11 +493,11 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The full persisted record. */ - abstract createOrUpdate>( - object: Omit, 'id'> & Partial, + abstract createOrUpdate>( + object: NewEditableDataWithoutNumeric & NewIdentifiable[TableName], columns: K[], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates or updates a single record. If a record exists with the given id, @@ -445,9 +506,9 @@ export abstract class Repository extends Query< * @returns The full persisted record. */ abstract createOrUpdate( - object: Omit, 'id'> & Partial, + object: NewEditableDataWithoutNumeric & NewIdentifiable[TableName], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates or updates a single record. If a record exists with the given id, @@ -457,12 +518,15 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The full persisted record. */ - abstract createOrUpdate>( - id: Identifier | undefined, - object: Omit, 'id'>, + abstract createOrUpdate>( + id: undefined | NewIndentifierValue[TableName]>, + object: Omit< + NewEditableDataWithoutNumeric, + NewIdentifierKey[TableName]> + >, columns: K[], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates or updates a single record. If a record exists with the given id, @@ -472,10 +536,13 @@ export abstract class Repository extends Query< * @returns The full persisted record. */ abstract createOrUpdate( - id: Identifier | undefined, - object: Omit, 'id'>, + id: undefined | NewIndentifierValue[TableName]>, + object: Omit< + NewEditableDataWithoutNumeric, + NewIdentifierKey[TableName]> + >, options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates or updates a single record. If a record exists with the given id, @@ -484,10 +551,10 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns Array of the persisted records. */ - abstract createOrUpdate>( - objects: Array, 'id'> & Partial>, + abstract createOrUpdate>( + objects: Array & NewIdentifiable[TableName]>, columns: K[] - ): Promise>[]>; + ): Promise>[]>; /** * Creates or updates a single record. If a record exists with the given id, @@ -496,8 +563,8 @@ export abstract class Repository extends Query< * @returns Array of the persisted records. */ abstract createOrUpdate( - objects: Array, 'id'> & Partial> - ): Promise>[]>; + objects: Array & NewIdentifiable[TableName]> + ): Promise>[]>; /** * Creates or replaces a single record. If a record exists with the given id, @@ -506,11 +573,11 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The full persisted record. */ - abstract createOrReplace>( - object: Omit, 'id'> & Partial, + abstract createOrReplace>( + object: NewEditableDataWithoutNumeric & NewIdentifiable[TableName], columns: K[], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates or replaces a single record. If a record exists with the given id, @@ -519,9 +586,9 @@ export abstract class Repository extends Query< * @returns The full persisted record. */ abstract createOrReplace( - object: Omit, 'id'> & Partial, + object: NewEditableDataWithoutNumeric & NewIdentifiable[TableName], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates or replaces a single record. If a record exists with the given id, @@ -531,12 +598,15 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The full persisted record. */ - abstract createOrReplace>( - id: Identifier | undefined, - object: Omit, 'id'>, + abstract createOrReplace>( + id: undefined | NewIndentifierValue[TableName]>, + object: Omit< + NewEditableDataWithoutNumeric, + NewIdentifierKey[TableName]> + >, columns: K[], options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates or replaces a single record. If a record exists with the given id, @@ -546,10 +616,13 @@ export abstract class Repository extends Query< * @returns The full persisted record. */ abstract createOrReplace( - id: Identifier | undefined, - object: Omit, 'id'>, + id: undefined | NewIndentifierValue[TableName]>, + object: Omit< + NewEditableDataWithoutNumeric, + NewIdentifierKey[TableName]> + >, options?: { ifVersion?: number } - ): Promise>>; + ): Promise>>; /** * Creates or replaces a single record. If a record exists with the given id, @@ -558,10 +631,10 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns Array of the persisted records. */ - abstract createOrReplace>( - objects: Array, 'id'> & Partial>, + abstract createOrReplace>( + objects: Array & NewIdentifiable[TableName]>, columns: K[] - ): Promise>[]>; + ): Promise>[]>; /** * Creates or replaces a single record. If a record exists with the given id, @@ -570,8 +643,8 @@ export abstract class Repository extends Query< * @returns Array of the persisted records. */ abstract createOrReplace( - objects: Array, 'id'> & Partial> - ): Promise>[]>; + objects: Array & NewIdentifiable[TableName]> + ): Promise>[]>; /** * Deletes a record given its unique id. @@ -579,10 +652,10 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The deleted record, null if the record could not be found. */ - abstract delete>( - object: Identifiable & Partial>, + abstract delete>( + object: NewEditableDataWithoutNumeric & NewIdentifiable[TableName], columns: K[] - ): Promise> | null>; + ): Promise> | null>; /** * Deletes a record given its unique id. @@ -590,8 +663,8 @@ export abstract class Repository extends Query< * @returns The deleted record, null if the record could not be found. */ abstract delete( - object: Identifiable & Partial> - ): Promise> | null>; + object: NewEditableDataWithoutNumeric & NewIdentifiable[TableName] + ): Promise> | null>; /** * Deletes a record given a unique id. @@ -599,17 +672,19 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns The deleted record, null if the record could not be found. */ - abstract delete>( - id: Identifier, + abstract delete>( + id: NewIndentifierValue[TableName]>, columns: K[] - ): Promise> | null>; + ): Promise> | null>; /** * Deletes a record given a unique id. * @param id The unique id. * @returns The deleted record, null if the record could not be found. */ - abstract delete(id: Identifier): Promise> | null>; + abstract delete( + id: NewIndentifierValue[TableName]> + ): Promise> | null>; /** * Deletes multiple records given an array of objects with ids. @@ -617,10 +692,10 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns Array of the deleted records in order (if a record could not be found null is returned). */ - abstract delete>( - objects: Array> & Identifiable>, + abstract delete>( + objects: Array & NewIdentifiable[TableName]>, columns: K[] - ): Promise> | null>>; + ): Promise> | null>>; /** * Deletes multiple records given an array of objects with ids. @@ -628,8 +703,8 @@ export abstract class Repository extends Query< * @returns Array of the deleted records in order (if a record could not be found null is returned). */ abstract delete( - objects: Array> & Identifiable> - ): Promise> | null>>; + objects: Array & NewIdentifiable[TableName]> + ): Promise> | null>>; /** * Deletes multiple records given an array of unique ids. @@ -637,17 +712,19 @@ export abstract class Repository extends Query< * @param columns Array of columns to be returned. If not specified, first level columns will be returned. * @returns Array of the deleted records in order (if a record could not be found null is returned). */ - abstract delete>( - objects: Identifier[], + abstract delete>( + objects: NewIndentifierValue[TableName]>[], columns: K[] - ): Promise> | null>>; + ): Promise> | null>>; /** * Deletes multiple records given an array of unique ids. * @param objects An array of ids. * @returns Array of the deleted records in order (if a record could not be found null is returned). */ - abstract delete(objects: Identifier[]): Promise> | null>>; + abstract delete( + objects: NewIndentifierValue[TableName]>[] + ): Promise> | null>>; /** * Deletes a record given its unique id. @@ -656,10 +733,10 @@ export abstract class Repository extends Query< * @returns The deleted record, null if the record could not be found. * @throws If the record could not be found. */ - abstract deleteOrThrow>( - object: Identifiable, + abstract deleteOrThrow>( + object: NewIdentifiable[TableName], columns: K[] - ): Promise>>; + ): Promise>>; /** * Deletes a record given its unique id. @@ -667,7 +744,9 @@ export abstract class Repository extends Query< * @returns The deleted record, null if the record could not be found. * @throws If the record could not be found. */ - abstract deleteOrThrow(object: Identifiable): Promise>>; + abstract deleteOrThrow( + object: NewIdentifiable[TableName] + ): Promise>>; /** * Deletes a record given a unique id. @@ -676,10 +755,10 @@ export abstract class Repository extends Query< * @returns The deleted record, null if the record could not be found. * @throws If the record could not be found. */ - abstract deleteOrThrow>( - id: Identifier, + abstract deleteOrThrow>( + id: NewIndentifierValue[TableName]>, columns: K[] - ): Promise>>; + ): Promise>>; /** * Deletes a record given a unique id. @@ -687,7 +766,9 @@ export abstract class Repository extends Query< * @returns The deleted record, null if the record could not be found. * @throws If the record could not be found. */ - abstract deleteOrThrow(id: Identifier): Promise>>; + abstract deleteOrThrow( + id: NewIndentifierValue[TableName]> + ): Promise>>; /** * Deletes multiple records given an array of objects with ids. @@ -696,10 +777,10 @@ export abstract class Repository extends Query< * @returns Array of the deleted records in order (if a record could not be found null is returned). * @throws If one or more records could not be found. */ - abstract deleteOrThrow>( - objects: Array> & Identifiable>, + abstract deleteOrThrow>( + objects: Array & NewIdentifiable[TableName]>, columns: K[] - ): Promise>>>; + ): Promise>>>; /** * Deletes multiple records given an array of objects with ids. @@ -708,8 +789,8 @@ export abstract class Repository extends Query< * @throws If one or more records could not be found. */ abstract deleteOrThrow( - objects: Array> & Identifiable> - ): Promise>>>; + objects: Array & NewIdentifiable[TableName]> + ): Promise>>>; /** * Deletes multiple records given an array of unique ids. @@ -718,10 +799,10 @@ export abstract class Repository extends Query< * @returns Array of the deleted records in order (if a record could not be found null is returned). * @throws If one or more records could not be found. */ - abstract deleteOrThrow>( - objects: Identifier[], + abstract deleteOrThrow>( + objects: NewIndentifierValue[TableName]>[], columns: K[] - ): Promise>>>; + ): Promise>>>; /** * Deletes multiple records given an array of unique ids. @@ -729,7 +810,9 @@ export abstract class Repository extends Query< * @returns Array of the deleted records in order. * @throws If one or more records could not be found. */ - abstract deleteOrThrow(objects: Identifier[]): Promise>>>; + abstract deleteOrThrow( + objects: NewIndentifierValue[TableName]>[] + ): Promise>>>; /** * Search for records in the table. @@ -743,12 +826,12 @@ export abstract class Repository extends Query< fuzziness?: FuzzinessExpression; prefix?: PrefixExpression; highlight?: HighlightExpression; - filter?: Filter; - boosters?: Boosters[]; + filter?: Filter; + boosters?: Boosters[]; page?: SearchPageConfig; - target?: TargetColumn[]; + target?: TargetColumn[]; } - ): Promise<{ records: SearchXataRecord>[] } & TotalCount>; + ): Promise<{ records: SearchXataRecord>[] } & TotalCount>; /** * Search for vectors in the table. @@ -756,7 +839,7 @@ export abstract class Repository extends Query< * @param query The vector to search for similarities. Must have the same dimension as the vector column used. * @param options The options to search with (like: spaceFunction) */ - abstract vectorSearch>( + abstract vectorSearch>( column: F, query: number[], options?: { @@ -775,66 +858,1500 @@ export abstract class Repository extends Query< * @minimum 1 */ size?: number; - filter?: Filter; + filter?: Filter; + } + ): Promise<{ records: SearchXataRecord>[] } & TotalCount>; + + /** + * Aggregates records in the table. + * @param expression The aggregations to perform. + * @param filter The filter to apply to the queried records. + * @returns The requested aggregations. + */ + abstract aggregate>>( + expression?: Expression, + filter?: Filter + ): Promise>; + + /** + * Experimental: Ask the database to perform a natural language question. + */ + abstract ask(question: string, options?: AskOptions): Promise; + + /** + * Experimental: Ask the database to perform a natural language question. + */ + abstract ask(question: string, options: AskOptions): Promise; + + /** + * Experimental: Ask the database to perform a natural language question. + */ + abstract ask(question: string, options: AskOptions & { onMessage: (message: AskResult) => void }): void; + + abstract query( + query: Query + ): Promise>; +} + +const computePrimaryKey = (schema: Schema, tableName: string): string => { + const table = schema.tables.find((table) => table.name === tableName); + const primaryKeys = (table as any)?.primaryKey ?? []; + if (primaryKeys.length === 1) { + // Throwing an error here if the primary key is not an Int or String instead of silently failing. + const primaryKeyType = table?.columns.find((col) => col.name === primaryKeys[0])?.type; + const validIdXataTypes = ['string', 'text', 'int', 'float']; + if (primaryKeyType && !validIdXataTypes.includes(primaryKeyType)) { + throw new Error( + `Primary key on ${tableName} must be one of type ${validIdXataTypes.join(', ')} to use the Xata SDK.` + ); + } + return primaryKeys[0]; + } else if (primaryKeys.length > 1) { + throw new Error(`Composite primary key on ${tableName} is not supported`); + } else { + const xata_id = table?.columns.find((col) => col.name === 'xata_id' && col.notNull); + if (!xata_id) { + throw new Error( + `Could not find a non composite primary key or xata_id on ${tableName} table. Create a primary key of adapt your table with Xata.` + ); + } + return 'xata_id'; + } +}; + +export class KyselyRepository + extends Query> + implements Repository +{ + #table: string; + #getFetchProps: () => ApiExtraProps; + #db: KyselyPluginResult; + #schema: DatabaseSchema; + #trace: TraceFunction; + #runTransaction: (params: SqlBatchQueryRequestBody) => Promise; + #primaryKey: string; + + constructor(options: { + table: string; + db: SchemaPluginResult; + pluginOptions: XataPluginOptions; + schema: DatabaseSchema; + }) { + super( + null, + { name: options.table, schema: options.schema.tables.find((table) => table.name === options.table) }, + {} + ); + + this.#table = options.table; + this.#db = new KyselyPlugin().build(options.pluginOptions); + // pass plugin options here. + this.#schema = options.schema; + this.#getFetchProps = () => ({ ...options.pluginOptions, sessionID: generateUUID() }); + this.#primaryKey = computePrimaryKey(this.#schema as any, this.#table); + this.#runTransaction = async (body: SqlBatchQueryRequestBody) => { + body.statements.unshift({ + statement: 'BEGIN', + params: [] + }); + body.statements.push({ + statement: 'COMMIT', + params: [] + }); + const { results } = await sqlBatchQuery({ + pathParams: { + workspace: '{workspaceId}', + dbBranchName: '{dbBranch}', + region: '{region}' + }, + ...this.#getFetchProps(), + body + }); + return results.flatMap((result) => { + if (result.warning) console.warn(result.warning); + return (result as Schemas.SQLResponseJSON).records?.map((record) => record) ?? []; + }) as any; + }; + + const trace = options.pluginOptions.trace ?? defaultTrace; + this.#trace = async ( + name: string, + fn: (options: { setAttributes: (attrs: AttributeDictionary) => void }) => T, + options: AttributeDictionary = {} + ) => { + return trace(name, fn, { + ...options, + [TraceAttributes.TABLE]: this.#table, + [TraceAttributes.KIND]: 'sdk-operation', + [TraceAttributes.VERSION]: VERSION + }); + }; + } + + async create>( + object: NewEditableDataWithoutNumeric & Partial[TableName]>, + columns: K[] + ): Promise>>; + async create( + object: NewEditableDataWithoutNumeric & Partial[TableName]> + ): Promise>>; + async create>( + id: NewIndentifierValue[TableName]>, + object: NewEditableDataWithoutNumeric, + columns: K[] + ): Promise>>; + async create( + id: NewIndentifierValue[TableName]>, + object: NewEditableDataWithoutNumeric + ): Promise>>; + async create>( + objects: Array & Partial[TableName]>>, + columns: K[] + ): Promise>[]>; + async create( + objects: Array & Partial[TableName]>> + ): Promise>[]>; + async create>( + a: + | NewIndentifierValue[TableName]> + | (NewEditableDataWithoutNumeric & Partial[TableName]>) + | Array & Partial[TableName]>>, + b?: NewEditableDataWithoutNumeric | K[], + c?: K[] + ): Promise< + | Readonly> + | Readonly>[] + | Readonly> + | Readonly>[] + > { + return this.#trace('create', async () => { + // Create many records + if (Array.isArray(a)) { + if (a.length === 0) return []; + const records = await this.#insertRecords(a, { createOnly: true }); + const columns = isValidSelectableColumns(b) ? b : (['*'] as K[]); + + // TODO: Transaction API does not support column projection + const result = await this.read(records as NewIdentifiable[TableName][], columns); + return result; + } + + // Create one record with id as param + if (isStringOrNumber(a) && isObject(b)) { + if (a === '') throw new Error("The id can't be empty"); + + const columns = isValidSelectableColumns(c) ? c : undefined; + return await this.#insertRecordWithId(a as any, b as NewEditableData, columns, { + createOnly: true + }); + } + + // Create one record with id as property + if (isObject(a) && isStringOrNumber((a as any)[this.#primaryKey])) { + if ((a as any)[this.#primaryKey] === '') { + throw new Error("The id can't be empty"); + } + const columns = isValidSelectableColumns(b) ? b : undefined; + return await this.#insertRecordWithId( + (a as any)[this.#primaryKey], + { ...a, [this.#primaryKey]: undefined } as NewEditableData, + columns, + { + createOnly: true + } + ); + } + + // Create one record without id + if (isObject(a)) { + const columns = isValidSelectableColumns(b) ? b : undefined; + return await this.#insertRecordWithoutId(a as any, columns); + } + throw new Error('Invalid arguments for create method'); + }); + } + + async #insertRecordWithoutId(object: NewEditableData, columns: SelectableColumn[] = ['*']) { + const record = await this.#transformObjectToApi(object); + + let statement: InsertQueryBuilder = this.#db.insertInto(this.#table); + if (Object.keys(record).length === 0) { + statement = statement.defaultValues(); + } else { + statement = statement.values(record); + } + if (selectAllColumns(columns)) { + statement = statement.returningAll(); + } else { + statement = statement.returning(columns); + } + const response = await statement.executeTakeFirst(); + + return initObjectKysely(this, this.#schema, this.#primaryKey, this.#table, response, columns); + } + + async #insertRecordWithId( + recordId: NewIndentifierValue[TableName]>, + object: NewEditableData, + columns: SelectableColumn[] = ['*'], + { createOnly }: { createOnly: boolean } + ) { + if (!recordId) return null; + + const record = await this.#transformObjectToApi(object); + + let statement: InsertQueryBuilder = this.#db + .insertInto(this.#table) + .values({ ...record, [this.#primaryKey]: recordId }); + + if (selectAllColumns(columns)) { + statement = statement.returningAll(); + } else { + statement = statement.returning(columns); + } + if (!createOnly) { + // any fields that are not in the record should be set to null + const fieldsToSetNull = await this.#transformObjectToApiAllFields(record); + statement = statement.onConflict((oc) => + oc.column(this.#primaryKey).doUpdateSet({ ...fieldsToSetNull, ...record, [this.#primaryKey]: recordId }) + ); + } + + const response = await statement.executeTakeFirst().catch((e) => { + if (e.status === 400 && e.message.includes('constraint violation')) { + e.status = 422; + } + throw e; + }); + + return initObjectKysely(this, this.#schema, this.#primaryKey, this.#table, response, columns) as any; + } + + async #insertRecords(objects: NewEditableData[], { createOnly }: { createOnly: boolean }) { + const operations = await promiseMap(objects, async (object) => await this.#transformObjectToApi(object)); + + const statements: SqlBatchQueryRequestBody['statements'] = []; + for (const operation of operations) { + let statement: InsertQueryBuilder = this.#db + .insertInto(this.#table) + .values(operation) + .returningAll(); + if (!createOnly) { + // any fields that are not in the record should be set to null + const fieldsToSetNull = await this.#transformObjectToApiAllFields(operation); + statement = statement.onConflict((oc) => + oc.column(this.#primaryKey).doUpdateSet({ ...fieldsToSetNull, ...operation }) + ); + } + statements.push({ statement: statement.compile().sql, params: statement.compile().parameters as any[] }); + } + + const results = await this.#runTransaction({ statements }); + + return results; + } + + async read>( + id: NewIndentifierValue[TableName]>, + columns: K[] + ): Promise | null>>; + async read( + id: NewIndentifierValue[TableName]> + ): Promise | null>>; + async read>( + ids: ReadonlyArray[TableName]>>, + columns: K[] + ): Promise> | null>>; + async read( + ids: ReadonlyArray[TableName]>> + ): Promise> | null>>; + async read>( + object: NewIdentifiable[TableName], + columns: K[] + ): Promise | null>>; + async read( + object: NewIdentifiable[TableName] + ): Promise | null>>; + async read>( + objects: NewIdentifiable[TableName][], + columns: K[] + ): Promise> | null>>; + async read( + objects: NewIdentifiable[TableName][] + ): Promise> | null>>; + async read>( + a: + | NewIndentifierValue[TableName]> + | ReadonlyArray[TableName]>> + | NewIdentifiable[TableName] + | NewIdentifiable[TableName][], + b?: K[] + ): Promise< + | Readonly> + | Array> | null> + | Readonly> + | Array> | null> + | null + > { + return this.#trace('read', async () => { + const columns = isValidSelectableColumns(b) ? b : ['*' as const]; + // Read many records + if (Array.isArray(a)) { + if (a.length === 0) return []; + + const ids = a.map((item) => extractIdKysely(item, this.#primaryKey)); + + const finalObjects = await this.getAll({ filter: { [this.#primaryKey]: { $any: compact(ids) } }, columns }); + // Maintain order of objects + const dictionary = finalObjects.reduce((acc, object) => { + acc[(object as any)[this.#primaryKey]] = object; + return acc; + }, {} as Dictionary); + + return ids.map((id) => dictionary[id ?? ('' as any)] ?? null); + } + + // Read one record + const id = extractIdKysely(a, this.#primaryKey); + if (id) { + try { + let statement: SelectQueryBuilder = this.#db + .selectFrom(this.#table) + .where(this.#primaryKey, '=', id); + + statement = generateSelectStatement({ + columnData: (this.#schema?.tables.find((table) => table.name === this.#table)?.columns as any) ?? [], + filter: {}, + columns, + stmt: statement, + schema: this.#schema as any, + primaryKey: this.#primaryKey, + tableName: this.#table, + db: this.#db + }); + + const response = await statement.executeTakeFirst(); + if (!response) return null; + return initObjectKysely( + this, + this.#schema, + this.#primaryKey, + this.#table, + response, + columns as SelectableColumn[] + ) as any; + } catch (e) { + if (isObject(e) && e.status === 404) { + return null; + } + + throw e; + } + } + + return null; + }); + } + + async readOrThrow>( + id: NewIndentifierValue[TableName]>, + columns: K[] + ): Promise>>; + async readOrThrow( + id: NewIndentifierValue[TableName]> + ): Promise>>; + async readOrThrow>( + ids: ReadonlyArray[TableName]>>, + columns: K[] + ): Promise>>>; + async readOrThrow( + ids: ReadonlyArray[TableName]>> + ): Promise>>>; + async readOrThrow>( + object: NewIdentifiable[TableName], + columns: K[] + ): Promise>>; + async readOrThrow( + object: NewIdentifiable[TableName] + ): Promise>>; + async readOrThrow>( + objects: NewIdentifiable[TableName][], + columns: K[] + ): Promise>>>; + async readOrThrow( + objects: NewIdentifiable[TableName][] + ): Promise>>>; + async readOrThrow>( + a: + | NewIndentifierValue[TableName]> + | ReadonlyArray[TableName]>> + | NewIdentifiable[TableName] + | NewIdentifiable[TableName][], + b?: K[] + ): Promise< + | Readonly> + | Readonly>[] + | Readonly> + | Readonly>[] + > { + return this.#trace('readOrThrow', async () => { + const result = await this.read(a as any, b as any); + + if (Array.isArray(result)) { + const missingIds = compact( + (a as Array[TableName]>) + .filter((_item, index) => result[index] === null) + .map((item) => extractIdKysely(item, this.#primaryKey)) + ); + + if (missingIds.length > 0) { + throw new Error(`Could not find records with ids: ${missingIds.join(', ')}`); + } + + return result as any; + } + + if (result === null) { + const id = extractIdKysely(a, this.#primaryKey) ?? 'unknown'; + throw new Error(`Record with id ${id} not found`); + } + + return result; + }); + } + + async update>( + object: Partial> & NewIdentifiable[TableName], + columns: K[] + ): Promise> | null>; + async update( + object: Partial> & NewIdentifiable[TableName] + ): Promise> | null>; + async update>( + id: NewIndentifierValue[TableName]>, + object: Partial>, + columns: K[] + ): Promise> | null>; + async update( + id: NewIndentifierValue[TableName]>, + object: Partial> + ): Promise> | null>; + async update>( + objects: Array> & NewIdentifiable[TableName]>, + columns: K[] + ): Promise> | null>>; + async update( + objects: Array> & NewIdentifiable[TableName]> + ): Promise> | null>>; + async update>( + a: + | NewIndentifierValue[TableName]> + | (Partial> & NewIdentifiable[TableName]) + | Array> & NewIdentifiable[TableName]>, + b?: Partial> | K[], + c?: K[] + ): Promise< + | Readonly> + | Array> | null> + | Readonly> + | Array> | null> + | null + > { + return this.#trace('update', async () => { + // Update many records + if (Array.isArray(a)) { + if (a.length === 0) return []; + + // TODO: Transaction API fails fast if one of the records is not found + const existing = await this.read(a, [this.#primaryKey] as SelectableColumn[]); + const updates = a.filter((_item, index) => (existing as any)[index] !== null); + + await this.#updateRecords(updates as any, { + upsert: false + }); + + const columns = isValidSelectableColumns(b) ? b : (['*'] as K[]); + + // TODO: Transaction API does not support column projection + const result = await this.read(a, columns); + return result; + } + + try { + // Update one record with id as param + if (isStringOrNumber(a) && isObject(b)) { + const columns = isValidSelectableColumns(c) ? c : undefined; + return await this.#updateRecordWithID(a as any, b as any, columns); + } + + // Update one record with id as property + if (isObject(a) && isStringOrNumber(a[this.#primaryKey])) { + const columns = isValidSelectableColumns(b) ? b : undefined; + return await this.#updateRecordWithID( + a[this.#primaryKey] as any, + { ...(a as any), [this.#primaryKey]: undefined } as any, + columns + ); + } + } catch (error: any) { + if (error.status === 422) return null; + throw error; + } + + throw new Error('Invalid arguments for update method'); + }); + } + + async updateOrThrow>( + object: Partial> & NewIdentifiable[TableName], + columns: K[] + ): Promise>>; + async updateOrThrow( + object: Partial> & NewIdentifiable[TableName] + ): Promise>>; + async updateOrThrow>( + id: NewIndentifierValue[TableName]>, + object: Partial>, + columns: K[] + ): Promise>>; + async updateOrThrow( + id: NewIndentifierValue[TableName]>, + object: Partial> + ): Promise>>; + async updateOrThrow>( + objects: Array> & NewIdentifiable[TableName]>, + columns: K[] + ): Promise>[]>; + async updateOrThrow( + objects: Array> & NewIdentifiable[TableName]> + ): Promise>[]>; + async updateOrThrow>( + a: + | NewIndentifierValue[TableName]> + | (Partial> & NewIdentifiable[TableName]) + | Array> & NewIdentifiable[TableName]>, + b?: Partial> | K[], + c?: K[] + ): Promise< + | Readonly> + | Array>> + | Readonly> + | Array>> + > { + return this.#trace('updateOrThrow', async () => { + const result = await this.update(a as any, b as any, c as any); + + if (Array.isArray(result)) { + const missingIds = compact( + (a as Array[TableName]>) + .filter((_item, index) => result[index] === null) + .map((item) => extractIdKysely(item, this.#primaryKey)) + ); + + if (missingIds.length > 0) { + throw new Error(`Could not find records with ids: ${missingIds.join(', ')}`); + } + + return result as any; + } + + if (result === null) { + const id = extractIdKysely(a, this.#primaryKey) ?? 'unknown'; + throw new Error(`Record with id ${id} not found`); + } + + return result; + }); + } + + async #updateRecordWithID( + recordId: NewIndentifierValue[TableName]>, + object: Partial>, + columns: SelectableColumn[] = ['*'] + ) { + if (!recordId) return null; + + // Ensure id is not present in the update payload + const { [this.#primaryKey]: _id, ...record } = await this.#transformObjectToApi(object); + + const numericOperations: NumericOperations[] = extractNumericOperations({ numericFilters: record }); + try { + let statement: UpdateQueryBuilder = this.#db + .updateTable(this.#table) + .where(this.#primaryKey, '=', recordId); + + if (Object.keys(record).length > 0) { + statement = statement.set(record); + } + + if (numericOperations.length > 0) { + for (const { field, operator, value } of numericOperations) { + statement = statement.set((eb) => ({ [field]: eb(field, operatorMap[operator], value) })); + } + } + if (selectAllColumns(columns)) { + statement = statement.returningAll(); + } else { + statement = statement.returning(columns); + } + const response = await statement.executeTakeFirst(); + if (!response) return null; + + return initObjectKysely(this, this.#schema, this.#primaryKey, this.#table, response, columns) as any; + } catch (e) { + if (isObject(e) && e.status === 404) { + return null; + } + + throw e; + } + } + + async #updateRecords( + objects: Array> & NewIdentifiable[TableName]>, + { upsert }: { upsert: boolean } + ) { + const operations = await promiseMap(objects, async (object) => { + const fields = await this.#transformObjectToApi(object); + return fields; + }); + const statements: SqlBatchQueryRequestBody['statements'] = []; + + for (const operation of operations) { + const { [this.#primaryKey]: id, ...fields } = operation; + + if (upsert) { + const numericOperations: NumericOperations[] = extractNumericOperations({ numericFilters: fields }); + let statement: InsertQueryBuilder = this.#db + .insertInto(this.#table) + .onConflict((oc) => oc.column(this.#primaryKey).doUpdateSet(fields)) + .returningAll(); + statement = + Object.keys(fields).length === 0 + ? statement.defaultValues() + : statement.values({ ...fields, [this.#primaryKey]: id }); + if (numericOperations.length > 0) { + for (const { field, operator, value } of numericOperations) { + statement = statement.values((eb) => ({ [field]: eb(field, operatorMap[operator], value) })); + } + } + statements.push({ + statement: statement.compile().sql, + params: statement.compile().parameters as any[] + }); + } else { + const numericOperations: NumericOperations[] = extractNumericOperations({ numericFilters: fields }); + let statement: UpdateQueryBuilder = this.#db + .updateTable(this.#table) + .where(this.#primaryKey, '=', id as string) + .returningAll(); + if (Object.keys(fields).length > 0) { + statement = statement.set(fields); + } + if (numericOperations.length > 0) { + for (const { field, operator, value } of numericOperations) { + statement = statement.set((eb) => ({ [field]: eb(field, operatorMap[operator], value) })); + } + } + statements.push({ + statement: statement.compile().sql, + params: statement.compile().parameters as any[] + }); + } + } + + const results = await this.#runTransaction({ statements }); + return results; + } + + async createOrUpdate>( + object: NewEditableData & Partial[TableName]>, + columns: K[] + ): Promise>>; + async createOrUpdate( + object: NewEditableData & Partial[TableName]> + ): Promise>>; + async createOrUpdate>( + id: NewIndentifierValue[TableName]>, + object: Omit, NewIdentifierKey[TableName]>>, + columns: K[] + ): Promise>>; + async createOrUpdate( + id: NewIndentifierValue[TableName]>, + object: Omit, NewIdentifierKey[TableName]>> + ): Promise>>; + async createOrUpdate>( + objects: Array & Partial[TableName]>>, + columns: K[] + ): Promise>[]>; + async createOrUpdate( + objects: Array & Partial[TableName]>> + ): Promise>[]>; + async createOrUpdate>( + a: + | NewIndentifierValue[TableName]> + | NewEditableData + | NewEditableData[], + b?: + | NewEditableData + | Omit, NewIdentifierKey[TableName]>> + | K[], + c?: K[] + ): Promise< + | Readonly> + | Array>> + | Readonly> + | Array>> + > { + return this.#trace('createOrUpdate', async () => { + // Create or update many records + if (Array.isArray(a)) { + if (a.length === 0) return []; + + await this.#updateRecords(a as any, { + upsert: true + }); + + const columns = isValidSelectableColumns(b) ? b : (['*'] as K[]); + + // TODO: Transaction API does not support column projection + const result = await this.read(a as any[], columns); + return result; + } + + // Create or update one record with id as param + if (isStringOrNumber(a) && isObject(b)) { + if (a === '') throw new Error("The id can't be empty"); + + const columns = isValidSelectableColumns(c) ? c : undefined; + return await this.#upsertRecordWithID(a as any, b as any, columns); + } + + // Create or update one record with id as property + if (isObject(a) && isStringOrNumber((a as any)[this.#primaryKey])) { + if ((a as any)[this.#primaryKey] === '') throw new Error("The id can't be empty"); + + const columns = isValidSelectableColumns(c) ? c : undefined; + return await this.#upsertRecordWithID( + (a as any)[this.#primaryKey], + { ...a, [this.#primaryKey]: undefined } as any, + columns + ); + } + + // Create with undefined id as param + if (!isDefined(a) && isObject(b)) { + return await this.create(b as any, c as K[]); + } + + // Create with undefined id as property + if (isObject(a) && !isDefined((a as any)[this.#primaryKey])) { + return await this.create(a as any, b as K[]); + } + + throw new Error('Invalid arguments for createOrUpdate method'); + }); + } + + async #upsertRecordWithID( + recordId: NewIndentifierValue[TableName]>, + object: Omit, 'xata_id'>, + columns: SelectableColumn[] = ['*'] + ) { + if (!recordId) return null; + + const updates = Object.fromEntries(Object.entries(object).map(([key, value]) => [key, value])); + let statement: InsertQueryBuilder = this.#db + .insertInto(this.#table) + .values({ ...object, [this.#primaryKey]: recordId }) + .onConflict((oc) => oc.column(this.#primaryKey).doUpdateSet(updates)); + if (selectAllColumns(columns)) { + statement = statement.returningAll(); + } else { + statement = statement.returning(columns); + } + const response = await statement.executeTakeFirst(); + + return initObjectKysely(this, this.#schema, this.#primaryKey, this.#table, response, columns) as any; + } + + async createOrReplace>( + object: NewEditableData & Partial[TableName]>, + columns: K[] + ): Promise>>; + async createOrReplace( + object: NewEditableData & Partial[TableName]> + ): Promise>>; + async createOrReplace>( + id: NewIndentifierValue[TableName]> | undefined, + object: Omit, NewIdentifierKey[TableName]>>, + columns: K[] + ): Promise>>; + async createOrReplace( + id: NewIndentifierValue[TableName]> | undefined, + object: Omit, NewIdentifierKey[TableName]>> + ): Promise>>; + async createOrReplace>( + objects: Array & Partial[TableName]>>, + columns: K[] + ): Promise>[]>; + async createOrReplace( + objects: Array & Partial[TableName]>> + ): Promise>[]>; + async createOrReplace>( + a: + | NewIndentifierValue[TableName]> + | NewEditableData + | NewEditableData[] + | undefined, + b?: + | NewEditableData + | Omit, NewIdentifierKey[TableName]>> + | K[], + c?: K[] + ): Promise< + | Readonly> + | Array>> + | Readonly> + | Array>> + > { + return this.#trace('createOrReplace', async () => { + // Create or replace many records + if (Array.isArray(a)) { + if (a.length === 0) return []; + + const records = await this.#insertRecords(a, { createOnly: false }); + + const columns = isValidSelectableColumns(b) ? b : (['*'] as K[]); + + // TODO: Transaction API does not support column projection + const result = await this.read(records as NewIdentifiable[TableName][], columns); + return result; + } + + // Create or replace one record with id as param + if (isStringOrNumber(a) && isObject(b)) { + if (a === '') throw new Error("The id can't be empty"); + + const columns = isValidSelectableColumns(c) ? c : undefined; + return await this.#insertRecordWithId(a as any, b as NewEditableData, columns, { + createOnly: false + }); + } + + // Create or replace one record with id as property + if (isObject(a) && isStringOrNumber((a as any)[this.#primaryKey])) { + if ((a as any)[this.#primaryKey] === '') throw new Error("The id can't be empty"); + + const columns = isValidSelectableColumns(c) ? c : undefined; + return await this.#insertRecordWithId( + (a as any)[this.#primaryKey], + { ...a, [this.#primaryKey]: undefined } as any, + columns, + { + createOnly: false + } + ); + } + + // Create with undefined id as param + if (!isDefined(a) && isObject(b)) { + return await this.create(b as any, c as K[]); + } + + // Create with undefined id as property + if (isObject(a) && !isDefined((a as any)[this.#primaryKey])) { + return await this.create(a as any, b as K[]); + } + + throw new Error('Invalid arguments for createOrReplace method'); + }); + } + + async deleteOrThrow>( + object: NewIdentifiable[TableName], + columns: K[] + ): Promise>>; + async deleteOrThrow( + object: NewIdentifiable[TableName] + ): Promise>>; + async deleteOrThrow>( + id: NewIndentifierValue[TableName]>, + columns: K[] + ): Promise>>; + async deleteOrThrow( + id: NewIndentifierValue[TableName]> + ): Promise>>; + async deleteOrThrow>( + objects: Array> & NewIdentifiable[TableName]>, + columns: K[] + ): Promise>>>; + async deleteOrThrow( + objects: Array> & NewIdentifiable[TableName]> + ): Promise>>>; + async deleteOrThrow>( + objects: NewIndentifierValue[TableName]>[], + columns: K[] + ): Promise>>>; + async deleteOrThrow( + objects: NewIndentifierValue[TableName]>[] + ): Promise>>>; + async deleteOrThrow>( + a: + | NewIndentifierValue[TableName]> + | NewIdentifiable[TableName] + | Array< + | NewIndentifierValue[TableName]> + | NewIdentifiable[TableName] + >, + b?: K[] + ): Promise< + | Readonly> + | Array>> + | Readonly> + | Array>> + > { + return this.#trace('deleteOrThrow', async () => { + const result = await this.delete(a as any, b as any); + + if (Array.isArray(result)) { + const missingIds = compact( + (a as Array[TableName]>) + .filter((_item, index) => result[index] === null) + .map((item) => extractIdKysely(item, this.#primaryKey)) + ); + + if (missingIds.length > 0) { + throw new Error(`Could not find records with ids: ${missingIds.join(', ')}`); + } + + return result as any; + } else if (result === null) { + const id = extractIdKysely(a, this.#primaryKey) ?? 'unknown'; + throw new Error(`Record with id ${id} not found`); + } + + return result; + }); + } + + async delete>( + object: Partial> & NewIdentifiable[TableName], + columns: K[] + ): Promise> | null>; + async delete( + object: Partial> & NewIdentifiable[TableName] + ): Promise> | null>; + async delete>( + id: NewIndentifierValue[TableName]>, + columns: K[] + ): Promise> | null>; + async delete( + id: NewIndentifierValue[TableName]> + ): Promise> | null>; + async delete>( + objects: Array> & NewIdentifiable[TableName]>, + columns: K[] + ): Promise> | null>>; + async delete( + objects: Array> & NewIdentifiable[TableName]> + ): Promise> | null>>; + async delete>( + objects: NewIndentifierValue[TableName]>[], + columns: K[] + ): Promise> | null>>; + async delete( + objects: NewIndentifierValue[TableName]>[] + ): Promise> | null>>; + async delete>( + a: + | NewIndentifierValue[TableName]> + | NewIdentifiable[TableName] + | Array< + | NewIndentifierValue[TableName]> + | NewIdentifiable[TableName] + >, + b?: K[] + ): Promise< + | Readonly> + | Array> | null> + | Readonly> + | Array> | null> + | null + > { + return this.#trace('delete', async () => { + // Delete many records + if (Array.isArray(a)) { + if (a.length === 0) return []; + + const ids = a.map((o) => { + if (isStringOrNumber(o)) return o; + if (isStringOrNumber(o[this.#primaryKey])) return o[this.#primaryKey]; + throw new Error('Invalid arguments for delete method'); + }); + + const columns = isValidSelectableColumns(b) ? b : (['*'] as K[]); + + // TODO: Transaction API does not support column projection + const result = await this.read(a as any, columns); + + await this.#deleteRecords(ids as any); + + return result; + } + + // Delete one record with id as param + if (isStringOrNumber(a)) { + return await this.#deleteRecord(a as any, b); + } + + // Delete one record with id as property + if (isObject(a) && isStringOrNumber(a[this.#primaryKey])) { + return await this.#deleteRecord((a as any)[this.#primaryKey], b); + } + + throw new Error('Invalid arguments for delete method'); + }); + } + + async #deleteRecord( + recordId: NewIndentifierValue[TableName]>, + columns: SelectableColumn[] = ['*'] + ) { + if (!recordId) return null; + + try { + let statement: DeleteQueryBuilder = this.#db + .deleteFrom(this.#table) + .where(this.#primaryKey, '=', recordId); + if (selectAllColumns(columns)) { + statement = statement.returningAll(); + } else { + statement = statement.returning(columns); + } + const response = await statement.executeTakeFirst(); + if (!response) return null; + return initObjectKysely(this, this.#schema, this.#primaryKey, this.#table, response, columns) as any; + } catch (e) { + if (isObject(e) && e.status === 404) { + return null; + } + + throw e; + } + } + + async #deleteRecords(recordIds: NewIndentifierValue[TableName]>[]) { + const statements: SqlBatchQueryRequestBody['statements'] = recordIds.map((id) => { + const statement = this.#db.deleteFrom(this.#table).where(this.#primaryKey, '=', id); + return { + statement: statement.compile().sql, + params: statement.compile().parameters as any[] + }; + }); + + return await this.#runTransaction({ + statements + }); + } + + async search( + query: string, + options: { + fuzziness?: FuzzinessExpression; + prefix?: PrefixExpression; + highlight?: HighlightExpression; + filter?: Filter; + boosters?: Boosters[]; + page?: SearchPageConfig; + target?: TargetColumn[]; + } = {} + ) { + return this.#trace('search', async () => { + const { records, totalCount } = await searchTable({ + pathParams: { + workspace: '{workspaceId}', + dbBranchName: '{dbBranch}', + region: '{region}', + tableName: this.#table + }, + body: { + query, + fuzziness: options.fuzziness, + prefix: options.prefix, + highlight: options.highlight, + filter: options.filter as Schemas.FilterExpression, + boosters: options.boosters as Schemas.BoosterExpression[], + page: options.page, + target: options.target as Schemas.TargetExpression + }, + ...this.#getFetchProps() + }); + + // TODO - Column selection not supported by search endpoint yet + return { + records: records.map((item) => + initObjectKysely(this, this.#schema, this.#primaryKey, this.#table, item, ['*']) + ) as any, + totalCount + }; + }); + } + + async vectorSearch>( + column: F, + query: number[], + options?: + | { + similarityFunction?: string | undefined; + size?: number | undefined; + filter?: Filter | undefined; + } + | undefined + ): Promise<{ records: SearchXataRecord>[] } & TotalCount> { + return this.#trace('vectorSearch', async () => { + const { records, totalCount } = await vectorSearchTable({ + pathParams: { + workspace: '{workspaceId}', + dbBranchName: '{dbBranch}', + region: '{region}', + tableName: this.#table + }, + body: { + column, + queryVector: query, + similarityFunction: options?.similarityFunction, + size: options?.size, + filter: options?.filter as Schemas.FilterExpression + }, + ...this.#getFetchProps() + }); + + // TODO - Column selection not supported by search endpoint yet + return { + records: records.map((item) => + initObjectKysely(this, this.#schema, this.#primaryKey, this.#table, item, ['*']) + ), + totalCount + } as any; + }); + } + + async aggregate>>( + aggs?: Expression, + filter?: Filter + ) { + return this.#trace('aggregate', async () => { + const result = await aggregateTable({ + pathParams: { + workspace: '{workspaceId}', + dbBranchName: '{dbBranch}', + region: '{region}', + tableName: this.#table + }, + body: { aggs, filter: filter as Schemas.FilterExpression }, + ...this.#getFetchProps() + }); + + return result as any; + }); + } + + async query( + query: Query + ): Promise> { + return this.#trace('query', async () => { + const data = query.getQueryOptions(); + + const cursorAfter = (data?.pagination as { after: string })?.after + ? (decode((data?.pagination as { after: string }).after) as CursorNavigationDecoded) + : undefined; + + const cursorBefore = (data?.pagination as { before: string })?.before + ? (decode((data?.pagination as { before: string }).before) as CursorNavigationDecoded) + : undefined; + + const cursorStart = (data?.pagination as { start: string })?.start + ? (decode((data?.pagination as { start: string }).start) as CursorNavigationDecoded) + : undefined; + + const cursorEnd = (data?.pagination as { end: string })?.end + ? (decode((data?.pagination as { end: string }).end) as CursorNavigationDecoded) + : undefined; + + const cursor = cursorAfter ?? cursorBefore ?? cursorStart ?? cursorEnd; + + const filter = cleanFilter(data.filter) ?? cleanFilter(cursor?.data?.filter); + const sort = data.sort + ? buildSortFilter(data.sort) + : cursor?.data.sort + ? buildSortFilter(cursor?.data?.sort) + : undefined; + const size = data?.pagination?.size ?? cursor?.data?.pagination?.size ?? PAGINATION_DEFAULT_SIZE; + const offset = data?.pagination?.offset ?? cursor?.data?.pagination?.offset ?? PAGINATION_DEFAULT_OFFSET; + + const columnData = this.#schema?.tables.find((table) => table.name === this.#table)?.columns ?? []; + + if (size && size > PAGINATION_MAX_SIZE) throw new Error(`page size exceeds max limit of ${PAGINATION_MAX_SIZE}`); + if (offset && offset > PAGINATION_MAX_OFFSET) + throw new Error(`page offset must not exceed ${PAGINATION_MAX_OFFSET}`); + if (data.sort && cursor) throw new Error('sort and cursor cannot be used together'); + + let statement = this.#db.selectFrom(this.#table); + + statement = generateSelectStatement({ + columnData: columnData as any[], + filter, + columns: data.columns as any[], + stmt: statement, + schema: this.#schema as any, + primaryKey: this.#primaryKey, + tableName: this.#table, + db: this.#db + }); + + if (size) { + statement = statement.limit(size); + } + + const buildSortStatement = (sort: ApiSortFilter[]) => { + const sortStatement = (statement: SelectQueryBuilder, column: string, order: string) => { + if (order === 'random') { + return statement.orderBy(sql`random()`); + } + return statement.orderBy(column === '*' ? `${this.#primaryKey}` : `${column}`, order as SortDirection); + }; + for (const element of sort) { + if (isSortFilterObject(element)) { + statement = sortStatement(statement, `${element.column}`, element.direction ?? 'asc'); + } else { + const keys = Object.keys(element); + for (const key of keys) { + statement = sortStatement(statement, `${key}`, (element as any)[key]); + } + } + } + }; + + if (sort) { + buildSortStatement(Array.isArray(sort) ? sort : [sort]); + } else { + // Necessary for cursor pagination + // TODO can you order by link fields? + statement = statement.orderBy(`${this.#primaryKey}`, 'asc'); + } + + if (offset) { + statement = statement.offset(offset); + } + + if (cursorAfter) { + statement = statement.where(this.#primaryKey, '>', cursorAfter.lastSeenId); + } + if (cursorBefore) { + statement = statement.where(this.#primaryKey, '<', cursorBefore.lastSeenId); + } + if (cursorStart) { + statement = statement.orderBy(this.#primaryKey, 'asc'); + } + if (cursorEnd) { + statement = statement.orderBy(this.#primaryKey, 'desc'); + } + + const response: { + [key: string]: unknown; + }[] = (await this.#db.executeQuery(statement)).rows; + + const lastSeenId: string = response.length > 0 ? (response[response.length - 1][this.#primaryKey] as string) : ''; + + const nextItem: { + [key: string]: unknown; + }[] = (await this.#db.executeQuery(statement.clearLimit().clearOffset().offset(response.length).limit(1))).rows; + + const records = response + .filter((record) => Object.keys(record).length > 0) + .map((record) => + initObjectKysely( + this, + this.#schema, + this.#primaryKey, + this.#table, + record, + (data.columns as SelectableColumn[]) ?? ['*'] + ) + ); + const meta = { + page: { + more: nextItem.length > 0, + size, + cursor: Cursor.from({ + lastSeenId: lastSeenId, + data: { + ...data, + pagination: { + size, + offset + } + } + }).toString() + } + }; + return new Page(query, meta, records); + }); + } + + async summarizeTable( + query: Query, + summaries?: Dictionary>, + summariesFilter?: Schemas.FilterExpression + ) { + return this.#trace('summarize', async () => { + const data = query.getQueryOptions(); + + const result = await summarizeTable({ + pathParams: { + workspace: '{workspaceId}', + dbBranchName: '{dbBranch}', + region: '{region}', + tableName: this.#table + }, + body: { + filter: cleanFilter(data.filter), + sort: data.sort !== undefined ? buildSortFilter(data.sort) : undefined, + columns: data.columns as SelectableColumn[], + consistency: data.consistency, + page: data.pagination?.size !== undefined ? { size: data.pagination?.size } : undefined, + summaries, + summariesFilter + }, + ...this.#getFetchProps() + }); + return { + ...result, + summaries: result.summaries.map((summary) => + initObjectKysely(this, this.#schema, this.#primaryKey, this.#table, summary, data.columns ?? []) + ) + }; + }); + } + + ask(question: string, options?: AskOptions & { onMessage?: (message: AskResult) => void }): any { + // Ask with session uses message, ask without session uses question param + const questionParam = options?.sessionId ? { message: question } : { question }; + const params = { + pathParams: { + workspace: '{workspaceId}', + dbBranchName: '{dbBranch}', + region: '{region}', + tableName: this.#table, + sessionId: options?.sessionId + }, + body: { + ...questionParam, + rules: options?.rules, + searchType: options?.searchType, + search: options?.searchType === 'keyword' ? options?.search : undefined, + vectorSearch: options?.searchType === 'vector' ? options?.vectorSearch : undefined + }, + ...this.#getFetchProps() + }; + + if (options?.onMessage) { + fetchSSERequest({ + endpoint: 'dataPlane', + url: '/db/{dbBranchName}/tables/{tableName}/ask/{sessionId}', + method: 'POST', + onMessage: (message: { text: string; records: string[] }) => { + options.onMessage?.({ answer: message.text, records: message.records }); + }, + ...params + }); + } else { + return askTableSession(params as any); } - ): Promise<{ records: SearchXataRecord>[] } & TotalCount>; + } - /** - * Aggregates records in the table. - * @param expression The aggregations to perform. - * @param filter The filter to apply to the queried records. - * @returns The requested aggregations. - */ - abstract aggregate>>( - expression?: Expression, - filter?: Filter - ): Promise>; + async #transformObjectToApiAllFields(object: any): Promise { + const schema = this.#schema.tables.find((table) => table.name === this.#table); + if (!schema) throw new Error(`Table ${this.#table} not found in schema`); - /** - * Experimental: Ask the database to perform a natural language question. - */ - abstract ask(question: string, options?: AskOptions): Promise; + const result: Dictionary = {}; - /** - * Experimental: Ask the database to perform a natural language question. - */ - abstract ask(question: string, options: AskOptions): Promise; + for (const column of schema.columns) { + // Ignore internal properties + if (['xata_version', 'xata_createdat', 'xata_updatedat'].includes(column.name)) continue; + if (Object.keys(object).includes(column.name)) continue; - /** - * Experimental: Ask the database to perform a natural language question. - */ - abstract ask(question: string, options: AskOptions & { onMessage: (message: AskResult) => void }): void; + result[column.name] = null; + } + + return result; + } + + async #transformObjectToApi(object: any): Promise { + const schema = this.#schema.tables.find((table) => table.name === this.#table); + if (!schema) throw new Error(`Table ${this.#table} not found in schema`); + + const result: Dictionary = {}; + + for (const [key, value] of Object.entries(object)) { + // Ignore internal properties + if (['xata_version', 'xata_createdat', 'xata_updatedat'].includes(key)) continue; + + const type = schema.columns.find((column) => column.name === key)?.type; + + switch (type) { + case 'link': { + result[key] = isObject(value) ? value[this.#primaryKey] : value; + break; + } + case 'datetime': { + result[key] = value instanceof Date ? value.toISOString() : value; + break; + } + case `file`: + result[key] = await parseInputFileEntry(value as InputXataFile); + break; + case 'file[]': + result[key] = await promiseMap(value as InputXataFile[], (item) => parseInputFileEntry(item)); + break; + case 'json': + result[key] = stringifyJson(value as any); + break; + default: + result[key] = value; + } + } - abstract query(query: Query): Promise>; + return result; + } } -export class RestRepository - extends Query> - implements Repository +export class RestRepository + extends Query> + implements Repository { #table: string; #getFetchProps: () => ApiExtraProps; #db: SchemaPluginResult; - #cache?: CacheImpl; - #schemaTables?: Schemas.Table[]; + #schema: DatabaseSchema; #trace: TraceFunction; constructor(options: { table: string; db: SchemaPluginResult; pluginOptions: XataPluginOptions; - schemaTables?: Schemas.Table[]; + schema: DatabaseSchema; }) { super( null, - { name: options.table, schema: options.schemaTables?.find((table) => table.name === options.table) }, + { name: options.table, schema: options.schema.tables.find((table) => table.name === options.table) }, {} ); this.#table = options.table; this.#db = options.db; - this.#cache = options.pluginOptions.cache; - this.#schemaTables = options.schemaTables; + this.#schema = options.schema; this.#getFetchProps = () => ({ ...options.pluginOptions, sessionID: generateUUID() }); const trace = options.pluginOptions.trace ?? defaultTrace; @@ -852,55 +2369,50 @@ export class RestRepository }; } - async create>( - object: EditableData & Partial, - columns: K[], - options?: { ifVersion?: number } - ): Promise>>; + async create>( + object: NewEditableDataWithoutNumeric & Partial[TableName]>, + columns: K[] + ): Promise>>; async create( - object: EditableData & Partial, - options?: { ifVersion?: number } - ): Promise>>; - async create>( - id: Identifier, - object: EditableData, - columns: K[], - options?: { ifVersion?: number } - ): Promise>>; + object: NewEditableDataWithoutNumeric & Partial[TableName]> + ): Promise>>; + async create>( + id: NewIndentifierValue[TableName]>, + object: NewEditableDataWithoutNumeric, + columns: K[] + ): Promise>>; async create( - id: Identifier, - object: EditableData, - options?: { ifVersion?: number } - ): Promise>>; - async create>( - objects: Array & Partial>, + id: NewIndentifierValue[TableName]>, + object: NewEditableDataWithoutNumeric + ): Promise>>; + async create>( + objects: Array & Partial[TableName]>>, columns: K[] - ): Promise>[]>; + ): Promise>[]>; async create( - objects: Array & Partial> - ): Promise>[]>; - async create>( + objects: Array & Partial[TableName]>> + ): Promise>[]>; + async create>( a: - | Identifier - | (EditableData & Partial) - | Array & Partial>, - b?: EditableData | K[] | { ifVersion?: number }, - c?: K[] | { ifVersion?: number }, - d?: { ifVersion?: number } + | NewIndentifierValue[TableName]> + | (NewEditableDataWithoutNumeric & Partial[TableName]>) + | Array & Partial[TableName]>>, + b?: NewEditableDataWithoutNumeric | K[], + c?: K[] ): Promise< - | Readonly> - | Readonly>[] - | Readonly> - | Readonly>[] + | Readonly> + | Readonly>[] + | Readonly> + | Readonly>[] > { return this.#trace('create', async () => { - const ifVersion = parseIfVersion(b, c, d); + const ifVersion = parseIfVersion(b, c, undefined); // Create many records if (Array.isArray(a)) { if (a.length === 0) return []; - const ids = await this.#insertRecords(a, { ifVersion, createOnly: true }); + const ids = await this.#insertRecords(a as any, { ifVersion, createOnly: true }); const columns = isValidSelectableColumns(b) ? b : (['*'] as K[]); @@ -914,28 +2426,34 @@ export class RestRepository if (a === '') throw new Error("The id can't be empty"); const columns = isValidSelectableColumns(c) ? c : undefined; - return await this.#insertRecordWithId(a, b as EditableData, columns, { createOnly: true, ifVersion }); + return await this.#insertRecordWithId(a, b as EditableData, columns, { + createOnly: true, + ifVersion + }); } // Create one record with id as property - if (isObject(a) && isString(a.id)) { - if (a.id === '') throw new Error("The id can't be empty"); + if (isObject(a) && isString((a as any).xata_id)) { + if ((a as any).xata_id === '') throw new Error("The id can't be empty"); const columns = isValidSelectableColumns(b) ? b : undefined; - return await this.#insertRecordWithId(a.id, { ...a, id: undefined }, columns, { createOnly: true, ifVersion }); + return await this.#insertRecordWithId((a as any).xata_id, { ...(a as any), xata_id: undefined }, columns, { + createOnly: true, + ifVersion + }); } // Create one record without id if (isObject(a)) { const columns = isValidSelectableColumns(b) ? b : undefined; - return this.#insertRecordWithoutId(a, columns); + return this.#insertRecordWithoutId(a as any, columns); } throw new Error('Invalid arguments for create method'); }); } - async #insertRecordWithoutId(object: EditableData, columns: SelectableColumn[] = ['*']) { + async #insertRecordWithoutId(object: EditableData, columns: SelectableColumn[] = ['*']) { const record = await this.#transformObjectToApi(object); const response = await insertRecord({ @@ -950,14 +2468,13 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); - return initObject(this.#db, schemaTables, this.#table, response, columns) as any; + return initObject(this.#db, this.#schema, this.#table, response, columns) as any; } async #insertRecordWithId( recordId: Identifier, - object: EditableData, - columns: SelectableColumn[] = ['*'], + object: EditableData, + columns: SelectableColumn[] = ['*'], { createOnly, ifVersion }: { createOnly: boolean; ifVersion?: number } ) { if (!recordId) return null; @@ -977,12 +2494,11 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); - return initObject(this.#db, schemaTables, this.#table, response, columns) as any; + return initObject(this.#db, this.#schema, this.#table, response, columns) as any; } async #insertRecords( - objects: EditableData[], + objects: EditableData[], { createOnly, ifVersion }: { createOnly: boolean; ifVersion?: number } ) { const operations = await promiseMap(objects, async (object) => { @@ -1017,34 +2533,34 @@ export class RestRepository return ids; } - async read>( + async read>( id: Identifier, columns: K[] - ): Promise | null>>; - async read(id: string): Promise | null>>; - async read>( + ): Promise | null>>; + async read(id: string): Promise | null>>; + async read>( ids: ReadonlyArray, columns: K[] - ): Promise> | null>>; - async read(ids: ReadonlyArray): Promise> | null>>; - async read>( + ): Promise> | null>>; + async read(ids: ReadonlyArray): Promise> | null>>; + async read>( object: Identifiable, columns: K[] - ): Promise | null>>; - async read(object: Identifiable): Promise | null>>; - async read>( + ): Promise | null>>; + async read(object: Identifiable): Promise | null>>; + async read>( objects: Identifiable[], columns: K[] - ): Promise> | null>>; - async read(objects: Identifiable[]): Promise> | null>>; - async read>( + ): Promise> | null>>; + async read(objects: Identifiable[]): Promise> | null>>; + async read>( a: Identifier | ReadonlyArray | Identifiable | Identifiable[], b?: K[] ): Promise< - | Readonly> - | Array> | null> - | Readonly> - | Array> | null> + | Readonly> + | Array> | null> + | Readonly> + | Array> | null> | null > { return this.#trace('read', async () => { @@ -1056,11 +2572,11 @@ export class RestRepository const ids = a.map((item) => extractId(item)); - const finalObjects = await this.getAll({ filter: { id: { $any: compact(ids) } }, columns }); + const finalObjects = await this.getAll({ filter: { xata_id: { $any: compact(ids) } }, columns }); // Maintain order of objects const dictionary = finalObjects.reduce((acc, object) => { - acc[object.id] = object; + acc[object.xata_id] = object; return acc; }, {} as Dictionary); @@ -1083,13 +2599,12 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); - return initObject( + return initObject( this.#db, - schemaTables, + this.#schema, this.#table, response, - columns as SelectableColumn[] + columns as SelectableColumn[] ) as any; } catch (e) { if (isObject(e) && e.status === 404) { @@ -1104,34 +2619,34 @@ export class RestRepository }); } - async readOrThrow>( + async readOrThrow>( id: Identifier, columns: K[] - ): Promise>>; - async readOrThrow(id: Identifier): Promise>>; - async readOrThrow>( + ): Promise>>; + async readOrThrow(id: Identifier): Promise>>; + async readOrThrow>( ids: ReadonlyArray, columns: K[] - ): Promise>>>; - async readOrThrow(ids: ReadonlyArray): Promise>>>; - async readOrThrow>( + ): Promise>>>; + async readOrThrow(ids: ReadonlyArray): Promise>>>; + async readOrThrow>( object: Identifiable, columns: K[] - ): Promise>>; - async readOrThrow(object: Identifiable): Promise>>; - async readOrThrow>( + ): Promise>>; + async readOrThrow(object: Identifiable): Promise>>; + async readOrThrow>( objects: Identifiable[], columns: K[] - ): Promise>>>; - async readOrThrow(objects: Identifiable[]): Promise>>>; - async readOrThrow>( + ): Promise>>>; + async readOrThrow(objects: Identifiable[]): Promise>>>; + async readOrThrow>( a: Identifier | ReadonlyArray | Identifiable | Identifiable[], b?: K[] ): Promise< - | Readonly> - | Readonly>[] - | Readonly> - | Readonly>[] + | Readonly> + | Readonly>[] + | Readonly> + | Readonly>[] > { return this.#trace('readOrThrow', async () => { const result = await this.read(a as any, b as any); @@ -1159,60 +2674,55 @@ export class RestRepository }); } - async update>( - object: Partial> & Identifiable, - columns: K[], - options?: { ifVersion?: number } - ): Promise> | null>; + async update>( + object: Partial> & NewIdentifiable[TableName], + columns: K[] + ): Promise> | null>; async update( - object: Partial> & Identifiable, - options?: { ifVersion?: number } - ): Promise> | null>; - async update>( - id: Identifier, - object: Partial>, - columns: K[], - options?: { ifVersion?: number } - ): Promise> | null>; + object: Partial> & NewIdentifiable[TableName] + ): Promise> | null>; + async update>( + id: NewIndentifierValue[TableName]>, + object: Partial>, + columns: K[] + ): Promise> | null>; async update( - id: Identifier, - object: Partial>, - options?: { ifVersion?: number } - ): Promise> | null>; - async update>( - objects: Array> & Identifiable>, + id: NewIndentifierValue[TableName]>, + object: Partial> + ): Promise> | null>; + async update>( + objects: Array> & NewIdentifiable[TableName]>, columns: K[] - ): Promise> | null>>; + ): Promise> | null>>; async update( - objects: Array> & Identifiable> - ): Promise> | null>>; - async update>( + objects: Array> & NewIdentifiable[TableName]> + ): Promise> | null>>; + async update>( a: - | Identifier - | (Partial> & Identifiable) - | Array> & Identifiable>, - b?: Partial> | K[] | { ifVersion?: number }, - c?: K[] | { ifVersion?: number }, - d?: { ifVersion?: number } + | NewIndentifierValue[TableName]> + | (Partial> & NewIdentifiable[TableName]) + | Array> & NewIdentifiable[TableName]>, + b?: Partial> | K[], + c?: K[] ): Promise< - | Readonly> - | Array> | null> - | Readonly> - | Array> | null> + | Readonly> + | Array> | null> + | Readonly> + | Array> | null> | null > { return this.#trace('update', async () => { - const ifVersion = parseIfVersion(b, c, d); + const ifVersion = parseIfVersion(b, c, undefined); // Update many records if (Array.isArray(a)) { if (a.length === 0) return []; // TODO: Transaction API fails fast if one of the records is not found - const existing = await this.read(a, ['id']); + const existing = await this.read(a, ['xata_id'] as SelectableColumn[]); const updates = a.filter((_item, index) => existing[index] !== null); - await this.#updateRecords(updates as Array> & Identifiable>, { + await this.#updateRecords(updates as Array> & Identifiable>, { ifVersion, upsert: false }); @@ -1228,13 +2738,15 @@ export class RestRepository // Update one record with id as param if (isString(a) && isObject(b)) { const columns = isValidSelectableColumns(c) ? c : undefined; - return await this.#updateRecordWithID(a, b as EditableData, columns, { ifVersion }); + return await this.#updateRecordWithID(a, b as EditableData, columns, { ifVersion }); } // Update one record with id as property - if (isObject(a) && isString(a.id)) { + if (isObject(a) && isString((a as any).xata_id)) { const columns = isValidSelectableColumns(b) ? b : undefined; - return await this.#updateRecordWithID(a.id, { ...a, id: undefined }, columns, { ifVersion }); + return await this.#updateRecordWithID((a as any).xata_id, { ...(a as any), xata_id: undefined }, columns, { + ifVersion + }); } } catch (error: any) { if (error.status === 422) return null; @@ -1245,49 +2757,44 @@ export class RestRepository }); } - async updateOrThrow>( - object: Partial> & Identifiable, - columns: K[], - options?: { ifVersion?: number } - ): Promise>>; + async updateOrThrow>( + object: Partial> & NewIdentifiable[TableName], + columns: K[] + ): Promise>>; async updateOrThrow( - object: Partial> & Identifiable, - options?: { ifVersion?: number } - ): Promise>>; - async updateOrThrow>( - id: Identifier, - object: Partial>, - columns: K[], - options?: { ifVersion?: number } - ): Promise>>; + object: Partial> & NewIdentifiable[TableName] + ): Promise>>; + async updateOrThrow>( + id: NewIndentifierValue[TableName]>, + object: Partial>, + columns: K[] + ): Promise>>; async updateOrThrow( - id: Identifier, - object: Partial>, - options?: { ifVersion?: number } - ): Promise>>; - async updateOrThrow>( - objects: Array> & Identifiable>, + id: NewIndentifierValue[TableName]>, + object: Partial> + ): Promise>>; + async updateOrThrow>( + objects: Array> & NewIdentifiable[TableName]>, columns: K[] - ): Promise>[]>; + ): Promise>[]>; async updateOrThrow( - objects: Array> & Identifiable> - ): Promise>[]>; - async updateOrThrow>( + objects: Array> & NewIdentifiable[TableName]> + ): Promise>[]>; + async updateOrThrow>( a: - | Identifier - | (Partial> & Identifiable) - | Array> & Identifiable>, - b?: Partial> | K[] | { ifVersion?: number }, - c?: K[] | { ifVersion?: number }, - d?: { ifVersion?: number } + | NewIndentifierValue[TableName]> + | (Partial> & NewIdentifiable[TableName]) + | Array> & NewIdentifiable[TableName]>, + b?: Partial> | K[], + c?: K[] ): Promise< - | Readonly> - | Array>> - | Readonly> - | Array>> + | Readonly> + | Array>> + | Readonly> + | Array>> > { return this.#trace('updateOrThrow', async () => { - const result = await this.update(a as any, b as any, c as any, d as any); + const result = await this.update(a as any, b as any, c as any); if (Array.isArray(result)) { const missingIds = compact( @@ -1314,14 +2821,14 @@ export class RestRepository async #updateRecordWithID( recordId: Identifier, - object: Partial>, - columns: SelectableColumn[] = ['*'], + object: Partial>, + columns: SelectableColumn[] = ['*'], { ifVersion }: { ifVersion?: number } ) { if (!recordId) return null; // Ensure id is not present in the update payload - const { id: _id, ...record } = await this.#transformObjectToApi(object); + const { xata_id: _id, ...record } = await this.#transformObjectToApi(object); try { const response = await updateRecordWithID({ @@ -1337,8 +2844,7 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); - return initObject(this.#db, schemaTables, this.#table, response, columns) as any; + return initObject(this.#db, this.#schema, this.#table, response, columns) as any; } catch (e) { if (isObject(e) && e.status === 404) { return null; @@ -1349,12 +2855,12 @@ export class RestRepository } async #updateRecords( - objects: Array> & Identifiable>, + objects: Array> & Identifiable>, { ifVersion, upsert }: { ifVersion?: number; upsert: boolean } ) { - const operations = await promiseMap(objects, async ({ id, ...object }) => { + const operations = await promiseMap(objects, async ({ xata_id, ...object }) => { const fields = await this.#transformObjectToApi(object); - return { update: { table: this.#table, id, ifVersion, upsert, fields } }; + return { update: { table: this.#table, id: xata_id, ifVersion, upsert, fields } }; }); const chunkedOperations: TransactionOperation[][] = chunk(operations, BULK_OPERATION_MAX_SIZE); @@ -1384,52 +2890,53 @@ export class RestRepository return ids; } - async createOrUpdate>( - object: EditableData & Partial, - columns: K[], - options?: { ifVersion?: number } - ): Promise>>; + async createOrUpdate>( + object: NewEditableData & Partial[TableName]>, + columns: K[] + ): Promise>>; async createOrUpdate( - object: EditableData & Partial, - options?: { ifVersion?: number } - ): Promise>>; - async createOrUpdate>( - id: Identifier, - object: Omit, 'id'>, - columns: K[], - options?: { ifVersion?: number } - ): Promise>>; + object: NewEditableData & Partial[TableName]> + ): Promise>>; + async createOrUpdate>( + id: NewIndentifierValue[TableName]>, + object: Omit, NewIdentifierKey[TableName]>>, + columns: K[] + ): Promise>>; async createOrUpdate( - id: Identifier, - object: Omit, 'id'>, - options?: { ifVersion?: number } - ): Promise>>; - async createOrUpdate>( - objects: Array & Partial>, + id: NewIndentifierValue[TableName]>, + object: Omit, NewIdentifierKey[TableName]>> + ): Promise>>; + async createOrUpdate>( + objects: Array & Partial[TableName]>>, columns: K[] - ): Promise>[]>; + ): Promise>[]>; async createOrUpdate( - objects: Array & Partial> - ): Promise>[]>; - async createOrUpdate>( - a: Identifier | EditableData | EditableData[], - b?: EditableData | Omit, 'id'> | K[] | { ifVersion?: number }, - c?: K[] | { ifVersion?: number }, - d?: { ifVersion?: number } + objects: Array & Partial[TableName]>> + ): Promise>[]>; + async createOrUpdate>( + a: + | NewIndentifierValue[TableName]> + | NewEditableData + | NewEditableData[], + b?: + | NewEditableData + | Omit, NewIdentifierKey[TableName]>> + | K[], + c?: K[] ): Promise< - | Readonly> - | Array>> - | Readonly> - | Array>> + | Readonly> + | Array>> + | Readonly> + | Array>> > { return this.#trace('createOrUpdate', async () => { - const ifVersion = parseIfVersion(b, c, d); + const ifVersion = parseIfVersion(b, c, undefined); // Create or update many records if (Array.isArray(a)) { if (a.length === 0) return []; - await this.#updateRecords(a as Array> & Identifiable>, { + await this.#updateRecords(a as Array> & Identifiable>, { ifVersion, upsert: true }); @@ -1437,7 +2944,7 @@ export class RestRepository const columns = isValidSelectableColumns(b) ? b : (['*'] as K[]); // TODO: Transaction API does not support column projection - const result = await this.read(a, columns); + const result = await this.read(a as any[], columns); return result; } @@ -1446,25 +2953,27 @@ export class RestRepository if (a === '') throw new Error("The id can't be empty"); const columns = isValidSelectableColumns(c) ? c : undefined; - return await this.#upsertRecordWithID(a, b as EditableData, columns, { ifVersion }); + return await this.#upsertRecordWithID(a, b as EditableData, columns, { ifVersion }); } // Create or update one record with id as property - if (isObject(a) && isString(a.id)) { - if (a.id === '') throw new Error("The id can't be empty"); + if (isObject(a) && isString((a as any).xata_id)) { + if ((a as any).xata_id === '') throw new Error("The id can't be empty"); const columns = isValidSelectableColumns(c) ? c : undefined; - return await this.#upsertRecordWithID(a.id, { ...a, id: undefined }, columns, { ifVersion }); + return await this.#upsertRecordWithID((a as any).xata_id, { ...(a as any), xata_id: undefined }, columns, { + ifVersion + }); } // Create with undefined id as param if (!isDefined(a) && isObject(b)) { - return await this.create(b as EditableData, c as K[]); + return await this.create(b as any, c as K[]); } // Create with undefined id as property - if (isObject(a) && !isDefined(a.id)) { - return await this.create(a as EditableData, b as K[]); + if (isObject(a) && !isDefined((a as any).xata_id)) { + return await this.create(a as any, b as K[]); } throw new Error('Invalid arguments for createOrUpdate method'); @@ -1473,8 +2982,8 @@ export class RestRepository async #upsertRecordWithID( recordId: Identifier, - object: Omit, 'id'>, - columns: SelectableColumn[] = ['*'], + object: Omit, 'xata_id'>, + columns: SelectableColumn[] = ['*'], { ifVersion }: { ifVersion?: number } ) { if (!recordId) return null; @@ -1492,56 +3001,57 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); - return initObject(this.#db, schemaTables, this.#table, response, columns) as any; + return initObject(this.#db, this.#schema, this.#table, response, columns) as any; } - async createOrReplace>( - object: EditableData & Partial, - columns: K[], - options?: { ifVersion?: number } - ): Promise>>; + async createOrReplace>( + object: NewEditableData & Partial[TableName]>, + columns: K[] + ): Promise>>; async createOrReplace( - object: EditableData & Partial, - options?: { ifVersion?: number } - ): Promise>>; - async createOrReplace>( - id: Identifier | undefined, - object: Omit, 'id'>, - columns: K[], - options?: { ifVersion?: number } - ): Promise>>; + object: NewEditableData & Partial[TableName]> + ): Promise>>; + async createOrReplace>( + id: NewIndentifierValue[TableName]> | undefined, + object: Omit, NewIdentifierKey[TableName]>>, + columns: K[] + ): Promise>>; async createOrReplace( - id: Identifier | undefined, - object: Omit, 'id'>, - options?: { ifVersion?: number } - ): Promise>>; - async createOrReplace>( - objects: Array & Partial>, + id: NewIndentifierValue[TableName]> | undefined, + object: Omit, NewIdentifierKey[TableName]>> + ): Promise>>; + async createOrReplace>( + objects: Array & Partial[TableName]>>, columns: K[] - ): Promise>[]>; + ): Promise>[]>; async createOrReplace( - objects: Array & Partial> - ): Promise>[]>; - async createOrReplace>( - a: Identifier | EditableData | EditableData[] | undefined, - b?: EditableData | Omit, 'id'> | K[] | { ifVersion?: number }, - c?: K[] | { ifVersion?: number }, - d?: { ifVersion?: number } + objects: Array & Partial[TableName]>> + ): Promise>[]>; + async createOrReplace>( + a: + | NewIndentifierValue[TableName]> + | NewEditableData + | NewEditableData[] + | undefined, + b?: + | NewEditableData + | Omit, NewIdentifierKey[TableName]>> + | K[], + c?: K[] ): Promise< - | Readonly> - | Array>> - | Readonly> - | Array>> + | Readonly> + | Array>> + | Readonly> + | Array>> > { return this.#trace('createOrReplace', async () => { - const ifVersion = parseIfVersion(b, c, d); + const ifVersion = parseIfVersion(b, c, undefined); // Create or replace many records if (Array.isArray(a)) { if (a.length === 0) return []; - const ids = await this.#insertRecords(a, { ifVersion, createOnly: false }); + const ids = await this.#insertRecords(a as any, { ifVersion, createOnly: false }); const columns = isValidSelectableColumns(b) ? b : (['*'] as K[]); @@ -1555,61 +3065,67 @@ export class RestRepository if (a === '') throw new Error("The id can't be empty"); const columns = isValidSelectableColumns(c) ? c : undefined; - return await this.#insertRecordWithId(a, b as EditableData, columns, { createOnly: false, ifVersion }); + return await this.#insertRecordWithId(a, b as EditableData, columns, { + createOnly: false, + ifVersion + }); } // Create or replace one record with id as property - if (isObject(a) && isString(a.id)) { - if (a.id === '') throw new Error("The id can't be empty"); + if (isObject(a) && isString((a as any).xata_id)) { + if ((a as any).xata_id === '') throw new Error("The id can't be empty"); const columns = isValidSelectableColumns(c) ? c : undefined; - return await this.#insertRecordWithId(a.id, { ...a, id: undefined }, columns, { createOnly: false, ifVersion }); + return await this.#insertRecordWithId((a as any).xata_id, { ...(a as any), xata_id: undefined }, columns, { + createOnly: false, + ifVersion + }); } // Create with undefined id as param if (!isDefined(a) && isObject(b)) { - return await this.create(b as EditableData, c as K[]); + return await this.create(b as any, c as K[]); } // Create with undefined id as property - if (isObject(a) && !isDefined(a.id)) { - return await this.create(a as EditableData, b as K[]); + if (isObject(a) && !isDefined((a as any).xata_id)) { + return await this.create(a as any, b as K[]); } throw new Error('Invalid arguments for createOrReplace method'); }); } - async delete>( + async delete>( object: Identifiable, columns: K[] - ): Promise> | null>; - async delete(object: Identifiable): Promise> | null>; - async delete>( + ): Promise> | null>; + async delete(object: Identifiable): Promise> | null>; + async delete>( id: Identifier, columns: K[] - ): Promise> | null>; - async delete(id: Identifier): Promise> | null>; - async delete>( - objects: Array> & Identifiable>, + ): Promise> | null>; + async delete(id: Identifier): Promise> | null>; + async delete>( + objects: Array> & Identifiable>, columns: K[] - ): Promise> | null>>; + ): Promise> | null>>; async delete( - objects: Array> & Identifiable> - ): Promise> | null>>; - async delete>( + objects: Array> & Identifiable> + ): Promise> | null>>; + async delete>( objects: Identifier[], columns: K[] - ): Promise> | null>>; - async delete(objects: Identifier[]): Promise> | null>>; - async delete>( + ): Promise> | null>>; + async delete(objects: Identifier[]): Promise> | null>>; + async delete>( a: Identifier | Identifiable | Array, b?: K[] ): Promise< - | Readonly> - | Array> | null> - | Readonly> - | Array> | null> + | Readonly> + | Array> | null> + | Readonly> + | Array> | null> | null > { return this.#trace('delete', async () => { @@ -1619,7 +3135,7 @@ export class RestRepository const ids = a.map((o) => { if (isString(o)) return o; - if (isString(o.id)) return o.id; + if (isString(o.xata_id)) return o.xata_id; throw new Error('Invalid arguments for delete method'); }); @@ -1639,44 +3155,44 @@ export class RestRepository } // Delete one record with id as property - if (isObject(a) && isString(a.id)) { - return this.#deleteRecord(a.id, b); + if (isObject(a) && isString(a.xata_id)) { + return this.#deleteRecord(a.xata_id, b); } throw new Error('Invalid arguments for delete method'); }); } - async deleteOrThrow>( + async deleteOrThrow>( object: Identifiable, columns: K[] - ): Promise>>; - async deleteOrThrow(object: Identifiable): Promise>>; - async deleteOrThrow>( + ): Promise>>; + async deleteOrThrow(object: Identifiable): Promise>>; + async deleteOrThrow>( id: Identifier, columns: K[] - ): Promise>>; - async deleteOrThrow(id: Identifier): Promise>>; - async deleteOrThrow>( - objects: Array> & Identifiable>, + ): Promise>>; + async deleteOrThrow(id: Identifier): Promise>>; + async deleteOrThrow>( + objects: Array> & Identifiable>, columns: K[] - ): Promise>>>; + ): Promise>>>; async deleteOrThrow( - objects: Array> & Identifiable> - ): Promise>>>; - async deleteOrThrow>( + objects: Array> & Identifiable> + ): Promise>>>; + async deleteOrThrow>( objects: Identifier[], columns: K[] - ): Promise>>>; - async deleteOrThrow(objects: Identifier[]): Promise>>>; - async deleteOrThrow>( + ): Promise>>>; + async deleteOrThrow(objects: Identifier[]): Promise>>>; + async deleteOrThrow>( a: Identifier | Identifiable | Array, b?: K[] ): Promise< - | Readonly> - | Array>> - | Readonly> - | Array>> + | Readonly> + | Array>> + | Readonly> + | Array>> > { return this.#trace('deleteOrThrow', async () => { const result = await this.delete(a as any, b as any); @@ -1702,7 +3218,7 @@ export class RestRepository }); } - async #deleteRecord(recordId: Identifier, columns: SelectableColumn[] = ['*']) { + async #deleteRecord(recordId: Identifier, columns: SelectableColumn[] = ['*']) { if (!recordId) return null; try { @@ -1718,8 +3234,7 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); - return initObject(this.#db, schemaTables, this.#table, response, columns) as any; + return initObject(this.#db, this.#schema, this.#table, response, columns) as any; } catch (e) { if (isObject(e) && e.status === 404) { return null; @@ -1754,10 +3269,10 @@ export class RestRepository fuzziness?: FuzzinessExpression; prefix?: PrefixExpression; highlight?: HighlightExpression; - filter?: Filter; - boosters?: Boosters[]; + filter?: Filter; + boosters?: Boosters[]; page?: SearchPageConfig; - target?: TargetColumn[]; + target?: TargetColumn[]; } = {} ) { return this.#trace('search', async () => { @@ -1781,27 +3296,25 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); - // TODO - Column selection not supported by search endpoint yet return { - records: records.map((item) => initObject(this.#db, schemaTables, this.#table, item, ['*'])) as any, + records: records.map((item) => initObject(this.#db, this.#schema, this.#table, item, ['*'])) as any, totalCount }; }); } - async vectorSearch>( + async vectorSearch>( column: F, query: number[], options?: | { similarityFunction?: string | undefined; size?: number | undefined; - filter?: Filter | undefined; + filter?: Filter | undefined; } | undefined - ): Promise<{ records: SearchXataRecord>[] } & TotalCount> { + ): Promise<{ records: SearchXataRecord>[] } & TotalCount> { return this.#trace('vectorSearch', async () => { const { records, totalCount } = await vectorSearchTable({ pathParams: { @@ -1820,19 +3333,17 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); - // TODO - Column selection not supported by search endpoint yet return { - records: records.map((item) => initObject(this.#db, schemaTables, this.#table, item, ['*'])), + records: records.map((item) => initObject(this.#db, this.#schema, this.#table, item, ['*'])), totalCount } as any; }); } - async aggregate>>( + async aggregate>>( aggs?: Expression, - filter?: Filter + filter?: Filter ) { return this.#trace('aggregate', async () => { const result = await aggregateTable({ @@ -1850,11 +3361,10 @@ export class RestRepository }); } - async query(query: Query): Promise> { + async query( + query: Query + ): Promise> { return this.#trace('query', async () => { - const cacheQuery = await this.#getCacheQuery(query); - if (cacheQuery) return new Page(query, cacheQuery.meta, cacheQuery.records); - const data = query.getQueryOptions(); const { meta, records: objects } = await queryTable({ @@ -1875,25 +3385,23 @@ export class RestRepository ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); const records = objects.map((record) => initObject( this.#db, - schemaTables, + this.#schema, this.#table, record, (data.columns as SelectableColumn[]) ?? ['*'] ) ); - await this.#setCacheQuery(query, meta, records); - return new Page(query, meta, records); + return new Page(query, meta, records); }); } async summarizeTable( - query: Query, - summaries?: Dictionary>, + query: Query, + summaries?: Dictionary>, summariesFilter?: Schemas.FilterExpression ) { return this.#trace('summarize', async () => { @@ -1909,7 +3417,7 @@ export class RestRepository body: { filter: cleanFilter(data.filter), sort: data.sort !== undefined ? buildSortFilter(data.sort) : undefined, - columns: data.columns as SelectableColumn[], + columns: data.columns as SelectableColumn[], consistency: data.consistency, page: data.pagination?.size !== undefined ? { size: data.pagination?.size } : undefined, summaries, @@ -1917,17 +3425,17 @@ export class RestRepository }, ...this.#getFetchProps() }); - const schemaTables = await this.#getSchemaTables(); + return { ...result, summaries: result.summaries.map((summary) => - initObject(this.#db, schemaTables, this.#table, summary, data.columns ?? []) + initObject(this.#db, this.#schema, this.#table, summary, data.columns ?? []) ) }; }); } - ask(question: string, options?: AskOptions & { onMessage?: (message: AskResult) => void }): any { + ask(question: string, options?: AskOptions & { onMessage?: (message: AskResult) => void }): any { // Ask with session uses message, ask without session uses question param const questionParam = options?.sessionId ? { message: question } : { question }; const params = { @@ -1963,53 +3471,21 @@ export class RestRepository } } - async #setCacheQuery(query: Query, meta: RecordsMetadata, records: XataRecord[]): Promise { - await this.#cache?.set(`query_${this.#table}:${query.key()}`, { date: new Date(), meta, records }); - } - - async #getCacheQuery( - query: Query - ): Promise<{ meta: RecordsMetadata; records: T[] } | null> { - const key = `query_${this.#table}:${query.key()}`; - const result = await this.#cache?.get<{ date: Date; meta: RecordsMetadata; records: T[] }>(key); - if (!result) return null; - - const defaultTTL = this.#cache?.defaultQueryTTL ?? -1; - const { cache: ttl = defaultTTL } = query.getQueryOptions(); - if (ttl < 0) return null; - - const hasExpired = result.date.getTime() + ttl < Date.now(); - return hasExpired ? null : result; - } - - async #getSchemaTables(): Promise { - if (this.#schemaTables) return this.#schemaTables; - - const { schema } = await getBranchDetails({ - pathParams: { workspace: '{workspaceId}', dbBranchName: '{dbBranch}', region: '{region}' }, - ...this.#getFetchProps() - }); - - this.#schemaTables = schema.tables; - return schema.tables; - } - async #transformObjectToApi(object: any): Promise { - const schemaTables = await this.#getSchemaTables(); - const schema = schemaTables.find((table) => table.name === this.#table); + const schema = this.#schema.tables.find((table) => table.name === this.#table); if (!schema) throw new Error(`Table ${this.#table} not found in schema`); const result: Dictionary = {}; for (const [key, value] of Object.entries(object)) { // Ignore internal properties - if (key === 'xata') continue; + if (['xata_version', 'xata_createdat', 'xata_updatedat'].includes(key)) continue; const type = schema.columns.find((column) => column.name === key)?.type; switch (type) { case 'link': { - result[key] = isIdentifiable(value) ? value.id : value; + result[key] = isIdentifiable(value) ? value.xata_id : value; break; } case 'datetime': { @@ -2034,18 +3510,18 @@ export class RestRepository } } -export const initObject = ( - db: Record>, - schemaTables: Schemas.Table[], +export const initObjectKysely = ( + repo: KyselyRepository, + schemaTables: DatabaseSchema, + primaryKey: string, table: string, object: Record, selectedColumns: SelectableColumn[] | SelectableColumnWithObjectNotation[] ) => { const data: Dictionary = {}; - const { xata, ...rest } = object ?? {}; - Object.assign(data, rest); + Object.assign(data, { ...object }); - const { columns } = schemaTables.find(({ name }) => name === table) ?? {}; + const { columns } = schemaTables.tables.find(({ name }) => name === table) ?? {}; if (!columns) console.error(`Table ${table} not found in schema`); for (const column of columns ?? []) { @@ -2056,7 +3532,7 @@ export const initObject = ( switch (column.type) { case 'datetime': { - const date = value !== undefined ? new Date(value as string) : null; + const date = value !== undefined && value !== null ? new Date(value as string) : null; if (date !== null && isNaN(date.getTime())) { console.error(`Failed to parse date ${value} for field ${column.name}`); @@ -2066,34 +3542,90 @@ export const initObject = ( break; } - case 'link': { - const linkTable = column.link?.table; + case 'file': + data[column.name] = isDefined(value) ? new XataFile(value as any) : null; + break; + case 'file[]': + data[column.name] = (value as XataArrayFile[])?.map((item) => new XataFile(item)) ?? null; + break; + case 'json': + data[column.name] = parseJson(value as string); + break; + default: + data[column.name] = value ?? null; - if (!linkTable) { - console.error(`Failed to parse link for field ${column.name}`); - } else if (isObject(value)) { - const selectedLinkColumns = (selectedColumns as string[]).reduce((acc, item) => { - if (item === column.name) { - return [...acc, '*']; - } + if (column.notNull === true && value === null) { + console.error(`Parse error, column ${column.name} is non nullable and value resolves null`); + } + break; + } + } - if (isString(item) && item.startsWith(`${column.name}.`)) { - const [, ...path] = item.split('.'); - return [...acc, path.join('.')]; - } + const record = { ...data }; + + record.read = async function (columns?: any) { + return repo.read(record[primaryKey] as any, columns); + }; - return acc; - }, [] as string[]); + record.update = async function (data: any, b?: any) { + const columns = isValidSelectableColumns(b) ? b : ['*']; + // @ts-ignore + return repo.update(record[primaryKey], data, columns); + }; - data[column.name] = initObject( - db, - schemaTables, - linkTable, - value, - selectedLinkColumns as SelectableColumn[] - ); + record.replace = async function (data: any, b?: any) { + const validColumns = isValidSelectableColumns(b) ? b : ['*']; + return repo.createOrReplace(record[primaryKey] as any, data, validColumns); + }; + + record.delete = async function () { + return repo.delete(record[primaryKey] as any); + }; + + record.toSerializable = function () { + return JSON.parse(JSON.stringify(record)); + }; + + record.toString = function () { + return JSON.stringify(record); + }; + + for (const prop of ['read', 'update', 'replace', 'delete', 'toSerializable', 'toString']) { + Object.defineProperty(record, prop, { enumerable: false }); + } + + Object.freeze(record); + // `as unkwnown` to avoid TS error on versions prior to 4.9 (can be removed once we drop support for older versions) + return record as unknown as T; +}; + +export const initObject = ( + db: Record>, + schema: DatabaseSchema, + table: string, + object: Record, + selectedColumns: SelectableColumn[] | SelectableColumnWithObjectNotation[] +) => { + const data: Dictionary = {}; + Object.assign(data, { ...object }); + + const { columns } = schema.tables.find(({ name }) => name === table) ?? {}; + if (!columns) console.error(`Table ${table} not found in schema`); + + for (const column of columns ?? []) { + // Ignore columns not selected + if (!isValidColumn(selectedColumns, column)) continue; + + const value = data[column.name]; + + switch (column.type) { + case 'datetime': { + const date = value !== undefined ? new Date(value as string) : null; + + if (date !== null && isNaN(date.getTime())) { + console.error(`Failed to parse date ${value} for field ${column.name}`); } else { - data[column.name] = null; + data[column.name] = date; } break; @@ -2118,39 +3650,28 @@ export const initObject = ( } const record = { ...data }; - const metadata = - xata !== undefined - ? { ...xata, createdAt: new Date(xata.createdAt), updatedAt: new Date(xata.updatedAt) } - : undefined; record.read = function (columns?: any) { - return db[table].read(record['id'] as string, columns); + return db[table].read(record['xata_id'] as any, columns); }; record.update = function (data: any, b?: any, c?: any) { const columns = isValidSelectableColumns(b) ? b : ['*']; const ifVersion = parseIfVersion(b, c); - return db[table].update(record['id'] as string, data, columns, { ifVersion }); + // @ts-ignore + return db[table].update(record['xata_id'] as any, data, columns, { ifVersion }); }; record.replace = function (data: any, b?: any, c?: any) { const columns = isValidSelectableColumns(b) ? b : ['*']; const ifVersion = parseIfVersion(b, c); - return db[table].createOrReplace(record['id'] as string, data, columns, { ifVersion }); + return db[table].createOrReplace(record['xata_id'] as any, data, columns, { ifVersion }); }; record.delete = function () { - return db[table].delete(record['id'] as string); - }; - - if (metadata !== undefined) { - record.xata = Object.freeze(metadata); - } - - record.getMetadata = function () { - return record.xata; + return db[table].delete(record['xata_id'] as any); }; record.toSerializable = function () { @@ -2161,7 +3682,7 @@ export const initObject = ( return JSON.stringify(record); }; - for (const prop of ['read', 'update', 'replace', 'delete', 'getMetadata', 'toSerializable', 'toString']) { + for (const prop of ['read', 'update', 'replace', 'delete', 'toSerializable', 'toString']) { Object.defineProperty(record, prop, { enumerable: false }); } @@ -2172,7 +3693,13 @@ export const initObject = ( function extractId(value: any): Identifier | undefined { if (isString(value)) return value; - if (isObject(value) && isString(value.id)) return value.id; + if (isObject(value) && isString(value.xata_id)) return value.xata_id; + return undefined; +} + +function extractIdKysely(value: any, primaryKey: string) { + if (isStringOrNumber(value)) return value as any; + if (isObject(value) && isStringOrNumber(value[primaryKey])) return value[primaryKey] as any; return undefined; } @@ -2196,3 +3723,202 @@ function parseIfVersion(...args: any[]): number | undefined { return undefined; } + +const operatorMap: { [operator: string]: BinaryOperatorExpression } = { + $increment: '+', + $decrement: '-', + $multiply: '*', + $divide: '/' +}; + +const operatorNames = Object.keys(operatorMap); + +type OperatorMap = keyof typeof operatorMap; +type NumericOperations = { field: string; operator: OperatorMap; value: number }; + +const removeKeysFromRecord = ({ record, path }: { path: string[]; record: { [k: string]: any } }) => { + for (const key of path) { + delete record[key]; + } +}; + +const extractNumericOperations = ({ numericFilters }: { numericFilters: { [key: string]: any } }) => { + const acc: NumericOperations[] = []; + const traverse = ({ current, path }: { current: { [key: string]: any } | string | number; path: string[] }) => { + if (typeof current === 'number' && path.some((r) => operatorNames.includes(r))) { + acc.push({ + field: path[path.length - 2], + operator: path[path.length - 1], + value: current + }); + removeKeysFromRecord({ record: numericFilters, path }); + path.pop(); + path.pop(); + } + if (isObject(current)) { + for (const key in current) { + path.push(key); + traverse({ current: (current as any)[key], path }); + } + } + }; + traverse({ current: numericFilters, path: [] }); + return acc; +}; + +export const columnSelectionObject = (col: string[]) => { + const result: ColumnSelectionObject = { links: {}, regular: [] }; + const traverse = (columnPath: string, path: string[]) => { + const [table, ...rest] = columnPath.split('.'); + if (!atPath(result, path)['links']) { + atPath(result, path)['links'] = {}; + } + if (!atPath(result, path)['regular']) { + atPath(result, path)['regular'] = []; + } + + if (rest.length > 0) { + traverse(rest.join('.'), [...path, 'links', table]); + } else { + atPath(result, path)['regular'].push(table); + } + }; + + col.forEach((c) => { + traverse(c, []); + }); + return result; +}; + +const selectAllColumns = (columns: SelectableColumn[] = ['*']) => { + return !columns || (columns && columns.length > 0 && columns[0] === '*') || (columns && columns.length === 0); +}; + +export const generateSelectStatement = ({ + filter, + columns, + stmt, + schema, + tableName, + primaryKey, + columnData, + db +}: { + filter: Filter; + columnData: Schemas.Column[]; + columns: any[]; + stmt: SelectQueryBuilder, string, {}>; + schema: Schema; + tableName: string; + primaryKey: string; + db: KyselyPluginResult>>>; +}) => { + const columnsSelected = columnSelectionObject(columns ?? []); + const visited: Set = new Set(); + + if (selectAllColumns(columns as any)) { + stmt = stmt.selectAll(); + if (filter) { + const topLevelFilters = relevantFilters(filter, true, tableName, visited); + if (topLevelFilters) { + stmt = stmt.where( + (eb) => + filterToKysely({ value: { [tableName]: topLevelFilters }, path: [] })( + eb, + columnData as any, + tableName + ) as any + ); + } + } + } else { + // TODO separate selection and filtering + const select = stmt.select((eb) => { + const selection = ( + fields: Selection, + eb: ExpressionBuilder, + lastParent: string + ): SelectExpression[] => { + const regularFields = fields.regular.includes('*') + ? [sql.raw('*')] + : fields.regular.includes(primaryKey) + ? fields.regular + : [primaryKey, ...fields.regular]; + if (Object.keys(fields.links).length > 0) { + const links: (string | RawBuilder | AliasedRawBuilder<{ [x: string]: any } | null, string>)[] = [ + ...regularFields + ]; + for (const key in fields.links) { + const table = schema.tables?.find((table) => table.name === lastParent); + const fk: BranchSchema['tables'][number]['foreignKeys'][number] | null = table + ? (Object.values((table as any)?.foreignKeys ?? {})?.find((fk) => + (fk as any).columns.includes(key) + ) as any) + : null; + if (!fk) continue; + const selectedColumns = selection(fields.links[key], eb, fk?.referencedTable); + + const filters = relevantFilters(filter, false, key, visited); + const conditions = filters + ? (filterToKysely({ value: filters, path: [] })(eb, columnData, tableName) as any) + : null; + const stmt = eb + .selectFrom(fk.referencedTable) + .select(selectedColumns) + .where(fk.referencedColumns[0], '=', eb.ref(`${lastParent}.${fk.columns[0]}`)); + const linkObject = conditions + ? jsonObjectFromCustom(stmt.where(conditions)).as(`${fk.columns[0]}`) + : jsonObjectFrom(stmt).as(`${fk.columns[0]}`); + + links.push(linkObject); + } + + return links as SelectExpression[]; + } + + // TODO maybe add regular fields filters on here instead of below + return regularFields as SelectExpression[]; + }; + return selection(columnsSelected, eb, tableName); + }); + + stmt = select; + + if (filter) { + const topLevelFilters = relevantFilters(filter, true, tableName, visited); + if (topLevelFilters) { + stmt = stmt.where( + (eb) => + filterToKysely({ value: { [tableName]: topLevelFilters }, path: [] })( + eb, + columnData as any, + tableName + ) as any + ); + } + } + const linkKeys = Object.keys(columnsSelected.links); + const visited2: Set = new Set(); + if (linkKeys.length > 0) { + const linkFilters = linkKeys.filter((link) => relevantFilters(filter, false, link, visited2)); + if (linkFilters.length > 0) { + stmt = db + .selectFrom(sql`${stmt}`.as('tmp')) + .selectAll() + .where((eb) => + eb.and([ + ...linkFilters.map((link) => { + return sql.raw(`"tmp"."${link}" is not null`); + }) + ]) + ); + } + } + } + + return stmt; +}; + +export function jsonObjectFromCustom(expr: Expression): RawBuilder | null> { + return sql`(select to_json(obj) from ${expr} as obj where obj is not null)`; +} diff --git a/packages/client/src/schema/selection.spec.ts b/packages/client/src/schema/selection.spec.ts index f3db5b729..44b84f147 100644 --- a/packages/client/src/schema/selection.spec.ts +++ b/packages/client/src/schema/selection.spec.ts @@ -1,11 +1,21 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { test } from 'vitest'; +import { expect, test } from 'vitest'; import { XataRecord } from './record'; import { SelectableColumn, SelectedPick, ValueAtColumn } from './selection'; import { XataFile } from './files'; +import { Dictionary } from '../util/types'; +import path from 'path'; +import { AliasedRawBuilder, ExpressionBuilder, RawBuilder, SelectExpression, SelectQueryBuilder, sql } from 'kysely'; +import { KyselyPlugin } from '../kysely'; +import { Model } from '@xata.io/kysely'; +import { jsonObjectFrom } from 'kysely/helpers/postgres'; interface Team { + xata_id: string; + xata_version: number; + xata_createdat: Date; + xata_updatedat: Date; name: string; labels?: string[] | null; owner?: UserRecord | null; @@ -14,6 +24,10 @@ interface Team { type TeamRecord = Team & XataRecord; interface User { + xata_id: string; + xata_version: number; + xata_createdat: Date; + xata_updatedat: Date; email?: string | null; full_name: string; team?: TeamRecord | null; @@ -27,7 +41,7 @@ type UserRecord = User & XataRecord; // SelectableColumn // // --------------------------------------------------------------------------- // -const validTeamColumns: SelectableColumn[] = ['*', 'id', 'name', 'owner.*', 'owner.date']; +const validTeamColumns: SelectableColumn[] = ['*', 'xata_id', 'name', 'owner.*', 'owner.date']; // @ts-expect-error const invalidFullNameTeamColumn: SelectableColumn = 'full_name'; @@ -41,12 +55,12 @@ const invalidReadTeamColumn: SelectableColumn = 'owner.read.*'; const invalidInternalDateColumns: SelectableColumn = 'owner.date.getFullYear'; // Internal columns -const internalVersionColumns: SelectableColumn = 'xata.version'; -const internalCreatedAtColumns: SelectableColumn = 'xata.createdAt'; -const internalUpdatedAtColumns: SelectableColumn = 'xata.updatedAt'; -const linkVersionColumns: SelectableColumn = 'owner.xata.version'; -const linkCreatedAtColumns: SelectableColumn = 'owner.xata.createdAt'; -const linkUpdatedAtColumns: SelectableColumn = 'owner.xata.updatedAt'; +const internalVersionColumns: SelectableColumn = 'xata_version'; +const internalCreatedAtColumns: SelectableColumn = 'xata_createdat'; +const internalUpdatedAtColumns: SelectableColumn = 'xata_updatedat'; +const linkVersionColumns: SelectableColumn = 'owner.xata_version'; +const linkCreatedAtColumns: SelectableColumn = 'owner.xata_createdat'; +const linkUpdatedAtColumns: SelectableColumn = 'owner.xata_updatedat'; // ValueAtColumn // // --------------------------------------------------------------------------- // @@ -59,33 +73,22 @@ const invalidLabelsValue: ValueAtColumn = [1]; // ---------------------------------------------------------------------------- // function test1(user: SelectedPick) { - user.id; + user.xata_id; user.read(); user.full_name; - user.xata.version; - user.xata.createdAt; - user.xata.updatedAt; + user.xata_version; + user.xata_createdat; + user.xata_updatedat; // @ts-expect-error - user.team.id; - user.team?.id; + user.team.xata_id; + user.team?.xata_id; user.team?.read(); // @ts-expect-error user.team?.name; - // TODO(link.xata) @ts-expect-error - user.team?.xata.version; - // TODO(link.xata) @ts-expect-error - user.team?.xata.createdAt; - // TODO(link.xata) @ts-expect-error - user.team?.xata.updatedAt; - - user.team?.xata?.version; - user.team?.xata?.createdAt; - user.team?.xata?.updatedAt; - - user.partner.id; + user.partner.xata_id; user.partner.read(); // @ts-expect-error user.partner.full_name; @@ -96,14 +99,14 @@ function test1(user: SelectedPick) { } function test2(user: SelectedPick) { - user.id; + user.xata_id; user.read(); user.full_name; - user.team?.id; + user.team?.xata_id; user.team?.read(); user.team?.name; user.team?.owner; - user.team?.owner?.id; + user.team?.owner?.xata_id; user.team?.owner?.read(); // @ts-expect-error user.team?.owner?.full_name; @@ -125,29 +128,29 @@ function test2(user: SelectedPick) { } function test3(user: SelectedPick) { - user.id; + user.xata_id; user.read(); // @ts-expect-error user.full_name; - user.team?.id; + user.team?.xata_id; user.team?.read(); // @ts-expect-error user.team?.name; - user.team?.owner?.id; + user.team?.owner?.xata_id; user.team?.owner?.read(); user.team?.owner?.full_name; } function test4(user: SelectedPick) { user.partner; - user.partner.id; + user.partner.xata_id; user.partner.read(); user.partner.full_name; // @ts-expect-error user.partner.full_name = null; // @ts-expect-error - user.team.id; - user.team?.id; + user.team.xata_id; + user.team?.xata_id; // @ts-expect-error user.team.read(); user.team?.read(); @@ -159,14 +162,14 @@ function test4(user: SelectedPick) { function test5(user: SelectedPick) { user.partner; - user.partner.id; + user.partner.xata_id; user.partner.read(); user.partner.full_name; // @ts-expect-error user.partner.full_name = null; // @ts-expect-error - user.team.id; - user.team?.id; + user.team.xata_id; + user.team?.xata_id; // @ts-expect-error user.team.read(); user.team?.read(); diff --git a/packages/client/src/schema/selection.ts b/packages/client/src/schema/selection.ts index f0d0afc6d..a4404d102 100644 --- a/packages/client/src/schema/selection.ts +++ b/packages/client/src/schema/selection.ts @@ -1,5 +1,5 @@ import { isObject, isString } from '../util/lang'; -import { If, IsArray, IsObject, StringKeys, UnionToIntersection, Values } from '../util/types'; +import { Dictionary, If, IsArray, IsObject, StringKeys, UnionToIntersection, Values } from '../util/types'; import { XataArrayFile, XataFile, XataFileFields } from './files'; import { Link, XataRecord } from './record'; @@ -7,10 +7,6 @@ import { Link, XataRecord } from './record'; export type SelectableColumn = // Alias for any property | '*' - // Alias for id (not in schema) - | 'id' - // Internal properties - | `xata.${'version' | 'createdAt' | 'updatedAt'}` // Properties of the current level | DataProps // Nested properties of the lower levels @@ -79,7 +75,7 @@ export type ColumnsByValue = Values<{ }>; // Public: Utility type to get the XataRecord built from a list of selected columns -export type SelectedPick[]> = XataRecord & +export type SelectedPick[]> = XataRecord & // For each column, we get its nested value and join it as an intersection UnionToIntersection< Values<{ @@ -99,14 +95,6 @@ export type ValueAtColumn = Recur ? never : Key extends '*' ? Values // Alias for any property - : Key extends 'id' - ? string // Alias for id (not in schema) - : Key extends 'xata.version' - ? number - : Key extends 'xata.createdAt' - ? Date - : Key extends 'xata.updatedAt' - ? Date : Key extends keyof Object ? Object[Key] // Properties of the current level : Key extends `${infer K}.${infer V}` @@ -163,7 +151,7 @@ type NestedColumns = RecursivePath['length'] ext >; // Private: Utility type to get object properties without XataRecord ones -type DataProps = Exclude, StringKeys>; +type DataProps = Exclude, StringKeys>>; // Private: Utility type to get the value of a column at a given path (nested object value) // For "foo.bar.baz" we return { foo: { bar: { baz: type } } } @@ -193,7 +181,7 @@ type NestedValueAtColumn> = ? // If the property is a link, we forward the type of the internal XataRecord // Since it can be nullable, we use ForwardNullable to avoid loosing the internal type // Links that are not expanded ["link"] instead of ["link.*"] don't have the xata property - ForwardNullable, ['*']>, 'xata' | 'getMetadata'>> + ForwardNullable, ['*']>> : O[K]; } : Key extends '*' @@ -205,3 +193,8 @@ type NestedValueAtColumn> = : unknown; //`Property ${Key} is invalid`; type ForwardNullable = T extends NonNullable ? R : R | null; + +export type ColumnSelectionObject = { + links: Dictionary; + regular: string[]; +}; diff --git a/packages/client/src/schema/sorting.spec.ts b/packages/client/src/schema/sorting.spec.ts index 14db7fbab..2363c43b0 100644 --- a/packages/client/src/schema/sorting.spec.ts +++ b/packages/client/src/schema/sorting.spec.ts @@ -4,6 +4,10 @@ import { XataRecord } from './record'; import { ApiSortFilter } from './sorting'; type Record = XataRecord & { + xata_id: string; + xata_version: number; + xata_createdat: Date; + xata_updatedat: Date; name: string; string: string; number: number; @@ -31,10 +35,10 @@ const sortWithRandomWildcard: ApiSortFilter = { '*': 'random' }; const sortWithRandomWildcardOnColumn: ApiSortFilter = { name: 'random' }; // Sort by updatedAt is allowed -const sortWithUpdatedAt: ApiSortFilter = { 'xata.updatedAt': 'asc' }; +const sortWithUpdatedAt: ApiSortFilter = { xata_updatedat: 'asc' }; // Sort by createdAt is allowed -const sortWithCreatedAt: ApiSortFilter = { 'xata.createdAt': 'asc' }; +const sortWithCreatedAt: ApiSortFilter = { xata_createdat: 'asc' }; // Sort by unknown metadata is not allowed //@ts-expect-error diff --git a/packages/client/src/schema/sorting.ts b/packages/client/src/schema/sorting.ts index e53f8579a..8e00e2524 100644 --- a/packages/client/src/schema/sorting.ts +++ b/packages/client/src/schema/sorting.ts @@ -1,6 +1,5 @@ import { isObject, isString } from '../util/lang'; import { SingleOrArray, Values } from '../util/types'; -import { XataRecord, XataRecordMetadata } from './record'; import { ColumnsByValue } from './selection'; export type SortDirection = 'asc' | 'desc'; @@ -8,37 +7,37 @@ export type SortDirection = 'asc' | 'desc'; type RandomFilter = { '*': 'random' }; type RandomFilterExtended = { column: '*'; direction: 'random' }; -export type SortColumns = ColumnsByValue | `xata.${keyof XataRecordMetadata}`; +export type SortColumns = ColumnsByValue; -export type SortFilterExtended> = +export type SortFilterExtended> = | RandomFilterExtended | { column: Columns; direction?: SortDirection; }; -export type SortFilter> = +export type SortFilter> = | Columns | SortFilterExtended | SortFilterBase | RandomFilter; -type SortFilterBase> = Values<{ +type SortFilterBase> = Values<{ [Key in Columns]: { [K in Key]: SortDirection }; }>; -export type ApiSortFilter> = SingleOrArray< +export type ApiSortFilter> = SingleOrArray< | RandomFilter | Values<{ [Key in Columns]: { [K in Key]: SortDirection }; }> >; -export function isSortFilterString(value: any): value is SortColumns { +export function isSortFilterString(value: any): value is SortColumns { return isString(value); } -export function isSortFilterBase(filter: SortFilter): filter is SortFilterBase { +export function isSortFilterBase(filter: SortFilter): filter is SortFilterBase { return ( isObject(filter) && Object.entries(filter).every(([key, value]) => { @@ -50,13 +49,11 @@ export function isSortFilterBase(filter: SortFilter(filter: SortFilter): filter is SortFilterExtended { +export function isSortFilterObject(filter: SortFilter): filter is SortFilterExtended { return isObject(filter) && !isSortFilterBase(filter) && filter.column !== undefined; } -export function buildSortFilter( - filter: SingleOrArray> -): ApiSortFilter { +export function buildSortFilter(filter: SingleOrArray>): ApiSortFilter { if (isSortFilterString(filter)) { return { [filter]: 'asc' } as { [key in SortColumns]: SortDirection }; } else if (Array.isArray(filter)) { diff --git a/packages/client/src/schema/summarize.ts b/packages/client/src/schema/summarize.ts index 78e58544f..0c0fe9e5f 100644 --- a/packages/client/src/schema/summarize.ts +++ b/packages/client/src/schema/summarize.ts @@ -1,37 +1,36 @@ import { Dictionary, ExactlyOne, SingleOrArray, StringKeys } from '../util/types'; import { Filter } from './filters'; -import { XataRecord } from './record'; import { ColumnsByValue, SelectableColumn, SelectedPick, ValueAtColumn } from './selection'; import { SortFilter } from './sorting'; -export type SummarizeExpression = ExactlyOne<{ - count: ColumnsByValue | '*'; - min: ColumnsByValue; - max: ColumnsByValue; - sum: ColumnsByValue; - average: ColumnsByValue; +export type SummarizeExpression = ExactlyOne<{ + count: ColumnsByValue | '*'; + min: ColumnsByValue; + max: ColumnsByValue; + sum: ColumnsByValue; + average: ColumnsByValue; }>; export type SummarizeParams< - Record extends XataRecord, - Expression extends Dictionary>, - Columns extends SelectableColumn[] + ObjectType, + Expression extends Dictionary>, + Columns extends SelectableColumn[] > = { summaries?: Expression; - summariesFilter?: SummarizeFilter; - filter?: Filter; + summariesFilter?: SummarizeFilter; + filter?: Filter; columns?: Columns; - sort?: SummarizeSort; + sort?: SummarizeSort; pagination?: { size: number }; consistency?: 'strong' | 'eventual'; }; export type SummarizeResult< - Record extends XataRecord, - Expression extends Dictionary>, - Columns extends SelectableColumn[] + ObjectType, + Expression extends Dictionary>, + Columns extends SelectableColumn[] > = { - summaries: SummarizeResultItem[]; + summaries: SummarizeResultItem[]; }; type SummarizeExpressionResultTypes = { @@ -42,18 +41,17 @@ type SummarizeExpressionResultTypes = { average: number; }; -type SummarizeSort< - Record extends XataRecord, - Expression extends Dictionary> -> = SingleOrArray | StringKeys>>; +type SummarizeSort>> = SingleOrArray< + SortFilter | StringKeys> +>; -type SummarizeValuePick>> = { +type SummarizeValuePick>> = { [K in StringKeys]: StringKeys extends infer SummarizeOperation ? SummarizeOperation extends keyof Expression[K] ? Expression[K][SummarizeOperation] extends infer Column - ? Column extends SelectableColumn + ? Column extends SelectableColumn ? SummarizeOperation extends keyof SummarizeExpressionResultTypes - ? SummarizeExpressionResultTypes>[SummarizeOperation] + ? SummarizeExpressionResultTypes>[SummarizeOperation] : never : never : never @@ -61,12 +59,12 @@ type SummarizeValuePick>> = Filter< - Record & SummarizeValuePick +type SummarizeFilter>> = Filter< + ObjectType & SummarizeValuePick >; type SummarizeResultItem< - Record extends XataRecord, - Expression extends Dictionary>, - Columns extends SelectableColumn[] -> = SummarizeValuePick & SelectedPick; + ObjectType, + Expression extends Dictionary>, + Columns extends SelectableColumn[] +> = SummarizeValuePick & SelectedPick; diff --git a/packages/client/src/search/boosters.spec.ts b/packages/client/src/search/boosters.spec.ts index ab7299ee0..f02b53018 100644 --- a/packages/client/src/search/boosters.spec.ts +++ b/packages/client/src/search/boosters.spec.ts @@ -55,7 +55,7 @@ const invalidBoosters1: Boosters[] = [ { numericBooster: { column: 'name', factor: 50, modifier: 'invalid' } }, { // @ts-expect-error - dateBooster: { column: 'createdAt', origin: '2020-01-21T00:00:00Z', scale: '1d', decay: 0.2 }, + dateBooster: { column: 'invalid', origin: '2020-01-21T00:00:00Z', scale: '1d', decay: 0.2 }, ifMatchesFilter: { noSuchColumn: 'test' } } ]; diff --git a/packages/client/src/search/boosters.ts b/packages/client/src/search/boosters.ts index a4ed86a61..c01a3d65c 100644 --- a/packages/client/src/search/boosters.ts +++ b/packages/client/src/search/boosters.ts @@ -1,4 +1,4 @@ -import { XataRecord, SelectableColumn, ValueAtColumn } from '../schema'; +import { SelectableColumn, ValueAtColumn } from '../schema'; import { Filter } from '../schema/filters'; import { ExclusiveOr, Values } from '../util/types'; @@ -72,7 +72,7 @@ type ValueBooster = { modifier?: 'none' | 'log' | 'log1p' | 'ln' | 'ln1p' | 'square' | 'sqrt' | 'reciprocal'; }; -export type Boosters = Values<{ +export type Boosters = Values<{ [K in SelectableColumn]: NonNullable> extends Date ? { dateBooster: { diff --git a/packages/client/src/search/index.ts b/packages/client/src/search/index.ts index 82371e1ce..8596835de 100644 --- a/packages/client/src/search/index.ts +++ b/packages/client/src/search/index.ts @@ -1,9 +1,9 @@ import { Responses, searchBranch } from '../api'; import { FuzzinessExpression, HighlightExpression, PrefixExpression, SearchPageConfig } from '../api/schemas'; import { XataPlugin, XataPluginOptions } from '../plugins'; -import { SchemaPluginResult } from '../schema'; +import { DatabaseSchema, SchemaInference, SchemaPluginResult } from '../schema'; import { Filter } from '../schema/filters'; -import { BaseData, XataRecord, XataRecordMetadata } from '../schema/record'; +import { BaseData, XataRecord } from '../schema/record'; import { initObject } from '../schema/repository'; import { SelectedPick } from '../schema/selection'; import { GetArrayInnerType, StringKeys, Values } from '../util/types'; @@ -64,37 +64,41 @@ export type SearchPluginResult> = { >; }; -export class SearchPlugin> extends XataPlugin { - constructor(private db: SchemaPluginResult) { +export class SearchPlugin extends XataPlugin { + constructor(private db: SchemaPluginResult) { super(); } - build(pluginOptions: XataPluginOptions): SearchPluginResult { + build(pluginOptions: XataPluginOptions): SearchPluginResult> { return { - all: async >(query: string, options: SearchOptions = {}) => { + all: async >>( + query: string, + options: SearchOptions, Tables> = {} + ) => { const { records, totalCount } = await this.#search(query, options, pluginOptions); return { totalCount, records: records.map((record) => { - const { table = 'orphan' } = record.xata; + const table = record.xata_table; + // TODO: Search endpoint doesn't support column selection - return { table, record: initObject(this.db, pluginOptions.tables, table, record, ['*']) } as any; + return { table, record: initObject(this.db, pluginOptions.schema, table, record, ['*']) } as any; }) }; }, - byTable: async >( + byTable: async >>( query: string, - options: SearchOptions = {} + options: SearchOptions, Tables> = {} ) => { const { records: rawRecords, totalCount } = await this.#search(query, options, pluginOptions); const records = rawRecords.reduce((acc, record) => { - const { table = 'orphan' } = record.xata; + const table = record.xata_table; const items = acc[table] ?? []; // TODO: Search endpoint doesn't support column selection - const item = initObject(this.db, pluginOptions.tables, table, record, ['*']); + const item = initObject(this.db, pluginOptions.schema, table, record, ['*']); return { ...acc, [table]: [...items, item] }; }, {} as any); @@ -103,9 +107,9 @@ export class SearchPlugin> extends Xa }; } - async #search>( + async #search>>( query: string, - options: SearchOptions, + options: SearchOptions, Tables>, pluginOptions: XataPluginOptions ) { const { tables, fuzziness, highlight, prefix, page } = options ?? {}; @@ -121,20 +125,17 @@ export class SearchPlugin> extends Xa } } -export type SearchXataRecord = Omit & { - xata: XataRecordMetadata & SearchExtraProperties; - getMetadata: () => XataRecordMetadata & SearchExtraProperties; -}; +export type SearchXataRecord = Record & SearchExtraProperties; type SearchExtraProperties = { /* * The record's table name. APIs that return records from multiple tables will set this field accordingly. */ - table: string; + xata_table: string; /* * Highlights of the record. This is used by the search APIs to indicate which fields and parts of the fields have matched the search. */ - highlight?: { + xata_highlight?: { [key: string]: | string[] | { @@ -144,7 +145,7 @@ type SearchExtraProperties = { /* * The record's relevancy score. This is returned by the search APIs. */ - score?: number; + xata_score?: number; }; type ReturnTable = Table extends Tables ? Table : never; diff --git a/packages/client/src/search/target.ts b/packages/client/src/search/target.ts index a40897f67..3a61d57a2 100644 --- a/packages/client/src/search/target.ts +++ b/packages/client/src/search/target.ts @@ -1,6 +1,6 @@ -import { SelectableColumn, XataRecord } from '../schema'; +import { SelectableColumn } from '../schema'; -export type TargetColumn = +export type TargetColumn = | SelectableColumn | { /** diff --git a/packages/client/src/sql/index.ts b/packages/client/src/sql/index.ts index 99436357f..0edc68b1b 100644 --- a/packages/client/src/sql/index.ts +++ b/packages/client/src/sql/index.ts @@ -99,7 +99,7 @@ export type SQLQueryResult = Mode exte ? SQLQueryResultArray : never; -type SQLPluginFunction = ( +export type SQLPluginFunction = ( query: Query, ...parameters: any[] ) => Promise< diff --git a/packages/client/src/transaction/index.ts b/packages/client/src/transaction/index.ts deleted file mode 100644 index b67f8111a..000000000 --- a/packages/client/src/transaction/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { branchTransaction } from '../api'; -import { XataPlugin, XataPluginOptions } from '../plugins'; -import { XataRecord } from '../schema/record'; -import { Narrow, StringKeys } from '../util/types'; -import { TransactionOperation, TransactionResults } from './operations'; - -export type TransactionPluginResult> = { - run: , Operations extends TransactionOperation[]>( - operations: Narrow - ) => Promise>; -}; - -export class TransactionPlugin> extends XataPlugin { - build(pluginOptions: XataPluginOptions): TransactionPluginResult { - return { - run: async (operations: any) => { - const response = await branchTransaction({ - pathParams: { workspace: '{workspaceId}', dbBranchName: '{dbBranch}', region: '{region}' }, - body: { operations: operations as any }, - ...pluginOptions - }); - - return response as any; - } - }; - } -} - -export * from './operations'; diff --git a/packages/client/src/transaction/operations.ts b/packages/client/src/transaction/operations.ts deleted file mode 100644 index 41f5cec69..000000000 --- a/packages/client/src/transaction/operations.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { BaseData, EditableData, SelectableColumn, SelectedPick, XataRecord } from '../schema'; -import { GetArrayInnerType, StringKeys, Values } from '../util/types'; - -export type TransactionOperation, Tables extends StringKeys> = - | { - insert: Values<{ - [Model in GetArrayInnerType>]: { table: Model } & InsertTransactionOperation< - Schemas[Model] & XataRecord - >; - }>; - } - | { - update: Values<{ - [Model in GetArrayInnerType>]: { table: Model } & UpdateTransactionOperation< - Schemas[Model] & XataRecord - >; - }>; - } - | { - delete: Values<{ - [Model in GetArrayInnerType>]: { table: Model } & DeleteTransactionOperation; - }>; - } - | { - get: Values<{ - [Model in GetArrayInnerType>]: { table: Model } & GetTransactionOperation< - Schemas[Model] & XataRecord - >; - }>; - }; - -export type InsertTransactionOperation = { - record: Partial>; - ifVersion?: number; - createOnly?: boolean; -}; - -export type UpdateTransactionOperation = { - id: string; - fields: Partial>; - ifVersion?: number; - upsert?: boolean; -}; - -export type DeleteTransactionOperation = { - id: string; - failIfMissing?: boolean; -}; - -export type GetTransactionOperation = { - id: string; - columns?: SelectableColumn[]; -}; - -type TransactionOperationSingleResult< - Schema extends Record, - Tables extends StringKeys, - Operation extends TransactionOperation -> = Operation extends { insert: { table: Tables; record: { id: infer Id } } } - ? { operation: 'insert'; id: Id; rows: number } - : Operation extends { insert: { table: Tables } } - ? { operation: 'insert'; id: string; rows: number } - : Operation extends { update: { table: Tables; id: infer Id } } - ? { operation: 'update'; id: Id; rows: number } - : Operation extends { delete: { table: Tables } } - ? { operation: 'delete'; rows: number } - : Operation extends { get: { table: infer Table } } - ? Table extends Tables - ? // TODO: Column inference is lost in this case. We should fix this in the future. - { operation: 'get'; columns: SelectedPick } - : never - : never; - -type TransactionOperationResults< - Schema extends Record, - Table extends StringKeys, - Operations extends TransactionOperation[] -> = Operations extends [infer Head, ...infer Rest] - ? Head extends TransactionOperation - ? Rest extends TransactionOperation[] - ? [TransactionOperationSingleResult, ...TransactionOperationResults] - : never - : never - : []; // Default to empty array, if we use never, the array inference will fail - -export type TransactionResults< - Schema extends Record, - Table extends StringKeys, - Operations extends TransactionOperation[] -> = { - results: TransactionOperationResults; -}; diff --git a/packages/client/src/types/global-deno.d.ts b/packages/client/src/types/global-deno.d.ts deleted file mode 100644 index e7ebf94f6..000000000 --- a/packages/client/src/types/global-deno.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare namespace Deno { - const env: { - get(name: string): string | undefined; - }; - - function run(options: { cmd: string[]; stdout?: string; stderr?: string }): { output(): Promise }; -} diff --git a/packages/client/src/types/global-node.d.ts b/packages/client/src/types/global-node.d.ts deleted file mode 100644 index bd2d275b5..000000000 --- a/packages/client/src/types/global-node.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare namespace process { - const env: Record; -} - -declare function require(module: string): any; diff --git a/packages/client/src/types/global-variables.d.ts b/packages/client/src/types/global-variables.d.ts deleted file mode 100644 index bad8d060e..000000000 --- a/packages/client/src/types/global-variables.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare const XATA_DATABASE_URL: string | undefined; -declare const XATA_API_KEY: string | undefined; -declare const XATA_BRANCH: string | undefined; -declare const XATA_ENABLE_BROWSER: string | boolean | undefined; diff --git a/packages/client/src/util/environment.ts b/packages/client/src/util/environment.ts index a47c831af..757aff50a 100644 --- a/packages/client/src/util/environment.ts +++ b/packages/client/src/util/environment.ts @@ -1,151 +1,36 @@ -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// +import { isObject } from './lang'; -import { isDefined, isObject } from './lang'; - -interface Environment { - apiKey: string | undefined; - databaseURL: string | undefined; - branch: string | undefined; - deployPreview: string | undefined; - deployPreviewBranch: string | undefined; - vercelGitCommitRef: string | undefined; - vercelGitRepoOwner: string | undefined; -} - -export function getEnvironment(): Environment { - // Node.js: process.env - try { - // Not using typeof process.env === 'object' because it's not working in some environments like Bun - if (isDefined(process) && isDefined(process.env)) { - return { - apiKey: process.env.XATA_API_KEY ?? getGlobalApiKey(), - databaseURL: process.env.XATA_DATABASE_URL ?? getGlobalDatabaseURL(), - branch: process.env.XATA_BRANCH ?? getGlobalBranch(), - deployPreview: process.env.XATA_PREVIEW, - deployPreviewBranch: process.env.XATA_PREVIEW_BRANCH, - vercelGitCommitRef: process.env.VERCEL_GIT_COMMIT_REF, - vercelGitRepoOwner: process.env.VERCEL_GIT_REPO_OWNER - }; - } - } catch (err) { - // Ignore: Should never happen - } - - // Deno: Deno.env.get - try { - if (isObject(Deno) && isObject(Deno.env)) { - return { - apiKey: Deno.env.get('XATA_API_KEY') ?? getGlobalApiKey(), - databaseURL: Deno.env.get('XATA_DATABASE_URL') ?? getGlobalDatabaseURL(), - branch: Deno.env.get('XATA_BRANCH') ?? getGlobalBranch(), - deployPreview: Deno.env.get('XATA_PREVIEW'), - deployPreviewBranch: Deno.env.get('XATA_PREVIEW_BRANCH'), - vercelGitCommitRef: Deno.env.get('VERCEL_GIT_COMMIT_REF'), - vercelGitRepoOwner: Deno.env.get('VERCEL_GIT_REPO_OWNER') - }; - } - } catch (err) { - // Ignore: Will fail if not using --allow-env - } - - return { - apiKey: getGlobalApiKey(), - databaseURL: getGlobalDatabaseURL(), - branch: getGlobalBranch(), - deployPreview: undefined, - deployPreviewBranch: undefined, - vercelGitCommitRef: undefined, - vercelGitRepoOwner: undefined - }; -} - -export function getEnableBrowserVariable() { +function parseEnvironment(environment: any): Record { try { - if (isObject(process) && isObject(process.env) && process.env.XATA_ENABLE_BROWSER !== undefined) { - return process.env.XATA_ENABLE_BROWSER === 'true'; + if (typeof environment === 'function') { + return new Proxy( + {}, + { + get(target) { + return environment(target); + } + } + ) as Record; } - } catch (err) { - // Ignore: Should never happen - } - try { - if (isObject(Deno) && isObject(Deno.env) && Deno.env.get('XATA_ENABLE_BROWSER') !== undefined) { - return Deno.env.get('XATA_ENABLE_BROWSER') === 'true'; + if (isObject(environment)) { + return environment as Record; } - } catch (err) { - // Ignore: Will fail if not using --allow-env - } - - try { - return XATA_ENABLE_BROWSER === true || XATA_ENABLE_BROWSER === 'true'; - } catch (err) { - return undefined; - } -} - -function getGlobalApiKey(): string | undefined { - try { - return XATA_API_KEY; - } catch (err) { - return undefined; - } -} - -function getGlobalDatabaseURL(): string | undefined { - try { - return XATA_DATABASE_URL; - } catch (err) { - return undefined; - } -} - -function getGlobalBranch(): string | undefined { - try { - return XATA_BRANCH; - } catch (err) { - return undefined; - } -} - -export function getDatabaseURL() { - try { - const { databaseURL } = getEnvironment(); - return databaseURL; - } catch (err) { - return undefined; + } catch (error) { + // noop } -} -export function getAPIKey() { - try { - const { apiKey } = getEnvironment(); - return apiKey; - } catch (err) { - return undefined; - } -} - -export function getBranch() { - try { - const { branch } = getEnvironment(); - return branch; - } catch (err) { - return undefined; - } + return {}; } export function buildPreviewBranchName({ org, branch }: { org: string; branch: string }) { return `preview-${org}-${branch}`; } -export function getPreviewBranch() { +export function getDeployPreviewBranch(environment: any) { try { - const { deployPreview, deployPreviewBranch, vercelGitCommitRef, vercelGitRepoOwner } = getEnvironment(); + const { deployPreview, deployPreviewBranch, vercelGitCommitRef, vercelGitRepoOwner } = + parseEnvironment(environment); if (deployPreviewBranch) return deployPreviewBranch; switch (deployPreview) { diff --git a/packages/client/src/util/lang.ts b/packages/client/src/util/lang.ts index d1a8bb2d9..07b092a8a 100644 --- a/packages/client/src/util/lang.ts +++ b/packages/client/src/util/lang.ts @@ -45,6 +45,10 @@ export function isNumber(value: any): value is number { return isDefined(value) && typeof value === 'number'; } +export function isStringOrNumber(value: any): value is string | number { + return isString(value) || isNumber(value); +} + export function parseNumber(value: any): number | undefined { if (isNumber(value)) { return value; diff --git a/packages/codegen/example/build-example.mjs b/packages/codegen/example/build-example.mjs index cb8920493..9b96f3bc3 100644 --- a/packages/codegen/example/build-example.mjs +++ b/packages/codegen/example/build-example.mjs @@ -31,11 +31,11 @@ async function main() { } function replaceImport(source) { - return source.replaceAll('@xata.io/client', '../../client/src'); + return source?.replaceAll('@xata.io/client', '../../client/src'); } function undoReplaceImport(source) { - return source.replaceAll('../../client/src', '@xata.io/client'); + return source?.replaceAll('../../client/src', '@xata.io/client'); } main().catch(console.error); diff --git a/packages/codegen/example/schema.json b/packages/codegen/example/schema.json index 47e78643b..26fd4e777 100644 --- a/packages/codegen/example/schema.json +++ b/packages/codegen/example/schema.json @@ -2,7 +2,37 @@ "tables": [ { "name": "teams", + "primaryKey": ["xata_id"], + "foreignKeys": { + "owner_owner": { + "name": "team_team", + "columns": ["team"], + "referencedTable": "teams", + "referencedColumns": ["xata_id"], + "onDelete": "SET NULL" + } + }, "columns": [ + { + "name": "xata_id", + "type": "string", + "notNull": true + }, + { + "name": "xata_version", + "type": "int", + "notNull": true + }, + { + "name": "xata_createdat", + "type": "datetime", + "notNull": true + }, + { + "name": "xata_updatedat", + "type": "datetime", + "notNull": true + }, { "name": "name", "type": "string" @@ -50,17 +80,48 @@ "table": "users" } } - ], - "revLinks": [ - { - "column": "team", - "table": "users" - } ] }, { "name": "users", + "primaryKey": ["xata_id"], + "foreignKeys": { + "owner_owner": { + "name": "team_team", + "columns": ["team"], + "referencedTable": "teams", + "referencedColumns": ["xata_id"], + "onDelete": "SET NULL" + }, + "pet_pet": { + "name": "pet_pet", + "columns": ["pet"], + "referencedTable": "pets", + "referencedColumns": ["xata_id"], + "onDelete": "SET NULL" + } + }, "columns": [ + { + "name": "xata_id", + "type": "string", + "notNull": true + }, + { + "name": "xata_version", + "type": "int", + "notNull": true + }, + { + "name": "xata_createdat", + "type": "datetime", + "notNull": true + }, + { + "name": "xata_updatedat", + "type": "datetime", + "notNull": true + }, { "name": "email", "type": "email", @@ -140,17 +201,32 @@ "dimension": 4 } } - ], - "revLinks": [ - { - "column": "owner", - "table": "teams" - } ] }, { "name": "pets", + "primaryKey": ["xata_id"], "columns": [ + { + "name": "xata_id", + "type": "string", + "notNull": true + }, + { + "name": "xata_version", + "type": "int", + "notNull": true + }, + { + "name": "xata_createdat", + "type": "datetime", + "notNull": true + }, + { + "name": "xata_updatedat", + "type": "datetime", + "notNull": true + }, { "name": "name", "type": "string" @@ -163,12 +239,6 @@ "name": "num_legs", "type": "int" } - ], - "revLinks": [ - { - "column": "pet", - "table": "users" - } ] } ] diff --git a/packages/codegen/example/types.d.ts b/packages/codegen/example/types.d.ts index e994082df..6abdeced2 100644 --- a/packages/codegen/example/types.d.ts +++ b/packages/codegen/example/types.d.ts @@ -1,175 +1,240 @@ import type { BaseClientOptions, SchemaInference, XataRecord } from '../../client/src'; -declare const tables: readonly [ - { - readonly name: 'teams'; - readonly columns: readonly [ - { - readonly name: 'name'; - readonly type: 'string'; - }, - { - readonly name: 'description'; - readonly type: 'text'; - }, - { - readonly name: 'labels'; - readonly type: 'multiple'; - }, - { - readonly name: 'index'; - readonly type: 'int'; - }, - { - readonly name: 'rating'; - readonly type: 'float'; - }, - { - readonly name: 'founded_date'; - readonly type: 'datetime'; - }, - { - readonly name: 'email'; - readonly type: 'email'; - }, - { - readonly name: 'plan'; - readonly type: 'string'; - }, - { - readonly name: 'dark'; - readonly type: 'bool'; - }, - { - readonly name: 'config'; - readonly type: 'json'; - }, - { - readonly name: 'owner'; - readonly type: 'link'; - readonly link: { +declare const schema: { + readonly tables: readonly [ + { + readonly name: 'teams'; + readonly columns: readonly [ + { + readonly name: 'xata_id'; + readonly type: 'string'; + readonly notNull: true; + }, + { + readonly name: 'xata_version'; + readonly type: 'int'; + readonly notNull: true; + }, + { + readonly name: 'xata_createdat'; + readonly type: 'datetime'; + readonly notNull: true; + }, + { + readonly name: 'xata_updatedat'; + readonly type: 'datetime'; + readonly notNull: true; + }, + { + readonly name: 'name'; + readonly type: 'string'; + }, + { + readonly name: 'description'; + readonly type: 'text'; + }, + { + readonly name: 'labels'; + readonly type: 'multiple'; + }, + { + readonly name: 'index'; + readonly type: 'int'; + }, + { + readonly name: 'rating'; + readonly type: 'float'; + }, + { + readonly name: 'founded_date'; + readonly type: 'datetime'; + }, + { + readonly name: 'email'; + readonly type: 'email'; + }, + { + readonly name: 'plan'; + readonly type: 'string'; + }, + { + readonly name: 'dark'; + readonly type: 'bool'; + }, + { + readonly name: 'config'; + readonly type: 'json'; + }, + { + readonly name: 'owner'; + readonly type: 'link'; + readonly link: { + readonly table: 'users'; + }; + } + ]; + readonly revLinks: readonly [ + { readonly table: 'users'; - }; - } - ]; - readonly revLinks: readonly [ - { - readonly table: 'users'; - readonly column: 'team'; - } - ]; - }, - { - readonly name: 'users'; - readonly columns: readonly [ - { - readonly name: 'email'; - readonly type: 'email'; - readonly unique: true; - }, - { - readonly name: 'name'; - readonly type: 'string'; - }, - { - readonly name: 'photo'; - readonly type: 'file'; - }, - { - readonly name: 'attachments'; - readonly type: 'file[]'; - }, - { - readonly name: 'plan'; - readonly type: 'string'; - }, - { - readonly name: 'dark'; - readonly type: 'bool'; - }, - { - readonly name: 'full_name'; - readonly type: 'string'; - readonly notNull: true; - readonly defaultValue: 'John Doe'; - }, - { - readonly name: 'index'; - readonly type: 'int'; - }, - { - readonly name: 'rating'; - readonly type: 'float'; - }, - { - readonly name: 'birthDate'; - readonly type: 'datetime'; - }, - { - readonly name: 'street'; - readonly type: 'string'; - }, - { - readonly name: 'zipcode'; - readonly type: 'int'; - }, - { - readonly name: 'team'; - readonly type: 'link'; - readonly link: { + readonly column: 'team'; + } + ]; + }, + { + readonly name: 'users'; + readonly columns: readonly [ + { + readonly name: 'xata_id'; + readonly type: 'string'; + readonly notNull: true; + }, + { + readonly name: 'xata_version'; + readonly type: 'int'; + readonly notNull: true; + }, + { + readonly name: 'xata_createdat'; + readonly type: 'datetime'; + readonly notNull: true; + }, + { + readonly name: 'xata_updatedat'; + readonly type: 'datetime'; + readonly notNull: true; + }, + { + readonly name: 'email'; + readonly type: 'email'; + readonly unique: true; + }, + { + readonly name: 'name'; + readonly type: 'string'; + }, + { + readonly name: 'photo'; + readonly type: 'file'; + readonly file: { + readonly defaultPublicAccess: true; + }; + }, + { + readonly name: 'attachments'; + readonly type: 'file[]'; + }, + { + readonly name: 'plan'; + readonly type: 'string'; + }, + { + readonly name: 'dark'; + readonly type: 'bool'; + }, + { + readonly name: 'full_name'; + readonly type: 'string'; + readonly notNull: true; + readonly defaultValue: 'John Doe'; + }, + { + readonly name: 'index'; + readonly type: 'int'; + }, + { + readonly name: 'rating'; + readonly type: 'float'; + }, + { + readonly name: 'birthDate'; + readonly type: 'datetime'; + }, + { + readonly name: 'street'; + readonly type: 'string'; + }, + { + readonly name: 'zipcode'; + readonly type: 'int'; + }, + { + readonly name: 'team'; + readonly type: 'link'; + readonly link: { + readonly table: 'teams'; + }; + }, + { + readonly name: 'pet'; + readonly type: 'link'; + readonly link: { + readonly table: 'pets'; + }; + }, + { + readonly name: 'account_value'; + readonly type: 'int'; + }, + { + readonly name: 'vector'; + readonly type: 'vector'; + readonly vector: { + readonly dimension: 4; + }; + } + ]; + readonly revLinks: readonly [ + { readonly table: 'teams'; - }; - }, - { - readonly name: 'pet'; - readonly type: 'link'; - readonly link: { - readonly table: 'pets'; - }; - }, - { - readonly name: 'account_value'; - readonly type: 'int'; - }, - { - readonly name: 'vector'; - readonly type: 'vector'; - readonly vector: { - readonly dimension: 4; - }; - } - ]; - readonly revLinks: readonly [ - { - readonly table: 'teams'; - readonly column: 'owner'; - } - ]; - }, - { - readonly name: 'pets'; - readonly columns: readonly [ - { - readonly name: 'name'; - readonly type: 'string'; - }, - { - readonly name: 'type'; - readonly type: 'string'; - }, - { - readonly name: 'num_legs'; - readonly type: 'int'; - } - ]; - readonly revLinks: readonly [ - { - readonly table: 'users'; - readonly column: 'pet'; - } - ]; - } -]; -export type SchemaTables = typeof tables; + readonly column: 'owner'; + } + ]; + }, + { + readonly name: 'pets'; + readonly columns: readonly [ + { + readonly name: 'xata_id'; + readonly type: 'string'; + readonly notNull: true; + }, + { + readonly name: 'xata_version'; + readonly type: 'int'; + readonly notNull: true; + }, + { + readonly name: 'xata_createdat'; + readonly type: 'datetime'; + readonly notNull: true; + }, + { + readonly name: 'xata_updatedat'; + readonly type: 'datetime'; + readonly notNull: true; + }, + { + readonly name: 'name'; + readonly type: 'string'; + }, + { + readonly name: 'type'; + readonly type: 'string'; + }, + { + readonly name: 'num_legs'; + readonly type: 'int'; + } + ]; + readonly revLinks: readonly [ + { + readonly table: 'users'; + readonly column: 'pet'; + } + ]; + } + ]; +}; +export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type Teams = InferredTypes['teams']; export type TeamsRecord = Teams & XataRecord; @@ -183,8 +248,7 @@ export type DatabaseSchema = { pets: PetsRecord; }; declare const DatabaseClient: any; -export declare class XataClient extends DatabaseClient { +export declare class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions); } -export declare const getXataClient: () => XataClient; export {}; diff --git a/packages/codegen/example/xata.cjs b/packages/codegen/example/xata.cjs index fe8c8c9da..545cec57a 100644 --- a/packages/codegen/example/xata.cjs +++ b/packages/codegen/example/xata.cjs @@ -1,83 +1,107 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -exports.getXataClient = exports.XataClient = void 0; -// Generated by Xata Codegen 0.27.0. Please do not edit. -const client_1 = require('../../client/src'); +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.XataClient = void 0; +const client_1 = require("../../client/src"); /** @typedef { import('./types').SchemaTables } SchemaTables */ /** @type { SchemaTables } */ -const tables = [ - { - name: 'teams', - columns: [ - { name: 'name', type: 'string' }, - { name: 'description', type: 'text' }, - { name: 'labels', type: 'multiple' }, - { name: 'index', type: 'int' }, - { name: 'rating', type: 'float' }, - { name: 'founded_date', type: 'datetime' }, - { name: 'email', type: 'email' }, - { name: 'plan', type: 'string' }, - { name: 'dark', type: 'bool' }, - { name: 'config', type: 'json' }, - { name: 'owner', type: 'link', link: { table: 'users' } } - ], - revLinks: [{ table: 'users', column: 'team' }] - }, - { - name: 'users', - columns: [ - { name: 'email', type: 'email', unique: true }, - { name: 'name', type: 'string' }, - { name: 'photo', type: 'file' }, - { name: 'attachments', type: 'file[]' }, - { name: 'plan', type: 'string' }, - { name: 'dark', type: 'bool' }, - { - name: 'full_name', - type: 'string', - notNull: true, - defaultValue: 'John Doe' - }, - { name: 'index', type: 'int' }, - { name: 'rating', type: 'float' }, - { name: 'birthDate', type: 'datetime' }, - { name: 'street', type: 'string' }, - { name: 'zipcode', type: 'int' }, - { name: 'team', type: 'link', link: { table: 'teams' } }, - { name: 'pet', type: 'link', link: { table: 'pets' } }, - { name: 'account_value', type: 'int' }, - { name: 'vector', type: 'vector', vector: { dimension: 4 } } - ], - revLinks: [{ table: 'teams', column: 'owner' }] - }, - { - name: 'pets', - columns: [ - { name: 'name', type: 'string' }, - { name: 'type', type: 'string' }, - { name: 'num_legs', type: 'int' } - ], - revLinks: [{ table: 'users', column: 'pet' }] - } -]; +const schema = { + tables: [ + { + name: "teams", + columns: [ + { name: "xata_id", type: "string", notNull: true }, + { name: "xata_version", type: "int", notNull: true }, + { name: "xata_createdat", type: "datetime", notNull: true }, + { name: "xata_updatedat", type: "datetime", notNull: true }, + { name: "name", type: "string" }, + { name: "description", type: "text" }, + { name: "labels", type: "multiple" }, + { name: "index", type: "int" }, + { name: "rating", type: "float" }, + { name: "founded_date", type: "datetime" }, + { name: "email", type: "email" }, + { name: "plan", type: "string" }, + { name: "dark", type: "bool" }, + { name: "config", type: "json" }, + { name: "owner", type: "link", link: { table: "users" } }, + ], + revLinks: [{ table: "users", column: "team" }], + }, + { + name: "users", + columns: [ + { name: "xata_id", type: "string", notNull: true }, + { name: "xata_version", type: "int", notNull: true }, + { name: "xata_createdat", type: "datetime", notNull: true }, + { name: "xata_updatedat", type: "datetime", notNull: true }, + { name: "email", type: "email", unique: true }, + { name: "name", type: "string" }, + { name: "photo", type: "file", file: { defaultPublicAccess: true } }, + { name: "attachments", type: "file[]" }, + { name: "plan", type: "string" }, + { name: "dark", type: "bool" }, + { + name: "full_name", + type: "string", + notNull: true, + defaultValue: "John Doe", + }, + { name: "index", type: "int" }, + { name: "rating", type: "float" }, + { name: "birthDate", type: "datetime" }, + { name: "street", type: "string" }, + { name: "zipcode", type: "int" }, + { name: "team", type: "link", link: { table: "teams" } }, + { name: "pet", type: "link", link: { table: "pets" } }, + { name: "account_value", type: "int" }, + { name: "vector", type: "vector", vector: { dimension: 4 } }, + ], + revLinks: [{ table: "teams", column: "owner" }], + }, + { + name: "pets", + columns: [ + { name: "xata_id", type: "string", notNull: true }, + { name: "xata_version", type: "int", notNull: true }, + { name: "xata_createdat", type: "datetime", notNull: true }, + { name: "xata_updatedat", type: "datetime", notNull: true }, + { name: "name", type: "string" }, + { name: "type", type: "string" }, + { name: "num_legs", type: "int" }, + ], + revLinks: [{ table: "users", column: "pet" }], + }, + { + name: "numeric", + primaryKey: ["xata_id"], + columns: [ + { name: "xata_id", type: "int", notNull: true }, + { name: "xata_version", type: "int", notNull: true }, + { name: "xata_createdat", type: "datetime", notNull: true }, + { name: "xata_updatedat", type: "datetime", notNull: true } + ] + } + ], +}; /** @type { import('../../client/src').ClientConstructor<{}> } */ const DatabaseClient = (0, client_1.buildClient)(); -const defaultOptions = { - databaseURL: 'https://test-r5vcv5.eu-west-1.xata.sh/db/test' -}; /** @typedef { import('./types').DatabaseSchema } DatabaseSchema */ /** @extends DatabaseClient */ class XataClient extends DatabaseClient { constructor(options) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + (0, client_1.getDeployPreviewBranch)(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } exports.XataClient = XataClient; -let instance = undefined; -/** @type { () => XataClient } */ -const getXataClient = () => { - if (instance) return instance; - instance = new XataClient(); - return instance; -}; -exports.getXataClient = getXataClient; diff --git a/packages/codegen/example/xata.js b/packages/codegen/example/xata.js index c305d4780..432022b15 100644 --- a/packages/codegen/example/xata.js +++ b/packages/codegen/example/xata.js @@ -1,78 +1,133 @@ -// Generated by Xata Codegen 0.27.0. Please do not edit. -import { buildClient } from '../../client/src'; +import { buildClient, getDeployPreviewBranch } from '../../client/src'; /** @typedef { import('./types').SchemaTables } SchemaTables */ /** @type { SchemaTables } */ -const tables = [ - { - name: 'teams', - columns: [ - { name: 'name', type: 'string' }, - { name: 'description', type: 'text' }, - { name: 'labels', type: 'multiple' }, - { name: 'index', type: 'int' }, - { name: 'rating', type: 'float' }, - { name: 'founded_date', type: 'datetime' }, - { name: 'email', type: 'email' }, - { name: 'plan', type: 'string' }, - { name: 'dark', type: 'bool' }, - { name: 'config', type: 'json' }, - { name: 'owner', type: 'link', link: { table: 'users' } } - ], - revLinks: [{ table: 'users', column: 'team' }] - }, - { - name: 'users', - columns: [ - { name: 'email', type: 'email', unique: true }, - { name: 'name', type: 'string' }, - { name: 'photo', type: 'file' }, - { name: 'attachments', type: 'file[]' }, - { name: 'plan', type: 'string' }, - { name: 'dark', type: 'bool' }, - { - name: 'full_name', - type: 'string', - notNull: true, - defaultValue: 'John Doe' +const schema = { + tables: [ + { + name: 'teams', + foreignKeys: { + owner_owner: { + name: 'owner_owner', + columns: ['owner'], + referencedTable: 'users', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + }, + pet_pet: { + name: 'pet_pet', + columns: ['pet'], + referencedTable: 'pets', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + } }, - { name: 'index', type: 'int' }, - { name: 'rating', type: 'float' }, - { name: 'birthDate', type: 'datetime' }, - { name: 'street', type: 'string' }, - { name: 'zipcode', type: 'int' }, - { name: 'team', type: 'link', link: { table: 'teams' } }, - { name: 'pet', type: 'link', link: { table: 'pets' } }, - { name: 'account_value', type: 'int' }, - { name: 'vector', type: 'vector', vector: { dimension: 4 } } - ], - revLinks: [{ table: 'teams', column: 'owner' }] - }, - { - name: 'pets', - columns: [ - { name: 'name', type: 'string' }, - { name: 'type', type: 'string' }, - { name: 'num_legs', type: 'int' } - ], - revLinks: [{ table: 'users', column: 'pet' }] - } -]; + columns: [ + { name: 'xata_id', type: 'string', notNull: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'name', type: 'string' }, + { name: 'description', type: 'text' }, + { name: 'labels', type: 'multiple' }, + { name: 'index', type: 'int' }, + { name: 'rating', type: 'float' }, + { name: 'founded_date', type: 'datetime' }, + { name: 'email', type: 'email' }, + { name: 'plan', type: 'string' }, + { name: 'dark', type: 'bool' }, + { name: 'config', type: 'json' }, + { name: 'owner', type: 'link', link: { table: 'users' } }, + { name: 'pet', type: 'link', link: { table: 'pets' } } + ], + revLinks: [{ table: 'users', column: 'team' }] + }, + { + name: 'users', + columns: [ + { name: 'xata_id', type: 'string', notNull: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'email', type: 'email', unique: true }, + { name: 'name', type: 'string' }, + { name: 'photo', type: 'file', file: { defaultPublicAccess: true } }, + { name: 'attachments', type: 'file[]' }, + { name: 'plan', type: 'string' }, + { name: 'dark', type: 'bool' }, + { + name: 'full_name', + type: 'string', + notNull: true, + defaultValue: 'John Doe' + }, + { name: 'index', type: 'int' }, + { name: 'rating', type: 'float' }, + { name: 'birthDate', type: 'datetime' }, + { name: 'street', type: 'string' }, + { name: 'zipcode', type: 'int' }, + { name: 'team', type: 'link', link: { table: 'teams' } }, + { name: 'pet', type: 'link', link: { table: 'pets' } }, + { name: 'account_value', type: 'int' }, + { name: 'vector', type: 'vector', vector: { dimension: 4 } } + ], + foreignKeys: { + owner_owner: { + name: 'team_team', + columns: ['team'], + referencedTable: 'teams', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + }, + pet_pet: { + name: 'pet_pet', + columns: ['pet'], + referencedTable: 'pets', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + } + }, + revLinks: [{ table: 'teams', column: 'owner' }] + }, + { + name: 'pets', + columns: [ + { name: 'xata_id', type: 'string', notNull: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'name', type: 'string' }, + { name: 'type', type: 'string' }, + { name: 'num_legs', type: 'int' } + ], + revLinks: [{ table: 'users', column: 'pet' }] + }, + { + name: 'numeric', + primaryKey: ['xata_id'], + columns: [ + { name: 'xata_id', type: 'int', notNull: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + } + ] +}; /** @type { import('../../client/src').ClientConstructor<{}> } */ const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: 'https://test-r5vcv5.eu-west-1.xata.sh/db/test' -}; /** @typedef { import('./types').DatabaseSchema } DatabaseSchema */ /** @extends DatabaseClient */ export class XataClient extends DatabaseClient { constructor(options) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: getDeployPreviewBranch(process.env) ?? process.env.XATA_BRANCH ?? 'main', + ...options + }, + schema + ); } } -let instance = undefined; -/** @type { () => XataClient } */ -export const getXataClient = () => { - if (instance) return instance; - instance = new XataClient(); - return instance; -}; diff --git a/packages/codegen/example/xata.ts b/packages/codegen/example/xata.ts index 963d89588..3dedaf396 100644 --- a/packages/codegen/example/xata.ts +++ b/packages/codegen/example/xata.ts @@ -1,97 +1,149 @@ -import { buildClient } from '../../client/src'; +import { buildClient, getDeployPreviewBranch } from '../../client/src'; import type { BaseClientOptions, SchemaInference, XataRecord } from '../../client/src'; -// This comment should be preserved by the codegen -const tables = [ - { - name: 'teams', - columns: [ - { name: 'name', type: 'string' }, - { name: 'description', type: 'text' }, - { name: 'labels', type: 'multiple' }, - { name: 'index', type: 'int' }, - { name: 'rating', type: 'float' }, - { name: 'founded_date', type: 'datetime' }, - { name: 'email', type: 'email' }, - { name: 'plan', type: 'string' }, - { name: 'dark', type: 'bool' }, - { name: 'config', type: 'json' }, - { name: 'owner', type: 'link', link: { table: 'users' } } - ], - revLinks: [{ table: 'users', column: 'team' }] - }, - { - name: 'users', - columns: [ - { name: 'email', type: 'email', unique: true }, - { name: 'name', type: 'string' }, - { name: 'photo', type: 'file' }, - { name: 'attachments', type: 'file[]' }, - { name: 'plan', type: 'string' }, - { name: 'dark', type: 'bool' }, - { - name: 'full_name', - type: 'string', - notNull: true, - defaultValue: 'John Doe' +const schema = { + tables: [ + { + name: 'teams', + primaryKey: ['xata_id'], + foreignKeys: { + owner_owner: { + name: 'owner_owner', + columns: ['owner'], + referencedTable: 'users', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + } }, - { name: 'index', type: 'int' }, - { name: 'rating', type: 'float' }, - { name: 'birthDate', type: 'datetime' }, - { name: 'street', type: 'string' }, - { name: 'zipcode', type: 'int' }, - { name: 'team', type: 'link', link: { table: 'teams' } }, - { name: 'pet', type: 'link', link: { table: 'pets' } }, - { name: 'account_value', type: 'int' }, - { name: 'vector', type: 'vector', vector: { dimension: 4 } } - ], - revLinks: [{ table: 'teams', column: 'owner' }] - }, - { - name: 'pets', - columns: [ - { name: 'name', type: 'string' }, - { name: 'type', type: 'string' }, - { name: 'num_legs', type: 'int' } - ], - revLinks: [{ table: 'users', column: 'pet' }] - } -] as const; + columns: [ + { name: 'xata_id', type: 'string', notNull: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'name', type: 'string' }, + { name: 'description', type: 'text' }, + { name: 'labels', type: 'multiple' }, + { name: 'index', type: 'int' }, + { name: 'rating', type: 'float' }, + { name: 'founded_date', type: 'datetime' }, + { name: 'email', type: 'email' }, + { name: 'plan', type: 'string' }, + { name: 'dark', type: 'bool' }, + { name: 'config', type: 'json' }, + { name: 'owner', type: 'link', link: { table: 'users' } } + ], + revLinks: [{ table: 'users', column: 'team' }] + }, + { + name: 'users', + primaryKey: ['xata_id'], + columns: [ + { name: 'xata_id', type: 'string', notNull: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'email', type: 'email', unique: true }, + { name: 'name', type: 'string' }, + { name: 'photo', type: 'file', file: { defaultPublicAccess: true } }, + { name: 'attachments', type: 'file[]' }, + { name: 'plan', type: 'string' }, + { name: 'dark', type: 'bool' }, + { + name: 'full_name', + type: 'string', + notNull: true, + defaultValue: 'John Doe' + }, + { name: 'index', type: 'int' }, + { name: 'rating', type: 'float' }, + { name: 'birthDate', type: 'datetime' }, + { name: 'street', type: 'string' }, + { name: 'zipcode', type: 'int' }, + { name: 'team', type: 'link', link: { table: 'teams' } }, + { name: 'pet', type: 'link', link: { table: 'pets' } }, + { name: 'account_value', type: 'int' }, + { name: 'vector', type: 'vector', vector: { dimension: 4 } } + ], + foreignKeys: { + owner_owner: { + name: 'team_team', + columns: ['team'], + referencedTable: 'teams', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + }, + pet_pet: { + name: 'pet_pet', + columns: ['pet'], + referencedTable: 'pets', + referencedColumns: ['xata_id'], + onDelete: 'SET NULL' + } + }, + revLinks: [{ table: 'teams', column: 'owner' }] + }, + { + name: 'pets', + primaryKey: [], + columns: [ + { name: 'xata_id', type: 'string', notNull: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true }, + { name: 'name', type: 'string' }, + { name: 'type', type: 'string' }, + { name: 'num_legs', type: 'int' } + ], + revLinks: [{ table: 'users', column: 'pet' }] + }, + { + name: 'numeric', + primaryKey: ['xata_id'], + columns: [ + { name: 'xata_id', type: 'int', notNull: true }, + { name: 'xata_version', type: 'int', notNull: true }, + { name: 'xata_createdat', type: 'datetime', notNull: true }, + { name: 'xata_updatedat', type: 'datetime', notNull: true } + ] + } + ] +} as const; -export type SchemaTables = typeof tables; +export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; + +export type Teams = InferredTypes['teams']; +export type TeamsRecord = Teams & XataRecord; + +export type Users = InferredTypes['users']; +export type UsersRecord = Users & XataRecord; + +export type Pets = InferredTypes['pets']; +export type PetsRecord = Pets & XataRecord; + +export type Numeric = InferredTypes['numeric']; +export type NumericRecord = Numeric & XataRecord; + export type DatabaseSchema = { teams: TeamsRecord; users: UsersRecord; pets: PetsRecord; + numeric: NumericRecord; }; const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: 'https://test-r5vcv5.eu-west-1.xata.sh/db/test' -}; - -export class XataClient extends DatabaseClient { +export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: getDeployPreviewBranch(process.env) ?? process.env.XATA_BRANCH ?? 'main', + ...options + }, + schema + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; - -export type Teams = InferredTypes['teams']; -export type TeamsRecord = Teams & XataRecord; - -export type Users = InferredTypes['users']; -export type UsersRecord = Users & XataRecord; - -export type Pets = InferredTypes['pets']; -export type PetsRecord = Pets & XataRecord; diff --git a/packages/codegen/src/codegen.ts b/packages/codegen/src/codegen.ts index a7f991b83..d9b1a150a 100644 --- a/packages/codegen/src/codegen.ts +++ b/packages/codegen/src/codegen.ts @@ -5,16 +5,12 @@ import * as parserTypeScript from 'prettier/parser-typescript.js'; import { Project, VariableDeclarationKind } from 'ts-morph'; import ts from 'typescript'; import { XataDatabaseSchema } from './schema'; -import { VERSION } from './version'; export type GenerateOptions = { schema: XataDatabaseSchema; - databaseURL: string; language: Language; moduleType?: ModuleType; javascriptTarget?: JavascriptTarget; - branch?: string; - workspace?: string; existingCode?: string; }; @@ -25,7 +21,7 @@ export type GenerateOutput = { }; export type Language = 'typescript' | 'javascript'; -export type ModuleType = 'esm' | 'cjs' | 'deno'; +export type ModuleType = 'esm' | 'cjs' | 'deno' | 'vite'; export type JavascriptTarget = keyof typeof ts.ScriptTarget | undefined; export function isValidJavascriptTarget(target?: string): target is JavascriptTarget { @@ -45,10 +41,8 @@ function getTypeName(tableName: string) { } export async function generate({ - databaseURL, - branch, language, - moduleType, + moduleType = 'esm', javascriptTarget, schema, existingCode @@ -65,7 +59,7 @@ export async function generate({ const sourceFile = project.createSourceFile('xata.ts', existingCode); const packageName = moduleType === 'deno' ? 'npm:@xata.io/client@latest' : '@xata.io/client'; - const packageImports = ['buildClient']; + const packageImports = ['buildClient', 'getDeployPreviewBranch']; const typeImports = ['BaseClientOptions', 'SchemaInference', 'XataRecord']; const importDeclarations = sourceFile @@ -93,11 +87,7 @@ export async function generate({ ); if (!sdkImport) { - sourceFile.addImportDeclaration({ - namedImports: packageImports, - moduleSpecifier: packageName, - leadingTrivia: existingCode ? undefined : `// Generated by Xata Codegen ${VERSION}. Please do not edit.\n` - }); + sourceFile.addImportDeclaration({ namedImports: packageImports, moduleSpecifier: packageName }); } else { const namedImports = new Set([...sdkImport.getNamedImports().map((i) => i.getName()), ...packageImports]); sdkImport.removeNamedImports(); @@ -122,14 +112,14 @@ export async function generate({ typesImport.addNamedImports([...namedImports]); } - // Add tables schema - const tablesList = sourceFile.getVariableDeclaration('tables'); - const tablesListContent = `${JSON.stringify(schema.tables)} as const`; + // Add database schema + const clientSchema = sourceFile.getVariableDeclaration('schema'); + const clientSchemaContent = `${JSON.stringify(schema)} as const`; - if (!tablesList) { + if (!clientSchema) { sourceFile.addVariableStatement({ declarationKind: VariableDeclarationKind.Const, - declarations: [{ name: 'tables', initializer: tablesListContent }], + declarations: [{ name: 'schema', initializer: clientSchemaContent }], leadingTrivia: language === 'javascript' ? `/** @typedef { import('./types').SchemaTables } SchemaTables */ @@ -138,12 +128,12 @@ export async function generate({ trailingTrivia: '\n' }); } else { - tablesList.setInitializer(tablesListContent); + clientSchema.setInitializer(clientSchemaContent); } // Add schema tables types const schemaTables = sourceFile.getTypeAlias('SchemaTables'); - const schemaTablesContent = `typeof tables`; + const schemaTablesContent = `typeof schema.tables`; if (!schemaTables) { sourceFile.addTypeAlias({ name: 'SchemaTables', type: schemaTablesContent, isExported: true }); @@ -224,28 +214,11 @@ export async function generate({ databaseClient.setInitializer(databaseClientContent); } - // Add default options - const defaultOptions = sourceFile.getVariableDeclaration('defaultOptions'); - const defaultOptionsContent = JSON.stringify({ databaseURL, branch }); - - if (!defaultOptions) { - sourceFile.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [{ name: 'defaultOptions', initializer: defaultOptionsContent }], - leadingTrivia: '\n' - }); - } else { - // TODO: merge with existing options - defaultOptions.setInitializer(defaultOptionsContent); - } - - // Add XataClient class - const xataClient = sourceFile.getClass('XataClient'); - - if (!xataClient) { + // Add XataClient class if doesn't exist already + if (!sourceFile.getClass('XataClient')) { sourceFile.addClass({ name: 'XataClient', - extends: 'DatabaseClient', + extends: 'DatabaseClient', isExported: true, leadingTrivia: language === 'javascript' @@ -261,52 +234,19 @@ export async function generate({ hasQuestionToken: true } ], - statements: `super({ ...defaultOptions, ...options }, tables);` + statements: `super({ + apiKey: ${envVariable(moduleType, 'XATA_API_KEY')}, + databaseURL: ${envVariable(moduleType, 'XATA_DATABASE_URL')}, + // Use deploy preview branch if available, otherwise use branch from environment + branch: getDeployPreviewBranch(${envLoader(moduleType)}) ?? ${envVariable( + moduleType, + 'XATA_BRANCH' + )} ?? 'main', + ...options + }, schema);` } ] }); - } else { - // noop: we don't want to overwrite their constructor - } - - // Add XataClient instance - const xataClientInstance = sourceFile.getVariableDeclaration('instance'); - const getXataClient = sourceFile.getVariableDeclaration('getXataClient'); - - if (!getXataClient) { - if (!xataClientInstance) { - sourceFile.addVariableStatement({ - declarationKind: VariableDeclarationKind.Let, - declarations: [ - { - name: 'instance', - initializer: 'undefined', - type: 'XataClient | undefined' - } - ], - trailingTrivia: '\n' - }); - } - - sourceFile.addVariableStatement({ - declarationKind: VariableDeclarationKind.Const, - declarations: [ - { - name: 'getXataClient', - initializer: `() => { - if (instance) return instance; - - instance = new XataClient(); - return instance; - }` - } - ], - isExported: true, - leadingTrivia: language === 'javascript' ? `\n/** @type { () => XataClient } */\n` : '\n', - trailingTrivia: '\n' - }); - } else { - // noop: we don't want to overwrite their instance getter } sourceFile.saveSync(); @@ -361,3 +301,27 @@ function emitDeclarations(code: string) { return files.get('index.d.ts'); } + +function envLoader(module: ModuleType) { + switch (module) { + case 'cjs': + case 'esm': + return `process.env`; + case 'deno': + return `Deno.env.get`; + case 'vite': + return `import.meta.env`; + } +} + +function envVariable(module: ModuleType, variable: string) { + switch (module) { + case 'cjs': + case 'esm': + return `process.env.${variable}`; + case 'deno': + return `Deno.env.get("${variable}")`; + case 'vite': + return `import.meta.env.${variable}`; + } +} diff --git a/packages/codegen/src/index.ts b/packages/codegen/src/index.ts index e49d64c0a..dea04ca17 100644 --- a/packages/codegen/src/index.ts +++ b/packages/codegen/src/index.ts @@ -1,4 +1,4 @@ export { generate, isValidJavascriptTarget, javascriptTargets } from './codegen'; export type { GenerateOptions, Language, ModuleType } from './codegen'; -export { parseSchemaFile, columnSchema, tableSchema, revlinkSchema } from './schema'; +export { parseSchemaFile, columnSchema, tableSchema } from './schema'; export type { Column, Table, XataDatabaseSchema } from './schema'; diff --git a/packages/codegen/src/schema.ts b/packages/codegen/src/schema.ts index 7f90b4b84..fb900d809 100644 --- a/packages/codegen/src/schema.ts +++ b/packages/codegen/src/schema.ts @@ -55,15 +55,10 @@ export const columnSchema: z.ZodSchema = z.lazy(() => }) ); -export const revlinkSchema = z.object({ - table: z.string(), - column: z.string() -}); - export const tableSchema = z.object({ name: z.string(), columns: z.array(columnSchema), - revLinks: z.array(revlinkSchema).optional() + primaryKey: z.array(z.string()).optional() }); export type Table = z.infer; diff --git a/packages/importer/package.json b/packages/importer/package.json index f51827c42..8fb2fa4b4 100644 --- a/packages/importer/package.json +++ b/packages/importer/package.json @@ -30,7 +30,7 @@ "homepage": "https://github.com/xataio/client-ts/blob/main/importer/README.md", "dependencies": { "@faker-js/faker": "^8.4.1", - "@xata.io/client": "^0.29.5", + "@xata.io/client": "workspace:*", "any-date-parser": "^1.5.4", "json5": "^2.2.3", "lodash.chunk": "^4.2.0", diff --git a/packages/importer/src/random-data.ts b/packages/importer/src/random-data.ts index 26cc39581..7f81a763f 100644 --- a/packages/importer/src/random-data.ts +++ b/packages/importer/src/random-data.ts @@ -1,5 +1,12 @@ import { fakerEN as faker } from '@faker-js/faker'; import { Schemas } from '@xata.io/client'; +import { z } from 'zod'; + +// TODO: Remove this once we migrate pgroll branches +type PgRollColumn = Schemas.Column & { + comment?: string; + pgType?: string; +}; export function generateRandomData(table: Schemas.Table, size: number) { const records: Record[] = []; @@ -11,7 +18,7 @@ export function generateRandomData(table: Schemas.Table, size: number) { return records; } -function randomRecord(columns: Schemas.Column[]) { +function randomRecord(columns: PgRollColumn[]) { const record: Record = {}; for (const column of columns) { record[column.name] = randomData(column); @@ -19,53 +26,53 @@ function randomRecord(columns: Schemas.Column[]) { return record; } -function randomData(column: Schemas.Column) { - switch (column.type) { - case 'text': - return faker.lorem.paragraphs(rand(2, 3)); - case 'email': - return faker.internet.email({ provider: 'acme.pets' }); +function randomData(column: PgRollColumn) { + const columnCommentType = narrowStringType(column.comment); + // Note that this is a best effort and seeding may fail for invalid Xata columns + // that are foreign keys, or have constraints such as length attached to them. + switch (column.pgType) { + case 'boolean': + return rand(0, 1) === 1; + case 'bigint': + case 'int8': + case 'integer': case 'int': + case 'int4': + case 'smallint': return rand(1, 100); - case 'float': + case 'double precision': + case 'float8': + case 'real': return rand(1, 10000) / rand(1, 100); - case 'bool': - return rand(0, 1) === 1; - case 'multiple': - return faker.word.words(rand(1, 3)).split(' '); - case 'string': - return randomString(column.name); - case 'datetime': + case 'text': + case 'varchar': + case 'character varying': + if (columnCommentType === 'email') return faker.internet.email({ provider: 'acme.pets' }); + return faker.word.words(3); + case 'timestamptz': return faker.date.recent({ days: rand(1, 10) }); - default: - return undefined; + case 'text[]': + return faker.word.words(rand(1, 3)).split(' '); } + + if (column.pgType?.startsWith('character(') || column.pgType?.startsWith('varchar(')) return faker.word.words(3); + if (column.pgType?.startsWith('numeric(')) return rand(1, 10000) / rand(1, 100); + + return undefined; } function rand(min: number, max: number) { return Math.floor(Math.random() * (max - min) + min); } -const generators: Record string> = { - city: () => faker.location.city(), - country: () => faker.location.country(), - county: () => faker.location.county(), - state: () => faker.location.state(), - street: () => faker.location.street(), - timezone: () => faker.location.timeZone(), - tz: () => faker.location.timeZone(), - zipcode: () => faker.location.zipCode(), - zip: () => faker.location.zipCode(), - department: () => faker.commerce.department(), - product: () => faker.commerce.product(), - company: () => faker.company.name(), - firstName: () => faker.person.firstName(), - lastName: () => faker.person.lastName(), - phone: () => faker.phone.number('501-###-###') -}; +export const xataStringColumns = ['email', 'text', 'string'] as const; -function randomString(columnName: string) { - const gen = generators[columnName.toLowerCase()]; - if (gen) return gen(); - return faker.word.words(2); -} +const XataStringColumn = z.object({ + ['xata.type']: z.enum(xataStringColumns) +}); + +const narrowStringType = (comment?: string): Schemas.Column['type'] => { + if (!comment) return 'text'; + const result = XataStringColumn.safeParse(JSON.parse(comment)); + return result.success ? result.data['xata.type'] : 'text'; +}; diff --git a/packages/importer/test/utils.ts b/packages/importer/test/utils.ts index d681172e0..cc910ab1a 100644 --- a/packages/importer/test/utils.ts +++ b/packages/importer/test/utils.ts @@ -22,8 +22,12 @@ export const getXataClientWithPlugin = () => { import: new XataImportPlugin() }); - return new XataClient({ - apiKey: 'xau_test123', - databaseURL: 'https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb' - }); + return new XataClient( + { + apiKey: 'xau_test123', + databaseURL: 'https://my-workspace-v0fo9s.us-east-1.xata.sh/db/mydb', + branch: 'main' + }, + { tables: [] } + ); }; diff --git a/packages/pgroll/package.json b/packages/pgroll/package.json index 04b20bb72..eda82cf4f 100644 --- a/packages/pgroll/package.json +++ b/packages/pgroll/package.json @@ -28,7 +28,7 @@ "bugs": { "url": "https://github.com/xataio/client-ts/issues" }, - "homepage": "https://github.com/xataio/client-ts/blob/main/importer/README.md", + "homepage": "https://github.com/xataio/client-ts", "dependencies": { "zod": "^3.23.8", "zod-to-json-schema": "^3.23.1" diff --git a/packages/plugin-client-cache/CHANGELOG.md b/packages/plugin-client-cache/CHANGELOG.md deleted file mode 100644 index e438c9e16..000000000 --- a/packages/plugin-client-cache/CHANGELOG.md +++ /dev/null @@ -1,396 +0,0 @@ -# @xata.io/plugin-client-cache - -## 0.1.47 - -### Patch Changes - -- Updated dependencies [[`6b754d2`](https://github.com/xataio/client-ts/commit/6b754d2f6a1f7b9378a96fe27502ff6c29ff5ed8)]: - - @xata.io/client@0.29.5 - -## 0.1.46 - -### Patch Changes - -- Updated dependencies [[`2140a24`](https://github.com/xataio/client-ts/commit/2140a24f32a94f36bab8c8268033c7dcf235dddc), [`d8032f2`](https://github.com/xataio/client-ts/commit/d8032f2e07bdcc653db1606796d27f08d397cdbe)]: - - @xata.io/client@0.29.4 - -## 0.1.45 - -### Patch Changes - -- Updated dependencies [[`02053fb`](https://github.com/xataio/client-ts/commit/02053fbb10479b8e9453691f957d3235762555aa), [`e27cb74`](https://github.com/xataio/client-ts/commit/e27cb74143aa9b6c654713878e5d3776858e5290)]: - - @xata.io/client@0.29.3 - -## 0.1.44 - -### Patch Changes - -- Updated dependencies [[`e8db1cd`](https://github.com/xataio/client-ts/commit/e8db1cd394ccbed32403548bf9d09a5c3973d850)]: - - @xata.io/client@0.29.2 - -## 0.1.43 - -### Patch Changes - -- Updated dependencies [[`d0f5d12`](https://github.com/xataio/client-ts/commit/d0f5d125e6c2f4c82f8a0a6b4a30d255c58e8326), [`212b53d`](https://github.com/xataio/client-ts/commit/212b53d07498def0d2ed8942691eff982e448969), [`9fd8c42`](https://github.com/xataio/client-ts/commit/9fd8c428d71b476f1951123c6cba5e803b983e54), [`368d4aa`](https://github.com/xataio/client-ts/commit/368d4aa16cd1cc1da93a142406c5d41bbc15b082)]: - - @xata.io/client@0.29.1 - -## 0.1.42 - -### Patch Changes - -- Updated dependencies [[`0ec026a`](https://github.com/xataio/client-ts/commit/0ec026a92bdb1a405cb9d90cb1d506ff159f98e8), [`6414bd3`](https://github.com/xataio/client-ts/commit/6414bd3d8bdb84961e68968df4b0b025503f0d72), [`27773df`](https://github.com/xataio/client-ts/commit/27773df5addf0013d1a7238ac490904e7aad2334)]: - - @xata.io/client@0.29.0 - -## 0.1.41 - -### Patch Changes - -- Updated dependencies [[`adc961b`](https://github.com/xataio/client-ts/commit/adc961b886b789010e6512c17cb2377eceab665a), [`6031a9d`](https://github.com/xataio/client-ts/commit/6031a9de63c264b7db5b031bb1795258c2bf8150)]: - - @xata.io/client@0.28.4 - -## 0.1.40 - -### Patch Changes - -- Updated dependencies [[`b7f3ec9`](https://github.com/xataio/client-ts/commit/b7f3ec9eabe3642929131e244bd774f4d3134482)]: - - @xata.io/client@0.28.3 - -## 0.1.39 - -### Patch Changes - -- Updated dependencies [[`c9178e1`](https://github.com/xataio/client-ts/commit/c9178e1e3f2268513e78dcfce396a99a8fca5dfb)]: - - @xata.io/client@0.28.2 - -## 0.1.38 - -### Patch Changes - -- Updated dependencies [[`9a7e3f5`](https://github.com/xataio/client-ts/commit/9a7e3f5029e53efc6750e9c86bab936427788209)]: - - @xata.io/client@0.28.1 - -## 0.1.37 - -### Patch Changes - -- Updated dependencies [[`e97d1999`](https://github.com/xataio/client-ts/commit/e97d1999f3c25f149213ceca81958e1674624e05)]: - - @xata.io/client@0.28.0 - -## 0.1.36 - -### Patch Changes - -- Updated dependencies [[`19c5dd47`](https://github.com/xataio/client-ts/commit/19c5dd47e3a032fcb19d990527b8faaa9326d97d), [`d282d18f`](https://github.com/xataio/client-ts/commit/d282d18f025094e0729ade6009b34fc0d34ebbba)]: - - @xata.io/client@0.27.0 - -## 0.1.35 - -### Patch Changes - -- Updated dependencies [[`302798e8`](https://github.com/xataio/client-ts/commit/302798e8d210c89f420a5c927e0f836a27dbaed9)]: - - @xata.io/client@0.26.9 - -## 0.1.34 - -### Patch Changes - -- Updated dependencies [[`fa2883b0`](https://github.com/xataio/client-ts/commit/fa2883b0639e48d68097401bf515c8cb95df5b4b), [`c04faece`](https://github.com/xataio/client-ts/commit/c04faece8830699d978e03c89f29e383e479e824), [`cb45cc9f`](https://github.com/xataio/client-ts/commit/cb45cc9f6829e1b555762e656cc1b0b2e977aaf9)]: - - @xata.io/client@0.26.8 - -## 0.1.33 - -### Patch Changes - -- Updated dependencies [[`0e1c50de`](https://github.com/xataio/client-ts/commit/0e1c50de5850db2dfbbdfff9d66eda3bf1322836), [`d093d363`](https://github.com/xataio/client-ts/commit/d093d363a51fc23c8513d51600bb3b31bbc45334)]: - - @xata.io/client@0.26.7 - -## 0.1.32 - -### Patch Changes - -- Updated dependencies [[`3330c9cf`](https://github.com/xataio/client-ts/commit/3330c9cf8d8db18b8e355a576e4afd589b6152bf), [`a738816d`](https://github.com/xataio/client-ts/commit/a738816d355f4415b0622bb5a23b4154f9855177)]: - - @xata.io/client@0.26.6 - -## 0.1.31 - -### Patch Changes - -- Updated dependencies [[`b9b9058f`](https://github.com/xataio/client-ts/commit/b9b9058f0bc81b660da45318c27191a62f041f21)]: - - @xata.io/client@0.26.5 - -## 0.1.30 - -### Patch Changes - -- Updated dependencies [[`7166797c`](https://github.com/xataio/client-ts/commit/7166797c28839198d20a9115d0414cebc2fed39b), [`b85df75f`](https://github.com/xataio/client-ts/commit/b85df75f2f466762a8b3d9824c9292c7e3db03fd)]: - - @xata.io/client@0.26.4 - -## 0.1.29 - -### Patch Changes - -- Updated dependencies [[`4910dce2`](https://github.com/xataio/client-ts/commit/4910dce29d3cc17d13aadf32e4eb476ffb571fad)]: - - @xata.io/client@0.26.3 - -## 0.1.28 - -### Patch Changes - -- Updated dependencies [[`22fccb51`](https://github.com/xataio/client-ts/commit/22fccb51709749c319897702c15749b74ce4b820)]: - - @xata.io/client@0.26.2 - -## 0.1.27 - -### Patch Changes - -- Updated dependencies [[`922e6e54`](https://github.com/xataio/client-ts/commit/922e6e54e8b31641770a36b6b4ff8f4fa65d304d), [`13f6f3e4`](https://github.com/xataio/client-ts/commit/13f6f3e4b1a2f925d50a5380b62ef1057f5c3893), [`f02fc165`](https://github.com/xataio/client-ts/commit/f02fc165bf6558e4377eb9f8e1d0f4222f004c70)]: - - @xata.io/client@0.26.1 - -## 0.1.26 - -### Patch Changes - -- Updated dependencies [[`6ec862f8`](https://github.com/xataio/client-ts/commit/6ec862f8f799eb692f62be79dd0b613b83a34780), [`0c0149ad`](https://github.com/xataio/client-ts/commit/0c0149ad1ee3f7c0fe9d31030552b022c907edb0)]: - - @xata.io/client@0.26.0 - -## 0.1.25 - -### Patch Changes - -- Updated dependencies [[`3b9e1984`](https://github.com/xataio/client-ts/commit/3b9e1984329cd0e9f885f6af4155cc85ab61ba41)]: - - @xata.io/client@0.25.3 - -## 0.1.24 - -### Patch Changes - -- Updated dependencies [[`9d1ace5f`](https://github.com/xataio/client-ts/commit/9d1ace5f12788755f3150449120b27cd030af3a9), [`f6665593`](https://github.com/xataio/client-ts/commit/f6665593c301dc9400834cbd68ae32b92bbdea5d)]: - - @xata.io/client@0.25.2 - -## 0.1.23 - -### Patch Changes - -- Updated dependencies [[`321c33ff`](https://github.com/xataio/client-ts/commit/321c33ff684c416a6cc813be2a970fd8f826b885)]: - - @xata.io/client@0.25.1 - -## 0.1.22 - -### Patch Changes - -- Updated dependencies [[`ac3b968f`](https://github.com/xataio/client-ts/commit/ac3b968f7ca476f2c82b1b2719d020fbd7164f38), [`aef9c834`](https://github.com/xataio/client-ts/commit/aef9c834a850494020e7585cb4ce67b446f9ccfc), [`bcce0d3b`](https://github.com/xataio/client-ts/commit/bcce0d3b0b53468937accd4fee3b5485c35f69ad), [`c1f2d264`](https://github.com/xataio/client-ts/commit/c1f2d2649e077cdffae97c90b9d2b1c75d6858fb), [`6c2c2630`](https://github.com/xataio/client-ts/commit/6c2c26308d4cbd25e7a9677c7d25c836396d4965)]: - - @xata.io/client@0.25.0 - -## 0.1.21 - -### Patch Changes - -- Updated dependencies [[`f01d1580`](https://github.com/xataio/client-ts/commit/f01d1580fc450cbf06eb8af85d68cf052fbe83a1), [`52290feb`](https://github.com/xataio/client-ts/commit/52290feb5bba57384cdc14e7722fb5d9883dc581)]: - - @xata.io/client@0.24.3 - -## 0.1.20 - -### Patch Changes - -- Updated dependencies [[`51561b52`](https://github.com/xataio/client-ts/commit/51561b52b56ad5ed9101d8faf12929891419cb2c)]: - - @xata.io/client@0.24.2 - -## 0.1.19 - -### Patch Changes - -- Updated dependencies [[`eaa774f5`](https://github.com/xataio/client-ts/commit/eaa774f542185ef92448155bcdff331686c4da9f)]: - - @xata.io/client@0.24.1 - -## 0.1.18 - -### Patch Changes - -- Updated dependencies [[`45aa2207`](https://github.com/xataio/client-ts/commit/45aa220728e98dd716a55a9a307474732a9b2bc1), [`f28080f0`](https://github.com/xataio/client-ts/commit/f28080f034f02704fe00d64b8f42e1127bde30c7), [`ac9c0604`](https://github.com/xataio/client-ts/commit/ac9c06042bb85105d9a38624676ce6ea5a27d488), [`c163b365`](https://github.com/xataio/client-ts/commit/c163b3658f23fb2eaad6243ebebc92624754099a)]: - - @xata.io/client@0.24.0 - -## 0.1.17 - -### Patch Changes - -- [#972](https://github.com/xataio/client-ts/pull/972) [`89375e76`](https://github.com/xataio/client-ts/commit/89375e76b790fed7e6a26bf3ac4ea9eaed1aecae) Thanks [@SferaDev](https://github.com/SferaDev)! - Add types to exports - -- Updated dependencies [[`20cc8c43`](https://github.com/xataio/client-ts/commit/20cc8c43e1659bf112ae2642948c84bfcf46a6ba), [`5099cbbd`](https://github.com/xataio/client-ts/commit/5099cbbd3065a60dcee2f1699afa1ee8ed5edb1c), [`89375e76`](https://github.com/xataio/client-ts/commit/89375e76b790fed7e6a26bf3ac4ea9eaed1aecae), [`5eaee932`](https://github.com/xataio/client-ts/commit/5eaee932b828907ae352d7c0d0584e860845434b), [`109b8790`](https://github.com/xataio/client-ts/commit/109b8790849532d9c442e7c03c67792aeafebd88)]: - - @xata.io/client@0.23.5 - -## 0.1.16 - -### Patch Changes - -- Updated dependencies [[`470cc71f`](https://github.com/xataio/client-ts/commit/470cc71f7c5c8b9fd50f789e157d2b2eecd0b3e8)]: - - @xata.io/client@0.23.4 - -## 0.1.15 - -### Patch Changes - -- Updated dependencies [[`344b0d68`](https://github.com/xataio/client-ts/commit/344b0d687962d569872d1e90d59818d28df7579c)]: - - @xata.io/client@0.23.3 - -## 0.1.14 - -### Patch Changes - -- Updated dependencies [[`c477c177`](https://github.com/xataio/client-ts/commit/c477c17795c01cbf945be413217944a5a38655a5), [`ecdc6553`](https://github.com/xataio/client-ts/commit/ecdc6553d4628289e88953ab6296b80f60e8f757)]: - - @xata.io/client@0.23.2 - -## 0.1.13 - -### Patch Changes - -- Updated dependencies [[`3026d708`](https://github.com/xataio/client-ts/commit/3026d70847830fd0f2024413d823380ff323806c)]: - - @xata.io/client@0.23.1 - -## 0.1.12 - -### Patch Changes - -- Updated dependencies [[`5838113f`](https://github.com/xataio/client-ts/commit/5838113fca042163b44d7cc7cc1686d5ef89b302)]: - - @xata.io/client@0.23.0 - -## 0.1.11 - -### Patch Changes - -- Updated dependencies [[`22e7dd29`](https://github.com/xataio/client-ts/commit/22e7dd29f7a51dccc087d5fd7fff32084c7733af), [`07fc879d`](https://github.com/xataio/client-ts/commit/07fc879d3f778536e39588e66d7a18b5a9d52ebe), [`58a1c24e`](https://github.com/xataio/client-ts/commit/58a1c24e5d30025dce243eecce44f09d4f65ed66), [`c2c6e244`](https://github.com/xataio/client-ts/commit/c2c6e24459b1acc07f0414066258071fbcf7dde9)]: - - @xata.io/client@0.22.4 - -## 0.1.10 - -### Patch Changes - -- Updated dependencies [[`4210b8c3`](https://github.com/xataio/client-ts/commit/4210b8c3c4169ba781a56deed7ba09c99788db1f)]: - - @xata.io/client@0.22.3 - -## 0.1.9 - -### Patch Changes - -- Updated dependencies [[`72e13bf9`](https://github.com/xataio/client-ts/commit/72e13bf99d0ebefef91c984a995a28b0e8ca2a8f)]: - - @xata.io/client@0.22.2 - -## 0.1.8 - -### Patch Changes - -- Updated dependencies [[`4cafde72`](https://github.com/xataio/client-ts/commit/4cafde728e4e9e5e83812d475d9980397ae78362), [`639710a5`](https://github.com/xataio/client-ts/commit/639710a52132f260bf3a26560a21ae2193abb71d)]: - - @xata.io/client@0.22.1 - -## 0.1.7 - -### Patch Changes - -- Updated dependencies [[`b2a4def4`](https://github.com/xataio/client-ts/commit/b2a4def4baf3eb18cd323895635e0bccb7f876f4), [`379e6144`](https://github.com/xataio/client-ts/commit/379e61446b21e7cbadd7fc59267736c6845ec566)]: - - @xata.io/client@0.22.0 - -## 0.1.6 - -### Patch Changes - -- [#828](https://github.com/xataio/client-ts/pull/828) [`039e35bf`](https://github.com/xataio/client-ts/commit/039e35bf9f01149f39bca39e1867908ca3468bb5) Thanks [@SferaDev](https://github.com/SferaDev)! - Add branded types to serializer - -- Updated dependencies [[`039e35bf`](https://github.com/xataio/client-ts/commit/039e35bf9f01149f39bca39e1867908ca3468bb5), [`039e35bf`](https://github.com/xataio/client-ts/commit/039e35bf9f01149f39bca39e1867908ca3468bb5)]: - - @xata.io/client@0.21.6 - -## 0.1.5 - -### Patch Changes - -- Updated dependencies [[`b131040a`](https://github.com/xataio/client-ts/commit/b131040a2d142c4e71a2e586fbf05cd9295af9a1), [`7ea810dc`](https://github.com/xataio/client-ts/commit/7ea810dc083ec284447e3bd27bd0465f887481e6), [`d124cbfb`](https://github.com/xataio/client-ts/commit/d124cbfb93d3d591e79bbe9e94c4b6304d825e71), [`d124cbfb`](https://github.com/xataio/client-ts/commit/d124cbfb93d3d591e79bbe9e94c4b6304d825e71), [`fb5ccdf9`](https://github.com/xataio/client-ts/commit/fb5ccdf9fa95c37d54fbc5d9c0bb45872c831609), [`7da604d2`](https://github.com/xataio/client-ts/commit/7da604d27990e20ecadba6122434fca563e6a8c9), [`4ae00036`](https://github.com/xataio/client-ts/commit/4ae00036b53c6c89e02a1fcfdd992f1a3c22892c), [`bdae6668`](https://github.com/xataio/client-ts/commit/bdae6668fb571d29f1b1068a54f6866a80d9b174), [`9486bdcc`](https://github.com/xataio/client-ts/commit/9486bdccc0af567bc5f2e8f91592b0143c539c45), [`9486bdcc`](https://github.com/xataio/client-ts/commit/9486bdccc0af567bc5f2e8f91592b0143c539c45)]: - - @xata.io/client@0.21.0 - -## 0.1.4 - -### Patch Changes - -- Updated dependencies [[`6cbeaa0`](https://github.com/xataio/client-ts/commit/6cbeaa00050b5aa99ab7c98052a906487263e026), [`a5a9aa5`](https://github.com/xataio/client-ts/commit/a5a9aa59987faa1d3d701d7431b8a96031e01ac7), [`c64b2eb`](https://github.com/xataio/client-ts/commit/c64b2eb9add70e75d419d418ab9608caac0dbfa1), [`485b217`](https://github.com/xataio/client-ts/commit/485b217079c4b2091d697e68622c48eddd130ceb), [`4d7499c`](https://github.com/xataio/client-ts/commit/4d7499ccbb135691350334fd8022f7a5da41c5f2)]: - - @xata.io/client@0.20.0 - -## 0.1.3 - -### Patch Changes - -- Updated dependencies [[`f80f051`](https://github.com/xataio/client-ts/commit/f80f05118dd0588861b8229114a469f016ef77ac), [`c14f431`](https://github.com/xataio/client-ts/commit/c14f431db020036ab2b059bcc52a5d56b321c8e7), [`2e341e5`](https://github.com/xataio/client-ts/commit/2e341e5c6140f9c4ddd74e479049992c26c43ea2), [`c8def01`](https://github.com/xataio/client-ts/commit/c8def013e9e2d5b634cdb2850f757a0b3e9e0a6d), [`f2f749f`](https://github.com/xataio/client-ts/commit/f2f749f4c64246a303da8d4a617773fc55c1d021), [`f2f749f`](https://github.com/xataio/client-ts/commit/f2f749f4c64246a303da8d4a617773fc55c1d021)]: - - @xata.io/client@0.19.0 - -## 0.1.2 - -### Patch Changes - -- Updated dependencies [[`330b076`](https://github.com/xataio/client-ts/commit/330b076a0781e3576c82afab76e3fb2a64f2e041), [`c3dfb4b`](https://github.com/xataio/client-ts/commit/c3dfb4babc990634b9e9747616ed93223178a2e7), [`699beb4`](https://github.com/xataio/client-ts/commit/699beb4bbf21cffa001d3f88a03246980e30250b), [`74b17aa`](https://github.com/xataio/client-ts/commit/74b17aaedc0dbdd79bfdcb182b2e70b61f98f5a5), [`83f20cd`](https://github.com/xataio/client-ts/commit/83f20cdbe53706c16016c4db3f318e679b24ec86), [`addfcc6`](https://github.com/xataio/client-ts/commit/addfcc67fca663defdd340111ea09c9188bad3ab), [`eb7ba59`](https://github.com/xataio/client-ts/commit/eb7ba594be2a1f0ab90956836bbeb912e188a46d), [`f1a0742`](https://github.com/xataio/client-ts/commit/f1a0742a04e1aefab14f46371a04a41069faec01)]: - - @xata.io/client@0.18.0 - -## 0.1.1 - -### Patch Changes - -- Updated dependencies [[`26e91d1`](https://github.com/xataio/client-ts/commit/26e91d1d84df082dedd7159271fc7c27ec87fefe), [`3332d43`](https://github.com/xataio/client-ts/commit/3332d43121367f61c8d87dfb7da2af65bd1c278f)]: - - @xata.io/client@0.17.0 - -## 0.1.0 - -### Minor Changes - -- [#485](https://github.com/xataio/client-ts/pull/485) [`7e04a3d`](https://github.com/xataio/client-ts/commit/7e04a3d1c51958a44f687a0036ead8bb3f5a2dfb) Thanks [@SferaDev](https://github.com/SferaDev)! - Remove record cache - -### Patch Changes - -- Updated dependencies [[`6a96ea5`](https://github.com/xataio/client-ts/commit/6a96ea5da4c5b7ca9a99b57ebbce8d6766b5d4d8), [`43f2560`](https://github.com/xataio/client-ts/commit/43f25605ddd0d2fd514a1542a14389d28955c500), [`a9cbb26`](https://github.com/xataio/client-ts/commit/a9cbb263fbca47cb91a827db252d95a5bb4079a6), [`7e04a3d`](https://github.com/xataio/client-ts/commit/7e04a3d1c51958a44f687a0036ead8bb3f5a2dfb)]: - - @xata.io/client@0.16.0 - -## 0.0.8 - -### Patch Changes - -- Updated dependencies [[`e923d11`](https://github.com/xataio/client-ts/commit/e923d11fe357519dc4ca3ae722670e6e70ccd1c6), [`599b52c`](https://github.com/xataio/client-ts/commit/599b52c3090222eedef85d1ad1e907874cd3e801)]: - - @xata.io/client@0.15.0 - -## 0.0.7 - -### Patch Changes - -- Updated dependencies [[`7547b7e`](https://github.com/xataio/client-ts/commit/7547b7edbc9a95c6620784cc5348316f27502c73), [`8812380`](https://github.com/xataio/client-ts/commit/881238062b5eeac2dc8b9ba156720e0acc22c5c5), [`0584a5b`](https://github.com/xataio/client-ts/commit/0584a5b207a21dbc36ddc1d44b276f1d5bb60dc5), [`8d8a912`](https://github.com/xataio/client-ts/commit/8d8a9129e36452266c4c12fe35b421f66e572498), [`e99010c`](https://github.com/xataio/client-ts/commit/e99010c9ab9d355abadcfbcf98b5a3fcc80c307a), [`c4be404`](https://github.com/xataio/client-ts/commit/c4be404a3ecb34df9b1ef4501c92f5bdc221f19c)]: - - @xata.io/client@0.14.0 - -## 0.0.6 - -### Patch Changes - -- Updated dependencies [[`c9f34ad`](https://github.com/xataio/client-ts/commit/c9f34ad37d75203083a1dec2fac2b03e096521af), [`5f82e43`](https://github.com/xataio/client-ts/commit/5f82e4394010f40dcbf3faf2d0bdb58a6fc1c37a)]: - - @xata.io/client@0.13.0 - -## 0.0.5 - -### Patch Changes - -- Updated dependencies [[`db3c88e`](https://github.com/xataio/client-ts/commit/db3c88e1f2bee6d308afb8d6e95b7c090a87e7a7), [`1cde95f`](https://github.com/xataio/client-ts/commit/1cde95f05a6b9fbf0564ea05400140f0cef41a3a), [`57bf0e2`](https://github.com/xataio/client-ts/commit/57bf0e2e049ed0498683ff42d287983f295342b7)]: - - @xata.io/client@0.12.0 - -## 0.0.4 - -### Patch Changes - -- Updated dependencies [[`505257c`](https://github.com/xataio/client-ts/commit/505257c0c42ca0c8beaf5c0f638037c576dcc43c), [`ff7e5c6`](https://github.com/xataio/client-ts/commit/ff7e5c6f211913196d8c28600d7a7675ed261688), [`bf64cb8`](https://github.com/xataio/client-ts/commit/bf64cb885d55a0271e966314384324f02ded084e), [`ce07601`](https://github.com/xataio/client-ts/commit/ce07601e4ddf9f75e20249d479dc04a63795ca96), [`bc64c28`](https://github.com/xataio/client-ts/commit/bc64c28fbfbb000c7190ac8092e2ef6a261df86f), [`12f1ce3`](https://github.com/xataio/client-ts/commit/12f1ce362f6cda27dfdb3afab0800282bddc8b5e), [`a73a2a2`](https://github.com/xataio/client-ts/commit/a73a2a2014c44cf88eaef42196ba1dba9d516b4a)]: - - @xata.io/client@0.11.0 - -## 0.0.3 - -### Patch Changes - -- Updated dependencies [[`6d76275`](https://github.com/xataio/client-ts/commit/6d7627555a404a4c2da42f4187df6f8300f9a46f), [`d1ec0df`](https://github.com/xataio/client-ts/commit/d1ec0df14834088a816919bfc68216f3f9b2d9ef), [`1864742`](https://github.com/xataio/client-ts/commit/18647428d8608841de514c3784fb711c39dccc6d), [`1af6f1a`](https://github.com/xataio/client-ts/commit/1af6f1aaa1123e77a895961581c87f06a88db698), [`be4eda8`](https://github.com/xataio/client-ts/commit/be4eda8f73037d97fef7de28b56d7471dd867875), [`99be734`](https://github.com/xataio/client-ts/commit/99be734827576d888aa12a579ed1983a0a8a8e83)]: - - @xata.io/client@0.10.0 - -## 0.0.2 - -### Patch Changes - -- [#247](https://github.com/xataio/client-ts/pull/247) [`53b4ad6`](https://github.com/xataio/client-ts/commit/53b4ad670c9f35387e4d0e26aec5ce0dfd340d07) Thanks [@SferaDev](https://github.com/SferaDev)! - Initial release - -- Updated dependencies [[`2fc2788`](https://github.com/xataio/client-ts/commit/2fc2788e583c047ffb2cd693f053f60ce608149c), [`a96da7c`](https://github.com/xataio/client-ts/commit/a96da7c8b548604ed25001390992531537675a44), [`e8d595f`](https://github.com/xataio/client-ts/commit/e8d595f54efe126b39c78cc771a5d69c551f4fba), [`c4dcd11`](https://github.com/xataio/client-ts/commit/c4dcd110d8f9dc3a7e4510f2f00257c9109e51fa), [`2848894`](https://github.com/xataio/client-ts/commit/284889446bbac5d6737086bf01a588d97b841730)]: - - @xata.io/client@0.9.0 diff --git a/packages/plugin-client-cache/src/index.ts b/packages/plugin-client-cache/src/index.ts deleted file mode 100644 index 43558e57f..000000000 --- a/packages/plugin-client-cache/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './lru-cache'; diff --git a/packages/plugin-client-cache/src/lru-cache.ts b/packages/plugin-client-cache/src/lru-cache.ts deleted file mode 100644 index eee419c94..000000000 --- a/packages/plugin-client-cache/src/lru-cache.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { LRUCache as LRU } from 'lru-cache'; -import { CacheImpl } from '@xata.io/client'; - -export type LRUCacheOptions = Partial>; - -export class LRUCache implements CacheImpl { - #cache: LRU; - defaultQueryTTL: number; - - constructor(options: LRUCacheOptions = {}) { - this.#cache = new LRU({ max: 500, ...options }); - this.defaultQueryTTL = options.ttl ?? 60 * 1000; - } - - async getAll(): Promise> { - const entries = this.#cache.dump().map(([key, { value }]) => [key, value]); - return Object.fromEntries(entries); - } - - async get(key: string): Promise { - return this.#cache.get(key) ?? null; - } - - async set(key: string, value: T): Promise { - this.#cache.set(key, value); - } - - async delete(key: string): Promise { - this.#cache.delete(key); - } - - async clear(): Promise { - this.#cache.clear(); - } -} diff --git a/packages/plugin-client-cloudflare/.npmignore b/packages/plugin-client-cloudflare/.npmignore deleted file mode 100644 index c97b8ad26..000000000 --- a/packages/plugin-client-cloudflare/.npmignore +++ /dev/null @@ -1,5 +0,0 @@ -src -tsconfig.json -rollup.config.mjs -.eslintrc.cjs -.gitignore diff --git a/packages/plugin-client-cloudflare/CHANGELOG.md b/packages/plugin-client-cloudflare/CHANGELOG.md deleted file mode 100644 index 3a3df74fa..000000000 --- a/packages/plugin-client-cloudflare/CHANGELOG.md +++ /dev/null @@ -1,320 +0,0 @@ -# @xata.io/plugin-client-cloudflare - -## 0.0.46 - -### Patch Changes - -- Updated dependencies [[`6b754d2`](https://github.com/xataio/client-ts/commit/6b754d2f6a1f7b9378a96fe27502ff6c29ff5ed8)]: - - @xata.io/client@0.29.5 - -## 0.0.45 - -### Patch Changes - -- Updated dependencies [[`2140a24`](https://github.com/xataio/client-ts/commit/2140a24f32a94f36bab8c8268033c7dcf235dddc), [`d8032f2`](https://github.com/xataio/client-ts/commit/d8032f2e07bdcc653db1606796d27f08d397cdbe)]: - - @xata.io/client@0.29.4 - -## 0.0.44 - -### Patch Changes - -- Updated dependencies [[`02053fb`](https://github.com/xataio/client-ts/commit/02053fbb10479b8e9453691f957d3235762555aa), [`e27cb74`](https://github.com/xataio/client-ts/commit/e27cb74143aa9b6c654713878e5d3776858e5290)]: - - @xata.io/client@0.29.3 - -## 0.0.43 - -### Patch Changes - -- Updated dependencies [[`e8db1cd`](https://github.com/xataio/client-ts/commit/e8db1cd394ccbed32403548bf9d09a5c3973d850)]: - - @xata.io/client@0.29.2 - -## 0.0.42 - -### Patch Changes - -- Updated dependencies [[`d0f5d12`](https://github.com/xataio/client-ts/commit/d0f5d125e6c2f4c82f8a0a6b4a30d255c58e8326), [`212b53d`](https://github.com/xataio/client-ts/commit/212b53d07498def0d2ed8942691eff982e448969), [`9fd8c42`](https://github.com/xataio/client-ts/commit/9fd8c428d71b476f1951123c6cba5e803b983e54), [`368d4aa`](https://github.com/xataio/client-ts/commit/368d4aa16cd1cc1da93a142406c5d41bbc15b082)]: - - @xata.io/client@0.29.1 - -## 0.0.41 - -### Patch Changes - -- Updated dependencies [[`0ec026a`](https://github.com/xataio/client-ts/commit/0ec026a92bdb1a405cb9d90cb1d506ff159f98e8), [`6414bd3`](https://github.com/xataio/client-ts/commit/6414bd3d8bdb84961e68968df4b0b025503f0d72), [`27773df`](https://github.com/xataio/client-ts/commit/27773df5addf0013d1a7238ac490904e7aad2334)]: - - @xata.io/client@0.29.0 - -## 0.0.40 - -### Patch Changes - -- Updated dependencies [[`adc961b`](https://github.com/xataio/client-ts/commit/adc961b886b789010e6512c17cb2377eceab665a), [`6031a9d`](https://github.com/xataio/client-ts/commit/6031a9de63c264b7db5b031bb1795258c2bf8150)]: - - @xata.io/client@0.28.4 - -## 0.0.39 - -### Patch Changes - -- Updated dependencies [[`b7f3ec9`](https://github.com/xataio/client-ts/commit/b7f3ec9eabe3642929131e244bd774f4d3134482)]: - - @xata.io/client@0.28.3 - -## 0.0.38 - -### Patch Changes - -- Updated dependencies [[`c9178e1`](https://github.com/xataio/client-ts/commit/c9178e1e3f2268513e78dcfce396a99a8fca5dfb)]: - - @xata.io/client@0.28.2 - -## 0.0.37 - -### Patch Changes - -- Updated dependencies [[`9a7e3f5`](https://github.com/xataio/client-ts/commit/9a7e3f5029e53efc6750e9c86bab936427788209)]: - - @xata.io/client@0.28.1 - -## 0.0.36 - -### Patch Changes - -- Updated dependencies [[`e97d1999`](https://github.com/xataio/client-ts/commit/e97d1999f3c25f149213ceca81958e1674624e05)]: - - @xata.io/client@0.28.0 - -## 0.0.35 - -### Patch Changes - -- Updated dependencies [[`19c5dd47`](https://github.com/xataio/client-ts/commit/19c5dd47e3a032fcb19d990527b8faaa9326d97d), [`d282d18f`](https://github.com/xataio/client-ts/commit/d282d18f025094e0729ade6009b34fc0d34ebbba)]: - - @xata.io/client@0.27.0 - -## 0.0.34 - -### Patch Changes - -- Updated dependencies [[`302798e8`](https://github.com/xataio/client-ts/commit/302798e8d210c89f420a5c927e0f836a27dbaed9)]: - - @xata.io/client@0.26.9 - -## 0.0.33 - -### Patch Changes - -- Updated dependencies [[`fa2883b0`](https://github.com/xataio/client-ts/commit/fa2883b0639e48d68097401bf515c8cb95df5b4b), [`c04faece`](https://github.com/xataio/client-ts/commit/c04faece8830699d978e03c89f29e383e479e824), [`cb45cc9f`](https://github.com/xataio/client-ts/commit/cb45cc9f6829e1b555762e656cc1b0b2e977aaf9)]: - - @xata.io/client@0.26.8 - -## 0.0.32 - -### Patch Changes - -- Updated dependencies [[`0e1c50de`](https://github.com/xataio/client-ts/commit/0e1c50de5850db2dfbbdfff9d66eda3bf1322836), [`d093d363`](https://github.com/xataio/client-ts/commit/d093d363a51fc23c8513d51600bb3b31bbc45334)]: - - @xata.io/client@0.26.7 - -## 0.0.31 - -### Patch Changes - -- Updated dependencies [[`3330c9cf`](https://github.com/xataio/client-ts/commit/3330c9cf8d8db18b8e355a576e4afd589b6152bf), [`a738816d`](https://github.com/xataio/client-ts/commit/a738816d355f4415b0622bb5a23b4154f9855177)]: - - @xata.io/client@0.26.6 - -## 0.0.30 - -### Patch Changes - -- Updated dependencies [[`b9b9058f`](https://github.com/xataio/client-ts/commit/b9b9058f0bc81b660da45318c27191a62f041f21)]: - - @xata.io/client@0.26.5 - -## 0.0.29 - -### Patch Changes - -- Updated dependencies [[`7166797c`](https://github.com/xataio/client-ts/commit/7166797c28839198d20a9115d0414cebc2fed39b), [`b85df75f`](https://github.com/xataio/client-ts/commit/b85df75f2f466762a8b3d9824c9292c7e3db03fd)]: - - @xata.io/client@0.26.4 - -## 0.0.28 - -### Patch Changes - -- Updated dependencies [[`4910dce2`](https://github.com/xataio/client-ts/commit/4910dce29d3cc17d13aadf32e4eb476ffb571fad)]: - - @xata.io/client@0.26.3 - -## 0.0.27 - -### Patch Changes - -- Updated dependencies [[`22fccb51`](https://github.com/xataio/client-ts/commit/22fccb51709749c319897702c15749b74ce4b820)]: - - @xata.io/client@0.26.2 - -## 0.0.26 - -### Patch Changes - -- Updated dependencies [[`922e6e54`](https://github.com/xataio/client-ts/commit/922e6e54e8b31641770a36b6b4ff8f4fa65d304d), [`13f6f3e4`](https://github.com/xataio/client-ts/commit/13f6f3e4b1a2f925d50a5380b62ef1057f5c3893), [`f02fc165`](https://github.com/xataio/client-ts/commit/f02fc165bf6558e4377eb9f8e1d0f4222f004c70)]: - - @xata.io/client@0.26.1 - -## 0.0.25 - -### Patch Changes - -- Updated dependencies [[`6ec862f8`](https://github.com/xataio/client-ts/commit/6ec862f8f799eb692f62be79dd0b613b83a34780), [`0c0149ad`](https://github.com/xataio/client-ts/commit/0c0149ad1ee3f7c0fe9d31030552b022c907edb0)]: - - @xata.io/client@0.26.0 - -## 0.0.24 - -### Patch Changes - -- Updated dependencies [[`3b9e1984`](https://github.com/xataio/client-ts/commit/3b9e1984329cd0e9f885f6af4155cc85ab61ba41)]: - - @xata.io/client@0.25.3 - -## 0.0.23 - -### Patch Changes - -- Updated dependencies [[`9d1ace5f`](https://github.com/xataio/client-ts/commit/9d1ace5f12788755f3150449120b27cd030af3a9), [`f6665593`](https://github.com/xataio/client-ts/commit/f6665593c301dc9400834cbd68ae32b92bbdea5d)]: - - @xata.io/client@0.25.2 - -## 0.0.22 - -### Patch Changes - -- Updated dependencies [[`321c33ff`](https://github.com/xataio/client-ts/commit/321c33ff684c416a6cc813be2a970fd8f826b885)]: - - @xata.io/client@0.25.1 - -## 0.0.21 - -### Patch Changes - -- Updated dependencies [[`ac3b968f`](https://github.com/xataio/client-ts/commit/ac3b968f7ca476f2c82b1b2719d020fbd7164f38), [`aef9c834`](https://github.com/xataio/client-ts/commit/aef9c834a850494020e7585cb4ce67b446f9ccfc), [`bcce0d3b`](https://github.com/xataio/client-ts/commit/bcce0d3b0b53468937accd4fee3b5485c35f69ad), [`c1f2d264`](https://github.com/xataio/client-ts/commit/c1f2d2649e077cdffae97c90b9d2b1c75d6858fb), [`6c2c2630`](https://github.com/xataio/client-ts/commit/6c2c26308d4cbd25e7a9677c7d25c836396d4965)]: - - @xata.io/client@0.25.0 - -## 0.0.20 - -### Patch Changes - -- Updated dependencies [[`f01d1580`](https://github.com/xataio/client-ts/commit/f01d1580fc450cbf06eb8af85d68cf052fbe83a1), [`52290feb`](https://github.com/xataio/client-ts/commit/52290feb5bba57384cdc14e7722fb5d9883dc581)]: - - @xata.io/client@0.24.3 - -## 0.0.19 - -### Patch Changes - -- Updated dependencies [[`51561b52`](https://github.com/xataio/client-ts/commit/51561b52b56ad5ed9101d8faf12929891419cb2c)]: - - @xata.io/client@0.24.2 - -## 0.0.18 - -### Patch Changes - -- Updated dependencies [[`eaa774f5`](https://github.com/xataio/client-ts/commit/eaa774f542185ef92448155bcdff331686c4da9f)]: - - @xata.io/client@0.24.1 - -## 0.0.17 - -### Patch Changes - -- Updated dependencies [[`45aa2207`](https://github.com/xataio/client-ts/commit/45aa220728e98dd716a55a9a307474732a9b2bc1), [`f28080f0`](https://github.com/xataio/client-ts/commit/f28080f034f02704fe00d64b8f42e1127bde30c7), [`ac9c0604`](https://github.com/xataio/client-ts/commit/ac9c06042bb85105d9a38624676ce6ea5a27d488), [`c163b365`](https://github.com/xataio/client-ts/commit/c163b3658f23fb2eaad6243ebebc92624754099a)]: - - @xata.io/client@0.24.0 - -## 0.0.16 - -### Patch Changes - -- [#972](https://github.com/xataio/client-ts/pull/972) [`89375e76`](https://github.com/xataio/client-ts/commit/89375e76b790fed7e6a26bf3ac4ea9eaed1aecae) Thanks [@SferaDev](https://github.com/SferaDev)! - Add types to exports - -- Updated dependencies [[`20cc8c43`](https://github.com/xataio/client-ts/commit/20cc8c43e1659bf112ae2642948c84bfcf46a6ba), [`5099cbbd`](https://github.com/xataio/client-ts/commit/5099cbbd3065a60dcee2f1699afa1ee8ed5edb1c), [`89375e76`](https://github.com/xataio/client-ts/commit/89375e76b790fed7e6a26bf3ac4ea9eaed1aecae), [`5eaee932`](https://github.com/xataio/client-ts/commit/5eaee932b828907ae352d7c0d0584e860845434b), [`109b8790`](https://github.com/xataio/client-ts/commit/109b8790849532d9c442e7c03c67792aeafebd88)]: - - @xata.io/client@0.23.5 - -## 0.0.15 - -### Patch Changes - -- Updated dependencies [[`470cc71f`](https://github.com/xataio/client-ts/commit/470cc71f7c5c8b9fd50f789e157d2b2eecd0b3e8)]: - - @xata.io/client@0.23.4 - -## 0.0.14 - -### Patch Changes - -- Updated dependencies [[`344b0d68`](https://github.com/xataio/client-ts/commit/344b0d687962d569872d1e90d59818d28df7579c)]: - - @xata.io/client@0.23.3 - -## 0.0.13 - -### Patch Changes - -- Updated dependencies [[`c477c177`](https://github.com/xataio/client-ts/commit/c477c17795c01cbf945be413217944a5a38655a5), [`ecdc6553`](https://github.com/xataio/client-ts/commit/ecdc6553d4628289e88953ab6296b80f60e8f757)]: - - @xata.io/client@0.23.2 - -## 0.0.12 - -### Patch Changes - -- Updated dependencies [[`3026d708`](https://github.com/xataio/client-ts/commit/3026d70847830fd0f2024413d823380ff323806c)]: - - @xata.io/client@0.23.1 - -## 0.0.11 - -### Patch Changes - -- Updated dependencies [[`5838113f`](https://github.com/xataio/client-ts/commit/5838113fca042163b44d7cc7cc1686d5ef89b302)]: - - @xata.io/client@0.23.0 - -## 0.0.10 - -### Patch Changes - -- Updated dependencies [[`22e7dd29`](https://github.com/xataio/client-ts/commit/22e7dd29f7a51dccc087d5fd7fff32084c7733af), [`07fc879d`](https://github.com/xataio/client-ts/commit/07fc879d3f778536e39588e66d7a18b5a9d52ebe), [`58a1c24e`](https://github.com/xataio/client-ts/commit/58a1c24e5d30025dce243eecce44f09d4f65ed66), [`c2c6e244`](https://github.com/xataio/client-ts/commit/c2c6e24459b1acc07f0414066258071fbcf7dde9)]: - - @xata.io/client@0.22.4 - -## 0.0.9 - -### Patch Changes - -- Updated dependencies [[`4210b8c3`](https://github.com/xataio/client-ts/commit/4210b8c3c4169ba781a56deed7ba09c99788db1f)]: - - @xata.io/client@0.22.3 - -## 0.0.8 - -### Patch Changes - -- Updated dependencies [[`72e13bf9`](https://github.com/xataio/client-ts/commit/72e13bf99d0ebefef91c984a995a28b0e8ca2a8f)]: - - @xata.io/client@0.22.2 - -## 0.0.7 - -### Patch Changes - -- Updated dependencies [[`4cafde72`](https://github.com/xataio/client-ts/commit/4cafde728e4e9e5e83812d475d9980397ae78362), [`639710a5`](https://github.com/xataio/client-ts/commit/639710a52132f260bf3a26560a21ae2193abb71d)]: - - @xata.io/client@0.22.1 - -## 0.0.6 - -### Patch Changes - -- Updated dependencies [[`b2a4def4`](https://github.com/xataio/client-ts/commit/b2a4def4baf3eb18cd323895635e0bccb7f876f4), [`379e6144`](https://github.com/xataio/client-ts/commit/379e61446b21e7cbadd7fc59267736c6845ec566)]: - - @xata.io/client@0.22.0 - -## 0.0.5 - -### Patch Changes - -- [#779](https://github.com/xataio/client-ts/pull/779) [`d17755f4`](https://github.com/xataio/client-ts/commit/d17755f4e804927d37be26f6404b14282cca7740) Thanks [@SferaDev](https://github.com/SferaDev)! - Do not throw error if limit reached - -- Updated dependencies [[`6c96da45`](https://github.com/xataio/client-ts/commit/6c96da4533500ec236547f47310e99461d5457e8)]: - - @xata.io/client@0.21.3 - -## 0.0.4 - -### Patch Changes - -- Updated dependencies [[`b131040a`](https://github.com/xataio/client-ts/commit/b131040a2d142c4e71a2e586fbf05cd9295af9a1), [`7ea810dc`](https://github.com/xataio/client-ts/commit/7ea810dc083ec284447e3bd27bd0465f887481e6), [`d124cbfb`](https://github.com/xataio/client-ts/commit/d124cbfb93d3d591e79bbe9e94c4b6304d825e71), [`d124cbfb`](https://github.com/xataio/client-ts/commit/d124cbfb93d3d591e79bbe9e94c4b6304d825e71), [`fb5ccdf9`](https://github.com/xataio/client-ts/commit/fb5ccdf9fa95c37d54fbc5d9c0bb45872c831609), [`7da604d2`](https://github.com/xataio/client-ts/commit/7da604d27990e20ecadba6122434fca563e6a8c9), [`4ae00036`](https://github.com/xataio/client-ts/commit/4ae00036b53c6c89e02a1fcfdd992f1a3c22892c), [`bdae6668`](https://github.com/xataio/client-ts/commit/bdae6668fb571d29f1b1068a54f6866a80d9b174), [`9486bdcc`](https://github.com/xataio/client-ts/commit/9486bdccc0af567bc5f2e8f91592b0143c539c45), [`9486bdcc`](https://github.com/xataio/client-ts/commit/9486bdccc0af567bc5f2e8f91592b0143c539c45)]: - - @xata.io/client@0.21.0 - -## 0.0.3 - -### Patch Changes - -- Updated dependencies [[`6cbeaa0`](https://github.com/xataio/client-ts/commit/6cbeaa00050b5aa99ab7c98052a906487263e026), [`a5a9aa5`](https://github.com/xataio/client-ts/commit/a5a9aa59987faa1d3d701d7431b8a96031e01ac7), [`c64b2eb`](https://github.com/xataio/client-ts/commit/c64b2eb9add70e75d419d418ab9608caac0dbfa1), [`485b217`](https://github.com/xataio/client-ts/commit/485b217079c4b2091d697e68622c48eddd130ceb), [`4d7499c`](https://github.com/xataio/client-ts/commit/4d7499ccbb135691350334fd8022f7a5da41c5f2)]: - - @xata.io/client@0.20.0 - -## 0.0.2 - -### Patch Changes - -- Updated dependencies [[`f80f051`](https://github.com/xataio/client-ts/commit/f80f05118dd0588861b8229114a469f016ef77ac), [`c14f431`](https://github.com/xataio/client-ts/commit/c14f431db020036ab2b059bcc52a5d56b321c8e7), [`2e341e5`](https://github.com/xataio/client-ts/commit/2e341e5c6140f9c4ddd74e479049992c26c43ea2), [`c8def01`](https://github.com/xataio/client-ts/commit/c8def013e9e2d5b634cdb2850f757a0b3e9e0a6d), [`f2f749f`](https://github.com/xataio/client-ts/commit/f2f749f4c64246a303da8d4a617773fc55c1d021), [`f2f749f`](https://github.com/xataio/client-ts/commit/f2f749f4c64246a303da8d4a617773fc55c1d021)]: - - @xata.io/client@0.19.0 diff --git a/packages/plugin-client-cloudflare/package.json b/packages/plugin-client-cloudflare/package.json deleted file mode 100644 index 078d9f89a..000000000 --- a/packages/plugin-client-cloudflare/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "@xata.io/plugin-client-cloudflare", - "version": "0.0.46", - "description": "", - "main": "./dist/index.cjs", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.cjs", - "types": "./dist/index.d.ts" - } - }, - "scripts": { - "build": "rimraf dist && rollup -c", - "tsc": "tsc --noEmit" - }, - "author": "", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/xataio/client-ts/issues" - }, - "dependencies": { - "@cloudflare/workers-types": "^4.20240620.0", - "@xata.io/client": "workspace:*" - } -} diff --git a/packages/plugin-client-cloudflare/rollup.config.mjs b/packages/plugin-client-cloudflare/rollup.config.mjs deleted file mode 100644 index 9c57e2aa4..000000000 --- a/packages/plugin-client-cloudflare/rollup.config.mjs +++ /dev/null @@ -1,29 +0,0 @@ -import dts from 'rollup-plugin-dts'; -import esbuild from 'rollup-plugin-esbuild'; - -export default [ - { - input: 'src/index.ts', - plugins: [esbuild()], - output: [ - { - file: `dist/index.cjs`, - format: 'cjs', - sourcemap: true - }, - { - file: `dist/index.mjs`, - format: 'es', - sourcemap: true - } - ] - }, - { - input: 'src/index.ts', - plugins: [dts()], - output: { - file: `dist/index.d.ts`, - format: 'es' - } - } -]; diff --git a/packages/plugin-client-cloudflare/src/cache.ts b/packages/plugin-client-cloudflare/src/cache.ts deleted file mode 100644 index 916313492..000000000 --- a/packages/plugin-client-cloudflare/src/cache.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { CacheImpl, serialize, deserialize } from '@xata.io/client'; - -export type CloudflareKVCacheOptions = { namespace: KVNamespace; ttl?: number }; - -export class CloudflareKVCache implements CacheImpl { - #kv: KVNamespace; - defaultQueryTTL: number; - - constructor(options: CloudflareKVCacheOptions) { - this.#kv = options.namespace; - this.defaultQueryTTL = options.ttl ?? 60 * 1000; - } - - // FIXME: Binding does not support bulk operations yet. - async getAll(): Promise> { - const keys = await this.#listAll(); - const values = await Promise.all(keys.map((key) => this.get(key))); - return keys.reduce((acc, key, index) => ({ ...acc, [key]: values[index] }), {}); - } - - async get(key: string): Promise { - try { - const value = await this.#kv.get(key); - if (value === null) { - return null; - } - - return deserialize(value) as T; - } catch (e) { - // Ignore, KV namespace limit reached - console.error('KV namespace error', e); - return null; - } - } - - async set(key: string, value: T): Promise { - try { - await this.#kv.put(key, serialize(value), { expirationTtl: this.defaultQueryTTL }); - } catch (e) { - // Ignore, KV namespace limit reached - console.error('KV namespace error', e); - } - } - - async delete(key: string): Promise { - try { - await this.#kv.delete(key); - } catch (e) { - // Ignore, KV namespace limit reached - console.error('KV namespace error', e); - } - } - - // FIXME: Binding does not support bulk operations yet. - async clear(): Promise { - const keys = await this.#listAll(); - for (const key in keys) { - await this.delete(key); - } - } - - async #listAll(): Promise { - const getKeys = async (cursor?: string): Promise<{ keys: string[]; cursor?: string }> => { - try { - const result = await this.#kv.list({ cursor }); - const keys = result.keys.map((key) => key.name); - const nextCursor = result.list_complete ? undefined : result.cursor; - - return { keys, cursor: nextCursor }; - } catch (e) { - // Ignore, KV namespace limit reached - console.error('KV namespace error', e); - return { keys: [] }; - } - }; - - const { keys, cursor } = await getKeys(); - - let currentCursor = cursor; - while (currentCursor) { - const { keys: nextKeys, cursor: nextCursor } = await getKeys(currentCursor); - keys.push(...nextKeys); - currentCursor = nextCursor; - } - - return keys; - } -} diff --git a/packages/plugin-client-cloudflare/src/index.ts b/packages/plugin-client-cloudflare/src/index.ts deleted file mode 100644 index 77e55d4ef..000000000 --- a/packages/plugin-client-cloudflare/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './cache'; diff --git a/packages/plugin-client-cloudflare/tsconfig.json b/packages/plugin-client-cloudflare/tsconfig.json deleted file mode 100644 index d223dc872..000000000 --- a/packages/plugin-client-cloudflare/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "target": "es2020", - "lib": ["esnext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "es2020", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": false, - "outDir": "dist", - "declaration": true, - "types": ["@cloudflare/workers-types"] - }, - "include": ["src"], - "exclude": ["node_modules"] -} diff --git a/packages/plugin-client-drizzle/test/drizzle.test.ts b/packages/plugin-client-drizzle/test/drizzle.test.ts index 213e68dbc..fe70cfe3c 100644 --- a/packages/plugin-client-drizzle/test/drizzle.test.ts +++ b/packages/plugin-client-drizzle/test/drizzle.test.ts @@ -53,13 +53,16 @@ function getDomain(host: HostProvider) { function getDrizzleClient(type: string, branch: string) { if (type === 'http') { - const xata = new BaseClient({ - apiKey, - host, - clientName: 'sdk-tests', - databaseURL: `https://${workspace}.${region}.${getDomain(host)}/db/${database}`, - branch - }); + const xata = new BaseClient( + { + apiKey, + host, + clientName: 'sdk-tests', + databaseURL: `https://${workspace}.${region}.${getDomain(host)}/db/${database}`, + branch + }, + { tables: [] } + ); return { db: drizzleHttp(xata, { schema, logger: ENABLE_LOGGING }) }; } else if (type === 'pg') { diff --git a/packages/plugin-client-kysely/package.json b/packages/plugin-client-kysely/package.json index 486f9ec56..9bd9091a9 100644 --- a/packages/plugin-client-kysely/package.json +++ b/packages/plugin-client-kysely/package.json @@ -21,9 +21,7 @@ "bugs": { "url": "https://github.com/xataio/client-ts/issues" }, - "dependencies": { - "@xata.io/client": "workspace:*" - }, + "dependencies": {}, "devDependencies": { "kysely": "^0.27.3" }, diff --git a/packages/plugin-client-kysely/src/driver.ts b/packages/plugin-client-kysely/src/driver.ts index bf32f020e..a63fe7c28 100644 --- a/packages/plugin-client-kysely/src/driver.ts +++ b/packages/plugin-client-kysely/src/driver.ts @@ -11,10 +11,9 @@ import { QueryCompiler, QueryResult } from 'kysely'; -import { SQLPluginResult } from '@xata.io/client'; export type XataDialectConfig = { - xata: { sql: SQLPluginResult }; + xata: { sql: any }; /** * The consistency level to use when reading data. * @default 'strong' diff --git a/packages/plugin-client-kysely/src/index.ts b/packages/plugin-client-kysely/src/index.ts index d337a3cd3..0110d6923 100644 --- a/packages/plugin-client-kysely/src/index.ts +++ b/packages/plugin-client-kysely/src/index.ts @@ -1,44 +1,5 @@ -import { SQLPlugin, XataPlugin, XataPluginOptions, XataRecord } from '@xata.io/client'; -import { Kysely } from 'kysely'; -import { XataDialect } from './driver'; - -export type KyselyPluginResult> = Kysely>; - -export class KyselyPlugin> extends XataPlugin { - build(pluginOptions: XataPluginOptions): KyselyPluginResult { - const xata = { sql: new SQLPlugin().build(pluginOptions) }; - - return new Kysely>({ - dialect: new XataDialect({ xata }) - }); - } -} - -type XataFilePgFields = { - id?: string; - mediaType?: string; - size?: number; - name?: string; - enablePublicUrl?: boolean; - signedUrlTimeout?: number; - storageKey?: string; - uploadKey?: string; - uploadUrlTimeout?: number; - version?: number; -}; - -type RowTypeFields = T extends { mediaType?: string } - ? XataFilePgFields - : T extends Array<{ mediaType?: string }> - ? XataFilePgFields[] - : T; - -type RowType = { - [K in keyof O]: RowTypeFields>; -}; - export type Model> = { - [Model in keyof Schemas]: RowType; + [Model in keyof Schemas]: Schemas[Model]; }; export * from './driver'; diff --git a/packages/plugin-client-kysely/test/kysely.test.ts b/packages/plugin-client-kysely/test/kysely.test.ts index 0dd94d9d6..61dea2016 100644 --- a/packages/plugin-client-kysely/test/kysely.test.ts +++ b/packages/plugin-client-kysely/test/kysely.test.ts @@ -34,16 +34,16 @@ afterEach(async (ctx) => { const file = new Blob(['hello'], { type: 'text/plain' }); describe('@xata.io/kysely plugin', () => { - test('Select multiple columns', async () => { + test.skip('Select multiple columns', async () => { const user = await xata.db.users.create({ name: 'John Doe', attachments: [file] }); - const users = await db.selectFrom('users').selectAll().where('id', '=', user.id).execute(); + const users = await db.selectFrom('users').selectAll().where('xata_id', '=', user.xata_id).execute(); expect(users).toHaveLength(1); expect(users[0].account_value).toBe(null); expect(users[0].attachments).toHaveLength(1); expect(users[0].attachments?.[0].enablePublicUrl).toBe(false); - expect(users[0].attachments?.[0].id).toBeDefined(); + expect(users[0].attachments?.[0].xata_id).toBeDefined(); expect(users[0].attachments?.[0].mediaType).toBe('application/octet-stream'); expect(users[0].attachments?.[0].name).toBe(''); expect(users[0].attachments?.[0].signedUrlTimeout).toBe(60); @@ -56,7 +56,7 @@ describe('@xata.io/kysely plugin', () => { expect(users[0].dark).toBe(null); expect(users[0].email).toBe(null); expect(users[0].full_name).toBe('John Doe'); - expect(users[0].id).toBeDefined(); + expect(users[0].xata_id).toBeDefined(); expect(users[0].index).toBe(null); expect(users[0].name).toBe('John Doe'); expect(users[0].pet).toBe(null); @@ -79,9 +79,9 @@ describe('@xata.io/kysely plugin', () => { test("Update record's column", async () => { const user = await xata.db.users.create({ name: 'John Doe' }); - await db.updateTable('users').set('name', 'Jane Doe').where('id', '=', user.id).execute(); + await db.updateTable('users').set('name', 'Jane Doe').where('xata_id', '=', user.xata_id).execute(); - const users = await db.selectFrom('users').selectAll().where('id', '=', user.id).execute(); + const users = await db.selectFrom('users').selectAll().where('xata_id', '=', user.xata_id).execute(); expect(users).toHaveLength(1); expect(users[0].name).toBe('Jane Doe'); @@ -90,18 +90,18 @@ describe('@xata.io/kysely plugin', () => { test('Update numeric column', async () => { const user = await xata.db.users.create({ account_value: 100 }); - await db.updateTable('users').set('account_value', 200).where('id', '=', user.id).execute(); + await db.updateTable('users').set('account_value', 200).where('xata_id', '=', user.xata_id).execute(); - const users = await db.selectFrom('users').selectAll().where('id', '=', user.id).execute(); + const users = await db.selectFrom('users').selectAll().where('xata_id', '=', user.xata_id).execute(); expect(users).toHaveLength(1); expect(users[0].account_value).toBe(200); - const incremented = await xata.db.users.update(user.id, { account_value: { $increment: 100 } }); + const incremented = await xata.db.users.update(user.xata_id, { account_value: { $increment: 100 } }); expect(incremented?.account_value).toBe(300); - const users2 = await db.selectFrom('users').selectAll().where('id', '=', user.id).execute(); + const users2 = await db.selectFrom('users').selectAll().where('xata_id', '=', user.xata_id).execute(); expect(users2).toHaveLength(1); expect(users2[0].account_value).toBe(300); @@ -110,7 +110,7 @@ describe('@xata.io/kysely plugin', () => { test("Select single columns with 'as' alias", async () => { const user = await xata.db.users.create({ name: 'John Doe' }); - const users = await db.selectFrom('users').select('name as name2').where('id', '=', user.id).execute(); + const users = await db.selectFrom('users').select('name as name2').where('xata_id', '=', user.xata_id).execute(); expect(users).toHaveLength(1); expect(users[0].name2).toBe('John Doe'); @@ -119,10 +119,14 @@ describe('@xata.io/kysely plugin', () => { test('Select multiple column type', async () => { const team = await xata.db.teams.create({ name: 'Team A', labels: ['A', 'B'] }); - const teams = await db.selectFrom('teams').select(['id', 'labels']).where('id', '=', team.id).execute(); + const teams = await db + .selectFrom('teams') + .select(['xata_id', 'labels']) + .where('xata_id', '=', team.xata_id) + .execute(); expect(teams).toHaveLength(1); - expect(teams[0].id).toBeDefined(); + expect(teams[0].xata_id).toBeDefined(); expect(teams[0].labels).toEqual(['A', 'B']); }); }); diff --git a/packages/plugin-client-cache/.npmignore b/packages/sql/.npmignore similarity index 100% rename from packages/plugin-client-cache/.npmignore rename to packages/sql/.npmignore diff --git a/packages/sql/CHANGELOG.md b/packages/sql/CHANGELOG.md new file mode 100644 index 000000000..129804bb5 --- /dev/null +++ b/packages/sql/CHANGELOG.md @@ -0,0 +1 @@ +# @xata.io/sql diff --git a/packages/sql/README.md b/packages/sql/README.md new file mode 100644 index 000000000..bb75e29f9 --- /dev/null +++ b/packages/sql/README.md @@ -0,0 +1 @@ +This package is meant to re-use code common for SQL connections to Xata. diff --git a/packages/plugin-client-cache/package.json b/packages/sql/package.json similarity index 51% rename from packages/plugin-client-cache/package.json rename to packages/sql/package.json index aa812ed07..9048e24e7 100644 --- a/packages/plugin-client-cache/package.json +++ b/packages/sql/package.json @@ -1,7 +1,8 @@ { - "name": "@xata.io/plugin-client-cache", - "version": "0.1.47", - "description": "", + "name": "@xata.io/sql", + "version": "0.0.1", + "description": "Helpers for SQL connection to Xata", + "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -13,21 +14,28 @@ } }, "scripts": { + "generate": "tsx scripts/build.ts", "build": "rimraf dist && rollup -c", "tsc": "tsc --noEmit" }, + "repository": { + "type": "git", + "url": "git+https://github.com/xataio/client-ts.git" + }, + "keywords": [], "author": "", "license": "Apache-2.0", "bugs": { "url": "https://github.com/xataio/client-ts/issues" }, + "homepage": "https://github.com/xataio/client-ts", "dependencies": { - "@xata.io/client": "workspace:*" + "json-stringify-deterministic": "^1.0.12", + "kysely": "^0.27.3", + "pako": "^2.1.0", + "rfc4648": "^1.5.3" }, "devDependencies": { - "lru-cache": "^10.3.0" - }, - "peerDependencies": { - "lru-cache": "^7" + "@types/pako": "^2.0.3" } } diff --git a/packages/plugin-client-cache/rollup.config.mjs b/packages/sql/rollup.config.mjs similarity index 100% rename from packages/plugin-client-cache/rollup.config.mjs rename to packages/sql/rollup.config.mjs diff --git a/packages/sql/src/cursor.ts b/packages/sql/src/cursor.ts new file mode 100644 index 000000000..b6ae1919d --- /dev/null +++ b/packages/sql/src/cursor.ts @@ -0,0 +1,52 @@ +import pako from 'pako'; +import { base64url } from 'rfc4648'; +import stringify from 'json-stringify-deterministic'; + +export class Cursor { + data: Data; + + constructor(cursor: string) { + const decoded = base64url.parse(cursor, { loose: true }); + const decompressed = pako.inflate(decoded, { to: 'string', raw: true }); + + const [encoding, format, ...rest] = decompressed; + if (encoding !== 'j' || format !== '1') { + throw new Error('Invalid cursor'); + } + + this.data = JSON.parse(rest.join('')); + } + + static #encode(data: Data): string { + const compressed = pako.deflate('j1' + stringify(data), { + raw: true, + strategy: pako.constants.Z_DEFAULT_STRATEGY, + level: -1 + }); + + return base64url.stringify(compressed, { pad: false }); + } + + static from(data: Data): Cursor { + return new Cursor(this.#encode(data)); + } + + toString(): string { + return Cursor.#encode(this.data); + } +} + +export function compactRecord(record: Record): Record { + return Object.fromEntries(Object.entries(record).filter(([, value]) => !!value)) as Record; +} + +export function decode(data: string): Record { + const decoded = base64url.parse(data, { loose: true }); + const decompressed = pako.inflate(decoded, { to: 'string', raw: true }); + const [encoding, format, ...rest] = decompressed; + if (encoding !== 'j' || format !== '1') { + throw new Error('Invalid cursor'); + } + + return JSON.parse(rest.join('')); +} diff --git a/packages/sql/src/index.ts b/packages/sql/src/index.ts new file mode 100644 index 000000000..87b9db0e6 --- /dev/null +++ b/packages/sql/src/index.ts @@ -0,0 +1 @@ +export * from './cursor'; diff --git a/packages/sql/src/util/lang.ts b/packages/sql/src/util/lang.ts new file mode 100644 index 000000000..f4740f05a --- /dev/null +++ b/packages/sql/src/util/lang.ts @@ -0,0 +1,26 @@ +export function isBlob(value: any): value is Blob { + try { + return value instanceof Blob; + } catch (error) { + // Node prior to v18.0.0 doesn't support instanceof Blob and throws a ReferenceError + return false; + } +} + +export function isObject(value: any): value is Record { + return ( + Boolean(value) && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date) && !isBlob(value) + ); +} + +export function isDefined(value: T | null | undefined): value is T { + return value !== null && value !== undefined; +} + +export function isString(value: any): value is string { + return isDefined(value) && typeof value === 'string'; +} + +export function isStringArray(value: any): value is string[] { + return isDefined(value) && Array.isArray(value) && value.every(isString); +} diff --git a/packages/plugin-client-cache/tsconfig.json b/packages/sql/tsconfig.json similarity index 94% rename from packages/plugin-client-cache/tsconfig.json rename to packages/sql/tsconfig.json index 654c56dd1..4e83031a3 100644 --- a/packages/plugin-client-cache/tsconfig.json +++ b/packages/sql/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es2020", - "lib": ["esnext"], + "lib": ["esnext", "dom"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d07ae0fc7..3947fbba6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,10 +15,10 @@ importers: version: 6.1.0 '@pnpm/read-project-manifest': specifier: ^6.0.3 - version: 6.0.3 + version: 6.0.4 '@pnpm/write-project-manifest': specifier: ^6.0.2 - version: 6.0.2 + version: 6.0.3 devDependencies: '@babel/core': specifier: ^7.24.7 @@ -34,7 +34,7 @@ importers: version: 0.5.0 '@changesets/cli': specifier: ^2.27.6 - version: 2.27.6 + version: 2.27.7 '@openapi-codegen/cli': specifier: ^2.0.2 version: 2.0.2(react@17.0.2) @@ -67,13 +67,13 @@ importers: version: 11.1.4(size-limit@11.1.4) '@types/node': specifier: ^20.14.9 - version: 20.14.9 + version: 20.14.10 '@typescript-eslint/eslint-plugin': specifier: ^7.14.1 - version: 7.14.1(@typescript-eslint/parser@7.14.1)(eslint@9.6.0)(typescript@5.5.2) + version: 7.15.0(@typescript-eslint/parser@7.15.0)(eslint@9.6.0)(typescript@5.5.3) '@typescript-eslint/parser': specifier: ^7.14.1 - version: 7.14.1(eslint@9.6.0)(typescript@5.5.2) + version: 7.15.0(eslint@9.6.0)(typescript@5.5.3) doctoc: specifier: ^2.2.1 version: 2.2.1 @@ -85,10 +85,10 @@ importers: version: 9.6.0 eslint-import-resolver-typescript: specifier: ^3.6.1 - version: 3.6.1(@typescript-eslint/parser@7.14.1)(eslint-plugin-import@2.29.1)(eslint@9.6.0) + version: 3.6.1(@typescript-eslint/parser@7.15.0)(eslint-plugin-import@2.29.1)(eslint@9.6.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.14.1)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + version: 2.29.1(@typescript-eslint/parser@7.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) husky: specifier: ^9.0.11 version: 9.0.11 @@ -97,7 +97,7 @@ importers: version: 15.2.7 msw: specifier: ^2.3.1 - version: 2.3.1(typescript@5.5.2) + version: 2.3.1(typescript@5.5.3) prettier: specifier: '=2.8.8' version: 2.8.8 @@ -112,7 +112,7 @@ importers: version: 2.0.0(rollup@4.18.0) rollup-plugin-dts: specifier: ^6.1.1 - version: 6.1.1(rollup@4.18.0)(typescript@5.5.2) + version: 6.1.1(rollup@4.18.0)(typescript@5.5.3) rollup-plugin-esbuild: specifier: ^6.1.1 version: 6.1.1(esbuild@0.21.5)(rollup@4.18.0) @@ -133,25 +133,25 @@ importers: version: 11.1.4 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.14.9)(typescript@5.5.2) + version: 10.9.2(@types/node@20.14.10)(typescript@5.5.3) tsx: specifier: ^4.16.0 - version: 4.16.0 + version: 4.16.2 turbo: specifier: ^2.0.6 version: 2.0.6 typescript: specifier: ^5.5.2 - version: 5.5.2 + version: 5.5.3 typescript-eslint: specifier: ^7.14.1 - version: 7.14.1(eslint@9.6.0)(typescript@5.5.2) + version: 7.15.0(eslint@9.6.0)(typescript@5.5.3) vite: specifier: ^5.3.2 - version: 5.3.2(@types/node@20.14.9) + version: 5.3.3(@types/node@20.14.10) vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@20.14.9) + version: 1.6.0(@types/node@20.14.10) zod: specifier: ^3.23.8 version: 3.23.8 @@ -160,16 +160,16 @@ importers: dependencies: '@oclif/core': specifier: ^4.0.7 - version: 4.0.7 + version: 4.0.8 '@oclif/plugin-help': specifier: ^6.2.4 - version: 6.2.4 + version: 6.2.5 '@oclif/plugin-not-found': specifier: ^3.2.8 - version: 3.2.8 + version: 3.2.10 '@oclif/plugin-plugins': specifier: ^5.3.3 - version: 5.3.3 + version: 5.3.4 '@types/ini': specifier: ^4.1.1 version: 4.1.1 @@ -199,7 +199,7 @@ importers: version: 5.3.0 cosmiconfig: specifier: ^9.0.0 - version: 9.0.0(typescript@5.5.2) + version: 9.0.0(typescript@5.5.3) deepmerge: specifier: ^4.3.1 version: 4.3.1 @@ -255,11 +255,11 @@ importers: specifier: ^0.2.3 version: 0.2.3 tslib: - specifier: ^2.6.3 - version: 2.6.3 + specifier: ^2.6.2 + version: 2.6.2 type-fest: - specifier: ^4.20.1 - version: 4.20.1 + specifier: ^4.18.1 + version: 4.18.1 which: specifier: ^4.0.0 version: 4.0.0 @@ -305,22 +305,31 @@ importers: version: 5.2.0(eslint@9.6.0) eslint-config-oclif-typescript: specifier: ^3.1.8 - version: 3.1.8(eslint@9.6.0)(typescript@5.5.2) + version: 3.1.8(eslint@9.6.0)(typescript@5.5.3) oclif: specifier: ^4.13.12 - version: 4.13.12 + version: 4.13.16 shx: specifier: ^0.3.4 version: 0.3.4 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.14.9)(typescript@5.5.2) + version: 10.9.2(@types/node@20.14.10)(typescript@5.5.3) typescript: specifier: ^5.5.2 - version: 5.5.2 + version: 5.5.3 packages/client: dependencies: + '@xata.io/kysely': + specifier: workspace:* + version: link:../plugin-client-kysely + '@xata.io/sql': + specifier: workspace:* + version: link:../sql + kysely: + specifier: ^0.27.3 + version: 0.27.3 typescript: specifier: '>=4.5' version: 5.2.2 @@ -341,7 +350,7 @@ importers: version: 23.0.0 typescript: specifier: ^5.5.2 - version: 5.5.2 + version: 5.5.3 zod: specifier: ^3.23.8 version: 3.23.8 @@ -359,7 +368,7 @@ importers: specifier: ^8.4.1 version: 8.4.1 '@xata.io/client': - specifier: ^0.29.5 + specifier: workspace:* version: link:../client any-date-parser: specifier: ^1.5.4 @@ -410,26 +419,7 @@ importers: version: 23.0.0 tsx: specifier: ^4.16.0 - version: 4.16.0 - - packages/plugin-client-cache: - dependencies: - '@xata.io/client': - specifier: workspace:* - version: link:../client - devDependencies: - lru-cache: - specifier: ^10.3.0 - version: 10.3.0 - - packages/plugin-client-cloudflare: - dependencies: - '@cloudflare/workers-types': - specifier: ^4.20240620.0 - version: 4.20240620.0 - '@xata.io/client': - specifier: workspace:* - version: link:../client + version: 4.16.2 packages/plugin-client-drizzle: dependencies: @@ -448,10 +438,6 @@ importers: version: 8.12.0 packages/plugin-client-kysely: - dependencies: - '@xata.io/client': - specifier: workspace:* - version: link:../client devDependencies: kysely: specifier: ^0.27.3 @@ -464,7 +450,7 @@ importers: version: 7.24.7 '@netlify/build': specifier: '=29.20.6' - version: 29.20.6(@types/node@20.14.9) + version: 29.20.6(@types/node@20.14.10) '@xata.io/client': specifier: workspace:* version: link:../client @@ -474,10 +460,10 @@ importers: version: 7.20.5 '@types/node': specifier: ^20.14.9 - version: 20.14.9 + version: 20.14.10 typescript: specifier: ^5.5.2 - version: 5.5.2 + version: 5.5.3 packages/plugin-client-opentelemetry: dependencies: @@ -488,6 +474,25 @@ importers: specifier: workspace:* version: link:../client + packages/sql: + dependencies: + json-stringify-deterministic: + specifier: ^1.0.12 + version: 1.0.12 + kysely: + specifier: ^0.27.3 + version: 0.27.3 + pako: + specifier: ^2.1.0 + version: 2.1.0 + rfc4648: + specifier: ^1.5.3 + version: 1.5.3 + devDependencies: + '@types/pako': + specifier: ^2.0.3 + version: 2.0.3 + packages: /@aashutoshrathi/word-wrap@1.2.6: resolution: @@ -535,7 +540,7 @@ packages: response-iterator: 0.2.6 symbol-observable: 4.0.0 ts-invariant: 0.10.3 - tslib: 2.6.3 + tslib: 2.6.2 zen-observable-ts: 1.2.5 dev: true @@ -545,8 +550,8 @@ packages: engines: { node: '>=16.0.0' } dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.598.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.2 dev: true /@aws-crypto/crc32c@5.2.0: @@ -554,8 +559,8 @@ packages: { integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== } dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.598.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.2 dev: true /@aws-crypto/sha1-browser@5.2.0: @@ -564,10 +569,10 @@ packages: dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.598.0 + '@aws-sdk/types': 3.609.0 '@aws-sdk/util-locate-window': 3.465.0 '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@aws-crypto/sha256-browser@5.2.0: @@ -577,10 +582,10 @@ packages: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.598.0 + '@aws-sdk/types': 3.609.0 '@aws-sdk/util-locate-window': 3.465.0 '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@aws-crypto/sha256-js@5.2.0: @@ -589,798 +594,606 @@ packages: engines: { node: '>=16.0.0' } dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.598.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.2 dev: true /@aws-crypto/supports-web-crypto@5.2.0: resolution: { integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@aws-crypto/util@5.2.0: resolution: { integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== } dependencies: - '@aws-sdk/types': 3.598.0 + '@aws-sdk/types': 3.609.0 '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@aws-sdk/client-cloudfront@3.600.0: + /@aws-sdk/client-cloudfront@3.609.0: resolution: - { integrity: sha512-5qO3lc6AvErAqia552zA8ADwFO3UiJpJ8R2jy7JL18RifmePVs/f0jPeWPtAoV81iehmFziLyu6pWUMnfh3EJg== } + { integrity: sha512-nG/vhAYlNCJ2BrDGLT/NYqjgewNhBGEk8HxqSiRYWTePvnTuN5z+Qqo+MzC4q9fnBfUcdLcwBbFFlIjgBPBbCQ== } engines: { node: '>=16.0.0' } dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@aws-sdk/xml-builder': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.2 - '@smithy/middleware-stack': 3.0.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/node-http-handler': 3.1.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/core': 3.609.0 + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/middleware-host-header': 3.609.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.609.0 + '@aws-sdk/middleware-user-agent': 3.609.0 + '@aws-sdk/region-config-resolver': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.609.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.609.0 + '@aws-sdk/xml-builder': 3.609.0 + '@smithy/config-resolver': 3.0.4 + '@smithy/core': 2.2.4 + '@smithy/fetch-http-handler': 3.2.0 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.3 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.7 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/node-http-handler': 3.1.1 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.2 - '@smithy/util-retry': 3.0.2 - '@smithy/util-stream': 3.0.4 + '@smithy/util-defaults-mode-browser': 3.0.7 + '@smithy/util-defaults-mode-node': 3.0.7 + '@smithy/util-endpoints': 2.0.4 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 + '@smithy/util-stream': 3.0.5 '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.1.1 - tslib: 2.6.3 + '@smithy/util-waiter': 3.1.2 + tslib: 2.6.2 transitivePeerDependencies: - aws-crt dev: true - /@aws-sdk/client-s3@3.606.0: + /@aws-sdk/client-s3@3.609.0: resolution: - { integrity: sha512-IGM/E8kVk/NY/kZwLdmGRsX1QYtuPljoNutM5kBRdtGahQL5VwVAve5PElPUArcsTkfTyW+LfXpznDeeHxMCcA== } + { integrity: sha512-lh8NxL9qm8eSphEcsTGjNMArYRlga4yTZCr3d7UPCRFiV1oz3e0EIA5EnxSriYi9P5Houi5d9GSWtPOel2mAow== } engines: { node: '>=16.0.0' } dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.606.0(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/client-sts': 3.606.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.606.0)(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/middleware-bucket-endpoint': 3.598.0 - '@aws-sdk/middleware-expect-continue': 3.598.0 - '@aws-sdk/middleware-flexible-checksums': 3.598.0 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-location-constraint': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-sdk-s3': 3.598.0 - '@aws-sdk/middleware-signing': 3.598.0 - '@aws-sdk/middleware-ssec': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/signature-v4-multi-region': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@aws-sdk/xml-builder': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/eventstream-serde-browser': 3.0.3 - '@smithy/eventstream-serde-config-resolver': 3.0.2 - '@smithy/eventstream-serde-node': 3.0.3 - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/hash-blob-browser': 3.1.1 - '@smithy/hash-node': 3.0.2 - '@smithy/hash-stream-node': 3.1.1 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/md5-js': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.2 - '@smithy/middleware-stack': 3.0.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/node-http-handler': 3.1.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/core': 3.609.0 + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/middleware-bucket-endpoint': 3.609.0 + '@aws-sdk/middleware-expect-continue': 3.609.0 + '@aws-sdk/middleware-flexible-checksums': 3.609.0 + '@aws-sdk/middleware-host-header': 3.609.0 + '@aws-sdk/middleware-location-constraint': 3.609.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.609.0 + '@aws-sdk/middleware-sdk-s3': 3.609.0 + '@aws-sdk/middleware-signing': 3.609.0 + '@aws-sdk/middleware-ssec': 3.609.0 + '@aws-sdk/middleware-user-agent': 3.609.0 + '@aws-sdk/region-config-resolver': 3.609.0 + '@aws-sdk/signature-v4-multi-region': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.609.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.609.0 + '@aws-sdk/xml-builder': 3.609.0 + '@smithy/config-resolver': 3.0.4 + '@smithy/core': 2.2.4 + '@smithy/eventstream-serde-browser': 3.0.4 + '@smithy/eventstream-serde-config-resolver': 3.0.3 + '@smithy/eventstream-serde-node': 3.0.4 + '@smithy/fetch-http-handler': 3.2.0 + '@smithy/hash-blob-browser': 3.1.2 + '@smithy/hash-node': 3.0.3 + '@smithy/hash-stream-node': 3.1.2 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/md5-js': 3.0.3 + '@smithy/middleware-content-length': 3.0.3 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.7 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/node-http-handler': 3.1.1 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-retry': 3.0.2 - '@smithy/util-stream': 3.0.4 + '@smithy/util-defaults-mode-browser': 3.0.7 + '@smithy/util-defaults-mode-node': 3.0.7 + '@smithy/util-endpoints': 2.0.4 + '@smithy/util-retry': 3.0.3 + '@smithy/util-stream': 3.0.5 '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.1.1 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - dev: true - - /@aws-sdk/client-sso-oidc@3.600.0: - resolution: - { integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw== } - engines: { node: '>=16.0.0' } - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.2 - '@smithy/middleware-stack': 3.0.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/node-http-handler': 3.1.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.2 - '@smithy/util-retry': 3.0.2 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + '@smithy/util-waiter': 3.1.2 + tslib: 2.6.2 transitivePeerDependencies: - aws-crt dev: true - /@aws-sdk/client-sso-oidc@3.606.0(@aws-sdk/client-sts@3.606.0): + /@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0): resolution: - { integrity: sha512-gL1FHPS6hwgMNS/A+Qh5bUyHOeRVOqdb7c6+i+9gR3wtGvt2lvoSm8w5DhS08Xiiacz2AqYRDEapp0xuyCrbBQ== } + { integrity: sha512-0bNPAyPdkWkS9EGB2A9BZDkBNrnVCBzk5lYRezoT4K3/gi9w1DTYH5tuRdwaTZdxW19U1mq7CV0YJJARKO1L9Q== } engines: { node: '>=16.0.0' } peerDependencies: - '@aws-sdk/client-sts': ^3.606.0 + '@aws-sdk/client-sts': ^3.609.0 dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.606.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.606.0)(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.2 - '@smithy/middleware-stack': 3.0.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/node-http-handler': 3.1.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/core': 3.609.0 + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/middleware-host-header': 3.609.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.609.0 + '@aws-sdk/middleware-user-agent': 3.609.0 + '@aws-sdk/region-config-resolver': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.609.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.609.0 + '@smithy/config-resolver': 3.0.4 + '@smithy/core': 2.2.4 + '@smithy/fetch-http-handler': 3.2.0 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.3 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.7 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/node-http-handler': 3.1.1 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.2 - '@smithy/util-retry': 3.0.2 + '@smithy/util-defaults-mode-browser': 3.0.7 + '@smithy/util-defaults-mode-node': 3.0.7 + '@smithy/util-endpoints': 2.0.4 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - dev: true - - /@aws-sdk/client-sso@3.598.0: - resolution: - { integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw== } - engines: { node: '>=16.0.0' } - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.2 - '@smithy/middleware-stack': 3.0.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/node-http-handler': 3.1.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.2 - '@smithy/util-retry': 3.0.2 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 transitivePeerDependencies: - aws-crt dev: true - /@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0): + /@aws-sdk/client-sso@3.609.0: resolution: - { integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA== } + { integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg== } engines: { node: '>=16.0.0' } dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.2 - '@smithy/middleware-stack': 3.0.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/node-http-handler': 3.1.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 + '@aws-sdk/core': 3.609.0 + '@aws-sdk/middleware-host-header': 3.609.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.609.0 + '@aws-sdk/middleware-user-agent': 3.609.0 + '@aws-sdk/region-config-resolver': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.609.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.609.0 + '@smithy/config-resolver': 3.0.4 + '@smithy/core': 2.2.4 + '@smithy/fetch-http-handler': 3.2.0 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.3 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.7 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/node-http-handler': 3.1.1 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.2 - '@smithy/util-retry': 3.0.2 + '@smithy/util-defaults-mode-browser': 3.0.7 + '@smithy/util-defaults-mode-node': 3.0.7 + '@smithy/util-endpoints': 2.0.4 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - aws-crt dev: true - /@aws-sdk/client-sts@3.606.0: + /@aws-sdk/client-sts@3.609.0: resolution: - { integrity: sha512-b11mAhjrkm3MMiAPoMGcmd6vsaz2120lg8rHG/NZCo9vB1K6Kc7WP+a1Q05TRMseer2egTtpWJfn44aVO97VqA== } + { integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ== } engines: { node: '>=16.0.0' } dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.606.0(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.606.0)(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.2 - '@smithy/middleware-stack': 3.0.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/node-http-handler': 3.1.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/core': 3.609.0 + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/middleware-host-header': 3.609.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.609.0 + '@aws-sdk/middleware-user-agent': 3.609.0 + '@aws-sdk/region-config-resolver': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.609.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.609.0 + '@smithy/config-resolver': 3.0.4 + '@smithy/core': 2.2.4 + '@smithy/fetch-http-handler': 3.2.0 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.3 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.7 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/node-http-handler': 3.1.1 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.2 - '@smithy/util-retry': 3.0.2 + '@smithy/util-defaults-mode-browser': 3.0.7 + '@smithy/util-defaults-mode-node': 3.0.7 + '@smithy/util-endpoints': 2.0.4 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 transitivePeerDependencies: - aws-crt dev: true - /@aws-sdk/core@3.598.0: + /@aws-sdk/core@3.609.0: resolution: - { integrity: sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g== } + { integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/core': 2.2.3 - '@smithy/protocol-http': 4.0.2 - '@smithy/signature-v4': 3.1.1 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 + '@smithy/core': 2.2.4 + '@smithy/protocol-http': 4.0.3 + '@smithy/signature-v4': 3.1.2 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 fast-xml-parser: 4.2.5 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@aws-sdk/credential-provider-env@3.598.0: + /@aws-sdk/credential-provider-env@3.609.0: resolution: - { integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w== } + { integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/credential-provider-http@3.598.0: + /@aws-sdk/credential-provider-http@3.609.0: resolution: - { integrity: sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g== } + { integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/node-http-handler': 3.1.0 - '@smithy/property-provider': 3.1.2 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/util-stream': 3.0.4 - tslib: 2.6.3 - dev: true - - /@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0): - resolution: - { integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA== } - engines: { node: '>=16.0.0' } - peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 - dependencies: - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/credential-provider-env': 3.598.0 - '@aws-sdk/credential-provider-http': 3.598.0 - '@aws-sdk/credential-provider-process': 3.598.0 - '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.1.2 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt + '@aws-sdk/types': 3.609.0 + '@smithy/fetch-http-handler': 3.2.0 + '@smithy/node-http-handler': 3.1.1 + '@smithy/property-provider': 3.1.3 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + '@smithy/util-stream': 3.0.5 + tslib: 2.6.2 dev: true - /@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.606.0)(@aws-sdk/client-sts@3.606.0): + /@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0): resolution: - { integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA== } + { integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog== } engines: { node: '>=16.0.0' } peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 - dependencies: - '@aws-sdk/client-sts': 3.606.0 - '@aws-sdk/credential-provider-env': 3.598.0 - '@aws-sdk/credential-provider-http': 3.598.0 - '@aws-sdk/credential-provider-process': 3.598.0 - '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.606.0) - '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.1.2 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - dev: true - - /@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0): - resolution: - { integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw== } - engines: { node: '>=16.0.0' } - dependencies: - '@aws-sdk/credential-provider-env': 3.598.0 - '@aws-sdk/credential-provider-http': 3.598.0 - '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/credential-provider-process': 3.598.0 - '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.1.2 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/client-sts': ^3.609.0 + dependencies: + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.1.3 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - aws-crt dev: true - /@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.606.0)(@aws-sdk/client-sts@3.606.0): + /@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0): resolution: - { integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw== } + { integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/credential-provider-env': 3.598.0 - '@aws-sdk/credential-provider-http': 3.598.0 - '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.606.0)(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/credential-provider-process': 3.598.0 - '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.606.0) - '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.1.2 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.1.3 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt dev: true - /@aws-sdk/credential-provider-process@3.598.0: - resolution: - { integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA== } - engines: { node: '>=16.0.0' } - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 - dev: true - - /@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0): + /@aws-sdk/credential-provider-process@3.609.0: resolution: - { integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA== } + { integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/client-sso': 3.598.0 - '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.606.0): + /@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0): resolution: - { integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA== } + { integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/client-sso': 3.598.0 - '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.606.0) - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/client-sso': 3.609.0 + '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt dev: true - /@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0): - resolution: - { integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w== } - engines: { node: '>=16.0.0' } - peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 - dependencies: - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 - dev: true - - /@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.606.0): + /@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0): resolution: - { integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w== } + { integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg== } engines: { node: '>=16.0.0' } peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 + '@aws-sdk/client-sts': ^3.609.0 dependencies: - '@aws-sdk/client-sts': 3.606.0 - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-bucket-endpoint@3.598.0: + /@aws-sdk/middleware-bucket-endpoint@3.609.0: resolution: - { integrity: sha512-PM7BcFfGUSkmkT6+LU9TyJiB4S8yI7dfuKQDwK5ZR3P7MKaK4Uj4yyDiv0oe5xvkF6+O2+rShj+eh8YuWkOZ/Q== } + { integrity: sha512-QhHRfr4e7FqaMUAnOAFdQVOR3yDLw40i1IZPo+TeiKyev9LEyYEX2l6DbdaIwAztofOpAxfFNj/IJ0V/efzz/w== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 + '@aws-sdk/types': 3.609.0 '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.2 - '@smithy/protocol-http': 4.0.2 - '@smithy/types': 3.2.0 + '@smithy/node-config-provider': 3.1.3 + '@smithy/protocol-http': 4.0.3 + '@smithy/types': 3.3.0 '@smithy/util-config-provider': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-expect-continue@3.598.0: + /@aws-sdk/middleware-expect-continue@3.609.0: resolution: - { integrity: sha512-ZuHW18kaeHR8TQyhEOYMr8VwiIh0bMvF7J1OTqXHxDteQIavJWA3CbfZ9sgS4XGtrBZDyHJhjZKeCfLhN2rq3w== } + { integrity: sha512-+zeg//mSer4JZRxOB/4mUOMUJyuYPwATnIC5moBB8P8Xe+mJaVRFy8qlCtzYNj2TycnlsBPzTK0j7P1yvDh97w== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/protocol-http': 4.0.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-flexible-checksums@3.598.0: + /@aws-sdk/middleware-flexible-checksums@3.609.0: resolution: - { integrity: sha512-xukAzds0GQXvMEY9G6qt+CzwVzTx8NyKKh04O2Q+nOch6QQ8Rs+2kTRy3Z4wQmXq2pK9hlOWb5nXA7HWpmz6Ng== } + { integrity: sha512-TJ4WE+ehT+qcrhr7/yJCzmJJPmUoPPWIbCnFzqGxauH/dpVBCslmd1vZg3h2VnfRiaDkc6f68dqYVc29CaurhQ== } engines: { node: '>=16.0.0' } dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 - '@aws-sdk/types': 3.598.0 + '@aws-sdk/types': 3.609.0 '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/types': 3.2.0 + '@smithy/protocol-http': 4.0.3 + '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-host-header@3.598.0: + /@aws-sdk/middleware-host-header@3.609.0: resolution: - { integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA== } + { integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/protocol-http': 4.0.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-location-constraint@3.598.0: + /@aws-sdk/middleware-location-constraint@3.609.0: resolution: - { integrity: sha512-8oybQxN3F1ISOMULk7JKJz5DuAm5hCUcxMW9noWShbxTJuStNvuHf/WLUzXrf8oSITyYzIHPtf8VPlKR7I3orQ== } + { integrity: sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-logger@3.598.0: + /@aws-sdk/middleware-logger@3.609.0: resolution: - { integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q== } + { integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-recursion-detection@3.598.0: + /@aws-sdk/middleware-recursion-detection@3.609.0: resolution: - { integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ== } + { integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/protocol-http': 4.0.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-sdk-s3@3.598.0: + /@aws-sdk/middleware-sdk-s3@3.609.0: resolution: - { integrity: sha512-5AGtLAh9wyK6ANPYfaKTqJY1IFJyePIxsEbxa7zS6REheAqyVmgJFaGu3oQ5XlxfGr5Uq59tFTRkyx26G1HkHA== } + { integrity: sha512-kvwjL6OJFhAGWoYaIWR7HmILjiVk6xVj6QEU6qZMA7FtGgvlKi4pLfs8Of+hQqo+2TEhUoxG/5t6WqwB8uxjsw== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 + '@aws-sdk/types': 3.609.0 '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.2 - '@smithy/protocol-http': 4.0.2 - '@smithy/signature-v4': 3.1.1 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 + '@smithy/node-config-provider': 3.1.3 + '@smithy/protocol-http': 4.0.3 + '@smithy/signature-v4': 3.1.2 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 '@smithy/util-config-provider': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-signing@3.598.0: + /@aws-sdk/middleware-signing@3.609.0: resolution: - { integrity: sha512-XKb05DYx/aBPqz6iCapsCbIl8aD8EihTuPCs51p75QsVfbQoVr4TlFfIl5AooMSITzojdAQqxt021YtvxjtxIQ== } + { integrity: sha512-2w3dBLjQVKIajYzokO4hduq8/0hSMUYHHmIo1Kdl+MSY8uwRBt12bLL6pyreobTcRMxizvn2ph/CQ9I1ST/WGQ== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/protocol-http': 4.0.2 - '@smithy/signature-v4': 3.1.1 - '@smithy/types': 3.2.0 - '@smithy/util-middleware': 3.0.2 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.3 + '@smithy/protocol-http': 4.0.3 + '@smithy/signature-v4': 3.1.2 + '@smithy/types': 3.3.0 + '@smithy/util-middleware': 3.0.3 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-ssec@3.598.0: + /@aws-sdk/middleware-ssec@3.609.0: resolution: - { integrity: sha512-f0p2xP8IC1uJ5e/tND1l81QxRtRFywEdnbtKCE0H6RSn4UIt2W3Dohe1qQDbnh27okF0PkNW6BJGdSAz3p7qbA== } + { integrity: sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/middleware-user-agent@3.598.0: + /@aws-sdk/middleware-user-agent@3.609.0: resolution: - { integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg== } + { integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.609.0 + '@smithy/protocol-http': 4.0.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/region-config-resolver@3.598.0: + /@aws-sdk/region-config-resolver@3.609.0: resolution: - { integrity: sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw== } + { integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.2 - '@smithy/types': 3.2.0 + '@aws-sdk/types': 3.609.0 + '@smithy/node-config-provider': 3.1.3 + '@smithy/types': 3.3.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.2 - tslib: 2.6.3 - dev: true - - /@aws-sdk/signature-v4-multi-region@3.598.0: - resolution: - { integrity: sha512-1r/EyTrO1gSa1FirnR8V7mabr7gk+l+HkyTI0fcTSr8ucB7gmYyW6WjkY8JCz13VYHFK62usCEDS7yoJoJOzTA== } - engines: { node: '>=16.0.0' } - dependencies: - '@aws-sdk/middleware-sdk-s3': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/signature-v4': 3.1.1 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/util-middleware': 3.0.3 + tslib: 2.6.2 dev: true - /@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0): + /@aws-sdk/signature-v4-multi-region@3.609.0: resolution: - { integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA== } + { integrity: sha512-FJs0BxVMyYOKNu7nzFI1kehfgWoYmdto5B8BSS29geUACF7jlOoeCfNZWVrnMjvAxVlSQ5O7Mr575932BnsycA== } engines: { node: '>=16.0.0' } - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.598.0 dependencies: - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/middleware-sdk-s3': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@smithy/protocol-http': 4.0.3 + '@smithy/signature-v4': 3.1.2 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.606.0): + /@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0): resolution: - { integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA== } + { integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ== } engines: { node: '>=16.0.0' } peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.598.0 + '@aws-sdk/client-sso-oidc': ^3.609.0 dependencies: - '@aws-sdk/client-sso-oidc': 3.606.0(@aws-sdk/client-sts@3.606.0) - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/types@3.598.0: + /@aws-sdk/types@3.609.0: resolution: - { integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ== } + { integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true /@aws-sdk/util-arn-parser@3.568.0: @@ -1388,18 +1201,18 @@ packages: { integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w== } engines: { node: '>=16.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@aws-sdk/util-endpoints@3.598.0: + /@aws-sdk/util-endpoints@3.609.0: resolution: - { integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ== } + { integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ== } engines: { node: '>=16.0.0' } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.2.0 - '@smithy/util-endpoints': 2.0.3 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/types': 3.3.0 + '@smithy/util-endpoints': 2.0.4 + tslib: 2.6.2 dev: true /@aws-sdk/util-locate-window@3.465.0: @@ -1407,22 +1220,22 @@ packages: { integrity: sha512-f+QNcWGswredzC1ExNAB/QzODlxwaTdXkNT5cvke2RLX8SFU5pYk6h4uCtWC0vWPELzOfMfloBrJefBzlarhsw== } engines: { node: '>=14.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@aws-sdk/util-user-agent-browser@3.598.0: + /@aws-sdk/util-user-agent-browser@3.609.0: resolution: - { integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw== } + { integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA== } dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.2.0 + '@aws-sdk/types': 3.609.0 + '@smithy/types': 3.3.0 bowser: 2.11.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@aws-sdk/util-user-agent-node@3.598.0: + /@aws-sdk/util-user-agent-node@3.609.0: resolution: - { integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A== } + { integrity: sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg== } engines: { node: '>=16.0.0' } peerDependencies: aws-crt: '>=1.0.0' @@ -1430,19 +1243,19 @@ packages: aws-crt: optional: true dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@aws-sdk/types': 3.609.0 + '@smithy/node-config-provider': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@aws-sdk/xml-builder@3.598.0: + /@aws-sdk/xml-builder@3.609.0: resolution: - { integrity: sha512-ZIa2RK7CHFTZ4gwK77WRtsZ6vF7xwRXxJ8KQIxK2duhoTVcn0xYxpFLdW9WZZZvdP9GIF3Loqvf8DRdeU5Jc7Q== } + { integrity: sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true /@babel/code-frame@7.24.6: @@ -1482,7 +1295,7 @@ packages: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -1593,7 +1406,7 @@ packages: '@babel/core': 7.24.7 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) lodash.debounce: 4.0.8 resolve: 1.22.6 transitivePeerDependencies: @@ -2876,7 +2689,7 @@ packages: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -2952,12 +2765,12 @@ packages: statuses: 2.0.1 dev: true - /@changesets/apply-release-plan@7.0.3: + /@changesets/apply-release-plan@7.0.4: resolution: - { integrity: sha512-klL6LCdmfbEe9oyfLxnidIf/stFXmrbFO/3gT5LU5pcyoZytzJe4gWpTBx3BPmyNPl16dZ1xrkcW7b98e3tYkA== } + { integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A== } dependencies: '@babel/runtime': 7.23.1 - '@changesets/config': 3.0.1 + '@changesets/config': 3.0.2 '@changesets/get-version-range-type': 0.4.0 '@changesets/git': 3.0.0 '@changesets/should-skip-package': 0.1.0 @@ -2972,13 +2785,13 @@ packages: semver: 7.6.2 dev: true - /@changesets/assemble-release-plan@6.0.2: + /@changesets/assemble-release-plan@6.0.3: resolution: - { integrity: sha512-n9/Tdq+ze+iUtjmq0mZO3pEhJTKkku9hUxtUadW30jlN7kONqJG3O6ALeXrmc6gsi/nvoCuKjqEJ68Hk8RbMTQ== } + { integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw== } dependencies: '@babel/runtime': 7.23.1 '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.0 + '@changesets/get-dependents-graph': 2.1.1 '@changesets/should-skip-package': 0.1.0 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 @@ -3003,19 +2816,19 @@ packages: - encoding dev: true - /@changesets/cli@2.27.6: + /@changesets/cli@2.27.7: resolution: - { integrity: sha512-PB7KS5JkCQ4WSXlnfThn8CXAHVwYxFdZvYTimhi12fls/tzj9iimUhKsYwkrKSbw1AiVlGCZtihj5Wkt6siIjA== } + { integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A== } hasBin: true dependencies: '@babel/runtime': 7.23.1 - '@changesets/apply-release-plan': 7.0.3 - '@changesets/assemble-release-plan': 6.0.2 + '@changesets/apply-release-plan': 7.0.4 + '@changesets/assemble-release-plan': 6.0.3 '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.1 + '@changesets/config': 3.0.2 '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.0 - '@changesets/get-release-plan': 4.0.2 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/get-release-plan': 4.0.3 '@changesets/git': 3.0.0 '@changesets/logger': 0.1.0 '@changesets/pre': 2.0.0 @@ -3042,12 +2855,12 @@ packages: term-size: 2.2.1 dev: true - /@changesets/config@3.0.1: + /@changesets/config@3.0.2: resolution: - { integrity: sha512-nCr8pOemUjvGJ8aUu8TYVjqnUL+++bFOQHBVmtNbLvKzIDkN/uiP/Z4RKmr7NNaiujIURHySDEGFPftR4GbTUA== } + { integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw== } dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.0 + '@changesets/get-dependents-graph': 2.1.1 '@changesets/logger': 0.1.0 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 @@ -3062,9 +2875,9 @@ packages: extendable-error: 0.1.7 dev: true - /@changesets/get-dependents-graph@2.1.0: + /@changesets/get-dependents-graph@2.1.1: resolution: - { integrity: sha512-QOt6pQq9RVXKGHPVvyKimJDYJumx7p4DO5MO9AhRJYgAPgv0emhNqAqqysSVKHBm4sxKlGN4S1zXOIb5yCFuhQ== } + { integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA== } dependencies: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 @@ -3083,13 +2896,13 @@ packages: - encoding dev: true - /@changesets/get-release-plan@4.0.2: + /@changesets/get-release-plan@4.0.3: resolution: - { integrity: sha512-rOalz7nMuMV2vyeP7KBeAhqEB7FM2GFPO5RQSoOoUKKH9L6wW3QyPA2K+/rG9kBrWl2HckPVES73/AuwPvbH3w== } + { integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA== } dependencies: '@babel/runtime': 7.23.1 - '@changesets/assemble-release-plan': 6.0.2 - '@changesets/config': 3.0.1 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/config': 3.0.2 '@changesets/pre': 2.0.0 '@changesets/read': 0.6.0 '@changesets/types': 6.0.0 @@ -3184,11 +2997,6 @@ packages: prettier: 2.8.8 dev: true - /@cloudflare/workers-types@4.20240620.0: - resolution: - { integrity: sha512-CQD8YS6evRob7LChvIX3gE3zYo0KVgaLDOu1SwNP1BVIS2Sa0b+FC8S1e1hhrNN8/E4chYlVN+FDAgA4KRDUEQ== } - dev: false - /@cspotcode/source-map-support@0.8.1: resolution: { integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== } @@ -3934,7 +3742,7 @@ packages: engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -3946,7 +3754,7 @@ packages: engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } dependencies: ajv: 6.12.6 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) espree: 10.1.0 globals: 14.0.0 ignore: 5.3.1 @@ -3996,7 +3804,7 @@ packages: engines: { node: ^8.13.0 || >=10.10.0 } dependencies: '@grpc/proto-loader': 0.7.10 - '@types/node': 20.14.9 + '@types/node': 20.14.10 /@grpc/proto-loader@0.7.10: resolution: @@ -4059,12 +3867,12 @@ packages: { integrity: sha512-tWZNBIS1CoekcwlMuyG2mr0a1Wo5lb5lEHwwWvZo+5GLgr3e9LLDTtmgtCWEwBpXMkxn9D+2W9j2FY6eZQq0tA== } dev: false - /@inquirer/confirm@3.1.12: + /@inquirer/confirm@3.1.14: resolution: - { integrity: sha512-s5Sod79QsBBi5Qm7zxCq9DcAD0i7WRcjd/LzsiIAWqWZKW4+OJTGrCgVSLGIHTulwbZgdxM4AAxpCXe86hv4/Q== } + { integrity: sha512-nbLSX37b2dGPtKWL3rPuR/5hOuD30S+pqJ/MuFiUEgN6GiMs8UMxiurKAMDzKt6C95ltjupa8zH6+3csXNHWpA== } engines: { node: '>=18' } dependencies: - '@inquirer/core': 9.0.0 + '@inquirer/core': 9.0.2 '@inquirer/type': 1.4.0 /@inquirer/confirm@3.1.8: @@ -4084,7 +3892,7 @@ packages: '@inquirer/figures': 1.0.2 '@inquirer/type': 1.3.2 '@types/mute-stream': 0.0.4 - '@types/node': 20.14.9 + '@types/node': 20.14.10 '@types/wrap-ansi': 3.0.0 ansi-escapes: 4.3.2 chalk: 4.1.2 @@ -4096,35 +3904,15 @@ packages: wrap-ansi: 6.2.0 dev: true - /@inquirer/core@8.2.3: + /@inquirer/core@9.0.2: resolution: - { integrity: sha512-WrpDVPAaxJQjHid3Ra4FhUO70YBzkHSYVyW5X48L5zHYdudoPISJqTRRWSeamHfaXda7PNNaC5Py5MEo7QwBNA== } - engines: { node: '>=18' } - dependencies: - '@inquirer/figures': 1.0.3 - '@inquirer/type': 1.3.3 - '@types/mute-stream': 0.0.4 - '@types/node': 20.14.9 - '@types/wrap-ansi': 3.0.0 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-spinners: 2.9.2 - cli-width: 4.1.0 - mute-stream: 1.0.0 - signal-exit: 4.1.0 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - dev: true - - /@inquirer/core@9.0.0: - resolution: - { integrity: sha512-y3q+fkCTGmvwk9Wf6yZlI3QGlLXbEm5M7Y7Eh8abaUbv+ffvmw2aB4FxSUrWaoaozwvEJSG60raHbCaUorXEzA== } + { integrity: sha512-nguvH3TZar3ACwbytZrraRTzGqyxJfYJwv+ZwqZNatAosdWQMP1GV8zvmkNlBe2JeZSaw0WYBHZk52pDpWC9qA== } engines: { node: '>=18' } dependencies: '@inquirer/figures': 1.0.3 '@inquirer/type': 1.4.0 '@types/mute-stream': 0.0.4 - '@types/node': 20.14.9 + '@types/node': 20.14.10 '@types/wrap-ansi': 3.0.0 ansi-escapes: 4.3.2 cli-spinners: 2.9.2 @@ -4133,7 +3921,7 @@ packages: signal-exit: 4.1.0 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.1 + yoctocolors-cjs: 2.1.2 /@inquirer/figures@1.0.2: resolution: @@ -4146,25 +3934,25 @@ packages: { integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw== } engines: { node: '>=18' } - /@inquirer/input@2.1.10: + /@inquirer/input@2.2.1: resolution: - { integrity: sha512-KEnho7O0YBj+peA40ZGOuBYf00EQnYbQlPsORgZYdjdUVUrMqQPW3qIvRNJIq+lYlc9RZrfHeMoAv+tWAoZFQg== } + { integrity: sha512-Yl1G6h7qWydzrJwqN777geeJVaAFL5Ly83aZlw4xHf8Z/BoTMfKRheyuMaQwOG7LQ4e5nQP7PxXdEg4SzQ+OKw== } engines: { node: '>=18' } dependencies: - '@inquirer/core': 8.2.3 - '@inquirer/type': 1.3.3 + '@inquirer/core': 9.0.2 + '@inquirer/type': 1.4.0 dev: true - /@inquirer/select@2.3.6: + /@inquirer/select@2.3.10: resolution: - { integrity: sha512-eLqlZXre69Jenmar5s+3018xF3lpaGfxVZLHkCzkrhtuTuFjpYtb0YpiYeZNKZm9pa+ih3s9acN/zRt+dDh+qA== } + { integrity: sha512-rr7iR0Zj1YFfgM8IUGimPD9Yukd+n/U63CnYT9kdum6DbRXtMxR45rrreP+EA9ixCnShr+W4xj7suRxC1+8t9g== } engines: { node: '>=18' } dependencies: - '@inquirer/core': 8.2.3 + '@inquirer/core': 9.0.2 '@inquirer/figures': 1.0.3 - '@inquirer/type': 1.3.3 + '@inquirer/type': 1.4.0 ansi-escapes: 4.3.2 - chalk: 4.1.2 + yoctocolors-cjs: 2.1.2 dev: true /@inquirer/type@1.3.2: @@ -4173,12 +3961,6 @@ packages: engines: { node: '>=18' } dev: true - /@inquirer/type@1.3.3: - resolution: - { integrity: sha512-xTUt0NulylX27/zMx04ZYar/kr1raaiFTVvQ5feljQsiAgdm0WPj4S73/ye0fbslh+15QrIuDvfCXTek7pMY5A== } - engines: { node: '>=18' } - dev: true - /@inquirer/type@1.4.0: resolution: { integrity: sha512-AjOqykVyjdJQvtfkNDGUyMYGF8xN50VUxftCQWsOyIo4DFRLr6VQhW0VItGI1JIyQGCGgIpKa7hMMwNhZb4OIw== } @@ -4214,7 +3996,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.14.9 + '@types/node': 20.14.10 '@types/yargs': 16.0.6 chalk: 4.1.2 dev: false @@ -4321,7 +4103,7 @@ packages: { integrity: sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw== } dev: false - /@netlify/build@29.20.6(@types/node@20.14.9): + /@netlify/build@29.20.6(@types/node@20.14.10): resolution: { integrity: sha512-AynL2Sn1bKMYzB4e05CsDObQYDVSN11f1rJCO/41iafmxhXXQWIYR3Q7qZeK30C4uHSk4WjhD/K18PBgQDsjhw== } engines: { node: ^14.16.0 || >=16.0.0 } @@ -4378,8 +4160,8 @@ packages: strip-ansi: 7.1.0 supports-color: 9.4.0 terminal-link: 3.0.0 - ts-node: 10.9.2(@types/node@20.14.9)(typescript@5.5.2) - typescript: 5.5.2 + ts-node: 10.9.2(@types/node@20.14.10)(typescript@5.5.3) + typescript: 5.5.3 uuid: 9.0.1 yargs: 17.7.2 transitivePeerDependencies: @@ -4892,7 +4674,7 @@ packages: clean-stack: 3.0.1 cli-progress: 3.12.0 color: 4.2.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.4(supports-color@8.1.1) ejs: 3.1.10 get-package-type: 0.1.0 globby: 11.1.0 @@ -4914,9 +4696,9 @@ packages: wrap-ansi: 7.0.0 dev: true - /@oclif/core@4.0.7: + /@oclif/core@4.0.8: resolution: - { integrity: sha512-sU4Dx+RXCWAkrMw8tQFYAL6VfcHYKLPxVC9iKfgTXr4aDhcCssDwrbgpx0Di1dnNxvQlDGUhuCEInZuIY/nNfw== } + { integrity: sha512-9AzNoRlKfIeuqOin+HK9cyouELeup7sX+MGIFc5dR+bnG0sSzFnV1A/Z57E7KWrY5NdWULHYT5NhiL1YpEhG2w== } engines: { node: '>=18.0.0' } dependencies: ansi-escapes: 4.3.2 @@ -4930,38 +4712,38 @@ packages: indent-string: 4.0.0 is-wsl: 2.2.0 lilconfig: 3.1.2 - minimatch: 9.0.4 + minimatch: 9.0.5 string-width: 4.2.3 supports-color: 8.1.1 widest-line: 3.1.0 wordwrap: 1.0.0 wrap-ansi: 7.0.0 - /@oclif/plugin-help@6.2.4: + /@oclif/plugin-help@6.2.5: resolution: - { integrity: sha512-0UrLxHseBuwh3JVRluJFe1eT1OT5dBvbwcbAYVO8uNi/ctmaWCAw4j737l8BFnf01GpDsloSAWyEX4dtG0P1zg== } + { integrity: sha512-/NgP6j5THCWDxQj3Mba+IIidf8fBtOT5Wh6ygb2WdWLSxcsRXSQUiJKKOXu8e/N5+KQeuG2Yko2hFxd2cZUzMQ== } engines: { node: '>=18.0.0' } dependencies: - '@oclif/core': 4.0.7 + '@oclif/core': 4.0.8 - /@oclif/plugin-not-found@3.2.8: + /@oclif/plugin-not-found@3.2.10: resolution: - { integrity: sha512-C2Iu/oZRNnGJct5DgB3Tg4DowNxpLugLb4hd/PQ18/UxQ/wz9mo0OYY6aFncn7If4gCsKiRUKSQA+3XXxMQi9w== } + { integrity: sha512-Bevp3hcv1IhNgljugIhxL5ARcwxsQmiR9yGOozURuZBX3IjsHBPhI2I92wKA2KM5zRgh4zOm6gvoP8gcHlhLJA== } engines: { node: '>=18.0.0' } dependencies: - '@inquirer/confirm': 3.1.12 - '@oclif/core': 4.0.7 + '@inquirer/confirm': 3.1.14 + '@oclif/core': 4.0.8 ansis: 3.2.0 fast-levenshtein: 3.0.0 - /@oclif/plugin-plugins@5.3.3: + /@oclif/plugin-plugins@5.3.4: resolution: - { integrity: sha512-W071oAdmdMpHwI1q9kGcuKijP6zwuZXi2rQvzEGivjwg/Uj51B1RNKvOYy/O7pd0lCZu+1s6LAZmb8wCwii6yQ== } + { integrity: sha512-gbLe+rfqP3dlphqOisFvbZ+adjobvIEhc78ferl3wFL4EazkIrcqrHYle77EjsaEiATqtCIeh3Ef41QCGoK9pA== } engines: { node: '>=18.0.0' } dependencies: - '@oclif/core': 4.0.7 + '@oclif/core': 4.0.8 ansis: 3.2.0 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) npm: 10.8.1 npm-package-arg: 11.0.2 npm-run-path: 5.3.0 @@ -4981,7 +4763,7 @@ packages: dependencies: '@oclif/core': 3.26.6 chalk: 5.3.0 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) http-call: 5.3.0 lodash: 4.17.21 transitivePeerDependencies: @@ -5756,6 +5538,27 @@ packages: strip-bom: 4.0.0 dev: false + /@pnpm/read-project-manifest@6.0.4: + resolution: + { integrity: sha512-yxfJQayRXlmcs7eQJkNVz4yuZw6x3lYAoODeRCI0S0Ez7G2ql+zoOGeigIe2UxI2B8xN3WyDgZ4G2CqV7t5cBw== } + engines: { node: '>=18.12' } + dependencies: + '@gwhitney/detect-indent': 7.0.1 + '@pnpm/error': 6.0.1 + '@pnpm/graceful-fs': 4.0.0 + '@pnpm/text.comments-parser': 3.0.0 + '@pnpm/types': 11.0.0 + '@pnpm/write-project-manifest': 6.0.3 + fast-deep-equal: 3.1.3 + is-windows: 1.0.2 + json5: 2.2.3 + lodash.clonedeep: 4.5.0 + parse-json: 5.2.0 + read-yaml-file: 2.1.0 + sort-keys: 4.2.0 + strip-bom: 4.0.0 + dev: false + /@pnpm/text.comments-parser@3.0.0: resolution: { integrity: sha512-BSGvYd59kPKVTUk1InekEp+TiPnJ8650/bQyiOUFSvqHi61YipcR+E4H2i3xTnk2e+GHdGbXvEtAZbQmyxb0/g== } @@ -5770,6 +5573,12 @@ packages: engines: { node: '>=18.12' } dev: false + /@pnpm/types@11.0.0: + resolution: + { integrity: sha512-BSdk9nlYLHHHLrTFNpmdrXrXVc+1sY/E1Fs1zqR8pY/KjpjVhxkruLZuXitPRPxbk4jSqm7UnG5WCz008iiaig== } + engines: { node: '>=18.12' } + dev: false + /@pnpm/write-project-manifest@6.0.2: resolution: { integrity: sha512-CxPzkjckRFnZx2SbZs6dzLH0ayjXwBHJjvbZ+Wks935J+e6f/JH8IQLhOBy/AT/C6iGKdoqOxuTnEnSvThb05w== } @@ -5782,6 +5591,18 @@ packages: write-yaml-file: 5.0.0 dev: false + /@pnpm/write-project-manifest@6.0.3: + resolution: + { integrity: sha512-0KLOjeLlBBgFa8c1NAI00l/UIyyhMnc505g1btWzhKAsCa9847uYitAT4FQETJVQzWroP7XaFu72OKbkxWBxBg== } + engines: { node: '>=18.12' } + dependencies: + '@pnpm/text.comments-parser': 3.0.0 + '@pnpm/types': 11.0.0 + json5: 2.2.3 + write-file-atomic: 5.0.1 + write-yaml-file: 5.0.0 + dev: false + /@protobufjs/aspromise@1.1.2: resolution: { integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== } @@ -6060,13 +5881,13 @@ packages: size-limit: 11.1.4 dev: true - /@smithy/abort-controller@3.1.0: + /@smithy/abort-controller@3.1.1: resolution: - { integrity: sha512-XOm4LkuC0PsK1sf2bBJLIlskn5ghmVxiEBVlo/jg0R8hxASBKYYgOoJEhKWgOr4vWGkN+5rC+oyBAqHYtxjnwQ== } + { integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true /@smithy/chunked-blob-reader-native@3.0.0: @@ -6074,152 +5895,152 @@ packages: { integrity: sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg== } dependencies: '@smithy/util-base64': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/chunked-blob-reader@3.0.0: resolution: { integrity: sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA== } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/config-resolver@3.0.3: + /@smithy/config-resolver@3.0.4: resolution: - { integrity: sha512-4wHqCMkdfVDP4qmr4fVPYOFOH+vKhOv3X4e6KEU9wIC8xXUQ24tnF4CW+sddGDX1zU86GGyQ7A+rg2xmUD6jpQ== } + { integrity: sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/node-config-provider': 3.1.2 - '@smithy/types': 3.2.0 + '@smithy/node-config-provider': 3.1.3 + '@smithy/types': 3.3.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.2 - tslib: 2.6.3 + '@smithy/util-middleware': 3.0.3 + tslib: 2.6.2 dev: true - /@smithy/core@2.2.3: + /@smithy/core@2.2.4: resolution: - { integrity: sha512-SpyLOL2vgE6sUYM6nQfu82OirCPkCDKctyG3aMgjMlDPTJpUlmlNH0ttu9ZWwzEjrzzr8uABmPjJTRI7gk1HFQ== } + { integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.2 - '@smithy/protocol-http': 4.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/util-middleware': 3.0.2 - tslib: 2.6.3 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.7 + '@smithy/middleware-serde': 3.0.3 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + '@smithy/util-middleware': 3.0.3 + tslib: 2.6.2 dev: true - /@smithy/credential-provider-imds@3.1.2: + /@smithy/credential-provider-imds@3.1.3: resolution: - { integrity: sha512-gqVmUaNoeqyrOAjgZg+rTmFLsphh/vS59LCMdFfVpthVS0jbfBzvBmEPktBd+y9ME4DYMGHFAMSYJDK8q0noOQ== } + { integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/node-config-provider': 3.1.2 - '@smithy/property-provider': 3.1.2 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 - tslib: 2.6.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/property-provider': 3.1.3 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + tslib: 2.6.2 dev: true - /@smithy/eventstream-codec@3.1.1: + /@smithy/eventstream-codec@3.1.2: resolution: - { integrity: sha512-s29NxV/ng1KXn6wPQ4qzJuQDjEtxLdS0+g5PQFirIeIZrp66FXVJ5IpZRowbt/42zB5dY8TqJ0G0L9KkgtsEZg== } + { integrity: sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw== } dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 3.2.0 + '@smithy/types': 3.3.0 '@smithy/util-hex-encoding': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/eventstream-serde-browser@3.0.3: + /@smithy/eventstream-serde-browser@3.0.4: resolution: - { integrity: sha512-ZXKmNAHl6SWKYuVmtoEc/hBQ7Nym/rbAx2SrqoJHn0i9QopIP7fG1AWmoFIeS5R3/VL6AwUIZMR0g8qnjjVRRA== } + { integrity: sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/eventstream-serde-universal': 3.0.3 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/eventstream-serde-universal': 3.0.4 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/eventstream-serde-config-resolver@3.0.2: + /@smithy/eventstream-serde-config-resolver@3.0.3: resolution: - { integrity: sha512-QbE3asvvBUZr7PwbOaxkSfKDjTAmWZkqh2G7pkYlD4jRkT1Y9nufeyu0OBPlLoF4+gl3YMpSVO7TESe8bVkD+g== } + { integrity: sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/eventstream-serde-node@3.0.3: + /@smithy/eventstream-serde-node@3.0.4: resolution: - { integrity: sha512-v61Ftn7x/ubWFqH7GHFAL/RaU7QZImTbuV95DYugYYItzpO7KaHYEuO8EskCaBpZEfzOxhUGKm4teS9YUSt69Q== } + { integrity: sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/eventstream-serde-universal': 3.0.3 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/eventstream-serde-universal': 3.0.4 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/eventstream-serde-universal@3.0.3: + /@smithy/eventstream-serde-universal@3.0.4: resolution: - { integrity: sha512-YXYt3Cjhu9tRrahbTec2uOjwOSeCNfQurcWPGNEUspBhqHoA3KrDrVj+jGbCLWvwkwhzqDnnaeHAxm+IxAjOAQ== } + { integrity: sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/eventstream-codec': 3.1.1 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/eventstream-codec': 3.1.2 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/fetch-http-handler@3.1.0: + /@smithy/fetch-http-handler@3.2.0: resolution: - { integrity: sha512-s7oQjEOUH9TYjctpITtWF4qxOdg7pBrP9eigEQ8SBsxF3dRFV0S28pGMllC83DUr7ECmErhO/BUwnULfoNhKgQ== } + { integrity: sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg== } dependencies: - '@smithy/protocol-http': 4.0.2 - '@smithy/querystring-builder': 3.0.2 - '@smithy/types': 3.2.0 + '@smithy/protocol-http': 4.0.3 + '@smithy/querystring-builder': 3.0.3 + '@smithy/types': 3.3.0 '@smithy/util-base64': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/hash-blob-browser@3.1.1: + /@smithy/hash-blob-browser@3.1.2: resolution: - { integrity: sha512-8RwdPG7arvL5pfMAFsH6jfBVcC7MDR1LYHjKevZPHREkVtORIQkRfm2K8px7giJt7x0zzQJnWamrsDM4ig8nTQ== } + { integrity: sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg== } dependencies: '@smithy/chunked-blob-reader': 3.0.0 '@smithy/chunked-blob-reader-native': 3.0.0 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/hash-node@3.0.2: + /@smithy/hash-node@3.0.3: resolution: - { integrity: sha512-43uGA6o6QJQdXwAogybdTDHDd3SCdKyoiHIHb8PpdE2rKmVicjG9b1UgVwdgO8QPytmVqHFaUw27M3LZKwu8Yg== } + { integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 + '@smithy/types': 3.3.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/hash-stream-node@3.1.1: + /@smithy/hash-stream-node@3.1.2: resolution: - { integrity: sha512-+uvJHPrFNE9crkh3INVS9FmDcx1DoywDgIzlRWlPy7gqoD8jG14os9ATIFY7wN/ARPz1EWlkCHUap70oXxMmjA== } + { integrity: sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 + '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/invalid-dependency@3.0.2: + /@smithy/invalid-dependency@3.0.3: resolution: - { integrity: sha512-+BAY3fMhomtq470tswXyrdVBSUhiLuhBVT+rOmpbz5e04YX+s1dX4NxTLzZGwBjCpeWZNtTxP8zbIvvFk81gUg== } + { integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw== } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true /@smithy/is-array-buffer@2.2.0: @@ -6227,7 +6048,7 @@ packages: { integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== } engines: { node: '>=14.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/is-array-buffer@3.0.0: @@ -6235,195 +6056,195 @@ packages: { integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ== } engines: { node: '>=16.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/md5-js@3.0.2: + /@smithy/md5-js@3.0.3: resolution: - { integrity: sha512-WlSK9br7fkVucTkCXporwuOttCR3cJ1GV70J8ENYXGNc0nUTPzMdWCyHztgnbbKoekVMjGZOEu+8I52nOdzqwQ== } + { integrity: sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q== } dependencies: - '@smithy/types': 3.2.0 + '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/middleware-content-length@3.0.2: + /@smithy/middleware-content-length@3.0.3: resolution: - { integrity: sha512-/Havz3PkYIEmwpqkyRTR21yJsWnFbD1ec4H1pUL+TkDnE7RCQkAVUQepLL/UeCaZeCBXvfdoKbOjSbV01xIinQ== } + { integrity: sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/protocol-http': 4.0.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/protocol-http': 4.0.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/middleware-endpoint@3.0.3: + /@smithy/middleware-endpoint@3.0.4: resolution: - { integrity: sha512-ARAXHodhj4tttKa9y75zvENdSoHq6VGsSi7XS3+yLutrnxttJs6N10UMInCC1yi3/bopT8xug3iOP/y9R6sKJQ== } + { integrity: sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/middleware-serde': 3.0.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - '@smithy/url-parser': 3.0.2 - '@smithy/util-middleware': 3.0.2 - tslib: 2.6.3 + '@smithy/middleware-serde': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.3 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-middleware': 3.0.3 + tslib: 2.6.2 dev: true - /@smithy/middleware-retry@3.0.6: + /@smithy/middleware-retry@3.0.7: resolution: - { integrity: sha512-ICsFKp8eAyIMmxN5UT3IU37S6886L879TKtgxPsn/VD/laYNwqTLmJaCAn5//+2fRIrV0dnHp6LFlMwdXlWoUQ== } + { integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/node-config-provider': 3.1.2 - '@smithy/protocol-http': 4.0.2 - '@smithy/service-error-classification': 3.0.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - '@smithy/util-middleware': 3.0.2 - '@smithy/util-retry': 3.0.2 - tslib: 2.6.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/protocol-http': 4.0.3 + '@smithy/service-error-classification': 3.0.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 + tslib: 2.6.2 uuid: 9.0.1 dev: true - /@smithy/middleware-serde@3.0.2: + /@smithy/middleware-serde@3.0.3: resolution: - { integrity: sha512-oT2abV5zLhBucJe1LIIFEcRgIBDbZpziuMPswTMbBQNcaEUycLFvX63zsFmqfwG+/ZQKsNx+BSE8W51CMuK7Yw== } + { integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/middleware-stack@3.0.2: + /@smithy/middleware-stack@3.0.3: resolution: - { integrity: sha512-6fRcxomlNKBPIy/YjcnC7YHpMAjRvGUYlYVJAfELqZjkW0vQegNcImjY7T1HgYA6u3pAcCxKVBLYnkTw8z/l0A== } + { integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/node-config-provider@3.1.2: + /@smithy/node-config-provider@3.1.3: resolution: - { integrity: sha512-388fEAa7+6ORj/BDC70peg3fyFBTTXJyXfXJ0Bwd6FYsRltePr2oGzIcm5AuC1WUSLtZ/dF+hYOnfTMs04rLvA== } + { integrity: sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/property-provider': 3.1.2 - '@smithy/shared-ini-file-loader': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/node-http-handler@3.1.0: + /@smithy/node-http-handler@3.1.1: resolution: - { integrity: sha512-pOpgB6B+VLXLwAyyvRz+ZAVXABlbAsJ2xvn3WZvrppAPImxwQOPFbeSUzWYMhpC8Tr7yQ3R8fG990QDhskkf1Q== } + { integrity: sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/abort-controller': 3.1.0 - '@smithy/protocol-http': 4.0.2 - '@smithy/querystring-builder': 3.0.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/abort-controller': 3.1.1 + '@smithy/protocol-http': 4.0.3 + '@smithy/querystring-builder': 3.0.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/property-provider@3.1.2: + /@smithy/property-provider@3.1.3: resolution: - { integrity: sha512-Hzp32BpeFFexBpO1z+ts8okbq/VLzJBadxanJAo/Wf2CmvXMBp6Q/TLWr7Js6IbMEcr0pDZ02V3u1XZkuQUJaA== } + { integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/protocol-http@4.0.2: + /@smithy/protocol-http@4.0.3: resolution: - { integrity: sha512-X/90xNWIOqSR2tLUyWxVIBdatpm35DrL44rI/xoeBWUuanE0iyCXJpTcnqlOpnEzgcu0xCKE06+g70TTu2j7RQ== } + { integrity: sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/querystring-builder@3.0.2: + /@smithy/querystring-builder@3.0.3: resolution: - { integrity: sha512-xhv1+HacDYsOLdNt7zW+8Fe779KYAzmWvzs9bC5NlKM8QGYCwwuFwDBynhlU4D5twgi2pZ14Lm4h6RiAazCtmA== } + { integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 + '@smithy/types': 3.3.0 '@smithy/util-uri-escape': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/querystring-parser@3.0.2: + /@smithy/querystring-parser@3.0.3: resolution: - { integrity: sha512-C5hyRKgrZGPNh5QqIWzXnW+LXVrPmVQO0iJKjHeb5v3C61ZkP9QhrKmbfchcTyg/VnaE0tMNf/nmLpQlWuiqpg== } + { integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/service-error-classification@3.0.2: + /@smithy/service-error-classification@3.0.3: resolution: - { integrity: sha512-cu0WV2XRttItsuXlcM0kq5MKdphbMMmSd2CXF122dJ75NrFE0o7rruXFGfxAp3BKzgF/DMxX+PllIA/cj4FHMw== } + { integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 + '@smithy/types': 3.3.0 dev: true - /@smithy/shared-ini-file-loader@3.1.2: + /@smithy/shared-ini-file-loader@3.1.3: resolution: - { integrity: sha512-tgnXrXbLMO8vo6VeuqabMw/eTzQHlLmZx0TC0TjtjJghnD0Xl4pEnJtBjTJr6XF5fHMNrt5BcczDXHJT9yNQnA== } + { integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/signature-v4@3.1.1: + /@smithy/signature-v4@3.1.2: resolution: - { integrity: sha512-2/vlG86Sr489XX8TA/F+VDA+P04ESef04pSz0wRtlQBExcSPjqO08rvrkcas2zLnJ51i+7ukOURCkgqixBYjSQ== } + { integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA== } engines: { node: '>=16.0.0' } dependencies: '@smithy/is-array-buffer': 3.0.0 - '@smithy/types': 3.2.0 + '@smithy/types': 3.3.0 '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.2 + '@smithy/util-middleware': 3.0.3 '@smithy/util-uri-escape': 3.0.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/smithy-client@3.1.4: + /@smithy/smithy-client@3.1.5: resolution: - { integrity: sha512-y6xJROGrIoitjpwXLY7P9luDHvuT9jWpAluliuSFdBymFxcl6iyQjo9U/JhYfRHFNTruqsvKOrOESVuPGEcRmQ== } + { integrity: sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/middleware-endpoint': 3.0.3 - '@smithy/middleware-stack': 3.0.2 - '@smithy/protocol-http': 4.0.2 - '@smithy/types': 3.2.0 - '@smithy/util-stream': 3.0.4 - tslib: 2.6.3 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-stack': 3.0.3 + '@smithy/protocol-http': 4.0.3 + '@smithy/types': 3.3.0 + '@smithy/util-stream': 3.0.5 + tslib: 2.6.2 dev: true - /@smithy/types@3.2.0: + /@smithy/types@3.3.0: resolution: - { integrity: sha512-cKyeKAPazZRVqm7QPvcPD2jEIt2wqDPAL1KJKb0f/5I7uhollvsWZuZKLclmyP6a+Jwmr3OV3t+X0pZUUHS9BA== } + { integrity: sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA== } engines: { node: '>=16.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/url-parser@3.0.2: + /@smithy/url-parser@3.0.3: resolution: - { integrity: sha512-pRiPHrgibeAr4avtXDoBHmTLtthwA4l8jKYRfZjNgp+bBPyxDMPRg2TMJaYxqbKemvrOkHu9MIBTv2RkdNfD6w== } + { integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A== } dependencies: - '@smithy/querystring-parser': 3.0.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/querystring-parser': 3.0.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true /@smithy/util-base64@3.0.0: @@ -6433,14 +6254,14 @@ packages: dependencies: '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/util-body-length-browser@3.0.0: resolution: { integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ== } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/util-body-length-node@3.0.0: @@ -6448,7 +6269,7 @@ packages: { integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA== } engines: { node: '>=16.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/util-buffer-from@2.2.0: @@ -6457,7 +6278,7 @@ packages: engines: { node: '>=14.0.0' } dependencies: '@smithy/is-array-buffer': 2.2.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/util-buffer-from@3.0.0: @@ -6466,7 +6287,7 @@ packages: engines: { node: '>=16.0.0' } dependencies: '@smithy/is-array-buffer': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/util-config-provider@3.0.0: @@ -6474,43 +6295,43 @@ packages: { integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ== } engines: { node: '>=16.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/util-defaults-mode-browser@3.0.6: + /@smithy/util-defaults-mode-browser@3.0.7: resolution: - { integrity: sha512-tAgoc++Eq+KL7g55+k108pn7nAob3GLWNEMbXhZIQyBcBNaE/o3+r4AEbae0A8bWvLRvArVsjeiuhMykGa04/A== } + { integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA== } engines: { node: '>= 10.0.0' } dependencies: - '@smithy/property-provider': 3.1.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 + '@smithy/property-provider': 3.1.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 bowser: 2.11.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/util-defaults-mode-node@3.0.6: + /@smithy/util-defaults-mode-node@3.0.7: resolution: - { integrity: sha512-UNerul6/E8aiCyFTBHk+RSIZCo7m96d/N5K3FeO/wFeZP6oy5HAicLzxqa85Wjv7MkXSxSySX29L/LwTV/QMag== } + { integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg== } engines: { node: '>= 10.0.0' } dependencies: - '@smithy/config-resolver': 3.0.3 - '@smithy/credential-provider-imds': 3.1.2 - '@smithy/node-config-provider': 3.1.2 - '@smithy/property-provider': 3.1.2 - '@smithy/smithy-client': 3.1.4 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/config-resolver': 3.0.4 + '@smithy/credential-provider-imds': 3.1.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/property-provider': 3.1.3 + '@smithy/smithy-client': 3.1.5 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/util-endpoints@2.0.3: + /@smithy/util-endpoints@2.0.4: resolution: - { integrity: sha512-Dyi+pfLglDHSGsKSYunuUUSFM5V0tz7UDgv1Ex97yg+Xkn0Eb0rH0rcvl1n0MaJ11fac3HKDOH0DkALyQYCQag== } + { integrity: sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/node-config-provider': 3.1.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true /@smithy/util-hex-encoding@3.0.0: @@ -6518,41 +6339,41 @@ packages: { integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ== } engines: { node: '>=16.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/util-middleware@3.0.2: + /@smithy/util-middleware@3.0.3: resolution: - { integrity: sha512-7WW5SD0XVrpfqljBYzS5rLR+EiDzl7wCVJZ9Lo6ChNFV4VYDk37Z1QI5w/LnYtU/QKnSawYoHRd7VjSyC8QRQQ== } + { integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/util-retry@3.0.2: + /@smithy/util-retry@3.0.3: resolution: - { integrity: sha512-HUVOb1k8p/IH6WFUjsLa+L9H1Zi/FAAB2CDOpWuffI1b2Txi6sknau8kNfC46Xrt39P1j2KDzCE1UlLa2eW5+A== } + { integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/service-error-classification': 3.0.2 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/service-error-classification': 3.0.3 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true - /@smithy/util-stream@3.0.4: + /@smithy/util-stream@3.0.5: resolution: - { integrity: sha512-CcMioiaOOsEVdb09pS7ux1ij7QcQ2jE/cE1+iin1DXMeRgAEQN/47m7Xztu7KFQuQsj0A5YwB2UN45q97CqKCg== } + { integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/fetch-http-handler': 3.1.0 - '@smithy/node-http-handler': 3.1.0 - '@smithy/types': 3.2.0 + '@smithy/fetch-http-handler': 3.2.0 + '@smithy/node-http-handler': 3.1.1 + '@smithy/types': 3.3.0 '@smithy/util-base64': 3.0.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-hex-encoding': 3.0.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/util-uri-escape@3.0.0: @@ -6560,7 +6381,7 @@ packages: { integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg== } engines: { node: '>=16.0.0' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/util-utf8@2.3.0: @@ -6569,7 +6390,7 @@ packages: engines: { node: '>=14.0.0' } dependencies: '@smithy/util-buffer-from': 2.2.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@smithy/util-utf8@3.0.0: @@ -6578,17 +6399,17 @@ packages: engines: { node: '>=16.0.0' } dependencies: '@smithy/util-buffer-from': 3.0.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true - /@smithy/util-waiter@3.1.1: + /@smithy/util-waiter@3.1.2: resolution: - { integrity: sha512-xT+Bbpe5sSrC7cCWSElOreDdWzqovR1V+7xrp+fmwGAA+TPYBb78iasaXjO1pa+65sY6JjW5GtGeIoJwCK9B1g== } + { integrity: sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw== } engines: { node: '>=16.0.0' } dependencies: - '@smithy/abort-controller': 3.1.0 - '@smithy/types': 3.2.0 - tslib: 2.6.3 + '@smithy/abort-controller': 3.1.1 + '@smithy/types': 3.3.0 + tslib: 2.6.2 dev: true /@swc/core-darwin-arm64@1.3.89: @@ -6744,7 +6565,7 @@ packages: { integrity: sha512-T0HO+VrU9VbLRiEx/kH4+gwGMHNMIGkp0Pok+p0I33saOOLyhfGvwOKQgvt2qkxzQEV2L5MtGB8EnW4r5d3CqQ== } dependencies: '@textlint/ast-node-types': 12.6.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) mdast-util-gfm-autolink-literal: 0.1.3 remark-footnotes: 3.0.0 remark-frontmatter: 3.0.0 @@ -6818,7 +6639,7 @@ packages: resolution: { integrity: sha512-D4PbNRbviKyppS5ivBGyFO29POlySLmA2HyUFE4p5QGazAMM3CwkKWcvTl8gvElSuxRh6FPKL8XmidX873ou4g== } dependencies: - '@types/node': 20.14.9 + '@types/node': 20.14.10 dev: true /@types/cookie@0.6.0: @@ -6927,16 +6748,16 @@ packages: resolution: { integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow== } dependencies: - '@types/node': 20.14.9 + '@types/node': 20.14.10 /@types/node@12.20.55: resolution: { integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== } dev: true - /@types/node@20.14.9: + /@types/node@20.14.10: resolution: - { integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== } + { integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ== } dependencies: undici-types: 5.26.5 @@ -6944,18 +6765,23 @@ packages: resolution: { integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A== } + /@types/pako@2.0.3: + resolution: + { integrity: sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q== } + dev: true + /@types/papaparse@5.3.14: resolution: { integrity: sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g== } dependencies: - '@types/node': 20.14.9 + '@types/node': 20.14.10 dev: true /@types/pg@8.11.6: resolution: { integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ== } dependencies: - '@types/node': 20.14.9 + '@types/node': 20.14.10 pg-protocol: 1.6.1 pg-types: 4.0.2 dev: true @@ -6974,7 +6800,7 @@ packages: resolution: { integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA== } dependencies: - '@types/node': 20.14.9 + '@types/node': 20.14.10 kleur: 3.0.3 dev: false @@ -7043,7 +6869,7 @@ packages: { integrity: sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw== } dev: true - /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@9.6.0)(typescript@5.5.3): resolution: { integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== } engines: { node: ^16.0.0 || >=18.0.0 } @@ -7056,26 +6882,26 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.21.0(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/parser': 6.21.0(eslint@9.6.0)(typescript@5.5.3) '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@9.6.0)(typescript@5.5.2) - '@typescript-eslint/utils': 6.21.0(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/type-utils': 6.21.0(eslint@9.6.0)(typescript@5.5.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.6.0)(typescript@5.5.3) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) eslint: 9.6.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.5.2) - typescript: 5.5.2 + ts-api-utils: 1.3.0(typescript@5.5.3) + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1)(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0)(eslint@9.6.0)(typescript@5.5.3): resolution: - { integrity: sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw== } + { integrity: sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA== } engines: { node: ^18.18.0 || >=20.0.0 } peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -7086,22 +6912,22 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.14.1(eslint@9.6.0)(typescript@5.5.2) - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/type-utils': 7.14.1(eslint@9.6.0)(typescript@5.5.2) - '@typescript-eslint/utils': 7.14.1(eslint@9.6.0)(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.14.1 + '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 7.15.0 + '@typescript-eslint/type-utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3) + '@typescript-eslint/utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3) + '@typescript-eslint/visitor-keys': 7.15.0 eslint: 9.6.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.2) - typescript: 5.5.2 + ts-api-utils: 1.3.0(typescript@5.5.3) + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.21.0(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/parser@6.21.0(eslint@9.6.0)(typescript@5.5.3): resolution: { integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== } engines: { node: ^16.0.0 || >=18.0.0 } @@ -7114,18 +6940,18 @@ packages: dependencies: '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.2) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.3) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) eslint: 9.6.0 - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@7.14.1(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3): resolution: - { integrity: sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA== } + { integrity: sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A== } engines: { node: ^18.18.0 || >=20.0.0 } peerDependencies: eslint: ^8.56.0 @@ -7134,13 +6960,13 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.14.1 - debug: 4.3.5(supports-color@9.4.0) + '@typescript-eslint/scope-manager': 7.15.0 + '@typescript-eslint/types': 7.15.0 + '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3) + '@typescript-eslint/visitor-keys': 7.15.0 + debug: 4.3.4(supports-color@9.4.0) eslint: 9.6.0 - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true @@ -7163,16 +6989,16 @@ packages: '@typescript-eslint/visitor-keys': 7.11.0 dev: true - /@typescript-eslint/scope-manager@7.14.1: + /@typescript-eslint/scope-manager@7.15.0: resolution: - { integrity: sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA== } + { integrity: sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw== } engines: { node: ^18.18.0 || >=20.0.0 } dependencies: - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/visitor-keys': 7.14.1 + '@typescript-eslint/types': 7.15.0 + '@typescript-eslint/visitor-keys': 7.15.0 dev: true - /@typescript-eslint/type-utils@6.21.0(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/type-utils@6.21.0(eslint@9.6.0)(typescript@5.5.3): resolution: { integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== } engines: { node: ^16.0.0 || >=18.0.0 } @@ -7183,19 +7009,19 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.2) - '@typescript-eslint/utils': 6.21.0(eslint@9.6.0)(typescript@5.5.2) - debug: 4.3.5(supports-color@9.4.0) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.6.0)(typescript@5.5.3) + debug: 4.3.4(supports-color@9.4.0) eslint: 9.6.0 - ts-api-utils: 1.3.0(typescript@5.5.2) - typescript: 5.5.2 + ts-api-utils: 1.3.0(typescript@5.5.3) + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/type-utils@7.14.1(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/type-utils@7.15.0(eslint@9.6.0)(typescript@5.5.3): resolution: - { integrity: sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ== } + { integrity: sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg== } engines: { node: ^18.18.0 || >=20.0.0 } peerDependencies: eslint: ^8.56.0 @@ -7204,12 +7030,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) - '@typescript-eslint/utils': 7.14.1(eslint@9.6.0)(typescript@5.5.2) - debug: 4.3.5(supports-color@9.4.0) + '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3) + '@typescript-eslint/utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3) + debug: 4.3.4(supports-color@9.4.0) eslint: 9.6.0 - ts-api-utils: 1.3.0(typescript@5.5.2) - typescript: 5.5.2 + ts-api-utils: 1.3.0(typescript@5.5.3) + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true @@ -7232,13 +7058,13 @@ packages: engines: { node: ^18.18.0 || >=20.0.0 } dev: true - /@typescript-eslint/types@7.14.1: + /@typescript-eslint/types@7.15.0: resolution: - { integrity: sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg== } + { integrity: sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw== } engines: { node: ^18.18.0 || >=20.0.0 } dev: true - /@typescript-eslint/typescript-estree@5.62.0(supports-color@9.4.0)(typescript@5.5.2): + /@typescript-eslint/typescript-estree@5.62.0(supports-color@9.4.0)(typescript@5.5.3): resolution: { integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== } engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } @@ -7250,17 +7076,17 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.2 - tsutils: 3.21.0(typescript@5.5.2) - typescript: 5.5.2 + tsutils: 3.21.0(typescript@5.5.3) + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: false - /@typescript-eslint/typescript-estree@6.21.0(typescript@5.5.2): + /@typescript-eslint/typescript-estree@6.21.0(typescript@5.5.3): resolution: { integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== } engines: { node: ^16.0.0 || >=18.0.0 } @@ -7272,18 +7098,18 @@ packages: dependencies: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.5.2) - typescript: 5.5.2 + ts-api-utils: 1.3.0(typescript@5.5.3) + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@7.11.0(typescript@5.5.2): + /@typescript-eslint/typescript-estree@7.11.0(typescript@5.5.3): resolution: { integrity: sha512-cxkhZ2C/iyi3/6U9EPc5y+a6csqHItndvN/CzbNXTNrsC3/ASoYQZEt9uMaEp+xFNjasqQyszp5TumAVKKvJeQ== } engines: { node: ^18.18.0 || >=20.0.0 } @@ -7295,20 +7121,20 @@ packages: dependencies: '@typescript-eslint/types': 7.11.0 '@typescript-eslint/visitor-keys': 7.11.0 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.5.2) - typescript: 5.5.2 + ts-api-utils: 1.3.0(typescript@5.5.3) + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@7.14.1(typescript@5.5.2): + /@typescript-eslint/typescript-estree@7.15.0(typescript@5.5.3): resolution: - { integrity: sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA== } + { integrity: sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ== } engines: { node: ^18.18.0 || >=20.0.0 } peerDependencies: typescript: '*' @@ -7316,20 +7142,20 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/visitor-keys': 7.14.1 - debug: 4.3.5(supports-color@9.4.0) + '@typescript-eslint/types': 7.15.0 + '@typescript-eslint/visitor-keys': 7.15.0 + debug: 4.3.4(supports-color@9.4.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.5.2) - typescript: 5.5.2 + ts-api-utils: 1.3.0(typescript@5.5.3) + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.21.0(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/utils@6.21.0(eslint@9.6.0)(typescript@5.5.3): resolution: { integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== } engines: { node: ^16.0.0 || >=18.0.0 } @@ -7341,7 +7167,7 @@ packages: '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.2) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.3) eslint: 9.6.0 semver: 7.6.2 transitivePeerDependencies: @@ -7349,7 +7175,7 @@ packages: - typescript dev: true - /@typescript-eslint/utils@7.11.0(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/utils@7.11.0(eslint@9.6.0)(typescript@5.5.3): resolution: { integrity: sha512-xlAWwPleNRHwF37AhrZurOxA1wyXowW4PqVXZVUNCLjB48CqdPJoJWkrpH2nij9Q3Lb7rtWindtoXwxjxlKKCA== } engines: { node: ^18.18.0 || >=20.0.0 } @@ -7359,24 +7185,24 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) '@typescript-eslint/scope-manager': 7.11.0 '@typescript-eslint/types': 7.11.0 - '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.5.2) + '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.5.3) eslint: 9.6.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@7.14.1(eslint@9.6.0)(typescript@5.5.2): + /@typescript-eslint/utils@7.15.0(eslint@9.6.0)(typescript@5.5.3): resolution: - { integrity: sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ== } + { integrity: sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA== } engines: { node: ^18.18.0 || >=20.0.0 } peerDependencies: eslint: ^8.56.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0) - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) + '@typescript-eslint/scope-manager': 7.15.0 + '@typescript-eslint/types': 7.15.0 + '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3) eslint: 9.6.0 transitivePeerDependencies: - supports-color @@ -7410,12 +7236,12 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@7.14.1: + /@typescript-eslint/visitor-keys@7.15.0: resolution: - { integrity: sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA== } + { integrity: sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw== } engines: { node: ^18.18.0 || >=20.0.0 } dependencies: - '@typescript-eslint/types': 7.14.1 + '@typescript-eslint/types': 7.15.0 eslint-visitor-keys: 3.4.3 dev: true @@ -7490,7 +7316,7 @@ packages: { integrity: sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA== } engines: { node: '>=8' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@wry/equality@0.5.6: @@ -7498,7 +7324,7 @@ packages: { integrity: sha512-D46sfMTngaYlrH+OspKf8mIJETntFnf6Hsjb0V41jAXJ7Bx2kB8Rv8RCUujuVWYttFtHkUNp7g+FwxNQAr6mXA== } engines: { node: '>=8' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /@wry/trie@0.4.3: @@ -7506,7 +7332,7 @@ packages: { integrity: sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== } engines: { node: '>=8' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /abbrev@1.1.1: @@ -7530,13 +7356,13 @@ packages: acorn: 8.11.3 dev: true - /acorn-jsx@5.3.2(acorn@8.12.0): + /acorn-jsx@5.3.2(acorn@8.12.1): resolution: { integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== } peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.12.0 + acorn: 8.12.1 dev: true /acorn-walk@8.2.0: @@ -7569,9 +7395,9 @@ packages: engines: { node: '>=0.4.0' } hasBin: true - /acorn@8.12.0: + /acorn@8.12.1: resolution: - { integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== } + { integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== } engines: { node: '>=0.4.0' } hasBin: true dev: true @@ -7581,7 +7407,7 @@ packages: { integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== } engines: { node: '>= 6.0.0' } dependencies: - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) transitivePeerDependencies: - supports-color dev: false @@ -8057,7 +7883,7 @@ packages: peerDependencies: ajv: 4.11.8 - 8 dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.24.6 '@humanwhocodes/momoa': 2.0.4 ajv: 8.12.0 chalk: 4.1.2 @@ -8346,7 +8172,7 @@ packages: { integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== } dependencies: pascal-case: 3.1.2 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /camelcase@6.3.0: @@ -8364,7 +8190,7 @@ packages: { integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== } dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 upper-case-first: 2.0.2 dev: true @@ -8438,7 +8264,7 @@ packages: path-case: 3.0.4 sentence-case: 3.0.4 snake-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /character-entities-legacy@1.1.4: @@ -8795,7 +8621,7 @@ packages: { integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== } dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 upper-case: 2.0.2 dev: true @@ -8838,7 +8664,7 @@ packages: resolution: { integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== } - /cosmiconfig@9.0.0(typescript@5.5.2): + /cosmiconfig@9.0.0(typescript@5.5.3): resolution: { integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== } engines: { node: '>=14' } @@ -8852,7 +8678,7 @@ packages: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 - typescript: 5.5.2 + typescript: 5.5.3 dev: false /cp-file@10.0.0: @@ -9017,7 +8843,7 @@ packages: ms: 2.1.3 dev: true - /debug@4.3.4: + /debug@4.3.4(supports-color@8.1.1): resolution: { integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== } engines: { node: '>=6.0' } @@ -9028,10 +8854,12 @@ packages: optional: true dependencies: ms: 2.1.2 + supports-color: 8.1.1 + dev: true - /debug@4.3.5(supports-color@8.1.1): + /debug@4.3.4(supports-color@9.4.0): resolution: - { integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== } + { integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== } engines: { node: '>=6.0' } peerDependencies: supports-color: '*' @@ -9040,9 +8868,9 @@ packages: optional: true dependencies: ms: 2.1.2 - supports-color: 8.1.1 + supports-color: 9.4.0 - /debug@4.3.5(supports-color@9.4.0): + /debug@4.3.5(supports-color@8.1.1): resolution: { integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== } engines: { node: '>=6.0' } @@ -9053,7 +8881,7 @@ packages: optional: true dependencies: ms: 2.1.2 - supports-color: 9.4.0 + supports-color: 8.1.1 /decompress-response@6.0.0: resolution: @@ -9245,10 +9073,10 @@ packages: { integrity: sha512-Mq8egjnW2NSCkzEb/Az15/JnBI/Ryyl6Po0Y+0mABTFvOS6DAyUGRZqz1nyhu4QJmWWe0zaGs/ITIBeWkvCkGw== } engines: { node: ^14.14.0 || >=16.0.0 } dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(supports-color@9.4.0)(typescript@5.5.2) + '@typescript-eslint/typescript-estree': 5.62.0(supports-color@9.4.0)(typescript@5.5.3) ast-module-types: 5.0.0 node-source-walk: 6.0.2 - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: false @@ -9339,7 +9167,7 @@ packages: { integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== } dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /dot-prop@7.2.0: @@ -9823,19 +9651,19 @@ packages: source-map: 0.6.1 dev: false - /eslint-config-oclif-typescript@3.1.8(eslint@9.6.0)(typescript@5.5.2): + /eslint-config-oclif-typescript@3.1.8(eslint@9.6.0)(typescript@5.5.3): resolution: { integrity: sha512-nxEKt95XesuRA+5R1L4weQvmr1U0io+qzRJ8xoRb9C9OXrQTVNgcLJMDxt8XsPsr6z6ZcB3cY8kR8BuF7nBbHQ== } engines: { node: '>=18.0.0' } dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@9.6.0)(typescript@5.5.2) - '@typescript-eslint/parser': 6.21.0(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@9.6.0)(typescript@5.5.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.6.0)(typescript@5.5.3) eslint-config-xo-space: 0.35.0(eslint@9.6.0) eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0)(eslint-plugin-import@2.29.1)(eslint@9.6.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) eslint-plugin-mocha: 10.4.3(eslint@9.6.0) eslint-plugin-n: 15.7.0(eslint@9.6.0) - eslint-plugin-perfectionist: 2.11.0(eslint@9.6.0)(typescript@5.5.2) + eslint-plugin-perfectionist: 2.11.0(eslint@9.6.0)(typescript@5.5.3) transitivePeerDependencies: - astro-eslint-parser - eslint @@ -9902,13 +9730,13 @@ packages: eslint: '*' eslint-plugin-import: '*' dependencies: - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) enhanced-resolve: 5.15.0 eslint: 9.6.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) fast-glob: 3.3.1 - get-tsconfig: 4.7.5 + get-tsconfig: 4.7.2 is-core-module: 2.13.0 is-glob: 4.0.3 transitivePeerDependencies: @@ -9918,7 +9746,7 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.14.1)(eslint-plugin-import@2.29.1)(eslint@9.6.0): + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.15.0)(eslint-plugin-import@2.29.1)(eslint@9.6.0): resolution: { integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== } engines: { node: ^14.18.0 || >=16.0.0 } @@ -9926,11 +9754,11 @@ packages: eslint: '*' eslint-plugin-import: '*' dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) enhanced-resolve: 5.15.0 eslint: 9.6.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.14.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.14.1)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) fast-glob: 3.3.1 get-tsconfig: 4.7.2 is-core-module: 2.13.0 @@ -9964,7 +9792,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/parser': 6.21.0(eslint@9.6.0)(typescript@5.5.3) debug: 3.2.7 eslint: 9.6.0 eslint-import-resolver-node: 0.3.9 @@ -9973,7 +9801,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.14.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0): resolution: { integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== } engines: { node: '>=4' } @@ -9995,11 +9823,11 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 7.14.1(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3) debug: 3.2.7 eslint: 9.6.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.14.1)(eslint-plugin-import@2.29.1)(eslint@9.6.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.15.0)(eslint-plugin-import@2.29.1)(eslint@9.6.0) transitivePeerDependencies: - supports-color dev: true @@ -10027,7 +9855,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/parser': 6.21.0(eslint@9.6.0)(typescript@5.5.3) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 @@ -10052,7 +9880,7 @@ packages: - supports-color dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0): resolution: { integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== } engines: { node: '>=4' } @@ -10063,7 +9891,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 7.14.1(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 @@ -10072,7 +9900,7 @@ packages: doctrine: 2.1.0 eslint: 9.6.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.14.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -10119,7 +9947,7 @@ packages: semver: 7.6.2 dev: true - /eslint-plugin-perfectionist@2.11.0(eslint@9.6.0)(typescript@5.5.2): + /eslint-plugin-perfectionist@2.11.0(eslint@9.6.0)(typescript@5.5.3): resolution: { integrity: sha512-XrtBtiu5rbQv88gl+1e2RQud9te9luYNvKIgM9emttQ2zutHPzY/AQUucwxscDKV4qlTkvLTxjOFvxqeDpPorw== } peerDependencies: @@ -10138,7 +9966,7 @@ packages: vue-eslint-parser: optional: true dependencies: - '@typescript-eslint/utils': 7.11.0(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/utils': 7.11.0(eslint@9.6.0)(typescript@5.5.3) eslint: 9.6.0 minimatch: 9.0.4 natural-compare-lite: 1.4.0 @@ -10240,7 +10068,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) escape-string-regexp: 4.0.0 eslint-scope: 8.0.1 eslint-visitor-keys: 4.0.0 @@ -10272,8 +10100,8 @@ packages: { integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } dependencies: - acorn: 8.12.0 - acorn-jsx: 5.3.2(acorn@8.12.0) + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 4.0.0 dev: true @@ -11088,7 +10916,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 15.8.0 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /graphql@15.8.0: @@ -11185,7 +11013,7 @@ packages: { integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== } dependencies: capital-case: 1.0.4 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /headers-polyfill@4.0.2: @@ -11244,7 +11072,7 @@ packages: { integrity: sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA== } engines: { node: ^16.14.0 || >=18.0.0 } dependencies: - lru-cache: 10.3.0 + lru-cache: 10.2.0 /hot-shots@10.0.0: resolution: @@ -11274,7 +11102,7 @@ packages: engines: { node: '>=8.0.0' } dependencies: content-type: 1.0.5 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) is-retry-allowed: 1.2.0 is-stream: 2.0.1 parse-json: 4.0.0 @@ -11302,7 +11130,7 @@ packages: engines: { node: '>= 6' } dependencies: agent-base: 6.0.2(supports-color@9.4.0) - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) transitivePeerDependencies: - supports-color dev: false @@ -11972,6 +11800,12 @@ packages: { integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== } dev: true + /json-stringify-deterministic@1.0.12: + resolution: + { integrity: sha512-q3PN0lbUdv0pmurkBNdJH3pfFvOTL/Zp0lquqpvcjfKzt6Y0j49EPHAmVHCAS4Ceq/Y+PejWTzyiVpoY71+D6g== } + engines: { node: '>= 4' } + dev: false + /json5@1.0.2: resolution: { integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== } @@ -12049,7 +11883,6 @@ packages: resolution: { integrity: sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA== } engines: { node: '>=14.0.0' } - dev: true /lazystream@1.0.1: resolution: @@ -12182,7 +12015,7 @@ packages: dependencies: chalk: 5.3.0 commander: 12.1.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) execa: 8.0.1 lilconfig: 3.1.1 listr2: 8.2.1 @@ -12409,7 +12242,7 @@ packages: resolution: { integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /lowercase-keys@3.0.0: @@ -12417,9 +12250,9 @@ packages: { integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - /lru-cache@10.3.0: + /lru-cache@10.2.0: resolution: - { integrity: sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ== } + { integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== } engines: { node: 14 || >=16.14 } /lru-cache@4.1.5: @@ -12740,7 +12573,7 @@ packages: resolution: { integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA== } dependencies: - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) parse-entities: 2.0.0 transitivePeerDependencies: - supports-color @@ -12849,6 +12682,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch@9.0.5: + resolution: + { integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== } + engines: { node: '>=16 || 14 >=14.17' } + dependencies: + brace-expansion: 2.0.1 + /minimist@1.2.8: resolution: { integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== } @@ -12949,7 +12789,7 @@ packages: { integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== } dev: true - /msw@2.3.1(typescript@5.5.2): + /msw@2.3.1(typescript@5.5.3): resolution: { integrity: sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig== } engines: { node: '>=18' } @@ -12976,8 +12816,8 @@ packages: outvariant: 1.4.2 path-to-regexp: 6.2.1 strict-event-emitter: 0.5.1 - type-fest: 4.20.1 - typescript: 5.5.2 + type-fest: 4.18.1 + typescript: 5.5.3 yargs: 17.7.2 dev: true @@ -13086,7 +12926,7 @@ packages: { integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== } dependencies: lower-case: 2.0.2 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /node-domexception@1.0.0: @@ -13475,25 +13315,25 @@ packages: { integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== } dev: true - /oclif@4.13.12: + /oclif@4.13.16: resolution: - { integrity: sha512-QSPIlQY2tAf/cjsbbhbXGMmuOXp5/W3ptkRtuC0VhQ8L4nKIKFBih6CSG1A18wfNSBHy4/xWmbywGJQA83SBdA== } + { integrity: sha512-YQIgDVga3XyJNKhPZ6BVp9pWbP8CVEIY5kYHj3G3mZXX679ntaIAawgYd66/bWBQE24483IBl3Jjnf1qb4EDZA== } engines: { node: '>=18.0.0' } hasBin: true dependencies: - '@aws-sdk/client-cloudfront': 3.600.0 - '@aws-sdk/client-s3': 3.606.0 - '@inquirer/confirm': 3.1.12 - '@inquirer/input': 2.1.10 - '@inquirer/select': 2.3.6 - '@oclif/core': 4.0.7 - '@oclif/plugin-help': 6.2.4 - '@oclif/plugin-not-found': 3.2.8 + '@aws-sdk/client-cloudfront': 3.609.0 + '@aws-sdk/client-s3': 3.609.0 + '@inquirer/confirm': 3.1.14 + '@inquirer/input': 2.2.1 + '@inquirer/select': 2.3.10 + '@oclif/core': 4.0.8 + '@oclif/plugin-help': 6.2.5 + '@oclif/plugin-not-found': 3.2.10 '@oclif/plugin-warn-if-update-available': 3.0.19 async-retry: 1.3.3 chalk: 4.1.2 change-case: 4.1.2 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) ejs: 3.1.10 find-yarn-workspace-root: 2.0.0 fs-extra: 8.1.0 @@ -13570,7 +13410,7 @@ packages: dependencies: '@wry/context': 0.7.3 '@wry/trie': 0.4.3 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /optionator@0.9.3: @@ -13795,6 +13635,11 @@ packages: p-timeout: 5.1.0 dev: false + /pako@2.1.0: + resolution: + { integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== } + dev: false + /papaparse@5.4.1: resolution: { integrity: sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw== } @@ -13805,7 +13650,7 @@ packages: { integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== } dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /parent-module@1.0.1: @@ -13891,7 +13736,7 @@ packages: { integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== } dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /password-prompt@1.1.3: @@ -13917,7 +13762,7 @@ packages: { integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== } dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /path-exists@4.0.0: @@ -13956,7 +13801,7 @@ packages: { integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== } engines: { node: '>=16 || 14 >=14.17' } dependencies: - lru-cache: 10.3.0 + lru-cache: 10.2.0 minipass: 7.0.3 dev: true @@ -14179,6 +14024,17 @@ packages: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 + dev: false + + /postcss@8.4.39: + resolution: + { integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw== } + engines: { node: ^10 || ^12 || >=14 } + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + dev: true /postgres-array@2.0.0: resolution: @@ -14386,7 +14242,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.14.9 + '@types/node': 20.14.10 long: 5.2.3 /protobufjs@7.3.2: @@ -14405,7 +14261,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.14.9 + '@types/node': 20.14.10 long: 5.2.3 dev: true @@ -14851,7 +14707,7 @@ packages: { integrity: sha512-+dtWQ7l2lqQDxheaG3jjyN1QI37gEwvzACSgjYi4/C2y+ZTUMeRW8BIOm+9NBKvwaMBUSZfPXVOt1skB0vBkRw== } engines: { node: '>=8.6.0' } dependencies: - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) module-details-from-path: 1.0.3 resolve: 1.22.6 transitivePeerDependencies: @@ -14863,7 +14719,7 @@ packages: { integrity: sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== } engines: { node: '>=8.6.0' } dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) module-details-from-path: 1.0.3 resolve: 1.22.6 transitivePeerDependencies: @@ -14953,6 +14809,11 @@ packages: { integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== } engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + /rfc4648@1.5.3: + resolution: + { integrity: sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ== } + dev: false + /rfdc@1.3.0: resolution: { integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== } @@ -15002,7 +14863,7 @@ packages: semver: 5.7.2 dev: true - /rollup-plugin-dts@6.1.1(rollup@4.18.0)(typescript@5.5.2): + /rollup-plugin-dts@6.1.1(rollup@4.18.0)(typescript@5.5.3): resolution: { integrity: sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA== } engines: { node: '>=16' } @@ -15012,7 +14873,7 @@ packages: dependencies: magic-string: 0.30.10 rollup: 4.18.0 - typescript: 5.5.2 + typescript: 5.5.3 optionalDependencies: '@babel/code-frame': 7.24.7 dev: true @@ -15026,7 +14887,7 @@ packages: rollup: ^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 dependencies: '@rollup/pluginutils': 5.0.5(rollup@4.18.0) - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) es-module-lexer: 1.3.1 esbuild: 0.21.5 get-tsconfig: 4.7.2 @@ -15129,7 +14990,7 @@ packages: resolution: { integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /safe-array-concat@1.0.1: @@ -15211,7 +15072,7 @@ packages: { integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== } dependencies: no-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 upper-case-first: 2.0.2 dev: true @@ -15453,7 +15314,7 @@ packages: { integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== } dependencies: dot-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.6.2 dev: true /sort-keys@4.2.0: @@ -15987,14 +15848,14 @@ packages: { integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== } dev: true - /ts-api-utils@1.3.0(typescript@5.5.2): + /ts-api-utils@1.3.0(typescript@5.5.3): resolution: { integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== } engines: { node: '>=16' } peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.5.2 + typescript: 5.5.3 dev: true /ts-invariant@0.10.3: @@ -16002,7 +15863,7 @@ packages: { integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== } engines: { node: '>=8' } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /ts-morph@23.0.0: @@ -16012,7 +15873,7 @@ packages: '@ts-morph/common': 0.24.0 code-block-writer: 13.0.1 - /ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.2): + /ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3): resolution: { integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== } hasBin: true @@ -16032,14 +15893,14 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.14.9 + '@types/node': 20.14.10 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.5.2 + typescript: 5.5.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 @@ -16060,11 +15921,6 @@ packages: /tslib@2.6.2: resolution: { integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== } - dev: true - - /tslib@2.6.3: - resolution: - { integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== } /tsutils@3.21.0(typescript@4.8.2): resolution: @@ -16077,7 +15933,7 @@ packages: typescript: 4.8.2 dev: true - /tsutils@3.21.0(typescript@5.5.2): + /tsutils@3.21.0(typescript@5.5.3): resolution: { integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== } engines: { node: '>= 6' } @@ -16085,12 +15941,12 @@ packages: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 5.5.2 + typescript: 5.5.3 dev: false - /tsx@4.16.0: + /tsx@4.16.2: resolution: - { integrity: sha512-MPgN+CuY+4iKxGoJNPv+1pyo5YWZAQ5XfsyobUG+zoKG7IkvCPLZDEyoIb8yLS2FcWci1nlxAqmvPlFWD5AFiQ== } + { integrity: sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ== } engines: { node: '>=18.0.0' } hasBin: true dependencies: @@ -16239,9 +16095,9 @@ packages: { integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== } engines: { node: '>=14.16' } - /type-fest@4.20.1: + /type-fest@4.18.1: resolution: - { integrity: sha512-R6wDsVsoS9xYOpy8vgeBlqpdOyzJ12HNfQhC/aAKWM3YoCV9TtunJzh/QpkMgeDhkoynDcw5f1y+qF9yc/HHyg== } + { integrity: sha512-qXhgeNsX15bM63h5aapNFcQid9jRF/l3ojDoDFmekDQEUufZ9U4ErVt6SjDxnHp48Ltrw616R8yNc3giJ3KvVQ== } engines: { node: '>=16' } /typed-array-buffer@1.0.0: @@ -16296,9 +16152,9 @@ packages: { integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== } dev: true - /typescript-eslint@7.14.1(eslint@9.6.0)(typescript@5.5.2): + /typescript-eslint@7.15.0(eslint@9.6.0)(typescript@5.5.3): resolution: - { integrity: sha512-Eo1X+Y0JgGPspcANKjeR6nIqXl4VL5ldXLc15k4m9upq+eY5fhU2IueiEZL6jmHrKH8aCfbIvM/v3IrX5Hg99w== } + { integrity: sha512-Ta40FhMXBCwHura4X4fncaCVkVcnJ9jnOq5+Lp4lN8F4DzHZtOwZdRvVBiNUGznUDHPwdGnrnwxmUOU2fFQqFA== } engines: { node: ^18.18.0 || >=20.0.0 } peerDependencies: eslint: ^8.56.0 @@ -16307,11 +16163,11 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 7.14.1(@typescript-eslint/parser@7.14.1)(eslint@9.6.0)(typescript@5.5.2) - '@typescript-eslint/parser': 7.14.1(eslint@9.6.0)(typescript@5.5.2) - '@typescript-eslint/utils': 7.14.1(eslint@9.6.0)(typescript@5.5.2) + '@typescript-eslint/eslint-plugin': 7.15.0(@typescript-eslint/parser@7.15.0)(eslint@9.6.0)(typescript@5.5.3) + '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3) + '@typescript-eslint/utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3) eslint: 9.6.0 - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color dev: true @@ -16330,9 +16186,9 @@ packages: hasBin: true dev: false - /typescript@5.5.2: + /typescript@5.5.3: resolution: - { integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew== } + { integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== } engines: { node: '>=14.17' } hasBin: true @@ -16482,14 +16338,14 @@ packages: resolution: { integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /upper-case@2.0.2: resolution: { integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== } dependencies: - tslib: 2.6.3 + tslib: 2.6.2 dev: true /uri-js@4.4.1: @@ -16560,17 +16416,17 @@ packages: vfile-message: 2.0.4 dev: true - /vite-node@1.6.0(@types/node@20.14.9): + /vite-node@1.6.0(@types/node@20.14.10): resolution: { integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw== } engines: { node: ^18.0.0 || >=20.0.0 } hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.5(supports-color@9.4.0) + debug: 4.3.4(supports-color@9.4.0) pathe: 1.1.1 picocolors: 1.0.0 - vite: 5.3.2(@types/node@20.14.9) + vite: 5.3.3(@types/node@20.14.10) transitivePeerDependencies: - '@types/node' - less @@ -16582,9 +16438,9 @@ packages: - terser dev: true - /vite@5.3.2(@types/node@20.14.9): + /vite@5.3.3(@types/node@20.14.10): resolution: - { integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA== } + { integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A== } engines: { node: ^18.0.0 || >=20.0.0 } hasBin: true peerDependencies: @@ -16611,15 +16467,15 @@ packages: terser: optional: true dependencies: - '@types/node': 20.14.9 - esbuild: 0.21.5 - postcss: 8.4.38 + '@types/node': 20.14.10 + esbuild: 0.21.3 + postcss: 8.4.39 rollup: 4.18.0 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest@1.6.0(@types/node@20.14.9): + /vitest@1.6.0(@types/node@20.14.10): resolution: { integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA== } engines: { node: ^18.0.0 || >=20.0.0 } @@ -16645,7 +16501,7 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.14.9 + '@types/node': 20.14.10 '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 '@vitest/snapshot': 1.6.0 @@ -16653,7 +16509,7 @@ packages: '@vitest/utils': 1.6.0 acorn-walk: 8.3.2 chai: 4.3.10 - debug: 4.3.4 + debug: 4.3.4(supports-color@9.4.0) execa: 8.0.1 local-pkg: 0.5.0 magic-string: 0.30.5 @@ -16663,8 +16519,8 @@ packages: strip-literal: 2.0.0 tinybench: 2.5.1 tinypool: 0.8.4 - vite: 5.3.2(@types/node@20.14.9) - vite-node: 1.6.0(@types/node@20.14.9) + vite: 5.3.3(@types/node@20.14.10) + vite-node: 1.6.0(@types/node@20.14.10) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -16998,9 +16854,9 @@ packages: { integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== } engines: { node: '>=12.20' } - /yoctocolors-cjs@2.1.1: + /yoctocolors-cjs@2.1.2: resolution: - { integrity: sha512-c6T13b6qYcJZvck7QbEFXrFX/Mu2KOjvAGiKHmYMUg96jxNpfP6i+psGW72BOPxOIDUJrORG+Kyu7quMX9CQBQ== } + { integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA== } engines: { node: '>=18' } /yoga-layout-prebuilt@1.10.0: diff --git a/test/__snapshots__/codegen.test.ts.snap b/test/__snapshots__/codegen.test.ts.snap index f31939de2..eeb4d0607 100644 --- a/test/__snapshots__/codegen.test.ts.snap +++ b/test/__snapshots__/codegen.test.ts.snap @@ -3,51 +3,66 @@ exports[`generate > should generate CJS code 1`] = ` ""use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getXataClient = exports.XataClient = void 0; -// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. +exports.XataClient = void 0; const client_1 = require("@xata.io/client"); /** @typedef { import('./types').SchemaTables } SchemaTables */ /** @type { SchemaTables } */ -const tables = [{ name: "users", columns: [{ name: "name", type: "string" }] }]; +const schema = { + tables: [ + { + name: "users", + primaryKey: ["xata_id"], + columns: [{ name: "name", type: "string" }], + }, + ], +}; /** @type { import('@xata.io/client').ClientConstructor<{}> } */ const DatabaseClient = (0, client_1.buildClient)(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", - branch: "feature-branch", -}; /** @typedef { import('./types').DatabaseSchema } DatabaseSchema */ /** @extends DatabaseClient */ class XataClient extends DatabaseClient { constructor(options) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + (0, client_1.getDeployPreviewBranch)(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } exports.XataClient = XataClient; -let instance = undefined; -/** @type { () => XataClient } */ -const getXataClient = () => { - if (instance) return instance; - instance = new XataClient(); - return instance; -}; -exports.getXataClient = getXataClient; " `; exports[`generate > should generate Deno code 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "npm:@xata.io/client@latest"; +"import { + buildClient, + getDeployPreviewBranch, +} from "npm:@xata.io/client@latest"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "npm:@xata.io/client@latest"; -const tables = [ - { name: "users", columns: [{ name: "name", type: "string" }] }, -] as const; - -export type SchemaTables = typeof tables; +const schema = { + tables: [ + { + name: "users", + primaryKey: ["xata_id"], + columns: [{ name: "name", type: "string" }], + }, + ], +} as const; + +export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type Users = InferredTypes["users"]; @@ -59,42 +74,45 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", - branch: "feature-branch", -}; - -export class XataClient extends DatabaseClient { +export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: Deno.env.get("XATA_API_KEY"), + databaseURL: Deno.env.get("XATA_DATABASE_URL"), + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(Deno.env.get) ?? + Deno.env.get("XATA_BRANCH") ?? + "main", + ...options, + }, + schema + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should ignore CJS for TS code 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "@xata.io/client"; -const tables = [ - { name: "users", columns: [{ name: "name", type: "string" }] }, -] as const; - -export type SchemaTables = typeof tables; +const schema = { + tables: [ + { + name: "users", + primaryKey: ["xata_id"], + columns: [{ name: "name", type: "string" }], + }, + ], +} as const; + +export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type Users = InferredTypes["users"]; @@ -106,42 +124,45 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", - branch: "feature-branch", -}; - -export class XataClient extends DatabaseClient { +export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should inject branch if passed 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "@xata.io/client"; -const tables = [ - { name: "users", columns: [{ name: "name", type: "string" }] }, -] as const; - -export type SchemaTables = typeof tables; +const schema = { + tables: [ + { + name: "users", + primaryKey: ["xata_id"], + columns: [{ name: "name", type: "string" }], + }, + ], +} as const; + +export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type Users = InferredTypes["users"]; @@ -153,57 +174,58 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", - branch: "feature-branch", -}; - -export class XataClient extends DatabaseClient { +export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should respect case naming 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "@xata.io/client"; -const tables = [ - { - name: "teams_Like", - columns: [ - { name: "name-test", type: "string" }, - { name: "labels_Test", type: "multiple" }, - { name: "ownerFoo", type: "link", link: { table: "users-foo" } }, - ], - }, - { - name: "users-foo", - columns: [ - { name: "email-random", type: "email" }, - { name: "full_name", type: "string" }, - { name: "teamLike", type: "link", link: { table: "teams_Like" } }, - ], - }, -] as const; - -export type SchemaTables = typeof tables; +const schema = { + tables: [ + { + name: "teams_Like", + primaryKey: ["xata_id"], + columns: [ + { name: "name-test", type: "string" }, + { name: "labels_Test", type: "multiple" }, + { name: "ownerFoo", type: "link", link: { table: "users-foo" } }, + ], + }, + { + name: "users-foo", + primaryKey: ["xata_id"], + columns: [ + { name: "email-random", type: "email" }, + { name: "full_name", type: "string" }, + { name: "teamLike", type: "link", link: { table: "teams_Like" } }, + ], + }, + ], +} as const; + +export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type TeamsLike = InferredTypes["teams_Like"]; @@ -219,47 +241,48 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", -}; - -export class XataClient extends DatabaseClient { +export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should respect numbers in names 1`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; import type { BaseClientOptions, SchemaInference, XataRecord, } from "@xata.io/client"; -const tables = [ - { - name: "1teams-case", - columns: [ - { name: "2nameCase", type: "string" }, - { name: "3Labels", type: "multiple" }, - ], - }, -] as const; - -export type SchemaTables = typeof tables; +const schema = { + tables: [ + { + name: "1teams-case", + primaryKey: ["xata_id"], + columns: [ + { name: "2nameCase", type: "string" }, + { name: "3Labels", type: "multiple" }, + ], + }, + ], +} as const; + +export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type $1teamsCase = InferredTypes["1teams-case"]; @@ -271,54 +294,58 @@ export type DatabaseSchema = { const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", -}; - -export class XataClient extends DatabaseClient { +export class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } - -let instance: XataClient | undefined = undefined; - -export const getXataClient = () => { - if (instance) return instance; - - instance = new XataClient(); - return instance; -}; " `; exports[`generate > should respect numbers in names 2`] = ` -"// Generated by Xata Codegen CODEGEN_VERSION. Please do not edit. -import { buildClient } from "@xata.io/client"; -const tables = [ - { - name: "1teams-case", - columns: [ - { name: "2nameCase", type: "string" }, - { name: "3Labels", type: "multiple" }, - ], - }, -]; -const DatabaseClient = buildClient(); -const defaultOptions = { - databaseURL: "https://workspace-1234.xata.sh/db/dbname", +"import { buildClient, getDeployPreviewBranch } from "@xata.io/client"; +const schema = { + tables: [ + { + name: "1teams-case", + primaryKey: ["xata_id"], + columns: [ + { name: "2nameCase", type: "string" }, + { name: "3Labels", type: "multiple" }, + ], + }, + ], }; +const DatabaseClient = buildClient(); export class XataClient extends DatabaseClient { constructor(options) { - super({ ...defaultOptions, ...options }, tables); + super( + { + apiKey: process.env.XATA_API_KEY, + databaseURL: process.env.XATA_DATABASE_URL, + // Use deploy preview branch if available, otherwise use branch from environment + branch: + getDeployPreviewBranch(process.env) ?? + process.env.XATA_BRANCH ?? + "main", + ...options, + }, + schema + ); } } -let instance = undefined; -export const getXataClient = () => { - if (instance) return instance; - instance = new XataClient(); - return instance; -}; " `; @@ -328,22 +355,25 @@ exports[`generate > should respect numbers in names 3`] = ` SchemaInference, XataRecord, } from "@xata.io/client"; -declare const tables: readonly [ - { - readonly name: "1teams-case"; - readonly columns: readonly [ - { - readonly name: "2nameCase"; - readonly type: "string"; - }, - { - readonly name: "3Labels"; - readonly type: "multiple"; - } - ]; - } -]; -export type SchemaTables = typeof tables; +declare const schema: { + readonly tables: readonly [ + { + readonly name: "1teams-case"; + readonly primaryKey: readonly ["xata_id"]; + readonly columns: readonly [ + { + readonly name: "2nameCase"; + readonly type: "string"; + }, + { + readonly name: "3Labels"; + readonly type: "multiple"; + } + ]; + } + ]; +}; +export type SchemaTables = typeof schema.tables; export type InferredTypes = SchemaInference; export type $1teamsCase = InferredTypes["1teams-case"]; export type $1teamsCaseRecord = $1teamsCase & XataRecord; @@ -351,10 +381,9 @@ export type DatabaseSchema = { "1teams-case": $1teamsCaseRecord; }; declare const DatabaseClient: any; -export declare class XataClient extends DatabaseClient { +export declare class XataClient extends DatabaseClient { constructor(options?: BaseClientOptions); } -export declare const getXataClient: () => XataClient; export {}; " `; diff --git a/test/codegen.test.ts b/test/codegen.test.ts index 8b80c19c6..9fcf03800 100644 --- a/test/codegen.test.ts +++ b/test/codegen.test.ts @@ -13,6 +13,7 @@ describe('generate', () => { tables: [ { name: '1teams-case', + primaryKey: ['xata_id'], columns: [ { name: '2nameCase', type: 'string' }, { name: '3Labels', type: 'multiple' } @@ -35,6 +36,7 @@ describe('generate', () => { tables: [ { name: 'teams_Like', + primaryKey: ['xata_id'], columns: [ { name: 'name-test', type: 'string' }, { name: 'labels_Test', type: 'multiple' }, @@ -43,6 +45,7 @@ describe('generate', () => { }, { name: 'users-foo', + primaryKey: ['xata_id'], columns: [ { name: 'email-random', type: 'email' }, { name: 'full_name', type: 'string' }, @@ -64,6 +67,7 @@ describe('generate', () => { tables: [ { name: 'users', + primaryKey: ['xata_id'], columns: [{ name: 'name', type: 'string' }] } ] @@ -82,6 +86,7 @@ describe('generate', () => { tables: [ { name: 'users', + primaryKey: ['xata_id'], columns: [{ name: 'name', type: 'string' }] } ] @@ -101,6 +106,7 @@ describe('generate', () => { tables: [ { name: 'users', + primaryKey: ['xata_id'], columns: [{ name: 'name', type: 'string' }] } ] @@ -120,6 +126,7 @@ describe('generate', () => { tables: [ { name: 'users', + primaryKey: ['xata_id'], columns: [{ name: 'name', type: 'string' }] } ] diff --git a/test/integration/cache.test.ts b/test/integration/cache.test.ts deleted file mode 100644 index 8ac6963aa..000000000 --- a/test/integration/cache.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from 'vitest'; -import { BaseClientOptions, SimpleCache } from '../../packages/client/src'; -import { XataClient } from '../../packages/codegen/example/xata'; -import { setUpTestEnvironment, TestEnvironmentResult } from '../utils/setup'; - -const cache = new SimpleCache(); - -let xata: XataClient; -let clientOptions: BaseClientOptions; -let hooks: TestEnvironmentResult['hooks']; - -beforeAll(async (ctx) => { - const result = await setUpTestEnvironment('cache', { cache }); - - xata = result.client; - clientOptions = result.clientOptions; - hooks = result.hooks; - - await hooks.beforeAll(ctx); -}); - -afterAll(async (ctx) => { - await hooks.afterAll(ctx); -}); - -beforeEach(async (ctx) => { - await hooks.beforeEach(ctx); -}); - -afterEach(async (ctx) => { - await cache.clear(); - await hooks.afterEach(ctx); -}); - -describe('cache', () => { - test('query with ttl', async () => { - const user = await xata.db.users.create({ full_name: 'John Doe' }); - - await cache.clear(); - - await xata.db.users.filter({ id: user.id }).getFirst(); - - const cacheItems = Object.entries(await cache.getAll()); - expect(Object.keys(cacheItems)).toHaveLength(1); - - const [cacheKey, value] = cacheItems[0] as any; - const cacheItem = await cache.get(cacheKey); - expect(cacheItem).not.toBeNull(); - expect(cacheItem?.records[0]?.full_name).toBe('John Doe'); - - await cache.set(cacheKey, { ...value, records: [{ ...user, full_name: 'Jane Doe' }] }); - - const query = await xata.db.users.filter({ id: user.id }).getFirst({ cache: 120000 }); - expect(query?.full_name).toBe('Jane Doe'); - }); - - test('query with expired ttl', async () => { - const user = await xata.db.users.create({ full_name: 'John Doe' }); - - await cache.clear(); - - await xata.db.users.filter({ id: user.id }).getFirst(); - - const cacheItems = Object.entries(await cache.getAll()); - expect(cacheItems).toHaveLength(1); - - const [key, value] = cacheItems[0] as any; - - await cache.set(key, { ...value, records: [{ ...user, full_name: 'Jane Doe' }] }); - - await new Promise((resolve) => setTimeout(resolve, 2000)); - - const query = await xata.db.users.filter({ id: user.id }).getFirst({ cache: 500 }); - expect(query?.full_name).toBe('John Doe'); - }); - - test("query with negative ttl doesn't cache", async () => { - const user = await xata.db.users.create({ full_name: 'John Doe' }); - - await cache.clear(); - - await xata.db.users.filter({ id: user.id }).getFirst(); - - const cacheItems = Object.entries(await cache.getAll()); - expect(cacheItems).toHaveLength(1); - - const [key, value] = cacheItems[0] as any; - - await cache.set(key, { ...value, records: [{ ...user, full_name: 'Jane Doe' }] }); - - const query = await xata.db.users.filter({ id: user.id }).getFirst({ cache: -1 }); - expect(query?.full_name).toBe('John Doe'); - }); - - test('no cache', async () => { - const client1 = new XataClient({ ...clientOptions, cache: undefined }); - const client2 = new XataClient({ ...clientOptions, cache: undefined }); - - const teamsA1 = await client1.db.teams.getAll(); - const teamsA2 = await client2.db.teams.getAll(); - - expect(teamsA1).toHaveLength(teamsA2.length); - - await client2.db.teams.create({}); - - const teamsB1 = await client1.db.teams.getAll(); - const teamsB2 = await client2.db.teams.getAll(); - - expect(teamsB1).toHaveLength(teamsB2.length); - expect(teamsB1).toHaveLength(teamsA1.length + 1); - expect(teamsB2).toHaveLength(teamsA2.length + 1); - }); -}); diff --git a/test/integration/create.test.ts b/test/integration/create.test.ts index 5296cfd62..3b864df82 100644 --- a/test/integration/create.test.ts +++ b/test/integration/create.test.ts @@ -30,94 +30,68 @@ describe('record creation', () => { test('create single user without id', async () => { const user = await xata.db.users.create({ name: 'User ships', birthDate: new Date() }); - expect(user.id).toBeDefined(); + expect(user.xata_id).toBeDefined(); expect(user.name).toBe('User ships'); expect(user.read).toBeDefined(); - expect(user.getMetadata).toBeDefined(); expect(user.birthDate).toBeInstanceOf(Date); - const metadata = user.getMetadata(); - expect(metadata.createdAt).toBeInstanceOf(Date); - expect(metadata.updatedAt).toBeInstanceOf(Date); - expect(metadata.version).toBe(0); - - expect(user.xata.createdAt).toBeInstanceOf(Date); - expect(user.xata.updatedAt).toBeInstanceOf(Date); - expect(user.xata.version).toBe(0); + expect(user.xata_createdat).toBeInstanceOf(Date); + expect(user.xata_updatedat).toBeInstanceOf(Date); + expect(user.xata_version).toBe(0); const json = user.toSerializable(); - expect(json.xata.createdAt).toBeDefined(); - expect(json.xata.updatedAt).toBeDefined(); - expect(json.xata.version).toBe(0); + expect(json.xata_createdat).toBeDefined(); + expect(json.xata_updatedat).toBeDefined(); + expect(json.xata_version).toBe(0); - expect(json.id).toBeDefined(); + expect(json.xata_id).toBeDefined(); expect(json.name).toBe('User ships'); // @ts-expect-error expect(json.read).not.toBeDefined(); - // @ts-expect-error - expect(json.getMetadata).not.toBeDefined(); expect(typeof json.birthDate).toBe('string'); }); test('create user with team', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - const user = await xata.db.users.create({ name: 'User ships', team }, ['*', 'team.*']); + const user = await xata.db.users.create({ name: 'User ships', team }, ['*']); - expect(user.id).toBeDefined(); + expect(user.xata_id).toBeDefined(); expect(user.name).toBe('User ships'); expect(user.read).toBeDefined(); - expect(user.getMetadata).toBeDefined(); expect(user.team).toBeDefined(); - expect(user.team?.id).toBe(team.id); - expect(user.team?.name).toBe('Team ships'); - expect(user.team?.read).toBeDefined(); - expect(user.team?.getMetadata).toBeDefined(); + expect(user.team).toBe(team.xata_id); - const userMetadata = user.getMetadata(); - expect(userMetadata.createdAt).toBeInstanceOf(Date); - expect(userMetadata.updatedAt).toBeInstanceOf(Date); - expect(userMetadata.version).toBe(0); - - expect(user.xata.createdAt).toBeInstanceOf(Date); - expect(user.xata.updatedAt).toBeInstanceOf(Date); - expect(user.xata.version).toBe(0); + expect(user.xata_createdat).toBeInstanceOf(Date); + expect(user.xata_updatedat).toBeInstanceOf(Date); + expect(user.xata_version).toBe(0); const json = user.toSerializable(); - expect(json.id).toBeDefined(); + expect(json.xata_id).toBeDefined(); expect(json.name).toBe('User ships'); // @ts-expect-error expect(json.read).not.toBeDefined(); - // @ts-expect-error - expect(json.getMetadata).not.toBeDefined(); expect(json.team).toBeDefined(); - expect(json.team?.id).toBe(team.id); - expect(json.team?.name).toBe('Team ships'); - // @ts-expect-error - expect(json.team.read).not.toBeDefined(); - // @ts-expect-error - expect(json.team.getMetadata).not.toBeDefined(); + expect(json.team).toBe(team.xata_id); }); test('create multiple teams without ids', async () => { - const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes' }], ['*', 'owner.*']); + const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes' }], ['*']); expect(teams).toHaveLength(2); - expect(teams[0].id).toBeDefined(); + expect(teams[0].xata_id).toBeDefined(); expect(teams[0].name).toBe('Team cars'); expect(teams[0].read).toBeDefined(); - expect(teams[1].id).toBeDefined(); + expect(teams[1].xata_id).toBeDefined(); expect(teams[1].name).toBe('Team planes'); expect(teams[1].read).toBeDefined(); - expect(teams[0].id).not.toBe(teams[1].id); + expect(teams[0].xata_id).not.toBe(teams[1].xata_id); expect(teams[0].labels).toBeNull(); expect(teams[1].labels).toBeNull(); expect(teams[0].owner).toBeNull(); - expect(teams[0].owner?.full_name).toBeUndefined(); - expect(teams[1].owner?.full_name).toBeUndefined(); }); test('create user with id', async () => { @@ -126,21 +100,21 @@ describe('record creation', () => { email: 'john4@doe.com' }); - const apiUser = await xata.db.users.filter({ id: user.id }).getFirst(); + const apiUser = await xata.db.users.filter({ xata_id: user.xata_id }).getFirst(); if (!apiUser) throw new Error('No user found'); - expect(user.id).toBe('a-unique-record-john-4'); + expect(user.xata_id).toBe('a-unique-record-john-4'); expect(user.read).toBeDefined(); expect(user.full_name).toBe('John Doe 4'); expect(user.full_name.startsWith('John')).toBe(true); - expect(user.id).toBe(apiUser.id); + expect(user.xata_id).toBe(apiUser.xata_id); expect(user.full_name).toBe(apiUser.full_name); expect(user.email).toBe(apiUser.email); - expect(user.xata.createdAt).toBeInstanceOf(Date); - expect(apiUser.xata.createdAt).toBeInstanceOf(Date); - expect(user.xata.createdAt.getTime()).toBe(apiUser.xata.createdAt.getTime()); + expect(user.xata_createdat).toBeInstanceOf(Date); + expect(apiUser.xata_createdat).toBeInstanceOf(Date); + expect(user.xata_createdat.getTime()).toBe(apiUser.xata_createdat.getTime()); expect( xata.db.users.create('a-unique-record-john-4', { @@ -152,19 +126,19 @@ describe('record creation', () => { test('create user with inlined id', async () => { const user = await xata.db.users.create({ - id: 'a-unique-record-john-5', + xata_id: 'a-unique-record-john-5', full_name: 'John Doe 5', email: 'john5@doe.com' }); - const apiUser = await xata.db.users.filter({ id: user.id }).getFirst(); + const apiUser = await xata.db.users.filter({ xata_id: user.xata_id }).getFirst(); if (!apiUser) throw new Error('No user found'); - expect(user.id).toBe('a-unique-record-john-5'); + expect(user.xata_id).toBe('a-unique-record-john-5'); expect(user.read).toBeDefined(); expect(user.full_name).toBe('John Doe 5'); - expect(user.id).toBe(apiUser.id); + expect(user.xata_id).toBe(apiUser.xata_id); expect(user.full_name).toBe(apiUser.full_name); expect(user.email).toBe(apiUser.email); }); @@ -181,7 +155,7 @@ describe('record creation', () => { test('create user with empty inline id is not allowed', async () => { expect( xata.db.users.create({ - id: '', + xata_id: '', full_name: 'John Doe 3', email: 'john3@doe.com' }) @@ -204,53 +178,56 @@ describe('record creation', () => { }); test('create multiple some with id and others without id', async () => { - const teams = await xata.db.teams.create([{ id: 'team_cars', name: 'Team cars' }, { name: 'Team planes' }]); + const teams = await xata.db.teams.create([{ xata_id: 'team_cars', name: 'Team cars' }, { name: 'Team planes' }]); expect(teams).toHaveLength(2); - expect(teams[0].id).toBe('team_cars'); + expect(teams[0].xata_id).toBe('team_cars'); expect(teams[0].name).toBe('Team cars'); expect(teams[0].read).toBeDefined(); - expect(teams[1].id).toBeDefined(); + expect(teams[1].xata_id).toBeDefined(); expect(teams[1].name).toBe('Team planes'); expect(teams[1].read).toBeDefined(); }); test('create multiple with returning columns', async () => { - const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes', labels: ['foo'] }], ['id']); + const teams = await xata.db.teams.create( + [{ name: 'Team cars' }, { name: 'Team planes', labels: ['foo'] }], + ['xata_id'] + ); expect(teams).toHaveLength(2); - expect(teams[0].id).toBeDefined(); + expect(teams[0].xata_id).toBeDefined(); // @ts-expect-error expect(teams[0].name).not.toBeDefined(); expect(teams[0].read).toBeDefined(); - expect(teams[1].id).toBeDefined(); + expect(teams[1].xata_id).toBeDefined(); // @ts-expect-error expect(teams[1].name).not.toBeDefined(); expect(teams[1].read).toBeDefined(); const team1 = await teams[0].read(); - expect(team1?.id).toBe(teams[0].id); + expect(team1?.xata_id).toBe(teams[0].xata_id); expect(team1?.name).toBe('Team cars'); - const team2 = await teams[1].read(['labels']); - expect(team2?.id).toBe(teams[1].id); + const team2 = await teams[1].read(['labels', 'xata_id']); + expect(team2?.xata_id).toBe(teams[1].xata_id); // @ts-expect-error expect(team2?.name).not.toBeDefined(); expect(team2?.labels).toEqual(['foo']); }); test('create single with returning columns', async () => { - const team = await xata.db.teams.create({ name: 'Team cars' }, ['id', 'owner']); + const team = await xata.db.teams.create({ name: 'Team cars' }, ['xata_id', 'owner']); expect(team).toBeDefined(); - expect(team.id).toBeDefined(); + expect(team.xata_id).toBeDefined(); // @ts-expect-error expect(team.name).not.toBeDefined(); expect(team.owner).toBeNull(); expect(team.read).toBeDefined(); const team1 = await team.read(); - expect(team1?.id).toBe(team.id); + expect(team1?.xata_id).toBe(team.xata_id); expect(team1?.name).toBe('Team cars'); }); @@ -258,7 +235,7 @@ describe('record creation', () => { const data = { full_name: 'John Doe 3', email: 'unique@example.com' }; const user = await xata.db.users.create(data); - expect(user.id).toBeDefined(); + expect(user.xata_id).toBeDefined(); expect(user.read).toBeDefined(); expect(user.full_name).toBe(data.full_name); expect(user.email).toBe(data.email); @@ -275,7 +252,7 @@ describe('record creation', () => { test('create and fail if already exists', async () => { const user1 = await xata.db.users.create({ full_name: 'John Doe 3', email: 'doe3@john.net' }); - expect(user1.id).toBeDefined(); + expect(user1.xata_id).toBeDefined(); expect(user1.read).toBeDefined(); expect(user1.full_name).toBe('John Doe 3'); @@ -285,7 +262,7 @@ describe('record creation', () => { test('create multiple fails if one of them already exists', async () => { const user1 = await xata.db.users.create({ full_name: 'John Doe 4', email: 'doe4@john.net' }); - expect(user1.id).toBeDefined(); + expect(user1.xata_id).toBeDefined(); expect(user1.read).toBeDefined(); expect(user1.full_name).toBe('John Doe 4'); @@ -318,7 +295,7 @@ describe('record creation', () => { ]); expect(teams).toHaveLength(2); - expect(teams[0].id).toBeDefined(); + expect(teams[0].xata_id).toBeDefined(); expect(teams[0].name).toMatchInlineSnapshot(` "Team 🚗" @@ -338,7 +315,7 @@ describe('record creation', () => { 🚕" `); - expect(teams[1].id).toBeDefined(); + expect(teams[1].xata_id).toBeDefined(); expect(teams[1].name).toMatchInlineSnapshot('"Team 🚀"'); expect(teams[1].labels).toMatchInlineSnapshot(` [ @@ -370,14 +347,13 @@ describe('record creation', () => { test("create link and read it's value", async () => { const user = await xata.db.users.create({ name: 'John Doe 3' }); - const team = await xata.db.teams.create({ name: 'Team cars', owner: user }, ['owner.name']); + const team = await xata.db.teams.create({ name: 'Team cars', owner: user.xata_id }, ['owner', 'xata_id']); expect(team).toBeDefined(); - expect(team.id).toBeDefined(); + expect(team.xata_id).toBeDefined(); // @ts-expect-error expect(team.name).toBeUndefined(); expect(team.owner).toBeDefined(); - expect(team.owner?.id).toBe(user.id); - expect(team.owner?.name).toBe('John Doe 3'); + expect(team.owner).toBe(user.xata_id); }); }); diff --git a/test/integration/createOrReplace.test.ts b/test/integration/createOrReplace.test.ts index 8d320ce15..541bc37ed 100644 --- a/test/integration/createOrReplace.test.ts +++ b/test/integration/createOrReplace.test.ts @@ -34,13 +34,13 @@ describe('record create or replace', () => { expect(team.email).toBe('ships@ilovethem.com'); expect(team.name).toBe('Team ships'); - const replacedTeam = await xata.db.teams.createOrReplace(team.id, { name: 'Team boats' }); + const replacedTeam = await xata.db.teams.createOrReplace(team.xata_id, { name: 'Team boats' }); - expect(replacedTeam.id).toBe(team.id); + expect(replacedTeam.xata_id).toBe(team.xata_id); expect(replacedTeam.read).toBeDefined(); expect(replacedTeam.email).toBeNull(); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); expect(replacedTeam.name).toBe('Team boats'); expect(apiTeam?.name).toBe('Team boats'); @@ -48,14 +48,14 @@ describe('record create or replace', () => { }); test('create or replace with optional id', async () => { - const id: string | undefined = undefined; + const xata_id: string | undefined = undefined; - const team = await xata.db.teams.createOrReplace({ id, name: 'Team ships' }); - expect(team.id).toBeDefined(); + const team = await xata.db.teams.createOrReplace({ xata_id, name: 'Team ships' }); + expect(team.xata_id).toBeDefined(); }); test('create or replace fails with empty id', async () => { - await expect(xata.db.teams.createOrReplace({ id: '', name: 'Team ships' })).rejects.toThrowError(); + await expect(xata.db.teams.createOrReplace({ xata_id: '', name: 'Team ships' })).rejects.toThrowError(); }); test('create or replace team with inline id', async () => { @@ -64,13 +64,13 @@ describe('record create or replace', () => { expect(team.read).toBeDefined(); expect(team.email).toBe('ships2@example.com'); - const replacedTeam = await xata.db.teams.createOrReplace({ id: team.id, name: 'Team boats' }); + const replacedTeam = await xata.db.teams.createOrReplace({ xata_id: team.xata_id, name: 'Team boats' }); - expect(replacedTeam.id).toBe(team.id); + expect(replacedTeam.xata_id).toBe(team.xata_id); expect(replacedTeam.read).toBeDefined(); expect(replacedTeam.email).toBeNull(); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); expect(replacedTeam.name).toBe('Team boats'); expect(apiTeam?.name).toBe('Team boats'); @@ -84,14 +84,14 @@ describe('record create or replace', () => { expect(team.email).toBe('ships3@example.com'); const replacedTeam = await xata.db.teams.createOrReplace([ - { id: team.id, name: 'Team boats' }, - { ...team, id: 'planes' } + { xata_id: team.xata_id, name: 'Team boats' }, + { ...team, xata_id: 'planes' } ]); - expect(replacedTeam[0].id).toBe(team.id); + expect(replacedTeam[0].xata_id).toBe(team.xata_id); expect(replacedTeam[0].read).toBeDefined(); expect(replacedTeam[0].email).toBeNull(); - expect(replacedTeam[1].id).toBe('planes'); + expect(replacedTeam[1].xata_id).toBe('planes'); expect(replacedTeam[1].read).toBeDefined(); expect(replacedTeam[1].email).toBe(team.email); }); diff --git a/test/integration/createOrUpdate.test.ts b/test/integration/createOrUpdate.test.ts index dc82eba7f..74718d3c6 100644 --- a/test/integration/createOrUpdate.test.ts +++ b/test/integration/createOrUpdate.test.ts @@ -30,39 +30,39 @@ describe('record create or update', () => { test('create or update single team with id', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - const updatedTeam = await xata.db.teams.createOrUpdate(team.id, { name: 'Team boats' }); + const updatedTeam = await xata.db.teams.createOrUpdate(team.xata_id, { name: 'Team boats' }); - expect(updatedTeam.id).toBe(team.id); + expect(updatedTeam.xata_id).toBe(team.xata_id); expect(updatedTeam.read).toBeDefined(); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); expect(updatedTeam.name).toBe('Team boats'); expect(apiTeam?.name).toBe('Team boats'); }); test('create or update with optional id', async () => { - const id: string | undefined = undefined; + const xata_id: string | undefined = undefined; - const team = await xata.db.teams.createOrUpdate(id, { name: 'Team ships' }); - expect(team.id).toBeDefined(); + const team = await xata.db.teams.createOrUpdate(xata_id, { name: 'Team ships' }); + expect(team.xata_id).toBeDefined(); }); test('create or update fails with empty id', async () => { - const id: string | undefined = ''; + const xata_id: string | undefined = ''; - await expect(xata.db.teams.createOrUpdate(id, { name: 'Team ships' })).rejects.toThrowError(); + await expect(xata.db.teams.createOrUpdate(xata_id, { name: 'Team ships' })).rejects.toThrowError(); }); test('create or update team with inline id', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - const updatedTeam = await xata.db.teams.createOrUpdate({ id: team.id, name: 'Team boats' }); + const updatedTeam = await xata.db.teams.createOrUpdate({ xata_id: team.xata_id, name: 'Team boats' }); - expect(updatedTeam.id).toBe(team.id); + expect(updatedTeam.xata_id).toBe(team.xata_id); expect(updatedTeam.read).toBeDefined(); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); expect(updatedTeam.name).toBe('Team boats'); expect(apiTeam?.name).toBe('Team boats'); @@ -71,17 +71,19 @@ describe('record create or update', () => { test('create or update multiple teams', async () => { const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes' }]); - const updatedTeams = await xata.db.teams.createOrUpdate(teams.map((team) => ({ id: team.id, name: 'Team boats' }))); + const updatedTeams = await xata.db.teams.createOrUpdate( + teams.map((team) => ({ xata_id: team.xata_id, name: 'Team boats' })) + ); expect(updatedTeams).toHaveLength(2); expect(updatedTeams[0].read).toBeDefined(); expect(updatedTeams[1].read).toBeDefined(); - expect(updatedTeams[0].id).toBe(teams[0].id); - expect(updatedTeams[1].id).toBe(teams[1].id); + expect(updatedTeams[0].xata_id).toBe(teams[0].xata_id); + expect(updatedTeams[1].xata_id).toBe(teams[1].xata_id); expect(updatedTeams[0].name).toBe('Team boats'); expect(updatedTeams[1].name).toBe('Team boats'); - const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ id: t.id })) }).getAll(); + const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ xata_id: t.xata_id })) }).getAll(); expect(apiTeams).toHaveLength(2); @@ -95,10 +97,10 @@ describe('record create or update', () => { }); test('create or update many without getting rate limited', async () => { - const newUsers = Array.from({ length: 250 }).map((_, i) => ({ id: `user-${i}`, full_name: `user-${i}` })); - const result = await Promise.all(newUsers.map((user) => xata.db.users.createOrUpdate(user, ['id']))); + const newUsers = Array.from({ length: 250 }).map((_, i) => ({ xata_id: `user-${i}`, full_name: `user-${i}` })); + const result = await Promise.all(newUsers.map((user) => xata.db.users.createOrUpdate(user, ['xata_id']))); expect(result).toHaveLength(250); - expect(result.every((item) => item.id)).toBeTruthy(); + expect(result.every((item) => item.xata_id)).toBeTruthy(); }, 100000); }); diff --git a/test/integration/delete.test.ts b/test/integration/delete.test.ts index 0c6918e43..174c14852 100644 --- a/test/integration/delete.test.ts +++ b/test/integration/delete.test.ts @@ -30,13 +30,13 @@ describe('record deletion', () => { test('delete single team with id', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - await xata.db.teams.delete(team.id); + await xata.db.teams.delete(team.xata_id); const copy = await team.read(); expect(copy).toBeNull(); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); expect(apiTeam).toBeNull(); }); @@ -44,17 +44,17 @@ describe('record deletion', () => { test('delete multiple teams with id list', async () => { const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes' }]); - const result = await xata.db.teams.delete(teams.map((team) => team.id)); + const result = await xata.db.teams.delete(teams.map((team) => team.xata_id)); expect(result.length).toBe(2); - expect(result[0]?.id).toBe(teams[0].id); - expect(result[1]?.id).toBe(teams[1].id); + expect(result[0]?.xata_id).toBe(teams[0].xata_id); + expect(result[1]?.xata_id).toBe(teams[1].xata_id); expect(result[0]?.read).toBeDefined(); expect(result[1]?.read).toBeDefined(); expect(result[0]?.name).toBe('Team cars'); expect(result[1]?.name).toBe('Team planes'); - const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ id: t.id })) }).getAll(); + const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ xata_id: t.xata_id })) }).getAll(); expect(apiTeams).toHaveLength(0); }); @@ -68,7 +68,7 @@ describe('record deletion', () => { expect(copy).toBeNull(); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); expect(apiTeam).toBeNull(); }); @@ -78,7 +78,7 @@ describe('record deletion', () => { await xata.db.teams.delete(teams); - const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ id: t.id })) }).getAll(); + const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ xata_id: t.xata_id })) }).getAll(); expect(apiTeams).toHaveLength(0); }); @@ -86,18 +86,18 @@ describe('record deletion', () => { test('delete multiple teams with invalid', async () => { const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes' }]); - const result = await xata.db.teams.delete([...teams, { id: 'invalid' }]); + const result = await xata.db.teams.delete([...teams, { xata_id: 'invalid' }]); expect(result.length).toBe(3); - expect(result[0]?.id).toBe(teams[0].id); - expect(result[1]?.id).toBe(teams[1].id); + expect(result[0]?.xata_id).toBe(teams[0].xata_id); + expect(result[1]?.xata_id).toBe(teams[1].xata_id); expect(result[0]?.read).toBeDefined(); expect(result[1]?.read).toBeDefined(); expect(result[0]?.name).toBe('Team cars'); expect(result[1]?.name).toBe('Team planes'); expect(result[2]).toBeNull(); - const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ id: t.id })) }).getAll(); + const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ xata_id: t.xata_id })) }).getAll(); expect(apiTeams).toHaveLength(0); }); @@ -110,7 +110,7 @@ describe('record deletion', () => { expect(copy).toBeNull(); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); expect(apiTeam).toBeNull(); }); @@ -119,14 +119,14 @@ describe('record deletion', () => { const valid = await xata.db.teams.create({ name: 'Team ships' }); const team1 = await xata.db.teams.delete('invalid'); - const team2 = await xata.db.teams.delete({ id: 'invalid', name: 'Team boats' }); - const team3 = await xata.db.teams.delete([{ id: 'invalid', name: 'Team boats' }, valid]); + const team2 = await xata.db.teams.delete({ xata_id: 'invalid', name: 'Team boats' }); + const team3 = await xata.db.teams.delete([{ xata_id: 'invalid', name: 'Team boats' }, valid]); expect(team1).toBeNull(); expect(team2).toBeNull(); expect(team3[0]).toBeNull(); expect(team3[1]).toBeDefined(); - expect(team3[1]?.id).toBe(valid.id); + expect(team3[1]?.xata_id).toBe(valid.xata_id); expect(team3[1]?.name).toBe('Team ships'); }); @@ -134,7 +134,7 @@ describe('record deletion', () => { const team = await xata.db.teams.create({ name: 'Team ships' }); const result = await xata.db.teams.delete(team); - expect(result?.id).toBe(team.id); + expect(result?.xata_id).toBe(team.xata_id); const result2 = await xata.db.teams.delete(team); expect(result2).toBeNull(); diff --git a/test/integration/files.test.ts b/test/integration/files.test.ts index 90504835f..0e91d2386 100644 --- a/test/integration/files.test.ts +++ b/test/integration/files.test.ts @@ -32,7 +32,7 @@ const json = new Blob([JSON.stringify({ hello: 'world' })], { type: 'application const csv = new Blob([['hello', 'world'].join(',')], { type: 'text/csv' }); const png = new Blob(['hello'], { type: 'image/png' }); -describe('file support', () => { +describe.skip('file support', () => { test('create file with record', async () => { const record = await xata.db.users.create( { @@ -68,7 +68,7 @@ describe('file support', () => { test('create file with binary endpoint JSON and mediaType override', async () => { const record = await xata.db.users.create({ name: 'another' }); - const file = await xata.files.upload({ table: 'users', column: 'attachments', record: record.id }, json, { + const file = await xata.files.upload({ table: 'users', column: 'attachments', record: record.xata_id }, json, { mediaType: 'text/plain' }); @@ -89,7 +89,7 @@ describe('file support', () => { test('create file with binary endpoint JSON', async () => { const record = await xata.db.users.create({ name: 'another' }); - const file = await xata.files.upload({ table: 'users', column: 'attachments', record: record.id }, json); + const file = await xata.files.upload({ table: 'users', column: 'attachments', record: record.xata_id }, json); expect(file.id).toBeDefined(); expect(file.mediaType).toBe('application/json'); @@ -108,7 +108,7 @@ describe('file support', () => { test('create file with binary endpoint CSV', async () => { const record = await xata.db.users.create({ name: 'another' }); - const file = await xata.files.upload({ table: 'users', column: 'attachments', record: record.id }, csv); + const file = await xata.files.upload({ table: 'users', column: 'attachments', record: record.xata_id }, csv); expect(file.id).toBeDefined(); expect(file.mediaType).toBe('text/csv'); @@ -128,7 +128,7 @@ describe('file support', () => { test('create XataFile on binary endpoint', async () => { const record = await xata.db.users.create({ name: 'another' }); const file = await xata.files.upload( - { table: 'users', column: 'attachments', record: record.id }, + { table: 'users', column: 'attachments', record: record.xata_id }, XataFile.fromBlob(csv) ); @@ -166,7 +166,7 @@ describe('file support', () => { expect(upload1.status).toBe(201); expect(upload2.status).toBe(201); - const user = await xata.db.users.read(result.id, [ + const user = await xata.db.users.read(result.xata_id, [ '*', 'photo.*', 'photo.base64Content', diff --git a/test/integration/json.test.ts b/test/integration/json.test.ts index 6f76e3da0..e4862a520 100644 --- a/test/integration/json.test.ts +++ b/test/integration/json.test.ts @@ -29,7 +29,7 @@ afterEach(async (ctx) => { describe('JSON support', () => { test('read returns json', async () => { const record = await xata.db.teams.create({ name: 'test', config: { hello: 'world' } }); - const read = await xata.db.teams.read(record.id, ['config']); + const read = await xata.db.teams.read(record.xata_id, ['config']); expect(read?.config.hello).toBe('world'); }); @@ -47,7 +47,7 @@ describe('JSON support', () => { expect(record.config.hello).toBe('world'); - await xata.db.teams.delete(record.id); + await xata.db.teams.delete(record.xata_id); }); test('create file with JSON as string', async () => { @@ -55,7 +55,7 @@ describe('JSON support', () => { expect(record.config.hello).toBe('world'); - await xata.db.teams.delete(record.id); + await xata.db.teams.delete(record.xata_id); }); test('create file with JSON array as object', async () => { @@ -63,7 +63,7 @@ describe('JSON support', () => { expect(record.config[0].hello[0]).toBe('world'); - await xata.db.teams.delete(record.id); + await xata.db.teams.delete(record.xata_id); }); test('create file with JSON array as string', async () => { @@ -71,7 +71,7 @@ describe('JSON support', () => { expect(record.config[0].hello[0]).toBe('world'); - await xata.db.teams.delete(record.id); + await xata.db.teams.delete(record.xata_id); }); test('filters work with JSON fields', async () => { @@ -86,7 +86,8 @@ describe('JSON support', () => { bg: { path: 'a/b/c', alpha: 0.8 - } + }, + themes: ['dark'] } }); @@ -101,7 +102,8 @@ describe('JSON support', () => { bg: { path: 'a/b/c', alpha: 0.2 - } + }, + themes: ['light'] } }); @@ -112,28 +114,29 @@ describe('JSON support', () => { bg: { path: 'a/b/c', alpha: 0.2 - } + }, + themes: ['light'] }) }) .getAll(); expect(filterEquals.length).toBe(1); - expect(filterEquals[0].id).toBe(r2.id); + expect(filterEquals[0].xata_id).toBe(r2.xata_id); const filterNodeEqualsString = await xata.db.teams.filter('config->bg->path', 'a/b/c').getAll(); expect(filterNodeEqualsString.length).toBe(2); const filterNodeEqualsNumber = await xata.db.teams.filter('config->bg->alpha', 0.8).getAll(); expect(filterNodeEqualsNumber.length).toBe(1); - expect(filterNodeEqualsNumber[0].id).toBe(r1.id); + expect(filterNodeEqualsNumber[0].xata_id).toBe(r1.xata_id); const filterNodeGreaterThan = await xata.db.teams.filter('config->bg->alpha', { $gt: 0.5 }).getAll(); expect(filterNodeGreaterThan.length).toBe(1); - expect(filterNodeGreaterThan[0].id).toBe(r1.id); + expect(filterNodeGreaterThan[0].xata_id).toBe(r1.xata_id); const filterNodeLessThan = await xata.db.teams.filter('config->bg->alpha', { $lt: 0.5 }).getAll(); expect(filterNodeLessThan.length).toBe(1); - expect(filterNodeLessThan[0].id).toBe(r2.id); + expect(filterNodeLessThan[0].xata_id).toBe(r2.xata_id); const filterNodeEqualsNumberNotFound = await xata.db.teams.filter('config->bg->alpha', 1).getAll(); expect(filterNodeEqualsNumberNotFound.length).toBe(0); @@ -144,6 +147,15 @@ describe('JSON support', () => { const filterNodeIsNot = await xata.db.teams.filter({ 'config->bg->alpha': { $isNot: 0.8 } }).getAll(); expect(filterNodeIsNot.length).toBe(1); + const filterWithArrayIndex = await xata.db.teams.filter({ 'config->themes->0': { $isNot: 'dark' } }).getAll(); + expect(filterWithArrayIndex.length).toBe(1); + + const filterWithArray = await xata.db.teams.filter({ 'config->themes': { $contains: 'dark' } }).getAll(); + expect(filterWithArray.length).toBe(1); + + const filterWithArray2 = await xata.db.teams.filter({ 'config->themes': { $is: '["dark"]' } }).getAll(); + expect(filterWithArray2.length).toBe(1); + const filterNodeContains = await xata.db.teams.filter({ 'config->bg->path': { $contains: 'a/b' } }).getAll(); expect(filterNodeContains.length).toBe(2); expect(filterNodeContains.map((r) => r.name).sort()).toEqual(['r1', 'r2']); @@ -212,15 +224,15 @@ describe('JSON support', () => { const recordsBySizeM = await xata.db.teams.filter({ 'config->size': 'M' }).getMany(); expect(recordsBySizeM.length).toBe(1); - expect(recordsBySizeM[0].id).toBe(record1.id); + expect(recordsBySizeM[0].xata_id).toBe(record1.xata_id); const recordsLengthGreater = await xata.db.teams.filter({ 'config->length': { $gt: 50 } }).getMany(); expect(recordsLengthGreater.length).toBe(1); - expect(recordsLengthGreater[0].id).toBe(record3.id); + expect(recordsLengthGreater[0].xata_id).toBe(record3.xata_id); const recordsBySubstring = await xata.db.teams.filter({ 'config->isbn': { $contains: '0140449334' } }).getMany(); expect(recordsBySubstring.length).toBe(1); - expect(recordsBySubstring[0].id).toBe(record2.id); + expect(recordsBySubstring[0].xata_id).toBe(record2.xata_id); const recordsWithNegationOperator = await xata.db.teams.filter({ 'config->color': { $isNot: 'yellow' } }).getMany(); expect(recordsWithNegationOperator.length).toBe(2); diff --git a/test/integration/primaryKey.test.ts b/test/integration/primaryKey.test.ts new file mode 100644 index 000000000..9526544af --- /dev/null +++ b/test/integration/primaryKey.test.ts @@ -0,0 +1,33 @@ +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from 'vitest'; +import { XataClient } from '../../packages/codegen/example/xata'; +import { setUpTestEnvironment, TestEnvironmentResult } from '../utils/setup'; + +let xata: XataClient; +let hooks: TestEnvironmentResult['hooks']; + +beforeAll(async (ctx) => { + const result = await setUpTestEnvironment('create'); + + xata = result.client; + hooks = result.hooks; + + return hooks.beforeAll(ctx); +}); + +afterAll(async (ctx) => { + await hooks.afterAll(ctx); +}); + +beforeEach(async (ctx) => { + await hooks.beforeEach(ctx); +}); + +afterEach(async (ctx) => { + await hooks.afterEach(ctx); +}); + +describe.skip('record CRUD', () => { + test('create', async () => { + const user = await xata.db.int_primary_key.read('1'); + }); +}); diff --git a/test/integration/query.test.ts b/test/integration/query.test.ts index a1bb91d08..bc8866822 100644 --- a/test/integration/query.test.ts +++ b/test/integration/query.test.ts @@ -2,10 +2,10 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } fr import { BaseClient, contains, + endsWith, iContains, includesAll, includesNone, - isXataRecord, lt, Repository, XataApiClient, @@ -42,13 +42,14 @@ beforeAll(async (ctx) => { await hooks.beforeAll(ctx); - const { id: ownerAnimalsId } = await xata.db.users.create(ownerAnimals); - const { id: ownerFruitsId } = await xata.db.users.create(ownerFruits); + const { xata_id: ownerAnimalsId } = await xata.db.users.create(ownerAnimals); + const { xata_id: ownerFruitsId } = await xata.db.users.create(ownerFruits); const fruitsTeam = await xata.db.teams.create({ name: 'Team fruits', labels: ['apple', 'banana', 'orange'], - owner: ownerFruitsId + owner: ownerFruitsId, + index: 1 }); const animalsTeam = await xata.db.teams.create({ @@ -166,6 +167,348 @@ describe('integration tests', () => { await xata.db.teams.delete(teams); }); + test('endsWith multiple conditions filter', async () => { + const teams = await xata.db.teams + .filter({ + name: { $endsWith: '& animals' }, + labels: { $includes: ['banananot'] } + }) + .getAll(); + expect(teams).toHaveLength(0); + }); + + test('$anyWithMultipleValues filter', async () => { + const teams = await xata.db.teams + .filter({ + name: { $any: ['Mixed team fruits & animals', 'Team animals'] } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('explicitAnyWithFilterList', async () => { + const teams = await xata.db.teams + .filter({ + $any: [ + { + name: 'Mixed team fruits & animals' + }, + { + name: 'Team animals' + } + ] + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('explicitAnyWithFilterObject', async () => { + const teams = await xata.db.teams + .filter({ + $any: { + name: 'Mixed team fruits & animals', + labels: { $includes: ['other'] } + } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('explicitAllWithFilterList', async () => { + const teams = await xata.db.teams + .filter({ + $all: { + name: 'Mixed team fruits & animals', + labels: { $includes: ['banana'] } + } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('nestWithNot', async () => { + const teams = await xata.db.teams + .filter({ + $not: { + name: 'Mixed team fruits & animals', + labels: { $includes: ['banana'] } + } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('explicitAllWithFilterList', async () => { + const teams = await xata.db.teams + .filter({ + $all: [ + { + name: 'Mixed team fruits & animals' + }, + { + labels: { $includes: ['banananot'] } + } + ] + }) + .getAll(); + expect(teams).toHaveLength(0); + }); + + test('rangeQueryWithLessFirst', async () => { + const teams = await xata.db.teams + .filter({ + index: { + $lt: 2, + $ge: 1 + } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('rangeQueryWithLessFirst', async () => { + const teams = await xata.db.teams + .filter({ + index: { + $lt: 2, + $gt: 1 + } + }) + .getAll(); + expect(teams).toHaveLength(0); + }); + + test('simpleIncludes', async () => { + const teams = await xata.db.teams + .filter({ + labels: { $includes: 'eagle' } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('simpleIncludesAny', async () => { + const teams = await xata.db.teams.filter({ labels: { $includesAny: 'eagle' } }).getAll(); + expect(teams).toHaveLength(1); + }); + + test('simpleIncludesAny', async () => { + const teams = await xata.db.teams + .filter({ + labels: { $includesAll: 'eagle' } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('simpleIncludesNone', async () => { + const teams = await xata.db.teams + .filter({ + labels: { $includesNone: 'eagle' } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('filterByOneColumn', async () => { + const teams = await xata.db.teams + .filter({ + name: 'Team fruits' + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('filterWithTheIsOperator', async () => { + const teams = await xata.db.teams + .filter({ + name: { $is: 'Team fruits' } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('filterWithAnyOperation', async () => { + const teams = await xata.db.teams + .filter({ + name: { $any: ['Team fruits', 'Team animals'] } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('filterWithExistsOperation', async () => { + const teams = await xata.db.teams + .filter({ + $exists: 'name' + }) + .getAll(); + expect(teams).toHaveLength(3); + }); + + test('filterWithExistsAndAndAnyOperations', async () => { + const teams = await xata.db.teams + .filter({ + $all: [{ $exists: 'name' }, { $exists: 'labels' }] + }) + .getAll(); + expect(teams).toHaveLength(3); + }); + + test('filterWithNotExistsOperation', async () => { + const teams = await xata.db.teams + .filter({ + $notExists: 'name' + }) + .getAll(); + expect(teams).toHaveLength(0); + }); + + test('filterWithPatternOperator', async () => { + const teams = await xata.db.teams + .filter({ + name: { $pattern: 'T*' } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('filterWithIPatterOperator', async () => { + const teams = await xata.db.teams + .filter({ + name: { $iPattern: 't*' } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('startsWith and endsWith', async () => { + const teams = await xata.db.teams + .filter({ + name: { $startsWith: 'Team', $endsWith: 'fruits' } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('not', async () => { + const teams = await xata.db.teams + .filter({ + $not: { name: 'Team fruits' } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('filterWithComplexNegations', async () => { + const teams = await xata.db.teams + .filter({ + $not: { $any: [{ name: 'Team fruits' }, { name: 'r2' }] } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('filterOnInternalColumnIsAllowed', async () => { + const teams = await xata.db.teams + .filter({ + xata_version: { $is: 0 } + }) + .getAll(); + expect(teams).toHaveLength(3); + }); + + test('simpleIncludesMultipleOpAndValue', async () => { + const teams = await xata.db.teams + .filter({ + labels: { $includes: [{ $contains: 'eag' }, 'eagle'] } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('includesWithManyComparisons', async () => { + const teams = await xata.db.teams + .filter({ + labels: { $includes: { $all: [{ $contains: 'monkey' }, { $contains: 'eagle' }] } } + }) + .getAll(); + expect(teams).toHaveLength(1); + }); + + test('includesWithModeAndArrayOfFilters', async () => { + const teams = await xata.db.teams + .filter({ + labels: { $includesNone: [{ $contains: 'eagle' }, 'abc', { $endsWith: 'bad' }] } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('includesWithMixOfAnyAndAllInPredicatePosition', async () => { + const teams = await xata.db.teams + .filter({ + labels: { $includes: { $any: { $all: [{ $startsWith: 'test' }, { $contains: 'x' }], $any: ['a', 'b'] } } } + }) + .getAll(); + expect(teams).toHaveLength(0); + }); + + test('filterWithArraysComplexNegations', async () => { + const teams = await xata.db.teams + .filter({ + labels: { + $includes: { + $all: [{ $contains: 'mon' }, { $not: { $endsWith: 'keyo' } }] + } + } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('filtersWithIncludesAll', async () => { + const teams = await xata.db.teams + .filter({ + labels: { + $includesAll: [{ $contains: 'mon' }] + } + }) + .getAll(); + expect(teams).toHaveLength(2); + }); + + test('endsWith filter', async () => { + const teams = await xata.db.teams.filter('name', endsWith('& animals')).getAll(); + expect(teams).toHaveLength(1); + expect(teams[0].name).toBe('Mixed team fruits & animals'); + }); + + test('$exists filter', async () => { + const teams = await xata.db.teams.filter({ $exists: 'name' }).getAll(); + expect(teams).toHaveLength(3); + }); + + test('$notExists filter', async () => { + const teams = await xata.db.teams.filter({ $notExists: 'name' }).getAll(); + expect(teams).toHaveLength(0); + }); + + test('$pattern filter', async () => { + const teams = await xata.db.teams.filter({ name: { $pattern: 'Mixed team fruits & *nimal?' } }).getAll(); + expect(teams).toHaveLength(1); + expect(teams[0].name).toBe('Mixed team fruits & animals'); + + const teams2 = await xata.db.teams.filter({ name: { $pattern: 'mixed team fruits & *nimal?' } }).getAll(); + expect(teams2).toHaveLength(0); + }); + + test('$iPattern filter', async () => { + const teams = await xata.db.teams.filter({ name: { $iPattern: 'mixed team fruits & *nimal?' } }).getAll(); + expect(teams).toHaveLength(1); + expect(teams[0].name).toBe('Mixed team fruits & animals'); + }); + test('multiple filter', async () => { const teams = await xata.db.teams.filter('name', contains('fruits')).filter('name', contains('Mixed')).getAll(); @@ -249,9 +592,9 @@ describe('integration tests', () => { if (!ownerAnimals) throw new Error('Could not find owner of team animals'); // Regression test on filtering on nullable property - const team = await xata.db.teams.filter('owner.id', ownerAnimals.id).getFirst(); + const team = await xata.db.teams.filter('owner', ownerAnimals.xata_id).getFirst(); - expect(team?.owner?.id).toEqual(ownerAnimals.id); + expect(team?.owner).toEqual(ownerAnimals.xata_id); }); test('filter on object', async () => { @@ -268,13 +611,6 @@ describe('integration tests', () => { expect(users[0].full_name).toBe('Owner of team fruits'); }); - test('filter on link', async () => { - const teams = await xata.db.teams.filter({ owner: { full_name: 'Owner of team fruits' } }).getAll(); - - expect(teams).toHaveLength(1); - expect(teams[0].name).toBe('Team fruits'); - }); - test('filter returns nothing', async () => { const teams = await xata.db.teams.filter('name', 'Not even possible').getAll(); @@ -317,7 +653,7 @@ describe('integration tests', () => { }); test('returns many records extended array map converts to a normal array', async () => { - const records1 = await xata.db.users.filter('team.name', 'Team fruits').getMany(); + const records1 = await xata.db.teams.filter('name', 'Team fruits').getMany(); const records2 = records1.map((item) => ({ ...item })); expect(records1.length).toBeGreaterThan(0); @@ -431,7 +767,7 @@ describe('integration tests', () => { test('get all users', async () => { const users = await xata.db.users.getAll(); expect(users).toHaveLength(mockUsers.length); - expect(users[0].id).toBeDefined(); + expect(users[0].xata_id).toBeDefined(); }); test('get first', async () => { @@ -440,11 +776,11 @@ describe('integration tests', () => { expect(user).toBeDefined(); expect(definedUser).toBeDefined(); - expect(user?.id).toBe(definedUser.id); + expect(user?.xata_id).toBe(definedUser.xata_id); }); test('get first not found', async () => { - const query = xata.db.users.filter('id', 'not-found'); + const query = xata.db.users.filter('xata_id', 'not-found'); const user = await query.getFirst(); @@ -479,7 +815,7 @@ describe('integration tests', () => { const user = await xata.db.users.select(['full_name']).getFirst(); expect(user).toBeDefined(); - expect(user?.id).toBeDefined(); + expect(user?.xata_id).toBeDefined(); expect(user?.full_name).toBeDefined(); //@ts-expect-error expect(user?.email).not.toBeDefined(); @@ -491,7 +827,7 @@ describe('integration tests', () => { }); expect(user).toBeDefined(); - expect(user?.id).toBeDefined(); + expect(user?.xata_id).toBeDefined(); expect(user?.full_name).toBeDefined(); expect(user?.email).toBeDefined(); }); @@ -503,10 +839,10 @@ describe('integration tests', () => { street: '123 Main St' }); - const records = await xata.db.users.filter('id', user.id).select(['*', 'team.*']).getAll(); + const records = await xata.db.users.filter('xata_id', user.xata_id).select(['*', 'team.*']).getAll(); expect(records).toHaveLength(1); - expect(records[0].id).toBe(user.id); + expect(records[0].xata_id).toBe(user.xata_id); expect(records[0].full_name).toBe('John Doe'); expect(records[0].street).toBe('123 Main St'); expect(records[0].team).toBeNull(); @@ -521,14 +857,14 @@ describe('integration tests', () => { street: '123 Main St' }); - const updatedUserResponse = await xata.db.users.update(user.id, { street: 'New street', zipcode: 11 }); + const updatedUserResponse = await xata.db.users.update(user.xata_id, { street: 'New street', zipcode: 11 }); - const updatedUser = await xata.db.users.filter({ id: user.id }).getFirst(); + const updatedUser = await xata.db.users.filter({ xata_id: user.xata_id }).getFirst(); if (!updatedUser) throw new Error('No user found'); await user.delete(); - expect(user.id).toBe(updatedUser.id); + expect(user.xata_id).toBe(updatedUser.xata_id); expect(user.street).toBe('123 Main St'); expect(user.zipcode).toBeNull(); @@ -550,7 +886,7 @@ describe('integration tests', () => { const updatedUserResponse = await user.update({ street: 'New street 2', zipcode: 22 }); - const updatedUser = await xata.db.users.filter({ id: user.id }).getFirst(); + const updatedUser = await xata.db.users.filter({ xata_id: user.xata_id }).getFirst(); if (!updatedUser) throw new Error('No user found'); await user.delete(); @@ -572,15 +908,15 @@ describe('integration tests', () => { email: 'john6@doe.com' }); - const apiUser = await xata.db.users.filter({ id: user.id }).getFirst(); + const apiUser = await xata.db.users.filter({ xata_id: user.xata_id }).getFirst(); if (!apiUser) throw new Error('No user found'); await user.delete(); - expect(user.id).toBe('my-good-old-john-6'); + expect(user.xata_id).toBe('my-good-old-john-6'); expect(user.full_name).toBe('John Doe 6'); - expect(user.id).toBe(apiUser.id); + expect(user.xata_id).toBe(apiUser.xata_id); expect(user.full_name).toBe(apiUser.full_name); expect(user.email).toBe(apiUser.email); }); @@ -610,18 +946,10 @@ describe('integration tests', () => { }); test('Pagination default value', async () => { - await api.table.createTable({ - pathParams: { workspace, region, dbBranchName: `${database}:main`, tableName: 'planes' } - }); - await api.table.setTableSchema({ - pathParams: { workspace, region, dbBranchName: `${database}:main`, tableName: 'planes' }, - body: { columns: [{ name: 'name', type: 'string' }] } - }); - const planes = Array.from({ length: PAGINATION_DEFAULT_SIZE + 50 }, (_, index) => ({ name: `Plane ${index}` })); - const createdPlanes = await baseClient.db.planes.create(planes); - const queriedPlanes = await baseClient.db.planes.getPaginated(); + const createdPlanes = await xata.db.users.create(planes); + const queriedPlanes = await xata.db.users.filter({ name: { $startsWith: 'Plane' } }).getPaginated(); expect(createdPlanes).toHaveLength(PAGINATION_DEFAULT_SIZE + 50); expect(queriedPlanes.records).toHaveLength(PAGINATION_DEFAULT_SIZE); @@ -629,126 +957,39 @@ describe('integration tests', () => { test('multiple errors in one response', async () => { const invalidUsers = [{ full_name: 'a name' }, { full_name: 1 }, { full_name: 2 }] as UsersRecord[]; - expect(xata.db.users.create(invalidUsers)).rejects.toHaveProperty('status', 400); }); - test('Link is a record object', async () => { - const user = await xata.db.users.create({ - full_name: 'Base User' - }); - - const team = await xata.db.teams.create({ - name: 'Base team', - owner: user - }); - - await user.update({ team }); - - const updatedUser = await user.read(); - expect(updatedUser?.team?.id).toEqual(team.id); - - // TODO(link.xata) @ts-expect-error - expect(updatedUser?.team?.xata?.version).not.toBeDefined(); - // TODO(link.xata) @ts-expect-error - expect(updatedUser?.team?.xata?.createdAt).not.toBeDefined(); - // TODO(link.xata) @ts-expect-error - expect(updatedUser?.team?.xata?.updatedAt).not.toBeDefined(); - - const response = await xata.db.teams.getFirst({ filter: { id: team.id }, columns: ['*', 'owner.*'] }); - const owner = await response?.owner?.read(); - - expect(response?.owner?.id).toBeDefined(); - expect(response?.owner?.full_name).toBeDefined(); - - expect(owner?.id).toBeDefined(); - expect(owner?.full_name).toBeDefined(); - - expect(response?.owner?.id).toBe(owner?.id); - expect(response?.owner?.full_name).toBe(owner?.full_name); - - const teamMetadata = response?.owner?.getMetadata(); - expect(teamMetadata?.createdAt).toBeInstanceOf(Date); - expect(teamMetadata?.updatedAt).toBeInstanceOf(Date); - expect(teamMetadata?.version).toBe(1); - - expect(response?.owner?.xata?.createdAt).toBeInstanceOf(Date); - expect(response?.owner?.xata?.updatedAt).toBeInstanceOf(Date); - expect(response?.owner?.xata?.version).toBe(1); - - const nestedObject = await xata.db.teams.getFirst({ - filter: { id: team.id }, - columns: ['owner.team', 'owner.full_name'] - }); - - const nestedProperty = nestedObject?.owner?.team; - const nestedName = nestedObject?.owner?.full_name; - - expect(nestedName).toEqual(user.full_name); - - expect(isXataRecord(nestedProperty)).toBe(true); - expect(nestedProperty?.name).toEqual(team.name); - // @ts-expect-error - expect(nestedProperty?.owner?.full_name).not.toBeDefined(); - - const nestedRead = await nestedProperty?.owner?.read(); - - expect(nestedRead?.id).toBeDefined(); - expect(nestedRead?.full_name).toEqual(user.full_name); - }); - - test('Update link with linked object', async () => { - const owner = await xata.db.users.create({ full_name: 'Example User' }); - const owner2 = await xata.db.users.create({ full_name: 'Example User 2' }); - - const team = await xata.db.teams.create({ name: 'Example Team', owner }); - const updated = await team.update({ owner: owner2 }); - - expect(team.owner?.id).toEqual(owner.id); - expect(updated?.owner?.id).toEqual(owner2.id); - }); - - test('Update link with linked object (string)', async () => { - const owner = await xata.db.users.create({ full_name: 'Example User' }); - const owner2 = await xata.db.users.create({ full_name: 'Example User 2' }); - - const team = await xata.db.teams.create({ name: 'Example Team', owner: owner.id }); - const updated = await team.update({ owner: owner2.id }); - - expect(team.owner?.id).toEqual(owner.id); - expect(updated?.owner?.id).toEqual(owner2.id); - }); - test('Filter with null value', async () => { const newOwner = await xata.db.users.create({ full_name: 'Example User' }); const newTeam = await xata.db.teams.create({ name: 'Example Team', owner: newOwner }); - const owner = await xata.db.users.filter({ id: newOwner.id }).getFirst(); + const owner = await xata.db.users.filter({ xata_id: newOwner.xata_id }).getFirst(); if (!owner) throw new Error('No user found'); - const team = await xata.db.teams.filter({ owner }).getFirst(); - expect(team?.id).toEqual(newTeam.id); + const team = await xata.db.teams.filter({ owner: owner.xata_id }).getFirst(); + expect(team?.xata_id).toEqual(newTeam.xata_id); }); test('Filter with multiple column', async () => { const newTeam = await xata.db.teams.create({ name: 'Example Team', labels: ['a', 'b'] }); const team = await xata.db.teams.filter({ labels: newTeam.labels }).getFirst(); - expect(team?.id).toEqual(newTeam.id); + expect(team?.xata_id).toEqual(newTeam.xata_id); }); test('Partial filters should work', async () => { const newTeam = await xata.db.teams.create({ name: 'A random real team', labels: ['a', 'b'] }); const maybeId = undefined; - const records = await xata.db.teams.filter({ id: maybeId, name: newTeam.name }).getMany(); + const records = await xata.db.teams.filter({ xata_id: maybeId, name: newTeam.name }).getMany(); expect(records).toHaveLength(1); - expect(records[0].id).toEqual(newTeam.id); + expect(records[0].xata_id).toEqual(newTeam.xata_id); const serialized = records.toSerializable(); expect(serialized).toHaveLength(1); - expect(serialized[0].id).toEqual(newTeam.id); + expect(serialized[0].xata_id).toEqual(newTeam.xata_id); const string = records.toString(); expect(string).toContain('A random real team'); diff --git a/test/integration/read.test.ts b/test/integration/read.test.ts index f3ce676b9..41f9d5db5 100644 --- a/test/integration/read.test.ts +++ b/test/integration/read.test.ts @@ -30,16 +30,16 @@ describe('record read', () => { test('read single team with id', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - const copy = await xata.db.teams.read(team.id); - const definedCopy = await xata.db.teams.readOrThrow(team.id); + const copy = await xata.db.teams.read(team.xata_id); + const definedCopy = await xata.db.teams.readOrThrow(team.xata_id); expect(copy).toBeDefined(); - expect(copy?.id).toBe(team.id); - expect(copy?.xata.createdAt).toBeInstanceOf(Date); + expect(copy?.xata_id).toBe(team.xata_id); + expect(copy?.xata_createdat).toBeInstanceOf(Date); expect(definedCopy).toBeDefined(); - expect(definedCopy.id).toBe(team.id); - expect(definedCopy.xata.createdAt).toBeInstanceOf(Date); + expect(definedCopy.xata_id).toBe(team.xata_id); + expect(definedCopy.xata_createdat).toBeInstanceOf(Date); }); test('read multiple teams ', async () => { @@ -49,27 +49,27 @@ describe('record read', () => { const definedCopies = await xata.db.teams.readOrThrow(teams); expect(copies).toHaveLength(2); - expect(copies[0]?.id).toBe(teams[0].id); - expect(copies[1]?.id).toBe(teams[1].id); + expect(copies[0]?.xata_id).toBe(teams[0].xata_id); + expect(copies[1]?.xata_id).toBe(teams[1].xata_id); expect(definedCopies).toHaveLength(2); - expect(definedCopies[0].id).toBe(teams[0].id); - expect(definedCopies[1].id).toBe(teams[1].id); + expect(definedCopies[0].xata_id).toBe(teams[0].xata_id); + expect(definedCopies[1].xata_id).toBe(teams[1].xata_id); }); test('read multiple teams with id list', async () => { const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes' }]); - const copies = await xata.db.teams.read(teams.map((team) => team.id)); - const definedCopies = await xata.db.teams.readOrThrow(teams.map((team) => team.id)); + const copies = await xata.db.teams.read(teams.map((team) => team.xata_id)); + const definedCopies = await xata.db.teams.readOrThrow(teams.map((team) => team.xata_id)); expect(copies).toHaveLength(2); - expect(copies[0]?.id).toBe(teams[0].id); - expect(copies[1]?.id).toBe(teams[1].id); + expect(copies[0]?.xata_id).toBe(teams[0].xata_id); + expect(copies[1]?.xata_id).toBe(teams[1].xata_id); expect(definedCopies).toHaveLength(2); - expect(definedCopies[0].id).toBe(teams[0].id); - expect(definedCopies[1].id).toBe(teams[1].id); + expect(definedCopies[0].xata_id).toBe(teams[0].xata_id); + expect(definedCopies[1].xata_id).toBe(teams[1].xata_id); }); test("read single and return null if team doesn't exist", async () => { @@ -84,17 +84,17 @@ describe('record read', () => { test("read multiple teams with id list and ignores a team if doesn't exist", async () => { const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes' }]); - const copies = await xata.db.teams.read(teams.map((team) => team.id).concat(['does-not-exist'])); + const copies = await xata.db.teams.read(teams.map((team) => team.xata_id).concat(['does-not-exist'])); expect(copies).toHaveLength(3); - expect(copies[0]?.id).toBe(teams[0].id); - expect(copies[1]?.id).toBe(teams[1].id); + expect(copies[0]?.xata_id).toBe(teams[0].xata_id); + expect(copies[1]?.xata_id).toBe(teams[1].xata_id); expect(copies[2]).toBeNull(); }); test("read multiple teams with id list and throws if a team doesn't exist", async () => { const teams = await xata.db.teams.create([{ name: 'Team cars' }, { name: 'Team planes' }]); - expect(xata.db.teams.readOrThrow(teams.map((team) => team.id).concat(['does-not-exist']))).rejects.toThrow(); + expect(xata.db.teams.readOrThrow(teams.map((team) => team.xata_id).concat(['does-not-exist']))).rejects.toThrow(); }); test('read multiple with empty array', async () => { @@ -136,22 +136,16 @@ describe('record read', () => { test('read with columns', async () => { const owner = await xata.db.users.create({ full_name: 'John', street: 'Newark' }); - const team = await xata.db.teams.create({ name: 'Team ships', labels: ['foo', 'bar'], owner }); + const team = await xata.db.teams.create({ name: 'Team ships', labels: ['foo', 'bar'], owner: owner.xata_id }); - const copy = await xata.db.teams.read(team.id, ['id', 'name', 'owner.street']); + const copy = await xata.db.teams.read(team.xata_id, ['xata_id', 'name', 'owner']); expect(copy).toBeDefined(); - expect(copy?.id).toBe(team.id); + expect(copy?.xata_id).toBe(team.xata_id); expect(copy?.name).toBe(team.name); // @ts-expect-error expect(copy?.labels).not.toBeDefined(); expect(copy?.owner).toBeDefined(); - expect(copy?.owner?.id).toBe(owner.id); - expect(copy?.owner?.street).toBe(owner.street); - // @ts-expect-error - expect(copy?.owner?.city).not.toBeDefined(); - // @ts-expect-error - expect(copy?.owner?.full_name).not.toBeDefined(); }); test('replace team with record method', async () => { @@ -162,7 +156,7 @@ describe('record read', () => { const replacedTeam = await team.replace({ name: 'Team boats' }); - expect(replacedTeam?.id).toBe(team.id); + expect(replacedTeam?.xata_id).toBe(team.xata_id); expect(replacedTeam?.read).toBeDefined(); expect(replacedTeam?.email).toBeNull(); }); diff --git a/test/integration/revlinks.test.ts b/test/integration/revlinks.test.ts deleted file mode 100644 index dd29a3f31..000000000 --- a/test/integration/revlinks.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from 'vitest'; -import { XataClient } from '../../packages/codegen/example/xata'; -import { TestEnvironmentResult, setUpTestEnvironment } from '../utils/setup'; - -let xata: XataClient; -let hooks: TestEnvironmentResult['hooks']; - -beforeAll(async (ctx) => { - const result = await setUpTestEnvironment('revlinks'); - - xata = result.client; - hooks = result.hooks; - - return hooks.beforeAll(ctx); -}); - -afterAll(async (ctx) => { - await hooks.afterAll(ctx); -}); - -beforeEach(async (ctx) => { - await hooks.beforeEach(ctx); -}); - -afterEach(async (ctx) => { - await hooks.afterEach(ctx); -}); - -describe('Revlinks', () => { - test('create user and team and link them', async () => { - const user = await xata.db.users.create({ name: 'test' }); - const team = await xata.db.teams.create({ name: 'test', owner: user }); - - expect(team.owner?.id).toBe(user.id); - - const records = await xata.db.users - .select([ - '*', - { - name: '<-teams.owner', - as: 'ownerTeams', - sort: [{ name: 'desc' }], - columns: ['name'], - limit: 10 - } - ]) - .getAll(); - - expect(records).toHaveLength(1); - expect(records[0]?.ownerTeams?.records).toHaveLength(1); - expect(records[0]?.ownerTeams?.records[0]?.name).toBe(team.name); - - await xata.db.users.delete(user.id); - await xata.db.teams.delete(team.id); - }); -}); diff --git a/test/integration/search.test.ts b/test/integration/search.test.ts index a22677bdb..ef51b8f3b 100644 --- a/test/integration/search.test.ts +++ b/test/integration/search.test.ts @@ -28,12 +28,12 @@ beforeAll(async (ctx) => { { name: 'Team fruits', labels: ['apple', 'banana', 'orange'], - owner: ownerFruits + owner: ownerFruits as any }, { name: 'Team animals', labels: ['monkey', 'lion', 'eagle', 'dolphin'], - owner: ownerAnimals + owner: ownerAnimals as any }, { name: 'Mixed team fruits & animals', @@ -67,11 +67,11 @@ describe( expect(records.length).toBeGreaterThan(0); expect(records.length).toBe(2); - expect(records[0].id).toBeDefined(); + expect(records[0].xata_id).toBeDefined(); expect(records[0].full_name?.includes('Owner')).toBeTruthy(); expect(records[0].read).toBeDefined(); - expect(records[0].getMetadata().score).toBeDefined(); - expect(records[0].getMetadata().table).toBe('users'); + expect(records[0].xata_score).toBeDefined(); + expect(records[0].xata_table).toBe('users'); }); test('search in table with filtering', async () => { @@ -81,10 +81,10 @@ describe( expect(totalCount).toBe(1); expect(records.length).toBe(1); - expect(records[0].id).toBeDefined(); + expect(records[0].xata_id).toBeDefined(); expect(records[0].full_name?.includes('Owner of team animals')).toBeTruthy(); expect(records[0].read).toBeDefined(); - expect(records[0].getMetadata().score).toBeDefined(); + expect(records[0].xata_score).toBeDefined(); }); test('search by tables with multiple tables', async () => { @@ -97,15 +97,15 @@ describe( expect(users.length).toBeGreaterThan(0); expect(teams.length).toBeGreaterThan(0); - expect(users[0].id).toBeDefined(); + expect(users[0].xata_id).toBeDefined(); expect(users[0].read).toBeDefined(); expect(users[0].full_name?.includes('fruits')).toBeTruthy(); - expect(users[0].getMetadata().score).toBeDefined(); + expect(users[0].xata_score).toBeDefined(); - expect(teams[0].id).toBeDefined(); + expect(teams[0].xata_id).toBeDefined(); expect(teams[0].read).toBeDefined(); expect(teams[0].name?.includes('fruits')).toBeTruthy(); - expect(users[0].getMetadata().score).toBeDefined(); + expect(users[0].xata_score).toBeDefined(); }); test('search by table with all tables', async () => { @@ -118,15 +118,15 @@ describe( expect(users.length).toBeGreaterThan(0); expect(teams.length).toBeGreaterThan(0); - expect(users[0].id).toBeDefined(); + expect(users[0].xata_id).toBeDefined(); expect(users[0].read).toBeDefined(); expect(users[0].full_name?.includes('fruits')).toBeTruthy(); - expect(users[0].getMetadata().score).toBeDefined(); + expect(users[0].xata_score).toBeDefined(); - expect(teams[0].id).toBeDefined(); + expect(teams[0].xata_id).toBeDefined(); expect(teams[0].read).toBeDefined(); expect(teams[0].name?.includes('fruits')).toBeTruthy(); - expect(teams[0].getMetadata().score).toBeDefined(); + expect(teams[0].xata_score).toBeDefined(); }); test('search all with multiple tables', async () => { @@ -136,17 +136,17 @@ describe( expect(totalCount).toBeGreaterThan(0); for (const result of records) { if (result.table === 'teams') { - expect(result.record.id).toBeDefined(); + expect(result.record.xata_id).toBeDefined(); expect(result.record.read).toBeDefined(); expect(result.record.name?.includes('fruits')).toBeTruthy(); - expect(result.record.getMetadata().score).toBeDefined(); - expect(result.record.getMetadata().table).toBe('teams'); + expect(result.record.xata_score).toBeDefined(); + expect(result.record.xata_table).toBe('teams'); } else { - expect(result.record.id).toBeDefined(); + expect(result.record.xata_id).toBeDefined(); expect(result.record.read).toBeDefined(); expect(result.record.full_name?.includes('fruits')).toBeTruthy(); - expect(result.record.getMetadata().table).toBe('users'); - expect(result.record.getMetadata().score).toBeDefined(); + expect(result.record.xata_table).toBe('users'); + expect(result.record.xata_score).toBeDefined(); } } }); @@ -157,10 +157,10 @@ describe( expect(totalCount).toBeGreaterThan(0); for (const result of records) { - expect(result.record.id).toBeDefined(); + expect(result.record.xata_id).toBeDefined(); expect(result.record.read).toBeDefined(); expect(result.record.name?.includes('fruits')).toBeTruthy(); - expect(result.record.getMetadata().score).toBeDefined(); + expect(result.record.xata_score).toBeDefined(); //@ts-expect-error result.table === 'users'; @@ -174,20 +174,20 @@ describe( expect(totalCount).toBeGreaterThan(0); for (const result of records) { if (result.table === 'teams') { - expect(result.record.id).toBeDefined(); + expect(result.record.xata_id).toBeDefined(); expect(result.record.read).toBeDefined(); expect(result.record.name?.includes('fruits')).toBeTruthy(); - expect(result.record.getMetadata().score).toBeDefined(); + expect(result.record.xata_score).toBeDefined(); } else if (result.table === 'users') { - expect(result.record.id).toBeDefined(); + expect(result.record.xata_id).toBeDefined(); expect(result.record.read).toBeDefined(); expect(result.record.full_name?.includes('fruits')).toBeTruthy(); - expect(result.record.getMetadata().score).toBeDefined(); + expect(result.record.xata_score).toBeDefined(); } else if (result.table === 'pets') { - expect(result.record.id).toBeDefined(); + expect(result.record.xata_id).toBeDefined(); expect(result.record.read).toBeDefined(); expect(result.record.name?.includes('fruits')).toBeTruthy(); - expect(result.record.getMetadata().score).toBeDefined(); + expect(result.record.xata_score).toBeDefined(); } } }); @@ -203,11 +203,11 @@ describe( expect(records[0].table).toBe('teams'); if (records[0].table === 'teams') { - expect(records[0].record.id).toBeDefined(); + expect(records[0].record.xata_id).toBeDefined(); expect(records[0].record.read).toBeDefined(); expect(records[0].record.name?.includes('fruits')).toBeTruthy(); - expect(records[0].record.getMetadata().score).toBeDefined(); - expect(records[0].record.xata.highlight).toBeDefined(); + expect(records[0].record.xata_score).toBeDefined(); + expect(records[0].record.xata_highlight).toBeDefined(); } }); @@ -224,10 +224,10 @@ describe( expect(page1.length).toBe(1); expect(page2.length).toBe(1); - expect(page1[0].id).not.toBe(page2[0].id); + expect(page1[0].xata_id).not.toBe(page2[0].xata_id); - expect(page1[0].id).toBe(owners[0].id); - expect(page2[0].id).toBe(owners[1].id); + expect(page1[0].xata_id).toBe(owners[0].xata_id); + expect(page2[0].xata_id).toBe(owners[1].xata_id); }); test('global search with page and offset', async () => { @@ -252,10 +252,10 @@ describe( expect(page1.length).toBe(1); expect(page2.length).toBe(1); - expect(page1[0].id).not.toBe(page2[0].id); + expect(page1[0].xata_id).not.toBe(page2[0].xata_id); - expect(page1[0].id).toBe(owners[0].id); - expect(page2[0].id).toBe(owners[1].id); + expect(page1[0].xata_id).toBe(owners[0].xata_id); + expect(page2[0].xata_id).toBe(owners[1].xata_id); }); }, { retry: 5 } diff --git a/test/integration/selection.test.ts b/test/integration/selection.test.ts new file mode 100644 index 000000000..d668f7488 --- /dev/null +++ b/test/integration/selection.test.ts @@ -0,0 +1,121 @@ +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from 'vitest'; +import { BaseClient, XataApiClient } from '../../packages/client/src'; +import { XataClient } from '../../packages/codegen/example/xata'; +import { animalUsers, fruitUsers, ownerAnimals, ownerFruits } from '../mock_data'; +import { setUpTestEnvironment, TestEnvironmentResult } from '../utils/setup'; + +let xata: XataClient; +let api: XataApiClient; +let baseClient: BaseClient; +let workspace: string; +let region: string; +let database: string; +let hooks: TestEnvironmentResult['hooks']; + +beforeAll(async (ctx) => { + const result = await setUpTestEnvironment('query'); + + xata = result.client; + api = result.api; + baseClient = result.baseClient; + workspace = result.workspace; + region = result.region; + database = result.database; + hooks = result.hooks; + + await hooks.beforeAll(ctx); + + const { xata_id: ownerAnimalsId } = await xata.db.users.create(ownerAnimals); + const { xata_id: ownerFruitsId } = await xata.db.users.create(ownerFruits); + + const fruitsTeam = await xata.db.teams.create({ + name: 'Team fruits', + labels: ['apple', 'banana', 'orange'], + owner: ownerFruitsId, + index: 1 + }); + + const animalsTeam = await xata.db.teams.create({ + name: 'Team animals', + labels: ['monkey', 'lion', 'eagle', 'dolphin'], + owner: ownerAnimalsId + }); + + await xata.db.teams.create({ + name: 'Mixed team fruits & animals', + labels: ['monkey', 'banana', 'apple', 'dolphin'] + }); + + await xata.db.users.create([ + ...animalUsers.map((item) => ({ ...item, team: animalsTeam })), + ...fruitUsers.map((item) => ({ ...item, team: fruitsTeam })) + ]); +}); + +afterAll(async (ctx) => { + await hooks.afterAll(ctx); +}); + +beforeEach(async (ctx) => { + await hooks.beforeEach(ctx); +}); + +afterEach(async (ctx) => { + await hooks.afterEach(ctx); +}); + +describe('integration tests', () => { + test('foreign keys not expanded by default', async () => { + const teams = await xata.db.teams.filter('name', 'Team fruits').getFirst({ + columns: ['owner', 'xata_id'] + }); + expect(teams).toBeDefined(); + expect(teams?.owner).toBeDefined(); + expect(teams?.owner.full_name).toBeUndefined(); + expect(teams?.owner.email).toBeUndefined(); + expect(teams?.xata_id).toBeDefined(); + }); + test('foreign keys brought back with .*', async () => { + const teams = await xata.db.teams.filter('name', 'Team fruits').getFirst({ + columns: ['owner.*', 'xata_id'] + }); + expect(teams).toBeDefined(); + expect(teams?.owner).toBeDefined(); + expect(teams?.owner.full_name).toBeDefined(); + expect(teams?.owner.email).toBeDefined(); + expect(teams?.xata_id).toBeDefined(); + }); + // TODO just name of foreign key should not be possible in types + test('foreign keys are null objects if no record matches', async () => { + const teams = await xata.db.teams.filter('name', 'Team fruits').getFirst({ + columns: ['pet', 'xata_id'] + }); + expect(teams).toBeDefined(); + expect(teams?.pet).toBeNull(); + + const teams2 = await xata.db.teams.filter('name', 'Team fruits').getFirst({ + columns: ['pet.xata_id', 'xata_id'] + }); + expect(teams2).toBeDefined(); + expect(teams2?.pet).toBeNull(); + expect(teams2?.pet?.xata_id).toBeUndefined(); + const teams3 = await xata.db.teams.filter('name', 'Team fruits').getFirst({ + columns: ['pet.*', 'xata_id'] + }); + expect(teams3).toBeDefined(); + expect(teams3?.pet).toBeNull(); + }); + test('specific foreign key fields are fetched', async () => { + const teams = await xata.db.teams.filter('name', 'Team fruits').getFirst({ + columns: ['owner.full_name', 'xata_id', 'owner.email', 'pet.*'] + }); + expect(teams).toBeDefined(); + expect(teams?.owner).toBeDefined(); + expect(teams?.owner.full_name).toBeDefined(); + expect(teams?.owner.email).toBeDefined(); + expect(teams?.owner.xata_id).toBeDefined(); + expect(teams?.xata_id).toBeDefined(); + expect(teams?.pet).toBeNull(); + }); + test.todo('combination of * and .field'); +}); diff --git a/test/integration/smoke.test.ts b/test/integration/smoke.test.ts index f7f2d59c6..eba0ea561 100644 --- a/test/integration/smoke.test.ts +++ b/test/integration/smoke.test.ts @@ -15,45 +15,28 @@ if (host === null) { const api = new XataApiClient({ apiKey: process.env.XATA_API_KEY, host }); const region = process.env.XATA_REGION || 'eu-west-1'; +const clusterId = process.env.XATA_CLUSTER_ID ?? 'shared-cluster'; +const hash = Math.random().toString(36).substr(2, 9); -const getWorkspaceName = () => `sdk-integration-api-client-${Math.random().toString(36).substr(2, 9)}`; +// For shared-cluster, we create a new workspace with a unique name +// while in dedicated clusters, we use the provided workspace name +const workspaceName = clusterId === 'shared-cluster' ? `sdk-smoke-${hash}` : process.env.XATA_WORKSPACE; +if (!workspaceName) throw new Error('XATA_WORKSPACE environment variable is not set'); describe('API Client Integration Tests', () => { test('Create, get and delete workspace with new apiKey', async () => { - const workspaceName = getWorkspaceName(); - - const newApiKey = await api.authentication.createUserAPIKey({ pathParams: { keyName: `${workspaceName}-key` } }); + const newApiKey = await api.authentication.createUserAPIKey({ pathParams: { keyName: `smoke-${hash}-key` } }); expect(newApiKey).toBeDefined(); - expect(newApiKey.name).toBe(`${workspaceName}-key`); + expect(newApiKey.name).toBe(`smoke-${hash}-key`); expect(newApiKey.key).toBeDefined(); + const workspace = await getOrCreateWorkspace(workspaceName); const newApi = new XataApiClient({ apiKey: newApiKey.key, host }); - const { id: workspace, name } = await newApi.workspaces.createWorkspace({ - body: { name: workspaceName, slug: `${workspaceName}-slug` } - }); - - await waitForReplication(newApi, workspace); - - expect(workspace).toBeDefined(); - expect(name).toBe(workspaceName); - - console.log('Created workspace', workspace); - - const foo = await newApi.workspaces.getWorkspace({ pathParams: { workspaceId: workspace } }); - - expect(foo.id).toBe(workspace); - expect(foo.slug).toBe(`${workspaceName}-slug`); - - const bar = await newApi.workspaces.getWorkspace({ pathParams: { workspaceId: workspace } }); - - expect(bar.id).toBe(workspace); - expect(bar.slug).toBe(`${workspaceName}-slug`); - const { databaseName: database } = await newApi.databases.createDatabase({ - pathParams: { workspaceId: workspace, dbName: `data-${workspace}` }, - body: { region } + pathParams: { workspaceId: workspace, dbName: `data-${workspace}-${hash}` }, + body: { region, defaultClusterID: clusterId } }); await waitForReplication(newApi, workspace, database); @@ -73,18 +56,21 @@ describe('API Client Integration Tests', () => { console.log('Created branch, table and schema'); - const { id } = await newApi.records.insertRecord({ + const response = await newApi.records.insertRecord({ pathParams: { workspace, region, dbBranchName: `${database}:branch`, tableName: 'table' }, body: { email: 'example@foo.bar' } }); + // @ts-expect-error Remove this once pgroll is normalized + const id = response.xata_id; + console.log('Created record', id); const record = await newApi.records.getRecord({ pathParams: { workspace, region, dbBranchName: `${database}:branch`, tableName: 'table', recordId: id } }); - expect(record.id).toBeDefined(); + expect(record.xata_id).toBeDefined(); expect(record.email).toEqual('example@foo.bar'); await waitForSearchIndexing(newApi, workspace, database); @@ -95,7 +81,7 @@ describe('API Client Integration Tests', () => { }); expect(search.totalCount).toEqual(1); - expect(search.records[0].id).toEqual(id); + expect(search.records[0].xata_id).toEqual(id); const failedSearch = await newApi.searchAndFilter.searchTable({ pathParams: { workspace, region, dbBranchName: `${database}:branch`, tableName: 'table' }, @@ -119,14 +105,35 @@ describe('API Client Integration Tests', () => { console.log('Deleted API key, record is no longer accessible'); - await api.workspaces.deleteWorkspace({ pathParams: { workspaceId: workspace } }); + if (clusterId === 'shared-cluster') { + await api.workspaces.deleteWorkspace({ pathParams: { workspaceId: workspace } }); - await expect(api.workspaces.getWorkspace({ pathParams: { workspaceId: workspace } })).rejects.toHaveProperty( - 'message' - ); + await expect(api.workspaces.getWorkspace({ pathParams: { workspaceId: workspace } })).rejects.toHaveProperty( + 'message' + ); + } }); }); +async function getOrCreateWorkspace(workspaceName: string): Promise { + if (clusterId === 'shared-cluster') { + const { id: workspace, name } = await api.workspaces.createWorkspace({ + body: { name: workspaceName, slug: `${workspaceName}-slug` } + }); + + await waitForReplication(api, workspace); + + expect(workspace).toBeDefined(); + expect(name).toBe(workspaceName); + + console.log('Created workspace', workspace); + + return workspace; + } + + return workspaceName; +} + async function waitForReplication(api: XataApiClient, workspace: string, database?: string): Promise { try { if (database === undefined) { diff --git a/test/integration/sql.test.ts b/test/integration/sql.test.ts index 60f105f41..6eb13d261 100644 --- a/test/integration/sql.test.ts +++ b/test/integration/sql.test.ts @@ -30,29 +30,14 @@ describe('SQL proxy', () => { test.skip('read single team with id', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - const { records, warning, columns } = await xata.sql`SELECT * FROM teams WHERE id = ${team.id}`; + const { records, warning, columns } = + await xata.sql`SELECT * FROM teams WHERE xata_id = ${team.xata_id}`; expect(warning).toBeUndefined(); expect(records).toHaveLength(1); expect(columns).toMatchInlineSnapshot(` [ - { - "name": "id", - "type": "text", - }, - { - "name": "xata.version", - "type": "int4", - }, - { - "name": "xata.createdAt", - "type": "timestamptz", - }, - { - "name": "xata.updatedAt", - "type": "timestamptz", - }, { "name": "name", "type": "text", @@ -67,7 +52,7 @@ describe('SQL proxy', () => { }, { "name": "index", - "type": "int8", + "type": "int4", }, { "name": "rating", @@ -93,6 +78,22 @@ describe('SQL proxy', () => { "name": "config", "type": "jsonb", }, + { + "name": "xata_id", + "type": "text", + }, + { + "name": "xata_version", + "type": "int4", + }, + { + "name": "xata_createdat", + "type": "timestamptz", + }, + { + "name": "xata_updatedat", + "type": "timestamptz", + }, { "name": "owner", "type": "text", @@ -100,7 +101,7 @@ describe('SQL proxy', () => { ] `); - expect(records[0].id).toBe(team.id); + expect(records[0].xata_id).toBe(team.xata_id); expect(records[0].name).toBe('Team ships'); }); @@ -114,22 +115,6 @@ describe('SQL proxy', () => { expect(columns).toMatchInlineSnapshot(` [ - { - "name": "id", - "type": "text", - }, - { - "name": "xata.version", - "type": "int4", - }, - { - "name": "xata.createdAt", - "type": "timestamptz", - }, - { - "name": "xata.updatedAt", - "type": "timestamptz", - }, { "name": "name", "type": "text", @@ -144,7 +129,7 @@ describe('SQL proxy', () => { }, { "name": "index", - "type": "int8", + "type": "int4", }, { "name": "rating", @@ -170,6 +155,22 @@ describe('SQL proxy', () => { "name": "config", "type": "jsonb", }, + { + "name": "xata_id", + "type": "text", + }, + { + "name": "xata_version", + "type": "int4", + }, + { + "name": "xata_createdat", + "type": "timestamptz", + }, + { + "name": "xata_updatedat", + "type": "timestamptz", + }, { "name": "owner", "type": "text", @@ -177,8 +178,8 @@ describe('SQL proxy', () => { ] `); - const record1 = records.find((record) => record.id === teams[0].id); - const record2 = records.find((record) => record.id === teams[1].id); + const record1 = records.find((record) => record.xata_id === teams[0].xata_id); + const record2 = records.find((record) => record.xata_id === teams[1].xata_id); expect(record1).toBeDefined(); expect(record1?.name).toBe('[A] Cars'); @@ -188,28 +189,12 @@ describe('SQL proxy', () => { test.skip('create team', async () => { const { records, warning, columns } = await xata.sql({ - statement: `INSERT INTO teams (name) VALUES ($1) RETURNING *`, - params: ['Team ships 2'] + statement: `INSERT INTO teams (xata_id, name) VALUES ($1, $2) RETURNING *`, + params: ['my-id', 'Team ships 2'] }); expect(columns).toMatchInlineSnapshot(` [ - { - "name": "id", - "type": "text", - }, - { - "name": "xata.version", - "type": "int4", - }, - { - "name": "xata.createdAt", - "type": "timestamptz", - }, - { - "name": "xata.updatedAt", - "type": "timestamptz", - }, { "name": "name", "type": "text", @@ -224,7 +209,7 @@ describe('SQL proxy', () => { }, { "name": "index", - "type": "int8", + "type": "int4", }, { "name": "rating", @@ -250,6 +235,22 @@ describe('SQL proxy', () => { "name": "config", "type": "jsonb", }, + { + "name": "xata_id", + "type": "text", + }, + { + "name": "xata_version", + "type": "int4", + }, + { + "name": "xata_createdat", + "type": "timestamptz", + }, + { + "name": "xata_updatedat", + "type": "timestamptz", + }, { "name": "owner", "type": "text", @@ -261,7 +262,7 @@ describe('SQL proxy', () => { expect(records).toHaveLength(1); expect(records[0].name).toBe('Team ships 2'); - const team = await xata.db.teams.read(records[0].id); + const team = await xata.db.teams.read(records[0].xata_id); expect(team).toBeDefined(); expect(team?.name).toBe('Team ships 2'); }); diff --git a/test/integration/summarize.test.ts b/test/integration/summarize.test.ts index cc999ee37..0538d05f7 100644 --- a/test/integration/summarize.test.ts +++ b/test/integration/summarize.test.ts @@ -27,11 +27,11 @@ beforeAll(async (ctx) => { rating: 10.5, plan: 'paid', dark: true, - pet: pet1.id, + pet: pet1.xata_id, account_value: 5 }, - { full_name: 'B', name: 'B', index: 10, rating: 10.5, plan: 'free', pet: pet2.id, account_value: 3 }, - { full_name: 'C', name: 'C', index: 30, rating: 40.0, plan: 'paid', pet: pet3.id } + { full_name: 'B', name: 'B', index: 10, rating: 10.5, plan: 'free', pet: pet2.xata_id, account_value: 3 }, + { full_name: 'C', name: 'C', index: 30, rating: 40.0, plan: 'paid', pet: pet3.xata_id } ]); }); @@ -426,7 +426,7 @@ describe('summarize', () => { const result = await xata.db.users.summarize({ columns: ['name'], summaries: { total: { count: '*' } }, - filter: { id: 'nomatches' } + filter: { xata_id: 'nomatches' } }); expect(result.summaries).toMatchInlineSnapshot('[]'); @@ -440,7 +440,7 @@ describe('summarize', () => { const result = await xata.db.users.summarize({ columns: ['name'], summaries: { total: { count: '*' } }, - filter: { id: user1?.id ?? '' } + filter: { xata_id: user1?.xata_id ?? '' } }); expect(result.summaries).toMatchInlineSnapshot(` diff --git a/test/integration/transactions.test.ts b/test/integration/transactions.test.ts deleted file mode 100644 index 71cf03456..000000000 --- a/test/integration/transactions.test.ts +++ /dev/null @@ -1,344 +0,0 @@ -import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from 'vitest'; -import { XataClient } from '../../packages/codegen/example/xata'; -import { setUpTestEnvironment, TestEnvironmentResult } from '../utils/setup'; - -let xata: XataClient; -let hooks: TestEnvironmentResult['hooks']; - -beforeAll(async (ctx) => { - const result = await setUpTestEnvironment('transactions'); - - xata = result.client; - hooks = result.hooks; - - return hooks.beforeAll(ctx); -}); - -afterAll(async (ctx) => { - await hooks.afterAll(ctx); -}); - -beforeEach(async (ctx) => { - await hooks.beforeEach(ctx); -}); - -afterEach(async (ctx) => { - await hooks.afterEach(ctx); -}); - -describe('insert transactions', () => { - test('do nothing if body contains no operations', async () => { - const response = await xata.transactions.run([]); - - expect(response.results).toEqual([]); - }); - - test('insert a record', async () => { - const response = await xata.transactions.run([{ insert: { table: 'teams', record: { name: 'a' } } }]); - - expect(response.results).toEqual([{ operation: 'insert', id: expect.any(String), rows: 1 }]); - - await xata.db.teams.delete({ id: response.results[0]?.id }); - }); - - test('insert by ID', async () => { - const response = await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'a' } } }]); - - expect(response.results).toEqual([{ operation: 'insert', id: 'i0', rows: 1, columns: {} }]); - - await xata.db.teams.delete({ id: 'i0' }); - }); - - test('insert with createOnly and explicit ID', async () => { - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i0', name: 'a' }, createOnly: true } } - ]); - - expect(response.results).toEqual([{ operation: 'insert', id: 'i0', rows: 1 }]); - - await xata.db.teams.delete({ id: 'i0' }); - }); - - test('replace existing record if createOnly is unset', async () => { - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }]); - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i0', name: 'b', index: 1 } } } - ]); - - expect(response.results).toEqual([{ operation: 'insert', id: 'i0', rows: 1, columns: {} }]); - - await xata.db.teams.delete({ id: 'i0' }); - }); - - test('replace existing record if createOnly is false', async () => { - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }]); - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i0', name: 'b', index: 1 }, createOnly: false } } - ]); - - expect(response.results).toEqual([{ operation: 'insert', id: 'i0', rows: 1, columns: {} }]); - - await xata.db.teams.delete({ id: 'i0' }); - }); - - test('replace when ifVersion set', async () => { - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }]); - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'b', index: 1 } } }]); - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i0', name: 'c', index: 2 }, ifVersion: 1 } } - ]); - - expect(response.results).toEqual([{ operation: 'insert', id: 'i0', rows: 1, columns: {} }]); - - await xata.db.teams.delete({ id: 'i0' }); - }); - - test('mix of operations', async () => { - await xata.transactions.run([{ insert: { table: 'users', record: { id: 'j0', full_name: 'z', index: 0 } } }]); - - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { name: 'a', index: 0 } } }, - { insert: { table: 'users', record: { id: 'j1', full_name: 'b', index: 1 } } }, - { insert: { table: 'teams', record: { id: 'i1', name: 'b', index: 1 }, createOnly: true } }, - { insert: { table: 'users', record: { id: 'j0', full_name: 'replaced', index: 2 }, ifVersion: 0 } } - ]); - - expect(response.results).toEqual([ - { operation: 'insert', id: expect.any(String), rows: 1 }, - { operation: 'insert', id: 'j1', rows: 1, columns: {} }, - { operation: 'insert', id: 'i1', rows: 1 }, - { operation: 'insert', id: 'j0', rows: 1, columns: {} } - ]); - - await xata.db.teams.delete({ id: response.results[0]?.id }); - await xata.db.teams.delete({ id: 'i1' }); - await xata.db.users.delete({ id: 'j1' }); - await xata.db.users.delete({ id: 'j0' }); - }); -}); - -describe('update transactions', () => { - test('update records', async () => { - await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 1 } } }, - { insert: { table: 'teams', record: { id: 'i1', name: 'b', index: 10 } } }, - { insert: { table: 'teams', record: { id: 'i2', name: 'c', index: 100 } } } - ]); - - const response = await xata.transactions.run([ - { update: { table: 'teams', id: 'i0', fields: { name: 'a1' } } }, - { update: { table: 'teams', id: 'i1', fields: { name: 'b1' } } }, - { update: { table: 'teams', id: 'i2', fields: { name: 'c1' } } }, - { update: { table: 'teams', id: 'i2', fields: { name: 'c1.1' } } } - ]); - - expect(response.results).toEqual([ - { operation: 'update', id: 'i0', rows: 1, columns: {} }, - { operation: 'update', id: 'i1', rows: 1, columns: {} }, - { operation: 'update', id: 'i2', rows: 1, columns: {} }, - { operation: 'update', id: 'i2', rows: 1, columns: {} } - ]); - - const records = await xata.db.teams.read(['i0', 'i1', 'i2']); - expect(records[0]?.name).toEqual('a1'); - expect(records[1]?.name).toEqual('b1'); - expect(records[2]?.name).toEqual('c1.1'); - - await xata.db.teams.delete(['i0', 'i1', 'i2']); - }); - - test('update ifVersion', async () => { - await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }, - { insert: { table: 'teams', record: { id: 'i0', name: 'b', index: 1 } } } - ]); - - const response = await xata.transactions.run([ - { update: { table: 'teams', id: 'i0', fields: { name: 'c', index: 2 }, ifVersion: 1 } } - ]); - - expect(response.results).toEqual([{ operation: 'update', id: 'i0', rows: 1, columns: {} }]); - - const record = await xata.db.teams.read('i0'); - expect(record?.name).toEqual('c'); - expect(record?.index).toEqual(2); - - await xata.db.teams.delete('i0'); - }); - - test('update an insert from same tx', async () => { - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'b', index: 0 } } }]); - - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i1', name: 'b', index: 1 } } }, - { update: { table: 'teams', id: 'i0', fields: { name: 'c', index: 2 } } }, - { update: { table: 'teams', id: 'i1', fields: { name: 'd', index: 3 } } } - ]); - - expect(response.results).toEqual([ - { operation: 'insert', id: 'i1', rows: 1, columns: {} }, - { operation: 'update', id: 'i0', rows: 1, columns: {} }, - { operation: 'update', id: 'i1', rows: 1, columns: {} } - ]); - - const records = await xata.db.teams.read(['i0', 'i1']); - expect(records[0]?.name).toEqual('c'); - expect(records[0]?.index).toEqual(2); - expect(records[1]?.name).toEqual('d'); - expect(records[1]?.index).toEqual(3); - - await xata.db.teams.delete(['i0', 'i1']); - }); - - test('upsert should insert record if it does not exist', async () => { - const response = await xata.transactions.run([ - { update: { table: 'teams', id: 'i0', fields: { name: 'a', index: 0 }, upsert: true } } - ]); - - expect(response.results).toEqual([{ operation: 'update', id: 'i0', rows: 1, columns: {} }]); - - const record = await xata.db.teams.read('i0'); - expect(record?.name).toEqual('a'); - expect(record?.index).toEqual(0); - - await xata.db.teams.delete('i0'); - }); - - test('upsert should update record if it already exists', async () => { - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }]); - - const response = await xata.transactions.run([ - { update: { table: 'teams', id: 'i0', fields: { name: 'b', index: 1 }, upsert: true } } - ]); - - expect(response.results).toEqual([{ operation: 'update', id: 'i0', rows: 1, columns: {} }]); - - const record = await xata.db.teams.read('i0'); - expect(record?.name).toEqual('b'); - expect(record?.index).toEqual(1); - - await xata.db.teams.delete('i0'); - }); - - test('upsert with ifVersion', async () => { - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }]); - // update i0 to version 1 - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'b', index: 1 } } }]); - - const response = await xata.transactions.run([ - { update: { table: 'teams', id: 'i0', fields: { name: 'c' }, upsert: true, ifVersion: 1 } } - ]); - - expect(response.results).toEqual([{ operation: 'update', id: 'i0', rows: 1, columns: {} }]); - - const record = await xata.db.teams.read('i0'); - expect(record?.name).toEqual('c'); - expect(record?.index).toEqual(1); - - await xata.db.teams.delete('i0'); - }); -}); - -describe('delete transactions', () => { - test('delete a record', async () => { - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }]); - - const response = await xata.transactions.run([{ delete: { table: 'teams', id: 'i0' } }]); - - expect(response.results).toEqual([{ operation: 'delete', rows: 1 }]); - - const record = await xata.db.teams.read('i0'); - expect(record).toBeNull(); - }); - - test('delete a record from same transaction', async () => { - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }, - { delete: { table: 'teams', id: 'i0' } } - ]); - - expect(response.results).toEqual([ - { operation: 'insert', id: 'i0', rows: 1, columns: {} }, - { operation: 'delete', rows: 1 } - ]); - - const record = await xata.db.teams.read('i0'); - expect(record).toBeNull(); - }); - - test('delete that affects no records does not abort transaction', async () => { - await xata.transactions.run([{ insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }]); - - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i1', name: 'b', index: 1 } } }, - { delete: { table: 'teams', id: 'i2' } } - ]); - - expect(response.results).toEqual([ - { operation: 'insert', id: 'i1', rows: 1, columns: {} }, - { operation: 'delete', rows: 0 } - ]); - - const record = await xata.db.teams.read('i1'); - expect(record?.name).toEqual('b'); - expect(record?.index).toEqual(1); - - await xata.db.teams.delete('i1'); - }); - - test('delete with failIfMissing', async () => { - const records = await xata.transactions.run([{ delete: { table: 'teams', id: 'ab', failIfMissing: false } }]); - - expect(records).toEqual({ results: [{ operation: 'delete', rows: 0 }] }); - - try { - await xata.transactions.run([{ delete: { table: 'teams', id: 'ab', failIfMissing: true } }]); - } catch (error: any) { - expect(error.errors).toEqual([{ index: 0, message: 'table [teams]: no rows deleted' }]); - return; - } - - throw new Error('should not reach here'); - }); -}); - -describe('combined transactions', () => { - test('insert, update, delete', async () => { - await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i0', name: 'a', index: 0 } } }, - { insert: { table: 'teams', record: { id: 'i1', name: 'b', index: 1 } } }, - { insert: { table: 'teams', record: { id: 'i2', name: 'c', index: 2 } } } - ]); - - const response = await xata.transactions.run([ - { insert: { table: 'teams', record: { id: 'i3', name: 'd', index: 3 } } }, - { update: { table: 'teams', id: 'i0', fields: { name: 'a1' } } }, - { update: { table: 'teams', id: 'i1', fields: { name: 'b1' } } }, - { update: { table: 'teams', id: 'i2', fields: { name: 'c1' } } }, - { update: { table: 'teams', id: 'i2', fields: { name: 'c1.1' } } }, - { delete: { table: 'teams', id: 'i3' } }, - { get: { table: 'teams', id: 'i0', columns: ['id', 'index', 'name'] } }, - { get: { table: 'teams', id: 'i1', columns: ['id', 'index', 'name'] } }, - { get: { table: 'teams', id: 'i2', columns: ['id', 'index', 'name'] } } - ]); - - expect(response.results).toEqual([ - { operation: 'insert', id: 'i3', rows: 1, columns: {} }, - { operation: 'update', id: 'i0', rows: 1, columns: {} }, - { operation: 'update', id: 'i1', rows: 1, columns: {} }, - { operation: 'update', id: 'i2', rows: 1, columns: {} }, - { operation: 'update', id: 'i2', rows: 1, columns: {} }, - { operation: 'delete', rows: 1 }, - { operation: 'get', columns: { id: 'i0', name: 'a1', index: 0 } }, - { operation: 'get', columns: { id: 'i1', name: 'b1', index: 1 } }, - { operation: 'get', columns: { id: 'i2', name: 'c1.1', index: 2 } } - ]); - - const records = await xata.db.teams.read(['i0', 'i1', 'i2']); - expect(records[0]?.name).toEqual('a1'); - expect(records[1]?.name).toEqual('b1'); - expect(records[2]?.name).toEqual('c1.1'); - - await xata.db.teams.delete(['i0', 'i1', 'i2']); - }); -}); diff --git a/test/integration/update.test.ts b/test/integration/update.test.ts index 80747e46f..394ef3369 100644 --- a/test/integration/update.test.ts +++ b/test/integration/update.test.ts @@ -30,11 +30,11 @@ describe('record update', () => { test('update single team', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - const updatedTeam = await xata.db.teams.update(team.id, { name: 'Team boats' }); + const updatedTeam = await xata.db.teams.update(team.xata_id, { name: 'Team boats' }); - expect(updatedTeam?.id).toBe(team.id); + expect(updatedTeam?.xata_id).toBe(team.xata_id); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); if (!apiTeam) throw new Error('No team found'); expect(updatedTeam?.name).toBe('Team boats'); @@ -48,7 +48,7 @@ describe('record update', () => { expect(updatedTeams).toHaveLength(2); - const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ id: t.id })) }).getAll(); + const apiTeams = await xata.db.teams.filter({ $any: teams.map((t) => ({ xata_id: t.xata_id })) }).getAll(); expect(apiTeams).toHaveLength(2); expect(apiTeams[0].name).toBe('Team boats'); @@ -58,11 +58,11 @@ describe('record update', () => { test('update team with inline id', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - const updatedTeam = await xata.db.teams.update({ id: team.id, name: 'Team boats' }); + const updatedTeam = await xata.db.teams.update({ xata_id: team.xata_id, name: 'Team boats' }); - expect(updatedTeam?.id).toBe(team.id); + expect(updatedTeam?.xata_id).toBe(team.xata_id); - const apiTeam = await xata.db.teams.filter({ id: team.id }).getFirst(); + const apiTeam = await xata.db.teams.filter({ xata_id: team.xata_id }).getFirst(); expect(updatedTeam?.name).toBe('Team boats'); expect(apiTeam?.name).toBe('Team boats'); @@ -77,92 +77,67 @@ describe('record update', () => { const valid = await xata.db.teams.create({ name: 'Team ships' }); const team1 = await xata.db.teams.update('invalid', { name: 'Team boats' }); - const team2 = await xata.db.teams.update({ id: 'invalid', name: 'Team boats' }); + const team2 = await xata.db.teams.update({ xata_id: 'invalid', name: 'Team boats' }); const team3 = await xata.db.teams.update([ - { id: 'invalid', name: 'Team boats' }, - { id: valid.id, name: 'Team boats 2' } + { xata_id: 'invalid', name: 'Team boats' }, + { xata_id: valid.xata_id, name: 'Team boats 2' } ]); expect(team1).toBeNull(); expect(team2).toBeNull(); expect(team3[0]).toBeNull(); expect(team3[1]).toBeDefined(); - expect(team3[1]?.id).toBe(valid.id); + expect(team3[1]?.xata_id).toBe(valid.xata_id); expect(team3[1]?.name).toBe('Team boats 2'); }); - test('update item with if version', async () => { - const team = await xata.db.teams.create({ name: 'Team ships' }); - const { version: versionA } = team.getMetadata(); - - const updatedTeam = await xata.db.teams.update(team.id, { name: 'Team boats' }, { ifVersion: versionA }); - const { version: versionB } = updatedTeam?.getMetadata() || {}; - - expect(updatedTeam?.id).toBe(team.id); - expect(versionB).toBe(versionA + 1); - - const updatedTeam2 = await xata.db.teams.update(team.id, { name: 'Team planes' }, { ifVersion: versionA }); - const { version: versionC } = updatedTeam2?.getMetadata() || {}; - - expect(updatedTeam2).toBeNull(); - expect(versionC).toBe(undefined); - - const updatedTeam3 = await team.update({ name: 'Team cars' }, { ifVersion: versionA }); - const { version: versionD } = updatedTeam3?.getMetadata() || {}; - - expect(updatedTeam3).toBeNull(); - expect(versionD).toBe(undefined); - - expect(xata.db.teams.updateOrThrow(team.id, { name: 'Team cars' }, { ifVersion: versionA })).rejects.toThrow(); - }); - test('update item with id column', async () => { const team = await xata.db.teams.create({ name: 'Team ships' }); - const update1 = await xata.db.teams.update(team.id, { name: 'Team boats' }); + const update1 = await xata.db.teams.update(team.xata_id, { name: 'Team boats' }); - expect(update1?.id).toBe(team.id); + expect(update1?.xata_id).toBe(team.xata_id); expect(update1?.name).toBe('Team boats'); - const update2 = await xata.db.teams.update({ id: team.id, name: 'Team planes' }); + const update2 = await xata.db.teams.update({ xata_id: team.xata_id, name: 'Team planes' }); - expect(update2?.id).toBe(team.id); + expect(update2?.xata_id).toBe(team.xata_id); expect(update2?.name).toBe('Team planes'); - const update3 = await xata.db.teams.update([{ id: team.id, name: 'Team cars' }]); + const update3 = await xata.db.teams.update([{ xata_id: team.xata_id, name: 'Team cars' }]); - expect(update3[0]?.id).toBe(team.id); + expect(update3[0]?.xata_id).toBe(team.xata_id); expect(update3[0]?.name).toBe('Team cars'); const update4 = await update1?.update({ name: 'Team trains' }); - expect(update4?.id).toBe(team.id); + expect(update4?.xata_id).toBe(team.xata_id); expect(update4?.name).toBe('Team trains'); - const update5 = await update1?.update({ id: update1?.id, name: 'Team boats' }); + const update5 = await update1?.update({ xata_id: update1?.xata_id, name: 'Team boats' }); - expect(update5?.id).toBe(team.id); + expect(update5?.xata_id).toBe(team.xata_id); expect(update5?.name).toBe('Team boats'); const copy = await update2?.read(); - expect(copy?.id).toBe(team.id); + expect(copy?.xata_id).toBe(team.xata_id); expect(copy?.name).toBe('Team boats'); }); test('update with numeric operations', async () => { const pet = await xata.db.pets.create({ name: 'Pet', num_legs: 1 }); - const update1 = await xata.db.pets.update(pet.id, { num_legs: { $increment: 3 } }); + const update1 = await xata.db.pets.update(pet.xata_id, { num_legs: { $increment: 3 } }); expect(update1?.num_legs).toBe(4); - const update2 = await xata.db.pets.update({ id: pet.id, num_legs: { $divide: 2 } }); + const update2 = await xata.db.pets.update({ xata_id: pet.xata_id, num_legs: { $divide: 2 } }); expect(update2?.num_legs).toBe(2); - const update3 = await xata.db.pets.update([{ id: pet.id, num_legs: { $multiply: 2 } }]); + const update3 = await xata.db.pets.update([{ xata_id: pet.xata_id, num_legs: { $multiply: 2 } }]); expect(update3[0]?.num_legs).toBe(4); - const update4 = await xata.db.pets.update(pet.id, { num_legs: { $decrement: 4 } }); + const update4 = await xata.db.pets.update(pet.xata_id, { num_legs: { $decrement: 4 } }); expect(update4?.num_legs).toBe(0); }); }); diff --git a/test/mock_data.ts b/test/mock_data.ts index 079136936..e688d6081 100644 --- a/test/mock_data.ts +++ b/test/mock_data.ts @@ -1,5 +1,6 @@ import { Schema } from '../packages/client/src/api/schemas'; import schemaJson from '../packages/codegen/example/schema.json'; +import { PgRollOperation } from '../packages/pgroll'; const animals = [ 'Ape', @@ -67,3 +68,102 @@ export const fruitUsers = fruits.map((fruit) => ({ export const mockUsers = [ownerFruits, ownerAnimals, ...animalUsers, ...fruitUsers]; export const schema = schemaJson as Schema; + +export const pgRollMigrations: PgRollOperation[] = [ + { + create_table: { + name: 'users', + columns: [ + { name: 'email', type: 'text', unique: true, nullable: true }, + { name: 'name', type: 'text', nullable: true }, + { name: 'photo', type: 'xata.xata_file', nullable: true, comment: `{ "xata.file.dpa": true }` }, + { name: 'attachments', type: 'xata.xata_file_array', nullable: true }, + { name: 'plan', type: 'text', nullable: true }, + { name: 'dark', type: 'boolean', nullable: true }, + { name: 'full_name', type: 'text', nullable: false, default: "'John Doe'" }, + { name: 'index', type: 'int8', nullable: true }, + { name: 'rating', type: 'float', nullable: true }, + { name: 'birthDate', type: 'timestamptz', nullable: true }, + { name: 'street', type: 'text', nullable: true }, + { name: 'zipcode', type: 'int', nullable: true }, + { name: 'account_value', type: 'int', nullable: true }, + { name: 'vector', type: 'real[]', nullable: true, comment: `{ "xata.search.dimension": 4 }` } + ] + } + }, + { + create_table: { + name: 'teams', + columns: [ + { name: 'name', type: 'text', nullable: true }, + { name: 'description', type: 'text', nullable: true }, + { name: 'labels', type: 'text[]', nullable: true }, + { name: 'index', type: 'int', nullable: true }, + { name: 'rating', type: 'float', nullable: true }, + { name: 'founded_date', type: 'timestamptz', nullable: true }, + { name: 'email', type: 'text', nullable: true }, + { name: 'plan', type: 'text', nullable: true }, + { name: 'dark', type: 'boolean', nullable: true }, + { name: 'config', type: 'jsonb', nullable: true } + ] + } + }, + { + create_table: { + name: 'pets', + columns: [ + { name: 'name', type: 'text', nullable: true }, + { name: 'type', type: 'text', nullable: true }, + { name: 'num_legs', type: 'int', nullable: true } + ] + } + }, + { + add_column: { + table: 'users', + column: { + name: 'team', + type: 'text', + nullable: true, + comment: `{ "xata.link": "teams" }`, + references: { name: 'fk_team_id', table: 'teams', column: 'xata_id' } + } + } + }, + { + add_column: { + table: 'users', + column: { + name: 'pet', + type: 'text', + nullable: true, + comment: `{ "xata.link": "pets" }`, + references: { name: 'fk_pet_id', table: 'pets', column: 'xata_id' } + } + } + }, + { + add_column: { + table: 'teams', + column: { + name: 'owner', + type: 'text', + nullable: true, + comment: `{ "xata.link": "users" }`, + references: { name: 'fk_owner_id', table: 'users', column: 'xata_id' } + } + } + }, + { + add_column: { + table: 'teams', + column: { + name: 'pet', + type: 'text', + nullable: true, + comment: `{ "xata.link": "pets" }`, + references: { name: 'fk_pets_id', table: 'pets', column: 'xata_id' } + } + } + } +]; diff --git a/test/utils/setup.ts b/test/utils/setup.ts index c35b73dbb..8421598c3 100644 --- a/test/utils/setup.ts +++ b/test/utils/setup.ts @@ -8,12 +8,12 @@ import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' import dotenv from 'dotenv'; import { join } from 'path'; import { File, Mock, Suite, TestContext, vi } from 'vitest'; -import { BaseClient, CacheImpl, XataApiClient } from '../../packages/client/src'; +import { BaseClient, XataApiClient } from '../../packages/client/src'; import { getHostUrl, parseProviderString } from '../../packages/client/src/api/providers'; import { TraceAttributes } from '../../packages/client/src/schema/tracing'; import { XataClient } from '../../packages/codegen/example/xata'; import { buildTraceFunction } from '../../packages/plugin-client-opentelemetry'; -import { schema } from '../mock_data'; +import { pgRollMigrations } from '../mock_data'; // Get environment variables before reading them dotenv.config({ path: join(process.cwd(), '.env') }); @@ -24,12 +24,12 @@ 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); +const clusterId = process.env.XATA_CLUSTER_ID ?? 'shared-cluster'; + +const region = process.env.XATA_REGION || 'us-east-1'; export type EnvironmentOptions = { - cache?: CacheImpl; fetch?: any; }; @@ -45,7 +45,6 @@ export type TestEnvironmentResult = { fetch: Mock; apiKey: string; branch: string; - cache?: CacheImpl; }; hooks: { beforeAll: (ctx: Suite | File) => Promise; @@ -57,7 +56,7 @@ export type TestEnvironmentResult = { export async function setUpTestEnvironment( prefix: string, - { cache, fetch: envFetch }: EnvironmentOptions = {} + { fetch: envFetch }: EnvironmentOptions = {} ): Promise { if (host === null) { throw new Error( @@ -81,8 +80,8 @@ export async function setUpTestEnvironment( const { databaseName: database } = await api.databases.createDatabase({ pathParams: { workspaceId: workspace, dbName: `sdk-integration-test-${prefix}-${id}` }, - body: { region }, - headers: { 'X-Xata-Files': 'true' } + body: { region, defaultClusterID: clusterId }, + headers: { 'X-Features': 'feat-pgroll-migrations=1' } }); const workspaceUrl = getHostUrl(host, 'workspaces').replace('{workspaceId}', workspace).replace('{region}', region); @@ -92,20 +91,26 @@ export async function setUpTestEnvironment( branch: 'main', apiKey, fetch, - cache, trace, clientName: 'sdk-tests' }; - const { edits } = await api.migrations.compareBranchWithUserSchema({ - pathParams: { workspace, region, dbBranchName: `${database}:main` }, - body: { schema } - }); + for (const operation of pgRollMigrations) { + const { jobID } = await api.migrations.applyMigration({ + pathParams: { workspace, region, dbBranchName: `${database}:main` }, + body: { operations: [operation] } + }); - await api.migrations.applyBranchSchemaEdit({ - pathParams: { workspace, region, dbBranchName: `${database}:main` }, - body: { edits } - }); + await waitForMigrationToFinish(api, workspace, region, database, 'main', jobID); + + if ('create_table' in operation) { + const { jobID } = await api.migrations.adaptTable({ + pathParams: { workspace, region, dbBranchName: `${database}:main`, tableName: operation.create_table.name } + }); + + await waitForMigrationToFinish(api, workspace, region, database, 'main', jobID); + } + } let span: Span | undefined; @@ -131,7 +136,7 @@ export async function setUpTestEnvironment( }; const client = new XataClient(clientOptions); - const baseClient = new BaseClient(clientOptions); + const baseClient = new BaseClient(clientOptions, client.schema); return { api, client, baseClient, clientOptions, database, workspace, region, hooks }; } @@ -164,3 +169,26 @@ declare module 'vitest' { span?: Span; } } + +async function waitForMigrationToFinish( + api: XataApiClient, + workspace: string, + region: string, + database: string, + branch: string, + jobId: string +) { + const { status, error } = await api.migrations.getMigrationJobStatus({ + pathParams: { workspace, region, dbBranchName: `${database}:${branch}`, jobId } + }); + if (status === 'failed') { + throw new Error(`Migration failed, ${error}`); + } + + if (status === 'completed') { + return; + } + + await new Promise((resolve) => setTimeout(resolve, 1000)); + return await waitForMigrationToFinish(api, workspace, region, database, branch, jobId); +}