Initial commit - Event Planner application
This commit is contained in:
170
node_modules/kysely/dist/esm/plugin/camel-case/camel-case-plugin.d.ts
generated
vendored
Normal file
170
node_modules/kysely/dist/esm/plugin/camel-case/camel-case-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
import type { QueryResult } from '../../driver/database-connection.js';
|
||||
import type { RootOperationNode } from '../../query-compiler/query-compiler.js';
|
||||
import type { UnknownRow } from '../../util/type-utils.js';
|
||||
import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs } from '../kysely-plugin.js';
|
||||
export interface CamelCasePluginOptions {
|
||||
/**
|
||||
* If true, camelCase is transformed into upper case SNAKE_CASE.
|
||||
* For example `fooBar => FOO_BAR` and `FOO_BAR => fooBar`
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
upperCase?: boolean;
|
||||
/**
|
||||
* If true, an underscore is added before each digit when converting
|
||||
* camelCase to snake_case. For example `foo12Bar => foo_12_bar` and
|
||||
* `foo_12_bar => foo12Bar`
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
underscoreBeforeDigits?: boolean;
|
||||
/**
|
||||
* If true, an underscore is added between consecutive upper case
|
||||
* letters when converting from camelCase to snake_case. For example
|
||||
* `fooBAR => foo_b_a_r` and `foo_b_a_r => fooBAR`.
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
underscoreBetweenUppercaseLetters?: boolean;
|
||||
/**
|
||||
* If true, nested object's keys will not be converted to camel case.
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
maintainNestedObjectKeys?: boolean;
|
||||
}
|
||||
/**
|
||||
* A plugin that converts snake_case identifiers in the database into
|
||||
* camelCase in the JavaScript side.
|
||||
*
|
||||
* For example let's assume we have a table called `person_table`
|
||||
* with columns `first_name` and `last_name` in the database. When
|
||||
* using `CamelCasePlugin` we would setup Kysely like this:
|
||||
*
|
||||
* ```ts
|
||||
* import * as Sqlite from 'better-sqlite3'
|
||||
* import { CamelCasePlugin, Kysely, SqliteDialect } from 'kysely'
|
||||
*
|
||||
* interface CamelCasedDatabase {
|
||||
* userMetadata: {
|
||||
* firstName: string
|
||||
* lastName: string
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const db = new Kysely<CamelCasedDatabase>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [new CamelCasePlugin()],
|
||||
* })
|
||||
*
|
||||
* const person = await db.selectFrom('userMetadata')
|
||||
* .where('firstName', '=', 'Arnold')
|
||||
* .select(['firstName', 'lastName'])
|
||||
* .executeTakeFirst()
|
||||
*
|
||||
* if (person) {
|
||||
* console.log(person.firstName)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The generated SQL (SQLite):
|
||||
*
|
||||
* ```sql
|
||||
* select "first_name", "last_name" from "user_metadata" where "first_name" = ?
|
||||
* ```
|
||||
*
|
||||
* As you can see from the example, __everything__ needs to be defined
|
||||
* in camelCase in the TypeScript code: table names, columns, schemas,
|
||||
* __everything__. When using the `CamelCasePlugin` Kysely works as if
|
||||
* the database was defined in camelCase.
|
||||
*
|
||||
* There are various options you can give to the plugin to modify
|
||||
* the way identifiers are converted. See {@link CamelCasePluginOptions}.
|
||||
* If those options are not enough, you can override this plugin's
|
||||
* `snakeCase` and `camelCase` methods to make the conversion exactly
|
||||
* the way you like:
|
||||
*
|
||||
* ```ts
|
||||
* class MyCamelCasePlugin extends CamelCasePlugin {
|
||||
* protected override snakeCase(str: string): string {
|
||||
* // ...
|
||||
*
|
||||
* return str
|
||||
* }
|
||||
*
|
||||
* protected override camelCase(str: string): string {
|
||||
* // ...
|
||||
*
|
||||
* return str
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export declare class CamelCasePlugin implements KyselyPlugin {
|
||||
#private;
|
||||
readonly opt: CamelCasePluginOptions;
|
||||
constructor(opt?: CamelCasePluginOptions);
|
||||
/**
|
||||
* This is called for each query before it is executed. You can modify the query by
|
||||
* transforming its {@link OperationNode} tree provided in {@link PluginTransformQueryArgs.node | args.node}
|
||||
* and returning the transformed tree. You'd usually want to use an {@link OperationNodeTransformer}
|
||||
* for this.
|
||||
*
|
||||
* If you need to pass some query-related data between this method and `transformResult` you
|
||||
* can use a `WeakMap` with {@link PluginTransformQueryArgs.queryId | args.queryId} as the key:
|
||||
*
|
||||
* ```ts
|
||||
* import type {
|
||||
* KyselyPlugin,
|
||||
* QueryResult,
|
||||
* RootOperationNode,
|
||||
* UnknownRow
|
||||
* } from 'kysely'
|
||||
*
|
||||
* interface MyData {
|
||||
* // ...
|
||||
* }
|
||||
* const data = new WeakMap<any, MyData>()
|
||||
*
|
||||
* const plugin = {
|
||||
* transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
* const something: MyData = {}
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* data.set(args.queryId, something)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.node
|
||||
* },
|
||||
*
|
||||
* async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
* // ...
|
||||
*
|
||||
* const something = data.get(args.queryId)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.result
|
||||
* }
|
||||
* } satisfies KyselyPlugin
|
||||
* ```
|
||||
*
|
||||
* You should use a `WeakMap` instead of a `Map` or some other strong references because `transformQuery`
|
||||
* is not always matched by a call to `transformResult` which would leave orphaned items in the map
|
||||
* and cause a memory leak.
|
||||
*/
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode;
|
||||
/**
|
||||
* This method is called for each query after it has been executed. The result
|
||||
* of the query can be accessed through {@link PluginTransformResultArgs.result | args.result}.
|
||||
* You can modify the result and return the modifier result.
|
||||
*/
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>;
|
||||
protected mapRow(row: UnknownRow): UnknownRow;
|
||||
protected snakeCase(str: string): string;
|
||||
protected camelCase(str: string): string;
|
||||
}
|
||||
119
node_modules/kysely/dist/esm/plugin/camel-case/camel-case-plugin.js
generated
vendored
Normal file
119
node_modules/kysely/dist/esm/plugin/camel-case/camel-case-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
/// <reference types="./camel-case-plugin.d.ts" />
|
||||
import { isPlainObject } from '../../util/object-utils.js';
|
||||
import { SnakeCaseTransformer } from './camel-case-transformer.js';
|
||||
import { createCamelCaseMapper, createSnakeCaseMapper, } from './camel-case.js';
|
||||
/**
|
||||
* A plugin that converts snake_case identifiers in the database into
|
||||
* camelCase in the JavaScript side.
|
||||
*
|
||||
* For example let's assume we have a table called `person_table`
|
||||
* with columns `first_name` and `last_name` in the database. When
|
||||
* using `CamelCasePlugin` we would setup Kysely like this:
|
||||
*
|
||||
* ```ts
|
||||
* import * as Sqlite from 'better-sqlite3'
|
||||
* import { CamelCasePlugin, Kysely, SqliteDialect } from 'kysely'
|
||||
*
|
||||
* interface CamelCasedDatabase {
|
||||
* userMetadata: {
|
||||
* firstName: string
|
||||
* lastName: string
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const db = new Kysely<CamelCasedDatabase>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [new CamelCasePlugin()],
|
||||
* })
|
||||
*
|
||||
* const person = await db.selectFrom('userMetadata')
|
||||
* .where('firstName', '=', 'Arnold')
|
||||
* .select(['firstName', 'lastName'])
|
||||
* .executeTakeFirst()
|
||||
*
|
||||
* if (person) {
|
||||
* console.log(person.firstName)
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The generated SQL (SQLite):
|
||||
*
|
||||
* ```sql
|
||||
* select "first_name", "last_name" from "user_metadata" where "first_name" = ?
|
||||
* ```
|
||||
*
|
||||
* As you can see from the example, __everything__ needs to be defined
|
||||
* in camelCase in the TypeScript code: table names, columns, schemas,
|
||||
* __everything__. When using the `CamelCasePlugin` Kysely works as if
|
||||
* the database was defined in camelCase.
|
||||
*
|
||||
* There are various options you can give to the plugin to modify
|
||||
* the way identifiers are converted. See {@link CamelCasePluginOptions}.
|
||||
* If those options are not enough, you can override this plugin's
|
||||
* `snakeCase` and `camelCase` methods to make the conversion exactly
|
||||
* the way you like:
|
||||
*
|
||||
* ```ts
|
||||
* class MyCamelCasePlugin extends CamelCasePlugin {
|
||||
* protected override snakeCase(str: string): string {
|
||||
* // ...
|
||||
*
|
||||
* return str
|
||||
* }
|
||||
*
|
||||
* protected override camelCase(str: string): string {
|
||||
* // ...
|
||||
*
|
||||
* return str
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class CamelCasePlugin {
|
||||
opt;
|
||||
#camelCase;
|
||||
#snakeCase;
|
||||
#snakeCaseTransformer;
|
||||
constructor(opt = {}) {
|
||||
this.opt = opt;
|
||||
this.#camelCase = createCamelCaseMapper(opt);
|
||||
this.#snakeCase = createSnakeCaseMapper(opt);
|
||||
this.#snakeCaseTransformer = new SnakeCaseTransformer(this.snakeCase.bind(this));
|
||||
}
|
||||
transformQuery(args) {
|
||||
return this.#snakeCaseTransformer.transformNode(args.node, args.queryId);
|
||||
}
|
||||
async transformResult(args) {
|
||||
if (args.result.rows && Array.isArray(args.result.rows)) {
|
||||
return {
|
||||
...args.result,
|
||||
rows: args.result.rows.map((row) => this.mapRow(row)),
|
||||
};
|
||||
}
|
||||
return args.result;
|
||||
}
|
||||
mapRow(row) {
|
||||
return Object.keys(row).reduce((obj, key) => {
|
||||
let value = row[key];
|
||||
if (Array.isArray(value)) {
|
||||
value = value.map((it) => (canMap(it, this.opt) ? this.mapRow(it) : it));
|
||||
}
|
||||
else if (canMap(value, this.opt)) {
|
||||
value = this.mapRow(value);
|
||||
}
|
||||
obj[this.camelCase(key)] = value;
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
snakeCase(str) {
|
||||
return this.#snakeCase(str);
|
||||
}
|
||||
camelCase(str) {
|
||||
return this.#camelCase(str);
|
||||
}
|
||||
}
|
||||
function canMap(obj, opt) {
|
||||
return isPlainObject(obj) && !opt?.maintainNestedObjectKeys;
|
||||
}
|
||||
9
node_modules/kysely/dist/esm/plugin/camel-case/camel-case-transformer.d.ts
generated
vendored
Normal file
9
node_modules/kysely/dist/esm/plugin/camel-case/camel-case-transformer.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { IdentifierNode } from '../../operation-node/identifier-node.js';
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import type { QueryId } from '../../util/query-id.js';
|
||||
import type { StringMapper } from './camel-case.js';
|
||||
export declare class SnakeCaseTransformer extends OperationNodeTransformer {
|
||||
#private;
|
||||
constructor(snakeCase: StringMapper);
|
||||
protected transformIdentifier(node: IdentifierNode, queryId: QueryId): IdentifierNode;
|
||||
}
|
||||
16
node_modules/kysely/dist/esm/plugin/camel-case/camel-case-transformer.js
generated
vendored
Normal file
16
node_modules/kysely/dist/esm/plugin/camel-case/camel-case-transformer.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/// <reference types="./camel-case-transformer.d.ts" />
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
export class SnakeCaseTransformer extends OperationNodeTransformer {
|
||||
#snakeCase;
|
||||
constructor(snakeCase) {
|
||||
super();
|
||||
this.#snakeCase = snakeCase;
|
||||
}
|
||||
transformIdentifier(node, queryId) {
|
||||
node = super.transformIdentifier(node, queryId);
|
||||
return {
|
||||
...node,
|
||||
name: this.#snakeCase(node.name),
|
||||
};
|
||||
}
|
||||
}
|
||||
15
node_modules/kysely/dist/esm/plugin/camel-case/camel-case.d.ts
generated
vendored
Normal file
15
node_modules/kysely/dist/esm/plugin/camel-case/camel-case.d.ts
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
export type StringMapper = (str: string) => string;
|
||||
/**
|
||||
* Creates a function that transforms camel case strings to snake case.
|
||||
*/
|
||||
export declare function createSnakeCaseMapper({ upperCase, underscoreBeforeDigits, underscoreBetweenUppercaseLetters, }?: {
|
||||
upperCase?: boolean | undefined;
|
||||
underscoreBeforeDigits?: boolean | undefined;
|
||||
underscoreBetweenUppercaseLetters?: boolean | undefined;
|
||||
}): StringMapper;
|
||||
/**
|
||||
* Creates a function that transforms snake case strings to camel case.
|
||||
*/
|
||||
export declare function createCamelCaseMapper({ upperCase, }?: {
|
||||
upperCase?: boolean | undefined;
|
||||
}): StringMapper;
|
||||
108
node_modules/kysely/dist/esm/plugin/camel-case/camel-case.js
generated
vendored
Normal file
108
node_modules/kysely/dist/esm/plugin/camel-case/camel-case.js
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/// <reference types="./camel-case.d.ts" />
|
||||
/**
|
||||
* Creates a function that transforms camel case strings to snake case.
|
||||
*/
|
||||
export function createSnakeCaseMapper({ upperCase = false, underscoreBeforeDigits = false, underscoreBetweenUppercaseLetters = false, } = {}) {
|
||||
return memoize((str) => {
|
||||
if (str.length === 0) {
|
||||
return str;
|
||||
}
|
||||
const upper = str.toUpperCase();
|
||||
const lower = str.toLowerCase();
|
||||
let out = lower[0];
|
||||
for (let i = 1, l = str.length; i < l; ++i) {
|
||||
const char = str[i];
|
||||
const prevChar = str[i - 1];
|
||||
const upperChar = upper[i];
|
||||
const prevUpperChar = upper[i - 1];
|
||||
const lowerChar = lower[i];
|
||||
const prevLowerChar = lower[i - 1];
|
||||
// If underScoreBeforeDigits is true then, well, insert an underscore
|
||||
// before digits :). Only the first digit gets an underscore if
|
||||
// there are multiple.
|
||||
if (underscoreBeforeDigits &&
|
||||
isDigit(char) &&
|
||||
!isDigit(prevChar) &&
|
||||
!out.endsWith('_')) {
|
||||
out += '_' + char;
|
||||
continue;
|
||||
}
|
||||
// Test if `char` is an upper-case character and that the character
|
||||
// actually has different upper and lower case versions.
|
||||
if (char === upperChar && upperChar !== lowerChar) {
|
||||
const prevCharacterIsUppercase = prevChar === prevUpperChar && prevUpperChar !== prevLowerChar;
|
||||
// If underscoreBetweenUppercaseLetters is true, we always place an underscore
|
||||
// before consecutive uppercase letters (e.g. "fooBAR" becomes "foo_b_a_r").
|
||||
// Otherwise, we don't (e.g. "fooBAR" becomes "foo_bar").
|
||||
if (underscoreBetweenUppercaseLetters || !prevCharacterIsUppercase) {
|
||||
out += '_' + lowerChar;
|
||||
}
|
||||
else {
|
||||
out += lowerChar;
|
||||
}
|
||||
}
|
||||
else {
|
||||
out += char;
|
||||
}
|
||||
}
|
||||
if (upperCase) {
|
||||
return out.toUpperCase();
|
||||
}
|
||||
else {
|
||||
return out;
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Creates a function that transforms snake case strings to camel case.
|
||||
*/
|
||||
export function createCamelCaseMapper({ upperCase = false, } = {}) {
|
||||
return memoize((str) => {
|
||||
if (str.length === 0) {
|
||||
return str;
|
||||
}
|
||||
if (upperCase && isAllUpperCaseSnakeCase(str)) {
|
||||
// Only convert to lower case if the string is all upper
|
||||
// case snake_case. This allows camelCase strings to go
|
||||
// through without changing.
|
||||
str = str.toLowerCase();
|
||||
}
|
||||
let out = str[0];
|
||||
for (let i = 1, l = str.length; i < l; ++i) {
|
||||
const char = str[i];
|
||||
const prevChar = str[i - 1];
|
||||
if (char !== '_') {
|
||||
if (prevChar === '_') {
|
||||
out += char.toUpperCase();
|
||||
}
|
||||
else {
|
||||
out += char;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
});
|
||||
}
|
||||
function isAllUpperCaseSnakeCase(str) {
|
||||
for (let i = 1, l = str.length; i < l; ++i) {
|
||||
const char = str[i];
|
||||
if (char !== '_' && char !== char.toUpperCase()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function isDigit(char) {
|
||||
return char >= '0' && char <= '9';
|
||||
}
|
||||
function memoize(func) {
|
||||
const cache = new Map();
|
||||
return (str) => {
|
||||
let mapped = cache.get(str);
|
||||
if (!mapped) {
|
||||
mapped = func(str);
|
||||
cache.set(str, mapped);
|
||||
}
|
||||
return mapped;
|
||||
};
|
||||
}
|
||||
70
node_modules/kysely/dist/esm/plugin/deduplicate-joins/deduplicate-joins-plugin.d.ts
generated
vendored
Normal file
70
node_modules/kysely/dist/esm/plugin/deduplicate-joins/deduplicate-joins-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { QueryResult } from '../../driver/database-connection.js';
|
||||
import type { RootOperationNode } from '../../query-compiler/query-compiler.js';
|
||||
import type { UnknownRow } from '../../util/type-utils.js';
|
||||
import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs } from '../kysely-plugin.js';
|
||||
/**
|
||||
* Plugin that removes duplicate joins from queries.
|
||||
*
|
||||
* See [this recipe](https://github.com/kysely-org/kysely/blob/master/site/docs/recipes/0008-deduplicate-joins.md)
|
||||
*/
|
||||
export declare class DeduplicateJoinsPlugin implements KyselyPlugin {
|
||||
#private;
|
||||
/**
|
||||
* This is called for each query before it is executed. You can modify the query by
|
||||
* transforming its {@link OperationNode} tree provided in {@link PluginTransformQueryArgs.node | args.node}
|
||||
* and returning the transformed tree. You'd usually want to use an {@link OperationNodeTransformer}
|
||||
* for this.
|
||||
*
|
||||
* If you need to pass some query-related data between this method and `transformResult` you
|
||||
* can use a `WeakMap` with {@link PluginTransformQueryArgs.queryId | args.queryId} as the key:
|
||||
*
|
||||
* ```ts
|
||||
* import type {
|
||||
* KyselyPlugin,
|
||||
* QueryResult,
|
||||
* RootOperationNode,
|
||||
* UnknownRow
|
||||
* } from 'kysely'
|
||||
*
|
||||
* interface MyData {
|
||||
* // ...
|
||||
* }
|
||||
* const data = new WeakMap<any, MyData>()
|
||||
*
|
||||
* const plugin = {
|
||||
* transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
* const something: MyData = {}
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* data.set(args.queryId, something)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.node
|
||||
* },
|
||||
*
|
||||
* async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
* // ...
|
||||
*
|
||||
* const something = data.get(args.queryId)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.result
|
||||
* }
|
||||
* } satisfies KyselyPlugin
|
||||
* ```
|
||||
*
|
||||
* You should use a `WeakMap` instead of a `Map` or some other strong references because `transformQuery`
|
||||
* is not always matched by a call to `transformResult` which would leave orphaned items in the map
|
||||
* and cause a memory leak.
|
||||
*/
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode;
|
||||
/**
|
||||
* This method is called for each query after it has been executed. The result
|
||||
* of the query can be accessed through {@link PluginTransformResultArgs.result | args.result}.
|
||||
* You can modify the result and return the modifier result.
|
||||
*/
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>;
|
||||
}
|
||||
16
node_modules/kysely/dist/esm/plugin/deduplicate-joins/deduplicate-joins-plugin.js
generated
vendored
Normal file
16
node_modules/kysely/dist/esm/plugin/deduplicate-joins/deduplicate-joins-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/// <reference types="./deduplicate-joins-plugin.d.ts" />
|
||||
import { DeduplicateJoinsTransformer } from './deduplicate-joins-transformer.js';
|
||||
/**
|
||||
* Plugin that removes duplicate joins from queries.
|
||||
*
|
||||
* See [this recipe](https://github.com/kysely-org/kysely/blob/master/site/docs/recipes/0008-deduplicate-joins.md)
|
||||
*/
|
||||
export class DeduplicateJoinsPlugin {
|
||||
#transformer = new DeduplicateJoinsTransformer();
|
||||
transformQuery(args) {
|
||||
return this.#transformer.transformNode(args.node, args.queryId);
|
||||
}
|
||||
transformResult(args) {
|
||||
return Promise.resolve(args.result);
|
||||
}
|
||||
}
|
||||
11
node_modules/kysely/dist/esm/plugin/deduplicate-joins/deduplicate-joins-transformer.d.ts
generated
vendored
Normal file
11
node_modules/kysely/dist/esm/plugin/deduplicate-joins/deduplicate-joins-transformer.d.ts
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { DeleteQueryNode } from '../../operation-node/delete-query-node.js';
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import type { SelectQueryNode } from '../../operation-node/select-query-node.js';
|
||||
import type { UpdateQueryNode } from '../../operation-node/update-query-node.js';
|
||||
import type { QueryId } from '../../util/query-id.js';
|
||||
export declare class DeduplicateJoinsTransformer extends OperationNodeTransformer {
|
||||
#private;
|
||||
protected transformSelectQuery(node: SelectQueryNode, queryId: QueryId): SelectQueryNode;
|
||||
protected transformUpdateQuery(node: UpdateQueryNode, queryId: QueryId): UpdateQueryNode;
|
||||
protected transformDeleteQuery(node: DeleteQueryNode, queryId: QueryId): DeleteQueryNode;
|
||||
}
|
||||
39
node_modules/kysely/dist/esm/plugin/deduplicate-joins/deduplicate-joins-transformer.js
generated
vendored
Normal file
39
node_modules/kysely/dist/esm/plugin/deduplicate-joins/deduplicate-joins-transformer.js
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/// <reference types="./deduplicate-joins-transformer.d.ts" />
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import { compare, freeze } from '../../util/object-utils.js';
|
||||
export class DeduplicateJoinsTransformer extends OperationNodeTransformer {
|
||||
transformSelectQuery(node, queryId) {
|
||||
return this.#transformQuery(super.transformSelectQuery(node, queryId));
|
||||
}
|
||||
transformUpdateQuery(node, queryId) {
|
||||
return this.#transformQuery(super.transformUpdateQuery(node, queryId));
|
||||
}
|
||||
transformDeleteQuery(node, queryId) {
|
||||
return this.#transformQuery(super.transformDeleteQuery(node, queryId));
|
||||
}
|
||||
#transformQuery(node) {
|
||||
if (!node.joins || node.joins.length === 0) {
|
||||
return node;
|
||||
}
|
||||
return freeze({
|
||||
...node,
|
||||
joins: this.#deduplicateJoins(node.joins),
|
||||
});
|
||||
}
|
||||
#deduplicateJoins(joins) {
|
||||
const out = [];
|
||||
for (let i = 0; i < joins.length; ++i) {
|
||||
let foundDuplicate = false;
|
||||
for (let j = 0; j < out.length; ++j) {
|
||||
if (compare(joins[i], out[j])) {
|
||||
foundDuplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundDuplicate) {
|
||||
out.push(joins[i]);
|
||||
}
|
||||
}
|
||||
return freeze(out);
|
||||
}
|
||||
}
|
||||
211
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.d.ts
generated
vendored
Normal file
211
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
import type { QueryResult } from '../../driver/database-connection.js';
|
||||
import type { RootOperationNode } from '../../query-compiler/query-compiler.js';
|
||||
import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs } from '../kysely-plugin.js';
|
||||
import type { UnknownRow } from '../../util/type-utils.js';
|
||||
import type { HandleEmptyInListsOptions } from './handle-empty-in-lists.js';
|
||||
/**
|
||||
* A plugin that allows handling `in ()` and `not in ()` expressions.
|
||||
*
|
||||
* These expressions are invalid SQL syntax for many databases, and result in runtime
|
||||
* database errors.
|
||||
*
|
||||
* The workarounds used by other libraries always involve modifying the query under
|
||||
* the hood, which is not aligned with Kysely's philosophy of WYSIWYG. We recommend manually checking
|
||||
* for empty arrays before passing them as arguments to `in` and `not in` expressions
|
||||
* instead, but understand that this can be cumbersome. Hence we're going with an
|
||||
* opt-in approach where you can choose if and how to handle these cases. We do
|
||||
* not want to make this the default behavior, as it can lead to unexpected behavior.
|
||||
* Use it at your own risk. Test it. Make sure it works as expected for you.
|
||||
*
|
||||
* Using this plugin also allows you to throw an error (thus avoiding unnecessary
|
||||
* requests to the database) or print a warning in these cases.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* The following strategy replaces the `in`/`not in` expression with a noncontingent
|
||||
* expression. A contradiction (falsy) `1 = 0` for `in`, and a tautology (truthy) `1 = 1` for `not in`),
|
||||
* similarily to how {@link https://github.com/knex/knex/blob/176151d8048b2a7feeb89a3d649a5580786d4f4e/docs/src/guide/query-builder.md#L1763 | Knex.js},
|
||||
* {@link https://github.com/prisma/prisma-engines/blob/99168c54187178484dae45d9478aa40cfd1866d2/quaint/src/visitor.rs#L804-L823 | PrismaORM},
|
||||
* {@link https://github.com/laravel/framework/blob/8.x/src/Illuminate/Database/Query/Grammars/Grammar.php#L284-L291 | Laravel},
|
||||
* {@link https://docs.sqlalchemy.org/en/13/core/engines.html#sqlalchemy.create_engine.params.empty_in_strategy | SQLAlchemy}
|
||||
* handle this.
|
||||
*
|
||||
* ```ts
|
||||
* import Sqlite from 'better-sqlite3'
|
||||
* import {
|
||||
* HandleEmptyInListsPlugin,
|
||||
* Kysely,
|
||||
* replaceWithNoncontingentExpression,
|
||||
* SqliteDialect,
|
||||
* } from 'kysely'
|
||||
* import type { Database } from 'type-editor' // imaginary module
|
||||
*
|
||||
* const db = new Kysely<Database>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [
|
||||
* new HandleEmptyInListsPlugin({
|
||||
* strategy: replaceWithNoncontingentExpression
|
||||
* })
|
||||
* ],
|
||||
* })
|
||||
*
|
||||
* const results = await db
|
||||
* .selectFrom('person')
|
||||
* .where('id', 'in', [])
|
||||
* .where('first_name', 'not in', [])
|
||||
* .selectAll()
|
||||
* .execute()
|
||||
* ```
|
||||
*
|
||||
* The generated SQL (SQLite):
|
||||
*
|
||||
* ```sql
|
||||
* select * from "person" where 1 = 0 and 1 = 1
|
||||
* ```
|
||||
*
|
||||
* The following strategy does the following:
|
||||
*
|
||||
* When `in`, pushes a `null` value into the empty list resulting in `in (null)`,
|
||||
* similiarly to how {@link https://github.com/typeorm/typeorm/blob/0280cdc451c35ef73c830eb1191c95d34f6ce06e/src/query-builder/QueryBuilder.ts#L919-L922 | TypeORM}
|
||||
* and {@link https://github.com/sequelize/sequelize/blob/0f2891c6897e12bf9bf56df344aae5b698f58c7d/packages/core/src/abstract-dialect/where-sql-builder.ts#L368-L379 | Sequelize}
|
||||
* handle `in ()`. `in (null)` is logically the equivalent of `= null`, which returns
|
||||
* `null`, which is a falsy expression in most SQL databases. We recommend NOT
|
||||
* using this strategy if you plan to use `in` in `select`, `returning`, or `output`
|
||||
* clauses, as the return type differs from the `SqlBool` default type for comparisons.
|
||||
*
|
||||
* When `not in`, casts the left operand as `char` and pushes a unique value into
|
||||
* the empty list resulting in `cast({{lhs}} as char) not in ({{VALUE}})`. Casting
|
||||
* is required to avoid database errors with non-string values.
|
||||
*
|
||||
* ```ts
|
||||
* import Sqlite from 'better-sqlite3'
|
||||
* import {
|
||||
* HandleEmptyInListsPlugin,
|
||||
* Kysely,
|
||||
* pushValueIntoList,
|
||||
* SqliteDialect
|
||||
* } from 'kysely'
|
||||
* import type { Database } from 'type-editor' // imaginary module
|
||||
*
|
||||
* const db = new Kysely<Database>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [
|
||||
* new HandleEmptyInListsPlugin({
|
||||
* strategy: pushValueIntoList('__kysely_no_values_were_provided__') // choose a unique value for not in. has to be something with zero chance being in the data.
|
||||
* })
|
||||
* ],
|
||||
* })
|
||||
*
|
||||
* const results = await db
|
||||
* .selectFrom('person')
|
||||
* .where('id', 'in', [])
|
||||
* .where('first_name', 'not in', [])
|
||||
* .selectAll()
|
||||
* .execute()
|
||||
* ```
|
||||
*
|
||||
* The generated SQL (SQLite):
|
||||
*
|
||||
* ```sql
|
||||
* select * from "person" where "id" in (null) and cast("first_name" as char) not in ('__kysely_no_values_were_provided__')
|
||||
* ```
|
||||
*
|
||||
* The following custom strategy throws an error when an empty list is encountered
|
||||
* to avoid unnecessary requests to the database:
|
||||
*
|
||||
* ```ts
|
||||
* import Sqlite from 'better-sqlite3'
|
||||
* import {
|
||||
* HandleEmptyInListsPlugin,
|
||||
* Kysely,
|
||||
* SqliteDialect
|
||||
* } from 'kysely'
|
||||
* import type { Database } from 'type-editor' // imaginary module
|
||||
*
|
||||
* const db = new Kysely<Database>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [
|
||||
* new HandleEmptyInListsPlugin({
|
||||
* strategy: () => {
|
||||
* throw new Error('Empty in/not-in is not allowed')
|
||||
* }
|
||||
* })
|
||||
* ],
|
||||
* })
|
||||
*
|
||||
* const results = await db
|
||||
* .selectFrom('person')
|
||||
* .where('id', 'in', [])
|
||||
* .selectAll()
|
||||
* .execute() // throws an error with 'Empty in/not-in is not allowed' message!
|
||||
* ```
|
||||
*/
|
||||
export declare class HandleEmptyInListsPlugin implements KyselyPlugin {
|
||||
#private;
|
||||
readonly opt: HandleEmptyInListsOptions;
|
||||
constructor(opt: HandleEmptyInListsOptions);
|
||||
/**
|
||||
* This is called for each query before it is executed. You can modify the query by
|
||||
* transforming its {@link OperationNode} tree provided in {@link PluginTransformQueryArgs.node | args.node}
|
||||
* and returning the transformed tree. You'd usually want to use an {@link OperationNodeTransformer}
|
||||
* for this.
|
||||
*
|
||||
* If you need to pass some query-related data between this method and `transformResult` you
|
||||
* can use a `WeakMap` with {@link PluginTransformQueryArgs.queryId | args.queryId} as the key:
|
||||
*
|
||||
* ```ts
|
||||
* import type {
|
||||
* KyselyPlugin,
|
||||
* QueryResult,
|
||||
* RootOperationNode,
|
||||
* UnknownRow
|
||||
* } from 'kysely'
|
||||
*
|
||||
* interface MyData {
|
||||
* // ...
|
||||
* }
|
||||
* const data = new WeakMap<any, MyData>()
|
||||
*
|
||||
* const plugin = {
|
||||
* transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
* const something: MyData = {}
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* data.set(args.queryId, something)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.node
|
||||
* },
|
||||
*
|
||||
* async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
* // ...
|
||||
*
|
||||
* const something = data.get(args.queryId)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.result
|
||||
* }
|
||||
* } satisfies KyselyPlugin
|
||||
* ```
|
||||
*
|
||||
* You should use a `WeakMap` instead of a `Map` or some other strong references because `transformQuery`
|
||||
* is not always matched by a call to `transformResult` which would leave orphaned items in the map
|
||||
* and cause a memory leak.
|
||||
*/
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode;
|
||||
/**
|
||||
* This method is called for each query after it has been executed. The result
|
||||
* of the query can be accessed through {@link PluginTransformResultArgs.result | args.result}.
|
||||
* You can modify the result and return the modifier result.
|
||||
*/
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>;
|
||||
}
|
||||
159
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.js
generated
vendored
Normal file
159
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
/// <reference types="./handle-empty-in-lists-plugin.d.ts" />
|
||||
import { HandleEmptyInListsTransformer } from './handle-empty-in-lists-transformer.js';
|
||||
/**
|
||||
* A plugin that allows handling `in ()` and `not in ()` expressions.
|
||||
*
|
||||
* These expressions are invalid SQL syntax for many databases, and result in runtime
|
||||
* database errors.
|
||||
*
|
||||
* The workarounds used by other libraries always involve modifying the query under
|
||||
* the hood, which is not aligned with Kysely's philosophy of WYSIWYG. We recommend manually checking
|
||||
* for empty arrays before passing them as arguments to `in` and `not in` expressions
|
||||
* instead, but understand that this can be cumbersome. Hence we're going with an
|
||||
* opt-in approach where you can choose if and how to handle these cases. We do
|
||||
* not want to make this the default behavior, as it can lead to unexpected behavior.
|
||||
* Use it at your own risk. Test it. Make sure it works as expected for you.
|
||||
*
|
||||
* Using this plugin also allows you to throw an error (thus avoiding unnecessary
|
||||
* requests to the database) or print a warning in these cases.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* The following strategy replaces the `in`/`not in` expression with a noncontingent
|
||||
* expression. A contradiction (falsy) `1 = 0` for `in`, and a tautology (truthy) `1 = 1` for `not in`),
|
||||
* similarily to how {@link https://github.com/knex/knex/blob/176151d8048b2a7feeb89a3d649a5580786d4f4e/docs/src/guide/query-builder.md#L1763 | Knex.js},
|
||||
* {@link https://github.com/prisma/prisma-engines/blob/99168c54187178484dae45d9478aa40cfd1866d2/quaint/src/visitor.rs#L804-L823 | PrismaORM},
|
||||
* {@link https://github.com/laravel/framework/blob/8.x/src/Illuminate/Database/Query/Grammars/Grammar.php#L284-L291 | Laravel},
|
||||
* {@link https://docs.sqlalchemy.org/en/13/core/engines.html#sqlalchemy.create_engine.params.empty_in_strategy | SQLAlchemy}
|
||||
* handle this.
|
||||
*
|
||||
* ```ts
|
||||
* import Sqlite from 'better-sqlite3'
|
||||
* import {
|
||||
* HandleEmptyInListsPlugin,
|
||||
* Kysely,
|
||||
* replaceWithNoncontingentExpression,
|
||||
* SqliteDialect,
|
||||
* } from 'kysely'
|
||||
* import type { Database } from 'type-editor' // imaginary module
|
||||
*
|
||||
* const db = new Kysely<Database>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [
|
||||
* new HandleEmptyInListsPlugin({
|
||||
* strategy: replaceWithNoncontingentExpression
|
||||
* })
|
||||
* ],
|
||||
* })
|
||||
*
|
||||
* const results = await db
|
||||
* .selectFrom('person')
|
||||
* .where('id', 'in', [])
|
||||
* .where('first_name', 'not in', [])
|
||||
* .selectAll()
|
||||
* .execute()
|
||||
* ```
|
||||
*
|
||||
* The generated SQL (SQLite):
|
||||
*
|
||||
* ```sql
|
||||
* select * from "person" where 1 = 0 and 1 = 1
|
||||
* ```
|
||||
*
|
||||
* The following strategy does the following:
|
||||
*
|
||||
* When `in`, pushes a `null` value into the empty list resulting in `in (null)`,
|
||||
* similiarly to how {@link https://github.com/typeorm/typeorm/blob/0280cdc451c35ef73c830eb1191c95d34f6ce06e/src/query-builder/QueryBuilder.ts#L919-L922 | TypeORM}
|
||||
* and {@link https://github.com/sequelize/sequelize/blob/0f2891c6897e12bf9bf56df344aae5b698f58c7d/packages/core/src/abstract-dialect/where-sql-builder.ts#L368-L379 | Sequelize}
|
||||
* handle `in ()`. `in (null)` is logically the equivalent of `= null`, which returns
|
||||
* `null`, which is a falsy expression in most SQL databases. We recommend NOT
|
||||
* using this strategy if you plan to use `in` in `select`, `returning`, or `output`
|
||||
* clauses, as the return type differs from the `SqlBool` default type for comparisons.
|
||||
*
|
||||
* When `not in`, casts the left operand as `char` and pushes a unique value into
|
||||
* the empty list resulting in `cast({{lhs}} as char) not in ({{VALUE}})`. Casting
|
||||
* is required to avoid database errors with non-string values.
|
||||
*
|
||||
* ```ts
|
||||
* import Sqlite from 'better-sqlite3'
|
||||
* import {
|
||||
* HandleEmptyInListsPlugin,
|
||||
* Kysely,
|
||||
* pushValueIntoList,
|
||||
* SqliteDialect
|
||||
* } from 'kysely'
|
||||
* import type { Database } from 'type-editor' // imaginary module
|
||||
*
|
||||
* const db = new Kysely<Database>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [
|
||||
* new HandleEmptyInListsPlugin({
|
||||
* strategy: pushValueIntoList('__kysely_no_values_were_provided__') // choose a unique value for not in. has to be something with zero chance being in the data.
|
||||
* })
|
||||
* ],
|
||||
* })
|
||||
*
|
||||
* const results = await db
|
||||
* .selectFrom('person')
|
||||
* .where('id', 'in', [])
|
||||
* .where('first_name', 'not in', [])
|
||||
* .selectAll()
|
||||
* .execute()
|
||||
* ```
|
||||
*
|
||||
* The generated SQL (SQLite):
|
||||
*
|
||||
* ```sql
|
||||
* select * from "person" where "id" in (null) and cast("first_name" as char) not in ('__kysely_no_values_were_provided__')
|
||||
* ```
|
||||
*
|
||||
* The following custom strategy throws an error when an empty list is encountered
|
||||
* to avoid unnecessary requests to the database:
|
||||
*
|
||||
* ```ts
|
||||
* import Sqlite from 'better-sqlite3'
|
||||
* import {
|
||||
* HandleEmptyInListsPlugin,
|
||||
* Kysely,
|
||||
* SqliteDialect
|
||||
* } from 'kysely'
|
||||
* import type { Database } from 'type-editor' // imaginary module
|
||||
*
|
||||
* const db = new Kysely<Database>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [
|
||||
* new HandleEmptyInListsPlugin({
|
||||
* strategy: () => {
|
||||
* throw new Error('Empty in/not-in is not allowed')
|
||||
* }
|
||||
* })
|
||||
* ],
|
||||
* })
|
||||
*
|
||||
* const results = await db
|
||||
* .selectFrom('person')
|
||||
* .where('id', 'in', [])
|
||||
* .selectAll()
|
||||
* .execute() // throws an error with 'Empty in/not-in is not allowed' message!
|
||||
* ```
|
||||
*/
|
||||
export class HandleEmptyInListsPlugin {
|
||||
opt;
|
||||
#transformer;
|
||||
constructor(opt) {
|
||||
this.opt = opt;
|
||||
this.#transformer = new HandleEmptyInListsTransformer(opt.strategy);
|
||||
}
|
||||
transformQuery(args) {
|
||||
return this.#transformer.transformNode(args.node, args.queryId);
|
||||
}
|
||||
async transformResult(args) {
|
||||
return args.result;
|
||||
}
|
||||
}
|
||||
8
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists-transformer.d.ts
generated
vendored
Normal file
8
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists-transformer.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { BinaryOperationNode } from '../../operation-node/binary-operation-node.js';
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import type { EmptyInListsStrategy } from './handle-empty-in-lists.js';
|
||||
export declare class HandleEmptyInListsTransformer extends OperationNodeTransformer {
|
||||
#private;
|
||||
constructor(strategy: EmptyInListsStrategy);
|
||||
protected transformBinaryOperation(node: BinaryOperationNode): BinaryOperationNode;
|
||||
}
|
||||
26
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists-transformer.js
generated
vendored
Normal file
26
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists-transformer.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/// <reference types="./handle-empty-in-lists-transformer.d.ts" />
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import { PrimitiveValueListNode } from '../../operation-node/primitive-value-list-node.js';
|
||||
import { OperatorNode } from '../../operation-node/operator-node.js';
|
||||
import { ValueListNode } from '../../operation-node/value-list-node.js';
|
||||
export class HandleEmptyInListsTransformer extends OperationNodeTransformer {
|
||||
#strategy;
|
||||
constructor(strategy) {
|
||||
super();
|
||||
this.#strategy = strategy;
|
||||
}
|
||||
transformBinaryOperation(node) {
|
||||
if (this.#isEmptyInListNode(node)) {
|
||||
return this.#strategy(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
#isEmptyInListNode(node) {
|
||||
const { operator, rightOperand } = node;
|
||||
return ((PrimitiveValueListNode.is(rightOperand) ||
|
||||
ValueListNode.is(rightOperand)) &&
|
||||
rightOperand.values.length === 0 &&
|
||||
OperatorNode.is(operator) &&
|
||||
(operator.operator === 'in' || operator.operator === 'not in'));
|
||||
}
|
||||
}
|
||||
44
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists.d.ts
generated
vendored
Normal file
44
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists.d.ts
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import { BinaryOperationNode } from '../../operation-node/binary-operation-node.js';
|
||||
import { OperatorNode } from '../../operation-node/operator-node.js';
|
||||
import type { PrimitiveValueListNode } from '../../operation-node/primitive-value-list-node.js';
|
||||
import { ValueListNode } from '../../operation-node/value-list-node.js';
|
||||
export interface HandleEmptyInListsOptions {
|
||||
/**
|
||||
* The strategy to use when handling `in ()` and `not in ()`.
|
||||
*
|
||||
* See {@link HandleEmptyInListsPlugin} for examples.
|
||||
*/
|
||||
strategy: EmptyInListsStrategy;
|
||||
}
|
||||
export type EmptyInListNode = BinaryOperationNode & {
|
||||
operator: OperatorNode & {
|
||||
operator: 'in' | 'not in';
|
||||
};
|
||||
rightOperand: (ValueListNode | PrimitiveValueListNode) & {
|
||||
values: Readonly<[]>;
|
||||
};
|
||||
};
|
||||
export type EmptyInListsStrategy = (node: EmptyInListNode) => BinaryOperationNode;
|
||||
/**
|
||||
* Replaces the `in`/`not in` expression with a noncontingent expression (always true or always
|
||||
* false) depending on the original operator.
|
||||
*
|
||||
* This is how Knex.js, PrismaORM, Laravel, and SQLAlchemy handle `in ()` and `not in ()`.
|
||||
*
|
||||
* See {@link pushValueIntoList} for an alternative strategy.
|
||||
*/
|
||||
export declare function replaceWithNoncontingentExpression(node: EmptyInListNode): BinaryOperationNode;
|
||||
/**
|
||||
* When `in`, pushes a `null` value into the list resulting in `in (null)`. This
|
||||
* is how TypeORM and Sequelize handle `in ()`. `in (null)` is logically the equivalent
|
||||
* of `= null`, which returns `null`, which is a falsy expression in most SQL databases.
|
||||
* We recommend NOT using this strategy if you plan to use `in` in `select`, `returning`,
|
||||
* or `output` clauses, as the return type differs from the `SqlBool` default type.
|
||||
*
|
||||
* When `not in`, casts the left operand as `char` and pushes a literal value into
|
||||
* the list resulting in `cast({{lhs}} as char) not in ({{VALUE}})`. Casting
|
||||
* is required to avoid database errors with non-string columns.
|
||||
*
|
||||
* See {@link replaceWithNoncontingentExpression} for an alternative strategy.
|
||||
*/
|
||||
export declare function pushValueIntoList(uniqueNotInLiteral: '__kysely_no_values_were_provided__' | (string & {})): EmptyInListsStrategy;
|
||||
63
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists.js
generated
vendored
Normal file
63
node_modules/kysely/dist/esm/plugin/handle-empty-in-lists/handle-empty-in-lists.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/// <reference types="./handle-empty-in-lists.d.ts" />
|
||||
import { BinaryOperationNode } from '../../operation-node/binary-operation-node.js';
|
||||
import { CastNode } from '../../operation-node/cast-node.js';
|
||||
import { DataTypeNode } from '../../operation-node/data-type-node.js';
|
||||
import { OperatorNode } from '../../operation-node/operator-node.js';
|
||||
import { ValueListNode } from '../../operation-node/value-list-node.js';
|
||||
import { ValueNode } from '../../operation-node/value-node.js';
|
||||
import { freeze } from '../../util/object-utils.js';
|
||||
let contradiction;
|
||||
let eq;
|
||||
let one;
|
||||
let tautology;
|
||||
/**
|
||||
* Replaces the `in`/`not in` expression with a noncontingent expression (always true or always
|
||||
* false) depending on the original operator.
|
||||
*
|
||||
* This is how Knex.js, PrismaORM, Laravel, and SQLAlchemy handle `in ()` and `not in ()`.
|
||||
*
|
||||
* See {@link pushValueIntoList} for an alternative strategy.
|
||||
*/
|
||||
export function replaceWithNoncontingentExpression(node) {
|
||||
const _one = (one ||= ValueNode.createImmediate(1));
|
||||
const _eq = (eq ||= OperatorNode.create('='));
|
||||
if (node.operator.operator === 'in') {
|
||||
return (contradiction ||= BinaryOperationNode.create(_one, _eq, ValueNode.createImmediate(0)));
|
||||
}
|
||||
return (tautology ||= BinaryOperationNode.create(_one, _eq, _one));
|
||||
}
|
||||
let char;
|
||||
let listNull;
|
||||
let listVal;
|
||||
/**
|
||||
* When `in`, pushes a `null` value into the list resulting in `in (null)`. This
|
||||
* is how TypeORM and Sequelize handle `in ()`. `in (null)` is logically the equivalent
|
||||
* of `= null`, which returns `null`, which is a falsy expression in most SQL databases.
|
||||
* We recommend NOT using this strategy if you plan to use `in` in `select`, `returning`,
|
||||
* or `output` clauses, as the return type differs from the `SqlBool` default type.
|
||||
*
|
||||
* When `not in`, casts the left operand as `char` and pushes a literal value into
|
||||
* the list resulting in `cast({{lhs}} as char) not in ({{VALUE}})`. Casting
|
||||
* is required to avoid database errors with non-string columns.
|
||||
*
|
||||
* See {@link replaceWithNoncontingentExpression} for an alternative strategy.
|
||||
*/
|
||||
export function pushValueIntoList(uniqueNotInLiteral) {
|
||||
return function pushValueIntoList(node) {
|
||||
if (node.operator.operator === 'in') {
|
||||
return freeze({
|
||||
...node,
|
||||
rightOperand: (listNull ||= ValueListNode.create([
|
||||
ValueNode.createImmediate(null),
|
||||
])),
|
||||
});
|
||||
}
|
||||
return freeze({
|
||||
...node,
|
||||
leftOperand: CastNode.create(node.leftOperand, (char ||= DataTypeNode.create('char'))),
|
||||
rightOperand: (listVal ||= ValueListNode.create([
|
||||
ValueNode.createImmediate(uniqueNotInLiteral),
|
||||
])),
|
||||
});
|
||||
};
|
||||
}
|
||||
73
node_modules/kysely/dist/esm/plugin/immediate-value/immediate-value-plugin.d.ts
generated
vendored
Normal file
73
node_modules/kysely/dist/esm/plugin/immediate-value/immediate-value-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import type { QueryResult } from '../../driver/database-connection.js';
|
||||
import type { RootOperationNode } from '../../query-compiler/query-compiler.js';
|
||||
import type { UnknownRow } from '../../util/type-utils.js';
|
||||
import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs } from '../kysely-plugin.js';
|
||||
/**
|
||||
* Transforms all ValueNodes to immediate.
|
||||
*
|
||||
* WARNING! This should never be part of the public API. Users should never use this.
|
||||
* This is an internal helper.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare class ImmediateValuePlugin implements KyselyPlugin {
|
||||
#private;
|
||||
/**
|
||||
* This is called for each query before it is executed. You can modify the query by
|
||||
* transforming its {@link OperationNode} tree provided in {@link PluginTransformQueryArgs.node | args.node}
|
||||
* and returning the transformed tree. You'd usually want to use an {@link OperationNodeTransformer}
|
||||
* for this.
|
||||
*
|
||||
* If you need to pass some query-related data between this method and `transformResult` you
|
||||
* can use a `WeakMap` with {@link PluginTransformQueryArgs.queryId | args.queryId} as the key:
|
||||
*
|
||||
* ```ts
|
||||
* import type {
|
||||
* KyselyPlugin,
|
||||
* QueryResult,
|
||||
* RootOperationNode,
|
||||
* UnknownRow
|
||||
* } from 'kysely'
|
||||
*
|
||||
* interface MyData {
|
||||
* // ...
|
||||
* }
|
||||
* const data = new WeakMap<any, MyData>()
|
||||
*
|
||||
* const plugin = {
|
||||
* transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
* const something: MyData = {}
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* data.set(args.queryId, something)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.node
|
||||
* },
|
||||
*
|
||||
* async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
* // ...
|
||||
*
|
||||
* const something = data.get(args.queryId)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.result
|
||||
* }
|
||||
* } satisfies KyselyPlugin
|
||||
* ```
|
||||
*
|
||||
* You should use a `WeakMap` instead of a `Map` or some other strong references because `transformQuery`
|
||||
* is not always matched by a call to `transformResult` which would leave orphaned items in the map
|
||||
* and cause a memory leak.
|
||||
*/
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode;
|
||||
/**
|
||||
* This method is called for each query after it has been executed. The result
|
||||
* of the query can be accessed through {@link PluginTransformResultArgs.result | args.result}.
|
||||
* You can modify the result and return the modifier result.
|
||||
*/
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>;
|
||||
}
|
||||
19
node_modules/kysely/dist/esm/plugin/immediate-value/immediate-value-plugin.js
generated
vendored
Normal file
19
node_modules/kysely/dist/esm/plugin/immediate-value/immediate-value-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/// <reference types="./immediate-value-plugin.d.ts" />
|
||||
import { ImmediateValueTransformer } from './immediate-value-transformer.js';
|
||||
/**
|
||||
* Transforms all ValueNodes to immediate.
|
||||
*
|
||||
* WARNING! This should never be part of the public API. Users should never use this.
|
||||
* This is an internal helper.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class ImmediateValuePlugin {
|
||||
#transformer = new ImmediateValueTransformer();
|
||||
transformQuery(args) {
|
||||
return this.#transformer.transformNode(args.node, args.queryId);
|
||||
}
|
||||
transformResult(args) {
|
||||
return Promise.resolve(args.result);
|
||||
}
|
||||
}
|
||||
15
node_modules/kysely/dist/esm/plugin/immediate-value/immediate-value-transformer.d.ts
generated
vendored
Normal file
15
node_modules/kysely/dist/esm/plugin/immediate-value/immediate-value-transformer.d.ts
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import type { PrimitiveValueListNode } from '../../operation-node/primitive-value-list-node.js';
|
||||
import { ValueNode } from '../../operation-node/value-node.js';
|
||||
/**
|
||||
* Transforms all ValueNodes to immediate.
|
||||
*
|
||||
* WARNING! This should never be part of the public API. Users should never use this.
|
||||
* This is an internal helper.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare class ImmediateValueTransformer extends OperationNodeTransformer {
|
||||
transformPrimitiveValueList(node: PrimitiveValueListNode): PrimitiveValueListNode;
|
||||
transformValue(node: ValueNode): ValueNode;
|
||||
}
|
||||
20
node_modules/kysely/dist/esm/plugin/immediate-value/immediate-value-transformer.js
generated
vendored
Normal file
20
node_modules/kysely/dist/esm/plugin/immediate-value/immediate-value-transformer.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/// <reference types="./immediate-value-transformer.d.ts" />
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import { ValueListNode } from '../../operation-node/value-list-node.js';
|
||||
import { ValueNode } from '../../operation-node/value-node.js';
|
||||
/**
|
||||
* Transforms all ValueNodes to immediate.
|
||||
*
|
||||
* WARNING! This should never be part of the public API. Users should never use this.
|
||||
* This is an internal helper.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class ImmediateValueTransformer extends OperationNodeTransformer {
|
||||
transformPrimitiveValueList(node) {
|
||||
return ValueListNode.create(node.values.map(ValueNode.createImmediate));
|
||||
}
|
||||
transformValue(node) {
|
||||
return ValueNode.createImmediate(node.value);
|
||||
}
|
||||
}
|
||||
72
node_modules/kysely/dist/esm/plugin/kysely-plugin.d.ts
generated
vendored
Normal file
72
node_modules/kysely/dist/esm/plugin/kysely-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { QueryResult } from '../driver/database-connection.js';
|
||||
import type { RootOperationNode } from '../query-compiler/query-compiler.js';
|
||||
import type { QueryId } from '../util/query-id.js';
|
||||
import type { UnknownRow } from '../util/type-utils.js';
|
||||
export interface KyselyPlugin {
|
||||
/**
|
||||
* This is called for each query before it is executed. You can modify the query by
|
||||
* transforming its {@link OperationNode} tree provided in {@link PluginTransformQueryArgs.node | args.node}
|
||||
* and returning the transformed tree. You'd usually want to use an {@link OperationNodeTransformer}
|
||||
* for this.
|
||||
*
|
||||
* If you need to pass some query-related data between this method and `transformResult` you
|
||||
* can use a `WeakMap` with {@link PluginTransformQueryArgs.queryId | args.queryId} as the key:
|
||||
*
|
||||
* ```ts
|
||||
* import type {
|
||||
* KyselyPlugin,
|
||||
* QueryResult,
|
||||
* RootOperationNode,
|
||||
* UnknownRow
|
||||
* } from 'kysely'
|
||||
*
|
||||
* interface MyData {
|
||||
* // ...
|
||||
* }
|
||||
* const data = new WeakMap<any, MyData>()
|
||||
*
|
||||
* const plugin = {
|
||||
* transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
* const something: MyData = {}
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* data.set(args.queryId, something)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.node
|
||||
* },
|
||||
*
|
||||
* async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
* // ...
|
||||
*
|
||||
* const something = data.get(args.queryId)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.result
|
||||
* }
|
||||
* } satisfies KyselyPlugin
|
||||
* ```
|
||||
*
|
||||
* You should use a `WeakMap` instead of a `Map` or some other strong references because `transformQuery`
|
||||
* is not always matched by a call to `transformResult` which would leave orphaned items in the map
|
||||
* and cause a memory leak.
|
||||
*/
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode;
|
||||
/**
|
||||
* This method is called for each query after it has been executed. The result
|
||||
* of the query can be accessed through {@link PluginTransformResultArgs.result | args.result}.
|
||||
* You can modify the result and return the modifier result.
|
||||
*/
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>;
|
||||
}
|
||||
export interface PluginTransformQueryArgs {
|
||||
readonly queryId: QueryId;
|
||||
readonly node: RootOperationNode;
|
||||
}
|
||||
export interface PluginTransformResultArgs {
|
||||
readonly queryId: QueryId;
|
||||
readonly result: QueryResult<UnknownRow>;
|
||||
}
|
||||
2
node_modules/kysely/dist/esm/plugin/kysely-plugin.js
generated
vendored
Normal file
2
node_modules/kysely/dist/esm/plugin/kysely-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/// <reference types="./kysely-plugin.d.ts" />
|
||||
export {};
|
||||
64
node_modules/kysely/dist/esm/plugin/noop-plugin.d.ts
generated
vendored
Normal file
64
node_modules/kysely/dist/esm/plugin/noop-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { QueryResult } from '../driver/database-connection.js';
|
||||
import type { RootOperationNode } from '../query-compiler/query-compiler.js';
|
||||
import type { UnknownRow } from '../util/type-utils.js';
|
||||
import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs } from './kysely-plugin.js';
|
||||
export declare class NoopPlugin implements KyselyPlugin {
|
||||
/**
|
||||
* This is called for each query before it is executed. You can modify the query by
|
||||
* transforming its {@link OperationNode} tree provided in {@link PluginTransformQueryArgs.node | args.node}
|
||||
* and returning the transformed tree. You'd usually want to use an {@link OperationNodeTransformer}
|
||||
* for this.
|
||||
*
|
||||
* If you need to pass some query-related data between this method and `transformResult` you
|
||||
* can use a `WeakMap` with {@link PluginTransformQueryArgs.queryId | args.queryId} as the key:
|
||||
*
|
||||
* ```ts
|
||||
* import type {
|
||||
* KyselyPlugin,
|
||||
* QueryResult,
|
||||
* RootOperationNode,
|
||||
* UnknownRow
|
||||
* } from 'kysely'
|
||||
*
|
||||
* interface MyData {
|
||||
* // ...
|
||||
* }
|
||||
* const data = new WeakMap<any, MyData>()
|
||||
*
|
||||
* const plugin = {
|
||||
* transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
* const something: MyData = {}
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* data.set(args.queryId, something)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.node
|
||||
* },
|
||||
*
|
||||
* async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
* // ...
|
||||
*
|
||||
* const something = data.get(args.queryId)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.result
|
||||
* }
|
||||
* } satisfies KyselyPlugin
|
||||
* ```
|
||||
*
|
||||
* You should use a `WeakMap` instead of a `Map` or some other strong references because `transformQuery`
|
||||
* is not always matched by a call to `transformResult` which would leave orphaned items in the map
|
||||
* and cause a memory leak.
|
||||
*/
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode;
|
||||
/**
|
||||
* This method is called for each query after it has been executed. The result
|
||||
* of the query can be accessed through {@link PluginTransformResultArgs.result | args.result}.
|
||||
* You can modify the result and return the modifier result.
|
||||
*/
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>;
|
||||
}
|
||||
9
node_modules/kysely/dist/esm/plugin/noop-plugin.js
generated
vendored
Normal file
9
node_modules/kysely/dist/esm/plugin/noop-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/// <reference types="./noop-plugin.d.ts" />
|
||||
export class NoopPlugin {
|
||||
transformQuery(args) {
|
||||
return args.node;
|
||||
}
|
||||
async transformResult(args) {
|
||||
return args.result;
|
||||
}
|
||||
}
|
||||
126
node_modules/kysely/dist/esm/plugin/parse-json-results/parse-json-results-plugin.d.ts
generated
vendored
Normal file
126
node_modules/kysely/dist/esm/plugin/parse-json-results/parse-json-results-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
import type { QueryResult } from '../../driver/database-connection.js';
|
||||
import type { RootOperationNode } from '../../query-compiler/query-compiler.js';
|
||||
import type { UnknownRow } from '../../util/type-utils.js';
|
||||
import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs } from '../kysely-plugin.js';
|
||||
export interface ParseJSONResultsPluginOptions {
|
||||
/**
|
||||
* When `'in-place'`, arrays' and objects' values are parsed in-place. This is
|
||||
* the most time and space efficient option.
|
||||
*
|
||||
* This can result in runtime errors if some objects/arrays are readonly.
|
||||
*
|
||||
* When `'create'`, new arrays and objects are created to avoid such errors.
|
||||
*
|
||||
* Defaults to `'in-place'`.
|
||||
*/
|
||||
objectStrategy?: ObjectStrategy;
|
||||
}
|
||||
type ObjectStrategy = 'in-place' | 'create';
|
||||
/**
|
||||
* Parses JSON strings in query results into JSON objects.
|
||||
*
|
||||
* This plugin can be useful with dialects that don't automatically parse
|
||||
* JSON into objects and arrays but return JSON strings instead.
|
||||
*
|
||||
* To apply this plugin globally, pass an instance of it to the `plugins` option
|
||||
* when creating a new `Kysely` instance:
|
||||
*
|
||||
* ```ts
|
||||
* import * as Sqlite from 'better-sqlite3'
|
||||
* import { Kysely, ParseJSONResultsPlugin, SqliteDialect } from 'kysely'
|
||||
* import type { Database } from 'type-editor' // imaginary module
|
||||
*
|
||||
* const db = new Kysely<Database>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [new ParseJSONResultsPlugin()],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* To apply this plugin to a single query:
|
||||
*
|
||||
* ```ts
|
||||
* import { ParseJSONResultsPlugin } from 'kysely'
|
||||
* import { jsonArrayFrom } from 'kysely/helpers/sqlite'
|
||||
*
|
||||
* const result = await db
|
||||
* .selectFrom('person')
|
||||
* .select((eb) => [
|
||||
* 'id',
|
||||
* 'first_name',
|
||||
* 'last_name',
|
||||
* jsonArrayFrom(
|
||||
* eb.selectFrom('pet')
|
||||
* .whereRef('owner_id', '=', 'person.id')
|
||||
* .select(['name', 'species'])
|
||||
* ).as('pets')
|
||||
* ])
|
||||
* .withPlugin(new ParseJSONResultsPlugin())
|
||||
* .execute()
|
||||
* ```
|
||||
*/
|
||||
export declare class ParseJSONResultsPlugin implements KyselyPlugin {
|
||||
#private;
|
||||
readonly opt: ParseJSONResultsPluginOptions;
|
||||
constructor(opt?: ParseJSONResultsPluginOptions);
|
||||
/**
|
||||
* This is called for each query before it is executed. You can modify the query by
|
||||
* transforming its {@link OperationNode} tree provided in {@link PluginTransformQueryArgs.node | args.node}
|
||||
* and returning the transformed tree. You'd usually want to use an {@link OperationNodeTransformer}
|
||||
* for this.
|
||||
*
|
||||
* If you need to pass some query-related data between this method and `transformResult` you
|
||||
* can use a `WeakMap` with {@link PluginTransformQueryArgs.queryId | args.queryId} as the key:
|
||||
*
|
||||
* ```ts
|
||||
* import type {
|
||||
* KyselyPlugin,
|
||||
* QueryResult,
|
||||
* RootOperationNode,
|
||||
* UnknownRow
|
||||
* } from 'kysely'
|
||||
*
|
||||
* interface MyData {
|
||||
* // ...
|
||||
* }
|
||||
* const data = new WeakMap<any, MyData>()
|
||||
*
|
||||
* const plugin = {
|
||||
* transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
* const something: MyData = {}
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* data.set(args.queryId, something)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.node
|
||||
* },
|
||||
*
|
||||
* async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
* // ...
|
||||
*
|
||||
* const something = data.get(args.queryId)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.result
|
||||
* }
|
||||
* } satisfies KyselyPlugin
|
||||
* ```
|
||||
*
|
||||
* You should use a `WeakMap` instead of a `Map` or some other strong references because `transformQuery`
|
||||
* is not always matched by a call to `transformResult` which would leave orphaned items in the map
|
||||
* and cause a memory leak.
|
||||
*/
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode;
|
||||
/**
|
||||
* This method is called for each query after it has been executed. The result
|
||||
* of the query can be accessed through {@link PluginTransformResultArgs.result | args.result}.
|
||||
* You can modify the result and return the modifier result.
|
||||
*/
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>;
|
||||
}
|
||||
export {};
|
||||
104
node_modules/kysely/dist/esm/plugin/parse-json-results/parse-json-results-plugin.js
generated
vendored
Normal file
104
node_modules/kysely/dist/esm/plugin/parse-json-results/parse-json-results-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
/// <reference types="./parse-json-results-plugin.d.ts" />
|
||||
import { isPlainObject, isString } from '../../util/object-utils.js';
|
||||
/**
|
||||
* Parses JSON strings in query results into JSON objects.
|
||||
*
|
||||
* This plugin can be useful with dialects that don't automatically parse
|
||||
* JSON into objects and arrays but return JSON strings instead.
|
||||
*
|
||||
* To apply this plugin globally, pass an instance of it to the `plugins` option
|
||||
* when creating a new `Kysely` instance:
|
||||
*
|
||||
* ```ts
|
||||
* import * as Sqlite from 'better-sqlite3'
|
||||
* import { Kysely, ParseJSONResultsPlugin, SqliteDialect } from 'kysely'
|
||||
* import type { Database } from 'type-editor' // imaginary module
|
||||
*
|
||||
* const db = new Kysely<Database>({
|
||||
* dialect: new SqliteDialect({
|
||||
* database: new Sqlite(':memory:'),
|
||||
* }),
|
||||
* plugins: [new ParseJSONResultsPlugin()],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* To apply this plugin to a single query:
|
||||
*
|
||||
* ```ts
|
||||
* import { ParseJSONResultsPlugin } from 'kysely'
|
||||
* import { jsonArrayFrom } from 'kysely/helpers/sqlite'
|
||||
*
|
||||
* const result = await db
|
||||
* .selectFrom('person')
|
||||
* .select((eb) => [
|
||||
* 'id',
|
||||
* 'first_name',
|
||||
* 'last_name',
|
||||
* jsonArrayFrom(
|
||||
* eb.selectFrom('pet')
|
||||
* .whereRef('owner_id', '=', 'person.id')
|
||||
* .select(['name', 'species'])
|
||||
* ).as('pets')
|
||||
* ])
|
||||
* .withPlugin(new ParseJSONResultsPlugin())
|
||||
* .execute()
|
||||
* ```
|
||||
*/
|
||||
export class ParseJSONResultsPlugin {
|
||||
opt;
|
||||
#objectStrategy;
|
||||
constructor(opt = {}) {
|
||||
this.opt = opt;
|
||||
this.#objectStrategy = opt.objectStrategy || 'in-place';
|
||||
}
|
||||
// noop
|
||||
transformQuery(args) {
|
||||
return args.node;
|
||||
}
|
||||
async transformResult(args) {
|
||||
return {
|
||||
...args.result,
|
||||
rows: parseArray(args.result.rows, this.#objectStrategy),
|
||||
};
|
||||
}
|
||||
}
|
||||
function parseArray(arr, objectStrategy) {
|
||||
const target = objectStrategy === 'create' ? new Array(arr.length) : arr;
|
||||
for (let i = 0; i < arr.length; ++i) {
|
||||
target[i] = parse(arr[i], objectStrategy);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
function parse(obj, objectStrategy) {
|
||||
if (isString(obj)) {
|
||||
return parseString(obj);
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
return parseArray(obj, objectStrategy);
|
||||
}
|
||||
if (isPlainObject(obj)) {
|
||||
return parseObject(obj, objectStrategy);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
function parseString(str) {
|
||||
if (maybeJson(str)) {
|
||||
try {
|
||||
return parse(JSON.parse(str), 'in-place');
|
||||
}
|
||||
catch (err) {
|
||||
// this catch block is intentionally empty.
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
function maybeJson(value) {
|
||||
return value.match(/^[\[\{]/) != null;
|
||||
}
|
||||
function parseObject(obj, objectStrategy) {
|
||||
const target = objectStrategy === 'create' ? {} : obj;
|
||||
for (const key in obj) {
|
||||
target[key] = parse(obj[key], objectStrategy);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
66
node_modules/kysely/dist/esm/plugin/with-schema/with-schema-plugin.d.ts
generated
vendored
Normal file
66
node_modules/kysely/dist/esm/plugin/with-schema/with-schema-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { QueryResult } from '../../driver/database-connection.js';
|
||||
import type { RootOperationNode } from '../../query-compiler/query-compiler.js';
|
||||
import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs } from '../kysely-plugin.js';
|
||||
import type { UnknownRow } from '../../util/type-utils.js';
|
||||
export declare class WithSchemaPlugin implements KyselyPlugin {
|
||||
#private;
|
||||
constructor(schema: string);
|
||||
/**
|
||||
* This is called for each query before it is executed. You can modify the query by
|
||||
* transforming its {@link OperationNode} tree provided in {@link PluginTransformQueryArgs.node | args.node}
|
||||
* and returning the transformed tree. You'd usually want to use an {@link OperationNodeTransformer}
|
||||
* for this.
|
||||
*
|
||||
* If you need to pass some query-related data between this method and `transformResult` you
|
||||
* can use a `WeakMap` with {@link PluginTransformQueryArgs.queryId | args.queryId} as the key:
|
||||
*
|
||||
* ```ts
|
||||
* import type {
|
||||
* KyselyPlugin,
|
||||
* QueryResult,
|
||||
* RootOperationNode,
|
||||
* UnknownRow
|
||||
* } from 'kysely'
|
||||
*
|
||||
* interface MyData {
|
||||
* // ...
|
||||
* }
|
||||
* const data = new WeakMap<any, MyData>()
|
||||
*
|
||||
* const plugin = {
|
||||
* transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
* const something: MyData = {}
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* data.set(args.queryId, something)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.node
|
||||
* },
|
||||
*
|
||||
* async transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
* // ...
|
||||
*
|
||||
* const something = data.get(args.queryId)
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* return args.result
|
||||
* }
|
||||
* } satisfies KyselyPlugin
|
||||
* ```
|
||||
*
|
||||
* You should use a `WeakMap` instead of a `Map` or some other strong references because `transformQuery`
|
||||
* is not always matched by a call to `transformResult` which would leave orphaned items in the map
|
||||
* and cause a memory leak.
|
||||
*/
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode;
|
||||
/**
|
||||
* This method is called for each query after it has been executed. The result
|
||||
* of the query can be accessed through {@link PluginTransformResultArgs.result | args.result}.
|
||||
* You can modify the result and return the modifier result.
|
||||
*/
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>;
|
||||
}
|
||||
14
node_modules/kysely/dist/esm/plugin/with-schema/with-schema-plugin.js
generated
vendored
Normal file
14
node_modules/kysely/dist/esm/plugin/with-schema/with-schema-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/// <reference types="./with-schema-plugin.d.ts" />
|
||||
import { WithSchemaTransformer } from './with-schema-transformer.js';
|
||||
export class WithSchemaPlugin {
|
||||
#transformer;
|
||||
constructor(schema) {
|
||||
this.#transformer = new WithSchemaTransformer(schema);
|
||||
}
|
||||
transformQuery(args) {
|
||||
return this.#transformer.transformNode(args.node, args.queryId);
|
||||
}
|
||||
async transformResult(args) {
|
||||
return args.result;
|
||||
}
|
||||
}
|
||||
18
node_modules/kysely/dist/esm/plugin/with-schema/with-schema-transformer.d.ts
generated
vendored
Normal file
18
node_modules/kysely/dist/esm/plugin/with-schema/with-schema-transformer.d.ts
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { AggregateFunctionNode } from '../../operation-node/aggregate-function-node.js';
|
||||
import type { FunctionNode } from '../../operation-node/function-node.js';
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import type { OperationNode } from '../../operation-node/operation-node.js';
|
||||
import type { ReferencesNode } from '../../operation-node/references-node.js';
|
||||
import { SchemableIdentifierNode } from '../../operation-node/schemable-identifier-node.js';
|
||||
import type { SelectModifierNode } from '../../operation-node/select-modifier-node.js';
|
||||
import type { QueryId } from '../../util/query-id.js';
|
||||
export declare class WithSchemaTransformer extends OperationNodeTransformer {
|
||||
#private;
|
||||
constructor(schema: string);
|
||||
protected transformNodeImpl<T extends OperationNode>(node: T, queryId: QueryId): T;
|
||||
protected transformSchemableIdentifier(node: SchemableIdentifierNode, queryId: QueryId): SchemableIdentifierNode;
|
||||
protected transformReferences(node: ReferencesNode, queryId: QueryId): ReferencesNode;
|
||||
protected transformAggregateFunction(node: AggregateFunctionNode, queryId: QueryId): AggregateFunctionNode;
|
||||
protected transformFunction(node: FunctionNode, queryId: QueryId): FunctionNode;
|
||||
protected transformSelectModifier(node: SelectModifierNode, queryId: QueryId): SelectModifierNode;
|
||||
}
|
||||
197
node_modules/kysely/dist/esm/plugin/with-schema/with-schema-transformer.js
generated
vendored
Normal file
197
node_modules/kysely/dist/esm/plugin/with-schema/with-schema-transformer.js
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
/// <reference types="./with-schema-transformer.d.ts" />
|
||||
import { AliasNode } from '../../operation-node/alias-node.js';
|
||||
import { IdentifierNode } from '../../operation-node/identifier-node.js';
|
||||
import { JoinNode } from '../../operation-node/join-node.js';
|
||||
import { ListNode } from '../../operation-node/list-node.js';
|
||||
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
|
||||
import { SchemableIdentifierNode } from '../../operation-node/schemable-identifier-node.js';
|
||||
import { TableNode } from '../../operation-node/table-node.js';
|
||||
import { UsingNode } from '../../operation-node/using-node.js';
|
||||
import { freeze } from '../../util/object-utils.js';
|
||||
// This object exist only so that we get a type error when a new RootOperationNode
|
||||
// is added. If you get a type error here, make sure to add the new root node and
|
||||
// handle it correctly in the transformer.
|
||||
//
|
||||
// DO NOT REFACTOR THIS EVEN IF IT SEEMS USELESS TO YOU!
|
||||
const ROOT_OPERATION_NODES = freeze({
|
||||
AlterTableNode: true,
|
||||
CreateIndexNode: true,
|
||||
CreateSchemaNode: true,
|
||||
CreateTableNode: true,
|
||||
CreateTypeNode: true,
|
||||
CreateViewNode: true,
|
||||
RefreshMaterializedViewNode: true,
|
||||
DeleteQueryNode: true,
|
||||
DropIndexNode: true,
|
||||
DropSchemaNode: true,
|
||||
DropTableNode: true,
|
||||
DropTypeNode: true,
|
||||
DropViewNode: true,
|
||||
InsertQueryNode: true,
|
||||
RawNode: true,
|
||||
SelectQueryNode: true,
|
||||
UpdateQueryNode: true,
|
||||
MergeQueryNode: true,
|
||||
});
|
||||
const SCHEMALESS_FUNCTIONS = {
|
||||
json_agg: true,
|
||||
to_json: true,
|
||||
};
|
||||
export class WithSchemaTransformer extends OperationNodeTransformer {
|
||||
#schema;
|
||||
#schemableIds = new Set();
|
||||
#ctes = new Set();
|
||||
constructor(schema) {
|
||||
super();
|
||||
this.#schema = schema;
|
||||
}
|
||||
transformNodeImpl(node, queryId) {
|
||||
if (!this.#isRootOperationNode(node)) {
|
||||
return super.transformNodeImpl(node, queryId);
|
||||
}
|
||||
const ctes = this.#collectCTEs(node);
|
||||
for (const cte of ctes) {
|
||||
this.#ctes.add(cte);
|
||||
}
|
||||
const tables = this.#collectSchemableIds(node);
|
||||
for (const table of tables) {
|
||||
this.#schemableIds.add(table);
|
||||
}
|
||||
const transformed = super.transformNodeImpl(node, queryId);
|
||||
for (const table of tables) {
|
||||
this.#schemableIds.delete(table);
|
||||
}
|
||||
for (const cte of ctes) {
|
||||
this.#ctes.delete(cte);
|
||||
}
|
||||
return transformed;
|
||||
}
|
||||
transformSchemableIdentifier(node, queryId) {
|
||||
const transformed = super.transformSchemableIdentifier(node, queryId);
|
||||
if (transformed.schema || !this.#schemableIds.has(node.identifier.name)) {
|
||||
return transformed;
|
||||
}
|
||||
return {
|
||||
...transformed,
|
||||
schema: IdentifierNode.create(this.#schema),
|
||||
};
|
||||
}
|
||||
transformReferences(node, queryId) {
|
||||
const transformed = super.transformReferences(node, queryId);
|
||||
if (transformed.table.table.schema) {
|
||||
return transformed;
|
||||
}
|
||||
return {
|
||||
...transformed,
|
||||
table: TableNode.createWithSchema(this.#schema, transformed.table.table.identifier.name),
|
||||
};
|
||||
}
|
||||
transformAggregateFunction(node, queryId) {
|
||||
return {
|
||||
...super.transformAggregateFunction({ ...node, aggregated: [] }, queryId),
|
||||
aggregated: this.#transformTableArgsWithoutSchemas(node, queryId, 'aggregated'),
|
||||
};
|
||||
}
|
||||
transformFunction(node, queryId) {
|
||||
return {
|
||||
...super.transformFunction({ ...node, arguments: [] }, queryId),
|
||||
arguments: this.#transformTableArgsWithoutSchemas(node, queryId, 'arguments'),
|
||||
};
|
||||
}
|
||||
transformSelectModifier(node, queryId) {
|
||||
return {
|
||||
...super.transformSelectModifier({ ...node, of: undefined }, queryId),
|
||||
of: node.of?.map((item) => TableNode.is(item) && !item.table.schema
|
||||
? {
|
||||
...item,
|
||||
table: this.transformIdentifier(item.table.identifier, queryId),
|
||||
}
|
||||
: this.transformNode(item, queryId)),
|
||||
};
|
||||
}
|
||||
#transformTableArgsWithoutSchemas(node, queryId, argsKey) {
|
||||
return SCHEMALESS_FUNCTIONS[node.func]
|
||||
? node[argsKey].map((arg) => !TableNode.is(arg) || arg.table.schema
|
||||
? this.transformNode(arg, queryId)
|
||||
: {
|
||||
...arg,
|
||||
table: this.transformIdentifier(arg.table.identifier, queryId),
|
||||
})
|
||||
: this.transformNodeList(node[argsKey], queryId);
|
||||
}
|
||||
#isRootOperationNode(node) {
|
||||
return node.kind in ROOT_OPERATION_NODES;
|
||||
}
|
||||
#collectSchemableIds(node) {
|
||||
const schemableIds = new Set();
|
||||
if ('name' in node && node.name && SchemableIdentifierNode.is(node.name)) {
|
||||
this.#collectSchemableId(node.name, schemableIds);
|
||||
}
|
||||
if ('from' in node && node.from) {
|
||||
for (const from of node.from.froms) {
|
||||
this.#collectSchemableIdsFromTableExpr(from, schemableIds);
|
||||
}
|
||||
}
|
||||
if ('into' in node && node.into) {
|
||||
this.#collectSchemableIdsFromTableExpr(node.into, schemableIds);
|
||||
}
|
||||
if ('table' in node && node.table) {
|
||||
this.#collectSchemableIdsFromTableExpr(node.table, schemableIds);
|
||||
}
|
||||
if ('joins' in node && node.joins) {
|
||||
for (const join of node.joins) {
|
||||
this.#collectSchemableIdsFromTableExpr(join.table, schemableIds);
|
||||
}
|
||||
}
|
||||
if ('using' in node && node.using) {
|
||||
if (JoinNode.is(node.using)) {
|
||||
this.#collectSchemableIdsFromTableExpr(node.using.table, schemableIds);
|
||||
}
|
||||
else {
|
||||
this.#collectSchemableIdsFromTableExpr(node.using, schemableIds);
|
||||
}
|
||||
}
|
||||
return schemableIds;
|
||||
}
|
||||
#collectCTEs(node) {
|
||||
const ctes = new Set();
|
||||
if ('with' in node && node.with) {
|
||||
this.#collectCTEIds(node.with, ctes);
|
||||
}
|
||||
return ctes;
|
||||
}
|
||||
#collectSchemableIdsFromTableExpr(node, schemableIds) {
|
||||
if (TableNode.is(node)) {
|
||||
return this.#collectSchemableId(node.table, schemableIds);
|
||||
}
|
||||
if (AliasNode.is(node) && TableNode.is(node.node)) {
|
||||
return this.#collectSchemableId(node.node.table, schemableIds);
|
||||
}
|
||||
if (ListNode.is(node)) {
|
||||
for (const table of node.items) {
|
||||
this.#collectSchemableIdsFromTableExpr(table, schemableIds);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (UsingNode.is(node)) {
|
||||
for (const table of node.tables) {
|
||||
this.#collectSchemableIdsFromTableExpr(table, schemableIds);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
#collectSchemableId(node, schemableIds) {
|
||||
const id = node.identifier.name;
|
||||
if (!this.#schemableIds.has(id) && !this.#ctes.has(id)) {
|
||||
schemableIds.add(id);
|
||||
}
|
||||
}
|
||||
#collectCTEIds(node, ctes) {
|
||||
for (const expr of node.expressions) {
|
||||
const cteId = expr.name.table.table.identifier.name;
|
||||
if (!this.#ctes.has(cteId)) {
|
||||
ctes.add(cteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user