hono-crud 0.3.2 → 0.4.0

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
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
13
  %b
14
14
  %b
15
15
  %b
16
+ %b
16
17
  ## [0.1.0] - 2025-01-29
17
18
 
18
19
  ### Added
@@ -42,3 +43,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
42
43
  [0.3.0]: https://github.com/kshdotdev/hono-crud/compare/v0.2.0...v0.3.0
43
44
  [0.3.1]: https://github.com/kshdotdev/hono-crud/compare/v0.3.0...v0.3.1
44
45
  [0.3.2]: https://github.com/kshdotdev/hono-crud/compare/v0.3.1...v0.3.2
46
+ [0.4.0]: https://github.com/kshdotdev/hono-crud/compare/v0.3.2...v0.4.0
@@ -1,8 +1,8 @@
1
1
  import { Table, SQL, Column } from 'drizzle-orm';
2
2
  import { M as MetaInput, I as IncludeOptions, F as FilterCondition, g as RelationConfig, h as NestedUpdateInput, i as NestedWriteResult, L as ListFilters, P as PaginatedResult, j as AggregateOptions, k as AggregateResult, S as SearchOptions, l as SearchResult } from '../../types-CA784ZtV.js';
3
3
  import { Env } from 'hono';
4
- import { C as CreateEndpoint, M as ModelObject, R as ReadEndpoint, U as UpdateEndpoint, D as DeleteEndpoint, L as ListEndpoint, a as RestoreEndpoint, B as BatchCreateEndpoint, b as BatchDeleteEndpoint, c as BatchRestoreEndpoint, d as BatchUpdateEndpoint, e as BatchUpdateItem, A as AggregateEndpoint, f as BatchUpsertEndpoint, g as UpsertEndpoint, E as ExportEndpoint, I as ImportEndpoint, S as SearchEndpoint, V as VersionCompareEndpoint, h as VersionHistoryEndpoint, i as VersionReadEndpoint, j as VersionRollbackEndpoint } from '../../import-B7KS4Wvh.js';
5
- import { A as AdapterBundle } from '../../index-5BEg0ty5.js';
4
+ import { C as CreateEndpoint, M as ModelObject, R as ReadEndpoint, U as UpdateEndpoint, D as DeleteEndpoint, L as ListEndpoint, a as RestoreEndpoint, B as BatchCreateEndpoint, b as BatchDeleteEndpoint, c as BatchRestoreEndpoint, d as BatchUpdateEndpoint, e as BatchUpdateItem, A as AggregateEndpoint, f as BatchUpsertEndpoint, g as UpsertEndpoint, E as ExportEndpoint, I as ImportEndpoint, S as SearchEndpoint, V as VersionCompareEndpoint, h as VersionHistoryEndpoint, i as VersionReadEndpoint, j as VersionRollbackEndpoint } from '../../import-BZ05PJMa.js';
5
+ import { A as AdapterBundle } from '../../index-bSnRLgcs.js';
6
6
  import { z } from 'zod';
7
7
  import '@hono/zod-openapi';
8
8
  import '../../route-DwSID3du.js';
@@ -731,7 +731,7 @@ declare const DrizzleAdapters: AdapterBundle;
731
731
  * createdAt: timestamp('created_at').defaultNow(),
732
732
  * });
733
733
  *
734
- * const { select: UserSchema, insert: CreateUserSchema } = createDrizzleSchemas(users);
734
+ * const { select: UserSchema, insert: CreateUserSchema } = await createDrizzleSchemas(users);
735
735
  * ```
736
736
  */
737
737
 
@@ -751,36 +751,36 @@ type DrizzleTable = {
751
751
  *
752
752
  * @param table - Drizzle table definition
753
753
  * @param refine - Optional refinements for specific columns
754
- * @returns Zod schema for the table's select type
754
+ * @returns Promise resolving to a Zod schema for the table's select type
755
755
  *
756
756
  * @example
757
757
  * ```ts
758
758
  * import { users } from './schema';
759
759
  * import { createSelectSchema } from 'hono-crud/adapters/drizzle';
760
760
  *
761
- * const UserSchema = createSelectSchema(users);
761
+ * const UserSchema = await createSelectSchema(users);
762
762
  * type User = z.infer<typeof UserSchema>;
763
763
  * ```
764
764
  */
765
- declare function createSelectSchema<T extends DrizzleTable>(table: T, refine?: Record<string, z.ZodTypeAny>): z.ZodObject<Record<string, z.ZodTypeAny>>;
765
+ declare function createSelectSchema<T extends DrizzleTable>(table: T, refine?: Record<string, z.ZodTypeAny>): Promise<z.ZodObject<Record<string, z.ZodTypeAny>>>;
766
766
  /**
767
767
  * Re-export createInsertSchema from drizzle-zod.
768
768
  * Creates a Zod schema for INSERT queries (columns with defaults become optional).
769
769
  *
770
770
  * @param table - Drizzle table definition
771
771
  * @param refine - Optional refinements for specific columns
772
- * @returns Zod schema for the table's insert type
772
+ * @returns Promise resolving to a Zod schema for the table's insert type
773
773
  *
774
774
  * @example
775
775
  * ```ts
776
776
  * import { users } from './schema';
777
777
  * import { createInsertSchema } from 'hono-crud/adapters/drizzle';
778
778
  *
779
- * const CreateUserSchema = createInsertSchema(users);
779
+ * const CreateUserSchema = await createInsertSchema(users);
780
780
  * type CreateUser = z.infer<typeof CreateUserSchema>;
781
781
  * ```
782
782
  */
783
- declare function createInsertSchema<T extends DrizzleTable>(table: T, refine?: Record<string, z.ZodTypeAny>): z.ZodObject<Record<string, z.ZodTypeAny>>;
783
+ declare function createInsertSchema<T extends DrizzleTable>(table: T, refine?: Record<string, z.ZodTypeAny>): Promise<z.ZodObject<Record<string, z.ZodTypeAny>>>;
784
784
  /**
785
785
  * Re-export createUpdateSchema from drizzle-zod (if available).
786
786
  * Creates a Zod schema for UPDATE queries (all columns become optional).
@@ -790,18 +790,18 @@ declare function createInsertSchema<T extends DrizzleTable>(table: T, refine?: R
790
790
  *
791
791
  * @param table - Drizzle table definition
792
792
  * @param refine - Optional refinements for specific columns
793
- * @returns Zod schema for the table's update type
793
+ * @returns Promise resolving to a Zod schema for the table's update type
794
794
  *
795
795
  * @example
796
796
  * ```ts
797
797
  * import { users } from './schema';
798
798
  * import { createUpdateSchema } from 'hono-crud/adapters/drizzle';
799
799
  *
800
- * const UpdateUserSchema = createUpdateSchema(users);
800
+ * const UpdateUserSchema = await createUpdateSchema(users);
801
801
  * type UpdateUser = z.infer<typeof UpdateUserSchema>;
802
802
  * ```
803
803
  */
804
- declare function createUpdateSchema<T extends DrizzleTable>(table: T, refine?: Record<string, z.ZodTypeAny>): z.ZodObject<Record<string, z.ZodTypeAny>>;
804
+ declare function createUpdateSchema<T extends DrizzleTable>(table: T, refine?: Record<string, z.ZodTypeAny>): Promise<z.ZodObject<Record<string, z.ZodTypeAny>>>;
805
805
  /**
806
806
  * Result of createDrizzleSchemas helper.
807
807
  */
@@ -830,7 +830,7 @@ interface DrizzleSchemas {
830
830
  * @param options.selectRefine - Refinements for select schema
831
831
  * @param options.updateRefine - Refinements for update schema
832
832
  * @param options.coerceDates - Whether to coerce date strings to Date objects (default: true)
833
- * @returns Object containing select, insert, and update schemas
833
+ * @returns Promise resolving to object containing select, insert, and update schemas
834
834
  *
835
835
  * @example
836
836
  * ```ts
@@ -845,7 +845,7 @@ interface DrizzleSchemas {
845
845
  * });
846
846
  *
847
847
  * // Generate schemas from table
848
- * const schemas = createDrizzleSchemas(users, {
848
+ * const schemas = await createDrizzleSchemas(users, {
849
849
  * insertRefine: {
850
850
  * email: z.string().email(), // Add email validation
851
851
  * },
@@ -866,24 +866,12 @@ declare function createDrizzleSchemas<T extends DrizzleTable>(table: T, options?
866
866
  updateRefine?: Record<string, z.ZodTypeAny>;
867
867
  /** Whether to coerce date strings to Date objects. Default: true */
868
868
  coerceDates?: boolean;
869
- }): DrizzleSchemas;
870
- /**
871
- * Async version of createDrizzleSchemas that handles lazy loading.
872
- * Use this if you're not sure drizzle-zod is already loaded.
873
- *
874
- * @param table - Drizzle table definition
875
- * @param options - Optional configuration
876
- * @returns Promise resolving to schemas object
877
- */
878
- declare function createDrizzleSchemasAsync<T extends DrizzleTable>(table: T, options?: {
879
- insertRefine?: Record<string, z.ZodTypeAny>;
880
- selectRefine?: Record<string, z.ZodTypeAny>;
881
- updateRefine?: Record<string, z.ZodTypeAny>;
882
869
  }): Promise<DrizzleSchemas>;
883
870
  /**
884
- * Checks if drizzle-zod is available.
885
- * @returns true if drizzle-zod can be imported
871
+ * Checks if drizzle-zod is available (from cache only).
872
+ * Returns true only if drizzle-zod has been successfully loaded before.
873
+ * @returns true if drizzle-zod has been loaded
886
874
  */
887
875
  declare function isDrizzleZodAvailable(): boolean;
888
876
 
889
- export { type Database, DrizzleAdapters, DrizzleAggregateEndpoint, DrizzleBatchCreateEndpoint, DrizzleBatchDeleteEndpoint, DrizzleBatchRestoreEndpoint, DrizzleBatchUpdateEndpoint, DrizzleBatchUpsertEndpoint, DrizzleCreateEndpoint, type DrizzleCrudClasses, type DrizzleDB, type DrizzleDatabase, type DrizzleDatabaseConstraint, DrizzleDeleteEndpoint, type DrizzleEnv, DrizzleExportEndpoint, DrizzleImportEndpoint, DrizzleListEndpoint, DrizzleReadEndpoint, DrizzleRestoreEndpoint, type DrizzleSchemas, DrizzleSearchEndpoint, DrizzleUpdateEndpoint, DrizzleUpsertEndpoint, DrizzleVersionCompareEndpoint, DrizzleVersionHistoryEndpoint, DrizzleVersionReadEndpoint, DrizzleVersionRollbackEndpoint, type QueryBuilder, batchLoadDrizzleRelations, buildWhereCondition, cast, createDrizzleCrud, createDrizzleSchemas, createDrizzleSchemasAsync, createInsertSchema, createSelectSchema, createUpdateSchema, getColumn, getTable, isDrizzleZodAvailable, loadDrizzleRelation, loadDrizzleRelations };
877
+ export { type Database, DrizzleAdapters, DrizzleAggregateEndpoint, DrizzleBatchCreateEndpoint, DrizzleBatchDeleteEndpoint, DrizzleBatchRestoreEndpoint, DrizzleBatchUpdateEndpoint, DrizzleBatchUpsertEndpoint, DrizzleCreateEndpoint, type DrizzleCrudClasses, type DrizzleDB, type DrizzleDatabase, type DrizzleDatabaseConstraint, DrizzleDeleteEndpoint, type DrizzleEnv, DrizzleExportEndpoint, DrizzleImportEndpoint, DrizzleListEndpoint, DrizzleReadEndpoint, DrizzleRestoreEndpoint, type DrizzleSchemas, DrizzleSearchEndpoint, DrizzleUpdateEndpoint, DrizzleUpsertEndpoint, DrizzleVersionCompareEndpoint, DrizzleVersionHistoryEndpoint, DrizzleVersionReadEndpoint, DrizzleVersionRollbackEndpoint, type QueryBuilder, batchLoadDrizzleRelations, buildWhereCondition, cast, createDrizzleCrud, createDrizzleSchemas, createInsertSchema, createSelectSchema, createUpdateSchema, getColumn, getTable, isDrizzleZodAvailable, loadDrizzleRelation, loadDrizzleRelations };
@@ -1,10 +1,9 @@
1
- import { CreateEndpoint, ReadEndpoint, UpdateEndpoint, DeleteEndpoint, ListEndpoint, RestoreEndpoint, BatchCreateEndpoint, BatchUpdateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, UpsertEndpoint, BatchUpsertEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionCompareEndpoint, VersionRollbackEndpoint, AggregateEndpoint, computeAggregations, SearchEndpoint, searchInMemory, ExportEndpoint, ImportEndpoint } from '../../chunk-LCRNKT65.js';
1
+ import { CreateEndpoint, ReadEndpoint, UpdateEndpoint, DeleteEndpoint, ListEndpoint, RestoreEndpoint, BatchCreateEndpoint, BatchUpdateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, UpsertEndpoint, BatchUpsertEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionCompareEndpoint, VersionRollbackEndpoint, AggregateEndpoint, computeAggregations, SearchEndpoint, searchInMemory, ExportEndpoint, ImportEndpoint } from '../../chunk-T6J4NSMC.js';
2
2
  import '../../chunk-CDCGYMC6.js';
3
3
  import '../../chunk-U3SUUXJK.js';
4
4
  import { getLogger } from '../../chunk-FST5S5UY.js';
5
5
  import { getTableColumns, eq, inArray, between, isNull, isNotNull, ilike, like, notInArray, lte, lt, gte, gt, ne, and, sql, or, desc, asc } from 'drizzle-orm';
6
6
  import { z } from 'zod';
7
- import { createRequire } from 'module';
8
7
 
9
8
  function cast(instance) {
10
9
  return instance;
@@ -1520,27 +1519,10 @@ var DrizzleAdapters = {
1520
1519
  UpdateEndpoint: DrizzleUpdateEndpoint,
1521
1520
  DeleteEndpoint: DrizzleDeleteEndpoint
1522
1521
  };
1523
- var require2 = createRequire(import.meta.url);
1524
1522
  var _drizzleZod = null;
1525
1523
  var _loadAttempted = false;
1526
1524
  var _loadError = null;
1527
- function tryLoadDrizzleZod() {
1528
- if (_loadAttempted) {
1529
- if (_loadError) throw _loadError;
1530
- return _drizzleZod;
1531
- }
1532
- _loadAttempted = true;
1533
- try {
1534
- _drizzleZod = require2("drizzle-zod");
1535
- return _drizzleZod;
1536
- } catch {
1537
- _loadError = new Error(
1538
- "drizzle-zod is not installed. Please install it: npm install drizzle-zod"
1539
- );
1540
- throw _loadError;
1541
- }
1542
- }
1543
- async function loadDrizzleZodAsync() {
1525
+ async function ensureDrizzleZod() {
1544
1526
  if (_loadAttempted) {
1545
1527
  if (_loadError) throw _loadError;
1546
1528
  return _drizzleZod;
@@ -1556,24 +1538,24 @@ async function loadDrizzleZodAsync() {
1556
1538
  throw _loadError;
1557
1539
  }
1558
1540
  }
1559
- function createSelectSchema(table, refine) {
1560
- const drizzleZod = tryLoadDrizzleZod();
1541
+ async function createSelectSchema(table, refine) {
1542
+ const drizzleZod = await ensureDrizzleZod();
1561
1543
  return drizzleZod.createSelectSchema(table, refine);
1562
1544
  }
1563
- function createInsertSchema(table, refine) {
1564
- const drizzleZod = tryLoadDrizzleZod();
1545
+ async function createInsertSchema(table, refine) {
1546
+ const drizzleZod = await ensureDrizzleZod();
1565
1547
  return drizzleZod.createInsertSchema(table, refine);
1566
1548
  }
1567
- function createUpdateSchema(table, refine) {
1568
- const drizzleZod = tryLoadDrizzleZod();
1549
+ async function createUpdateSchema(table, refine) {
1550
+ const drizzleZod = await ensureDrizzleZod();
1569
1551
  if (drizzleZod.createUpdateSchema) {
1570
1552
  return drizzleZod.createUpdateSchema(table, refine);
1571
1553
  }
1572
1554
  const insertSchema = drizzleZod.createInsertSchema(table, refine);
1573
1555
  return insertSchema.partial();
1574
1556
  }
1575
- function createDrizzleSchemas(table, options) {
1576
- const drizzleZod = tryLoadDrizzleZod();
1557
+ async function createDrizzleSchemas(table, options) {
1558
+ const drizzleZod = await ensureDrizzleZod();
1577
1559
  const shouldCoerceDates = options?.coerceDates !== false;
1578
1560
  const dateColumns = shouldCoerceDates ? getDateColumns(table) : /* @__PURE__ */ new Set();
1579
1561
  const select = drizzleZod.createSelectSchema(table, options?.selectRefine);
@@ -1590,17 +1572,8 @@ function createDrizzleSchemas(table, options) {
1590
1572
  }
1591
1573
  return { select, insert, update };
1592
1574
  }
1593
- async function createDrizzleSchemasAsync(table, options) {
1594
- await loadDrizzleZodAsync();
1595
- return createDrizzleSchemas(table, options);
1596
- }
1597
1575
  function isDrizzleZodAvailable() {
1598
- try {
1599
- tryLoadDrizzleZod();
1600
- return true;
1601
- } catch {
1602
- return false;
1603
- }
1576
+ return _drizzleZod !== null;
1604
1577
  }
1605
1578
  var coercedDate = z.preprocess(
1606
1579
  (val) => {
@@ -1665,4 +1638,4 @@ function applyDateCoercion(schema, dateColumns) {
1665
1638
  return z.object(newShape);
1666
1639
  }
1667
1640
 
1668
- export { DrizzleAdapters, DrizzleAggregateEndpoint, DrizzleBatchCreateEndpoint, DrizzleBatchDeleteEndpoint, DrizzleBatchRestoreEndpoint, DrizzleBatchUpdateEndpoint, DrizzleBatchUpsertEndpoint, DrizzleCreateEndpoint, DrizzleDeleteEndpoint, DrizzleExportEndpoint, DrizzleImportEndpoint, DrizzleListEndpoint, DrizzleReadEndpoint, DrizzleRestoreEndpoint, DrizzleSearchEndpoint, DrizzleUpdateEndpoint, DrizzleUpsertEndpoint, DrizzleVersionCompareEndpoint, DrizzleVersionHistoryEndpoint, DrizzleVersionReadEndpoint, DrizzleVersionRollbackEndpoint, batchLoadDrizzleRelations, buildWhereCondition, cast, createDrizzleCrud, createDrizzleSchemas, createDrizzleSchemasAsync, createInsertSchema, createSelectSchema, createUpdateSchema, getColumn, getTable, isDrizzleZodAvailable, loadDrizzleRelation, loadDrizzleRelations };
1641
+ export { DrizzleAdapters, DrizzleAggregateEndpoint, DrizzleBatchCreateEndpoint, DrizzleBatchDeleteEndpoint, DrizzleBatchRestoreEndpoint, DrizzleBatchUpdateEndpoint, DrizzleBatchUpsertEndpoint, DrizzleCreateEndpoint, DrizzleDeleteEndpoint, DrizzleExportEndpoint, DrizzleImportEndpoint, DrizzleListEndpoint, DrizzleReadEndpoint, DrizzleRestoreEndpoint, DrizzleSearchEndpoint, DrizzleUpdateEndpoint, DrizzleUpsertEndpoint, DrizzleVersionCompareEndpoint, DrizzleVersionHistoryEndpoint, DrizzleVersionReadEndpoint, DrizzleVersionRollbackEndpoint, batchLoadDrizzleRelations, buildWhereCondition, cast, createDrizzleCrud, createDrizzleSchemas, createInsertSchema, createSelectSchema, createUpdateSchema, getColumn, getTable, isDrizzleZodAvailable, loadDrizzleRelation, loadDrizzleRelations };
@@ -1,7 +1,7 @@
1
1
  import { Env } from 'hono';
2
- import { C as CreateEndpoint, M as ModelObject, D as DeleteEndpoint, L as ListEndpoint, R as ReadEndpoint, a as RestoreEndpoint, U as UpdateEndpoint, B as BatchCreateEndpoint, b as BatchDeleteEndpoint, c as BatchRestoreEndpoint, d as BatchUpdateEndpoint, e as BatchUpdateItem, f as BatchUpsertEndpoint, A as AggregateEndpoint, E as ExportEndpoint, I as ImportEndpoint, S as SearchEndpoint, g as UpsertEndpoint, V as VersionCompareEndpoint, h as VersionHistoryEndpoint, i as VersionReadEndpoint, j as VersionRollbackEndpoint } from '../../import-B7KS4Wvh.js';
2
+ import { C as CreateEndpoint, M as ModelObject, D as DeleteEndpoint, L as ListEndpoint, R as ReadEndpoint, a as RestoreEndpoint, U as UpdateEndpoint, B as BatchCreateEndpoint, b as BatchDeleteEndpoint, c as BatchRestoreEndpoint, d as BatchUpdateEndpoint, e as BatchUpdateItem, f as BatchUpsertEndpoint, A as AggregateEndpoint, E as ExportEndpoint, I as ImportEndpoint, S as SearchEndpoint, g as UpsertEndpoint, V as VersionCompareEndpoint, h as VersionHistoryEndpoint, i as VersionReadEndpoint, j as VersionRollbackEndpoint } from '../../import-BZ05PJMa.js';
3
3
  import { M as MetaInput, g as RelationConfig, L as ListFilters, P as PaginatedResult, I as IncludeOptions, h as NestedUpdateInput, i as NestedWriteResult, j as AggregateOptions, k as AggregateResult, S as SearchOptions, l as SearchResult } from '../../types-CA784ZtV.js';
4
- import { B as BulkPatchEndpoint, C as CloneEndpoint } from '../../bulk-patch-B6j0q_p_.js';
4
+ import { B as BulkPatchEndpoint, C as CloneEndpoint } from '../../bulk-patch-f4TR8L-q.js';
5
5
  import 'zod';
6
6
  import '../../route-DwSID3du.js';
7
7
  import 'hono/utils/http-status';
@@ -1,5 +1,5 @@
1
- export { MemoryAggregateEndpoint, MemoryBatchCreateEndpoint, MemoryBatchDeleteEndpoint, MemoryBatchRestoreEndpoint, MemoryBatchUpdateEndpoint, MemoryBatchUpsertEndpoint, MemoryBulkPatchEndpoint, MemoryCloneEndpoint, MemoryCreateEndpoint, MemoryDeleteEndpoint, MemoryExportEndpoint, MemoryImportEndpoint, MemoryListEndpoint, MemoryReadEndpoint, MemoryRestoreEndpoint, MemorySearchEndpoint, MemoryUpdateEndpoint, MemoryUpsertEndpoint, MemoryVersionCompareEndpoint, MemoryVersionHistoryEndpoint, MemoryVersionReadEndpoint, MemoryVersionRollbackEndpoint, clearStorage, getStorage, getStore, storage } from '../../chunk-G7OXZ24G.js';
2
- import '../../chunk-LCRNKT65.js';
1
+ export { MemoryAggregateEndpoint, MemoryBatchCreateEndpoint, MemoryBatchDeleteEndpoint, MemoryBatchRestoreEndpoint, MemoryBatchUpdateEndpoint, MemoryBatchUpsertEndpoint, MemoryBulkPatchEndpoint, MemoryCloneEndpoint, MemoryCreateEndpoint, MemoryDeleteEndpoint, MemoryExportEndpoint, MemoryImportEndpoint, MemoryListEndpoint, MemoryReadEndpoint, MemoryRestoreEndpoint, MemorySearchEndpoint, MemoryUpdateEndpoint, MemoryUpsertEndpoint, MemoryVersionCompareEndpoint, MemoryVersionHistoryEndpoint, MemoryVersionReadEndpoint, MemoryVersionRollbackEndpoint, clearStorage, getStorage, getStore, storage } from '../../chunk-G7IQ5DWC.js';
2
+ import '../../chunk-T6J4NSMC.js';
3
3
  import '../../chunk-CDCGYMC6.js';
4
4
  import '../../chunk-U3SUUXJK.js';
5
5
  import '../../chunk-FST5S5UY.js';
@@ -1,5 +1,5 @@
1
1
  import { Env } from 'hono';
2
- import { C as CreateEndpoint, M as ModelObject, D as DeleteEndpoint, L as ListEndpoint, R as ReadEndpoint, U as UpdateEndpoint, B as BatchCreateEndpoint, b as BatchDeleteEndpoint, c as BatchRestoreEndpoint, d as BatchUpdateEndpoint, e as BatchUpdateItem, f as BatchUpsertEndpoint, a as RestoreEndpoint, A as AggregateEndpoint, E as ExportEndpoint, I as ImportEndpoint, S as SearchEndpoint, g as UpsertEndpoint, V as VersionCompareEndpoint, h as VersionHistoryEndpoint, i as VersionReadEndpoint, j as VersionRollbackEndpoint } from '../../import-B7KS4Wvh.js';
2
+ import { C as CreateEndpoint, M as ModelObject, D as DeleteEndpoint, L as ListEndpoint, R as ReadEndpoint, U as UpdateEndpoint, B as BatchCreateEndpoint, b as BatchDeleteEndpoint, c as BatchRestoreEndpoint, d as BatchUpdateEndpoint, e as BatchUpdateItem, f as BatchUpsertEndpoint, a as RestoreEndpoint, A as AggregateEndpoint, E as ExportEndpoint, I as ImportEndpoint, S as SearchEndpoint, g as UpsertEndpoint, V as VersionCompareEndpoint, h as VersionHistoryEndpoint, i as VersionReadEndpoint, j as VersionRollbackEndpoint } from '../../import-BZ05PJMa.js';
3
3
  import { M as MetaInput, L as ListFilters, P as PaginatedResult, I as IncludeOptions, j as AggregateOptions, k as AggregateResult, m as AggregateField, S as SearchOptions, l as SearchResult } from '../../types-CA784ZtV.js';
4
4
  import 'zod';
5
5
  import '../../route-DwSID3du.js';
@@ -1,4 +1,4 @@
1
- import { CreateEndpoint, ReadEndpoint, UpdateEndpoint, DeleteEndpoint, ListEndpoint, RestoreEndpoint, BatchCreateEndpoint, BatchUpdateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, BatchUpsertEndpoint, SearchEndpoint, searchInMemory, ExportEndpoint, ImportEndpoint, UpsertEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionCompareEndpoint, VersionRollbackEndpoint, AggregateEndpoint, computeAggregations } from '../../chunk-LCRNKT65.js';
1
+ import { CreateEndpoint, ReadEndpoint, UpdateEndpoint, DeleteEndpoint, ListEndpoint, RestoreEndpoint, BatchCreateEndpoint, BatchUpdateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, BatchUpsertEndpoint, SearchEndpoint, searchInMemory, ExportEndpoint, ImportEndpoint, UpsertEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionCompareEndpoint, VersionRollbackEndpoint, AggregateEndpoint, computeAggregations } from '../../chunk-T6J4NSMC.js';
2
2
  import '../../chunk-CDCGYMC6.js';
3
3
  import '../../chunk-U3SUUXJK.js';
4
4
  import '../../chunk-FST5S5UY.js';
@@ -1,4 +1,4 @@
1
- export { AuthenticatedEndpoint, JWTClaimsSchema, allOf, allowAll, anyOf, createAPIKeyMiddleware, createAuthMiddleware, createJWTMiddleware, decodeJWT, defaultHashAPIKey, denyAll, optionalAuth, parseJWTClaims, requireAllRoles, requireAnyPermission, requireAuth, requireAuthenticated, requireAuthentication, requireOwnership, requireOwnershipOrRole, requirePermissions, requireRoles, safeParseJWTClaims, validateAPIKey, validateAPIKeyEntry, validateJWTClaims, verifyJWT, withAuth } from '../chunk-KP4HGONO.js';
1
+ export { AuthenticatedEndpoint, JWTClaimsSchema, allOf, allowAll, anyOf, createAPIKeyMiddleware, createAuthMiddleware, createJWTMiddleware, decodeJWT, defaultHashAPIKey, denyAll, optionalAuth, parseJWTClaims, requireAllRoles, requireAnyPermission, requireAuth, requireAuthenticated, requireAuthentication, requireOwnership, requireOwnershipOrRole, requirePermissions, requireRoles, safeParseJWTClaims, validateAPIKey, validateAPIKeyEntry, validateJWTClaims, verifyJWT, withAuth } from '../chunk-57J3NJYN.js';
2
2
  export { MemoryAPIKeyStorage, generateAPIKey, getAPIKeyStorage, hashAPIKey, isValidAPIKeyFormat, setAPIKeyStorage } from '../chunk-6LS3LNIH.js';
3
3
  import '../chunk-CDCGYMC6.js';
4
4
  import '../chunk-FST5S5UY.js';
@@ -2,7 +2,7 @@ import { ZodObject, ZodRawShape } from 'zod';
2
2
  import { Env } from 'hono';
3
3
  import { O as OpenAPIRoute } from './route-DwSID3du.js';
4
4
  import { M as MetaInput, N as NormalizedSoftDeleteConfig, a as NormalizedMultiTenantConfig, O as OpenAPIRouteSchema, H as HookMode, L as ListFilters } from './types-CA784ZtV.js';
5
- import { M as ModelObject } from './import-B7KS4Wvh.js';
5
+ import { M as ModelObject } from './import-BZ05PJMa.js';
6
6
 
7
7
  /**
8
8
  * Base endpoint for cloning/duplicating a resource.
@@ -1,5 +1,5 @@
1
1
  export { RedisCacheStorage } from '../chunk-YJMGFVHZ.js';
2
- export { MemoryCacheStorage, createInvalidationPattern, createRelatedPatterns, generateCacheKey, getCacheStorage, matchesPattern, parseCacheKey, setCacheStorage, withCache, withCacheInvalidation } from '../chunk-WOFE46VS.js';
2
+ export { MemoryCacheStorage, createInvalidationPattern, createRelatedPatterns, generateCacheKey, getCacheStorage, matchesPattern, parseCacheKey, setCacheStorage, withCache, withCacheInvalidation } from '../chunk-Y74QJH7Y.js';
3
3
  import '../chunk-6LS3LNIH.js';
4
4
  import '../chunk-U3SUUXJK.js';
5
5
  import '../chunk-FST5S5UY.js';
@@ -355,9 +355,7 @@ function requireRoles(...roles) {
355
355
  const userRoles = user.roles || [];
356
356
  const hasRole = roles.some((role) => userRoles.includes(role));
357
357
  if (!hasRole) {
358
- throw new ForbiddenException(
359
- `Required role: ${roles.join(" or ")}`
360
- );
358
+ throw new ForbiddenException("Insufficient permissions");
361
359
  }
362
360
  await next();
363
361
  };
@@ -371,9 +369,7 @@ function requireAllRoles(...roles) {
371
369
  const userRoles = user.roles || [];
372
370
  const hasAllRoles = roles.every((role) => userRoles.includes(role));
373
371
  if (!hasAllRoles) {
374
- throw new ForbiddenException(
375
- `Required roles: ${roles.join(" and ")}`
376
- );
372
+ throw new ForbiddenException("Insufficient permissions");
377
373
  }
378
374
  await next();
379
375
  };
@@ -387,9 +383,7 @@ function requirePermissions(...permissions) {
387
383
  const userPermissions = user.permissions || [];
388
384
  const hasAllPermissions = permissions.every((perm) => userPermissions.includes(perm));
389
385
  if (!hasAllPermissions) {
390
- throw new ForbiddenException(
391
- `Required permissions: ${permissions.join(", ")}`
392
- );
386
+ throw new ForbiddenException("Insufficient permissions");
393
387
  }
394
388
  await next();
395
389
  };
@@ -403,9 +397,7 @@ function requireAnyPermission(...permissions) {
403
397
  const userPermissions = user.permissions || [];
404
398
  const hasAnyPermission = permissions.some((perm) => userPermissions.includes(perm));
405
399
  if (!hasAnyPermission) {
406
- throw new ForbiddenException(
407
- `Required permission: ${permissions.join(" or ")}`
408
- );
400
+ throw new ForbiddenException("Insufficient permissions");
409
401
  }
410
402
  await next();
411
403
  };
@@ -453,7 +445,7 @@ function requireOwnershipOrRole(getOwnerId, ...roles) {
453
445
  await next();
454
446
  return;
455
447
  }
456
- throw new ForbiddenException("Access denied: not resource owner and missing required role");
448
+ throw new ForbiddenException("Access denied");
457
449
  };
458
450
  }
459
451
  function allOf(...guards) {
@@ -1,4 +1,4 @@
1
- import { CreateEndpoint, ReadEndpoint, UpdateEndpoint, DeleteEndpoint, ListEndpoint, RestoreEndpoint, BatchCreateEndpoint, BatchUpdateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, BatchUpsertEndpoint, getSchemaFields, parseListFilters, UpsertEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionCompareEndpoint, VersionRollbackEndpoint, AggregateEndpoint, computeAggregations, SearchEndpoint, searchInMemory, ExportEndpoint, ImportEndpoint } from './chunk-LCRNKT65.js';
1
+ import { CreateEndpoint, ReadEndpoint, UpdateEndpoint, DeleteEndpoint, ListEndpoint, RestoreEndpoint, BatchCreateEndpoint, BatchUpdateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, BatchUpsertEndpoint, getSchemaFields, parseListFilters, UpsertEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionCompareEndpoint, VersionRollbackEndpoint, AggregateEndpoint, computeAggregations, SearchEndpoint, searchInMemory, ExportEndpoint, ImportEndpoint } from './chunk-T6J4NSMC.js';
2
2
  import { OpenAPIRoute } from './chunk-CDCGYMC6.js';
3
3
  import { decodeCursor, encodeCursor, getSoftDeleteConfig, getMultiTenantConfig, extractTenantId, applyComputedFields } from './chunk-U3SUUXJK.js';
4
4
  import { NotFoundException } from './chunk-FST5S5UY.js';
@@ -1,4 +1,4 @@
1
- import { matchPath } from './chunk-WOFE46VS.js';
1
+ import { matchPath } from './chunk-Y74QJH7Y.js';
2
2
 
3
3
  // src/logging/storage/memory.ts
4
4
  var MemoryLoggingStorage = class {
@@ -4552,6 +4552,11 @@ var AggregateEndpoint = class extends OpenAPIRoute {
4552
4552
  * Override to restrict which fields can be aggregated.
4553
4553
  */
4554
4554
  aggregateConfig = {};
4555
+ /**
4556
+ * Maximum number of GROUP BY fields allowed per query.
4557
+ * Prevents cardinality explosion from too many grouping dimensions.
4558
+ */
4559
+ maxGroupByFields = 5;
4555
4560
  /**
4556
4561
  * Fields that can be used for filtering.
4557
4562
  * Empty array means all fields are allowed.
@@ -4690,6 +4695,11 @@ var AggregateEndpoint = class extends OpenAPIRoute {
4690
4695
  }
4691
4696
  }
4692
4697
  if (options.groupBy) {
4698
+ if (options.groupBy.length > this.maxGroupByFields) {
4699
+ throw new InputValidationException(
4700
+ `Maximum ${this.maxGroupByFields} GROUP BY fields allowed`
4701
+ );
4702
+ }
4693
4703
  for (const field of options.groupBy) {
4694
4704
  if (config.groupByFields.length > 0 && !config.groupByFields.includes(field)) {
4695
4705
  throw new Error(`Field '${field}' is not allowed for GROUP BY`);
@@ -5146,6 +5156,10 @@ var SearchEndpoint = class extends OpenAPIRoute {
5146
5156
  * Minimum query length required to perform search.
5147
5157
  */
5148
5158
  minQueryLength = 2;
5159
+ /**
5160
+ * Maximum query length allowed to prevent CPU exhaustion from long search strings.
5161
+ */
5162
+ maxQueryLength = 500;
5149
5163
  /**
5150
5164
  * HTML tag used to wrap highlighted matches.
5151
5165
  */
@@ -5242,7 +5256,7 @@ var SearchEndpoint = class extends OpenAPIRoute {
5242
5256
  getQuerySchema() {
5243
5257
  const shape = {
5244
5258
  // Search parameters
5245
- q: z.string().min(this.minQueryLength).describe("Search query"),
5259
+ q: z.string().min(this.minQueryLength).max(this.maxQueryLength).describe("Search query"),
5246
5260
  fields: z.string().optional().describe(
5247
5261
  `Comma-separated fields to search. Available: ${Object.keys(this.getSearchableFields()).join(", ")}`
5248
5262
  ),
@@ -5564,6 +5578,11 @@ function escapeCsvValue(value, options = {}) {
5564
5578
  return value ? "true" : "false";
5565
5579
  }
5566
5580
  const str = String(value);
5581
+ const firstChar = str.charAt(0);
5582
+ if (firstChar === "=" || firstChar === "+" || firstChar === "-" || firstChar === "@" || firstChar === " " || firstChar === "\r") {
5583
+ const escaped = str.replace(/"/g, '""');
5584
+ return `" ${escaped}"`;
5585
+ }
5567
5586
  const needsQuoting = str.includes(delimiter) || str.includes('"') || str.includes("\n") || str.includes("\r");
5568
5587
  if (needsQuoting) {
5569
5588
  const escaped = str.replace(/"/g, '""');
@@ -5936,7 +5955,8 @@ var ExportEndpoint = class extends ListEndpoint {
5936
5955
  * Gets the filename for the export.
5937
5956
  */
5938
5957
  getExportFilename(format) {
5939
- const baseName = this.exportFilename || this._meta.model.tableName;
5958
+ const rawName = this.exportFilename || this._meta.model.tableName;
5959
+ const baseName = rawName.replace(/[^a-zA-Z0-9_-]/g, "_");
5940
5960
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5941
5961
  return `${baseName}-export-${timestamp}.${format}`;
5942
5962
  }
@@ -6030,6 +6050,10 @@ var ExportEndpoint = class extends ListEndpoint {
6030
6050
  * Escapes a CSV field value for safe output.
6031
6051
  */
6032
6052
  escapeCsvField(value) {
6053
+ const firstChar = value.charAt(0);
6054
+ if (firstChar === "=" || firstChar === "+" || firstChar === "-" || firstChar === "@" || firstChar === " " || firstChar === "\r") {
6055
+ return `" ${value.replace(/"/g, '""')}"`;
6056
+ }
6033
6057
  if (value.includes(",") || value.includes('"') || value.includes("\n") || value.includes("\r")) {
6034
6058
  return `"${value.replace(/"/g, '""')}"`;
6035
6059
  }
@@ -6080,12 +6104,13 @@ var ExportEndpoint = class extends ListEndpoint {
6080
6104
  * Overrides pagination to fetch up to maxExportRecords.
6081
6105
  */
6082
6106
  async fetchAllForExport(filters) {
6107
+ const effectiveLimit = Math.min(this.maxExportRecords, 1e5);
6083
6108
  const exportFilters = {
6084
6109
  ...filters,
6085
6110
  options: {
6086
6111
  ...filters.options,
6087
6112
  page: 1,
6088
- per_page: this.maxExportRecords
6113
+ per_page: effectiveLimit
6089
6114
  }
6090
6115
  };
6091
6116
  const result = await this.list(exportFilters);
@@ -6125,6 +6150,8 @@ var ImportEndpoint = class extends OpenAPIRoute {
6125
6150
  immutableFields = [];
6126
6151
  /** CSV parsing options. */
6127
6152
  csvOptions = {};
6153
+ /** Maximum body size in bytes for import requests (default: 10MB). */
6154
+ maxBodySize = 10 * 1024 * 1024;
6128
6155
  /** Fields that are optional during import (won't cause validation errors if missing). */
6129
6156
  optionalImportFields = [];
6130
6157
  // Audit logging
@@ -6322,6 +6349,9 @@ var ImportEndpoint = class extends OpenAPIRoute {
6322
6349
  }
6323
6350
  if (contentType.includes("text/csv")) {
6324
6351
  const csvContent = await ctx.req.text();
6352
+ if (csvContent.length > this.maxBodySize) {
6353
+ throw new InputValidationException(`Request body exceeds maximum size of ${this.maxBodySize} bytes`);
6354
+ }
6325
6355
  return this.parseCsvData(csvContent);
6326
6356
  }
6327
6357
  if (contentType.includes("multipart/form-data")) {
@@ -6331,9 +6361,17 @@ var ImportEndpoint = class extends OpenAPIRoute {
6331
6361
  throw new InputValidationException("No file provided in form data");
6332
6362
  }
6333
6363
  const content = await file.text();
6364
+ if (content.length > this.maxBodySize) {
6365
+ throw new InputValidationException(`Uploaded file exceeds maximum size of ${this.maxBodySize} bytes`);
6366
+ }
6334
6367
  const filename = file.name.toLowerCase();
6335
6368
  if (filename.endsWith(".json")) {
6336
- const body = JSON.parse(content);
6369
+ let body;
6370
+ try {
6371
+ body = JSON.parse(content);
6372
+ } catch {
6373
+ throw new InputValidationException("Invalid JSON content in uploaded file");
6374
+ }
6337
6375
  const items = Array.isArray(body) ? body : body.items;
6338
6376
  if (!items || !Array.isArray(items)) {
6339
6377
  throw new InputValidationException('JSON file must contain an array or an object with "items" array');
@@ -6348,7 +6386,12 @@ var ImportEndpoint = class extends OpenAPIRoute {
6348
6386
  }
6349
6387
  const trimmed = content.trim();
6350
6388
  if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
6351
- const body = JSON.parse(content);
6389
+ let body;
6390
+ try {
6391
+ body = JSON.parse(content);
6392
+ } catch {
6393
+ throw new InputValidationException("Invalid JSON content in uploaded file");
6394
+ }
6352
6395
  const items = Array.isArray(body) ? body : body.items;
6353
6396
  if (!items || !Array.isArray(items)) {
6354
6397
  throw new InputValidationException('JSON file must contain an array or an object with "items" array');
@@ -85,24 +85,22 @@ function shouldExcludePath(path, includePaths, excludePaths) {
85
85
  }
86
86
  return true;
87
87
  }
88
- function extractClientIp(ctx, ipHeader = "X-Forwarded-For", trustProxy = true) {
89
- if (trustProxy) {
90
- const proxyHeader = ctx.req.header(ipHeader);
91
- if (proxyHeader) {
92
- const firstIP = proxyHeader.split(",")[0].trim();
93
- if (firstIP) {
94
- return firstIP;
95
- }
96
- }
97
- const realIP = ctx.req.header("X-Real-IP");
98
- if (realIP) {
99
- return realIP.trim();
100
- }
101
- const cfIP = ctx.req.header("CF-Connecting-IP");
102
- if (cfIP) {
103
- return cfIP.trim();
88
+ function extractClientIp(ctx, ipHeader = "X-Forwarded-For", _trustProxy = false) {
89
+ const proxyHeader = ctx.req.header(ipHeader);
90
+ if (proxyHeader) {
91
+ const firstIP = proxyHeader.split(",")[0].trim();
92
+ if (firstIP) {
93
+ return firstIP;
104
94
  }
105
95
  }
96
+ const realIP = ctx.req.header("X-Real-IP");
97
+ if (realIP) {
98
+ return realIP.trim();
99
+ }
100
+ const cfIP = ctx.req.header("CF-Connecting-IP");
101
+ if (cfIP) {
102
+ return cfIP.trim();
103
+ }
106
104
  const raw = ctx.req.raw;
107
105
  if (raw && "socket" in raw && raw.socket && typeof raw.socket === "object") {
108
106
  const socket = raw.socket;
@@ -166,6 +164,14 @@ function generateRequestId() {
166
164
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
167
165
  return crypto.randomUUID();
168
166
  }
167
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
168
+ const bytes = new Uint8Array(16);
169
+ crypto.getRandomValues(bytes);
170
+ bytes[6] = bytes[6] & 15 | 64;
171
+ bytes[8] = bytes[8] & 63 | 128;
172
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
173
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
174
+ }
169
175
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
170
176
  const r = Math.random() * 16 | 0;
171
177
  const v = c === "x" ? r : r & 3 | 8;
@@ -182,7 +188,7 @@ var RateLimitExceededException = class extends ApiException {
182
188
  };
183
189
 
184
190
  // src/rate-limit/utils.ts
185
- function extractIP(ctx, ipHeader = "X-Forwarded-For", trustProxy = true) {
191
+ function extractIP(ctx, ipHeader = "X-Forwarded-For", trustProxy = false) {
186
192
  if (trustProxy) {
187
193
  const proxyHeader = ctx.req.header(ipHeader);
188
194
  if (proxyHeader) {
@@ -317,7 +323,7 @@ function createLoggingMiddleware(config = {}) {
317
323
  const includeQuery = config.includeQuery ?? true;
318
324
  const includeClientIp = config.includeClientIp ?? true;
319
325
  const ipHeader = config.ipHeader ?? "X-Forwarded-For";
320
- const trustProxy = config.trustProxy ?? true;
326
+ const trustProxy = config.trustProxy ?? false;
321
327
  const minResponseTimeMs = config.minResponseTimeMs ?? 0;
322
328
  const generateRequestId2 = config.generateRequestId ?? generateRequestId;
323
329
  return async (ctx, next) => {
@@ -609,7 +615,7 @@ var MemoryCacheStorage = class {
609
615
  maxEntries;
610
616
  constructor(options) {
611
617
  this.defaultTtl = options?.defaultTtl ?? 300;
612
- this.maxEntries = options?.maxEntries ?? 0;
618
+ this.maxEntries = options?.maxEntries ?? 1e4;
613
619
  }
614
620
  /**
615
621
  * Get a cached entry by key.
@@ -2057,6 +2057,11 @@ declare abstract class AggregateEndpoint<E extends Env = Env, M extends MetaInpu
2057
2057
  * Override to restrict which fields can be aggregated.
2058
2058
  */
2059
2059
  protected aggregateConfig: AggregateConfig;
2060
+ /**
2061
+ * Maximum number of GROUP BY fields allowed per query.
2062
+ * Prevents cardinality explosion from too many grouping dimensions.
2063
+ */
2064
+ protected maxGroupByFields: number;
2060
2065
  /**
2061
2066
  * Fields that can be used for filtering.
2062
2067
  * Empty array means all fields are allowed.
@@ -2162,6 +2167,10 @@ declare abstract class SearchEndpoint<E extends Env = Env, M extends MetaInput =
2162
2167
  * Minimum query length required to perform search.
2163
2168
  */
2164
2169
  protected minQueryLength: number;
2170
+ /**
2171
+ * Maximum query length allowed to prevent CPU exhaustion from long search strings.
2172
+ */
2173
+ protected maxQueryLength: number;
2165
2174
  /**
2166
2175
  * HTML tag used to wrap highlighted matches.
2167
2176
  */
@@ -2701,6 +2710,8 @@ declare abstract class ImportEndpoint<E extends Env = Env, M extends MetaInput =
2701
2710
  protected immutableFields: string[];
2702
2711
  /** CSV parsing options. */
2703
2712
  protected csvOptions: Partial<CsvParseOptions>;
2713
+ /** Maximum body size in bytes for import requests (default: 10MB). */
2714
+ protected maxBodySize: number;
2704
2715
  /** Fields that are optional during import (won't cause validation errors if missing). */
2705
2716
  protected optionalImportFields: string[];
2706
2717
  private _auditLogger?;
@@ -1,4 +1,4 @@
1
- import { P as PathPattern, c as RateLimitConfig, f as RateLimitStorage, F as FixedWindowEntry, S as SlidingWindowEntry, d as RateLimitEntry } from './types-Bc7ebF5x.js';
1
+ import { P as PathPattern, c as RateLimitConfig, f as RateLimitStorage, F as FixedWindowEntry, S as SlidingWindowEntry, d as RateLimitEntry } from './types-BO3G_MZk.js';
2
2
  import { HTTPException } from 'hono/http-exception';
3
3
  import { ZodError } from 'zod';
4
4
  import { ContentfulStatusCode } from 'hono/utils/http-status';
@@ -85,7 +85,7 @@ declare class RateLimitExceededException extends ApiException {
85
85
  *
86
86
  * @param ctx - Hono context
87
87
  * @param ipHeader - Header name for proxy IP (default: 'X-Forwarded-For')
88
- * @param trustProxy - Whether to trust proxy headers (default: true)
88
+ * @param trustProxy - Whether to trust proxy headers (default: false)
89
89
  * @returns The client IP address or 'unknown'
90
90
  */
91
91
  declare function extractIP<E extends Env>(ctx: Context<E>, ipHeader?: string, trustProxy?: boolean): string;
@@ -1,5 +1,5 @@
1
1
  import { M as MetaInput, H as HookMode } from './types-CA784ZtV.js';
2
- import { M as ModelObject } from './import-B7KS4Wvh.js';
2
+ import { M as ModelObject } from './import-BZ05PJMa.js';
3
3
 
4
4
  /**
5
5
  * Config-Based API for defining CRUD endpoints.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { A as AuthEnv } from './memory-B_NZbOQP.js';
2
2
  export { a as APIKeyConfig, b as APIKeyEntry, c as APIKeyLookupResult, d as AuthConfig, e as AuthType, f as AuthUser, g as AuthorizationCheck, E as EndpointAuthConfig, G as Guard, J as JWTAlgorithm, h as JWTClaims, i as JWTClaimsSchema, j as JWTConfig, M as MemoryAPIKeyStorage, O as OwnershipExtractor, P as PathPattern, V as ValidatedJWTClaims, k as generateAPIKey, l as getAPIKeyStorage, m as hashAPIKey, n as isValidAPIKeyFormat, p as parseJWTClaims, s as safeParseJWTClaims, o as setAPIKeyStorage } from './memory-B_NZbOQP.js';
3
- import { L as LoggingEnv } from './types-B8CWg4nO.js';
4
- export { a as LogEntry, b as LogLevel, c as LogQueryOptions, d as LoggingConfig, P as LoggingPathPattern, e as LoggingStorage, R as RedactField, f as RequestBodyConfig, g as RequestLogEntry, h as ResponseBodyConfig, i as ResponseLogEntry } from './types-B8CWg4nO.js';
5
- import { R as RateLimitEnv } from './types-Bc7ebF5x.js';
6
- export { F as FixedWindowEntry, K as KeyExtractor, a as KeyStrategy, O as OnRateLimitExceeded, b as RateLimitAlgorithm, c as RateLimitConfig, d as RateLimitEntry, P as RateLimitPathPattern, e as RateLimitResult, f as RateLimitStorage, g as RateLimitTier, S as SlidingWindowEntry, T as TierFunction } from './types-Bc7ebF5x.js';
3
+ import { L as LoggingEnv } from './types-DnQWCfXp.js';
4
+ export { a as LogEntry, b as LogLevel, c as LogQueryOptions, d as LoggingConfig, P as LoggingPathPattern, e as LoggingStorage, R as RedactField, f as RequestBodyConfig, g as RequestLogEntry, h as ResponseBodyConfig, i as ResponseLogEntry } from './types-DnQWCfXp.js';
5
+ import { R as RateLimitEnv } from './types-BO3G_MZk.js';
6
+ export { F as FixedWindowEntry, K as KeyExtractor, a as KeyStrategy, O as OnRateLimitExceeded, b as RateLimitAlgorithm, c as RateLimitConfig, d as RateLimitEntry, P as RateLimitPathPattern, e as RateLimitResult, f as RateLimitStorage, g as RateLimitTier, S as SlidingWindowEntry, T as TierFunction } from './types-BO3G_MZk.js';
7
7
  import { StorageEnv } from './storage/index.js';
8
8
  export { StorageMiddlewareConfig, StorageRegistry, createAPIKeyStorageMiddleware, createAuditStorageMiddleware, createCacheStorageMiddleware, createLoggingStorageMiddleware, createNullableRegistry, createRateLimitStorageMiddleware, createRegistryWithDefault, createStorageMiddleware, createVersioningStorageMiddleware, resolveAPIKeyStorage, resolveAuditStorage, resolveCacheStorage, resolveLoggingStorage, resolveRateLimitStorage, resolveVersioningStorage } from './storage/index.js';
9
9
  import { O as OpenAPIRoute } from './route-DwSID3du.js';
@@ -12,12 +12,12 @@ import { Env, MiddlewareHandler, Hono, Context, ErrorHandler } from 'hono';
12
12
  import { OpenAPIHono, Hook } from '@hono/zod-openapi';
13
13
  import { O as OpenAPIRouteSchema, t as SearchFieldConfig, u as SearchMode, M as MetaInput, H as HookMode } from './types-CA784ZtV.js';
14
14
  export { s as AggregateConfig, m as AggregateField, w as AggregateOperation, j as AggregateOptions, k as AggregateResult, b as AuditAction, c as AuditConfig, f as AuditFieldChange, A as AuditLogEntry, C as CascadeAction, x as CascadeConfig, y as ComputedFieldConfig, z as ComputedFieldFn, B as ComputedFieldsConfig, E as ErrorResponse, F as FilterCondition, o as FilterConfig, p as FilterOperator, D as HandleArgs, G as HookConfig, J as HookFn, I as IncludeOptions, K as InferMeta, Q as InferModel, T as InferSchema, L as ListFilters, U as ListOptions, n as Model, W as ModelTable, X as MultiTenantConfig, Y as NestedCreateManyInput, Z as NestedCreateOneInput, h as NestedUpdateInput, _ as NestedWriteConfig, i as NestedWriteResult, $ as NestedWritesConfig, q as NormalizedAuditConfig, a as NormalizedMultiTenantConfig, N as NormalizedSoftDeleteConfig, r as NormalizedVersioningConfig, P as PaginatedResult, a0 as PartialBy, g as RelationConfig, a1 as RelationType, a2 as RelationsConfig, a3 as RequiredBy, R as RouteOptions, a4 as SchemaKeys, a5 as SearchConfig, S as SearchOptions, l as SearchResult, v as SearchResultItem, a6 as SoftDeleteConfig, a7 as SuccessResponse, V as ValidatedData, d as VersionHistoryEntry, e as VersioningConfig, a8 as applyComputedFields, a9 as applyComputedFieldsToArray, aa as calculateChanges, ab as decodeCursor, ac as defineMeta, ad as defineModel, ae as encodeCursor, af as extractNestedData, ag as extractTenantId, ah as getAuditConfig, ai as getMultiTenantConfig, aj as getSoftDeleteConfig, ak as getVersioningConfig, al as isDirectNestedData, am as parseAggregateField, an as parseAggregateQuery, ao as parseSearchMode } from './types-CA784ZtV.js';
15
- import { A as ApiException } from './index-Dm8-ODYA.js';
16
- export { a as AggregationException, C as CacheException, b as ConfigurationException, c as ConflictException, F as ForbiddenException, I as InputValidationException, M as MemoryRateLimitStorage, d as MemoryRateLimitStorageOptions, N as NotFoundException, R as RateLimitExceededException, e as RedisRateLimitClient, f as RedisRateLimitStorage, g as RedisRateLimitStorageOptions, U as UnauthorizedException, h as createRateLimitMiddleware, i as extractAPIKey, j as extractIP, k as extractUserId, l as generateKey, m as getRateLimitStorage, n as matchPath, r as resetRateLimit, s as setRateLimitStorage, o as shouldSkipPath } from './index-Dm8-ODYA.js';
15
+ import { A as ApiException } from './index-431Ovi2o.js';
16
+ export { a as AggregationException, C as CacheException, b as ConfigurationException, c as ConflictException, F as ForbiddenException, I as InputValidationException, M as MemoryRateLimitStorage, d as MemoryRateLimitStorageOptions, N as NotFoundException, R as RateLimitExceededException, e as RedisRateLimitClient, f as RedisRateLimitStorage, g as RedisRateLimitStorageOptions, U as UnauthorizedException, h as createRateLimitMiddleware, i as extractAPIKey, j as extractIP, k as extractUserId, l as generateKey, m as getRateLimitStorage, n as matchPath, r as resetRateLimit, s as setRateLimitStorage, o as shouldSkipPath } from './index-431Ovi2o.js';
17
17
  export { a as AuditLogStorage, A as AuditLogger, M as MemoryAuditLogStorage, b as MemoryVersioningStorage, V as VersionManager, c as VersioningStorage, d as createAuditLogger, e as createVersionManager, g as getAuditStorage, f as getVersioningStorage, s as setAuditStorage, h as setVersioningStorage } from './versioning-BG8-R7C0.js';
18
- import { M as ModelObject } from './import-B7KS4Wvh.js';
19
- export { A as AggregateEndpoint, B as BatchCreateEndpoint, b as BatchDeleteEndpoint, k as BatchDeleteResult, c as BatchRestoreEndpoint, l as BatchRestoreResult, d as BatchUpdateEndpoint, e as BatchUpdateItem, m as BatchUpdateResult, f as BatchUpsertEndpoint, n as BatchUpsertItemResult, o as BatchUpsertResult, p as CascadeResult, C as CreateEndpoint, q as CsvGenerateOptions, r as CsvParseError, s as CsvParseOptions, t as CsvParseResult, u as CsvValidationResult, D as DeleteEndpoint, E as ExportEndpoint, v as ExportFormat, w as ExportOptions, x as ExportResult, F as FieldSelection, y as FieldSelectionConfig, I as ImportEndpoint, z as ImportMode, G as ImportOptions, H as ImportResult, J as ImportRowResult, K as ImportRowStatus, N as ImportSummary, L as ListEndpoint, O as ListEndpointConfig, R as ReadEndpoint, a as RestoreEndpoint, S as SearchEndpoint, P as SingleEndpointConfig, U as UpdateEndpoint, Q as UpdateEndpointConfig, g as UpsertEndpoint, T as UpsertResult, V as VersionCompareEndpoint, h as VersionHistoryEndpoint, i as VersionReadEndpoint, j as VersionRollbackEndpoint, W as applyFieldSelection, X as applyFieldSelectionToArray, Y as computeAggregations, Z as createCsvStream, _ as csvToJson, $ as escapeCsvValue, a0 as generateCsv, a1 as getSchemaFields, a2 as inferCsvContentType, a3 as jsonToCsv, a4 as parseCsv, a5 as parseFieldSelection, a6 as parseFilterValue, a7 as parseListFilters, a8 as searchInMemory, a9 as validateCsvHeaders } from './import-B7KS4Wvh.js';
20
- export { B as BulkPatchEndpoint, a as BulkPatchResult, C as CloneEndpoint } from './bulk-patch-B6j0q_p_.js';
18
+ import { M as ModelObject } from './import-BZ05PJMa.js';
19
+ export { A as AggregateEndpoint, B as BatchCreateEndpoint, b as BatchDeleteEndpoint, k as BatchDeleteResult, c as BatchRestoreEndpoint, l as BatchRestoreResult, d as BatchUpdateEndpoint, e as BatchUpdateItem, m as BatchUpdateResult, f as BatchUpsertEndpoint, n as BatchUpsertItemResult, o as BatchUpsertResult, p as CascadeResult, C as CreateEndpoint, q as CsvGenerateOptions, r as CsvParseError, s as CsvParseOptions, t as CsvParseResult, u as CsvValidationResult, D as DeleteEndpoint, E as ExportEndpoint, v as ExportFormat, w as ExportOptions, x as ExportResult, F as FieldSelection, y as FieldSelectionConfig, I as ImportEndpoint, z as ImportMode, G as ImportOptions, H as ImportResult, J as ImportRowResult, K as ImportRowStatus, N as ImportSummary, L as ListEndpoint, O as ListEndpointConfig, R as ReadEndpoint, a as RestoreEndpoint, S as SearchEndpoint, P as SingleEndpointConfig, U as UpdateEndpoint, Q as UpdateEndpointConfig, g as UpsertEndpoint, T as UpsertResult, V as VersionCompareEndpoint, h as VersionHistoryEndpoint, i as VersionReadEndpoint, j as VersionRollbackEndpoint, W as applyFieldSelection, X as applyFieldSelectionToArray, Y as computeAggregations, Z as createCsvStream, _ as csvToJson, $ as escapeCsvValue, a0 as generateCsv, a1 as getSchemaFields, a2 as inferCsvContentType, a3 as jsonToCsv, a4 as parseCsv, a5 as parseFieldSelection, a6 as parseFilterValue, a7 as parseListFilters, a8 as searchInMemory, a9 as validateCsvHeaders } from './import-BZ05PJMa.js';
20
+ export { B as BulkPatchEndpoint, a as BulkPatchResult, C as CloneEndpoint } from './bulk-patch-f4TR8L-q.js';
21
21
  export { S as ScalarConfig, a as ScalarTheme, U as UIOptions, s as scalarUI, b as setupDocs, c as setupDocsIndex, d as setupReDoc, e as setupScalar, f as setupSwaggerUI } from './ui-CNJUoCg1.js';
22
22
  import { z } from 'zod';
23
23
  export { AuthEndpointMethods, AuthenticatedEndpoint, JWTClaimsValidationOptions, allOf, allowAll, anyOf, createAPIKeyMiddleware, createAuthMiddleware, createJWTMiddleware, decodeJWT, defaultHashAPIKey, denyAll, optionalAuth, requireAllRoles, requireAnyPermission, requireAuth, requireAuthenticated, requireAuthentication, requireOwnership, requireOwnershipOrRole, requirePermissions, requireRoles, validateAPIKey, validateAPIKeyEntry, validateJWTClaims, verifyJWT, withAuth } from './auth/index.js';
@@ -25,7 +25,7 @@ export { C as CacheConfig, a as CacheEntry, b as CacheInvalidationConfig, c as C
25
25
  export { CacheEndpointMethods, CacheInvalidationMethods, MemoryCacheStorage, RedisCacheStorage, RedisCacheStorageOptions, RedisClient, createInvalidationPattern, createRelatedPatterns, generateCacheKey, getCacheStorage, matchesPattern, parseCacheKey, setCacheStorage, withCache, withCacheInvalidation } from './cache/index.js';
26
26
  export { MemoryLoggingStorage, MemoryLoggingStorageOptions, createLoggingMiddleware, extractClientIp, extractHeaders, extractUserId as extractLoggingUserId, extractQuery, generateRequestId, getLoggingStorage, getRequestId, getRequestStartTime, isAllowedContentType, matchPath as matchLoggingPath, redactHeaders, redactObject, setLoggingStorage, shouldExcludePath, shouldRedact, truncateBody } from './logging/index.js';
27
27
  import * as hono_types from 'hono/types';
28
- export { A as AdapterBundle, C as ConfigCreateEndpoint, D as ConfigDeleteEndpoint, L as ConfigListEndpoint, R as ConfigReadEndpoint, U as ConfigUpdateEndpoint, E as EndpointsConfig, G as GeneratedEndpoints, M as MemoryAdapters, d as defineEndpoints } from './index-5BEg0ty5.js';
28
+ export { A as AdapterBundle, C as ConfigCreateEndpoint, D as ConfigDeleteEndpoint, L as ConfigListEndpoint, R as ConfigReadEndpoint, U as ConfigUpdateEndpoint, E as EndpointsConfig, G as GeneratedEndpoints, M as MemoryAdapters, d as defineEndpoints } from './index-bSnRLgcs.js';
29
29
  import 'hono/utils/http-status';
30
30
  import 'hono/http-exception';
31
31
 
@@ -484,6 +484,11 @@ declare class CrudEventEmitter {
484
484
  private tableListeners;
485
485
  /** Listeners for all events on all tables */
486
486
  private globalListeners;
487
+ /** Maximum listeners per event key. 0 = unlimited. */
488
+ private maxListeners;
489
+ constructor(options?: {
490
+ maxListeners?: number;
491
+ });
487
492
  /**
488
493
  * Subscribe to a specific event type on a specific table.
489
494
  */
@@ -546,10 +551,17 @@ interface SubscribeEndpointConfig {
546
551
  filter?: (event: CrudEventPayload, ctx: Context) => boolean;
547
552
  /** Heartbeat interval in milliseconds. @default 30000 */
548
553
  heartbeatInterval?: number;
554
+ /** Maximum concurrent SSE connections per table. @default 1000 */
555
+ maxConnections?: number;
556
+ /** Connection timeout in milliseconds. @default 300000 (5 min) */
557
+ connectionTimeout?: number;
558
+ /** Fields to strip from event payloads before streaming. @default ['password', 'token', 'secret', 'apiKey', 'creditCard', 'ssn'] */
559
+ excludeFields?: string[];
549
560
  }
550
561
  /**
551
562
  * Create an SSE subscription handler for real-time CRUD events.
552
563
  * Uses Hono's built-in `streamSSE()` which works on Cloudflare Workers.
564
+ * Edge-compatible: uses stream.sleep() instead of setInterval/setTimeout.
553
565
  *
554
566
  * @example
555
567
  * ```ts
@@ -1535,8 +1547,11 @@ declare class MemoryIdempotencyStorage implements IdempotencyStorage {
1535
1547
  private cleanupInterval;
1536
1548
  /** Timestamp of last cleanup run */
1537
1549
  private lastCleanup;
1550
+ /** Maximum number of entries before evicting oldest */
1551
+ private maxEntries;
1538
1552
  constructor(options?: {
1539
1553
  cleanupInterval?: number;
1554
+ maxEntries?: number;
1540
1555
  });
1541
1556
  private maybeCleanup;
1542
1557
  private cleanupExpired;
package/dist/index.js CHANGED
@@ -1,19 +1,19 @@
1
- import { MemoryDeleteEndpoint, MemoryUpdateEndpoint, MemoryReadEndpoint, MemoryListEndpoint, MemoryCreateEndpoint } from './chunk-G7OXZ24G.js';
2
- export { BulkPatchEndpoint, CloneEndpoint } from './chunk-G7OXZ24G.js';
1
+ import { MemoryDeleteEndpoint, MemoryUpdateEndpoint, MemoryReadEndpoint, MemoryListEndpoint, MemoryCreateEndpoint } from './chunk-G7IQ5DWC.js';
2
+ export { BulkPatchEndpoint, CloneEndpoint } from './chunk-G7IQ5DWC.js';
3
3
  export { scalarUI, setupDocs, setupDocsIndex, setupReDoc, setupScalar, setupSwaggerUI } from './chunk-CNE7UR5E.js';
4
- export { AuthenticatedEndpoint, JWTClaimsSchema, allOf, allowAll, anyOf, createAPIKeyMiddleware, createAuthMiddleware, createJWTMiddleware, decodeJWT, defaultHashAPIKey, denyAll, optionalAuth, parseJWTClaims, requireAllRoles, requireAnyPermission, requireAuth, requireAuthenticated, requireAuthentication, requireOwnership, requireOwnershipOrRole, requirePermissions, requireRoles, safeParseJWTClaims, validateAPIKey, validateAPIKeyEntry, validateJWTClaims, verifyJWT, withAuth } from './chunk-KP4HGONO.js';
4
+ export { AuthenticatedEndpoint, JWTClaimsSchema, allOf, allowAll, anyOf, createAPIKeyMiddleware, createAuthMiddleware, createJWTMiddleware, decodeJWT, defaultHashAPIKey, denyAll, optionalAuth, parseJWTClaims, requireAllRoles, requireAnyPermission, requireAuth, requireAuthenticated, requireAuthentication, requireOwnership, requireOwnershipOrRole, requirePermissions, requireRoles, safeParseJWTClaims, validateAPIKey, validateAPIKeyEntry, validateJWTClaims, verifyJWT, withAuth } from './chunk-57J3NJYN.js';
5
5
  export { RedisCacheStorage } from './chunk-YJMGFVHZ.js';
6
- export { MemoryLoggingStorage } from './chunk-IPGNLIJV.js';
6
+ export { MemoryLoggingStorage } from './chunk-I36Y45QN.js';
7
7
  export { MemoryRateLimitStorage, RedisRateLimitStorage } from './chunk-MANRQHEB.js';
8
8
  export { createAPIKeyStorageMiddleware, createAuditStorageMiddleware, createCacheStorageMiddleware, createLoggingStorageMiddleware, createRateLimitStorageMiddleware, createStorageMiddleware, createVersioningStorageMiddleware } from './chunk-ZCHXXN7T.js';
9
- import { getRequestId } from './chunk-WOFE46VS.js';
10
- export { MemoryCacheStorage, RateLimitExceededException, createInvalidationPattern, createLoggingMiddleware, createRateLimitMiddleware, createRelatedPatterns, extractAPIKey, extractClientIp, extractHeaders, extractIP, extractUserId as extractLoggingUserId, extractQuery, extractUserId2 as extractUserId, generateCacheKey, generateKey, generateRequestId, getCacheStorage, getErrorMessage, getLoggingStorage, getRateLimitStorage, getRequestId, getRequestStartTime, isAllowedContentType, matchPath as matchLoggingPath, matchPath2 as matchPath, matchesPattern, parseCacheKey, redactHeaders, redactObject, resetRateLimit, resolveAPIKeyStorage, resolveAuditStorage, resolveCacheStorage, resolveLoggingStorage, resolveRateLimitStorage, resolveVersioningStorage, setCacheStorage, setLoggingStorage, setRateLimitStorage, shouldExcludePath, shouldRedact, shouldSkipPath, toError, truncateBody, withCache, withCacheInvalidation, wrapError } from './chunk-WOFE46VS.js';
9
+ import { getRequestId } from './chunk-Y74QJH7Y.js';
10
+ export { MemoryCacheStorage, RateLimitExceededException, createInvalidationPattern, createLoggingMiddleware, createRateLimitMiddleware, createRelatedPatterns, extractAPIKey, extractClientIp, extractHeaders, extractIP, extractUserId as extractLoggingUserId, extractQuery, extractUserId2 as extractUserId, generateCacheKey, generateKey, generateRequestId, getCacheStorage, getErrorMessage, getLoggingStorage, getRateLimitStorage, getRequestId, getRequestStartTime, isAllowedContentType, matchPath as matchLoggingPath, matchPath2 as matchPath, matchesPattern, parseCacheKey, redactHeaders, redactObject, resetRateLimit, resolveAPIKeyStorage, resolveAuditStorage, resolveCacheStorage, resolveLoggingStorage, resolveRateLimitStorage, resolveVersioningStorage, setCacheStorage, setLoggingStorage, setRateLimitStorage, shouldExcludePath, shouldRedact, shouldSkipPath, toError, truncateBody, withCache, withCacheInvalidation, wrapError } from './chunk-Y74QJH7Y.js';
11
11
  export { MemoryAPIKeyStorage, generateAPIKey, getAPIKeyStorage, hashAPIKey, isValidAPIKeyFormat, setAPIKeyStorage } from './chunk-6LS3LNIH.js';
12
- export { AggregateEndpoint, BatchCreateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, BatchUpdateEndpoint, BatchUpsertEndpoint, CreateEndpoint, DeleteEndpoint, ExportEndpoint, ImportEndpoint, ListEndpoint, ReadEndpoint, RestoreEndpoint, SearchEndpoint, UpdateEndpoint, UpsertEndpoint, VersionCompareEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionRollbackEndpoint, applyFieldSelection, applyFieldSelectionToArray, buildSearchConfig, calculateScore, computeAggregations, createCsvStream, csvToJson, escapeCsvValue, generateCsv, generateETag, generateHighlights, getSchemaFields, inferCsvContentType, jsonToCsv, matchesIfMatch, matchesIfNoneMatch, parseCsv, parseFieldSelection, parseFilterValue, parseListFilters, parseSearchFields, searchInMemory, termFrequency, tokenize, tokenizeQuery, validateCsvHeaders } from './chunk-LCRNKT65.js';
12
+ export { AggregateEndpoint, BatchCreateEndpoint, BatchDeleteEndpoint, BatchRestoreEndpoint, BatchUpdateEndpoint, BatchUpsertEndpoint, CreateEndpoint, DeleteEndpoint, ExportEndpoint, ImportEndpoint, ListEndpoint, ReadEndpoint, RestoreEndpoint, SearchEndpoint, UpdateEndpoint, UpsertEndpoint, VersionCompareEndpoint, VersionHistoryEndpoint, VersionReadEndpoint, VersionRollbackEndpoint, applyFieldSelection, applyFieldSelectionToArray, buildSearchConfig, calculateScore, computeAggregations, createCsvStream, csvToJson, escapeCsvValue, generateCsv, generateETag, generateHighlights, getSchemaFields, inferCsvContentType, jsonToCsv, matchesIfMatch, matchesIfNoneMatch, parseCsv, parseFieldSelection, parseFilterValue, parseListFilters, parseSearchFields, searchInMemory, termFrequency, tokenize, tokenizeQuery, validateCsvHeaders } from './chunk-T6J4NSMC.js';
13
13
  import { isRouteClass } from './chunk-CDCGYMC6.js';
14
14
  export { OpenAPIRoute, isRouteClass } from './chunk-CDCGYMC6.js';
15
15
  export { AuditLogger, MemoryAuditLogStorage, MemoryVersioningStorage, VersionManager, applyComputedFields, applyComputedFieldsToArray, calculateChanges, createAuditLogger, createVersionManager, decodeCursor, defineMeta, defineModel, encodeCursor, extractNestedData, extractTenantId, getAuditConfig, getAuditStorage, getMultiTenantConfig, getSoftDeleteConfig, getVersioningConfig, getVersioningStorage, isDirectNestedData, parseAggregateField, parseAggregateQuery, parseSearchMode, setAuditStorage, setVersioningStorage } from './chunk-U3SUUXJK.js';
16
- import { createRegistryWithDefault, ApiException, InputValidationException, getLogger } from './chunk-FST5S5UY.js';
16
+ import { createRegistryWithDefault, ApiException, InputValidationException, getLogger, getContextVar } from './chunk-FST5S5UY.js';
17
17
  export { AggregationException, ApiException, CacheException, ConfigurationException, ConflictException, ForbiddenException, InputValidationException, NotFoundException, StorageRegistry, UnauthorizedException, createNullableRegistry, createRegistryWithDefault, getAuthType, getRequestId as getContextRequestId, getContextVar, getLogger, getTenantId, getUser, getUserId, getUserPermissions, getUserRoles, hasAllPermissions, hasAllRoles, hasAnyRole, hasPermission, hasRole, setContextVar, setLogger } from './chunk-FST5S5UY.js';
18
18
  import { OpenAPIHono, createRoute } from '@hono/zod-openapi';
19
19
  import { HTTPException } from 'hono/http-exception';
@@ -290,6 +290,11 @@ var CrudEventEmitter = class {
290
290
  tableListeners = /* @__PURE__ */ new Map();
291
291
  /** Listeners for all events on all tables */
292
292
  globalListeners = /* @__PURE__ */ new Set();
293
+ /** Maximum listeners per event key. 0 = unlimited. */
294
+ maxListeners;
295
+ constructor(options) {
296
+ this.maxListeners = options?.maxListeners ?? 100;
297
+ }
293
298
  /**
294
299
  * Subscribe to a specific event type on a specific table.
295
300
  */
@@ -298,7 +303,13 @@ var CrudEventEmitter = class {
298
303
  if (!this.listeners.has(key)) {
299
304
  this.listeners.set(key, /* @__PURE__ */ new Set());
300
305
  }
301
- this.listeners.get(key).add(listener);
306
+ const set = this.listeners.get(key);
307
+ if (this.maxListeners > 0 && set.size >= this.maxListeners) {
308
+ getLogger().warn(`Max listeners (${this.maxListeners}) reached for event "${key}". Listener not added.`);
309
+ return { unsubscribe: () => {
310
+ } };
311
+ }
312
+ set.add(listener);
302
313
  return {
303
314
  unsubscribe: () => {
304
315
  this.listeners.get(key)?.delete(listener);
@@ -312,7 +323,13 @@ var CrudEventEmitter = class {
312
323
  if (!this.tableListeners.has(table)) {
313
324
  this.tableListeners.set(table, /* @__PURE__ */ new Set());
314
325
  }
315
- this.tableListeners.get(table).add(listener);
326
+ const set = this.tableListeners.get(table);
327
+ if (this.maxListeners > 0 && set.size >= this.maxListeners) {
328
+ getLogger().warn(`Max listeners (${this.maxListeners}) reached for table "${table}". Listener not added.`);
329
+ return { unsubscribe: () => {
330
+ } };
331
+ }
332
+ set.add(listener);
316
333
  return {
317
334
  unsubscribe: () => {
318
335
  this.tableListeners.get(table)?.delete(listener);
@@ -323,6 +340,11 @@ var CrudEventEmitter = class {
323
340
  * Subscribe to all events on all tables.
324
341
  */
325
342
  onAny(listener) {
343
+ if (this.maxListeners > 0 && this.globalListeners.size >= this.maxListeners) {
344
+ getLogger().warn(`Max global listeners (${this.maxListeners}) reached. Listener not added.`);
345
+ return { unsubscribe: () => {
346
+ } };
347
+ }
326
348
  this.globalListeners.add(listener);
327
349
  return {
328
350
  unsubscribe: () => {
@@ -413,19 +435,46 @@ function setEventEmitter(emitter) {
413
435
  }
414
436
 
415
437
  // src/endpoints/subscribe.ts
438
+ var connectionCounts = /* @__PURE__ */ new Map();
439
+ var DEFAULT_EXCLUDE_FIELDS = ["password", "token", "secret", "apiKey", "creditCard", "ssn"];
440
+ function stripSensitiveFields(data, excludeFields) {
441
+ if (data === null || data === void 0 || typeof data !== "object") {
442
+ return data;
443
+ }
444
+ if (Array.isArray(data)) {
445
+ return data.map((item) => stripSensitiveFields(item, excludeFields));
446
+ }
447
+ const result = {};
448
+ for (const [key, value] of Object.entries(data)) {
449
+ if (!excludeFields.includes(key)) {
450
+ result[key] = typeof value === "object" && value !== null ? stripSensitiveFields(value, excludeFields) : value;
451
+ }
452
+ }
453
+ return result;
454
+ }
416
455
  function createSubscribeHandler(config) {
417
456
  const {
418
457
  table,
419
458
  events: eventFilter,
420
459
  emitter: customEmitter,
421
460
  filter,
422
- heartbeatInterval = 3e4
461
+ heartbeatInterval = 3e4,
462
+ maxConnections = 1e3,
463
+ connectionTimeout = 3e5,
464
+ excludeFields = DEFAULT_EXCLUDE_FIELDS
423
465
  } = config;
424
466
  return (ctx) => {
467
+ const currentCount = connectionCounts.get(table) || 0;
468
+ if (currentCount >= maxConnections) {
469
+ return ctx.json(
470
+ { success: false, error: { code: "TOO_MANY_CONNECTIONS", message: "Too many SSE connections" } },
471
+ 503
472
+ );
473
+ }
474
+ connectionCounts.set(table, currentCount + 1);
425
475
  return streamSSE(ctx, async (stream) => {
426
476
  const emitter = customEmitter ?? getEventEmitter();
427
477
  let subscription;
428
- let heartbeatTimer = null;
429
478
  const listener = async (event) => {
430
479
  if (eventFilter && eventFilter.length > 0 && !eventFilter.includes(event.type)) {
431
480
  return;
@@ -433,6 +482,8 @@ function createSubscribeHandler(config) {
433
482
  if (filter && !filter(event, ctx)) {
434
483
  return;
435
484
  }
485
+ const sanitizedData = excludeFields.length > 0 ? stripSensitiveFields(event.data, excludeFields) : event.data;
486
+ const sanitizedPreviousData = event.previousData && excludeFields.length > 0 ? stripSensitiveFields(event.previousData, excludeFields) : event.previousData;
436
487
  try {
437
488
  await stream.writeSSE({
438
489
  event: `${event.table}.${event.type}`,
@@ -440,8 +491,8 @@ function createSubscribeHandler(config) {
440
491
  type: event.type,
441
492
  table: event.table,
442
493
  recordId: event.recordId,
443
- data: event.data,
444
- previousData: event.previousData,
494
+ data: sanitizedData,
495
+ previousData: sanitizedPreviousData,
445
496
  timestamp: event.timestamp
446
497
  }),
447
498
  id: `${event.table}-${event.recordId}-${Date.now()}`
@@ -450,23 +501,38 @@ function createSubscribeHandler(config) {
450
501
  }
451
502
  };
452
503
  subscription = emitter.onTable(table, listener);
453
- heartbeatTimer = setInterval(async () => {
454
- try {
455
- await stream.writeSSE({
456
- event: "heartbeat",
457
- data: JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString() })
458
- });
459
- } catch {
460
- }
461
- }, heartbeatInterval);
462
- stream.onAbort(() => {
504
+ const cleanup = () => {
463
505
  subscription.unsubscribe();
464
- if (heartbeatTimer) {
465
- clearInterval(heartbeatTimer);
506
+ const count = connectionCounts.get(table) || 1;
507
+ if (count <= 1) {
508
+ connectionCounts.delete(table);
509
+ } else {
510
+ connectionCounts.set(table, count - 1);
466
511
  }
512
+ };
513
+ stream.onAbort(() => {
514
+ cleanup();
467
515
  });
516
+ const startTime = Date.now();
517
+ let lastHeartbeat = startTime;
468
518
  while (!stream.closed) {
469
519
  await stream.sleep(1e3);
520
+ const now = Date.now();
521
+ if (now - startTime >= connectionTimeout) {
522
+ stream.abort();
523
+ break;
524
+ }
525
+ if (now - lastHeartbeat >= heartbeatInterval) {
526
+ lastHeartbeat = now;
527
+ try {
528
+ await stream.writeSSE({
529
+ event: "heartbeat",
530
+ data: JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString() })
531
+ });
532
+ } catch {
533
+ break;
534
+ }
535
+ }
470
536
  }
471
537
  });
472
538
  };
@@ -756,8 +822,11 @@ var MemoryIdempotencyStorage = class {
756
822
  cleanupInterval;
757
823
  /** Timestamp of last cleanup run */
758
824
  lastCleanup = 0;
825
+ /** Maximum number of entries before evicting oldest */
826
+ maxEntries;
759
827
  constructor(options) {
760
828
  this.cleanupInterval = options?.cleanupInterval ?? 6e4;
829
+ this.maxEntries = options?.maxEntries ?? 1e4;
761
830
  }
762
831
  maybeCleanup() {
763
832
  if (this.cleanupInterval <= 0) return;
@@ -792,6 +861,12 @@ var MemoryIdempotencyStorage = class {
792
861
  }
793
862
  async set(key, entry, ttlMs) {
794
863
  this.maybeCleanup();
864
+ if (this.maxEntries > 0 && this.entries.size >= this.maxEntries && !this.entries.has(key)) {
865
+ const oldestKey = this.entries.keys().next().value;
866
+ if (oldestKey !== void 0) {
867
+ this.entries.delete(oldestKey);
868
+ }
869
+ }
795
870
  this.entries.set(key, {
796
871
  entry,
797
872
  expiresAt: Date.now() + ttlMs
@@ -866,7 +941,9 @@ function idempotency(config) {
866
941
  if (!storage) {
867
942
  return next();
868
943
  }
869
- const existing = await storage.get(idempotencyKey);
944
+ const userId = getContextVar(ctx, "userId") || "anonymous";
945
+ const scopedKey = `${userId}:${idempotencyKey}`;
946
+ const existing = await storage.get(scopedKey);
870
947
  if (existing) {
871
948
  return new Response(existing.body, {
872
949
  status: existing.statusCode,
@@ -877,7 +954,7 @@ function idempotency(config) {
877
954
  }
878
955
  });
879
956
  }
880
- const locked = await storage.isLocked(idempotencyKey);
957
+ const locked = await storage.isLocked(scopedKey);
881
958
  if (locked) {
882
959
  return ctx.json(
883
960
  {
@@ -890,7 +967,7 @@ function idempotency(config) {
890
967
  409
891
968
  );
892
969
  }
893
- const acquired = await storage.lock(idempotencyKey, lockTimeoutMs);
970
+ const acquired = await storage.lock(scopedKey, lockTimeoutMs);
894
971
  if (!acquired) {
895
972
  return ctx.json(
896
973
  {
@@ -912,15 +989,15 @@ function idempotency(config) {
912
989
  headers[key] = value;
913
990
  });
914
991
  const entry = {
915
- key: idempotencyKey,
992
+ key: scopedKey,
916
993
  statusCode: response.status,
917
994
  body,
918
995
  headers,
919
996
  createdAt: Date.now()
920
997
  };
921
- await storage.set(idempotencyKey, entry, ttlMs);
998
+ await storage.set(scopedKey, entry, ttlMs);
922
999
  } finally {
923
- await storage.unlock(idempotencyKey);
1000
+ await storage.unlock(scopedKey);
924
1001
  }
925
1002
  };
926
1003
  }
@@ -1,5 +1,5 @@
1
- import { P as PathPattern, R as RedactField, d as LoggingConfig, e as LoggingStorage, a as LogEntry, c as LogQueryOptions } from '../types-B8CWg4nO.js';
2
- export { b as LogLevel, L as LoggingEnv, f as RequestBodyConfig, g as RequestLogEntry, h as ResponseBodyConfig, i as ResponseLogEntry } from '../types-B8CWg4nO.js';
1
+ import { P as PathPattern, R as RedactField, d as LoggingConfig, e as LoggingStorage, a as LogEntry, c as LogQueryOptions } from '../types-DnQWCfXp.js';
2
+ export { b as LogLevel, L as LoggingEnv, f as RequestBodyConfig, g as RequestLogEntry, h as ResponseBodyConfig, i as ResponseLogEntry } from '../types-DnQWCfXp.js';
3
3
  import { Env, Context, MiddlewareHandler } from 'hono';
4
4
 
5
5
  /**
@@ -50,10 +50,10 @@ declare function shouldExcludePath(path: string, includePaths: PathPattern[], ex
50
50
  *
51
51
  * @param ctx - Hono context
52
52
  * @param ipHeader - Header name for proxy IP (default: 'X-Forwarded-For')
53
- * @param trustProxy - Whether to trust proxy headers (default: true)
53
+ * @param trustProxy - Whether to trust proxy headers (default: false)
54
54
  * @returns The client IP address or undefined
55
55
  */
56
- declare function extractClientIp<E extends Env>(ctx: Context<E>, ipHeader?: string, trustProxy?: boolean): string | undefined;
56
+ declare function extractClientIp<E extends Env>(ctx: Context<E>, ipHeader?: string, _trustProxy?: boolean): string | undefined;
57
57
  /**
58
58
  * Extract headers from a Headers object to a plain object.
59
59
  *
@@ -1,5 +1,5 @@
1
- export { MemoryLoggingStorage } from '../chunk-IPGNLIJV.js';
2
- export { createLoggingMiddleware, extractClientIp, extractHeaders, extractQuery, extractUserId, generateRequestId, getLoggingStorage, getRequestId, getRequestStartTime, isAllowedContentType, matchPath, redactHeaders, redactObject, setLoggingStorage, shouldExcludePath, shouldRedact, truncateBody } from '../chunk-WOFE46VS.js';
1
+ export { MemoryLoggingStorage } from '../chunk-I36Y45QN.js';
2
+ export { createLoggingMiddleware, extractClientIp, extractHeaders, extractQuery, extractUserId, generateRequestId, getLoggingStorage, getRequestId, getRequestStartTime, isAllowedContentType, matchPath, redactHeaders, redactObject, setLoggingStorage, shouldExcludePath, shouldRedact, truncateBody } from '../chunk-Y74QJH7Y.js';
3
3
  import '../chunk-6LS3LNIH.js';
4
4
  import '../chunk-U3SUUXJK.js';
5
5
  import '../chunk-FST5S5UY.js';
@@ -1,5 +1,5 @@
1
- export { F as FixedWindowEntry, K as KeyExtractor, a as KeyStrategy, O as OnRateLimitExceeded, P as PathPattern, b as RateLimitAlgorithm, c as RateLimitConfig, d as RateLimitEntry, R as RateLimitEnv, e as RateLimitResult, f as RateLimitStorage, g as RateLimitTier, S as SlidingWindowEntry, T as TierFunction } from '../types-Bc7ebF5x.js';
2
- export { M as MemoryRateLimitStorage, d as MemoryRateLimitStorageOptions, R as RateLimitExceededException, e as RedisRateLimitClient, f as RedisRateLimitStorage, g as RedisRateLimitStorageOptions, h as createRateLimitMiddleware, i as extractAPIKey, j as extractIP, k as extractUserId, l as generateKey, m as getRateLimitStorage, n as matchPath, r as resetRateLimit, s as setRateLimitStorage, o as shouldSkipPath } from '../index-Dm8-ODYA.js';
1
+ export { F as FixedWindowEntry, K as KeyExtractor, a as KeyStrategy, O as OnRateLimitExceeded, P as PathPattern, b as RateLimitAlgorithm, c as RateLimitConfig, d as RateLimitEntry, R as RateLimitEnv, e as RateLimitResult, f as RateLimitStorage, g as RateLimitTier, S as SlidingWindowEntry, T as TierFunction } from '../types-BO3G_MZk.js';
2
+ export { M as MemoryRateLimitStorage, d as MemoryRateLimitStorageOptions, R as RateLimitExceededException, e as RedisRateLimitClient, f as RedisRateLimitStorage, g as RedisRateLimitStorageOptions, h as createRateLimitMiddleware, i as extractAPIKey, j as extractIP, k as extractUserId, l as generateKey, m as getRateLimitStorage, n as matchPath, r as resetRateLimit, s as setRateLimitStorage, o as shouldSkipPath } from '../index-431Ovi2o.js';
3
3
  import 'hono';
4
4
  import 'hono/http-exception';
5
5
  import 'zod';
@@ -1,5 +1,5 @@
1
1
  export { MemoryRateLimitStorage, RedisRateLimitStorage } from '../chunk-MANRQHEB.js';
2
- export { RateLimitExceededException, createRateLimitMiddleware, extractAPIKey, extractIP, extractUserId2 as extractUserId, generateKey, getRateLimitStorage, matchPath2 as matchPath, resetRateLimit, setRateLimitStorage, shouldSkipPath } from '../chunk-WOFE46VS.js';
2
+ export { RateLimitExceededException, createRateLimitMiddleware, extractAPIKey, extractIP, extractUserId2 as extractUserId, generateKey, getRateLimitStorage, matchPath2 as matchPath, resetRateLimit, setRateLimitStorage, shouldSkipPath } from '../chunk-Y74QJH7Y.js';
3
3
  import '../chunk-6LS3LNIH.js';
4
4
  import '../chunk-U3SUUXJK.js';
5
5
  import '../chunk-FST5S5UY.js';
@@ -1,6 +1,6 @@
1
1
  import { Env, MiddlewareHandler, Context } from 'hono';
2
- import { f as RateLimitStorage } from '../types-Bc7ebF5x.js';
3
- import { e as LoggingStorage } from '../types-B8CWg4nO.js';
2
+ import { f as RateLimitStorage } from '../types-BO3G_MZk.js';
3
+ import { e as LoggingStorage } from '../types-DnQWCfXp.js';
4
4
  import { f as CacheStorage } from '../types-DlIkjpdK.js';
5
5
  import { a as AuditLogStorage, c as VersioningStorage } from '../versioning-BG8-R7C0.js';
6
6
  import { M as MemoryAPIKeyStorage } from '../memory-B_NZbOQP.js';
@@ -1,5 +1,5 @@
1
1
  export { createAPIKeyStorageMiddleware, createAuditStorageMiddleware, createCacheStorageMiddleware, createLoggingStorageMiddleware, createRateLimitStorageMiddleware, createStorageMiddleware, createVersioningStorageMiddleware } from '../chunk-ZCHXXN7T.js';
2
- export { resolveAPIKeyStorage, resolveAuditStorage, resolveCacheStorage, resolveLoggingStorage, resolveRateLimitStorage, resolveVersioningStorage } from '../chunk-WOFE46VS.js';
2
+ export { resolveAPIKeyStorage, resolveAuditStorage, resolveCacheStorage, resolveLoggingStorage, resolveRateLimitStorage, resolveVersioningStorage } from '../chunk-Y74QJH7Y.js';
3
3
  import '../chunk-6LS3LNIH.js';
4
4
  import '../chunk-U3SUUXJK.js';
5
5
  export { StorageRegistry, createNullableRegistry, createRegistryWithDefault } from '../chunk-FST5S5UY.js';
@@ -192,8 +192,8 @@ interface RateLimitConfig<E extends Env = Env> {
192
192
  ipHeader?: string;
193
193
  /**
194
194
  * Whether to trust the proxy header for IP extraction.
195
- * Set to false if not behind a trusted proxy.
196
- * @default true
195
+ * Set to true if behind a trusted proxy.
196
+ * @default false
197
197
  */
198
198
  trustProxy?: boolean;
199
199
  /**
@@ -257,7 +257,7 @@ interface LoggingConfig<E extends Env = Env> {
257
257
  ipHeader?: string;
258
258
  /**
259
259
  * Whether to trust the proxy header for IP extraction.
260
- * @default true
260
+ * @default false
261
261
  */
262
262
  trustProxy?: boolean;
263
263
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono-crud",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "CRUD generator for Hono with Zod validation and OpenAPI generation",
5
5
  "author": "Kauan Guesser <contato@kauan.net>",
6
6
  "license": "MIT",