@hono-crud/drizzle 0.1.10 → 0.1.12

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,62 @@
1
1
  # @hono-crud/drizzle
2
2
 
3
+ ## 0.1.12
4
+
5
+ ### Patch Changes
6
+
7
+ - 376d8d8: Adapter correctness batch:
8
+
9
+ - **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.
10
+ - **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.
11
+ - **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.
12
+ - **BatchDelete** responses go through the full finalize pipeline (computed fields, serialization profile, transform) instead of serializer-only.
13
+ - **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.
14
+ - **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).
15
+ - **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`.
16
+ - **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.
17
+
18
+ - 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.
19
+
20
+ - **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.
21
+ - **`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.
22
+ - **`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.
23
+ - **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.
24
+ - 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.
25
+
26
+ - 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.
27
+ - 7a7808d: Verb & sugar-surface parity batch:
28
+
29
+ - **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.
30
+ - **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.
31
+ - **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.
32
+ - **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)`.
33
+ - **`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'`.
34
+ - **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).
35
+ - **`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.
36
+ - **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.
37
+ - **Env-generic config middlewares**: `EndpointsConfig<M, E>` threads `E` so per-endpoint `middlewares` are `MiddlewareHandler<E>[]`, matching the functional and builder surfaces.
38
+
39
+ ## 0.1.11
40
+
41
+ ### Patch Changes
42
+
43
+ - 66f789c: Naming + docs sweep (breaking, patch): one name per role across the whole library, and the docs now document the code that exists.
44
+
45
+ - Middleware factories: `idempotency()` → `createIdempotencyMiddleware()`. (`multiTenant()`/`apiVersion()` keep their feature-named forms — each anchors an accessor family sharing its prefix; the doctrine in CLAUDE.md documents the rule and the exceptions.)
46
+ - Surface packages are Hono-idiomatic factories you mount yourself: swagger's `setupSwaggerUI`/`setupReDoc`/`setupDocsIndex` (app mutators) become `swaggerUI()`/`redocUI()`/`docsIndex()` returning `MiddlewareHandler` with `SwaggerUIConfig`/`RedocUIConfig`/`DocsIndexConfig` (adopting scalar's `specUrl`/`pageTitle` vocabulary; `UIOptions` deleted); scalar's `setupScalar` is deleted (`app.get(path, scalarUI(config))`); health's `createHealthEndpoints`/`createHealthHandler` collapse into `createHealthRoutes(config): Hono` mounted via `app.route()`.
47
+ - The word "Versioning" now means record history only: HTTP negotiation renames to `ApiVersioningConfig` (was `VersioningMiddlewareConfig`), `ApiVersionStrategy`, `ApiVersionTransformer`, `apiVersionedResponse()`.
48
+ - Multi-tenant has one source-of-truth union: new exported `TenantIdSource` owned by the model-level `MultiTenantConfig`; the middleware config derives from it and renames to `MultiTenantMiddlewareConfig` (was `*Options`). Runtime defaults unchanged.
49
+ - `CrudMcpOptions` → `CrudMcpConfig` per the now-documented Config-vs-Options rule.
50
+ - Rate-limit key prefix unified: one exported `DEFAULT_RATE_LIMIT_KEY_PREFIX = 'rl'`; the KV and Redis storage prefixes no longer add their own divergent defaults ('rl:'/'ratelimit:'), so all backends build identical keys. In-flight rate-limit windows under old prefixes expire naturally.
51
+ - Vestigial `RouterOptions.base`/`docs_url`/`redoc_url` deleted (UI paths live in the swagger/scalar packages; `openapi_url` stays).
52
+ - One API-key hasher: the canonical `hashAPIKey` moves to `auth/hash.ts` and `defaultHashAPIKey` is now an alias of it — hashing is byte-identical, stored keys keep matching.
53
+ - Sorting aliases removed from the functional config: `orderByFields`/`defaultOrderBy`/`defaultOrderDirection` and `SortingConfig.defaultDirection` are gone — canonical `sortFields`/`defaultSort`/`defaultOrder` only.
54
+ - Drizzle's stale 5-verb `DrizzleAdapters` bundle in factory.ts is deleted; the 17-entry bundle in the package barrel is the only one, mirroring prisma and memory.
55
+ - Docs: CLAUDE.md/AGENTS.md gain the naming doctrine and a corrected Drizzle Adapter Pattern section using the real type names (`DrizzleDatabaseConstraint`, `Database<Row>`, `QueryBuilder<Row>`, `cast<Row>()`); all docs/READMEs/examples migrated to the new names and call patterns.
56
+
57
+ - Updated dependencies [66f789c]
58
+ - hono-crud@0.13.13
59
+
3
60
  ## 0.1.10
4
61
 
5
62
  ### 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,
19
- type DrizzleDatabase,
17
+ DrizzleReadEndpoint,
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 `Drizzle*Endpoint` classes, `createDrizzleCrud`, `createDrizzleSchemas`, and the `DrizzleDatabase` 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
 
@@ -123,7 +123,7 @@ type DrizzleDialect = (typeof DRIZZLE_DIALECTS)[number];
123
123
  *
124
124
  * @example
125
125
  * ```ts
126
- * import { DrizzleEnv } from 'hono-crud/adapters/drizzle';
126
+ * import { DrizzleEnv } from '@hono-crud/drizzle';
127
127
  *
128
128
  * type AppEnv = DrizzleEnv<typeof db>;
129
129
  *
@@ -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;
@@ -847,7 +904,7 @@ interface CreateDrizzleCrudOptions {
847
904
  *
848
905
  * @example
849
906
  * ```ts
850
- * import { createDrizzleCrud } from 'hono-crud/adapters/drizzle';
907
+ * import { createDrizzleCrud } from '@hono-crud/drizzle';
851
908
  *
852
909
  * const projectMeta = defineMeta({ model: ProjectModel, fields: projectSchemas.insert });
853
910
  * const Project = createDrizzleCrud(db, projectMeta, { dialect: 'pg' });
@@ -878,7 +935,7 @@ declare function createDrizzleCrud<M extends MetaInput, E extends Env = Env>(db:
878
935
  * @example
879
936
  * ```ts
880
937
  * import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
881
- * import { createDrizzleSchemas } from 'hono-crud/adapters/drizzle';
938
+ * import { createDrizzleSchemas } from '@hono-crud/drizzle';
882
939
  *
883
940
  * const users = pgTable('users', {
884
941
  * id: uuid('id').primaryKey().defaultRandom(),
@@ -902,7 +959,7 @@ declare function createDrizzleCrud<M extends MetaInput, E extends Env = Env>(db:
902
959
  * @example
903
960
  * ```ts
904
961
  * import { users } from './schema';
905
- * import { createSelectSchema } from 'hono-crud/adapters/drizzle';
962
+ * import { createSelectSchema } from '@hono-crud/drizzle';
906
963
  *
907
964
  * const UserSchema = await createSelectSchema(users);
908
965
  * type User = z.infer<typeof UserSchema>;
@@ -920,7 +977,7 @@ declare function createSelectSchema<T extends DrizzleTable>(table: T, refine?: R
920
977
  * @example
921
978
  * ```ts
922
979
  * import { users } from './schema';
923
- * import { createInsertSchema } from 'hono-crud/adapters/drizzle';
980
+ * import { createInsertSchema } from '@hono-crud/drizzle';
924
981
  *
925
982
  * const CreateUserSchema = await createInsertSchema(users);
926
983
  * type CreateUser = z.infer<typeof CreateUserSchema>;
@@ -941,7 +998,7 @@ declare function createInsertSchema<T extends DrizzleTable>(table: T, refine?: R
941
998
  * @example
942
999
  * ```ts
943
1000
  * import { users } from './schema';
944
- * import { createUpdateSchema } from 'hono-crud/adapters/drizzle';
1001
+ * import { createUpdateSchema } from '@hono-crud/drizzle';
945
1002
  *
946
1003
  * const UpdateUserSchema = await createUpdateSchema(users);
947
1004
  * type UpdateUser = z.infer<typeof UpdateUserSchema>;
@@ -981,7 +1038,7 @@ interface DrizzleSchemas {
981
1038
  * @example
982
1039
  * ```ts
983
1040
  * import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
984
- * import { createDrizzleSchemas, defineModel, defineMeta } from 'hono-crud/adapters/drizzle';
1041
+ * import { createDrizzleSchemas, defineModel, defineMeta } from '@hono-crud/drizzle';
985
1042
  * import { z } from 'zod';
986
1043
  *
987
1044
  * const users = pgTable('users', {
@@ -1023,8 +1080,8 @@ declare function isDrizzleZodAvailable(): boolean;
1023
1080
  /**
1024
1081
  * Drizzle adapter bundle for use with `defineEndpoints`.
1025
1082
  *
1026
- * Populates the 11 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 A=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()}},q=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 A{_meta=e;db=o},Read:class extends q{_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:A,ListEndpoint:F,ReadEndpoint:q,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,A as DrizzleCreateEndpoint,Z as DrizzleDeleteEndpoint,G as DrizzleExportEndpoint,H as DrizzleImportEndpoint,F as DrizzleListEndpoint,q 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||[]};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.10",
3
+ "version": "0.1.12",
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.12"
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.14"
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.14"
61
60
  },
62
61
  "peerDependenciesMeta": {
63
62
  "drizzle-zod": {