Compare commits

...

1 Commits

Author SHA1 Message Date
Sasha
fc14275c8b start 2025-01-16 20:39:31 +02:00
30 changed files with 1534 additions and 7 deletions

1
packages/db-mysql/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/migrations

View File

@@ -0,0 +1,10 @@
.tmp
**/.git
**/.hg
**/.pnp.*
**/.svn
**/.yarn/**
**/build
**/dist/**
**/node_modules
**/temp

15
packages/db-mysql/.swcrc Normal file
View File

@@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"sourceMaps": true,
"jsc": {
"target": "esnext",
"parser": {
"syntax": "typescript",
"tsx": true,
"dts": true
}
},
"module": {
"type": "es6"
}
}

View File

@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2018-2025 Payload CMS, Inc. <info@payloadcms.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,30 @@
# Payload SQLite Adapter
Official SQLite adapter for [Payload](https://payloadcms.com).
- [Main Repository](https://github.com/payloadcms/payload)
- [Payload Docs](https://payloadcms.com/docs)
## Installation
```bash
npm install @payloadcms/db-sqlite
```
## Usage
```ts
import { buildConfig } from 'payload/config'
import { sqliteAdapter } from '@payloadcms/db-sqlite'
export default buildConfig({
db: sqliteAdapter({
client: {
url: process.env.DATABASE_URI,
},
}),
// ...rest of config
})
```
More detailed usage can be found in the [Payload Docs](https://payloadcms.com/docs/configuration/overview).

View File

@@ -0,0 +1,38 @@
import * as esbuild from 'esbuild'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
import { commonjs } from '@hyrious/esbuild-plugin-commonjs'
async function build() {
const resultServer = await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'node',
format: 'esm',
outfile: 'dist/index.js',
splitting: false,
external: [
'*.scss',
'*.css',
'drizzle-kit',
'libsql',
'pg',
'@payloadcms/translations',
'@payloadcms/drizzle',
'payload',
'payload/*',
],
minify: true,
metafile: true,
tsconfig: path.resolve(dirname, './tsconfig.json'),
plugins: [commonjs()],
sourcemap: true,
})
console.log('db-sqlite bundled successfully')
fs.writeFileSync('meta_server.json', JSON.stringify(resultServer.metafile))
}
await build()

View File

@@ -0,0 +1,18 @@
import { rootEslintConfig, rootParserOptions } from '../../eslint.config.js'
/** @typedef {import('eslint').Linter.Config} Config */
/** @type {Config[]} */
export const index = [
...rootEslintConfig,
{
languageOptions: {
parserOptions: {
...rootParserOptions,
tsconfigRootDir: import.meta.dirname,
},
},
},
]
export default index

View File

@@ -0,0 +1,128 @@
{
"name": "@payloadcms/db-mysql",
"version": "3.17.1",
"description": "The officially supported MySQL database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
"url": "https://github.com/payloadcms/payload.git",
"directory": "packages/db-mysql"
},
"license": "MIT",
"author": "Payload <dev@payloadcms.com> (https://payloadcms.com)",
"maintainers": [
{
"name": "Payload",
"email": "info@payloadcms.com",
"url": "https://payloadcms.com"
}
],
"type": "module",
"exports": {
".": {
"import": "./src/index.ts",
"require": "./src/index.ts",
"types": "./src/index.ts"
},
"./types": {
"import": "./src/types.ts",
"require": "./src/types.ts",
"types": "./src/types.ts"
},
"./migration-utils": {
"import": "./src/exports/migration-utils.ts",
"require": "./src/exports/migration-utils.ts",
"types": "./src/exports/migration-utils.ts"
},
"./drizzle": {
"import": "./src/drizzle-proxy/index.ts",
"types": "./src/drizzle-proxy/index.ts",
"default": "./src/drizzle-proxy/index.ts"
},
"./drizzle/mysql-core": {
"import": "./src/drizzle-proxy/mysql-core.ts",
"types": "./src/drizzle-proxy/mysql-core.ts",
"default": "./src/drizzle-proxy/mysql-core.ts"
},
"./drizzle/relations": {
"import": "./src/drizzle-proxy/relations.ts",
"types": "./src/drizzle-proxy/relations.ts",
"default": "./src/drizzle-proxy/relations.ts"
}
},
"main": "./src/index.ts",
"types": "./src/types.ts",
"files": [
"dist",
"mock.js"
],
"scripts": {
"build": "pnpm build:swc && pnpm build:types",
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
"build:types": "tsc --emitDeclarationOnly --outDir dist",
"clean": "rimraf -g {dist,*.tsbuildinfo}",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"prepack": "pnpm clean && pnpm turbo build",
"prepublishOnly": "pnpm clean && pnpm turbo build"
},
"dependencies": {
"@payloadcms/drizzle": "workspace:*",
"console-table-printer": "2.12.1",
"drizzle-kit": "0.28.0",
"drizzle-orm": "0.36.1",
"mysql2": "3.12.0",
"prompts": "2.4.2",
"to-snake-case": "1.0.0",
"uuid": "9.0.0"
},
"devDependencies": {
"@payloadcms/eslint-config": "workspace:*",
"@types/to-snake-case": "1.0.0",
"payload": "workspace:*"
},
"peerDependencies": {
"payload": "workspace:*"
},
"publishConfig": {
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./types": {
"import": "./dist/types.js",
"require": "./dist/types.js",
"types": "./dist/types.d.ts"
},
"./migration-utils": {
"import": "./dist/exports/migration-utils.js",
"require": "./dist/exports/migration-utils.js",
"types": "./dist/exports/migration-utils.d.ts"
},
"./drizzle": {
"import": "./dist/drizzle-proxy/index.js",
"types": "./dist/drizzle-proxy/index.d.ts",
"default": "./dist/drizzle-proxy/index.js"
},
"./drizzle/sqlite-core": {
"import": "./dist/drizzle-proxy/sqlite-core.js",
"types": "./dist/drizzle-proxy/sqlite-core.d.ts",
"default": "./dist/drizzle-proxy/sqlite-core.js"
},
"./drizzle/libsql": {
"import": "./dist/drizzle-proxy/libsql.js",
"types": "./dist/drizzle-proxy/libsql.d.ts",
"default": "./dist/drizzle-proxy/libsql.js"
},
"./drizzle/relations": {
"import": "./dist/drizzle-proxy/relations.js",
"types": "./dist/drizzle-proxy/relations.d.ts",
"default": "./dist/drizzle-proxy/relations.js"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts"
}
}

View File

@@ -0,0 +1,132 @@
import type { ColumnToCodeConverter } from '@payloadcms/drizzle/types'
export const columnToCodeConverter: ColumnToCodeConverter = ({
adapter,
addImport,
column,
locales,
tableKey,
}) => {
let columnBuilderFn: string = column.type
const columnBuilderArgsArray: string[] = []
let defaultStatement: null | string = null
switch (column.type) {
case 'boolean': {
columnBuilderFn = 'integer'
columnBuilderArgsArray.push("mode: 'boolean'")
break
}
case 'enum': {
let options: string[]
if ('locale' in column) {
options = locales
} else {
options = column.options
}
columnBuilderFn = 'text'
columnBuilderArgsArray.push(`enum: [${options.map((locale) => `'${locale}'`).join(', ')}]`)
break
}
case 'geometry':
case 'jsonb': {
columnBuilderFn = 'text'
columnBuilderArgsArray.push("mode: 'json'")
break
}
case 'serial': {
columnBuilderFn = 'integer'
break
}
case 'timestamp': {
columnBuilderFn = 'text'
defaultStatement = `default(sql\`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))\`)`
break
}
case 'uuid': {
columnBuilderFn = 'text'
if (column.defaultRandom) {
addImport('crypto', 'randomUUID')
defaultStatement = `$defaultFn(() => randomUUID())`
}
break
}
case 'varchar': {
columnBuilderFn = 'text'
break
}
default: {
columnBuilderFn = column.type
}
}
addImport(`${adapter.packageName}/drizzle/sqlite-core`, columnBuilderFn)
let columnBuilderArgs = ''
if (columnBuilderArgsArray.length) {
columnBuilderArgs = `, {${columnBuilderArgsArray.join(',')}}`
}
let code = `${columnBuilderFn}('${column.name}'${columnBuilderArgs})`
if (column.notNull) {
code = `${code}.notNull()`
}
if (column.primaryKey) {
let arg = ''
if (column.type === 'integer' && column.autoIncrement) {
arg = `{ autoIncrement: true }`
}
code = `${code}.primaryKey(${arg})`
}
if (defaultStatement) {
code = `${code}.${defaultStatement}`
} else if (typeof column.default !== 'undefined') {
let sanitizedDefault = column.default
if (column.type === 'jsonb' || column.type === 'geometry') {
sanitizedDefault = `'${JSON.stringify(column.default)}'`
} else if (typeof column.default === 'string') {
sanitizedDefault = JSON.stringify(column.default)
} else if (column.type === 'numeric') {
sanitizedDefault = `'${column.default}'`
}
code = `${code}.default(${sanitizedDefault})`
}
if (column.reference) {
let callback = `()`
if (column.reference.table === tableKey) {
addImport(`${adapter.packageName}/drizzle/sqlite-core`, 'type AnySQLiteColumn')
callback = `${callback}: AnySQLiteColumn`
}
callback = `${callback} => ${column.reference.table}.${column.reference.name}`
code = `${code}.references(${callback}, {
${column.reference.onDelete ? `onDelete: '${column.reference.onDelete}'` : ''}
})`
}
return code
}

View File

@@ -0,0 +1,57 @@
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { Connect } from 'payload'
import { pushDevSchema } from '@payloadcms/drizzle'
import { drizzle } from 'drizzle-orm/mysql2'
import type { MySQLAdapter } from './types.js'
export const connect: Connect = async function connect(
this: MySQLAdapter,
options = {
hotReload: false,
},
) {
const { hotReload } = options
this.schema = {
...this.tables,
...this.relations,
}
try {
const logger = this.logger || false
// this.drizzle = drizzle(this.client, {})
if (!hotReload) {
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
this.payload.logger.info(`---- DROPPING TABLES ----`)
await this.dropDatabase({ adapter: this })
this.payload.logger.info('---- DROPPED TABLES ----')
}
}
} catch (err) {
this.payload.logger.error({ err, msg: `Error: cannot connect to SQLite: ${err.message}` })
if (typeof this.rejectInitializing === 'function') {
this.rejectInitializing()
}
process.exit(1)
}
// Only push schema if not in production
if (
process.env.NODE_ENV !== 'production' &&
process.env.PAYLOAD_MIGRATING !== 'true' &&
this.push !== false
) {
await pushDevSchema(this as unknown as DrizzleAdapter)
}
if (typeof this.resolveInitializing === 'function') {
this.resolveInitializing()
}
if (process.env.NODE_ENV === 'production' && this.prodMigrations) {
await this.migrate({ migrations: this.prodMigrations })
}
}

View File

@@ -0,0 +1,32 @@
import type { ChainedMethods } from '@payloadcms/drizzle/types'
import { chainMethods } from '@payloadcms/drizzle'
import { count } from 'drizzle-orm'
import type { CountDistinct, MySQLAdapter } from './types.js'
export const countDistinct: CountDistinct = async function countDistinct(
this: MySQLAdapter,
{ db, joins, tableName, where },
) {
const chainedMethods: ChainedMethods = []
joins.forEach(({ condition, table }) => {
chainedMethods.push({
args: [table, condition],
method: 'leftJoin',
})
})
const countResult = await chainMethods({
methods: chainedMethods,
query: db
.select({
count: count(),
})
.from(this.tables[tableName])
.where(where),
})
return Number(countResult[0].count)
}

View File

@@ -0,0 +1,9 @@
export const convertPathToJSONTraversal = (incomingSegments: string[]): string => {
const segments = [...incomingSegments]
segments.shift()
return segments.reduce((res, segment) => {
const formattedSegment = Number.isNaN(parseInt(segment)) ? `'${segment}'` : segment
return `${res}->>${formattedSegment}`
}, '')
}

View File

@@ -0,0 +1,86 @@
import type { CreateJSONQueryArgs } from '@payloadcms/drizzle/types'
type FromArrayArgs = {
isRoot?: true
operator: string
pathSegments: string[]
table: string
treatAsArray?: string[]
value: boolean | number | string
}
const fromArray = ({
isRoot,
operator,
pathSegments,
table,
treatAsArray,
value,
}: FromArrayArgs) => {
const newPathSegments = pathSegments.slice(1)
const alias = `${pathSegments[isRoot ? 0 : 1]}_alias_${newPathSegments.length}`
return `EXISTS (
SELECT 1
FROM json_each(${table}.${pathSegments[0]}) AS ${alias}
WHERE ${createJSONQuery({
operator,
pathSegments: newPathSegments,
table: alias,
treatAsArray,
value,
})}
)`
}
type CreateConstraintArgs = {
alias?: string
operator: string
pathSegments: string[]
treatAsArray?: string[]
value: boolean | number | string
}
const createConstraint = ({
alias,
operator,
pathSegments,
value,
}: CreateConstraintArgs): string => {
const newAlias = `${pathSegments[0]}_alias_${pathSegments.length - 1}`
let formattedValue = value
let formattedOperator = operator
if (['contains', 'like'].includes(operator)) {
formattedOperator = 'like'
formattedValue = `%${value}%`
} else if (operator === 'equals') {
formattedOperator = '='
}
return `EXISTS (
SELECT 1
FROM json_each(${alias}.value -> '${pathSegments[0]}') AS ${newAlias}
WHERE ${newAlias}.value ->> '${pathSegments[1]}' ${formattedOperator} '${formattedValue}'
)`
}
export const createJSONQuery = ({
operator,
pathSegments,
table,
treatAsArray,
value,
}: CreateJSONQueryArgs): string => {
if (treatAsArray.includes(pathSegments[1])) {
return fromArray({
operator,
pathSegments,
table,
treatAsArray,
value,
})
}
return createConstraint({ alias: table, operator, pathSegments, treatAsArray, value })
}

View File

@@ -0,0 +1,15 @@
import type { DrizzleMySQLSnapshotJSON } from 'drizzle-kit/api'
// TODO: check, prob right
export const defaultDrizzleSnapshot: DrizzleMySQLSnapshotJSON = {
id: '00000000-0000-0000-0000-000000000000',
_meta: {
columns: {},
tables: {},
},
dialect: 'mysql',
prevId: '00000000-0000-0000-0000-00000000000',
tables: {},
version: '5',
views: {},
}

View File

@@ -0,0 +1,6 @@
import type { DeleteWhere } from './types.js'
export const deleteWhere: DeleteWhere = async function deleteWhere({ db, tableName, where }) {
const table = this.tables[tableName]
await db.delete(table).where(where)
}

View File

@@ -0,0 +1 @@
export * from 'drizzle-orm'

View File

@@ -0,0 +1 @@
export * from 'drizzle-orm/libsql'

View File

@@ -0,0 +1 @@
export * from 'drizzle-orm/relations'

View File

@@ -0,0 +1 @@
export * from 'drizzle-orm/sqlite-core'

View File

@@ -0,0 +1,20 @@
import type { DropDatabase } from './types.js'
const getTables = (adapter) => {
return adapter.client.execute(`
SELECT table_name AS name
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = DATABASE();
`)
}
const dropTables = (adapter, rows) => {
const multi = `
${rows.map(({ name }) => `DROP TABLE IF EXISTS \`${name}\``).join(';\n ')};`
return adapter.client.executeMultiple(multi)
}
export const dropDatabase: DropDatabase = async function dropDatabase({ adapter }) {
const result = await getTables(adapter)
await dropTables(adapter, result.rows)
}

View File

@@ -0,0 +1,15 @@
import { sql } from 'drizzle-orm'
import type { Execute } from './types.js'
export const execute: Execute<any> = function execute({ db, drizzle, raw, sql: statement }) {
const executeFrom = db ?? drizzle
if (raw) {
const result = executeFrom.execute(sql.raw(raw))
return result
} else {
const result = executeFrom.execute(statement)
return result
}
}

View File

@@ -0,0 +1,189 @@
import type { Operators } from '@payloadcms/drizzle'
import type { DatabaseAdapterObj, Payload } from 'payload'
import {
beginTransaction,
buildCreateMigration,
commitTransaction,
count,
countGlobalVersions,
countVersions,
create,
createGlobal,
createGlobalVersion,
createSchemaGenerator,
createVersion,
deleteMany,
deleteOne,
deleteVersions,
destroy,
find,
findGlobal,
findGlobalVersions,
findMigrationDir,
findOne,
findVersions,
migrate,
migrateDown,
migrateFresh,
migrateRefresh,
migrateReset,
migrateStatus,
operatorMap,
queryDrafts,
rollbackTransaction,
updateGlobal,
updateGlobalVersion,
updateOne,
updateVersion,
} from '@payloadcms/drizzle'
import { like } from 'drizzle-orm'
import { createDatabaseAdapter, defaultBeginTransaction } from 'payload'
import { fileURLToPath } from 'url'
import type { Args, MySQLAdapter } from './types.js'
import { columnToCodeConverter } from './columnToCodeConverter.js'
import { connect } from './connect.js'
import { countDistinct } from './countDistinct.js'
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal.js'
import { createJSONQuery } from './createJSONQuery/index.js'
import { defaultDrizzleSnapshot } from './defaultSnapshot.js'
import { deleteWhere } from './deleteWhere.js'
import { dropDatabase } from './dropDatabase.js'
import { execute } from './execute.js'
import { init } from './init.js'
import { insert } from './insert.js'
import { requireDrizzleKit } from './requireDrizzleKit.js'
export type { MigrateDownArgs, MigrateUpArgs } from './types.js'
export { sql } from 'drizzle-orm'
const filename = fileURLToPath(import.meta.url)
export function mysqlAdapter(args: Args): DatabaseAdapterObj<MySQLAdapter> {
const sqliteIDType = args.idType || 'number'
const payloadIDType = sqliteIDType === 'uuid' ? 'text' : 'number'
function adapter({ payload }: { payload: Payload }) {
const migrationDir = findMigrationDir(args.migrationDir)
let resolveInitializing
let rejectInitializing
const initializing = new Promise<void>((res, rej) => {
resolveInitializing = res
rejectInitializing = rej
})
// sqlite's like operator is case-insensitive, so we overwrite the DrizzleAdapter operators to not use ilike
const operators = {
...operatorMap,
contains: like,
like,
} as unknown as Operators
return createDatabaseAdapter<MySQLAdapter>({
name: 'mysql',
afterSchemaInit: args.afterSchemaInit ?? [],
autoIncrement: args.autoIncrement ?? false,
beforeSchemaInit: args.beforeSchemaInit ?? [],
client: undefined,
clientConfig: args.client,
defaultDrizzleSnapshot,
drizzle: undefined,
features: {
json: true,
},
fieldConstraints: {},
generateSchema: createSchemaGenerator({
columnToCodeConverter,
corePackageSuffix: 'sqlite-core',
defaultOutputFile: args.generateSchemaOutputFile,
tableImport: 'sqliteTable',
}),
idType: sqliteIDType,
initializing,
localesSuffix: args.localesSuffix || '_locales',
logger: args.logger,
operators,
prodMigrations: args.prodMigrations,
push: args.push,
rawRelations: {},
rawTables: {},
relations: {},
relationshipsSuffix: args.relationshipsSuffix || '_rels',
schema: {},
schemaName: args.schemaName,
sessions: {},
tableNameMap: new Map<string, string>(),
tables: {},
transactionOptions: args.transactionOptions || undefined,
versionsSuffix: args.versionsSuffix || '_v',
// DatabaseAdapter
beginTransaction: args.transactionOptions ? beginTransaction : defaultBeginTransaction(),
commitTransaction,
connect,
convertPathToJSONTraversal,
count,
countDistinct,
countGlobalVersions,
countVersions,
create,
createGlobal,
createGlobalVersion,
createJSONQuery,
createMigration: buildCreateMigration({
executeMethod: 'run',
filename,
sanitizeStatements({ sqlExecute, statements }) {
return statements
.map((statement) => `${sqlExecute}${statement?.replaceAll('`', '\\`')}\`)`)
.join('\n')
},
}),
createVersion,
defaultIDType: payloadIDType,
deleteMany,
deleteOne,
deleteVersions,
deleteWhere,
destroy,
dropDatabase,
execute,
find,
findGlobal,
findGlobalVersions,
findOne,
findVersions,
indexes: new Set<string>(),
init,
insert,
migrate,
migrateDown,
migrateFresh,
migrateRefresh,
migrateReset,
migrateStatus,
migrationDir,
packageName: '@payloadcms/db-sqlite',
payload,
queryDrafts,
rejectInitializing,
requireDrizzleKit,
resolveInitializing,
rollbackTransaction,
updateGlobal,
updateGlobalVersion,
updateOne,
updateVersion,
upsert: updateOne,
})
}
return {
defaultIDType: payloadIDType,
init: adapter,
}
}

View File

@@ -0,0 +1,39 @@
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { Init } from 'payload'
import { buildDrizzleRelations, buildRawSchema, executeSchemaHooks } from '@payloadcms/drizzle'
import type { MySQLAdapter } from './types.js'
import { buildDrizzleTable } from './schema/buildDrizzleTable.js'
import { setColumnID } from './schema/setColumnID.js'
export const init: Init = async function init(this: MySQLAdapter) {
let locales: string[] | undefined
this.rawRelations = {}
this.rawTables = {}
if (this.payload.config.localization) {
locales = this.payload.config.localization.locales.map(({ code }) => code)
}
const adapter = this as unknown as DrizzleAdapter
buildRawSchema({
adapter,
setColumnID,
})
await executeSchemaHooks({ type: 'beforeSchemaInit', adapter: this })
for (const tableName in this.rawTables) {
buildDrizzleTable({ adapter, locales, rawTable: this.rawTables[tableName] })
}
buildDrizzleRelations({
adapter,
})
await executeSchemaHooks({ type: 'afterSchemaInit', adapter: this })
}

View File

@@ -0,0 +1,34 @@
import { eq } from 'drizzle-orm'
import type { Insert } from './types.js'
export const insert: Insert = async function insert({
db,
onConflictDoUpdate,
tableName,
values,
}): Promise<Record<string, unknown>[]> {
const table = this.tables[tableName]
let IDPromise: Promise<number | string>
if (onConflictDoUpdate) {
IDPromise = db
.insert(table)
.values(values)
.onDuplicateKeyUpdate(onConflictDoUpdate)
.$returningId()
.then(([{ id }]) => id)
} else {
IDPromise = db
.insert(table)
.values(values)
.$returningId()
.then(([{ id }]) => id)
}
const id = await IDPromise
const docs = await db.select().from(table).where(eq(table.id, id))
return docs
}

View File

@@ -0,0 +1,19 @@
import type { RequireDrizzleKit } from '@payloadcms/drizzle/types'
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
export const requireDrizzleKit: RequireDrizzleKit = () => {
const {
generateMySQLDrizzleJson,
generateMySQLMigration,
pushSQLiteSchema,
} = require('drizzle-kit/api')
return {
generateDrizzleJson: generateMySQLDrizzleJson,
generateMigration: generateMySQLMigration,
pushSchema: pushSQLiteSchema,
}
}

View File

@@ -0,0 +1,163 @@
import type { BuildDrizzleTable, RawColumn } from '@payloadcms/drizzle/types'
import type { ForeignKeyBuilder, IndexBuilder } from 'drizzle-orm/sqlite-core'
import { sql } from 'drizzle-orm'
import {
foreignKey,
index,
integer,
numeric,
sqliteTable,
text,
uniqueIndex,
} from 'drizzle-orm/sqlite-core'
import { v4 as uuidv4 } from 'uuid'
const rawColumnBuilderMap: Partial<Record<RawColumn['type'], any>> = {
integer,
numeric,
text,
}
export const buildDrizzleTable: BuildDrizzleTable = ({ adapter, locales, rawTable }) => {
const columns: Record<string, any> = {}
for (const [key, column] of Object.entries(rawTable.columns)) {
switch (column.type) {
case 'boolean': {
columns[key] = integer(column.name, { mode: 'boolean' })
break
}
case 'enum':
if ('locale' in column) {
columns[key] = text(column.name, { enum: locales as [string, ...string[]] })
} else {
columns[key] = text(column.name, { enum: column.options as [string, ...string[]] })
}
break
case 'geometry':
case 'jsonb': {
columns[key] = text(column.name, { mode: 'json' })
break
}
case 'serial': {
columns[key] = integer(column.name)
break
}
case 'timestamp': {
let builder = text(column.name)
if (column.defaultNow) {
builder = builder.default(sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`)
}
columns[key] = builder
break
}
case 'uuid': {
let builder = text(column.name, { length: 36 })
if (column.defaultRandom) {
builder = builder.$defaultFn(() => uuidv4())
}
columns[key] = builder
break
}
case 'varchar': {
columns[key] = text(column.name)
break
}
default:
columns[key] = rawColumnBuilderMap[column.type](column.name)
break
}
if (column.reference) {
columns[key].references(() => adapter.tables[column.reference.table][column.reference.name], {
onDelete: column.reference.onDelete,
})
}
if (column.primaryKey) {
let args: Record<string, unknown> | undefined = undefined
if (column.type === 'integer' && column.autoIncrement) {
args = { autoIncrement: true }
}
columns[key].primaryKey(args)
}
if (column.notNull) {
columns[key].notNull()
}
if (typeof column.default !== 'undefined') {
let sanitizedDefault = column.default
if (column.type === 'geometry' && Array.isArray(column.default)) {
sanitizedDefault = JSON.stringify({
type: 'Point',
coordinates: [column.default[0], column.default[1]],
})
}
columns[key].default(sanitizedDefault)
}
}
const extraConfig = (cols: any) => {
const config: Record<string, ForeignKeyBuilder | IndexBuilder> = {}
if (rawTable.indexes) {
for (const [key, rawIndex] of Object.entries(rawTable.indexes)) {
let fn: any = index
if (rawIndex.unique) {
fn = uniqueIndex
}
if (Array.isArray(rawIndex.on)) {
if (rawIndex.on.length) {
config[key] = fn(rawIndex.name).on(...rawIndex.on.map((colName) => cols[colName]))
}
} else {
config[key] = fn(rawIndex.name).on(cols[rawIndex.on])
}
}
}
if (rawTable.foreignKeys) {
for (const [key, rawForeignKey] of Object.entries(rawTable.foreignKeys)) {
let builder = foreignKey({
name: rawForeignKey.name,
columns: rawForeignKey.columns.map((colName) => cols[colName]) as any,
foreignColumns: rawForeignKey.foreignColumns.map(
(column) => adapter.tables[column.table][column.name],
),
})
if (rawForeignKey.onDelete) {
builder = builder.onDelete(rawForeignKey.onDelete)
}
if (rawForeignKey.onUpdate) {
builder = builder.onDelete(rawForeignKey.onUpdate)
}
config[key] = builder
}
}
return config
}
adapter.tables[rawTable.name] = sqliteTable(rawTable.name, columns as any, extraConfig as any)
}

View File

@@ -0,0 +1,46 @@
import type { SetColumnID } from '@payloadcms/drizzle/types'
import type { MySQLAdapter } from '../types.js'
export const setColumnID: SetColumnID = ({ adapter, columns, fields }) => {
const idField = fields.find((field) => field.name === 'id')
if (idField) {
if (idField.type === 'number') {
columns.id = {
name: 'id',
type: 'numeric',
primaryKey: true,
}
return 'numeric'
}
if (idField.type === 'text') {
columns.id = {
name: 'id',
type: 'text',
primaryKey: true,
}
return 'text'
}
}
if (adapter.idType === 'uuid') {
columns.id = {
name: 'id',
type: 'uuid',
defaultRandom: true,
primaryKey: true,
}
return 'uuid'
}
columns.id = {
name: 'id',
type: 'integer',
autoIncrement: (adapter as unknown as MySQLAdapter).autoIncrement,
primaryKey: true,
}
return 'integer'
}

View File

@@ -0,0 +1,265 @@
import type { Client, Config, ResultSet } from '@libsql/client'
import type { extendDrizzleTable, Operators } from '@payloadcms/drizzle'
import type { BuildQueryJoinAliases, DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
import type {
AnyMySqlColumn,
// MySqlInsertOnDuplicateKeyUpdateConfig,
MySqlTableWithColumns,
MySqlTransactionConfig,
} from 'drizzle-orm/mysql-core'
import type { MySql2Database, MySqlRawQueryResult } from 'drizzle-orm/mysql2'
import type { Payload, PayloadRequest } from 'payload'
type SQLiteSchema = {
relations: Record<string, GenericRelation>
tables: Record<string, MySqlTableWithColumns<any>>
}
type SQLiteSchemaHookArgs = {
extendTable: typeof extendDrizzleTable
schema: SQLiteSchema
}
export type SQLiteSchemaHook = (args: SQLiteSchemaHookArgs) => Promise<SQLiteSchema> | SQLiteSchema
export type Args = {
/**
* Transform the schema after it's built.
* You can use it to customize the schema with features that aren't supported by Payload.
* Examples may include: composite indices, generated columns, vectors
*/
afterSchemaInit?: SQLiteSchemaHook[]
/**
* Enable [AUTOINCREMENT](https://www.sqlite.org/autoinc.html) for Primary Keys.
* This ensures that the same ID cannot be reused from previously deleted rows.
*/
autoIncrement?: boolean
/**
* Transform the schema before it's built.
* You can use it to preserve an existing database schema and if there are any collissions Payload will override them.
* To generate Drizzle schema from the database, see [Drizzle Kit introspection](https://orm.drizzle.team/kit-docs/commands#introspect--pull)
*/
beforeSchemaInit?: SQLiteSchemaHook[]
client: Config
/** Generated schema from payload generate:db-schema file path */
generateSchemaOutputFile?: string
idType?: 'number' | 'uuid'
localesSuffix?: string
logger?: DrizzleConfig['logger']
migrationDir?: string
prodMigrations?: {
down: (args: MigrateDownArgs) => Promise<void>
name: string
up: (args: MigrateUpArgs) => Promise<void>
}[]
push?: boolean
relationshipsSuffix?: string
schemaName?: string
transactionOptions?: false | MySqlTransactionConfig
versionsSuffix?: string
}
export type GenericColumns = {
[x: string]: AnyMySqlColumn
}
export type GenericTable = MySqlTableWithColumns<{
columns: GenericColumns
dialect: string
name: string
schema: string
}>
export type GenericRelation = Relations<string, Record<string, Relation<string>>>
export type CountDistinct = (args: {
db: MySql2Database
joins: BuildQueryJoinAliases
tableName: string
where: SQL
}) => Promise<number>
export type DeleteWhere = (args: {
db: MySql2Database
tableName: string
where: SQL
}) => Promise<void>
export type DropDatabase = (args: { adapter: MySQLAdapter }) => Promise<void>
export type Execute<T> = (args: {
db?: MySql2Database
drizzle?: MySql2Database
raw?: string
sql?: SQL<unknown>
}) => Promise<MySqlRawQueryResult>
export type Insert = (args: {
db: MySql2Database
// TODO:
onConflictDoUpdate?: any
tableName: string
values: Record<string, unknown> | Record<string, unknown>[]
}) => Promise<Record<string, unknown>[]>
// Explicitly omit drizzle property for complete override in SQLiteAdapter, required in ts 5.5
type SQLiteDrizzleAdapter = Omit<
DrizzleAdapter,
| 'countDistinct'
| 'deleteWhere'
| 'drizzle'
| 'dropDatabase'
| 'execute'
| 'idType'
| 'insert'
| 'operators'
| 'relations'
>
export interface GeneratedDatabaseSchema {
schemaUntyped: Record<string, unknown>
}
type ResolveSchemaType<T> = 'schema' extends keyof T
? T['schema']
: GeneratedDatabaseSchema['schemaUntyped']
type Drizzle = { $client: Client } & MySql2Database<ResolveSchemaType<GeneratedDatabaseSchema>>
export type MySQLAdapter = {
afterSchemaInit: SQLiteSchemaHook[]
autoIncrement: boolean
beforeSchemaInit: SQLiteSchemaHook[]
client: Client
clientConfig: Args['client']
countDistinct: CountDistinct
defaultDrizzleSnapshot: any
deleteWhere: DeleteWhere
drizzle: Drizzle
dropDatabase: DropDatabase
execute: Execute<unknown>
/**
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
* Used for returning properly formed errors from unique fields
*/
fieldConstraints: Record<string, Record<string, string>>
idType: Args['idType']
initializing: Promise<void>
insert: Insert
localesSuffix?: string
logger: DrizzleConfig['logger']
operators: Operators
prodMigrations?: {
down: (args: MigrateDownArgs) => Promise<void>
name: string
up: (args: MigrateUpArgs) => Promise<void>
}[]
push: boolean
rejectInitializing: () => void
relations: Record<string, GenericRelation>
relationshipsSuffix?: string
resolveInitializing: () => void
schema: Record<string, GenericRelation | GenericTable>
schemaName?: Args['schemaName']
tableNameMap: Map<string, string>
tables: Record<string, GenericTable>
transactionOptions: MySqlTransactionConfig
versionsSuffix?: string
} & SQLiteDrizzleAdapter
export type IDType = 'integer' | 'numeric' | 'text'
export type MigrateUpArgs = {
/**
* The SQLite Drizzle instance that you can use to execute SQL directly within the current transaction.
* @example
* ```ts
* import { type MigrateUpArgs, sql } from '@payloadcms/db-sqlite'
*
* export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const { rows: posts } = await db.run(sql`SELECT * FROM posts`)
* }
* ```
*/
db: Drizzle
/**
* The Payload instance that you can use to execute Local API methods
* To use the current transaction you must pass `req` to arguments
* @example
* ```ts
* import { type MigrateUpArgs } from '@payloadcms/db-sqlite'
*
* export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
* const posts = await payload.find({ collection: 'posts', req })
* }
* ```
*/
payload: Payload
/**
* The `PayloadRequest` object that contains the current transaction
*/
req: PayloadRequest
}
export type MigrateDownArgs = {
/**
* The SQLite Drizzle instance that you can use to execute SQL directly within the current transaction.
* @example
* ```ts
* import { type MigrateDownArgs, sql } from '@payloadcms/db-sqlite'
*
* export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
* const { rows: posts } = await db.run(sql`SELECT * FROM posts`)
* }
* ```
*/
db: Drizzle
/**
* The Payload instance that you can use to execute Local API methods
* To use the current transaction you must pass `req` to arguments
* @example
* ```ts
* import { type MigrateDownArgs } from '@payloadcms/db-sqlite'
*
* export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
* const posts = await payload.find({ collection: 'posts', req })
* }
* ```
*/
payload: Payload
/**
* The `PayloadRequest` object that contains the current transaction
*/
req: PayloadRequest
}
declare module 'payload' {
export interface DatabaseAdapter
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
DrizzleAdapter {
beginTransaction: (options?: MySqlTransactionConfig) => Promise<null | number | string>
drizzle: Drizzle
/**
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
* Used for returning properly formed errors from unique fields
*/
fieldConstraints: Record<string, Record<string, string>>
idType: Args['idType']
initializing: Promise<void>
localesSuffix?: string
logger: DrizzleConfig['logger']
prodMigrations?: {
down: (args: MigrateDownArgs) => Promise<void>
name: string
up: (args: MigrateUpArgs) => Promise<void>
}[]
push: boolean
rejectInitializing: () => void
relationshipsSuffix?: string
resolveInitializing: () => void
schema: Record<string, GenericRelation | GenericTable>
tableNameMap: Map<string, string>
transactionOptions: MySqlTransactionConfig
versionsSuffix?: string
}
}

View File

@@ -0,0 +1,19 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
/* TODO: remove the following lines */
"strict": false,
"noUncheckedIndexedAccess": false,
},
"references": [
{
"path": "../payload"
},
{
"path": "../translations"
},
{
"path": "../drizzle"
}
]
}

129
pnpm-lock.yaml generated
View File

@@ -111,7 +111,7 @@ importers:
version: 0.28.0
drizzle-orm:
specifier: 0.36.1
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(pg@8.11.3)(react@19.0.0)
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(mysql2@3.12.0)(pg@8.11.3)(react@19.0.0)
escape-html:
specifier: ^1.0.3
version: 1.0.3
@@ -301,6 +301,43 @@ importers:
specifier: workspace:*
version: link:../payload
packages/db-mysql:
dependencies:
'@payloadcms/drizzle':
specifier: workspace:*
version: link:../drizzle
console-table-printer:
specifier: 2.12.1
version: 2.12.1
drizzle-kit:
specifier: 0.28.0
version: 0.28.0
drizzle-orm:
specifier: 0.36.1
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(mysql2@3.12.0)(pg@8.11.3)(react@19.0.0)
mysql2:
specifier: 3.12.0
version: 3.12.0
prompts:
specifier: 2.4.2
version: 2.4.2
to-snake-case:
specifier: 1.0.0
version: 1.0.0
uuid:
specifier: 9.0.0
version: 9.0.0
devDependencies:
'@payloadcms/eslint-config':
specifier: workspace:*
version: link:../eslint-config
'@types/to-snake-case':
specifier: 1.0.0
version: 1.0.0
payload:
specifier: workspace:*
version: link:../payload
packages/db-postgres:
dependencies:
'@payloadcms/drizzle':
@@ -317,7 +354,7 @@ importers:
version: 0.28.0
drizzle-orm:
specifier: 0.36.1
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(pg@8.11.3)(react@19.0.0)
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(mysql2@3.12.0)(pg@8.11.3)(react@19.0.0)
pg:
specifier: 8.11.3
version: 8.11.3
@@ -363,7 +400,7 @@ importers:
version: 0.28.0
drizzle-orm:
specifier: 0.36.1
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(pg@8.11.3)(react@19.0.0)
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(mysql2@3.12.0)(pg@8.11.3)(react@19.0.0)
prompts:
specifier: 2.4.2
version: 2.4.2
@@ -403,7 +440,7 @@ importers:
version: 0.28.0
drizzle-orm:
specifier: 0.36.1
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(pg@8.11.3)(react@19.0.0)
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(mysql2@3.12.0)(pg@8.11.3)(react@19.0.0)
pg:
specifier: 8.11.3
version: 8.11.3
@@ -443,7 +480,7 @@ importers:
version: 2.12.1
drizzle-orm:
specifier: 0.36.1
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(pg@8.11.3)(react@19.0.0)
version: 0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(mysql2@3.12.0)(pg@8.11.3)(react@19.0.0)
prompts:
specifier: 2.4.2
version: 2.4.2
@@ -5600,6 +5637,10 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
aws-ssl-profiles@1.1.2:
resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==}
engines: {node: '>= 6.0.0'}
axe-core@4.10.2:
resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==}
engines: {node: '>=4'}
@@ -6198,6 +6239,10 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
denque@2.1.0:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
engines: {node: '>=0.10'}
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@@ -6996,6 +7041,9 @@ packages:
resolution: {integrity: sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==}
engines: {node: '>=14'}
generate-function@2.3.1:
resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==}
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
@@ -7467,6 +7515,9 @@ packages:
is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
is-property@1.0.2:
resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==}
is-reference@1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
@@ -7928,6 +7979,9 @@ packages:
resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
engines: {node: '>=18'}
long@5.2.4:
resolution: {integrity: sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==}
longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
@@ -7949,6 +8003,14 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
lru-cache@7.18.3:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
lru.min@1.1.1:
resolution: {integrity: sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==}
engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'}
magic-string@0.30.12:
resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
@@ -8265,6 +8327,14 @@ packages:
multipasta@0.2.5:
resolution: {integrity: sha512-c8eMDb1WwZcE02WVjHoOmUVk7fnKU/RmUcosHACglrWAuPQsEJv+E8430sXj6jNc1jHw0zrS16aCjQh4BcEb4A==}
mysql2@3.12.0:
resolution: {integrity: sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==}
engines: {node: '>= 8.0'}
named-placeholders@1.1.3:
resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==}
engines: {node: '>=12.0.0'}
nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -9274,6 +9344,9 @@ packages:
engines: {node: '>=10'}
hasBin: true
seq-queue@0.0.5:
resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==}
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
@@ -9459,6 +9532,10 @@ packages:
sqids@0.3.0:
resolution: {integrity: sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw==}
sqlstring@2.3.3:
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
engines: {node: '>= 0.6'}
stable-hash@0.0.4:
resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==}
@@ -15126,6 +15203,8 @@ snapshots:
dependencies:
possible-typed-array-names: 1.0.0
aws-ssl-profiles@1.1.2: {}
axe-core@4.10.2: {}
axios@1.4.0:
@@ -15764,6 +15843,8 @@ snapshots:
delayed-stream@1.0.0: {}
denque@2.1.0: {}
dequal@2.0.3: {}
destr@2.0.3: {}
@@ -15836,7 +15917,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
drizzle-orm@0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(pg@8.11.3)(react@19.0.0):
drizzle-orm@0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(mysql2@3.12.0)(pg@8.11.3)(react@19.0.0):
optionalDependencies:
'@libsql/client': 0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
'@neondatabase/serverless': 0.9.5
@@ -15844,10 +15925,11 @@ snapshots:
'@types/pg': 8.10.2
'@types/react': 19.0.1
'@vercel/postgres': 0.9.0
mysql2: 3.12.0
pg: 8.11.3
react: 19.0.0
drizzle-orm@0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(pg@8.11.3)(react@19.0.0):
drizzle-orm@0.36.1(@libsql/client@0.14.0(bufferutil@4.0.8))(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@types/react@19.0.1)(@vercel/postgres@0.9.0)(mysql2@3.12.0)(pg@8.11.3)(react@19.0.0):
optionalDependencies:
'@libsql/client': 0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
'@neondatabase/serverless': 0.9.5
@@ -15855,6 +15937,7 @@ snapshots:
'@types/pg': 8.11.6
'@types/react': 19.0.1
'@vercel/postgres': 0.9.0
mysql2: 3.12.0
pg: 8.11.3
react: 19.0.0
@@ -16741,6 +16824,10 @@ snapshots:
- encoding
- supports-color
generate-function@2.3.1:
dependencies:
is-property: 1.0.2
gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {}
@@ -17219,6 +17306,8 @@ snapshots:
is-potential-custom-element-name@1.0.1: {}
is-property@1.0.2: {}
is-reference@1.2.1:
dependencies:
'@types/estree': 1.0.6
@@ -17909,6 +17998,8 @@ snapshots:
strip-ansi: 7.1.0
wrap-ansi: 9.0.0
long@5.2.4: {}
longest-streak@3.1.0: {}
loose-envify@1.4.0:
@@ -17925,6 +18016,10 @@ snapshots:
dependencies:
yallist: 3.1.1
lru-cache@7.18.3: {}
lru.min@1.1.1: {}
magic-string@0.30.12:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@@ -18372,6 +18467,22 @@ snapshots:
multipasta@0.2.5: {}
mysql2@3.12.0:
dependencies:
aws-ssl-profiles: 1.1.2
denque: 2.1.0
generate-function: 2.3.1
iconv-lite: 0.6.3
long: 5.2.4
lru.min: 1.1.1
named-placeholders: 1.1.3
seq-queue: 0.0.5
sqlstring: 2.3.3
named-placeholders@1.1.3:
dependencies:
lru-cache: 7.18.3
nanoid@3.3.7: {}
napi-build-utils@1.0.2: {}
@@ -19380,6 +19491,8 @@ snapshots:
semver@7.6.3: {}
seq-queue@0.0.5: {}
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
@@ -19608,6 +19721,8 @@ snapshots:
sqids@0.3.0: {}
sqlstring@2.3.3: {}
stable-hash@0.0.4: {}
stack-utils@2.0.6: