@hono-crud/drizzle 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,69 @@
1
1
  # @hono-crud/drizzle
2
2
 
3
+ ## 0.1.13
4
+
5
+ ### Patch Changes
6
+
7
+ - 4ba4a85: Owner-scope relation includes (`?include=`) — security fix for cross-tenant exposure.
8
+
9
+ Previously, loading a relation via `?include=` fetched the related rows by foreign key alone, ignoring the **related** model's access scope. A caller who could read a parent row could therefore read a related row in another tenant (or a soft-deleted one) through the include — a cross-tenant data leak.
10
+
11
+ Relations can now declare a `scope` naming the related table's owner and soft-delete columns:
12
+
13
+ ```ts
14
+ relations: {
15
+ post: {
16
+ type: 'belongsTo', model: 'posts', table: posts,
17
+ foreignKey: 'postId', localKey: 'id',
18
+ scope: { tenantField: 'authorId', softDeleteField: 'deletedAt' },
19
+ },
20
+ }
21
+ ```
22
+
23
+ When set, included related rows are filtered to the request's resolved tenant id and exclude soft-deleted rows (unless `?withDeleted=true`), so a foreign key pointing at another tenant's row resolves to `null` (belongsTo/hasOne) or is omitted (hasMany). The filtering lives in the core orchestrator (`batchLoadRelations` / `loadRelationsForItem`), so it applies identically across the drizzle, memory, and prisma adapters; the endpoint threads the parent request's tenant id + `withDeleted` into `IncludeOptions.scope` (Read via core; List via each adapter).
24
+
25
+ New public types: `RelationScopeConfig`, `RelationRequestScope`; new fields `RelationConfig.scope` and `IncludeOptions.scope`; new protected `getRelationScope()` on the endpoint base class.
26
+
27
+ Backward-compatible and opt-in: relations without `scope` (or requests that resolve no tenant) behave exactly as before. Declare `scope` on any relation whose related model is access-scoped to close the leak.
28
+
29
+ Note: scoping is applied as a post-fetch filter in the orchestrator (related rows are fetched, then filtered before being mapped back onto the parent — they never reach the response), not yet pushed down to the adapter `WHERE`/`where`. Correct and leak-free; pushing the predicate into the query is a performance follow-up.
30
+
31
+ ## 0.1.12
32
+
33
+ ### Patch Changes
34
+
35
+ - 376d8d8: Adapter correctness batch:
36
+
37
+ - **Prisma soft-delete**: `read` and `update` now exclude soft-deleted rows (previously deleted data stayed readable and updatable on Prisma only), and `PrismaUpdateEndpoint` implements `findExisting` — restoring write policies, ETag If-Match, versioning, and audit prior-state capture that silently no-oped.
38
+ - **Prisma wiring parity**: new `getPrismaClient` resolver (`_tx` → class field → `CONTEXT_KEYS.prismaClient` context slot) and `createPrismaCrud(prisma, meta)` factory mirroring the Drizzle adapter; the `prisma` class field is now optional.
39
+ - **Upsert match-and-restore**: upsert/import/batchUpsert now match soft-deleted rows and clear the soft-delete field on update, identically across all three adapters (previously single upsert skipped soft-deleted rows — unique-constraint errors on SQL — while batch upsert overwrote them without restoring). Drizzle native ON CONFLICT paths document their divergence.
40
+ - **BatchDelete** responses go through the full finalize pipeline (computed fields, serialization profile, transform) instead of serializer-only.
41
+ - **like/ilike contract**: one cross-adapter definition — literal substring needle (`%` stripped, `_` inert), `like` follows database collation case behavior, `ilike` always case-insensitive. Drizzle no longer passes live user wildcards into SQL LIKE and no longer emits PostgreSQL-only `ILIKE` on sqlite/mysql.
42
+ - **Workers waitUntil**: logging flush, cache invalidation (`withCacheInvalidation`), api-key `updateLastUsed`, and error-handler hooks now register through `waitUntil` so they survive the response on Cloudflare Workers. `emitAsync` removed (zero call sites). One exported `WaitUntil` type (`WaitUntilFn` removed).
43
+ - **Prisma LIKE wildcard leak**: Prisma's `contains` compiles to SQL LIKE without escaping, so user-supplied `_` acted as a live single-char wildcard in like/ilike filters and search (verified against Postgres). Needles are now escaped via `escapeLikeWildcards`.
44
+ - **Prisma `useTransaction` honored**: Create/Update/Delete now wrap `handle()` in `$transaction` when `useTransaction = true` (mirroring Drizzle) — previously the flag existed but did nothing on single-record verbs, so hooks saw no transaction and an after-hook throw did not roll back the write.
45
+
46
+ - fd3895f: Cursor pagination is now real on all three adapters. Previously core advertised cursor query params and `next_cursor`/`prev_cursor` response fields, but only the memory adapter implemented them — Drizzle and Prisma silently fell back to offset pagination.
47
+
48
+ - **Drizzle**: keyset via `WHERE cursorField > decoded ORDER BY cursorField LIMIT n+1`; **Prisma**: native `cursor` + `skip: 1` + `take: n+1`. All three adapters build the cursor-mode `result_info` envelope through one shared core helper, so the shape is byte-identical.
49
+ - **`prev_cursor` removed** (breaking): cursor walks are next-only (Stripe-style) — SQL keyset "previous" requires a reversed query and was only ever implemented in memory.
50
+ - **`order_by` is forced to the cursor field during cursor walks** (documented on the query param) — previously the three adapters could diverge on sort semantics mid-walk.
51
+ - **No silent degradation**: cursor query params and `next_cursor` only appear in the OpenAPI schema when the endpoint enables cursor pagination AND the adapter supports it; enabling it on an unsupporting adapter throws `ConfigurationException` instead of quietly serving offset pages.
52
+ - List-query logic deduplicated per adapter (`executeDrizzleListQuery` / memory store query helper, mirroring Prisma's existing `executePrismaQuery`) and batch OpenAPI scaffolding shared by the three id-keyed batch verbs.
53
+
54
+ - bf12169: `hono-crud` is now a peerDependency (caret range) instead of an exact-pinned dependency. Previously, published packages pinned the exact core version (e.g. `0.13.13`), so an app on any other core version got two physical copies of `hono-crud` installed — silently corrupting satellite exception codes through `createErrorHandler` (e.g. `RATE_LIMIT_EXCEEDED` degraded to `HTTP_ERROR`, `details.retryAfter` dropped) and breaking `setLogger()` for all adapter logging. The resolver now dedupes onto the app's single copy. npm >= 7 and pnpm >= 8 auto-install peers, so installs are unchanged.
55
+ - 7a7808d: Verb & sugar-surface parity batch:
56
+
57
+ - **Config API verb parity**: `defineEndpoints` gains the 5 missing verbs — `bulkPatch`, `versionHistory`, `versionRead`, `versionCompare`, `versionRollback` — completing all 22 `registerCrud` slots. `AdapterBundle` gains the matching optional slots, and all three first-party bundles (Memory/Drizzle/Prisma) now fill every slot. `GeneratedEndpoints` is derived as a `Pick` over `CrudEndpoints`, so the two types can never drift again.
58
+ - **Loud config failure (BREAKING)**: configuring a verb whose adapter bundle lacks the matching base class now throws a plain `Error` at definition time instead of silently skipping the route. Correct configurations are unaffected.
59
+ - **BulkPatch on every adapter**: new `DrizzleBulkPatchEndpoint` (single `UPDATE ... WHERE` + RETURNING) and `PrismaBulkPatchEndpoint` (`updateMany`; count-only, `returnRecords` unsupported). `MemoryBulkPatchEndpoint` now respects soft-delete visibility (soft-deleted rows are never patched) and bumps managed `updatedAt` — previously it patched deleted rows and skipped the timestamp bump. Core `BulkPatchEndpoint` ships a default `getUpdateSchema()` (model schema minus managed fields, partial) so it works from the config API without subclassing. New conformance cell pins the contract on all three adapters.
60
+ - **OpenAPI schema overrides now actually merge (fix)**: user-supplied `responses`/`request`/`security`/`operationId` previously type-checked but were clobbered by the generated blocks on every surface. A shared `mergeRouteSchema` seam (exported from `hono-crud`) now merges user blocks over the generated schema in every endpoint `getSchema()`. Config `openapi` widened to `Partial<OpenAPIRouteSchema>` and the builder gains `.openapi(schema)`.
61
+ - **`searchFieldName` → `searchParamName` (BREAKING)**: the list-endpoint inline-search query-param knob is renamed everywhere (`ListEndpoint`, `ListFilterParseOptions`, functional `ListConfig`, `NormalizedEndpointConfig`); it names a query parameter, not a model field. Builder `.searchParam()` and config `search.paramName` spellings are unchanged. The default divergence is deliberate and now documented: inline list search defaults to `'search'`, the dedicated `/search` route to `'q'`.
62
+ - **Builder alias removal (BREAKING)**: pre-1.0 back-compat aliases `orderBy()` and `defaultOrder()` are deleted — `.sortable()` / `.defaultSort()` are the only spellings. The builder also no longer hardcodes its own copy of factory defaults (20/100/'search'/'id'/'sequential'/false); unset knobs pass `undefined` through so `generateEndpointClass` is the single source of defaults (behavior identical).
63
+ - **`bodySchema` parity**: functional `CreateConfig`/`UpdateConfig` gain `bodySchema` and the Create/Update builders gain `.bodySchema(schema)`, matching the class and config APIs. Fixed a factory bug where the generated `getBodySchema` override crashed body-schema-less verbs.
64
+ - **Honest hook typing**: the core create/update/delete trio across functional/builder/config now types the hook context as the exported `HookContext` (previously the actively-wrong `tx?: unknown`) and reuses the exported `AfterUpdateHook`/`AfterDeleteHook` aliases. Extended-verb config hook bags are retyped to what is actually passed (upsert gets `isCreate: boolean`, batch-create gets per-item `(item, index)`, batch-update gets `(id, data)`, import gets `(row, rowNumber, mode, tx?)`, clone's `before` gets the prepared clone payload; phantom `tx` params that were never passed are dropped). Previously-dead config hooks now fire: `search.hooks.after` is wired to `afterSearch`, `batchUpsert.hooks.before/after` to `beforeBatch`/`afterBatch`, and `AggregateEndpoint` gains an `after(result)` lifecycle hook so `aggregate.hooks.after` works.
65
+ - **Env-generic config middlewares**: `EndpointsConfig<M, E>` threads `E` so per-endpoint `middlewares` are `MiddlewareHandler<E>[]`, matching the functional and builder surfaces.
66
+
3
67
  ## 0.1.11
4
68
 
5
69
  ### Patch Changes
package/README.md CHANGED
@@ -11,20 +11,39 @@ npm install @hono-crud/drizzle hono-crud hono zod drizzle-orm drizzle-zod
11
11
  ## Usage
12
12
 
13
13
  ```ts
14
- import { fromHono, registerCrud, defineModel, defineMeta } from 'hono-crud';
15
14
  import {
16
15
  DrizzleCreateEndpoint,
17
- DrizzleReadEndpoint,
18
16
  DrizzleListEndpoint,
17
+ DrizzleReadEndpoint,
19
18
  type DrizzleDatabaseConstraint,
20
19
  } from '@hono-crud/drizzle';
20
+ import { pgTable, text } from 'drizzle-orm/pg-core';
21
+ import { Hono } from 'hono';
22
+ import { defineMeta, defineModel, fromHono, registerCrud } from 'hono-crud';
23
+ import { z } from 'zod';
21
24
 
22
- const app = fromHono(new Hono());
23
- registerCrud(app, '/users', {
24
- model,
25
- meta,
26
- endpoints: { create: DrizzleCreateEndpoint, read: DrizzleReadEndpoint, list: DrizzleListEndpoint },
25
+ declare const drizzleDb: DrizzleDatabaseConstraint; // your drizzle instance
26
+
27
+ const users = pgTable('users', {
28
+ id: text('id').primaryKey(),
29
+ name: text('name').notNull(),
27
30
  });
31
+
32
+ const UserSchema = z.object({ id: z.uuid(), name: z.string() });
33
+ const UserModel = defineModel({
34
+ tableName: 'users',
35
+ schema: UserSchema,
36
+ primaryKeys: ['id'],
37
+ table: users,
38
+ });
39
+ const userMeta = defineMeta({ model: UserModel });
40
+
41
+ class UserCreate extends DrizzleCreateEndpoint { _meta = userMeta; db = drizzleDb; }
42
+ class UserRead extends DrizzleReadEndpoint { _meta = userMeta; db = drizzleDb; }
43
+ class UserList extends DrizzleListEndpoint { _meta = userMeta; db = drizzleDb; }
44
+
45
+ const app = fromHono(new Hono());
46
+ registerCrud(app, '/users', { create: UserCreate, read: UserRead, list: UserList });
28
47
  ```
29
48
 
30
- Exports `DrizzleAdapters` (the 17-entry adapter bundle), the `Drizzle*Endpoint` classes, `createDrizzleCrud`, `createDrizzleSchemas`, and the `DrizzleDatabaseConstraint` type.
49
+ Exports `DrizzleAdapters` (the 22-entry adapter bundle), the `Drizzle*Endpoint` classes, `createDrizzleCrud`, `createDrizzleSchemas`, and the `DrizzleDatabaseConstraint` type.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MetaInput, IncludeOptions, FilterCondition, RelationConfig, CreateEndpoint, ModelObject, ReadEndpoint, UpdateEndpoint, NestedUpdateInput, NestedWriteResult, DeleteEndpoint, ListEndpoint, ListFilters, PaginatedResult, RestoreEndpoint, BatchCreateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, BatchUpdateEndpoint, BatchUpdateItem, AggregateEndpoint, AggregateOptions, AggregateResult, BatchUpsertEndpoint, CloneEndpoint, UpsertEndpoint, SearchEndpoint, SearchOptions, SearchResult, ExportEndpoint, ImportEndpoint, VersionCompareEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionRollbackEndpoint, AdapterBundle } from 'hono-crud/internal';
1
+ import { MetaInput, IncludeOptions, FilterCondition, RelationConfig, CreateEndpoint, ModelObject, ReadEndpoint, UpdateEndpoint, NestedUpdateInput, NestedWriteResult, DeleteEndpoint, ListEndpoint, ListFilters, PaginatedResult, RestoreEndpoint, BatchCreateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, BatchUpdateEndpoint, BatchUpdateItem, AggregateEndpoint, AggregateOptions, AggregateResult, BatchUpsertEndpoint, BulkPatchEndpoint, CloneEndpoint, UpsertEndpoint, SearchEndpoint, SearchOptions, SearchResult, ExportEndpoint, ImportEndpoint, VersionCompareEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionRollbackEndpoint, AdapterBundle } from 'hono-crud/internal';
2
2
  import { Env } from 'hono';
3
3
  import { z } from 'zod';
4
4
 
@@ -178,8 +178,11 @@ declare function loadDrizzleRelations<T extends Record<string, unknown>, M exten
178
178
  declare function batchLoadDrizzleRelations<T extends Record<string, unknown>, M extends MetaInput>(db: DrizzleDatabaseConstraint, items: T[], meta: M, includeOptions?: IncludeOptions): Promise<T[]>;
179
179
  /**
180
180
  * Builds a where condition from filter conditions.
181
+ *
182
+ * `dialect` drives the `like`/`ilike` substring matching (see
183
+ * {@link substringMatch}); all other operators are dialect-agnostic.
181
184
  */
182
- declare function buildWhereCondition(table: DrizzleTable, filter: FilterCondition): DrizzleSql | undefined;
185
+ declare function buildWhereCondition(table: DrizzleTable, filter: FilterCondition, dialect?: DrizzleDialect): DrizzleSql | undefined;
183
186
  /**
184
187
  * Shape of a `SELECT count(*) AS count` row produced by the Drizzle query
185
188
  * builder. The builder returns rows as `unknown`, so this names the one row
@@ -195,6 +198,31 @@ interface CountRow {
195
198
  * would otherwise repeat at every call site.
196
199
  */
197
200
  declare function readCount(result: unknown): number;
201
+ /**
202
+ * Emit a dialect-correct substring-match SQL expression.
203
+ *
204
+ * By using the native substring-position function for each dialect, the
205
+ * needle is never injected into a LIKE pattern, so LIKE wildcards (`%`,
206
+ * `_`) and the LIKE escape character lose their special meaning entirely —
207
+ * no wildcard surface means no escape needed.
208
+ *
209
+ * Case-insensitive (default) dialect mapping:
210
+ * - `'sqlite'` → `INSTR(LOWER(col), LOWER(needle)) > 0`
211
+ * - `'pg'` → `POSITION(LOWER(needle) IN LOWER(col)) > 0`
212
+ * - `'mysql'` → `LOCATE(LOWER(needle), LOWER(col)) > 0`
213
+ *
214
+ * With `caseSensitive: true` the LOWER() wrapping is dropped, so case
215
+ * behavior follows the database collation (binary on sqlite/pg, the
216
+ * column collation on mysql) — matching the cross-adapter `like`
217
+ * contract on FilterOperator.
218
+ *
219
+ * All three functions return a 1-based position (or 0 for "not found"),
220
+ * so `> 0` is the dialect-agnostic predicate that means "needle is a
221
+ * substring of col".
222
+ */
223
+ declare function substringMatch(col: DrizzleColumn | DrizzleSql, needle: string, dialect: DrizzleDialect, options?: {
224
+ caseSensitive?: boolean;
225
+ }): DrizzleSql;
198
226
 
199
227
  /**
200
228
  * Drizzle Create endpoint.
@@ -395,6 +423,8 @@ declare abstract class DrizzleListEndpoint<E extends Env = Env, M extends MetaIn
395
423
  * See {@link DrizzleUpsertEndpoint.dialect} for full semantics.
396
424
  */
397
425
  protected dialect: DrizzleDialect;
426
+ /** Keyset cursor pagination is implemented (`WHERE cursorField > decoded`). */
427
+ protected supportsCursorPagination: boolean;
398
428
  /** Gets the database instance from property or context */
399
429
  protected getDb(): DB;
400
430
  protected getTable(): DrizzleTable;
@@ -497,26 +527,6 @@ declare abstract class DrizzleBatchRestoreEndpoint<E extends Env = Env, M extend
497
527
  }>;
498
528
  }
499
529
 
500
- /**
501
- * Emit a dialect-correct case-insensitive substring-match SQL expression.
502
- *
503
- * Replaces the previous `LOWER(col) LIKE LOWER('%needle%') ESCAPE '\\'`
504
- * approach: by using the native substring-position function for each
505
- * dialect, the needle is never injected into a pattern, so LIKE
506
- * wildcards (`%`, `_`) and the LIKE escape character lose their
507
- * special meaning entirely — no wildcard surface means no escape
508
- * needed.
509
- *
510
- * Dialect mapping:
511
- * - `'sqlite'` → `INSTR(LOWER(col), LOWER(needle)) > 0`
512
- * - `'pg'` → `POSITION(LOWER(needle) IN LOWER(col)) > 0`
513
- * - `'mysql'` → `LOCATE(LOWER(needle), LOWER(col)) > 0`
514
- *
515
- * All three return a 1-based position (or 0 for "not found"), so
516
- * `> 0` is the dialect-agnostic predicate that means "needle is a
517
- * substring of col".
518
- */
519
- declare function substringMatch(col: DrizzleColumn | DrizzleSql, needle: string, dialect: DrizzleDialect): DrizzleSql;
520
530
  /**
521
531
  * Drizzle Upsert endpoint.
522
532
  * Creates a record if it doesn't exist, updates it if it does.
@@ -548,6 +558,13 @@ declare abstract class DrizzleUpsertEndpoint<E extends Env = Env, M extends Meta
548
558
  * Note: This method cannot accurately determine if the record was created or updated.
549
559
  * The `created` flag is set to `false` by default. If you need accurate create/update
550
560
  * tracking, use the standard upsert pattern (useNativeUpsert = false).
561
+ *
562
+ * Soft-delete divergence from the standard path: the standard upsert
563
+ * matches a soft-deleted row and restores it ("match-and-restore"),
564
+ * whereas this native path guards DO UPDATE with `WHERE deletedAt IS
565
+ * NULL` — a conflict with a soft-deleted row neither inserts nor
566
+ * updates and returns no row. This is a raw-SQL limitation, documented
567
+ * rather than unified.
551
568
  */
552
569
  protected nativeUpsert(data: Partial<ModelObject<M['model']>>, _tx?: unknown): Promise<{
553
570
  data: ModelObject<M['model']>;
@@ -579,6 +596,13 @@ declare abstract class DrizzleBatchUpsertEndpoint<E extends Env = Env, M extends
579
596
  * Note: This method cannot accurately determine which records were created vs updated.
580
597
  * All records are marked as `created: false`. If you need accurate tracking,
581
598
  * use the standard batch upsert pattern (useNativeUpsert = false).
599
+ *
600
+ * Soft-delete divergence from the standard path: the standard batch
601
+ * upsert matches soft-deleted rows and restores them
602
+ * ("match-and-restore"), whereas this native path's DO UPDATE has no
603
+ * soft-delete guard — a conflict with a soft-deleted row overwrites its
604
+ * data WITHOUT clearing the soft-delete field. This is a raw-SQL
605
+ * limitation, documented rather than unified.
582
606
  */
583
607
  protected nativeBatchUpsert(items: Partial<ModelObject<M['model']>>[], _tx?: unknown): Promise<{
584
608
  items: Array<{
@@ -595,6 +619,34 @@ declare abstract class DrizzleBatchUpsertEndpoint<E extends Env = Env, M extends
595
619
  }>;
596
620
  }>;
597
621
  }
622
+ /**
623
+ * Drizzle Bulk Patch endpoint: `PATCH /resource/bulk` updates every record
624
+ * matching the query-string filters in a single `UPDATE ... WHERE`.
625
+ *
626
+ * Soft-deleted records are never bulk-patched (the WHERE clause adds
627
+ * `IS NULL` on the soft-delete column when configured), and managed update
628
+ * fields (`updatedAt`) are bumped — same semantics as DrizzleBatchUpdate.
629
+ */
630
+ declare abstract class DrizzleBulkPatchEndpoint<E extends Env = Env, M extends MetaInput = MetaInput, DB extends DrizzleDatabaseConstraint = DrizzleDatabaseConstraint> extends BulkPatchEndpoint<E, M> {
631
+ /** Drizzle database instance. Can be undefined if using context injection. */
632
+ db?: DB;
633
+ /**
634
+ * SQL dialect of the underlying Drizzle database. Drives the
635
+ * `like`/`ilike` substring matching in filter conditions.
636
+ */
637
+ protected dialect: DrizzleDialect;
638
+ /** Gets the database instance from property or context. */
639
+ protected getDb(): DB;
640
+ protected getTable(): DrizzleTable;
641
+ protected getColumn(field: string): DrizzleColumn;
642
+ /** WHERE clause shared by countMatching/applyPatch: filters + soft-delete guard. */
643
+ private buildConditions;
644
+ countMatching(filters: ListFilters): Promise<number>;
645
+ applyPatch(data: Partial<ModelObject<M['model']>>, filters: ListFilters): Promise<{
646
+ updated: number;
647
+ records?: ModelObject<M['model']>[];
648
+ }>;
649
+ }
598
650
  /**
599
651
  * Drizzle Version History endpoint.
600
652
  * Lists all versions for a record.
@@ -647,6 +699,11 @@ declare abstract class DrizzleVersionRollbackEndpoint<E extends Env = Env, M ext
647
699
  declare abstract class DrizzleAggregateEndpoint<E extends Env = Env, M extends MetaInput = MetaInput, DB extends DrizzleDatabaseConstraint = DrizzleDatabaseConstraint> extends AggregateEndpoint<E, M> {
648
700
  /** Drizzle database instance. Can be undefined if using context injection. */
649
701
  db?: DB;
702
+ /**
703
+ * SQL dialect of the underlying Drizzle database. Drives the
704
+ * `like`/`ilike` substring matching in filter conditions.
705
+ */
706
+ protected dialect: DrizzleDialect;
650
707
  /** Gets the database instance from property or context. */
651
708
  protected getDb(): DB;
652
709
  protected getTable(): DrizzleTable;
@@ -1023,8 +1080,8 @@ declare function isDrizzleZodAvailable(): boolean;
1023
1080
  /**
1024
1081
  * Drizzle adapter bundle for use with `defineEndpoints`.
1025
1082
  *
1026
- * Populates the 16 verbs Drizzle implements natively plus a stub
1027
- * `CloneEndpoint` (throws on request — subclass to implement).
1083
+ * Populates every `defineEndpoints` slot (all 22 verbs), including
1084
+ * `CloneEndpoint` (a stub that throws on request — subclass to implement).
1028
1085
  *
1029
1086
  * @example
1030
1087
  * ```ts
@@ -1033,4 +1090,4 @@ declare function isDrizzleZodAvailable(): boolean;
1033
1090
  */
1034
1091
  declare const DrizzleAdapters: AdapterBundle;
1035
1092
 
1036
- export { type CountRow, type CreateDrizzleCrudOptions, DRIZZLE_DIALECTS, type Database, DrizzleAdapters, DrizzleAggregateEndpoint, DrizzleBatchCreateEndpoint, DrizzleBatchDeleteEndpoint, DrizzleBatchRestoreEndpoint, DrizzleBatchUpdateEndpoint, DrizzleBatchUpsertEndpoint, DrizzleCloneEndpoint, type DrizzleColumn, DrizzleCreateEndpoint, type DrizzleCrudClasses, type DrizzleDatabaseConstraint, DrizzleDeleteEndpoint, type DrizzleDialect, type DrizzleEnv, DrizzleExportEndpoint, DrizzleImportEndpoint, DrizzleListEndpoint, DrizzleReadEndpoint, DrizzleRestoreEndpoint, type DrizzleSchemas, DrizzleSearchEndpoint, type DrizzleSql, type DrizzleTable, DrizzleUpdateEndpoint, DrizzleUpsertEndpoint, DrizzleVersionCompareEndpoint, DrizzleVersionHistoryEndpoint, DrizzleVersionReadEndpoint, DrizzleVersionRollbackEndpoint, type QueryBuilder, batchLoadDrizzleRelations, buildWhereCondition, cast, createDrizzleCrud, createDrizzleSchemas, createInsertSchema, createSelectSchema, createUpdateSchema, getColumn, getTable, isDrizzleZodAvailable, loadDrizzleRelation, loadDrizzleRelations, readCount, substringMatch };
1093
+ export { type CountRow, type CreateDrizzleCrudOptions, DRIZZLE_DIALECTS, type Database, DrizzleAdapters, DrizzleAggregateEndpoint, DrizzleBatchCreateEndpoint, DrizzleBatchDeleteEndpoint, DrizzleBatchRestoreEndpoint, DrizzleBatchUpdateEndpoint, DrizzleBatchUpsertEndpoint, DrizzleBulkPatchEndpoint, DrizzleCloneEndpoint, type DrizzleColumn, DrizzleCreateEndpoint, type DrizzleCrudClasses, type DrizzleDatabaseConstraint, DrizzleDeleteEndpoint, type DrizzleDialect, type DrizzleEnv, DrizzleExportEndpoint, DrizzleImportEndpoint, DrizzleListEndpoint, DrizzleReadEndpoint, DrizzleRestoreEndpoint, type DrizzleSchemas, DrizzleSearchEndpoint, type DrizzleSql, type DrizzleTable, DrizzleUpdateEndpoint, DrizzleUpsertEndpoint, DrizzleVersionCompareEndpoint, DrizzleVersionHistoryEndpoint, DrizzleVersionReadEndpoint, DrizzleVersionRollbackEndpoint, type QueryBuilder, batchLoadDrizzleRelations, buildWhereCondition, cast, createDrizzleCrud, createDrizzleSchemas, createInsertSchema, createSelectSchema, createUpdateSchema, getColumn, getTable, isDrizzleZodAvailable, loadDrizzleRelation, loadDrizzleRelations, readCount, substringMatch };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import {getTableColumns,between,isNull,isNotNull,ilike,like,notInArray,inArray,lte,lt,gte,gt,ne as ne$1,eq,sql,desc,asc,and,or}from'drizzle-orm';import {resolveRelationValueAsync,loadRelationsForItem,batchLoadRelations,assertNever,UpsertEndpoint,BatchUpsertEndpoint,VersionHistoryEndpoint,VersionReadEndpoint,VersionCompareEndpoint,VersionRollbackEndpoint,AggregateEndpoint,isFilterOperator,computeAggregations,SearchEndpoint,searchInMemory,ExportEndpoint,ImportEndpoint,CloneEndpoint,CreateEndpoint,getLogger,ReadEndpoint,UpdateEndpoint,DeleteEndpoint,ListEndpoint,RestoreEndpoint,BatchCreateEndpoint,BatchUpdateEndpoint,BatchDeleteEndpoint,BatchRestoreEndpoint,CONTEXT_KEYS}from'hono-crud/internal';import {z}from'zod';function X(o){return o}function m(...o){return and(...o)}function S(...o){return or(...o)}var Ke=["sqlite","pg","mysql"];function h(o){if(!o.model.table)throw new Error(`Model ${o.model.tableName} does not have a table reference`);return o.model.table}function p(o,e){let t=getTableColumns(o),n=t[e];if(!n)throw new Error(`Column '${e}' not found in table. Available columns: ${Object.keys(t).join(", ")}`);return n}function ae(o){return {resolveRelation:e=>e.table??null,fetchRelated:(e,t,n)=>o.select().from(e).where(inArray(p(e,t),n))}}async function $e(o,e,t,n){let r=n.table;if(!r)return e;let i=ae(o),a=await resolveRelationValueAsync(e,n,r,i.fetchRelated);return {...e,[t]:a}}async function le(o,e,t,n){return loadRelationsForItem(e,t,ae(o),n)}async function B(o,e,t,n){return batchLoadRelations(e,t,ae(o),n)}function k(o,e){let t=p(o,e.field);switch(e.operator){case "eq":return eq(t,e.value);case "ne":return ne$1(t,e.value);case "gt":return gt(t,e.value);case "gte":return gte(t,e.value);case "lt":return lt(t,e.value);case "lte":return lte(t,e.value);case "in":return inArray(t,e.value);case "nin":return notInArray(t,e.value);case "like":return like(t,e.value);case "ilike":return ilike(t,e.value);case "null":return e.value?isNull(t):isNotNull(t);case "between":{let[n,r]=e.value;return between(t,n,r)}default:return assertNever(e.operator)}}function O(o){return Number(o[0]?.count)||0}function f(o){let e=o;if(e._tx)return e._tx;if(e.db)return e.db;let t=e.context?.get?.(CONTEXT_KEYS.db);if(t)return t;throw new Error(`Database not configured. Either:
1
+ import {getTableColumns,between,isNull,isNotNull,notInArray,inArray,lte,lt,gte,gt,ne as ne$1,eq,sql,and,desc,asc,or}from'drizzle-orm';import {resolveRelationValueAsync,loadRelationsForItem,batchLoadRelations,assertNever,CreateEndpoint,getLogger,ReadEndpoint,UpdateEndpoint,DeleteEndpoint,ListEndpoint,buildCursorPage,RestoreEndpoint,BatchCreateEndpoint,BatchUpdateEndpoint,BatchDeleteEndpoint,BatchRestoreEndpoint,UpsertEndpoint,BatchUpsertEndpoint,BulkPatchEndpoint,VersionHistoryEndpoint,VersionReadEndpoint,VersionCompareEndpoint,VersionRollbackEndpoint,AggregateEndpoint,isFilterOperator,computeAggregations,SearchEndpoint,searchInMemory,ExportEndpoint,ImportEndpoint,CloneEndpoint,CONTEXT_KEYS,ConfigurationException,decodeCursor}from'hono-crud/internal';import {z as z$1}from'zod';function G(o){return o}function g(...o){return and(...o)}function J(...o){return or(...o)}var $e=["sqlite","pg","mysql"];function z(o){if(!o.model.table)throw new Error(`Model ${o.model.tableName} does not have a table reference`);return o.model.table}function p(o,e){let n=getTableColumns(o),t=n[e];if(!t)throw new Error(`Column '${e}' not found in table. Available columns: ${Object.keys(n).join(", ")}`);return t}function be(o){return {resolveRelation:e=>e.table??null,fetchRelated:(e,n,t)=>o.select().from(e).where(inArray(p(e,n),t))}}async function Ke(o,e,n,t){let r=t.table;if(!r)return e;let s=be(o),a=await resolveRelationValueAsync(e,t,r,s.fetchRelated);return {...e,[n]:a}}async function me(o,e,n,t){return loadRelationsForItem(e,n,be(o),t)}async function v(o,e,n,t){return batchLoadRelations(e,n,be(o),t)}function W(o,e,n="sqlite"){let t=p(o,e.field);switch(e.operator){case "eq":return eq(t,e.value);case "ne":return ne$1(t,e.value);case "gt":return gt(t,e.value);case "gte":return gte(t,e.value);case "lt":return lt(t,e.value);case "lte":return lte(t,e.value);case "in":return inArray(t,e.value);case "nin":return notInArray(t,e.value);case "like":return B(t,String(e.value).replace(/%/g,""),n,{caseSensitive:true});case "ilike":return B(t,String(e.value).replace(/%/g,""),n);case "null":return e.value?isNull(t):isNotNull(t);case "between":{let[r,s]=e.value;return between(t,r,s)}default:return assertNever(e.operator)}}function S(o){return Number(o[0]?.count)||0}async function V(o){let{db:e,table:n,filters:t,dialect:r,searchFields:s=[],softDeleteConfig:a,defaultPerPage:i=20,extraConditions:l=[],cursorField:d}=o,c=[];if(a?.enabled){let y=p(n,a.field);t.options.onlyDeleted?c.push(isNotNull(y)):t.options.withDeleted||c.push(isNull(y));}for(let y of t.filters){let k=W(n,y,r);k&&c.push(k);}if(t.options.search&&s.length>0){let y=t.options.search,k=s.map(xe=>B(p(n,xe),y,r));c.push(J(...k));}c.push(...l);let u=c.length>0?g(...c):void 0,D=await e.select({count:sql`count(*)`}).from(n).where(u),f=S(D),h=d!==void 0&&(t.options.cursor!==void 0||t.options.limit!==void 0),C=u,w=false;if(h&&t.options.cursor){let y=decodeCursor(t.options.cursor);y!==null&&(C=g(u,gt(p(n,d),y)),w=true);}let M=e.select().from(n).where(C);if(t.options.order_by){let y=p(n,t.options.order_by),k=t.options.order_by_direction==="desc"?desc:asc;M=M.orderBy(k(y));}if(h){let y=t.options.limit||t.options.per_page||i;return {records:await M.limit(y+1),totalCount:f,page:0,perPage:y,totalPages:0,cursor:{limit:y,applied:w}}}let E=t.options.page||1,T=t.options.per_page||i,Y=await M.limit(T).offset((E-1)*T),Q=Math.ceil(f/T);return {records:Y,totalCount:f,page:E,perPage:T,totalPages:Q}}function ee(o,e){return {result:o,result_info:{page:e.page,per_page:e.perPage,total_count:e.totalCount,total_pages:e.totalPages,has_next_page:e.page<e.totalPages,has_prev_page:e.page>1}}}async function te(o,e,n,t,r){let s=[];for(let i of r){let l=t[i];l!==void 0&&s.push(eq(n(i),l));}return s.length===0?null:(await o.select().from(e).where(g(...s)).limit(1))[0]||null}function B(o,e,n,t){if(t?.caseSensitive)switch(n){case "pg":return sql`POSITION(${e} IN ${o}) > 0`;case "mysql":return sql`LOCATE(${e}, ${o}) > 0`;default:return sql`INSTR(${o}, ${e}) > 0`}switch(n){case "pg":return sql`POSITION(LOWER(${e}) IN LOWER(${o})) > 0`;case "mysql":return sql`LOCATE(LOWER(${e}), LOWER(${o})) > 0`;default:return sql`INSTR(LOWER(${o}), LOWER(${e})) > 0`}}function b(o){let e=o;if(e._tx)return e._tx;if(e.db)return e.db;let n=e.context?.get?.(CONTEXT_KEYS.db);if(n)return n;throw new ConfigurationException(`Database not configured. Either:
2
2
  1. Set db property: db = myDb;
3
3
  2. Use middleware: c.set(CONTEXT_KEYS.db, myDb);
4
- 3. Use factory: createDrizzleCrud(db, meta)`)}function ee(o,e,t){switch(t){case "pg":return sql`POSITION(LOWER(${e}) IN LOWER(${o})) > 0`;case "mysql":return sql`LOCATE(LOWER(${e}), LOWER(${o})) > 0`;default:return sql`INSTR(LOWER(${o}), LOWER(${e})) > 0`}}function at(o){return o.split(/\s+/).filter(e=>e.length>0)}var j=class extends UpsertEndpoint{db;dialect="sqlite";getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async findExisting(e){let t=this.getTable(),n=this.getUpsertKeys(),r=this.getSoftDeleteConfig(),i=[];for(let s of n){let l=e[s];l!==void 0&&i.push(eq(this.getColumn(s),l));}return r.enabled&&i.push(isNull(this.getColumn(r.field))),i.length===0?null:(await this.getDb().select().from(t).where(m(...i)).limit(1))[0]||null}async create(e){let t=this.getTable(),n=this.applyManagedInsertFields(e,"drizzle");return (await this.getDb().insert(t).values(n).returning())[0]}async update(e,t){let n=this.getTable(),r=this._meta.model.primaryKeys[0],i=e[r];return (await this.getDb().update(n).set(this.applyManagedUpdateFields(t)).where(eq(this.getColumn(r),i)).returning())[0]}async nativeUpsert(e,t){let n=this.getTable(),r=this.getUpsertKeys(),i=this._meta.model.primaryKeys[0],a=this.getSoftDeleteConfig(),s=this.getTimestampsConfig(),l=this.applyManagedInsertFields(e,"drizzle"),d={};for(let[g,C]of Object.entries(e))!r.includes(g)&&g!==i&&(this.createOnlyFields?.includes(g)||(d[g]=C));s.enabled&&(d[s.updatedAt]=Date.now());let u=r.map(g=>this.getColumn(g)),c;a.enabled&&(c=isNull(this.getColumn(a.field)));let b=Object.keys(d).length>0?d:{[i]:sql`${this.getColumn(i)}`},M=this.getDb().insert(n).values(l);return this.dialect==="mysql"?{data:(await M.onDuplicateKeyUpdate({set:b}).returning())[0],created:false}:{data:(await M.onConflictDoUpdate({target:u,set:b,where:c}).returning())[0],created:false}}},_=class extends BatchUpsertEndpoint{db;dialect="sqlite";getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async findExisting(e){let t=this.getTable(),n=this.getUpsertKeys(),r=[];for(let a of n){let s=e[a];s!==void 0&&r.push(eq(this.getColumn(a),s));}return r.length===0?null:(await this.getDb().select().from(t).where(m(...r)).limit(1))[0]||null}async create(e){let t=this.getTable(),n=this.applyManagedInsertFields(e,"drizzle");return (await this.getDb().insert(t).values(n).returning())[0]}async update(e,t){let n=this.getTable(),r=this._meta.model.primaryKeys[0],i=e[r];return (await this.getDb().update(n).set(this.applyManagedUpdateFields(t)).where(eq(this.getColumn(r),i)).returning())[0]}async nativeBatchUpsert(e,t){if(e.length===0)return {items:[],createdCount:0,updatedCount:0,totalCount:0};let n=this.getTable(),r=this.getUpsertKeys(),i=this._meta.model.primaryKeys[0],a=this.getTimestampsConfig(),s=e.map(z=>this.applyManagedInsertFields(z,"drizzle")),l={},d=e[0];for(let z of Object.keys(d))!r.includes(z)&&z!==i&&(this.createOnlyFields?.includes(z)||(l[z]=sql`excluded.${sql.identifier(z)}`));a.enabled&&(l[a.updatedAt]=Date.now());let u=r.map(z=>this.getColumn(z)),c=Object.keys(l).length>0?l:{[i]:sql`${this.getColumn(i)}`},b=this.getDb().insert(n).values(s),M=await(this.dialect==="mysql"?b.onDuplicateKeyUpdate({set:c}):b.onConflictDoUpdate({target:u,set:c})).returning();return {items:M.map((z,g)=>({data:z,created:false,index:g})),createdCount:0,updatedCount:M.length,totalCount:M.length}}},ue=class extends VersionHistoryEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async recordExists(e){let t=this.getTable(),n=await this.getDb().select({count:sql`count(*)`}).from(t).where(eq(this.getColumn("id"),e));return O(n)>0}},pe=class extends VersionReadEndpoint{},ge=class extends VersionCompareEndpoint{},me=class extends VersionRollbackEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async rollback(e,t,n){let r=this.getTable(),i=this.getVersioningConfig().field;return (await this.getDb().update(r).set({...t,[i]:n}).where(eq(this.getColumn("id"),e)).returning())[0]}},Y=class extends AggregateEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async aggregate(e){let t=this.getTable(),n=[],r=this.getSoftDeleteConfig();if(r.enabled){let{query:s}=await this.getValidatedData();s?.withDeleted===true||s?.withDeleted==="true"||n.push(isNull(this.getColumn(r.field)));}if(e.filters)for(let[s,l]of Object.entries(e.filters))if(typeof l=="object"&&l!==null)for(let[d,u]of Object.entries(l)){if(!isFilterOperator(d)){n.push(sql`1 = 0`);continue}let c=k(t,{field:s,operator:d,value:u});c&&n.push(c);}else n.push(eq(this.getColumn(s),l));let i=n.length>0?m(...n):void 0,a=await this.getDb().select().from(t).where(i);return computeAggregations(a,e)}},I=class extends SearchEndpoint{db;dialect="sqlite";getDb(){return f(this)}useNativeSearch=false;vectorColumn;vectorConfig="english";getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async search(e,t){let n=this.getTable(),r=[],i=this.getSoftDeleteConfig();i.enabled&&(t.options.onlyDeleted?r.push(isNotNull(this.getColumn(i.field))):t.options.withDeleted||r.push(isNull(this.getColumn(i.field))));for(let E of t.filters){let y=k(n,E);y&&r.push(y);}let a=this.getSearchableFields(),s=e.fields||Object.keys(a);if(this.useNativeSearch&&this.vectorColumn){let E=this.getColumn(this.vectorColumn),y=e.mode==="phrase"?sql`phraseto_tsquery(${this.vectorConfig}, ${e.query})`:e.mode==="all"?sql`plainto_tsquery(${this.vectorConfig}, ${e.query})`:sql`to_tsquery(${this.vectorConfig}, ${e.query.split(/\s+/).join(" | ")})`;r.push(sql`${E} @@ ${y}`);}else {let E=(y,x)=>{try{let se=this.getColumn(y);return ee(sql`CAST(${se} AS TEXT)`,x,this.dialect)}catch{return}};if(e.mode==="all"){let y=at(e.query);if(y.length>0){let x=[];for(let se of y){let de=s.map(ie=>E(ie,se)).filter(ie=>ie!==void 0);de.length>0&&x.push(S(...de));}x.length>0&&r.push(m(...x));}}else {let y=s.map(x=>E(x,e.query)).filter(x=>x!==void 0);y.length>0&&r.push(S(...y));}}let l=r.length>0?m(...r):void 0,d=await this.getDb().select({count:sql`count(*)`}).from(n).where(l),u=O(d),c=this.getDb().select().from(n).where(l);if(t.options.order_by){let E=this.getColumn(t.options.order_by),y=t.options.order_by_direction==="desc"?desc:asc;c=c.orderBy(y(E));}let b=t.options.page||1,M=t.options.per_page||this.defaultPerPage;c=c.limit(M).offset((b-1)*M);let z=await c,g=e.mode==="all"?{...e,mode:"any"}:e,C=searchInMemory(z,g,a),R={relations:t.options.include||[]},W=C.map(E=>E.item),oe=await B(this.getDb(),W,this._meta,R);return {items:C.map((E,y)=>({...E,item:oe[y]})),totalCount:u}}},G=class extends ExportEndpoint{db;dialect="sqlite";getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async list(e){let t=this.getTable(),n=[],r=this.getSoftDeleteConfig();if(r.enabled){let g=this.getColumn(r.field);e.options.onlyDeleted?n.push(isNotNull(g)):e.options.withDeleted||n.push(isNull(g));}for(let g of e.filters){let C=k(t,g);C&&n.push(C);}if(e.options.search&&this.searchFields.length>0){let g=e.options.search,C=this.searchFields.map(R=>{let W=this.getColumn(R);return ee(W,g,this.dialect)});n.push(S(...C));}let i=n.length>0?m(...n):void 0,a=await this.getDb().select({count:sql`count(*)`}).from(t).where(i),s=O(a),l=this.getDb().select().from(t).where(i);if(e.options.order_by){let g=this.getColumn(e.options.order_by),C=e.options.order_by_direction==="desc"?desc:asc;l=l.orderBy(C(g));}let d=e.options.page||1,u=e.options.per_page||this.defaultPerPage;l=l.limit(u).offset((d-1)*u);let c=await l,b={relations:e.options.include||[]},M=await B(this.getDb(),c,this._meta,b),z=Math.ceil(s/u);return {result:M,result_info:{page:d,per_page:u,total_count:s,total_pages:z,has_next_page:d<z,has_prev_page:d>1}}}},H=class extends ImportEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async findExisting(e){let t=this.getTable(),n=this.getUpsertKeys(),r=this.getSoftDeleteConfig(),i=[];for(let s of n){let l=e[s];l!==void 0&&i.push(eq(this.getColumn(s),l));}return r.enabled&&i.push(isNull(this.getColumn(r.field))),i.length===0?null:(await this.getDb().select().from(t).where(m(...i)).limit(1))[0]||null}async create(e){let t=this.getTable(),n=this.applyManagedInsertFields(e,"drizzle");return (await this.getDb().insert(t).values(n).returning())[0]}async update(e,t){let n=this.getTable(),r=this._meta.model.primaryKeys[0],i=e[r];return (await this.getDb().update(n).set(this.applyManagedUpdateFields(t)).where(eq(this.getColumn(r),i)).returning())[0]}},J=class extends CloneEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}generateId(){return crypto.randomUUID()}async findSource(e,t){let n=this.getTable(),r=this.getColumn(this.lookupField),i=this.getSoftDeleteConfig(),a=[eq(r,e)];if(t)for(let[l,d]of Object.entries(t))a.push(eq(this.getColumn(l),d));i.enabled&&a.push(isNull(this.getColumn(i.field)));let s=await this.getDb().select().from(n).where(m(...a)).limit(1);return s[0]?s[0]:null}async createClone(e){let t=this.getTable(),n=this.applyManagedInsertFields(e,"drizzle",()=>this.generateId());return (await this.getDb().insert(t).values(n).returning())[0]}};var q=class extends CreateEndpoint{db;useTransaction=false;getDb(){return f(this)}getTable(){return h(this._meta)}getRelatedTable(e){return e.table}async create(e,t){let n=t??this.getDb(),r=this.getTable(),i=this.applyManagedInsertFields(e,"drizzle");return (await n.insert(r).values(i).returning())[0]}async createNested(e,t,n,r,i){let a=i??this.getDb(),s=this.getRelatedTable(n);if(!s)return getLogger().warn(`Related table not found for ${t}. Add 'table' to the relation config.`),[];let l=Array.isArray(r)?r:[r],d=[];for(let u of l){if(typeof u!="object"||u===null)continue;let c={...u,id:crypto.randomUUID(),[n.foreignKey]:e},b=await a.insert(s).values(c).returning();b[0]&&d.push(b[0]);}return d}async handle(){return this.useTransaction?this.getDb().transaction(async e=>{this._tx=e;try{return await super.handle()}finally{this._tx=void 0;}}):super.handle()}},A=class extends ReadEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async read(e,t,n){let r=this.getTable(),i=this.getColumn(this.lookupField),a=this.getSoftDeleteConfig(),s=[eq(i,e)];if(t)for(let[u,c]of Object.entries(t))s.push(eq(this.getColumn(u),c));a.enabled&&s.push(isNull(this.getColumn(a.field)));let l=await this.getDb().select().from(r).where(m(...s)).limit(1);return l[0]?await le(this.getDb(),l[0],this._meta,n):null}},U=class extends UpdateEndpoint{db;useTransaction=false;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}getRelatedTable(e){return e.table}async findExisting(e,t,n){let r=n??this.getDb(),i=this.getTable(),a=this.getColumn(this.lookupField),s=this.getSoftDeleteConfig(),l=[eq(a,e)];if(t)for(let[u,c]of Object.entries(t))l.push(eq(this.getColumn(u),c));return s.enabled&&l.push(isNull(this.getColumn(s.field))),(await r.select().from(i).where(m(...l)).limit(1))[0]||null}async update(e,t,n,r){let i=r??this.getDb(),a=this.getTable(),s=this.getColumn(this.lookupField),l=this.getSoftDeleteConfig(),d=[eq(s,e)];if(n)for(let[c,b]of Object.entries(n))d.push(eq(this.getColumn(c),b));return l.enabled&&d.push(isNull(this.getColumn(l.field))),(await i.update(a).set(this.applyManagedUpdateFields(t)).where(m(...d)).returning())[0]||null}async processNestedWrites(e,t,n,r,i){let a=i??this.getDb(),s=this.getRelatedTable(n);if(!s)return getLogger().warn(`Related table not found for ${t}. Add 'table' to the relation config.`),{created:[],updated:[],deleted:[],connected:[],disconnected:[]};let l={created:[],updated:[],deleted:[],connected:[],disconnected:[]},d=p(s,n.foreignKey),u=p(s,"id");if(r.create){let c=Array.isArray(r.create)?r.create:[r.create];for(let b of c){if(typeof b!="object"||b===null)continue;let M={...b,id:crypto.randomUUID(),[n.foreignKey]:e},z=await a.insert(s).values(M).returning();z[0]&&l.created.push(z[0]);}}if(r.update)for(let c of r.update){if(!c.id||!(await a.select().from(s).where(m(eq(u,c.id),eq(d,e))).limit(1))[0])continue;let{id:M,...z}=c,g=await a.update(s).set(z).where(eq(u,M)).returning();g[0]&&l.updated.push(g[0]);}if(r.delete)for(let c of r.delete)(await a.delete(s).where(m(eq(u,c),eq(d,e))).returning())[0]&&l.deleted.push(c);if(r.connect)for(let c of r.connect)(await a.update(s).set({[n.foreignKey]:e}).where(eq(u,c)).returning())[0]&&l.connected.push(c);if(r.disconnect)for(let c of r.disconnect)(await a.update(s).set({[n.foreignKey]:null}).where(m(eq(u,c),eq(d,e))).returning())[0]&&l.disconnected.push(c);return l}async handle(){return this.useTransaction?this.getDb().transaction(async e=>{this._tx=e;try{return await super.handle()}finally{this._tx=void 0;}}):super.handle()}},Z=class extends DeleteEndpoint{db;useTransaction=false;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}getRelatedTable(e){return e.table}async findForDelete(e,t,n){let r=n??this.getDb(),i=this.getTable(),a=this.getColumn(this.lookupField),s=this.getSoftDeleteConfig(),l=[eq(a,e)];if(t)for(let[u,c]of Object.entries(t))l.push(eq(this.getColumn(u),c));return s.enabled&&l.push(isNull(this.getColumn(s.field))),(await r.select().from(i).where(m(...l)).limit(1))[0]||null}async delete(e,t,n){let r=n??this.getDb(),i=this.getTable(),a=this.getColumn(this.lookupField),s=this.getSoftDeleteConfig(),l=[eq(a,e)];if(t)for(let[d,u]of Object.entries(t))l.push(eq(this.getColumn(d),u));return s.enabled&&l.push(isNull(this.getColumn(s.field))),s.enabled?(await r.update(i).set({[s.field]:new Date}).where(m(...l)).returning())[0]||null:(await r.delete(i).where(m(...l)).returning())[0]||null}async countRelated(e,t,n,r){let i=r??this.getDb(),a=this.getRelatedTable(n);if(!a)return 0;let s=p(a,n.foreignKey),l=await i.select({count:sql`count(*)`}).from(a).where(eq(s,e));return O(l)}async deleteRelated(e,t,n,r){let i=r??this.getDb(),a=this.getRelatedTable(n);if(!a)return 0;let s=p(a,n.foreignKey);return (await i.delete(a).where(eq(s,e)).returning()).length}async nullifyRelated(e,t,n,r){let i=r??this.getDb(),a=this.getRelatedTable(n);if(!a)return 0;let s=p(a,n.foreignKey);return (await i.update(a).set({[n.foreignKey]:null}).where(eq(s,e)).returning()).length}async handle(){return this.useTransaction?this.getDb().transaction(async e=>{this._tx=e;try{return await super.handle()}finally{this._tx=void 0;}}):super.handle()}},F=class extends ListEndpoint{db;dialect="sqlite";getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async list(e){let t=this.getTable(),n=[],r=this.getSoftDeleteConfig();if(r.enabled){let C=this.getColumn(r.field);e.options.onlyDeleted?n.push(isNotNull(C)):e.options.withDeleted||n.push(isNull(C));}for(let C of e.filters){let R=k(t,C);R&&n.push(R);}if(e.options.search&&this.searchFields.length>0){let C=e.options.search,R=this.searchFields.map(W=>{let oe=this.getColumn(W);return ee(oe,C,this.dialect)});n.push(S(...R));}let i=n.length>0?m(...n):void 0,a=this.getDb(),s=await a.select({count:sql`count(*)`}).from(t).where(i),l=O(s),d=a.select().from(t).where(i);if(e.options.order_by){let C=this.getColumn(e.options.order_by),R=e.options.order_by_direction==="desc"?desc:asc;d=d.orderBy(R(C));}let u=e.options.page||1,c=e.options.per_page||this.defaultPerPage;d=d.limit(c).offset((u-1)*c);let b=await d,M={relations:e.options.include||[]},z=await B(this.getDb(),b,this._meta,M),g=Math.ceil(l/c);return {result:z,result_info:{page:u,per_page:c,total_count:l,total_pages:g,has_next_page:u<g,has_prev_page:u>1}}}},N=class extends RestoreEndpoint{db;useTransaction=false;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async restore(e,t,n){let r=n??this.getDb(),i=this.getTable(),a=this.getColumn(this.lookupField),s=this.getSoftDeleteConfig(),l=[eq(a,e)];if(t)for(let[u,c]of Object.entries(t))l.push(eq(this.getColumn(u),c));return l.push(isNotNull(this.getColumn(s.field))),(await r.update(i).set({[s.field]:null}).where(m(...l)).returning())[0]||null}async handle(){return this.useTransaction?this.getDb().transaction(async e=>{this._tx=e;try{return await super.handle()}finally{this._tx=void 0;}}):super.handle()}};var L=class extends BatchCreateEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}async batchCreate(e){let t=this.getTable(),n=e.map(i=>this.applyManagedInsertFields(i,"drizzle"));return await this.getDb().insert(t).values(n).returning()}},K=class extends BatchUpdateEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async batchUpdate(e){let t=this.getTable(),n=this.getColumn(this.lookupField),r=this.getSoftDeleteConfig(),i=[],a=[];for(let s of e){let l=[eq(n,s.id)];r.enabled&&l.push(isNull(this.getColumn(r.field)));let d=await this.getDb().update(t).set(this.applyManagedUpdateFields(s.data)).where(m(...l)).returning();d[0]?i.push(d[0]):a.push(s.id);}return {updated:i,notFound:a}}},$=class extends BatchDeleteEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async batchDelete(e){let t=this.getTable(),n=this.getColumn(this.lookupField),r=this.getSoftDeleteConfig(),i=[inArray(n,e)];r.enabled&&i.push(isNull(this.getColumn(r.field)));let a;r.enabled?a=await this.getDb().update(t).set({[r.field]:new Date}).where(m(...i)).returning():a=await this.getDb().delete(t).where(m(...i)).returning();let s=a,l=new Set(s.map(u=>String(u[this.lookupField]))),d=e.filter(u=>!l.has(u));return {deleted:s,notFound:d}}},Q=class extends BatchRestoreEndpoint{db;getDb(){return f(this)}getTable(){return h(this._meta)}getColumn(e){return p(this.getTable(),e)}async batchRestore(e){let t=this.getTable(),n=this.getColumn(this.lookupField),r=this.getSoftDeleteConfig(),i=[inArray(n,e),isNotNull(this.getColumn(r.field))],s=await this.getDb().update(t).set({[r.field]:null}).where(m(...i)).returning(),l=new Set(s.map(u=>String(u[this.lookupField]))),d=e.filter(u=>!l.has(u));return {restored:s,notFound:d}}};function fn(o,e,t){let n=t?.dialect??"sqlite";return {Create:class extends q{_meta=e;db=o},Read:class extends A{_meta=e;db=o},Update:class extends U{_meta=e;db=o},Delete:class extends Z{_meta=e;db=o},List:class extends F{_meta=e;db=o;dialect=n},Restore:class extends N{_meta=e;db=o},Upsert:class extends j{_meta=e;db=o;dialect=n},Search:class extends I{_meta=e;db=o;dialect=n},BatchCreate:class extends L{_meta=e;db=o},BatchUpdate:class extends K{_meta=e;db=o},BatchDelete:class extends ${_meta=e;db=o},BatchRestore:class extends Q{_meta=e;db=o},BatchUpsert:class extends _{_meta=e;db=o;dialect=n}}}var ne=null,we=false,te=null;async function re(){if(we){if(te)throw te;return ne}we=true;try{return ne=await import('drizzle-zod'),ne}catch{throw te=new Error("drizzle-zod is not installed. Please install it: npm install drizzle-zod"),te}}async function yt(o,e){return (await re()).createSelectSchema(o,e)}async function wt(o,e){return (await re()).createInsertSchema(o,e)}async function Et(o,e){let t=await re();return t.createUpdateSchema?t.createUpdateSchema(o,e):t.createInsertSchema(o,e).partial()}async function Tt(o,e){let t=await re(),n=e?.coerceDates!==false,r=n?kt(o):new Set,i=t.createSelectSchema(o,e?.selectRefine),a=t.createInsertSchema(o,e?.insertRefine),s;return t.createUpdateSchema?s=t.createUpdateSchema(o,e?.updateRefine):s=t.createInsertSchema(o,e?.updateRefine).partial(),n&&r.size>0&&(a=Ee(a,r),s=Ee(s,r)),{select:i,insert:a,update:s}}function Rt(){return ne!==null}var xt=z.preprocess(o=>{if(o instanceof Date)return o;if(typeof o=="string"){let e=new Date(o);if(!isNaN(e.getTime()))return e}return o},z.date()),Ot=z.preprocess(o=>{if(o==null)return null;if(o instanceof Date)return o;if(typeof o=="string"){let e=new Date(o);if(!isNaN(e.getTime()))return e}return o},z.date().nullable());function kt(o){let e=new Set,t=o;for(let[n,r]of Object.entries(t)){if(n==="_"||n==="$inferInsert"||n==="$inferSelect")continue;let i=r;if(!i||typeof i!="object")continue;let a=String(i.dataType??"").toLowerCase(),s=String(i.columnType??"").toLowerCase(),l=i.config,d=String(l?.dataType??"").toLowerCase();(a.includes("timestamp")||a.includes("date")||a.includes("datetime")||s.includes("pgtimestamp")||s.includes("pgdate")||s.includes("mysqltimestamp")||s.includes("mysqldate")||s.includes("sqlitetimestamp")||d.includes("timestamp")||d.includes("date"))&&e.add(n);}return e}function Ee(o,e){if(e.size===0)return o;let t=o.shape,n={};for(let[r,i]of Object.entries(t))if(e.has(r)){let a=i.isOptional?.()??false,s=i.isNullable?.()??false,l=xt;(s||a)&&(l=Ot),a&&(l=l.optional()),n[r]=l;}else n[r]=i;return z.object(n)}var Un={CreateEndpoint:q,ListEndpoint:F,ReadEndpoint:A,UpdateEndpoint:U,DeleteEndpoint:Z,RestoreEndpoint:N,BatchCreateEndpoint:L,BatchUpdateEndpoint:K,BatchDeleteEndpoint:$,BatchRestoreEndpoint:Q,BatchUpsertEndpoint:_,SearchEndpoint:I,AggregateEndpoint:Y,ExportEndpoint:G,ImportEndpoint:H,UpsertEndpoint:j,CloneEndpoint:J};export{Ke as DRIZZLE_DIALECTS,Un as DrizzleAdapters,Y as DrizzleAggregateEndpoint,L as DrizzleBatchCreateEndpoint,$ as DrizzleBatchDeleteEndpoint,Q as DrizzleBatchRestoreEndpoint,K as DrizzleBatchUpdateEndpoint,_ as DrizzleBatchUpsertEndpoint,J as DrizzleCloneEndpoint,q as DrizzleCreateEndpoint,Z as DrizzleDeleteEndpoint,G as DrizzleExportEndpoint,H as DrizzleImportEndpoint,F as DrizzleListEndpoint,A as DrizzleReadEndpoint,N as DrizzleRestoreEndpoint,I as DrizzleSearchEndpoint,U as DrizzleUpdateEndpoint,j as DrizzleUpsertEndpoint,ge as DrizzleVersionCompareEndpoint,ue as DrizzleVersionHistoryEndpoint,pe as DrizzleVersionReadEndpoint,me as DrizzleVersionRollbackEndpoint,B as batchLoadDrizzleRelations,k as buildWhereCondition,X as cast,fn as createDrizzleCrud,Tt as createDrizzleSchemas,wt as createInsertSchema,yt as createSelectSchema,Et as createUpdateSchema,p as getColumn,h as getTable,Rt as isDrizzleZodAvailable,$e as loadDrizzleRelation,le as loadDrizzleRelations,O as readCount,ee as substringMatch};
4
+ 3. Use factory: createDrizzleCrud(db, meta)`)}var j=class extends CreateEndpoint{db;useTransaction=false;getDb(){return b(this)}getTable(){return z(this._meta)}getRelatedTable(e){return e.table}async create(e,n){let t=n??this.getDb(),r=this.getTable(),s=this.applyManagedInsertFields(e,"drizzle");return (await t.insert(r).values(s).returning())[0]}async createNested(e,n,t,r,s){let a=s??this.getDb(),i=this.getRelatedTable(t);if(!i)return getLogger().warn(`Related table not found for ${n}. Add 'table' to the relation config.`),[];let l=Array.isArray(r)?r:[r],d=[];for(let c of l){if(typeof c!="object"||c===null)continue;let u={...c,id:crypto.randomUUID(),[t.foreignKey]:e},D=await a.insert(i).values(u).returning();D[0]&&d.push(D[0]);}return d}async handle(){return this.useTransaction?this.getDb().transaction(async e=>{this._tx=e;try{return await super.handle()}finally{this._tx=void 0;}}):super.handle()}},P=class extends ReadEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async read(e,n,t){let r=this.getTable(),s=this.getColumn(this.lookupField),a=this.getSoftDeleteConfig(),i=[eq(s,e)];if(n)for(let[c,u]of Object.entries(n))i.push(eq(this.getColumn(c),u));a.enabled&&i.push(isNull(this.getColumn(a.field)));let l=await this.getDb().select().from(r).where(g(...i)).limit(1);return l[0]?await me(this.getDb(),l[0],this._meta,t):null}},I=class extends UpdateEndpoint{db;useTransaction=false;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}getRelatedTable(e){return e.table}async findExisting(e,n,t){let r=t??this.getDb(),s=this.getTable(),a=this.getColumn(this.lookupField),i=this.getSoftDeleteConfig(),l=[eq(a,e)];if(n)for(let[c,u]of Object.entries(n))l.push(eq(this.getColumn(c),u));return i.enabled&&l.push(isNull(this.getColumn(i.field))),(await r.select().from(s).where(g(...l)).limit(1))[0]||null}async update(e,n,t,r){let s=r??this.getDb(),a=this.getTable(),i=this.getColumn(this.lookupField),l=this.getSoftDeleteConfig(),d=[eq(i,e)];if(t)for(let[u,D]of Object.entries(t))d.push(eq(this.getColumn(u),D));return l.enabled&&d.push(isNull(this.getColumn(l.field))),(await s.update(a).set(this.applyManagedUpdateFields(n)).where(g(...d)).returning())[0]||null}async processNestedWrites(e,n,t,r,s){let a=s??this.getDb(),i=this.getRelatedTable(t);if(!i)return getLogger().warn(`Related table not found for ${n}. Add 'table' to the relation config.`),{created:[],updated:[],deleted:[],connected:[],disconnected:[]};let l={created:[],updated:[],deleted:[],connected:[],disconnected:[]},d=p(i,t.foreignKey),c=p(i,"id");if(r.create){let u=Array.isArray(r.create)?r.create:[r.create];for(let D of u){if(typeof D!="object"||D===null)continue;let f={...D,id:crypto.randomUUID(),[t.foreignKey]:e},h=await a.insert(i).values(f).returning();h[0]&&l.created.push(h[0]);}}if(r.update)for(let u of r.update){if(!u.id||!(await a.select().from(i).where(g(eq(c,u.id),eq(d,e))).limit(1))[0])continue;let{id:f,...h}=u,C=await a.update(i).set(h).where(eq(c,f)).returning();C[0]&&l.updated.push(C[0]);}if(r.delete)for(let u of r.delete)(await a.delete(i).where(g(eq(c,u),eq(d,e))).returning())[0]&&l.deleted.push(u);if(r.connect)for(let u of r.connect)(await a.update(i).set({[t.foreignKey]:e}).where(eq(c,u)).returning())[0]&&l.connected.push(u);if(r.disconnect)for(let u of r.disconnect)(await a.update(i).set({[t.foreignKey]:null}).where(g(eq(c,u),eq(d,e))).returning())[0]&&l.disconnected.push(u);return l}async handle(){return this.useTransaction?this.getDb().transaction(async e=>{this._tx=e;try{return await super.handle()}finally{this._tx=void 0;}}):super.handle()}},_=class extends DeleteEndpoint{db;useTransaction=false;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}getRelatedTable(e){return e.table}async findForDelete(e,n,t){let r=t??this.getDb(),s=this.getTable(),a=this.getColumn(this.lookupField),i=this.getSoftDeleteConfig(),l=[eq(a,e)];if(n)for(let[c,u]of Object.entries(n))l.push(eq(this.getColumn(c),u));return i.enabled&&l.push(isNull(this.getColumn(i.field))),(await r.select().from(s).where(g(...l)).limit(1))[0]||null}async delete(e,n,t){let r=t??this.getDb(),s=this.getTable(),a=this.getColumn(this.lookupField),i=this.getSoftDeleteConfig(),l=[eq(a,e)];if(n)for(let[d,c]of Object.entries(n))l.push(eq(this.getColumn(d),c));return i.enabled&&l.push(isNull(this.getColumn(i.field))),i.enabled?(await r.update(s).set({[i.field]:new Date}).where(g(...l)).returning())[0]||null:(await r.delete(s).where(g(...l)).returning())[0]||null}async countRelated(e,n,t,r){let s=r??this.getDb(),a=this.getRelatedTable(t);if(!a)return 0;let i=p(a,t.foreignKey),l=await s.select({count:sql`count(*)`}).from(a).where(eq(i,e));return S(l)}async deleteRelated(e,n,t,r){let s=r??this.getDb(),a=this.getRelatedTable(t);if(!a)return 0;let i=p(a,t.foreignKey);return (await s.delete(a).where(eq(i,e)).returning()).length}async nullifyRelated(e,n,t,r){let s=r??this.getDb(),a=this.getRelatedTable(t);if(!a)return 0;let i=p(a,t.foreignKey);return (await s.update(a).set({[t.foreignKey]:null}).where(eq(i,e)).returning()).length}async handle(){return this.useTransaction?this.getDb().transaction(async e=>{this._tx=e;try{return await super.handle()}finally{this._tx=void 0;}}):super.handle()}},A=class extends ListEndpoint{db;dialect="sqlite";supportsCursorPagination=true;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async list(e){let n=await V({db:this.getDb(),table:this.getTable(),filters:e,dialect:this.dialect,searchFields:this.searchFields,softDeleteConfig:this.getSoftDeleteConfig(),defaultPerPage:this.defaultPerPage,cursorField:this.isCursorPaginationActive()?this.cursorField||"id":void 0}),t={relations:e.options.include||[],scope:this.getRelationScope(e.options.withDeleted)};if(n.cursor){let{items:s,result_info:a}=buildCursorPage({rows:n.records,limit:n.cursor.limit,totalCount:n.totalCount,cursorField:this.cursorField||"id",cursorApplied:n.cursor.applied});return {result:await v(this.getDb(),s,this._meta,t),result_info:a}}let r=await v(this.getDb(),n.records,this._meta,t);return ee(r,n)}},q=class extends RestoreEndpoint{db;useTransaction=false;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async restore(e,n,t){let r=t??this.getDb(),s=this.getTable(),a=this.getColumn(this.lookupField),i=this.getSoftDeleteConfig(),l=[eq(a,e)];if(n)for(let[c,u]of Object.entries(n))l.push(eq(this.getColumn(c),u));return l.push(isNotNull(this.getColumn(i.field))),(await r.update(s).set({[i.field]:null}).where(g(...l)).returning())[0]||null}async handle(){return this.useTransaction?this.getDb().transaction(async e=>{this._tx=e;try{return await super.handle()}finally{this._tx=void 0;}}):super.handle()}};var U=class extends BatchCreateEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}async batchCreate(e){let n=this.getTable(),t=e.map(s=>this.applyManagedInsertFields(s,"drizzle"));return await this.getDb().insert(n).values(t).returning()}},F=class extends BatchUpdateEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async batchUpdate(e){let n=this.getTable(),t=this.getColumn(this.lookupField),r=this.getSoftDeleteConfig(),s=[],a=[];for(let i of e){let l=[eq(t,i.id)];r.enabled&&l.push(isNull(this.getColumn(r.field)));let d=await this.getDb().update(n).set(this.applyManagedUpdateFields(i.data)).where(g(...l)).returning();d[0]?s.push(d[0]):a.push(i.id);}return {updated:s,notFound:a}}},Z=class extends BatchDeleteEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async batchDelete(e){let n=this.getTable(),t=this.getColumn(this.lookupField),r=this.getSoftDeleteConfig(),s=[inArray(t,e)];r.enabled&&s.push(isNull(this.getColumn(r.field)));let a;r.enabled?a=await this.getDb().update(n).set({[r.field]:new Date}).where(g(...s)).returning():a=await this.getDb().delete(n).where(g(...s)).returning();let i=a,l=new Set(i.map(c=>String(c[this.lookupField]))),d=e.filter(c=>!l.has(c));return {deleted:i,notFound:d}}},L=class extends BatchRestoreEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async batchRestore(e){let n=this.getTable(),t=this.getColumn(this.lookupField),r=this.getSoftDeleteConfig(),s=[inArray(t,e),isNotNull(this.getColumn(r.field))],i=await this.getDb().update(n).set({[r.field]:null}).where(g(...s)).returning(),l=new Set(i.map(c=>String(c[this.lookupField]))),d=e.filter(c=>!l.has(c));return {restored:i,notFound:d}}};function Et(o){return o.split(/\s+/).filter(e=>e.length>0)}var N=class extends UpsertEndpoint{db;dialect="sqlite";getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async findExisting(e){return te(this.getDb(),this.getTable(),n=>this.getColumn(n),e,this.getUpsertKeys())}async create(e){let n=this.getTable(),t=this.applyManagedInsertFields(e,"drizzle");return (await this.getDb().insert(n).values(t).returning())[0]}async update(e,n){let t=this.getTable(),r=this._meta.model.primaryKeys[0],s=e[r];return (await this.getDb().update(t).set(this.applyManagedUpdateFields(n)).where(eq(this.getColumn(r),s)).returning())[0]}async nativeUpsert(e,n){let t=this.getTable(),r=this.getUpsertKeys(),s=this._meta.model.primaryKeys[0],a=this.getSoftDeleteConfig(),i=this.getTimestampsConfig(),l=this.applyManagedInsertFields(e,"drizzle"),d={};for(let[C,w]of Object.entries(e))!r.includes(C)&&C!==s&&(this.createOnlyFields?.includes(C)||(d[C]=w));i.enabled&&(d[i.updatedAt]=Date.now());let c=r.map(C=>this.getColumn(C)),u;a.enabled&&(u=isNull(this.getColumn(a.field)));let D=Object.keys(d).length>0?d:{[s]:sql`${this.getColumn(s)}`},f=this.getDb().insert(t).values(l);return this.dialect==="mysql"?{data:(await f.onDuplicateKeyUpdate({set:D}).returning())[0],created:false}:{data:(await f.onConflictDoUpdate({target:c,set:D,where:u}).returning())[0],created:false}}},$=class extends BatchUpsertEndpoint{db;dialect="sqlite";getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async findExisting(e){return te(this.getDb(),this.getTable(),n=>this.getColumn(n),e,this.getUpsertKeys())}async create(e){let n=this.getTable(),t=this.applyManagedInsertFields(e,"drizzle");return (await this.getDb().insert(n).values(t).returning())[0]}async update(e,n){let t=this.getTable(),r=this._meta.model.primaryKeys[0],s=e[r];return (await this.getDb().update(t).set(this.applyManagedUpdateFields(n)).where(eq(this.getColumn(r),s)).returning())[0]}async nativeBatchUpsert(e,n){if(e.length===0)return {items:[],createdCount:0,updatedCount:0,totalCount:0};let t=this.getTable(),r=this.getUpsertKeys(),s=this._meta.model.primaryKeys[0],a=this.getTimestampsConfig(),i=e.map(h=>this.applyManagedInsertFields(h,"drizzle")),l={},d=e[0];for(let h of Object.keys(d))!r.includes(h)&&h!==s&&(this.createOnlyFields?.includes(h)||(l[h]=sql`excluded.${sql.identifier(h)}`));a.enabled&&(l[a.updatedAt]=Date.now());let c=r.map(h=>this.getColumn(h)),u=Object.keys(l).length>0?l:{[s]:sql`${this.getColumn(s)}`},D=this.getDb().insert(t).values(i),f=await(this.dialect==="mysql"?D.onDuplicateKeyUpdate({set:u}):D.onConflictDoUpdate({target:c,set:u})).returning();return {items:f.map((h,C)=>({data:h,created:false,index:C})),createdCount:0,updatedCount:f.length,totalCount:f.length}}},ne=class extends BulkPatchEndpoint{db;dialect="sqlite";getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}buildConditions(e){let n=this.getTable(),t=[];for(let s of e.filters){let a=W(n,s,this.dialect);a&&t.push(a);}let r=this.getSoftDeleteConfig();return r.enabled&&t.push(isNull(this.getColumn(r.field))),t}async countMatching(e){let n=this.buildConditions(e),t=await this.getDb().select({count:sql`count(*)`}).from(this.getTable()).where(g(...n));return S(t)}async applyPatch(e,n){let t=this.buildConditions(n),r=await this.getDb().update(this.getTable()).set(this.applyManagedUpdateFields(e)).where(g(...t)).returning();return {updated:r.length,records:r}}},re=class extends VersionHistoryEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async recordExists(e){let n=this.getTable(),t=await this.getDb().select({count:sql`count(*)`}).from(n).where(eq(this.getColumn("id"),e));return S(t)>0}},oe=class extends VersionReadEndpoint{},se=class extends VersionCompareEndpoint{},ie=class extends VersionRollbackEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async rollback(e,n,t){let r=this.getTable(),s=this.getVersioningConfig().field;return (await this.getDb().update(r).set({...n,[s]:t}).where(eq(this.getColumn("id"),e)).returning())[0]}},ae=class extends AggregateEndpoint{db;dialect="sqlite";getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async aggregate(e){let n=this.getTable(),t=[],r=this.getSoftDeleteConfig();if(r.enabled){let{query:i}=await this.getValidatedData();i?.withDeleted===true||i?.withDeleted==="true"||t.push(isNull(this.getColumn(r.field)));}if(e.filters)for(let[i,l]of Object.entries(e.filters))if(typeof l=="object"&&l!==null)for(let[d,c]of Object.entries(l)){if(!isFilterOperator(d)){t.push(sql`1 = 0`);continue}let u=W(n,{field:i,operator:d,value:c},this.dialect);u&&t.push(u);}else t.push(eq(this.getColumn(i),l));let s=t.length>0?g(...t):void 0,a=await this.getDb().select().from(n).where(s);return computeAggregations(a,e)}},K=class extends SearchEndpoint{db;dialect="sqlite";getDb(){return b(this)}useNativeSearch=false;vectorColumn;vectorConfig="english";getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async search(e,n){let t=this.getTable(),r=[],s=this.getSearchableFields(),a=e.fields||Object.keys(s);if(this.useNativeSearch&&this.vectorColumn){let w=this.getColumn(this.vectorColumn),M=e.mode==="phrase"?sql`phraseto_tsquery(${this.vectorConfig}, ${e.query})`:e.mode==="all"?sql`plainto_tsquery(${this.vectorConfig}, ${e.query})`:sql`to_tsquery(${this.vectorConfig}, ${e.query.split(/\s+/).join(" | ")})`;r.push(sql`${w} @@ ${M}`);}else {let w=(M,E)=>{try{let T=this.getColumn(M);return B(sql`CAST(${T} AS TEXT)`,E,this.dialect)}catch{return}};if(e.mode==="all"){let M=Et(e.query);if(M.length>0){let E=[];for(let T of M){let Y=a.map(Q=>w(Q,T)).filter(Q=>Q!==void 0);Y.length>0&&E.push(J(...Y));}E.length>0&&r.push(g(...E));}}else {let M=a.map(E=>w(E,e.query)).filter(E=>E!==void 0);M.length>0&&r.push(J(...M));}}let i=await V({db:this.getDb(),table:t,filters:n,dialect:this.dialect,softDeleteConfig:this.getSoftDeleteConfig(),defaultPerPage:this.defaultPerPage,extraConditions:r}),l=i.records,d=i.totalCount,c=e.mode==="all"?{...e,mode:"any"}:e,u=searchInMemory(l,c,s),D={relations:n.options.include||[]},f=u.map(w=>w.item),h=await v(this.getDb(),f,this._meta,D);return {items:u.map((w,M)=>({...w,item:h[M]})),totalCount:d}}},le=class extends ExportEndpoint{db;dialect="sqlite";getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async list(e){let n=await V({db:this.getDb(),table:this.getTable(),filters:e,dialect:this.dialect,searchFields:this.searchFields,softDeleteConfig:this.getSoftDeleteConfig(),defaultPerPage:this.defaultPerPage}),t={relations:e.options.include||[]},r=await v(this.getDb(),n.records,this._meta,t);return ee(r,n)}},de=class extends ImportEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}async findExisting(e){return te(this.getDb(),this.getTable(),n=>this.getColumn(n),e,this.getUpsertKeys())}async create(e){let n=this.getTable(),t=this.applyManagedInsertFields(e,"drizzle");return (await this.getDb().insert(n).values(t).returning())[0]}async update(e,n){let t=this.getTable(),r=this._meta.model.primaryKeys[0],s=e[r];return (await this.getDb().update(t).set(this.applyManagedUpdateFields(n)).where(eq(this.getColumn(r),s)).returning())[0]}},ce=class extends CloneEndpoint{db;getDb(){return b(this)}getTable(){return z(this._meta)}getColumn(e){return p(this.getTable(),e)}generateId(){return crypto.randomUUID()}async findSource(e,n){let t=this.getTable(),r=this.getColumn(this.lookupField),s=this.getSoftDeleteConfig(),a=[eq(r,e)];if(n)for(let[l,d]of Object.entries(n))a.push(eq(this.getColumn(l),d));s.enabled&&a.push(isNull(this.getColumn(s.field)));let i=await this.getDb().select().from(t).where(g(...a)).limit(1);return i[0]?i[0]:null}async createClone(e){let n=this.getTable(),t=this.applyManagedInsertFields(e,"drizzle",()=>this.generateId());return (await this.getDb().insert(n).values(t).returning())[0]}};function yn(o,e,n){let t=n?.dialect??"sqlite";return {Create:class extends j{_meta=e;db=o},Read:class extends P{_meta=e;db=o},Update:class extends I{_meta=e;db=o},Delete:class extends _{_meta=e;db=o},List:class extends A{_meta=e;db=o;dialect=t},Restore:class extends q{_meta=e;db=o},Upsert:class extends N{_meta=e;db=o;dialect=t},Search:class extends K{_meta=e;db=o;dialect=t},BatchCreate:class extends U{_meta=e;db=o},BatchUpdate:class extends F{_meta=e;db=o},BatchDelete:class extends Z{_meta=e;db=o},BatchRestore:class extends L{_meta=e;db=o},BatchUpsert:class extends ${_meta=e;db=o;dialect=t}}}var ge=null,Re=false,pe=null;async function ze(){if(Re){if(pe)throw pe;return ge}Re=true;try{return ge=await import('drizzle-zod'),ge}catch{throw pe=new Error("drizzle-zod is not installed. Please install it: npm install drizzle-zod"),pe}}async function Rt(o,e){return (await ze()).createSelectSchema(o,e)}async function Tt(o,e){return (await ze()).createInsertSchema(o,e)}async function xt(o,e){let n=await ze();return n.createUpdateSchema?n.createUpdateSchema(o,e):n.createInsertSchema(o,e).partial()}async function kt(o,e){let n=await ze(),t=e?.coerceDates!==false,r=t?Bt(o):new Set,s=n.createSelectSchema(o,e?.selectRefine),a=n.createInsertSchema(o,e?.insertRefine),i;return n.createUpdateSchema?i=n.createUpdateSchema(o,e?.updateRefine):i=n.createInsertSchema(o,e?.updateRefine).partial(),t&&r.size>0&&(a=Te(a,r),i=Te(i,r)),{select:s,insert:a,update:i}}function Ot(){return ge!==null}var vt=z$1.preprocess(o=>{if(o instanceof Date)return o;if(typeof o=="string"){let e=new Date(o);if(!isNaN(e.getTime()))return e}return o},z$1.date()),St=z$1.preprocess(o=>{if(o==null)return null;if(o instanceof Date)return o;if(typeof o=="string"){let e=new Date(o);if(!isNaN(e.getTime()))return e}return o},z$1.date().nullable());function Bt(o){let e=new Set,n=o;for(let[t,r]of Object.entries(n)){if(t==="_"||t==="$inferInsert"||t==="$inferSelect")continue;let s=r;if(!s||typeof s!="object")continue;let a=String(s.dataType??"").toLowerCase(),i=String(s.columnType??"").toLowerCase(),l=s.config,d=String(l?.dataType??"").toLowerCase();(a.includes("timestamp")||a.includes("date")||a.includes("datetime")||i.includes("pgtimestamp")||i.includes("pgdate")||i.includes("mysqltimestamp")||i.includes("mysqldate")||i.includes("sqlitetimestamp")||d.includes("timestamp")||d.includes("date"))&&e.add(t);}return e}function Te(o,e){if(e.size===0)return o;let n=o.shape,t={};for(let[r,s]of Object.entries(n))if(e.has(r)){let a=s.isOptional?.()??false,i=s.isNullable?.()??false,l=vt;(i||a)&&(l=St),a&&(l=l.optional()),t[r]=l;}else t[r]=s;return z$1.object(t)}var Ln={CreateEndpoint:j,ListEndpoint:A,ReadEndpoint:P,UpdateEndpoint:I,DeleteEndpoint:_,RestoreEndpoint:q,BatchCreateEndpoint:U,BatchUpdateEndpoint:F,BatchDeleteEndpoint:Z,BatchRestoreEndpoint:L,BatchUpsertEndpoint:$,SearchEndpoint:K,AggregateEndpoint:ae,ExportEndpoint:le,ImportEndpoint:de,UpsertEndpoint:N,CloneEndpoint:ce,BulkPatchEndpoint:ne,VersionHistoryEndpoint:re,VersionReadEndpoint:oe,VersionCompareEndpoint:se,VersionRollbackEndpoint:ie};export{$e as DRIZZLE_DIALECTS,Ln as DrizzleAdapters,ae as DrizzleAggregateEndpoint,U as DrizzleBatchCreateEndpoint,Z as DrizzleBatchDeleteEndpoint,L as DrizzleBatchRestoreEndpoint,F as DrizzleBatchUpdateEndpoint,$ as DrizzleBatchUpsertEndpoint,ne as DrizzleBulkPatchEndpoint,ce as DrizzleCloneEndpoint,j as DrizzleCreateEndpoint,_ as DrizzleDeleteEndpoint,le as DrizzleExportEndpoint,de as DrizzleImportEndpoint,A as DrizzleListEndpoint,P as DrizzleReadEndpoint,q as DrizzleRestoreEndpoint,K as DrizzleSearchEndpoint,I as DrizzleUpdateEndpoint,N as DrizzleUpsertEndpoint,se as DrizzleVersionCompareEndpoint,re as DrizzleVersionHistoryEndpoint,oe as DrizzleVersionReadEndpoint,ie as DrizzleVersionRollbackEndpoint,v as batchLoadDrizzleRelations,W as buildWhereCondition,G as cast,yn as createDrizzleCrud,kt as createDrizzleSchemas,Tt as createInsertSchema,Rt as createSelectSchema,xt as createUpdateSchema,p as getColumn,z as getTable,Ot as isDrizzleZodAvailable,Ke as loadDrizzleRelation,me as loadDrizzleRelations,S as readCount,B as substringMatch};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hono-crud/drizzle",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Drizzle ORM CRUD adapter for hono-crud",
5
5
  "author": "Kauan Guesser <contato@kauan.net>",
6
6
  "license": "MIT",
@@ -42,22 +42,21 @@
42
42
  "publishConfig": {
43
43
  "access": "public"
44
44
  },
45
- "dependencies": {
46
- "hono-crud": "0.13.13"
47
- },
48
45
  "peerDependencies": {
49
46
  "drizzle-orm": ">=0.30.0",
50
47
  "drizzle-zod": ">=0.8.0",
51
48
  "hono": ">=4.11.7 <5",
52
- "zod": ">=4.0.0"
49
+ "zod": ">=4.0.0",
50
+ "hono-crud": "^0.13.15"
53
51
  },
54
52
  "devDependencies": {
55
53
  "drizzle-orm": "^0.45.1",
56
54
  "drizzle-zod": "^0.8.3",
57
- "hono": "^4.11.4",
55
+ "hono": "^4.11.7",
58
56
  "tsup": "^8.4.0",
59
57
  "typescript": "^5.8.3",
60
- "zod": "^4.3.5"
58
+ "zod": "^4.3.5",
59
+ "hono-crud": "0.13.15"
61
60
  },
62
61
  "peerDependenciesMeta": {
63
62
  "drizzle-zod": {