feat: adds new Cloudflare D1 SQLite adapter, R2 storage adapter and Cloudflare template (#12537)

This feat adds support for
- [D1 Cloudflare SQLite](https://developers.cloudflare.com/d1/)
- R2 storage directly (previously it was via S3 SDK)
- Cloudflare 1-click deploy template

---------

Co-authored-by: Paul Popus <paul@payloadcms.com>
This commit is contained in:
Sasha
2025-09-29 23:58:18 +03:00
committed by GitHub
parent 9248fc41e8
commit 99043eeaeb
106 changed files with 12377 additions and 130 deletions

View File

@@ -278,3 +278,35 @@ sqliteAdapter({
],
})
```
## D1 Database
<Banner type="warning">
This adapter is currently in beta as it is new and could be subject to changes
which may be considered breaking
</Banner>
We also provide a separate adapter to connect to [Cloudflare D1](https://developers.cloudflare.com/d1/), which is a serverless SQLite database.
To use it, install the package `@payloadcms/db-d1-sqlite` and configure it as follows:
```ts
import { sqliteD1Adapter } from '@payloadcms/db-d1-sqlite'
export default buildConfig({
// Your config goes here
collections: [
// Collections go here
],
// Configure the D1 adapter here
db: sqliteD1Adapter({
// D1-specific arguments go here.
// `binding` is required and should match the D1 database binding name in your Cloudflare Worker environment.
binding: cloudflare.env.D1,
}),
})
```
It inherits the options from the SQLite adapter above with the exception of the connection options in favour of the `binding`.
You can see our [Cloudflare D1 template](https://github.com/payloadcms/payload/tree/main/templates/with-cloudflare-d1) for a full example of how to set this up.

View File

@@ -15,6 +15,7 @@ Payload offers additional storage adapters to handle file uploads. These adapter
| Azure | [`@payloadcms/storage-azure`](https://github.com/payloadcms/payload/tree/main/packages/storage-azure) |
| Google Cloud Storage | [`@payloadcms/storage-gcs`](https://github.com/payloadcms/payload/tree/main/packages/storage-gcs) |
| Uploadthing | [`@payloadcms/storage-uploadthing`](https://github.com/payloadcms/payload/tree/main/packages/storage-uploadthing) |
| R2 | [`@payloadcms/storage-r2`](https://github.com/payloadcms/payload/tree/main/packages/storage-r2) |
## Vercel Blob Storage
@@ -277,6 +278,43 @@ export default buildConfig({
| `defaultKeyType` | Default key type for file operations | `fileKey` |
| `clientUploads` | Do uploads directly on the client to bypass limits on Vercel. | |
## R2 Storage
<Banner type="warning">
**Note**: The R2 Storage Adapter is in **beta** as some aspects of it may
change on any minor releases.
</Banner>
[`@payloadcms/storage-r2`](https://www.npmjs.com/package/@payloadcms/storage-r2)
Use this adapter to store uploads in a Cloudflare R2 bucket via the Cloudflare Workers environment. If you're trying to connect to R2 using the S3 API then you should use the [S3](#s3-storage) adapter instead.
### Installation#r2-installation
```sh
pnpm add @payloadcms/storage-r2
```
### Usage#r2-usage
- Configure the `collections` object to specify which collections should use r2. The slug _must_ match one of your existing collection slugs and be an `upload` type.
- Pass in the R2 bucket binding to the `bucket` option, this should be done in the environment where Payload is running (e.g. Cloudflare Worker).
- You can conditionally datamine whether or not to enable the plugin with the `enabled` option.
```ts
export default buildConfig({
collections: [Media],
plugins: [
r2Storage({
collections: {
media: true,
},
bucket: cloudflare.env.R2,
}),
],
})
```
## Custom Storage Adapters
If you need to create a custom storage adapter, you can use the [`@payloadcms/plugin-cloud-storage`](https://www.npmjs.com/package/@payloadcms/plugin-cloud-storage) package. This package is used internally by the storage adapters mentioned above.

View File

@@ -19,6 +19,7 @@
"build:core": "turbo build --filter \"!@payloadcms/plugin-*\" --filter \"!@payloadcms/storage-*\" --filter \"!blank\" --filter \"!website\"",
"build:core:force": "pnpm clean:build && pnpm build:core --no-cache --force",
"build:create-payload-app": "turbo build --filter create-payload-app",
"build:db-d1-sqlite": "turbo build --filter \"@payloadcms/db-d1-sqlite\"",
"build:db-mongodb": "turbo build --filter \"@payloadcms/db-mongodb\"",
"build:db-postgres": "turbo build --filter \"@payloadcms/db-postgres\"",
"build:db-sqlite": "turbo build --filter \"@payloadcms/db-sqlite\"",

1
packages/db-d1-sqlite/.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

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,33 @@
# Payload D1 SQLite Adapter
Official D1 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-d1-sqlite
```
## Usage
```ts
import { sqliteD1Adapter } from '@payloadcms/db-d1-sqlite'
export default buildConfig({
// Your config goes here
collections: [
// Collections go here
],
// Configure the D1 adapter here
db: sqliteD1Adapter({
// D1-specific arguments go here.
// `binding` is required and should match the D1 database binding name in your Cloudflare Worker environment.
binding: cloudflare.env.D1,
}),
})
```
More detailed usage can be found in the [Payload Docs](https://payloadcms.com/docs/database/sqlite).

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,134 @@
{
"name": "@payloadcms/db-d1-sqlite",
"version": "3.57.0",
"description": "The officially supported D1 SQLite database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
"url": "https://github.com/payloadcms/payload.git",
"directory": "packages/db-d1-sqlite"
},
"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/exports/types-deprecated.ts",
"require": "./src/exports/types-deprecated.ts",
"types": "./src/exports/types-deprecated.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/sqlite-core": {
"import": "./src/drizzle-proxy/sqlite-core.ts",
"types": "./src/drizzle-proxy/sqlite-core.ts",
"default": "./src/drizzle-proxy/sqlite-core.ts"
},
"./drizzle/d1": {
"import": "./src/drizzle-proxy/d1.ts",
"types": "./src/drizzle-proxy/d1.ts",
"default": "./src/drizzle-proxy/d1.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/index.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.31.4",
"drizzle-orm": "0.44.2",
"prompts": "2.4.2",
"to-snake-case": "1.0.0",
"uuid": "9.0.0"
},
"devDependencies": {
"@payloadcms/eslint-config": "workspace:*",
"@types/pg": "8.10.2",
"@types/to-snake-case": "1.0.0",
"@types/uuid": "10.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/exports/types-deprecated.js",
"require": "./dist/exports/types-deprecated.js",
"types": "./dist/exports/types-deprecated.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/d1": {
"import": "./dist/drizzle-proxy/d1.js",
"types": "./dist/drizzle-proxy/d1.d.ts",
"default": "./dist/drizzle-proxy/d1.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,61 @@
import type { DrizzleAdapter } from '@payloadcms/drizzle'
import type { Connect, Migration } from 'payload'
import { pushDevSchema } from '@payloadcms/drizzle'
import { drizzle } from 'drizzle-orm/d1'
import type { SQLiteD1Adapter } from './types.js'
export const connect: Connect = async function connect(
this: SQLiteD1Adapter,
options = {
hotReload: false,
},
) {
const { hotReload } = options
this.schema = {
...this.tables,
...this.relations,
}
try {
const logger = this.logger || false
this.drizzle = drizzle(this.binding, { logger, schema: this.schema })
this.client = this.drizzle.$client as any
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) {
const message = err instanceof Error ? err.message : String(err)
this.payload.logger.error({ err, msg: `Error: cannot connect to SQLite: ${message}` })
if (typeof this.rejectInitializing === 'function') {
this.rejectInitializing()
}
console.error(err)
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 as Migration[] })
}
}

View File

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

View File

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

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,67 @@
import type { Execute } from '@payloadcms/drizzle'
import type { SQLiteRaw } from 'drizzle-orm/sqlite-core/query-builders/raw'
import { sql } from 'drizzle-orm'
interface D1Meta {
changed_db: boolean
changes: number
duration: number
last_row_id: number
rows_read: number
rows_written: number
/**
* True if-and-only-if the database instance that executed the query was the primary.
*/
served_by_primary?: boolean
/**
* The region of the database instance that executed the query.
*/
served_by_region?: string
size_after: number
timings?: {
/**
* The duration of the SQL query execution by the database instance. It doesn't include any network time.
*/
sql_duration_ms: number
}
}
interface D1Response {
error?: never
meta: D1Meta & Record<string, unknown>
success: true
}
type D1Result<T = unknown> = {
results: T[]
} & D1Response
export const execute: Execute<any> = function execute({ db, drizzle, raw, sql: statement }) {
const executeFrom: any = (db ?? drizzle)!
const mapToLibSql = (query: SQLiteRaw<D1Result<unknown>>): any => {
const execute = query.execute
query.execute = async () => {
const result: D1Result = await execute()
const resultLibSQL = {
columns: undefined,
columnTypes: undefined,
lastInsertRowid: BigInt(result.meta.last_row_id),
rows: result.results as any[],
rowsAffected: result.meta.rows_written,
}
return Object.assign(result, resultLibSQL)
}
return query
}
if (raw) {
const result = mapToLibSql(executeFrom.run(sql.raw(raw)))
return result
} else {
const result = mapToLibSql(executeFrom.run(statement))
return result
}
}

View File

@@ -0,0 +1,79 @@
import type {
Args as _Args,
CountDistinct as _CountDistinct,
DeleteWhere as _DeleteWhere,
DropDatabase as _DropDatabase,
Execute as _Execute,
GeneratedDatabaseSchema as _GeneratedDatabaseSchema,
GenericColumns as _GenericColumns,
GenericRelation as _GenericRelation,
GenericTable as _GenericTable,
IDType as _IDType,
Insert as _Insert,
MigrateDownArgs as _MigrateDownArgs,
MigrateUpArgs as _MigrateUpArgs,
SQLiteD1Adapter as _SQLiteAdapter,
SQLiteSchemaHook as _SQLiteSchemaHook,
} from '../types.js'
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type SQLiteAdapter = _SQLiteAdapter
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type Args = _Args
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type CountDistinct = _CountDistinct
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type DeleteWhere = _DeleteWhere
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type DropDatabase = _DropDatabase
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type Execute<T> = _Execute<T>
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type GeneratedDatabaseSchema = _GeneratedDatabaseSchema
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type GenericColumns = _GenericColumns
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type GenericRelation = _GenericRelation
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type GenericTable = _GenericTable
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type IDType = _IDType
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type Insert = _Insert
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type MigrateDownArgs = _MigrateDownArgs
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type MigrateUpArgs = _MigrateUpArgs
/**
* @deprecated - import from `@payloadcms/db-sqlite` instead
*/
export type SQLiteSchemaHook = _SQLiteSchemaHook

View File

@@ -0,0 +1,223 @@
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,
updateJobs,
updateMany,
updateOne,
updateVersion,
} from '@payloadcms/drizzle'
import {
columnToCodeConverter,
convertPathToJSONTraversal,
countDistinct,
createJSONQuery,
defaultDrizzleSnapshot,
deleteWhere,
dropDatabase,
init,
insert,
requireDrizzleKit,
} from '@payloadcms/drizzle/sqlite'
import { like, notLike } from 'drizzle-orm'
import { createDatabaseAdapter, defaultBeginTransaction } from 'payload'
import { fileURLToPath } from 'url'
import type { Args, SQLiteD1Adapter } from './types.js'
import { connect } from './connect.js'
import { execute } from './execute.js'
const filename = fileURLToPath(import.meta.url)
export function sqliteD1Adapter(args: Args): DatabaseAdapterObj<SQLiteD1Adapter> {
const sqliteIDType = args.idType || 'number'
const payloadIDType = sqliteIDType === 'uuid' ? 'text' : 'number'
const allowIDOnCreate = args.allowIDOnCreate ?? false
function adapter({ payload }: { payload: Payload }) {
const migrationDir = findMigrationDir(args.migrationDir)
let resolveInitializing: () => void = () => {}
let rejectInitializing: () => void = () => {}
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,
not_like: notLike,
} as unknown as Operators
return createDatabaseAdapter<SQLiteD1Adapter>({
name: 'sqlite',
afterSchemaInit: args.afterSchemaInit ?? [],
allowIDOnCreate,
autoIncrement: args.autoIncrement ?? false,
beforeSchemaInit: args.beforeSchemaInit ?? [],
binding: args.binding,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
client: undefined,
defaultDrizzleSnapshot,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
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,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
push: args.push,
rawRelations: {},
rawTables: {},
relations: {},
relationshipsSuffix: args.relationshipsSuffix || '_rels',
schema: {},
schemaName: args.schemaName,
sessions: {},
tableNameMap: new Map<string, string>(),
tables: {},
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
execute,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
transactionOptions: args.transactionOptions || undefined,
updateJobs,
updateMany,
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,
find,
findGlobal,
findGlobalVersions,
findOne,
findVersions,
indexes: new Set<string>(),
init,
insert,
migrate,
migrateDown,
migrateFresh,
migrateRefresh,
migrateReset,
migrateStatus,
migrationDir,
packageName: '@payloadcms/db-d1-sqlite',
payload,
queryDrafts,
rejectInitializing,
requireDrizzleKit,
resolveInitializing,
rollbackTransaction,
updateGlobal,
updateGlobalVersion,
updateOne,
updateVersion,
upsert: updateOne,
})
}
return {
name: 'd1-sqlite',
allowIDOnCreate,
defaultIDType: payloadIDType,
init: adapter,
}
}
/**
* @todo deprecate /types subpath export in 4.0
*/
export type {
Args as SQLiteAdapterArgs,
CountDistinct,
DeleteWhere,
DropDatabase,
Execute,
GeneratedDatabaseSchema,
GenericColumns,
GenericRelation,
GenericTable,
IDType,
Insert,
MigrateDownArgs,
MigrateUpArgs,
SQLiteD1Adapter as SQLiteAdapter,
SQLiteSchemaHook,
} from './types.js'
export { sql } from 'drizzle-orm'

View File

@@ -0,0 +1,199 @@
import type { ResultSet } from '@libsql/client'
import type { BuildQueryJoinAliases, DrizzleAdapter, extendDrizzleTable } from '@payloadcms/drizzle'
import type { BaseSQLiteAdapter, BaseSQLiteArgs } from '@payloadcms/drizzle/sqlite'
import type { DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
import type { AnyD1Database, DrizzleD1Database } from 'drizzle-orm/d1'
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
import type {
AnySQLiteColumn,
SQLiteInsertOnConflictDoUpdateConfig,
SQLiteTableWithColumns,
SQLiteTransactionConfig,
} from 'drizzle-orm/sqlite-core'
import type { SQLiteRaw } from 'drizzle-orm/sqlite-core/query-builders/raw'
import type { Payload, PayloadRequest } from 'payload'
type SQLiteSchema = {
relations: Record<string, GenericRelation>
tables: Record<string, SQLiteTableWithColumns<any>>
}
type SQLiteSchemaHookArgs = {
extendTable: typeof extendDrizzleTable
schema: SQLiteSchema
}
export type SQLiteSchemaHook = (args: SQLiteSchemaHookArgs) => Promise<SQLiteSchema> | SQLiteSchema
export type Args = {
binding: AnyD1Database
} & BaseSQLiteArgs
export type GenericColumns = {
[x: string]: AnySQLiteColumn
}
export type GenericTable = SQLiteTableWithColumns<{
columns: GenericColumns
dialect: string
name: string
schema: string
}>
export type GenericRelation = Relations<string, Record<string, Relation<string>>>
export type CountDistinct = (args: {
db: LibSQLDatabase
joins: BuildQueryJoinAliases
tableName: string
where: SQL
}) => Promise<number>
export type DeleteWhere = (args: {
db: LibSQLDatabase
tableName: string
where: SQL
}) => Promise<void>
export type DropDatabase = (args: { adapter: SQLiteD1Adapter }) => Promise<void>
export type Execute<T> = (args: {
db?: LibSQLDatabase
drizzle?: LibSQLDatabase
raw?: string
sql?: SQL<unknown>
}) => SQLiteRaw<Promise<T>> | SQLiteRaw<ResultSet>
export type Insert = (args: {
db: LibSQLDatabase
onConflictDoUpdate?: SQLiteInsertOnConflictDoUpdateConfig<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: AnyD1Database } & DrizzleD1Database<Record<string, any>>
export type SQLiteD1Adapter = {
binding: Args['binding']
client: AnyD1Database
drizzle: Drizzle
} & BaseSQLiteAdapter &
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?: SQLiteTransactionConfig) => 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: SQLiteTransactionConfig
versionsSuffix?: string
}
}

View File

@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"references": [
{
"path": "../payload"
},
{
"path": "../translations"
},
{
"path": "../drizzle"
}
]
}

View File

@@ -180,8 +180,6 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
find,
findGlobal,
findGlobalVersions,
updateJobs,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
findOne,
findVersions,
indexes: new Set<string>(),
@@ -199,6 +197,7 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
queryDrafts,
rawRelations: {},
rawTables: {},
updateJobs,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
rejectInitializing,
requireDrizzleKit,

View File

@@ -41,24 +41,26 @@ import {
updateVersion,
upsert,
} from '@payloadcms/drizzle'
import {
columnToCodeConverter,
convertPathToJSONTraversal,
countDistinct,
createJSONQuery,
defaultDrizzleSnapshot,
deleteWhere,
dropDatabase,
execute,
init,
insert,
requireDrizzleKit,
} from '@payloadcms/drizzle/sqlite'
import { like, notLike } from 'drizzle-orm'
import { createDatabaseAdapter, defaultBeginTransaction } from 'payload'
import { fileURLToPath } from 'url'
import type { Args, SQLiteAdapter } 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'
const filename = fileURLToPath(import.meta.url)
@@ -69,8 +71,8 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
function adapter({ payload }: { payload: Payload }) {
const migrationDir = findMigrationDir(args.migrationDir)
let resolveInitializing
let rejectInitializing
let resolveInitializing: () => void = () => {}
let rejectInitializing: () => void = () => {}
const initializing = new Promise<void>((res, rej) => {
resolveInitializing = res
@@ -131,7 +133,6 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
updateJobs,
updateMany,
versionsSuffix: args.versionsSuffix || '_v',
// DatabaseAdapter
beginTransaction: args.transactionOptions ? beginTransaction : defaultBeginTransaction(),
commitTransaction,
@@ -166,7 +167,6 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
find,
findGlobal,
findGlobalVersions,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
findOne,
findVersions,
indexes: new Set<string>(),
@@ -182,10 +182,8 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
packageName: '@payloadcms/db-sqlite',
payload,
queryDrafts,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
rejectInitializing,
requireDrizzleKit,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
resolveInitializing,
rollbackTransaction,
updateGlobal,

View File

@@ -1,5 +1,6 @@
import type { Client, Config, ResultSet } from '@libsql/client'
import type { extendDrizzleTable, Operators } from '@payloadcms/drizzle'
import type { BaseSQLiteAdapter, BaseSQLiteArgs } from '@payloadcms/drizzle/sqlite'
import type { BuildQueryJoinAliases, DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
@@ -56,23 +57,7 @@ export type Args = {
*/
blocksAsJSON?: boolean
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 | SQLiteTransactionConfig
versionsSuffix?: string
}
} & BaseSQLiteArgs
export type GenericColumns = {
[x: string]: AnySQLiteColumn
@@ -142,45 +127,11 @@ type ResolveSchemaType<T> = 'schema' extends keyof T
type Drizzle = { $client: Client } & LibSQLDatabase<ResolveSchemaType<GeneratedDatabaseSchema>>
export type SQLiteAdapter = {
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: SQLiteTransactionConfig
versionsSuffix?: string
} & SQLiteDrizzleAdapter
} & BaseSQLiteAdapter &
SQLiteDrizzleAdapter
export type IDType = 'integer' | 'numeric' | 'text'

View File

@@ -178,8 +178,6 @@ export function vercelPostgresAdapter(args: Args = {}): DatabaseAdapterObj<Verce
findDistinct,
findGlobal,
findGlobalVersions,
readReplicaOptions: args.readReplicas,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
findOne,
findVersions,
init,
@@ -194,6 +192,7 @@ export function vercelPostgresAdapter(args: Args = {}): DatabaseAdapterObj<Verce
packageName: '@payloadcms/db-vercel-postgres',
payload,
queryDrafts,
readReplicaOptions: args.readReplicas,
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
rejectInitializing,
requireDrizzleKit,

View File

@@ -30,6 +30,11 @@
"types": "./src/exports/postgres.ts",
"default": "./src/exports/postgres.ts"
},
"./sqlite": {
"import": "./src/exports/sqlite.ts",
"types": "./src/exports/sqlite.ts",
"default": "./src/exports/sqlite.ts"
},
"./types": {
"import": "./src/exports/types-deprecated.ts",
"types": "./src/exports/types-deprecated.ts",
@@ -82,6 +87,11 @@
"types": "./dist/exports/postgres.d.ts",
"default": "./dist/exports/postgres.js"
},
"./sqlite": {
"import": "./dist/exports/sqlite.js",
"types": "./dist/exports/sqlite.d.ts",
"default": "./dist/exports/sqlite.js"
},
"./types": {
"import": "./dist/exports/types-deprecated.js",
"types": "./dist/exports/types-deprecated.d.ts",

View File

@@ -23,7 +23,7 @@ export async function createGlobalVersion<T extends TypeWithID>(
updatedAt,
versionData,
}: CreateGlobalVersionArgs,
) {
): Promise<TypeWithVersion<T>> {
const db = await getTransaction(this, req)
const global = this.payload.globals.config.find(({ slug }) => slug === globalSlug)

View File

@@ -24,7 +24,7 @@ export async function createVersion<T extends TypeWithID>(
updatedAt,
versionData,
}: CreateVersionArgs<T>,
) {
): Promise<TypeWithVersion<T>> {
const db = await getTransaction(this, req)
const collection = this.payload.collections[collectionSlug].config
const defaultTableName = toSnakeCase(collection.slug)

View File

@@ -0,0 +1,12 @@
export { columnToCodeConverter } from '../sqlite/columnToCodeConverter.js'
export { countDistinct } from '../sqlite/countDistinct.js'
export { convertPathToJSONTraversal } from '../sqlite/createJSONQuery/convertPathToJSONTraversal.js'
export { createJSONQuery } from '../sqlite/createJSONQuery/index.js'
export { defaultDrizzleSnapshot } from '../sqlite/defaultSnapshot.js'
export { deleteWhere } from '../sqlite/deleteWhere.js'
export { dropDatabase } from '../sqlite/dropDatabase.js'
export { execute } from '../sqlite/execute.js'
export { init } from '../sqlite/init.js'
export { insert } from '../sqlite/insert.js'
export { requireDrizzleKit } from '../sqlite/requireDrizzleKit.js'
export * from '../sqlite/types.js'

View File

@@ -9,7 +9,7 @@ import { findMany } from './find/findMany.js'
export async function findOne<T extends TypeWithID>(
this: DrizzleAdapter,
{ collection, draftsEnabled, joins, locale, req, select, where }: FindOneArgs,
): Promise<T> {
): Promise<null | T> {
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))

View File

@@ -1,4 +1,4 @@
import type { ColumnToCodeConverter } from '@payloadcms/drizzle/types'
import type { ColumnToCodeConverter } from '../types.js'
export const columnToCodeConverter: ColumnToCodeConverter = ({
adapter,

View File

@@ -2,10 +2,10 @@ import type { SQLiteSelect } from 'drizzle-orm/sqlite-core'
import { count, sql } from 'drizzle-orm'
import type { CountDistinct, SQLiteAdapter } from './types.js'
import type { BaseSQLiteAdapter, CountDistinct } from './types.js'
export const countDistinct: CountDistinct = async function countDistinct(
this: SQLiteAdapter,
this: BaseSQLiteAdapter,
{ column, db, joins, tableName, where },
) {
// When we don't have any joins - use a simple COUNT(*) query.

View File

@@ -1,4 +1,4 @@
import type { CreateJSONQueryArgs } from '@payloadcms/drizzle/types'
import type { CreateJSONQueryArgs } from '../../types.js'
type FromArrayArgs = {
isRoot?: true
@@ -122,7 +122,7 @@ export const createJSONQuery = ({
return sql
}
if (treatAsArray?.includes(pathSegments[1]!) && table) {
if (treatAsArray?.includes(pathSegments[1]) && table) {
return fromArray({
operator,
pathSegments,

View File

@@ -1,9 +1,9 @@
import type { DeleteWhere, SQLiteAdapter } from './types.js'
import type { BaseSQLiteAdapter, DeleteWhere } from './types.js'
export const deleteWhere: DeleteWhere = async function (
// Here 'this' is not a parameter. See:
// https://www.typescriptlang.org/docs/handbook/2/classes.html#this-parameters
this: SQLiteAdapter,
this: BaseSQLiteAdapter,
{ db, tableName, where },
) {
const table = this.tables[tableName]

View File

@@ -1,15 +1,15 @@
import type { Row } from '@libsql/client'
import type { DropDatabase, SQLiteAdapter } from './types.js'
import type { BaseSQLiteAdapter, DropDatabase } from './types.js'
const getTables = (adapter: SQLiteAdapter) => {
const getTables = (adapter: BaseSQLiteAdapter) => {
return adapter.client.execute(`SELECT name
FROM sqlite_master
WHERE type = 'table'
AND name NOT LIKE 'sqlite_%';`)
}
const dropTables = (adapter: SQLiteAdapter, rows: Row[]) => {
const dropTables = (adapter: BaseSQLiteAdapter, rows: Row[]) => {
const multi = `
PRAGMA foreign_keys = OFF;\n
${rows.map(({ name }) => `DROP TABLE IF EXISTS ${name as string}`).join(';\n ')};\n

View File

@@ -3,13 +3,13 @@ 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)!
const executeFrom = (db ?? drizzle)
if (raw) {
const result = executeFrom.run(sql.raw(raw))
return result
} else {
const result = executeFrom.run(statement!)
const result = executeFrom.run(statement)
return result
}
}

View File

@@ -1,14 +1,15 @@
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { Init } from 'payload'
import { buildDrizzleRelations, buildRawSchema, executeSchemaHooks } from '@payloadcms/drizzle'
import type { SQLiteAdapter } from './types.js'
import type { DrizzleAdapter } from '../types.js'
import type { BaseSQLiteAdapter } from './types.js'
import { buildDrizzleRelations } from '../schema/buildDrizzleRelations.js'
import { buildRawSchema } from '../schema/buildRawSchema.js'
import { executeSchemaHooks } from '../utilities/executeSchemaHooks.js'
import { buildDrizzleTable } from './schema/buildDrizzleTable.js'
import { setColumnID } from './schema/setColumnID.js'
export const init: Init = async function init(this: SQLiteAdapter) {
export const init: Init = async function init(this: BaseSQLiteAdapter) {
let locales: string[] | undefined
this.rawRelations = {}
@@ -28,7 +29,7 @@ export const init: Init = async function init(this: SQLiteAdapter) {
await executeSchemaHooks({ type: 'beforeSchemaInit', adapter: this })
for (const tableName in this.rawTables) {
buildDrizzleTable({ adapter, locales: locales!, rawTable: this.rawTables[tableName]! })
buildDrizzleTable({ adapter, locales, rawTable: this.rawTables[tableName] })
}
buildDrizzleRelations({

View File

@@ -1,9 +1,9 @@
import type { Insert, SQLiteAdapter } from './types.js'
import type { BaseSQLiteAdapter, Insert } from './types.js'
export const insert: Insert = async function (
// Here 'this' is not a parameter. See:
// https://www.typescriptlang.org/docs/handbook/2/classes.html#this-parameters
this: SQLiteAdapter,
this: BaseSQLiteAdapter,
{ db, onConflictDoUpdate, tableName, values },
): Promise<Record<string, unknown>[]> {
const table = this.tables[tableName]

View File

@@ -1,7 +1,7 @@
import type { RequireDrizzleKit } from '@payloadcms/drizzle/types'
import { createRequire } from 'module'
import type { RequireDrizzleKit } from '../types.js'
const require = createRequire(import.meta.url)
export const requireDrizzleKit: RequireDrizzleKit = () => {

View File

@@ -1,4 +1,3 @@
import type { BuildDrizzleTable, RawColumn } from '@payloadcms/drizzle/types'
import type { ForeignKeyBuilder, IndexBuilder } from 'drizzle-orm/sqlite-core'
import { sql } from 'drizzle-orm'
@@ -13,6 +12,8 @@ import {
} from 'drizzle-orm/sqlite-core'
import { v4 as uuidv4 } from 'uuid'
import type { BuildDrizzleTable, RawColumn } from '../../types.js'
const rawColumnBuilderMap: Partial<Record<RawColumn['type'], any>> = {
integer,
numeric,

View File

@@ -1,6 +1,5 @@
import type { SetColumnID } from '@payloadcms/drizzle/types'
import type { SQLiteAdapter } from '../types.js'
import type { SetColumnID } from '../../types.js'
import type { BaseSQLiteAdapter } from '../types.js'
export const setColumnID: SetColumnID = ({ adapter, columns, fields }) => {
const idField = fields.find((field) => field.name === 'id')
@@ -38,7 +37,7 @@ export const setColumnID: SetColumnID = ({ adapter, columns, fields }) => {
columns.id = {
name: 'id',
type: 'integer',
autoIncrement: (adapter as unknown as SQLiteAdapter).autoIncrement,
autoIncrement: (adapter as unknown as BaseSQLiteAdapter).autoIncrement,
primaryKey: true,
}

View File

@@ -0,0 +1,244 @@
import type { Client, ResultSet } from '@libsql/client'
import type { DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
import type { DrizzleD1Database } from 'drizzle-orm/d1'
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
import type {
AnySQLiteColumn,
SQLiteColumn,
SQLiteInsertOnConflictDoUpdateConfig,
SQLiteTableWithColumns,
SQLiteTransactionConfig,
} from 'drizzle-orm/sqlite-core'
import type { SQLiteRaw } from 'drizzle-orm/sqlite-core/query-builders/raw'
import type { Payload, PayloadRequest } from 'payload'
import type { Operators } from '../queries/operatorMap.js'
import type { BuildQueryJoinAliases, DrizzleAdapter } from '../types.js'
import type { extendDrizzleTable } from '../utilities/extendDrizzleTable.js'
type SQLiteSchema = {
relations: Record<string, GenericRelation>
tables: Record<string, SQLiteTableWithColumns<any>>
}
type SQLiteSchemaHookArgs = {
extendTable: typeof extendDrizzleTable
schema: SQLiteSchema
}
export type SQLiteSchemaHook = (args: SQLiteSchemaHookArgs) => Promise<SQLiteSchema> | SQLiteSchema
export type BaseSQLiteArgs = {
/**
* 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 this flag if you want to thread your own ID to create operation data, for example:
* ```ts
* // doc created with id 1
* const doc = await payload.create({ collection: 'posts', data: {id: 1, title: "my title"}})
* ```
*/
allowIDOnCreate?: boolean
/**
* 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[]
/** 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 | SQLiteTransactionConfig
versionsSuffix?: string
}
export type GenericColumns = {
[x: string]: AnySQLiteColumn
}
export type GenericTable = SQLiteTableWithColumns<{
columns: GenericColumns
dialect: string
name: string
schema: string
}>
export type GenericRelation = Relations<string, Record<string, Relation<string>>>
export type CountDistinct = (args: {
column?: SQLiteColumn<any>
db: LibSQLDatabase
joins: BuildQueryJoinAliases
tableName: string
where: SQL
}) => Promise<number>
export type DeleteWhere = (args: {
db: LibSQLDatabase
tableName: string
where: SQL
}) => Promise<void>
export type DropDatabase = (args: { adapter: BaseSQLiteAdapter }) => Promise<void>
export type Execute<T> = (args: {
db?: DrizzleD1Database | LibSQLDatabase
drizzle?: DrizzleD1Database | LibSQLDatabase
raw?: string
sql?: SQL<unknown>
}) => SQLiteRaw<Promise<T>> | SQLiteRaw<ResultSet>
export type Insert = (args: {
db: LibSQLDatabase
onConflictDoUpdate?: SQLiteInsertOnConflictDoUpdateConfig<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 } & LibSQLDatabase<ResolveSchemaType<GeneratedDatabaseSchema>>
export type BaseSQLiteAdapter = {
afterSchemaInit: SQLiteSchemaHook[]
autoIncrement: boolean
beforeSchemaInit: SQLiteSchemaHook[]
client: Client
countDistinct: CountDistinct
defaultDrizzleSnapshot: any
deleteWhere: DeleteWhere
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: BaseSQLiteArgs['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?: BaseSQLiteArgs['schemaName']
tableNameMap: Map<string, string>
tables: Record<string, GenericTable>
transactionOptions: SQLiteTransactionConfig
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
}

View File

@@ -26,7 +26,7 @@ export async function updateGlobalVersion<T extends TypeWithID>(
versionData,
where: whereArg,
}: UpdateGlobalVersionArgs<T>,
) {
): Promise<TypeWithVersion<T>> {
const db = await getTransaction(this, req)
const globalConfig: SanitizedGlobalConfig = this.payload.globals.config.find(
({ slug }) => slug === global,

View File

@@ -26,7 +26,7 @@ export async function updateVersion<T extends TypeWithID>(
versionData,
where: whereArg,
}: UpdateVersionArgs<T>,
) {
): Promise<TypeWithVersion<T>> {
const db = await getTransaction(this, req)
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const whereToUse = whereArg || { id: { equals: id } }

View File

@@ -116,19 +116,20 @@ export const buildCreateMigration = ({
fs.writeFileSync(`${filePath}.json`, JSON.stringify(drizzleJsonAfter, null, 2))
}
const data = getMigrationTemplate({
downSQL: downSQL || ` // Migration code`,
imports,
packageName: payload.db.packageName,
upSQL: upSQL || ` // Migration code`,
})
const fullPath = `${filePath}.ts`
// write migration
fs.writeFileSync(
`${filePath}.ts`,
getMigrationTemplate({
downSQL: downSQL || ` // Migration code`,
imports,
packageName: payload.db.packageName,
upSQL: upSQL || ` // Migration code`,
}),
)
fs.writeFileSync(fullPath, data)
writeMigrationIndex({ migrationsDir: payload.db.migrationDir })
payload.logger.info({ msg: `Migration created at ${filePath}.ts` })
payload.logger.info({ msg: `Migration created at ${fullPath}` })
}
}

View File

@@ -151,6 +151,7 @@ export const rootEslintConfig = [
'../db-postgres/relationships-v2-v3.mjs',
'../db-postgres/scripts/renamePredefinedMigrations.ts',
'../db-sqlite/bundle.js',
'../db-d1-sqlite/bundle.js',
'../db-vercel-postgres/relationships-v2-v3.mjs',
'../db-vercel-postgres/scripts/renamePredefinedMigrations.ts',
'../plugin-cloud-storage/azure.d.ts',

View File

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

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,77 @@
{
"name": "@payloadcms/storage-r2",
"version": "3.48.0",
"description": "Payload storage adapter for Cloudflare R2",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
"url": "https://github.com/payloadcms/payload.git",
"directory": "packages/storage-r2"
},
"license": "MIT",
"author": "Payload <dev@payloadcms.com> (https://payloadcms.com)",
"maintainers": [
{
"name": "Payload",
"email": "info@payloadcms.com",
"url": "https://payloadcms.com"
}
],
"sideEffects": false,
"type": "module",
"exports": {
".": {
"import": "./src/index.ts",
"types": "./src/index.ts",
"default": "./src/index.ts"
},
"./client": {
"import": "./src/exports/client.ts",
"types": "./src/exports/client.ts",
"default": "./src/exports/client.ts"
}
},
"main": "./src/index.ts",
"types": "./src/index.ts",
"files": [
"dist"
],
"scripts": {
"build": "pnpm build:types && pnpm build:swc",
"build:clean": "find . \\( -type d \\( -name build -o -name dist -o -name .cache \\) -o -type f -name tsconfig.tsbuildinfo \\) -exec rm -rf {} + && pnpm build",
"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",
"prepublishOnly": "pnpm clean && pnpm turbo build"
},
"dependencies": {
"@payloadcms/plugin-cloud-storage": "workspace:*"
},
"devDependencies": {
"payload": "workspace:*"
},
"peerDependencies": {
"payload": "workspace:*"
},
"engines": {
"node": "^18.20.2 || >=20.9.0"
},
"publishConfig": {
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./client": {
"import": "./dist/exports/client.js",
"types": "./dist/exports/client.d.ts",
"default": "./dist/exports/client.js"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts"
}
}

View File

@@ -0,0 +1,15 @@
import type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'
import path from 'path'
import type { R2Bucket } from './types.js'
interface Args {
bucket: R2Bucket
}
export const getHandleDelete = ({ bucket }: Args): HandleDelete => {
return async ({ doc: { prefix = '' }, filename }) => {
await bucket.delete(path.posix.join(prefix, filename))
}
}

View File

@@ -0,0 +1,21 @@
import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'
import type { CollectionConfig } from 'payload'
import path from 'path'
import type { R2Bucket } from './types.js'
interface Args {
bucket: R2Bucket
collection: CollectionConfig
prefix?: string
}
export const getHandleUpload = ({ bucket, prefix = '' }: Args): HandleUpload => {
return async ({ data, file }) => {
// Read more: https://github.com/cloudflare/workers-sdk/issues/6047#issuecomment-2691217843
const buffer = process.env.NODE_ENV === 'development' ? new Blob([file.buffer]) : file.buffer
await bucket.put(path.posix.join(prefix, file.filename), buffer)
return data
}
}

View File

@@ -0,0 +1,89 @@
import type {
Adapter,
PluginOptions as CloudStoragePluginOptions,
CollectionOptions,
GeneratedAdapter,
} from '@payloadcms/plugin-cloud-storage/types'
import type { Config, Plugin, UploadCollectionSlug } from 'payload'
import { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'
import type { R2Bucket } from './types.js'
import { getHandleDelete } from './handleDelete.js'
import { getHandleUpload } from './handleUpload.js'
import { getHandler } from './staticHandler.js'
export interface R2StorageOptions {
bucket: R2Bucket
/**
* Collection options to apply the R2 adapter to.
*/
collections: Partial<Record<UploadCollectionSlug, Omit<CollectionOptions, 'adapter'> | true>>
enabled?: boolean
}
type R2StoragePlugin = (r2StorageArgs: R2StorageOptions) => Plugin
export const r2Storage: R2StoragePlugin =
(r2StorageOptions) =>
(incomingConfig: Config): Config => {
const adapter = r2StorageInternal(r2StorageOptions)
const isPluginDisabled = r2StorageOptions.enabled === false
if (isPluginDisabled) {
return incomingConfig
}
// Add adapter to each collection option object
const collectionsWithAdapter: CloudStoragePluginOptions['collections'] = Object.entries(
r2StorageOptions.collections,
).reduce(
(acc, [slug, collOptions]) => ({
...acc,
[slug]: {
...(collOptions === true ? {} : collOptions),
adapter,
},
}),
{} as Record<string, CollectionOptions>,
)
// Set disableLocalStorage: true for collections specified in the plugin options
const config = {
...incomingConfig,
collections: (incomingConfig.collections || []).map((collection) => {
if (!collectionsWithAdapter[collection.slug]) {
return collection
}
return {
...collection,
upload: {
...(typeof collection.upload === 'object' ? collection.upload : {}),
disableLocalStorage: true,
},
}
}),
}
return cloudStoragePlugin({
collections: collectionsWithAdapter,
})(config)
}
function r2StorageInternal({ bucket }: R2StorageOptions): Adapter {
return ({ collection, prefix }): GeneratedAdapter => {
return {
name: 'r2',
handleDelete: getHandleDelete({ bucket }),
handleUpload: getHandleUpload({
bucket,
collection,
prefix,
}),
staticHandler: getHandler({ bucket, collection }),
}
}
}

View File

@@ -0,0 +1,36 @@
import type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'
import type { CollectionConfig } from 'payload'
import path from 'path'
import type { R2Bucket } from './types.js'
interface Args {
bucket: R2Bucket
collection: CollectionConfig
prefix?: string
}
const isMiniflare = process.env.NODE_ENV === 'development'
export const getHandler = ({ bucket, prefix = '' }: Args): StaticHandler => {
return async (req, { params: { filename } }) => {
// Due to https://github.com/cloudflare/workers-sdk/issues/6047
// We cannot send a Headers instance to Miniflare
const obj = await bucket?.get(path.posix.join(prefix, filename), {
range: isMiniflare ? undefined : req.headers,
})
if (obj?.body == undefined) {
return new Response(null, { status: 404 })
}
const headers = new Headers()
if (!isMiniflare) {
obj.writeHttpMetadata(headers)
}
return obj.etag === (req.headers.get('etag') || req.headers.get('if-none-match'))
? new Response(null, { headers, status: 304 })
: new Response(obj.body, { headers, status: 200 })
}
}

View File

@@ -0,0 +1,26 @@
export interface R2Bucket {
createMultipartUpload(key: string, options?: any): Promise<any>
delete(keys: string | string[]): Promise<void>
get(
key: string,
options: {
onlyIf: any | Headers
} & any,
): Promise<any | null>
get(key: string, options?: any): Promise<any | null>
head(key: string): Promise<any>
list(options?: any): Promise<any>
put(
key: string,
value: ArrayBuffer | ArrayBufferView | Blob | null | ReadableStream | string,
options?: {
onlyIf: any
} & any,
): Promise<any | null>
put(
key: string,
value: ArrayBuffer | ArrayBufferView | Blob | null | ReadableStream | string,
options?: any,
): Promise<any>
resumeMultipartUpload(key: string, uploadId: string): any
}

View File

@@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true, // Make sure typescript knows that this module depends on their references
"noEmit": false /* Do not emit outputs. */,
"emitDeclarationOnly": true,
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
"rootDir": "./src" /* Specify the root folder within your source files. */,
// Do not include DOM and DOM.Iterable as this is a server-only package.
"lib": ["ES2022"],
},
"exclude": ["dist", "node_modules"],
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
"references": [{ "path": "../payload" }, { "path": "../plugin-cloud-storage" }]
}

252
pnpm-lock.yaml generated
View File

@@ -101,7 +101,7 @@ importers:
version: 0.31.4
drizzle-orm:
specifier: 0.44.2
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3)
escape-html:
specifier: ^1.0.3
version: 1.0.3
@@ -270,6 +270,46 @@ importers:
specifier: 22.15.30
version: 22.15.30
packages/db-d1-sqlite:
dependencies:
'@payloadcms/drizzle':
specifier: workspace:*
version: link:../drizzle
console-table-printer:
specifier: 2.12.1
version: 2.12.1
drizzle-kit:
specifier: 0.31.4
version: 0.31.4
drizzle-orm:
specifier: 0.44.2
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3)
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/pg':
specifier: 8.10.2
version: 8.10.2
'@types/to-snake-case':
specifier: 1.0.0
version: 1.0.0
'@types/uuid':
specifier: 10.0.0
version: 10.0.0
payload:
specifier: workspace:*
version: link:../payload
packages/db-mongodb:
dependencies:
mongoose:
@@ -323,7 +363,7 @@ importers:
version: 0.31.4
drizzle-orm:
specifier: 0.44.2
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3)
pg:
specifier: 8.16.3
version: 8.16.3
@@ -369,7 +409,7 @@ importers:
version: 0.31.4
drizzle-orm:
specifier: 0.44.2
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3)
prompts:
specifier: 2.4.2
version: 2.4.2
@@ -412,7 +452,7 @@ importers:
version: 0.31.4
drizzle-orm:
specifier: 0.44.2
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3)
pg:
specifier: 8.16.3
version: 8.16.3
@@ -455,7 +495,7 @@ importers:
version: 2.0.3
drizzle-orm:
specifier: 0.44.2
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3)
prompts:
specifier: 2.4.2
version: 2.4.2
@@ -1473,6 +1513,16 @@ importers:
specifier: workspace:*
version: link:../payload
packages/storage-r2:
dependencies:
'@payloadcms/plugin-cloud-storage':
specifier: workspace:*
version: link:../plugin-cloud-storage
devDependencies:
payload:
specifier: workspace:*
version: link:../payload
packages/storage-s3:
dependencies:
'@aws-sdk/client-s3':
@@ -1941,12 +1991,21 @@ importers:
'@date-fns/tz':
specifier: 1.2.0
version: 1.2.0
'@miniflare/d1':
specifier: 2.14.4
version: 2.14.4
'@miniflare/shared':
specifier: 2.14.4
version: 2.14.4
'@next/env':
specifier: 15.4.4
version: 15.4.4
'@payloadcms/admin-bar':
specifier: workspace:*
version: link:../packages/admin-bar
'@payloadcms/db-d1-sqlite':
specifier: workspace:*
version: link:../packages/db-d1-sqlite
'@payloadcms/db-mongodb':
specifier: workspace:*
version: link:../packages/db-mongodb
@@ -2064,6 +2123,9 @@ importers:
babel-plugin-react-compiler:
specifier: 19.1.0-rc.3
version: 19.1.0-rc.3
better-sqlite3:
specifier: 11.10.0
version: 11.10.0
comment-json:
specifier: ^4.2.3
version: 4.2.5
@@ -2084,7 +2146,7 @@ importers:
version: 0.31.4
drizzle-orm:
specifier: 0.44.2
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3)
escape-html:
specifier: 1.0.3
version: 1.0.3
@@ -4025,6 +4087,9 @@ packages:
cjs-module-lexer:
optional: true
'@iarna/toml@2.2.5':
resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==}
'@img/sharp-darwin-arm64@0.33.5':
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -4611,6 +4676,31 @@ packages:
cpu: [x64]
os: [win32]
'@miniflare/core@2.14.4':
resolution: {integrity: sha512-FMmZcC1f54YpF4pDWPtdQPIO8NXfgUxCoR9uyrhxKJdZu7M6n8QKopPVNuaxR40jcsdxb7yKoQoFWnHfzJD9GQ==}
engines: {node: '>=16.13'}
deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4
'@miniflare/d1@2.14.4':
resolution: {integrity: sha512-pMBVq9XWxTDdm+RRCkfXZP+bREjPg1JC8s8C0JTovA9OGmLQXqGTnFxIaS9vf1d8k3uSUGhDzPTzHr0/AUW1gA==}
engines: {node: '>=16.7'}
deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4
'@miniflare/queues@2.14.4':
resolution: {integrity: sha512-aXQ5Ik8Iq1KGMBzGenmd6Js/jJgqyYvjom95/N9GptCGpiVWE5F0XqC1SL5rCwURbHN+aWY191o8XOFyY2nCUA==}
engines: {node: '>=16.7'}
deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4
'@miniflare/shared@2.14.4':
resolution: {integrity: sha512-upl4RSB3hyCnITOFmRZjJj4A72GmkVrtfZTilkdq5Qe5TTlzsjVeDJp7AuNUM9bM8vswRo+N5jOiot6O4PVwwQ==}
engines: {node: '>=16.13'}
deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4
'@miniflare/watcher@2.14.4':
resolution: {integrity: sha512-PYn05ET2USfBAeXF6NZfWl0O32KVyE8ncQ/ngysrh3hoIV7l3qGGH7ubeFx+D8VWQ682qYhwGygUzQv2j1tGGg==}
engines: {node: '>=16.13'}
deprecated: Miniflare v2 is no longer supported. Please upgrade to Miniflare v4
'@monaco-editor/loader@1.5.0':
resolution: {integrity: sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==}
@@ -6243,6 +6333,9 @@ packages:
'@types/babel__traverse@7.20.6':
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
'@types/better-sqlite3@7.6.13':
resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
'@types/busboy@1.5.4':
resolution: {integrity: sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==}
@@ -7121,6 +7214,9 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
better-sqlite3@11.10.0:
resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==}
bignumber.js@9.1.2:
resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==}
@@ -7136,6 +7232,9 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
birecord@0.1.1:
resolution: {integrity: sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw==}
@@ -7203,6 +7302,9 @@ packages:
resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==}
engines: {node: '>=6.14.2'}
builtins@5.1.0:
resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==}
bundle-name@4.1.0:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
engines: {node: '>=18'}
@@ -8349,6 +8451,10 @@ packages:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'}
execa@6.1.0:
resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
execa@8.0.1:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
engines: {node: '>=16.17'}
@@ -8470,6 +8576,9 @@ packages:
resolution: {integrity: sha512-mROwiKLZf/Kwa/2Rol+OOZQn1eyTkPB3ZTwC0ExY6OLFCbgxHYZvBm7xI77NvfZFMKBsmuXfmLJnD4eEftEhrA==}
engines: {node: '>=18'}
file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
filename-reserved-regex@3.0.0:
resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -8912,6 +9021,10 @@ packages:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
human-signals@3.0.1:
resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
engines: {node: '>=12.20.0'}
human-signals@5.0.0:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'}
@@ -9560,6 +9673,10 @@ packages:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
kleur@4.1.5:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
language-subtag-registry@0.3.23:
resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
@@ -10161,6 +10278,9 @@ packages:
resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
npx-import@1.1.4:
resolution: {integrity: sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==}
nwsapi@2.2.20:
resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==}
@@ -10325,6 +10445,9 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
parse-package-name@1.0.0:
resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==}
parse-passwd@1.0.0:
resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
engines: {node: '>=0.10.0'}
@@ -11224,6 +11347,9 @@ packages:
server-only@0.0.1:
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
set-cookie-parser@2.7.1:
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
@@ -12146,6 +12272,9 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
urlpattern-polyfill@4.0.3:
resolution: {integrity: sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==}
use-callback-ref@1.3.3:
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
engines: {node: '>=10'}
@@ -12217,6 +12346,10 @@ packages:
validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
validate-npm-package-name@4.0.0:
resolution: {integrity: sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
varint@6.0.0:
resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
@@ -13487,7 +13620,7 @@ snapshots:
'@babel/core': 7.27.3
'@babel/helper-compilation-targets': 7.27.2
'@babel/helper-plugin-utils': 7.27.1
debug: 4.3.7
debug: 4.4.1
lodash.debounce: 4.0.8
resolve: 1.22.8
transitivePeerDependencies:
@@ -13498,7 +13631,7 @@ snapshots:
'@babel/core': 7.27.3
'@babel/helper-compilation-targets': 7.27.2
'@babel/helper-plugin-utils': 7.27.1
debug: 4.3.7
debug: 4.4.1
lodash.debounce: 4.0.8
resolve: 1.22.8
transitivePeerDependencies:
@@ -14953,6 +15086,8 @@ snapshots:
optionalDependencies:
cjs-module-lexer: 1.4.1
'@iarna/toml@2.2.5': {}
'@img/sharp-darwin-arm64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.0.4
@@ -15620,6 +15755,39 @@ snapshots:
'@libsql/win32-x64-msvc@0.4.7':
optional: true
'@miniflare/core@2.14.4':
dependencies:
'@iarna/toml': 2.2.5
'@miniflare/queues': 2.14.4
'@miniflare/shared': 2.14.4
'@miniflare/watcher': 2.14.4
busboy: 1.6.0
dotenv: 16.4.7
kleur: 4.1.5
set-cookie-parser: 2.7.1
undici: 5.28.4
urlpattern-polyfill: 4.0.3
'@miniflare/d1@2.14.4':
dependencies:
'@miniflare/core': 2.14.4
'@miniflare/shared': 2.14.4
'@miniflare/queues@2.14.4':
dependencies:
'@miniflare/shared': 2.14.4
'@miniflare/shared@2.14.4':
dependencies:
'@types/better-sqlite3': 7.6.13
kleur: 4.1.5
npx-import: 1.1.4
picomatch: 2.3.1
'@miniflare/watcher@2.14.4':
dependencies:
'@miniflare/shared': 2.14.4
'@monaco-editor/loader@1.5.0':
dependencies:
state-local: 1.0.7
@@ -17446,6 +17614,10 @@ snapshots:
dependencies:
'@babel/types': 7.27.3
'@types/better-sqlite3@7.6.13':
dependencies:
'@types/node': 22.5.4
'@types/busboy@1.5.4':
dependencies:
'@types/node': 22.15.30
@@ -17748,7 +17920,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 8.14.0
'@typescript-eslint/visitor-keys': 8.14.0
debug: 4.3.7
debug: 4.4.1
fast-glob: 3.3.2
is-glob: 4.0.3
minimatch: 9.0.5
@@ -18545,6 +18717,11 @@ snapshots:
base64-js@1.5.1: {}
better-sqlite3@11.10.0:
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.2
bignumber.js@9.1.2: {}
bin-version-check@5.1.0:
@@ -18560,6 +18737,10 @@ snapshots:
binary-extensions@2.3.0: {}
bindings@1.5.0:
dependencies:
file-uri-to-path: 1.0.0
birecord@0.1.1: {}
bl@4.1.0:
@@ -18635,6 +18816,10 @@ snapshots:
dependencies:
node-gyp-build: 4.8.2
builtins@5.1.0:
dependencies:
semver: 7.7.1
bundle-name@4.1.0:
dependencies:
run-applescript: 7.0.0
@@ -19190,21 +19375,25 @@ snapshots:
transitivePeerDependencies:
- supports-color
drizzle-orm@0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3):
drizzle-orm@0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3):
optionalDependencies:
'@libsql/client': 0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
'@opentelemetry/api': 1.9.0
'@types/better-sqlite3': 7.6.13
'@types/pg': 8.10.2
'@vercel/postgres': 0.9.0
better-sqlite3: 11.10.0
gel: 2.0.1
pg: 8.16.3
drizzle-orm@0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3):
drizzle-orm@0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(better-sqlite3@11.10.0)(gel@2.0.1)(pg@8.16.3):
optionalDependencies:
'@libsql/client': 0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
'@opentelemetry/api': 1.9.0
'@types/better-sqlite3': 7.6.13
'@types/pg': 8.11.6
'@vercel/postgres': 0.9.0
better-sqlite3: 11.10.0
gel: 2.0.1
pg: 8.16.3
@@ -19995,6 +20184,18 @@ snapshots:
signal-exit: 3.0.7
strip-final-newline: 2.0.0
execa@6.1.0:
dependencies:
cross-spawn: 7.0.6
get-stream: 6.0.1
human-signals: 3.0.1
is-stream: 3.0.0
merge-stream: 2.0.0
npm-run-path: 5.3.0
onetime: 6.0.0
signal-exit: 3.0.7
strip-final-newline: 3.0.0
execa@8.0.1:
dependencies:
cross-spawn: 7.0.5
@@ -20117,6 +20318,8 @@ snapshots:
token-types: 6.0.0
uint8array-extras: 1.4.0
file-uri-to-path@1.0.0: {}
filename-reserved-regex@3.0.0: {}
filenamify@6.0.0:
@@ -20597,7 +20800,7 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.1
debug: 4.3.7
debug: 4.4.1
transitivePeerDependencies:
- supports-color
@@ -20618,7 +20821,7 @@ snapshots:
https-proxy-agent@7.0.5:
dependencies:
agent-base: 7.1.1
debug: 4.3.7
debug: 4.4.1
transitivePeerDependencies:
- supports-color
@@ -20631,6 +20834,8 @@ snapshots:
human-signals@2.1.0: {}
human-signals@3.0.1: {}
human-signals@5.0.0: {}
husky@9.0.11: {}
@@ -21455,6 +21660,8 @@ snapshots:
kleur@3.0.3: {}
kleur@4.1.5: {}
language-subtag-registry@0.3.23: {}
language-tags@1.0.9:
@@ -22063,7 +22270,7 @@ snapshots:
new-find-package-json@2.0.0:
dependencies:
debug: 4.3.7
debug: 4.4.1
transitivePeerDependencies:
- supports-color
@@ -22248,6 +22455,13 @@ snapshots:
dependencies:
path-key: 4.0.0
npx-import@1.1.4:
dependencies:
execa: 6.1.0
parse-package-name: 1.0.0
semver: 7.7.1
validate-npm-package-name: 4.0.0
nwsapi@2.2.20: {}
nypm@0.3.12:
@@ -22449,6 +22663,8 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
parse-package-name@1.0.0: {}
parse-passwd@1.0.0: {}
parse5@7.3.0:
@@ -23344,6 +23560,8 @@ snapshots:
server-only@0.0.1: {}
set-cookie-parser@2.7.1: {}
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
@@ -24481,6 +24699,8 @@ snapshots:
dependencies:
punycode: 2.3.1
urlpattern-polyfill@4.0.3: {}
use-callback-ref@1.3.3(@types/react@19.1.8)(react@19.1.1):
dependencies:
react: 19.1.1
@@ -24536,6 +24756,10 @@ snapshots:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
validate-npm-package-name@4.0.0:
dependencies:
builtins: 5.1.0
varint@6.0.0: {}
vfile-message@4.0.2:

View File

@@ -0,0 +1 @@
PAYLOAD_SECRET=THISISYOURSECRET

54
templates/with-cloudflare-d1/.gitignore vendored Normal file
View File

@@ -0,0 +1,54 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
/.idea/*
!/.idea/runConfigurations
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.env
/media
# Playwright
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
# OpenNext
.open-next
.wrangler

View File

@@ -0,0 +1 @@
legacy-peer-deps=true

View File

@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": false
}

View File

@@ -0,0 +1,3 @@
{
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
}

View File

@@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug full stack",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/next/dist/bin/next",
"runtimeArgs": ["--inspect"],
"skipFiles": ["<node_internals>/**"],
"serverReadyAction": {
"action": "debugWithChrome",
"killOnServerStop": true,
"pattern": "- Local:.+(https?://.+)",
"uriFormat": "%s",
"webRoot": "${workspaceFolder}"
},
"cwd": "${workspaceFolder}"
}
]
}

View File

@@ -0,0 +1,40 @@
{
"npm.packageManager": "pnpm",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"editor.formatOnSaveMode": "file",
"typescript.tsdk": "node_modules/typescript/lib",
"[javascript][typescript][typescriptreact]": {
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
}

View File

@@ -0,0 +1 @@
--install.ignore-engines true

View File

@@ -0,0 +1,99 @@
# Payload Cloudflare Template
[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/payloadcms/payload/tree/feat/add-d1-adapter/templates/with-cloudflare-d1)
This template comes configured with the bare minimum to get started on anything you need.
## Quick start
This template can be deployed directly to Cloudflare Workers by clicking the button to take you to the setup screen.
From there you can connect your code to a git provider such Github or Gitlab, name your Workers, D1 Database and R2 Bucket as well as attach any additional environment variables or services you need.
## Quick Start - local setup
To spin up this template locally, follow these steps:
### Clone
After you click the `Deploy` button above, you'll want to have standalone copy of this repo on your machine. Cloudflare will connect your app to a git provider such as Github and you can access your code from there.
### Local Development
## How it works
Out of the box, using [`Wrangler`](https://developers.cloudflare.com/workers/wrangler/) will automatically create local bindings for you to connect to the remote services and it can even create a local mock of the services you're using with Cloudflare.
We've pre-configured Payload for you with the following:
### Collections
See the [Collections](https://payloadcms.com/docs/configuration/collections) docs for details on how to extend this functionality.
- #### Users (Authentication)
Users are auth-enabled collections that have access to the admin panel.
For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth) or the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs.
- #### Media
This is the uploads enabled collection.
### Image Storage (R2)
Images will be served from an R2 bucket which you can then further configure to use a CDN to serve for your frontend directly.
### D1 Database
The Worker will have direct access to a D1 SQLite database which Wrangler can connect locally to, just note that you won't have a connection string as you would typically with other providers.
## Working with Cloudflare
Firstly, after installing dependencies locally you need to authenticate with Wrangler by running:
```bash
pnpm wrangler login
```
This will take you to Cloudflare to login and then you can use the Wrangler CLI locally for anything, use `pnpm wrangler help` to see all available options.
Wrangler is pretty smart so it will automatically bind your services for local development just by running `pnpm dev`.
## Deployments
When you're ready to deploy, first make sure you have created your migrations:
```bash
pnpm payload migrate:create
```
Then run the following command:
```bash
pnpm run deploy
```
This will spin up Wrangler in `production` mode, run any created migrations, build the app and then deploy the bundle up to Cloudflare.
That's it! You can if you wish move these steps into your CI pipeline as well.
## Enabling logs
By default logs are not enabled for your API, we've made this decision because it does run against your quota so we've left it opt-in. But you can easily enable logs in one click in the Cloudflare panel, [see docs](https://developers.cloudflare.com/workers/observability/logs/workers-logs/#enable-workers-logs).
## Known issues
### GraphQL
We are currently waiting on some issues with GraphQL to be [fixed upstream in Workers](https://github.com/cloudflare/workerd/issues/5175) so full support for GraphQL is not currently guaranteed when deployed.
### Worker size limits
We currently recommend deploying this template to the Paid Workers plan due to bundle [size limits](https://developers.cloudflare.com/workers/platform/limits/#worker-size) of 3mb. We're actively trying to reduce our bundle footprint over time to better meet this metric.
This also applies to your own code, in the case of importing a lot of libraries you may find yourself limited by the bundle.
## Questions
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { FlatCompat } from '@eslint/eslintrc'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
})
const eslintConfig = [
...compat.extends('next/core-web-vitals', 'next/typescript'),
{
rules: {
'@typescript-eslint/ban-ts-comment': 'warn',
'@typescript-eslint/no-empty-object-type': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': [
'warn',
{
vars: 'all',
args: 'after-used',
ignoreRestSiblings: false,
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^(_|ignore)',
},
],
},
},
{
ignores: ['.next/'],
},
]
export default eslintConfig

View File

@@ -0,0 +1,20 @@
import { withPayload } from '@payloadcms/next/withPayload'
import { initOpenNextCloudflareForDev } from '@opennextjs/cloudflare'
initOpenNextCloudflareForDev({ environment: process.env.CLOUDFLARE_ENV })
/** @type {import('next').NextConfig} */
const nextConfig = {
// Your Next.js config here
webpack: (webpackConfig: any) => {
webpackConfig.resolve.extensionAlias = {
'.cjs': ['.cts', '.cjs'],
'.js': ['.ts', '.tsx', '.js', '.jsx'],
'.mjs': ['.mts', '.mjs'],
}
return webpackConfig
},
}
export default withPayload(nextConfig, { devBundleServerPackages: false })

View File

@@ -0,0 +1,4 @@
// default open-next.config.ts file created by @opennextjs/cloudflare
import { defineCloudflareConfig } from '@opennextjs/cloudflare/config'
export default defineCloudflareConfig({})

View File

@@ -0,0 +1,84 @@
{
"name": "with-cloudflare-d1",
"version": "1.0.0",
"description": "A blank template to get started with Payload 3.0",
"license": "MIT",
"type": "module",
"scripts": {
"build": "cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=8000\" next build",
"deploy": "pnpm run deploy:database && pnpm run deploy:app",
"deploy:app": "opennextjs-cloudflare build -e $CLOUDFLARE_ENV && opennextjs-cloudflare deploy -e $CLOUDFLARE_ENV",
"deploy:database": "NODE_ENV=production PAYLOAD_SECRET=ignore payload migrate && wrangler d1 execute D1 --command 'PRAGMA optimize' --remote",
"dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"devsafe": "rm -rf .next && rm -rf .open-next && cross-env NODE_OPTIONS=--no-deprecation next dev",
"generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap",
"generate:types": "pnpm run generate:types:cloudflare && pnpm run generate:types:payload",
"generate:types:cloudflare": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts",
"generate:types:payload": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types",
"ii": "pnpm install --ignore-workspace",
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview -e $CLOUDFLARE_ENV",
"start": "cross-env NODE_OPTIONS=--no-deprecation next start",
"test": "pnpm run test:int && pnpm run test:e2e",
"test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" pnpm exec playwright test",
"test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts"
},
"dependencies": {
"@opennextjs/cloudflare": "^1.6.5",
"@payloadcms/db-d1-sqlite": "3.58.0-internal.8775d75",
"@payloadcms/next": "3.58.0-internal.8775d75",
"@payloadcms/payload-cloud": "3.58.0-internal.8775d75",
"@payloadcms/richtext-lexical": "3.58.0-internal.8775d75",
"@payloadcms/storage-r2": "3.58.0-internal.8775d75",
"@payloadcms/ui": "3.58.0-internal.8775d75",
"cross-env": "^7.0.3",
"dotenv": "16.4.7",
"graphql": "^16.8.1",
"next": "15.4.4",
"payload": "3.58.0-internal.8775d75",
"react": "19.1.0",
"react-dom": "19.1.0"
},
"devDependencies": {
"@playwright/test": "1.54.1",
"@testing-library/react": "16.3.0",
"@types/node": "^22.5.4",
"@types/react": "19.1.8",
"@types/react-dom": "19.1.6",
"@vitejs/plugin-react": "4.5.2",
"eslint": "^9.16.0",
"eslint-config-next": "15.4.4",
"jsdom": "26.1.0",
"playwright": "1.54.1",
"playwright-core": "1.54.1",
"prettier": "^3.4.2",
"typescript": "5.7.3",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.2.3",
"wrangler": "^4.26.1"
},
"engines": {
"node": "^18.20.2 || >=20.9.0",
"pnpm": "^9 || ^10"
},
"pnpm": {
"onlyBuiltDependencies": [
"sharp",
"esbuild",
"unrs-resolver"
]
},
"cloudflare": {
"bindings": {
"PAYLOAD_SECRET": {
"description": "Generate a random string using `openssl rand -hex 32`"
}
}
},
"overrides": {
"@payloadcms/db-d1-sqlite": {
"drizzle-kit": "0.30.6"
}
}
}

View File

@@ -0,0 +1,41 @@
import { defineConfig, devices } from '@playwright/test'
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
import 'dotenv/config'
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests/e2e',
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
command: 'pnpm dev',
reuseExistingServer: true,
url: 'http://localhost:3000',
},
})

View File

@@ -0,0 +1,19 @@
import React from 'react'
import './styles.css'
export const metadata = {
description: 'A blank template using Payload in a Next.js app.',
title: 'Payload Blank Template',
}
export default async function RootLayout(props: { children: React.ReactNode }) {
const { children } = props
return (
<html lang="en">
<body>
<main>{children}</main>
</body>
</html>
)
}

View File

@@ -0,0 +1,59 @@
import { headers as getHeaders } from 'next/headers.js'
import Image from 'next/image'
import { getPayload } from 'payload'
import React from 'react'
import { fileURLToPath } from 'url'
import config from '@/payload.config'
import './styles.css'
export default async function HomePage() {
const headers = await getHeaders()
const payloadConfig = await config
const payload = await getPayload({ config: payloadConfig })
const { user } = await payload.auth({ headers })
const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}`
return (
<div className="home">
<div className="content">
<picture>
<source srcSet="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-favicon.svg" />
<Image
alt="Payload Logo"
height={65}
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-favicon.svg"
width={65}
/>
</picture>
{!user && <h1>Welcome to your new project.</h1>}
{user && <h1>Welcome back, {user.email}</h1>}
<div className="links">
<a
className="admin"
href={payloadConfig.routes.admin}
rel="noopener noreferrer"
target="_blank"
>
Go to admin panel
</a>
<a
className="docs"
href="https://payloadcms.com/docs"
rel="noopener noreferrer"
target="_blank"
>
Documentation
</a>
</div>
</div>
<div className="footer">
<p>Update this page by editing</p>
<a className="codeLink" href={fileURL}>
<code>app/(frontend)/page.tsx</code>
</a>
</div>
</div>
)
}

View File

@@ -0,0 +1,164 @@
:root {
--font-mono: 'Roboto Mono', monospace;
}
* {
box-sizing: border-box;
}
html {
font-size: 18px;
line-height: 32px;
background: rgb(0, 0, 0);
-webkit-font-smoothing: antialiased;
}
html,
body,
#app {
height: 100%;
}
body {
font-family: system-ui;
font-size: 18px;
line-height: 32px;
margin: 0;
color: rgb(1000, 1000, 1000);
@media (max-width: 1024px) {
font-size: 15px;
line-height: 24px;
}
}
img {
max-width: 100%;
height: auto;
display: block;
}
h1 {
margin: 40px 0;
font-size: 64px;
line-height: 70px;
font-weight: bold;
@media (max-width: 1024px) {
margin: 24px 0;
font-size: 42px;
line-height: 42px;
}
@media (max-width: 768px) {
font-size: 38px;
line-height: 38px;
}
@media (max-width: 400px) {
font-size: 32px;
line-height: 32px;
}
}
p {
margin: 24px 0;
@media (max-width: 1024px) {
margin: calc(var(--base) * 0.75) 0;
}
}
a {
color: currentColor;
&:focus {
opacity: 0.8;
outline: none;
}
&:active {
opacity: 0.7;
outline: none;
}
}
svg {
vertical-align: middle;
}
.home {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
height: 100vh;
padding: 45px;
max-width: 1024px;
margin: 0 auto;
overflow: hidden;
@media (max-width: 400px) {
padding: 24px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-grow: 1;
h1 {
text-align: center;
}
}
.links {
display: flex;
align-items: center;
gap: 12px;
a {
text-decoration: none;
padding: 0.25rem 0.5rem;
border-radius: 4px;
}
.admin {
color: rgb(0, 0, 0);
background: rgb(1000, 1000, 1000);
border: 1px solid rgb(0, 0, 0);
}
.docs {
color: rgb(1000, 1000, 1000);
background: rgb(0, 0, 0);
border: 1px solid rgb(1000, 1000, 1000);
}
}
.footer {
display: flex;
align-items: center;
gap: 8px;
@media (max-width: 1024px) {
flex-direction: column;
gap: 6px;
}
p {
margin: 0;
}
.codeLink {
text-decoration: none;
padding: 0 0.5rem;
background: rgb(60, 60, 60);
border-radius: 4px;
}
}
}

View File

@@ -0,0 +1,24 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../importMap'
type Args = {
params: Promise<{
segments: string[]
}>
searchParams: Promise<{
[key: string]: string | string[]
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) =>
NotFoundPage({ config, params, searchParams, importMap })
export default NotFound

View File

@@ -0,0 +1,24 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../importMap'
type Args = {
params: Promise<{
segments: string[]
}>
searchParams: Promise<{
[key: string]: string | string[]
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) =>
RootPage({ config, params, searchParams, importMap })
export default Page

View File

@@ -0,0 +1 @@
export const importMap = {}

View File

@@ -0,0 +1,19 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import {
REST_DELETE,
REST_GET,
REST_OPTIONS,
REST_PATCH,
REST_POST,
REST_PUT,
} from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)
export const PUT = REST_PUT(config)
export const OPTIONS = REST_OPTIONS(config)

View File

@@ -0,0 +1,7 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
export const GET = GRAPHQL_PLAYGROUND_GET(config)

View File

@@ -0,0 +1,8 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)
export const OPTIONS = REST_OPTIONS(config)

View File

@@ -0,0 +1,31 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import type { ServerFunctionClient } from 'payload'
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts'
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const serverFunction: ServerFunctionClient = async function (args) {
'use server'
return handleServerFunctions({
...args,
config,
importMap,
})
}
const Layout = ({ children }: Args) => (
<RootLayout config={config} importMap={importMap} serverFunction={serverFunction}>
{children}
</RootLayout>
)
export default Layout

View File

@@ -0,0 +1,12 @@
import configPromise from '@payload-config'
import { getPayload } from 'payload'
export const GET = async (request: Request) => {
const payload = await getPayload({
config: configPromise,
})
return Response.json({
message: 'This is an example of a custom route.',
})
}

View File

@@ -0,0 +1,20 @@
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
upload: {
// These are not supported on Workers yet due to lack of sharp
crop: false,
focalPoint: false,
},
}

View File

@@ -0,0 +1,13 @@
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',
admin: {
useAsTitle: 'email',
},
auth: true,
fields: [
// Email added by default
// Add more fields as needed
],
}

View File

@@ -0,0 +1,655 @@
{
"version": "6",
"dialect": "sqlite",
"tables": {
"users_sessions": {
"name": "users_sessions",
"columns": {
"_order": {
"name": "_order",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"_parent_id": {
"name": "_parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"expires_at": {
"name": "expires_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"users_sessions_order_idx": {
"name": "users_sessions_order_idx",
"columns": ["_order"],
"isUnique": false
},
"users_sessions_parent_id_idx": {
"name": "users_sessions_parent_id_idx",
"columns": ["_parent_id"],
"isUnique": false
}
},
"foreignKeys": {
"users_sessions_parent_id_fk": {
"name": "users_sessions_parent_id_fk",
"tableFrom": "users_sessions",
"tableTo": "users",
"columnsFrom": ["_parent_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reset_password_token": {
"name": "reset_password_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"reset_password_expiration": {
"name": "reset_password_expiration",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"salt": {
"name": "salt",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"hash": {
"name": "hash",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"login_attempts": {
"name": "login_attempts",
"type": "numeric",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": 0
},
"lock_until": {
"name": "lock_until",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"users_updated_at_idx": {
"name": "users_updated_at_idx",
"columns": ["updated_at"],
"isUnique": false
},
"users_created_at_idx": {
"name": "users_created_at_idx",
"columns": ["created_at"],
"isUnique": false
},
"users_email_idx": {
"name": "users_email_idx",
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"media": {
"name": "media",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"alt": {
"name": "alt",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"thumbnail_u_r_l": {
"name": "thumbnail_u_r_l",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"filename": {
"name": "filename",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"mime_type": {
"name": "mime_type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"filesize": {
"name": "filesize",
"type": "numeric",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"width": {
"name": "width",
"type": "numeric",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"height": {
"name": "height",
"type": "numeric",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"media_updated_at_idx": {
"name": "media_updated_at_idx",
"columns": ["updated_at"],
"isUnique": false
},
"media_created_at_idx": {
"name": "media_created_at_idx",
"columns": ["created_at"],
"isUnique": false
},
"media_filename_idx": {
"name": "media_filename_idx",
"columns": ["filename"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"payload_locked_documents": {
"name": "payload_locked_documents",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"global_slug": {
"name": "global_slug",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
}
},
"indexes": {
"payload_locked_documents_global_slug_idx": {
"name": "payload_locked_documents_global_slug_idx",
"columns": ["global_slug"],
"isUnique": false
},
"payload_locked_documents_updated_at_idx": {
"name": "payload_locked_documents_updated_at_idx",
"columns": ["updated_at"],
"isUnique": false
},
"payload_locked_documents_created_at_idx": {
"name": "payload_locked_documents_created_at_idx",
"columns": ["created_at"],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"payload_locked_documents_rels": {
"name": "payload_locked_documents_rels",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"parent_id": {
"name": "parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"path": {
"name": "path",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"users_id": {
"name": "users_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"media_id": {
"name": "media_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"payload_locked_documents_rels_order_idx": {
"name": "payload_locked_documents_rels_order_idx",
"columns": ["order"],
"isUnique": false
},
"payload_locked_documents_rels_parent_idx": {
"name": "payload_locked_documents_rels_parent_idx",
"columns": ["parent_id"],
"isUnique": false
},
"payload_locked_documents_rels_path_idx": {
"name": "payload_locked_documents_rels_path_idx",
"columns": ["path"],
"isUnique": false
},
"payload_locked_documents_rels_users_id_idx": {
"name": "payload_locked_documents_rels_users_id_idx",
"columns": ["users_id"],
"isUnique": false
},
"payload_locked_documents_rels_media_id_idx": {
"name": "payload_locked_documents_rels_media_id_idx",
"columns": ["media_id"],
"isUnique": false
}
},
"foreignKeys": {
"payload_locked_documents_rels_parent_fk": {
"name": "payload_locked_documents_rels_parent_fk",
"tableFrom": "payload_locked_documents_rels",
"tableTo": "payload_locked_documents",
"columnsFrom": ["parent_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
"payload_locked_documents_rels_users_fk": {
"name": "payload_locked_documents_rels_users_fk",
"tableFrom": "payload_locked_documents_rels",
"tableTo": "users",
"columnsFrom": ["users_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
"payload_locked_documents_rels_media_fk": {
"name": "payload_locked_documents_rels_media_fk",
"tableFrom": "payload_locked_documents_rels",
"tableTo": "media",
"columnsFrom": ["media_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"payload_preferences": {
"name": "payload_preferences",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"key": {
"name": "key",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"value": {
"name": "value",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
}
},
"indexes": {
"payload_preferences_key_idx": {
"name": "payload_preferences_key_idx",
"columns": ["key"],
"isUnique": false
},
"payload_preferences_updated_at_idx": {
"name": "payload_preferences_updated_at_idx",
"columns": ["updated_at"],
"isUnique": false
},
"payload_preferences_created_at_idx": {
"name": "payload_preferences_created_at_idx",
"columns": ["created_at"],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"payload_preferences_rels": {
"name": "payload_preferences_rels",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"parent_id": {
"name": "parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"path": {
"name": "path",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"users_id": {
"name": "users_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"payload_preferences_rels_order_idx": {
"name": "payload_preferences_rels_order_idx",
"columns": ["order"],
"isUnique": false
},
"payload_preferences_rels_parent_idx": {
"name": "payload_preferences_rels_parent_idx",
"columns": ["parent_id"],
"isUnique": false
},
"payload_preferences_rels_path_idx": {
"name": "payload_preferences_rels_path_idx",
"columns": ["path"],
"isUnique": false
},
"payload_preferences_rels_users_id_idx": {
"name": "payload_preferences_rels_users_id_idx",
"columns": ["users_id"],
"isUnique": false
}
},
"foreignKeys": {
"payload_preferences_rels_parent_fk": {
"name": "payload_preferences_rels_parent_fk",
"tableFrom": "payload_preferences_rels",
"tableTo": "payload_preferences",
"columnsFrom": ["parent_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
"payload_preferences_rels_users_fk": {
"name": "payload_preferences_rels_users_fk",
"tableFrom": "payload_preferences_rels",
"tableTo": "users",
"columnsFrom": ["users_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"payload_migrations": {
"name": "payload_migrations",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"batch": {
"name": "batch",
"type": "numeric",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))"
}
},
"indexes": {
"payload_migrations_updated_at_idx": {
"name": "payload_migrations_updated_at_idx",
"columns": ["updated_at"],
"isUnique": false
},
"payload_migrations_created_at_idx": {
"name": "payload_migrations_created_at_idx",
"columns": ["created_at"],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
},
"id": "892a870d-47bc-44f3-8237-e7d4c7022b26",
"prevId": "00000000-0000-0000-0000-000000000000"
}

View File

@@ -0,0 +1,157 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-d1-sqlite'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.run(sql`CREATE TABLE \`users_sessions\` (
\`_order\` integer NOT NULL,
\`_parent_id\` integer NOT NULL,
\`id\` text PRIMARY KEY NOT NULL,
\`created_at\` text,
\`expires_at\` text NOT NULL,
FOREIGN KEY (\`_parent_id\`) REFERENCES \`users\`(\`id\`) ON UPDATE no action ON DELETE cascade
);
`)
await db.run(sql`CREATE INDEX \`users_sessions_order_idx\` ON \`users_sessions\` (\`_order\`);`)
await db.run(
sql`CREATE INDEX \`users_sessions_parent_id_idx\` ON \`users_sessions\` (\`_parent_id\`);`,
)
await db.run(sql`CREATE TABLE \`users\` (
\`id\` integer PRIMARY KEY NOT NULL,
\`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL,
\`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL,
\`email\` text NOT NULL,
\`reset_password_token\` text,
\`reset_password_expiration\` text,
\`salt\` text,
\`hash\` text,
\`login_attempts\` numeric DEFAULT 0,
\`lock_until\` text
);
`)
await db.run(sql`CREATE INDEX \`users_updated_at_idx\` ON \`users\` (\`updated_at\`);`)
await db.run(sql`CREATE INDEX \`users_created_at_idx\` ON \`users\` (\`created_at\`);`)
await db.run(sql`CREATE UNIQUE INDEX \`users_email_idx\` ON \`users\` (\`email\`);`)
await db.run(sql`CREATE TABLE \`media\` (
\`id\` integer PRIMARY KEY NOT NULL,
\`alt\` text NOT NULL,
\`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL,
\`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL,
\`url\` text,
\`thumbnail_u_r_l\` text,
\`filename\` text,
\`mime_type\` text,
\`filesize\` numeric,
\`width\` numeric,
\`height\` numeric
);
`)
await db.run(sql`CREATE INDEX \`media_updated_at_idx\` ON \`media\` (\`updated_at\`);`)
await db.run(sql`CREATE INDEX \`media_created_at_idx\` ON \`media\` (\`created_at\`);`)
await db.run(sql`CREATE UNIQUE INDEX \`media_filename_idx\` ON \`media\` (\`filename\`);`)
await db.run(sql`CREATE TABLE \`payload_locked_documents\` (
\`id\` integer PRIMARY KEY NOT NULL,
\`global_slug\` text,
\`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL,
\`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL
);
`)
await db.run(
sql`CREATE INDEX \`payload_locked_documents_global_slug_idx\` ON \`payload_locked_documents\` (\`global_slug\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_locked_documents_updated_at_idx\` ON \`payload_locked_documents\` (\`updated_at\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_locked_documents_created_at_idx\` ON \`payload_locked_documents\` (\`created_at\`);`,
)
await db.run(sql`CREATE TABLE \`payload_locked_documents_rels\` (
\`id\` integer PRIMARY KEY NOT NULL,
\`order\` integer,
\`parent_id\` integer NOT NULL,
\`path\` text NOT NULL,
\`users_id\` integer,
\`media_id\` integer,
FOREIGN KEY (\`parent_id\`) REFERENCES \`payload_locked_documents\`(\`id\`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (\`users_id\`) REFERENCES \`users\`(\`id\`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (\`media_id\`) REFERENCES \`media\`(\`id\`) ON UPDATE no action ON DELETE cascade
);
`)
await db.run(
sql`CREATE INDEX \`payload_locked_documents_rels_order_idx\` ON \`payload_locked_documents_rels\` (\`order\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_locked_documents_rels_parent_idx\` ON \`payload_locked_documents_rels\` (\`parent_id\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_locked_documents_rels_path_idx\` ON \`payload_locked_documents_rels\` (\`path\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_locked_documents_rels_users_id_idx\` ON \`payload_locked_documents_rels\` (\`users_id\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_locked_documents_rels_media_id_idx\` ON \`payload_locked_documents_rels\` (\`media_id\`);`,
)
await db.run(sql`CREATE TABLE \`payload_preferences\` (
\`id\` integer PRIMARY KEY NOT NULL,
\`key\` text,
\`value\` text,
\`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL,
\`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL
);
`)
await db.run(
sql`CREATE INDEX \`payload_preferences_key_idx\` ON \`payload_preferences\` (\`key\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_preferences_updated_at_idx\` ON \`payload_preferences\` (\`updated_at\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_preferences_created_at_idx\` ON \`payload_preferences\` (\`created_at\`);`,
)
await db.run(sql`CREATE TABLE \`payload_preferences_rels\` (
\`id\` integer PRIMARY KEY NOT NULL,
\`order\` integer,
\`parent_id\` integer NOT NULL,
\`path\` text NOT NULL,
\`users_id\` integer,
FOREIGN KEY (\`parent_id\`) REFERENCES \`payload_preferences\`(\`id\`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (\`users_id\`) REFERENCES \`users\`(\`id\`) ON UPDATE no action ON DELETE cascade
);
`)
await db.run(
sql`CREATE INDEX \`payload_preferences_rels_order_idx\` ON \`payload_preferences_rels\` (\`order\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_preferences_rels_parent_idx\` ON \`payload_preferences_rels\` (\`parent_id\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_preferences_rels_path_idx\` ON \`payload_preferences_rels\` (\`path\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_preferences_rels_users_id_idx\` ON \`payload_preferences_rels\` (\`users_id\`);`,
)
await db.run(sql`CREATE TABLE \`payload_migrations\` (
\`id\` integer PRIMARY KEY NOT NULL,
\`name\` text,
\`batch\` numeric,
\`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL,
\`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL
);
`)
await db.run(
sql`CREATE INDEX \`payload_migrations_updated_at_idx\` ON \`payload_migrations\` (\`updated_at\`);`,
)
await db.run(
sql`CREATE INDEX \`payload_migrations_created_at_idx\` ON \`payload_migrations\` (\`created_at\`);`,
)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.run(sql`DROP TABLE \`users_sessions\`;`)
await db.run(sql`DROP TABLE \`users\`;`)
await db.run(sql`DROP TABLE \`media\`;`)
await db.run(sql`DROP TABLE \`payload_locked_documents\`;`)
await db.run(sql`DROP TABLE \`payload_locked_documents_rels\`;`)
await db.run(sql`DROP TABLE \`payload_preferences\`;`)
await db.run(sql`DROP TABLE \`payload_preferences_rels\`;`)
await db.run(sql`DROP TABLE \`payload_migrations\`;`)
}

View File

@@ -0,0 +1,9 @@
import * as migration_20250929_111647 from './20250929_111647'
export const migrations = [
{
up: migration_20250929_111647.up,
down: migration_20250929_111647.down,
name: '20250929_111647',
},
]

View File

@@ -0,0 +1,299 @@
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
/**
* Supported timezones in IANA format.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "supportedTimezones".
*/
export type SupportedTimezones =
| 'Pacific/Midway'
| 'Pacific/Niue'
| 'Pacific/Honolulu'
| 'Pacific/Rarotonga'
| 'America/Anchorage'
| 'Pacific/Gambier'
| 'America/Los_Angeles'
| 'America/Tijuana'
| 'America/Denver'
| 'America/Phoenix'
| 'America/Chicago'
| 'America/Guatemala'
| 'America/New_York'
| 'America/Bogota'
| 'America/Caracas'
| 'America/Santiago'
| 'America/Buenos_Aires'
| 'America/Sao_Paulo'
| 'Atlantic/South_Georgia'
| 'Atlantic/Azores'
| 'Atlantic/Cape_Verde'
| 'Europe/London'
| 'Europe/Berlin'
| 'Africa/Lagos'
| 'Europe/Athens'
| 'Africa/Cairo'
| 'Europe/Moscow'
| 'Asia/Riyadh'
| 'Asia/Dubai'
| 'Asia/Baku'
| 'Asia/Karachi'
| 'Asia/Tashkent'
| 'Asia/Calcutta'
| 'Asia/Dhaka'
| 'Asia/Almaty'
| 'Asia/Jakarta'
| 'Asia/Bangkok'
| 'Asia/Shanghai'
| 'Asia/Singapore'
| 'Asia/Tokyo'
| 'Asia/Seoul'
| 'Australia/Brisbane'
| 'Australia/Sydney'
| 'Pacific/Guam'
| 'Pacific/Noumea'
| 'Pacific/Auckland'
| 'Pacific/Fiji';
export interface Config {
auth: {
users: UserAuthOperations;
};
blocks: {};
collections: {
users: User;
media: Media;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsSelect: {
users: UsersSelect<false> | UsersSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: number;
};
globals: {};
globalsSelect: {};
locale: null;
user: User & {
collection: 'users';
};
jobs: {
tasks: unknown;
workflows: unknown;
};
}
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
password: string;
};
registerFirstUser: {
email: string;
password: string;
};
unlock: {
email: string;
password: string;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: number;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
sessions?:
| {
id: string;
createdAt?: string | null;
expiresAt: string;
}[]
| null;
password?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: number;
alt: string;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
focalX?: number | null;
focalY?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: number;
document?:
| ({
relationTo: 'users';
value: number | User;
} | null)
| ({
relationTo: 'media';
value: number | Media;
} | null);
globalSlug?: string | null;
user: {
relationTo: 'users';
value: number | User;
};
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".
*/
export interface PayloadPreference {
id: number;
user: {
relationTo: 'users';
value: number | User;
};
key?: string | null;
value?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations".
*/
export interface PayloadMigration {
id: number;
name?: string | null;
batch?: number | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select".
*/
export interface UsersSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
email?: T;
resetPasswordToken?: T;
resetPasswordExpiration?: T;
salt?: T;
hash?: T;
loginAttempts?: T;
lockUntil?: T;
sessions?:
| T
| {
id?: T;
createdAt?: T;
expiresAt?: T;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media_select".
*/
export interface MediaSelect<T extends boolean = true> {
alt?: T;
updatedAt?: T;
createdAt?: T;
url?: T;
thumbnailURL?: T;
filename?: T;
mimeType?: T;
filesize?: T;
width?: T;
height?: T;
focalX?: T;
focalY?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents_select".
*/
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
document?: T;
globalSlug?: T;
user?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences_select".
*/
export interface PayloadPreferencesSelect<T extends boolean = true> {
user?: T;
key?: T;
value?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations_select".
*/
export interface PayloadMigrationsSelect<T extends boolean = true> {
name?: T;
batch?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".
*/
export interface Auth {
[k: string]: unknown;
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}

View File

@@ -0,0 +1,53 @@
// storage-adapter-import-placeholder
import { sqliteD1Adapter } from '@payloadcms/db-d1-sqlite'
import { payloadCloudPlugin } from '@payloadcms/payload-cloud'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import { buildConfig } from 'payload'
import { fileURLToPath } from 'url'
import { CloudflareContext, getCloudflareContext } from '@opennextjs/cloudflare'
import { GetPlatformProxyOptions } from 'wrangler'
import { r2Storage } from '@payloadcms/storage-r2'
import { Users } from './collections/Users'
import { Media } from './collections/Media'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const cloudflare = process.argv.find((value) => value.match(/^(generate|migrate):?/))
? await getCloudflareContextFromWrangler()
: await getCloudflareContext({ async: true })
export default buildConfig({
admin: {
user: Users.slug,
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [Users, Media],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
db: sqliteD1Adapter({ binding: cloudflare.env.D1 }),
plugins: [
payloadCloudPlugin(),
r2Storage({
bucket: cloudflare.env.R2,
collections: { media: true },
}),
],
})
// Adapted from https://github.com/opennextjs/opennextjs-cloudflare/blob/d00b3a13e42e65aad76fba41774815726422cc39/packages/cloudflare/src/api/cloudflare-context.ts#L328C36-L328C46
function getCloudflareContextFromWrangler(): Promise<CloudflareContext> {
return import(`${'__wrangler'.replaceAll('_', '')}`).then(({ getPlatformProxy }) =>
getPlatformProxy({
environment: process.env.CLOUDFLARE_ENV,
experimental: { remoteBindings: process.env.NODE_ENV === 'production' },
} satisfies GetPlatformProxyOptions),
)
}

View File

@@ -0,0 +1 @@
NODE_OPTIONS="--no-deprecation --no-experimental-strip-types"

View File

@@ -0,0 +1,20 @@
import { test, expect, Page } from '@playwright/test'
test.describe('Frontend', () => {
let page: Page
test.beforeAll(async ({ browser }, testInfo) => {
const context = await browser.newContext()
page = await context.newPage()
})
test('can go on homepage', async ({ page }) => {
await page.goto('http://localhost:3000')
await expect(page).toHaveTitle(/Payload Blank Template/)
const headging = page.locator('h1').first()
await expect(headging).toHaveText('Welcome to your new project.')
})
})

View File

@@ -0,0 +1,20 @@
import { getPayload, Payload } from 'payload'
import config from '@/payload.config'
import { describe, it, beforeAll, expect } from 'vitest'
let payload: Payload
describe('API', () => {
beforeAll(async () => {
const payloadConfig = await config
payload = await getPayload({ config: payloadConfig })
})
it('fetches users', async () => {
const users = await payload.find({
collection: 'users',
})
expect(users).toBeDefined()
})
})

View File

@@ -0,0 +1,45 @@
{
"compilerOptions": {
"baseUrl": ".",
"lib": [
"DOM",
"DOM.Iterable",
"ES2022"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"strictNullChecks": false,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./src/*"
],
"@payload-config": [
"./src/payload.config.ts"
]
},
"target": "ES2022",
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
],
}

View File

@@ -0,0 +1,12 @@
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [tsconfigPaths(), react()],
test: {
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],
include: ['tests/int/**/*.int.spec.ts'],
},
})

View File

@@ -0,0 +1,4 @@
// Any setup scripts you might need go here
// Load .env files
import 'dotenv/config'

View File

@@ -0,0 +1,80 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"main": ".open-next/worker.js",
"name": "my-app",
"compatibility_date": "2025-05-05",
"compatibility_flags": [
// Enable Node.js API
// see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag
"nodejs_compat",
// Allow to fetch URLs in your app
// see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#global-fetch-strictly-public
"global_fetch_strictly_public",
// Enable MessagePort, used by undici
"expose_global_message_channel",
],
"assets": {
"directory": ".open-next/assets",
"binding": "ASSETS",
},
"d1_databases": [
{
"binding": "D1",
"database_id": "DATABASE_ID",
"database_name": "my-app",
"remote": true,
},
],
"services": [
// The service should match the "name" of your worker
// {
// "binding": "WORKER_SELF_REFERENCE",
// "service": "my-app"
// },
],
"r2_buckets": [
{
"binding": "R2",
"bucket_name": "my-app",
},
// Create a R2 binding with the binding name "NEXT_INC_CACHE_R2_BUCKET"
// {
// "binding": "NEXT_INC_CACHE_R2_BUCKET",
// "bucket_name": "<BUCKET_NAME>",
// },
],
// Here's how to configure an additional environment
// It can be deployed with `CLOUDFLARE_ENV=staging npm run deploy`
// "env": {
// "staging": {
// "name": "my-app-staging",
// "d1_databases": [
// {
// "binding": "D1",
// "database_id": "DATABASE_ID",
// "database_name": "my-app-staging",
// "experimental_remote": true
// }
// ],
// "services": [
// // The service should match the "name" of your worker
// // {
// // "binding": "WORKER_SELF_REFERENCE",
// // "service": "my-app-staging"
// // },
// ],
// "r2_buckets": [
// {
// "binding": "R2",
// "bucket_name": "my-app-staging"
// }
// // Create a R2 binding with the binding name "NEXT_INC_CACHE_R2_BUCKET"
// // {
// // "binding": "NEXT_INC_CACHE_R2_BUCKET",
// // "bucket_name": "<BUCKET_NAME>",
// // },
// ]
// }
// }
}

View File

@@ -113,6 +113,11 @@ export const allDatabaseAdapters = {
process.env.POSTGRES_URL || 'postgresql://postgres:postgres@127.0.0.1:54322/postgres',
},
})`,
d1: `
import { sqliteD1Adapter } from '@payloadcms/db-d1-sqlite'
export const databaseAdapter = sqliteD1Adapter({ binding: global.d1 })
`,
}
/**

Some files were not shown because too many files have changed in this diff Show More