Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/six-moose-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kivotos/core": minor
---

[[DRIZZ-69] Plugin system](https://app.plane.so/softnetics/browse/DRIZZ-69/)
6 changes: 5 additions & 1 deletion examples/erp/drizzlify/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { usersCollection } from './collections/users'
import { baseConfig, builder } from './helper'

const baseServerConfig = defineServerConfig(baseConfig, {
collections: [usersCollection, postsCollection, categoriesCollection],
collections: {
users: usersCollection,
posts: postsCollection,
categories: categoriesCollection,
},
endpoints: {
customOne: builder.endpoint(
{
Expand Down
5 changes: 0 additions & 5 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"test": "vitest run"
},
"dependencies": {
"better-auth": "^1.2.7",
"cookie-es": "^2.0.0",
"drizzle-orm": "^0.41.0",
"remeda": "^2.21.2",
Expand All @@ -26,10 +25,6 @@
},
"devDependencies": {
"@types/json-schema": "^7.0.15",
"clsx": "^2.1.1",
"tailwind-merge": "^3.0.2",
"tailwind-variants": "^1.0.0",
"tw-animate-css": "^1.2.4",
"type-fest": "^4.38.0",
"vitest": "^3.0.9"
}
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/auth/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,3 @@ export function createAuthHandlers<TAuthConfig extends AuthConfig>(config: TAuth
handlers,
}
}

export type AuthHandlers = ReturnType<typeof createAuthHandlers>['handlers']
13 changes: 3 additions & 10 deletions packages/core/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { AnyColumn, AnyTable } from 'drizzle-orm'
import * as R from 'remeda'
import type { Simplify } from 'type-fest'

Expand All @@ -8,15 +7,7 @@ import { createAuthHandlers } from './handlers'
import { getFieldsClient, type MinimalContext } from '../config'
import type { ApiRouteHandler } from '../endpoint'
import type { Fields, FieldsClient } from '../field'

export type AnyTypedColumn<T> = AnyColumn & { _: { data: T; dialect: 'pg' } }
export type WithHasDefault<T> = T & { _: { hasDefault: true } }
export type WithNotNull<T> = T & { _: { notNull: true } }
export type WithAnyTable<TColumns extends Record<string, AnyColumn>> = AnyTable<{
dialect: 'pg'
columns: TColumns
}> &
TColumns
import type { AnyTypedColumn, WithAnyTable, WithHasDefault, WithNotNull } from '../table'

export type AnyUserTable = WithAnyTable<{
id: WithHasDefault<WithNotNull<AnyTypedColumn<string>>>
Expand Down Expand Up @@ -134,6 +125,8 @@ export type Auth<
>
}

export type AuthHandlers = Auth<any, any>['handlers']

export type AuthClient = {
login: {
emailAndPassword: {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class Builder<
TSlug extends string = string,
TTableKey extends GetAllTableTsNames<TFullSchema> = GetAllTableTsNames<TFullSchema>,
TFields extends Fields<TContext> = Fields<TContext>,
TApiRouter extends ApiRouter<TContext> = ApiRouter<TContext>,
TApiRouter extends ApiRouter<TContext> = {},
>(tableTsName: TTableKey, config: CollectionConfig<TSlug, TContext, TFields, TApiRouter>) {
const table = this.config.schema[tableTsName]
const tableRelationalConfig =
Expand Down
92 changes: 47 additions & 45 deletions packages/core/src/collection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Many, Table, TableRelationalConfig } from 'drizzle-orm'
import type { ConditionalExcept, Simplify } from 'type-fest'
import type { ConditionalExcept, Simplify, UnionToIntersection, ValueOf } from 'type-fest'
import z from 'zod'

import type { MinimalContext } from './config'
Expand All @@ -23,7 +23,7 @@ import {
fieldsToZodObject,
type FieldsWithFieldName,
} from './field'
import type { JoinArrays, ToZodObject } from './utils'
import type { ToZodObject } from './utils'

type SimplifyConditionalExcept<Base, Condition> = Simplify<ConditionalExcept<Base, Condition>>

Expand Down Expand Up @@ -421,7 +421,7 @@ export type CollectionConfig<
TSlug extends string = string,
TContext extends MinimalContext = MinimalContext,
TFields extends FieldsInitial<TContext> = FieldsInitial<TContext>,
TAppRouter extends ApiRouter<TContext> = ApiRouter<TContext>,
TAppRouter extends ApiRouter<TContext> = {},
> = {
slug: TSlug
primaryField: Extract<keyof TFields, string>
Expand All @@ -435,7 +435,7 @@ export type Collection<
TFullSchema extends Record<string, unknown> = Record<string, unknown>,
TContext extends MinimalContext = MinimalContext,
TFields extends Fields<TContext> = Fields<TContext>,
TApiRouter extends ApiRouter<TContext> = ApiRouter<TContext>,
TApiRouter extends ApiRouter<TContext> = {},
> = {
_: {
table: TFullSchema[TTableName] extends Table<any> ? TFullSchema[TTableName] : never
Expand All @@ -458,13 +458,12 @@ export type ToClientCollection<TCollection extends Collection<any, any, any, any
>

export type ToClientCollectionList<
TCollections extends Collection<any, any, any, any, any, any>[],
> = TCollections extends [infer TCollection, ...infer TCollectionsRest]
? [
ToClientCollection<TCollection extends Collection ? TCollection : never>,
...ToClientCollectionList<TCollectionsRest extends Collection[] ? TCollectionsRest : never>,
]
: []
TCollections extends Record<string, Collection<any, any, any, any, any, any>>,
> = {
[TKey in keyof TCollections]: TCollections[TKey] extends Collection<any, any, any, any, any, any>
? ToClientCollection<TCollections[TKey]>
: never
}

// TODO: proper omit
export type ClientCollection<
Expand Down Expand Up @@ -521,30 +520,31 @@ export type GetAllTableTsNames<TFullSchema extends Record<string, unknown>> = Ex
export type ExtractCollectionCustomEndpoints<
TCollection extends Collection<any, any, any, any, any, any>,
> = TCollection['admin']['endpoints'] extends infer TEndpoints
? TEndpoints extends Record<string, ApiRouteSchema>
? TEndpoints extends Record<string, ApiRoute<any, any>>
? {
[TEndpoint in Extract<
keyof TEndpoints,
string
> as `${TCollection['slug']}.${TEndpoint}`]: TEndpoints[TEndpoint]
[TEndpoint in keyof TEndpoints as TEndpoints[TEndpoint]['schema'] extends ApiRouteSchema
? `${TCollection['slug']}.${TEndpoint extends string ? TEndpoint : never}`
: never]: TEndpoints[TEndpoint]
}
: never
: never

export type ExtractAllCollectionCustomEndpoints<
TCollections extends Collection<any, any, any, any, any, any>[],
> = JoinArrays<{
[TCollectionIndex in keyof TCollections]: TCollections[TCollectionIndex] extends Collection<
any,
any,
any,
any,
any,
any
>
? ExtractCollectionCustomEndpoints<TCollections[TCollectionIndex]>
: {}
}>
TCollections extends Record<string, Collection<any, any, any, any, any, any>>,
> = UnionToIntersection<
ValueOf<{
[TCollectionIndex in keyof TCollections]: TCollections[TCollectionIndex] extends Collection<
any,
any,
any,
any,
any,
any
>
? ExtractCollectionCustomEndpoints<TCollections[TCollectionIndex]>
: {}
}>
>

type SuccessResponse<TFunc extends (...args: any) => any> = ToZodObject<Awaited<ReturnType<TFunc>>>

Expand Down Expand Up @@ -619,25 +619,27 @@ export type ExtractCollectionDefaultEndpoints<
}

export type ExtractAllCollectionDefaultEndpoints<
TCollections extends Collection<any, any, any, any, any, any>[],
> = JoinArrays<{
[TCollectionIndex in keyof TCollections]: TCollections[TCollectionIndex] extends Collection<
any,
any,
any,
any,
any,
any
>
? ExtractCollectionDefaultEndpoints<TCollections[TCollectionIndex]>
: {}
}>
TCollections extends Record<string, Collection<any, any, any, any, any, any>>,
> = UnionToIntersection<
ValueOf<{
[TCollectionIndex in keyof TCollections]: TCollections[TCollectionIndex] extends Collection<
any,
any,
any,
any,
any,
any
>
? ExtractCollectionDefaultEndpoints<TCollections[TCollectionIndex]>
: {}
}>
>

export function getAllCollectionEndpoints<
TCollections extends Collection<any, any, any, any, any, ApiRouter<any>>[],
TCollections extends Record<string, Collection<any, any, any, any, any, any>>,
>(collections: TCollections) {
const customEndpoints = Object.fromEntries(
collections.flatMap((collection) => {
Object.values(collections).flatMap((collection) => {
const endpoints = collection.admin.endpoints
if (endpoints) {
return Object.entries(endpoints).map(([key, value]) => {
Expand All @@ -649,7 +651,7 @@ export function getAllCollectionEndpoints<
)

const defaultEndpoints = Object.fromEntries(
collections.flatMap((collection) => {
Object.values(collections).flatMap((collection) => {
return Object.entries(collection.admin.api).map(([method, fn]) => {
const endpointName = `${collection.slug}.${method}`
const fields = collection.fields
Expand Down
62 changes: 36 additions & 26 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import type { NodePgDatabase } from 'drizzle-orm/node-postgres'
import * as R from 'remeda'
import type { Simplify } from 'type-fest'

import { type AuthClient, type AuthConfig, createAuth, getAuthClient } from './auth'
import type { AuthHandlers } from './auth/handlers'
import {
type AuthClient,
type AuthConfig,
type AuthHandlers,
createAuth,
getAuthClient,
} from './auth'
import {
type ClientCollection,
type Collection,
Expand All @@ -22,6 +27,7 @@ import {
type ToRecordApiRouteSchema,
} from './endpoint'
import type { Field, FieldClient, Fields, FieldsClient } from './field'
import type { KivotosPlugin, MergePlugins } from './plugins'
import { isRelationField } from './utils'

export type MinimalContext<
Expand Down Expand Up @@ -53,14 +59,10 @@ export interface BaseConfig<
export interface ServerConfig<
TFullSchema extends Record<string, unknown> = Record<string, unknown>,
TContext extends MinimalContext<TFullSchema> = MinimalContext<TFullSchema>,
TCollections extends Collection<any, any, any, any, any, any>[] = Collection<
any,
any,
any,
any,
any,
any
>[],
TCollections extends Record<string, Collection<any, any, any, any, any, any>> = Record<
string,
Collection<any, any, any, any, any, any>
>,
TApiRouter extends ApiRouter<TContext> = AuthHandlers & ApiRouter<any>,
> extends BaseConfig<TFullSchema> {
context: TContext
Expand Down Expand Up @@ -95,23 +97,20 @@ export function defineBaseConfig<
export function defineServerConfig<
TFullSchema extends Record<string, unknown> = Record<string, unknown>,
TContext extends MinimalContext<TFullSchema> = MinimalContext<TFullSchema>,
const TCollections extends Collection<any, any, any, any, any, any>[] = Collection<
any,
any,
any,
any,
any,
any
>[],
const TCollections extends Record<string, Collection<any, any, any, any, any, any>> = Record<
string,
Collection<any, any, any, any, any, any>
>,
const TEndpoints extends ApiRouter<MinimalContext<TFullSchema, TContext>> = {},
const TPlugins extends KivotosPlugin<any>[] = [],
>(
baseConfig: BaseConfig<TFullSchema, TContext>,
config: { collections: TCollections; endpoints?: TEndpoints }
config: { collections: TCollections; endpoints?: TEndpoints; plugins?: TPlugins }
) {
const auth = createAuth(baseConfig.auth, baseConfig.context)
const collectionEndpoints = getAllCollectionEndpoints(config.collections)

return {
let serverConfig = {
...baseConfig,
collections: config.collections,
endpoints: {
Expand All @@ -131,10 +130,19 @@ export function defineServerConfig<
ExtractAllCollectionCustomEndpoints<TCollections> &
ExtractAllCollectionDefaultEndpoints<TCollections>
>

for (const plugin of config.plugins ?? []) {
serverConfig = plugin(serverConfig)
}

return serverConfig as MergePlugins<typeof serverConfig, TPlugins>
}

export interface ClientConfig<
TCollections extends ClientCollection[] = ClientCollection[],
TCollections extends Record<string, ClientCollection<any, any, any, any, any, any>> = Record<
string,
ClientCollection<any, any, any, any, any, any>
>,
TApiRouter extends ClientApiRouter = ClientApiRouter,
> {
auth: AuthClient
Expand Down Expand Up @@ -184,17 +192,17 @@ export function getFieldsClient(fields: Fields<any>): FieldsClient {
return R.mapValues(fields, (value, key) => getFieldClient(key, value))
}

export function getClientCollection<const TCollection extends Collection>(
collection: TCollection
): ToClientCollection<TCollection> {
export function getClientCollection<
const TCollection extends Collection<any, any, any, any, any, any>,
>(collection: TCollection): ToClientCollection<TCollection> {
return R.pipe(collection, R.omit(['_', 'admin']), (collection) => ({
...collection,
fields: getFieldsClient(collection.fields),
})) as unknown as ToClientCollection<TCollection>
}

export function getClientConfig<
TCollections extends Collection<any, any, any, any, any, any>[],
TCollections extends Record<string, Collection<any, any, any, any, any, any>>,
TApiRouter extends ApiRouter<any>,
>(
serverConfig: ServerConfig<any, any, TCollections, TApiRouter>
Expand All @@ -209,7 +217,9 @@ export function getClientConfig<

return {
auth: getAuthClient(serverConfig.auth),
collections: collections.map(getClientCollection) as ToClientCollectionList<TCollections>,
collections: R.mapValues(collections, (s) =>
getClientCollection(s as Collection<any, any, any, any, any, any>)
) as ToClientCollectionList<TCollections>,
endpoints: clientEndpoints,
$types: undefined as any,
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ export type ToClientApiRouteSchema<TApiRouter extends ApiRouter<any>> = {
}

export function createEndpoint<
TApiEndpointSchema extends ApiRouteSchema,
TContext extends Record<string, unknown> = Record<string, unknown>,
const TApiEndpointSchema extends ApiRouteSchema,
const TContext extends Record<string, unknown> = Record<string, unknown>,
>(schema: TApiEndpointSchema, handler: ApiRouteHandler<TContext, TApiEndpointSchema>) {
return {
schema,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { AuthHandlers } from './auth/handlers'
export type { AuthHandlers } from './auth'
export { Builder } from './builder'
export type {
ApiReturnType,
Expand Down
Loading
Loading