@classytic/mongokit 3.0.4 → 3.0.6

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.
@@ -1,3 +1,3 @@
1
- export { a as aggregate, c as create, _ as deleteActions, r as read, u as update } from '../index-wXrOSYWG.js';
1
+ export { a as aggregate, c as create, _ as deleteActions, r as read, u as update } from '../index-CkwbNdpJ.js';
2
2
  import 'mongoose';
3
- import '../types-DAl69QgM.js';
3
+ import '../types-DDDYo18H.js';
@@ -1,5 +1,5 @@
1
1
  import { Model, ClientSession, PipelineStage } from 'mongoose';
2
- import { A as AnyDocument, C as CreateOptions, f as ObjectId, n as OperationOptions, S as SelectSpec, g as PopulateSpec, h as SortSpec, U as UpdateOptions, p as UpdateWithValidationResult, o as UpdateManyResult, D as DeleteResult, W as GroupResult, T as LookupOptions, X as MinMaxResult } from './types-DAl69QgM.js';
2
+ import { A as AnyDocument, C as CreateOptions, h as ObjectId, n as OperationOptions, S as SelectSpec, e as PopulateSpec, f as SortSpec, U as UpdateOptions, p as UpdateWithValidationResult, o as UpdateManyResult, D as DeleteResult, X as GroupResult, T as LookupOptions, Y as MinMaxResult } from './types-DDDYo18H.js';
3
3
 
4
4
  /**
5
5
  * Create Actions
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { A as AnyDocument, e as PluginType, P as PaginationConfig, R as RepositoryOptions, f as ObjectId, S as SelectSpec, g as PopulateSpec, h as SortSpec, a as OffsetPaginationResult, b as KeysetPaginationResult, U as UpdateOptions, d as AggregatePaginationResult, i as RepositoryContext, H as HttpError } from './types-DAl69QgM.js';
2
- export { c as AggregatePaginationOptions, j as AnyModel, Y as CacheAdapter, _ as CacheOperationOptions, Z as CacheOptions, $ as CacheStats, a1 as CascadeOptions, a0 as CascadeRelation, C as CreateOptions, z as CrudSchemas, B as DecodedCursor, D as DeleteResult, E as EventPayload, F as FieldPreset, x as FieldRules, w as FilterQuery, W as GroupResult, l as HookMode, J as JsonSchema, K as KeysetPaginationOptions, L as Logger, T as LookupOptions, X as MinMaxResult, O as OffsetPaginationOptions, n as OperationOptions, m as PaginationResult, v as ParsedQuery, r as Plugin, s as PluginFunction, u as RepositoryEvent, t as RepositoryInstance, y as SchemaBuilderOptions, N as SoftDeleteFilterMode, M as SoftDeleteOptions, Q as SoftDeleteRepository, k as SortDirection, o as UpdateManyResult, p as UpdateWithValidationResult, q as UserContext, I as ValidationChainOptions, V as ValidationResult, G as ValidatorDefinition } from './types-DAl69QgM.js';
1
+ import { A as AnyDocument, g as PluginType, P as PaginationConfig, R as RepositoryOptions, h as ObjectId, S as SelectSpec, e as PopulateSpec, f as SortSpec, a as OffsetPaginationResult, b as KeysetPaginationResult, U as UpdateOptions, d as AggregatePaginationResult, W as WithTransactionOptions, i as RepositoryContext, H as HttpError } from './types-DDDYo18H.js';
2
+ export { c as AggregatePaginationOptions, j as AnyModel, Z as CacheAdapter, $ as CacheOperationOptions, _ as CacheOptions, a0 as CacheStats, a2 as CascadeOptions, a1 as CascadeRelation, C as CreateOptions, z as CrudSchemas, B as DecodedCursor, D as DeleteResult, E as EventPayload, F as FieldPreset, x as FieldRules, w as FilterQuery, X as GroupResult, l as HookMode, J as JsonSchema, K as KeysetPaginationOptions, L as Logger, T as LookupOptions, Y as MinMaxResult, O as OffsetPaginationOptions, n as OperationOptions, m as PaginationResult, v as ParsedQuery, r as Plugin, s as PluginFunction, u as RepositoryEvent, t as RepositoryInstance, y as SchemaBuilderOptions, N as SoftDeleteFilterMode, M as SoftDeleteOptions, Q as SoftDeleteRepository, k as SortDirection, o as UpdateManyResult, p as UpdateWithValidationResult, q as UserContext, I as ValidationChainOptions, V as ValidationResult, G as ValidatorDefinition } from './types-DDDYo18H.js';
3
3
  import * as mongoose from 'mongoose';
4
4
  import { Model, ClientSession, PipelineStage, PopulateOptions } from 'mongoose';
5
5
  import { PaginationEngine } from './pagination/PaginationEngine.js';
6
6
  export { aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, fieldFilterPlugin, immutableField, methodRegistryPlugin, mongoOperationsPlugin, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin } from './plugins/index.js';
7
- export { F as FilterValue, O as OperatorMap, Q as QueryParser, h as QueryParserOptions, b as createError, c as createFieldPreset, d as createMemoryCache, f as filterResponseData, g as getFieldsForUser, a as getMongooseProjection, e as queryParser } from './queryParser-Bek4yy3x.js';
8
- export { i as actions } from './index-wXrOSYWG.js';
7
+ export { F as FilterValue, O as OperatorMap, Q as QueryParser, b as QueryParserOptions, h as buildCrudSchemasFromModel, e as buildCrudSchemasFromMongooseSchema, l as createError, c as createFieldPreset, m as createMemoryCache, f as filterResponseData, g as getFieldsForUser, i as getImmutableFields, a as getMongooseProjection, j as getSystemManagedFields, k as isFieldUpdateAllowed, d as queryParser, v as validateUpdateBody } from './queryParser-Do3SgsyJ.js';
8
+ export { i as actions } from './index-CkwbNdpJ.js';
9
9
 
10
10
  /**
11
11
  * Repository Pattern - Data Access Layer
@@ -203,7 +203,8 @@ declare class Repository<TDoc = AnyDocument> {
203
203
  /**
204
204
  * Execute callback within a transaction
205
205
  */
206
- withTransaction<T>(callback: (session: ClientSession) => Promise<T>): Promise<T>;
206
+ withTransaction<T>(callback: (session: ClientSession | null) => Promise<T>, options?: WithTransactionOptions): Promise<T>;
207
+ private _isTransactionUnsupported;
207
208
  /**
208
209
  * Execute custom query with event emission
209
210
  */
@@ -238,4 +239,4 @@ declare class Repository<TDoc = AnyDocument> {
238
239
  */
239
240
  declare function createRepository<TDoc>(Model: mongoose.Model<TDoc, any, any, any>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
240
241
 
241
- export { AggregatePaginationResult, AnyDocument, HttpError, KeysetPaginationResult, ObjectId, OffsetPaginationResult, PaginationConfig, PaginationEngine, PluginType, PopulateSpec, Repository, RepositoryContext, RepositoryOptions, SelectSpec, SortSpec, UpdateOptions, createRepository, Repository as default };
242
+ export { AggregatePaginationResult, AnyDocument, HttpError, KeysetPaginationResult, ObjectId, OffsetPaginationResult, PaginationConfig, PaginationEngine, PluginType, PopulateSpec, Repository, RepositoryContext, RepositoryOptions, SelectSpec, SortSpec, UpdateOptions, WithTransactionOptions, createRepository, Repository as default };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import mongoose from 'mongoose';
1
+ import mongoose4 from 'mongoose';
2
2
 
3
3
  var __defProp = Object.defineProperty;
4
4
  var __export = (target, all) => {
@@ -521,12 +521,12 @@ function validateCursorVersion(cursorVersion, expectedVersion) {
521
521
  }
522
522
  function serializeValue(value) {
523
523
  if (value instanceof Date) return value.toISOString();
524
- if (value instanceof mongoose.Types.ObjectId) return value.toString();
524
+ if (value instanceof mongoose4.Types.ObjectId) return value.toString();
525
525
  return value;
526
526
  }
527
527
  function getValueType(value) {
528
528
  if (value instanceof Date) return "date";
529
- if (value instanceof mongoose.Types.ObjectId) return "objectid";
529
+ if (value instanceof mongoose4.Types.ObjectId) return "objectid";
530
530
  if (typeof value === "boolean") return "boolean";
531
531
  if (typeof value === "number") return "number";
532
532
  if (typeof value === "string") return "string";
@@ -537,7 +537,7 @@ function rehydrateValue(serialized, type) {
537
537
  case "date":
538
538
  return new Date(serialized);
539
539
  case "objectid":
540
- return new mongoose.Types.ObjectId(serialized);
540
+ return new mongoose4.Types.ObjectId(serialized);
541
541
  case "boolean":
542
542
  return Boolean(serialized);
543
543
  case "number":
@@ -1108,20 +1108,41 @@ var Repository = class {
1108
1108
  /**
1109
1109
  * Execute callback within a transaction
1110
1110
  */
1111
- async withTransaction(callback) {
1112
- const session = await mongoose.startSession();
1113
- session.startTransaction();
1111
+ async withTransaction(callback, options = {}) {
1112
+ const session = await mongoose4.startSession();
1113
+ let started = false;
1114
1114
  try {
1115
+ session.startTransaction();
1116
+ started = true;
1115
1117
  const result = await callback(session);
1116
1118
  await session.commitTransaction();
1117
1119
  return result;
1118
1120
  } catch (error) {
1119
- await session.abortTransaction();
1120
- throw error;
1121
+ const err = error;
1122
+ if (options.allowFallback && this._isTransactionUnsupported(err)) {
1123
+ if (typeof options.onFallback === "function") {
1124
+ options.onFallback(err);
1125
+ }
1126
+ if (started && session.inTransaction()) {
1127
+ try {
1128
+ await session.abortTransaction();
1129
+ } catch {
1130
+ }
1131
+ }
1132
+ return await callback(null);
1133
+ }
1134
+ if (started && session.inTransaction()) {
1135
+ await session.abortTransaction();
1136
+ }
1137
+ throw err;
1121
1138
  } finally {
1122
1139
  session.endSession();
1123
1140
  }
1124
1141
  }
1142
+ _isTransactionUnsupported(error) {
1143
+ const message = (error.message || "").toLowerCase();
1144
+ return message.includes("transaction numbers are only allowed on a replica set member") || message.includes("replica set") || message.includes("mongos");
1145
+ }
1125
1146
  /**
1126
1147
  * Execute custom query with event emission
1127
1148
  */
@@ -1172,11 +1193,11 @@ var Repository = class {
1172
1193
  * Handle errors with proper HTTP status codes
1173
1194
  */
1174
1195
  _handleError(error) {
1175
- if (error instanceof mongoose.Error.ValidationError) {
1196
+ if (error instanceof mongoose4.Error.ValidationError) {
1176
1197
  const messages = Object.values(error.errors).map((err) => err.message);
1177
1198
  return createError(400, `Validation Error: ${messages.join(", ")}`);
1178
1199
  }
1179
- if (error instanceof mongoose.Error.CastError) {
1200
+ if (error instanceof mongoose4.Error.CastError) {
1180
1201
  return createError(400, `Invalid ${error.path}: ${error.value}`);
1181
1202
  }
1182
1203
  if (error.status && error.message) return error;
@@ -2207,7 +2228,7 @@ function cascadePlugin(options) {
2207
2228
  }
2208
2229
  const isSoftDelete = context.softDeleted === true;
2209
2230
  const cascadeDelete = async (relation) => {
2210
- const RelatedModel = mongoose.models[relation.model];
2231
+ const RelatedModel = mongoose4.models[relation.model];
2211
2232
  if (!RelatedModel) {
2212
2233
  logger?.warn?.(`Cascade delete skipped: model '${relation.model}' not found`, {
2213
2234
  parentModel: context.model,
@@ -2298,7 +2319,7 @@ function cascadePlugin(options) {
2298
2319
  }
2299
2320
  const isSoftDelete = context.softDeleted === true;
2300
2321
  const cascadeDeleteMany = async (relation) => {
2301
- const RelatedModel = mongoose.models[relation.model];
2322
+ const RelatedModel = mongoose4.models[relation.model];
2302
2323
  if (!RelatedModel) {
2303
2324
  logger?.warn?.(`Cascade deleteMany skipped: model '${relation.model}' not found`, {
2304
2325
  parentModel: context.model
@@ -2414,6 +2435,244 @@ function createMemoryCache(maxEntries = 1e3) {
2414
2435
  }
2415
2436
  };
2416
2437
  }
2438
+ function isMongooseSchema(value) {
2439
+ return value instanceof mongoose4.Schema;
2440
+ }
2441
+ function isPlainObject(value) {
2442
+ return Object.prototype.toString.call(value) === "[object Object]";
2443
+ }
2444
+ function isObjectIdType(t) {
2445
+ return t === mongoose4.Schema.Types.ObjectId || t === mongoose4.Types.ObjectId;
2446
+ }
2447
+ function buildCrudSchemasFromMongooseSchema(mongooseSchema, options = {}) {
2448
+ const tree = mongooseSchema?.obj || {};
2449
+ const jsonCreate = buildJsonSchemaForCreate(tree, options);
2450
+ const jsonUpdate = buildJsonSchemaForUpdate(jsonCreate, options);
2451
+ const jsonParams = {
2452
+ type: "object",
2453
+ properties: { id: { type: "string", pattern: "^[0-9a-fA-F]{24}$" } },
2454
+ required: ["id"]
2455
+ };
2456
+ const jsonQuery = buildJsonSchemaForQuery(tree, options);
2457
+ return { createBody: jsonCreate, updateBody: jsonUpdate, params: jsonParams, listQuery: jsonQuery };
2458
+ }
2459
+ function buildCrudSchemasFromModel(mongooseModel, options = {}) {
2460
+ if (!mongooseModel || !mongooseModel.schema) {
2461
+ throw new Error("Invalid mongoose model");
2462
+ }
2463
+ return buildCrudSchemasFromMongooseSchema(mongooseModel.schema, options);
2464
+ }
2465
+ function getImmutableFields(options = {}) {
2466
+ const immutable = [];
2467
+ const fieldRules = options?.fieldRules || {};
2468
+ Object.entries(fieldRules).forEach(([field, rules]) => {
2469
+ if (rules.immutable || rules.immutableAfterCreate) {
2470
+ immutable.push(field);
2471
+ }
2472
+ });
2473
+ (options?.update?.omitFields || []).forEach((f) => {
2474
+ if (!immutable.includes(f)) immutable.push(f);
2475
+ });
2476
+ return immutable;
2477
+ }
2478
+ function getSystemManagedFields(options = {}) {
2479
+ const systemManaged = [];
2480
+ const fieldRules = options?.fieldRules || {};
2481
+ Object.entries(fieldRules).forEach(([field, rules]) => {
2482
+ if (rules.systemManaged) {
2483
+ systemManaged.push(field);
2484
+ }
2485
+ });
2486
+ return systemManaged;
2487
+ }
2488
+ function isFieldUpdateAllowed(fieldName, options = {}) {
2489
+ const immutableFields = getImmutableFields(options);
2490
+ const systemManagedFields = getSystemManagedFields(options);
2491
+ return !immutableFields.includes(fieldName) && !systemManagedFields.includes(fieldName);
2492
+ }
2493
+ function validateUpdateBody(body = {}, options = {}) {
2494
+ const violations = [];
2495
+ const immutableFields = getImmutableFields(options);
2496
+ const systemManagedFields = getSystemManagedFields(options);
2497
+ Object.keys(body).forEach((field) => {
2498
+ if (immutableFields.includes(field)) {
2499
+ violations.push({ field, reason: "Field is immutable" });
2500
+ } else if (systemManagedFields.includes(field)) {
2501
+ violations.push({ field, reason: "Field is system-managed" });
2502
+ }
2503
+ });
2504
+ return {
2505
+ valid: violations.length === 0,
2506
+ violations
2507
+ };
2508
+ }
2509
+ function jsonTypeFor(def, options, seen) {
2510
+ if (Array.isArray(def)) {
2511
+ if (def[0] === mongoose4.Schema.Types.Mixed) {
2512
+ return { type: "array", items: { type: "object", additionalProperties: true } };
2513
+ }
2514
+ return { type: "array", items: jsonTypeFor(def[0] ?? String, options, seen) };
2515
+ }
2516
+ if (isPlainObject(def) && "type" in def) {
2517
+ const typedDef = def;
2518
+ if (typedDef.enum && Array.isArray(typedDef.enum) && typedDef.enum.length) {
2519
+ return { type: "string", enum: typedDef.enum.map(String) };
2520
+ }
2521
+ if (Array.isArray(typedDef.type)) {
2522
+ const inner = typedDef.type[0] !== void 0 ? typedDef.type[0] : String;
2523
+ if (inner === mongoose4.Schema.Types.Mixed) {
2524
+ return { type: "array", items: { type: "object", additionalProperties: true } };
2525
+ }
2526
+ return { type: "array", items: jsonTypeFor(inner, options, seen) };
2527
+ }
2528
+ if (typedDef.type === String) return { type: "string" };
2529
+ if (typedDef.type === Number) return { type: "number" };
2530
+ if (typedDef.type === Boolean) return { type: "boolean" };
2531
+ if (typedDef.type === Date) {
2532
+ const mode = options?.dateAs || "datetime";
2533
+ return mode === "date" ? { type: "string", format: "date" } : { type: "string", format: "date-time" };
2534
+ }
2535
+ if (typedDef.type === Map || typedDef.type === mongoose4.Schema.Types.Map) {
2536
+ const ofSchema = jsonTypeFor(typedDef.of || String, options, seen);
2537
+ return { type: "object", additionalProperties: ofSchema };
2538
+ }
2539
+ if (typedDef.type === mongoose4.Schema.Types.Mixed) {
2540
+ return { type: "object", additionalProperties: true };
2541
+ }
2542
+ if (isObjectIdType(typedDef.type)) {
2543
+ return { type: "string", pattern: "^[0-9a-fA-F]{24}$" };
2544
+ }
2545
+ if (isMongooseSchema(typedDef.type)) {
2546
+ const obj = typedDef.type.obj;
2547
+ if (obj && typeof obj === "object") {
2548
+ if (seen.has(obj)) return { type: "object", additionalProperties: true };
2549
+ seen.add(obj);
2550
+ return convertTreeToJsonSchema(obj, options, seen);
2551
+ }
2552
+ }
2553
+ }
2554
+ if (def === String) return { type: "string" };
2555
+ if (def === Number) return { type: "number" };
2556
+ if (def === Boolean) return { type: "boolean" };
2557
+ if (def === Date) {
2558
+ const mode = options?.dateAs || "datetime";
2559
+ return mode === "date" ? { type: "string", format: "date" } : { type: "string", format: "date-time" };
2560
+ }
2561
+ if (isObjectIdType(def)) return { type: "string", pattern: "^[0-9a-fA-F]{24}$" };
2562
+ if (isPlainObject(def)) {
2563
+ if (seen.has(def)) return { type: "object", additionalProperties: true };
2564
+ seen.add(def);
2565
+ return convertTreeToJsonSchema(def, options, seen);
2566
+ }
2567
+ return {};
2568
+ }
2569
+ function convertTreeToJsonSchema(tree, options, seen = /* @__PURE__ */ new WeakSet()) {
2570
+ if (!tree || typeof tree !== "object") {
2571
+ return { type: "object", properties: {} };
2572
+ }
2573
+ if (seen.has(tree)) {
2574
+ return { type: "object", additionalProperties: true };
2575
+ }
2576
+ seen.add(tree);
2577
+ const properties = {};
2578
+ const required = [];
2579
+ for (const [key, val] of Object.entries(tree || {})) {
2580
+ if (key === "__v" || key === "_id" || key === "id") continue;
2581
+ const cfg = isPlainObject(val) && "type" in val ? val : { };
2582
+ properties[key] = jsonTypeFor(val, options, seen);
2583
+ if (cfg.required === true) required.push(key);
2584
+ }
2585
+ const schema = { type: "object", properties };
2586
+ if (required.length) schema.required = required;
2587
+ return schema;
2588
+ }
2589
+ function buildJsonSchemaForCreate(tree, options) {
2590
+ const base = convertTreeToJsonSchema(tree, options, /* @__PURE__ */ new WeakSet());
2591
+ const fieldsToOmit = /* @__PURE__ */ new Set(["createdAt", "updatedAt", "__v"]);
2592
+ (options?.create?.omitFields || []).forEach((f) => fieldsToOmit.add(f));
2593
+ const fieldRules = options?.fieldRules || {};
2594
+ Object.entries(fieldRules).forEach(([field, rules]) => {
2595
+ if (rules.systemManaged) {
2596
+ fieldsToOmit.add(field);
2597
+ }
2598
+ });
2599
+ fieldsToOmit.forEach((field) => {
2600
+ if (base.properties?.[field]) {
2601
+ delete base.properties[field];
2602
+ }
2603
+ if (base.required) {
2604
+ base.required = base.required.filter((k) => k !== field);
2605
+ }
2606
+ });
2607
+ const reqOv = options?.create?.requiredOverrides || {};
2608
+ const optOv = options?.create?.optionalOverrides || {};
2609
+ base.required = base.required || [];
2610
+ for (const [k, v] of Object.entries(reqOv)) {
2611
+ if (v && !base.required.includes(k)) base.required.push(k);
2612
+ }
2613
+ for (const [k, v] of Object.entries(optOv)) {
2614
+ if (v && base.required) base.required = base.required.filter((x) => x !== k);
2615
+ }
2616
+ Object.entries(fieldRules).forEach(([field, rules]) => {
2617
+ if (rules.optional && base.required) {
2618
+ base.required = base.required.filter((x) => x !== field);
2619
+ }
2620
+ });
2621
+ const schemaOverrides = options?.create?.schemaOverrides || {};
2622
+ for (const [k, override] of Object.entries(schemaOverrides)) {
2623
+ if (base.properties?.[k]) {
2624
+ base.properties[k] = override;
2625
+ }
2626
+ }
2627
+ if (options?.strictAdditionalProperties === true) {
2628
+ base.additionalProperties = false;
2629
+ }
2630
+ return base;
2631
+ }
2632
+ function buildJsonSchemaForUpdate(createJson, options) {
2633
+ const clone = JSON.parse(JSON.stringify(createJson));
2634
+ delete clone.required;
2635
+ const fieldsToOmit = /* @__PURE__ */ new Set();
2636
+ (options?.update?.omitFields || []).forEach((f) => fieldsToOmit.add(f));
2637
+ const fieldRules = options?.fieldRules || {};
2638
+ Object.entries(fieldRules).forEach(([field, rules]) => {
2639
+ if (rules.immutable || rules.immutableAfterCreate) {
2640
+ fieldsToOmit.add(field);
2641
+ }
2642
+ });
2643
+ fieldsToOmit.forEach((field) => {
2644
+ if (clone.properties?.[field]) {
2645
+ delete clone.properties[field];
2646
+ }
2647
+ });
2648
+ if (options?.strictAdditionalProperties === true) {
2649
+ clone.additionalProperties = false;
2650
+ }
2651
+ return clone;
2652
+ }
2653
+ function buildJsonSchemaForQuery(_tree, options) {
2654
+ const basePagination = {
2655
+ type: "object",
2656
+ properties: {
2657
+ page: { type: "string" },
2658
+ limit: { type: "string" },
2659
+ sort: { type: "string" },
2660
+ populate: { type: "string" },
2661
+ search: { type: "string" },
2662
+ select: { type: "string" },
2663
+ lean: { type: "string" },
2664
+ includeDeleted: { type: "string" }
2665
+ },
2666
+ additionalProperties: true
2667
+ };
2668
+ const filterable = options?.query?.filterableFields || {};
2669
+ for (const [k, v] of Object.entries(filterable)) {
2670
+ if (basePagination.properties) {
2671
+ basePagination.properties[k] = v && typeof v === "object" && "type" in v ? v : { type: "string" };
2672
+ }
2673
+ }
2674
+ return basePagination;
2675
+ }
2417
2676
  var QueryParser = class {
2418
2677
  options;
2419
2678
  operators = {
@@ -2687,7 +2946,7 @@ var QueryParser = class {
2687
2946
  const stringValue = String(value);
2688
2947
  if (stringValue === "true") return true;
2689
2948
  if (stringValue === "false") return false;
2690
- if (mongoose.Types.ObjectId.isValid(stringValue) && stringValue.length === 24) {
2949
+ if (mongoose4.Types.ObjectId.isValid(stringValue) && stringValue.length === 24) {
2691
2950
  return stringValue;
2692
2951
  }
2693
2952
  return stringValue;
@@ -2784,4 +3043,4 @@ var index_default = Repository;
2784
3043
  * ```
2785
3044
  */
2786
3045
 
2787
- export { PaginationEngine, QueryParser, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createError, createFieldPreset, createMemoryCache, createRepository, index_default as default, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, immutableField, methodRegistryPlugin, mongoOperationsPlugin, queryParser_default as queryParser, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
3046
+ export { PaginationEngine, QueryParser, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, createError, createFieldPreset, createMemoryCache, createRepository, index_default as default, fieldFilterPlugin, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getSystemManagedFields, immutableField, isFieldUpdateAllowed, methodRegistryPlugin, mongoOperationsPlugin, queryParser_default as queryParser, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin };
@@ -1,5 +1,5 @@
1
1
  import { Model } from 'mongoose';
2
- import { A as AnyDocument, P as PaginationConfig, O as OffsetPaginationOptions, a as OffsetPaginationResult, K as KeysetPaginationOptions, b as KeysetPaginationResult, c as AggregatePaginationOptions, d as AggregatePaginationResult } from '../types-DAl69QgM.js';
2
+ import { A as AnyDocument, P as PaginationConfig, O as OffsetPaginationOptions, a as OffsetPaginationResult, K as KeysetPaginationOptions, b as KeysetPaginationResult, c as AggregatePaginationOptions, d as AggregatePaginationResult } from '../types-DDDYo18H.js';
3
3
 
4
4
  /**
5
5
  * Pagination Engine
@@ -1,4 +1,4 @@
1
- import { F as FieldPreset, r as Plugin, L as Logger, M as SoftDeleteOptions, t as RepositoryInstance, G as ValidatorDefinition, I as ValidationChainOptions, i as RepositoryContext, Z as CacheOptions, a1 as CascadeOptions } from '../types-DAl69QgM.js';
1
+ import { F as FieldPreset, r as Plugin, L as Logger, M as SoftDeleteOptions, t as RepositoryInstance, G as ValidatorDefinition, I as ValidationChainOptions, i as RepositoryContext, _ as CacheOptions, a2 as CascadeOptions } from '../types-DDDYo18H.js';
2
2
  import 'mongoose';
3
3
 
4
4
  /**
@@ -1,4 +1,5 @@
1
- import { q as UserContext, F as FieldPreset, H as HttpError, Y as CacheAdapter, v as ParsedQuery } from './types-DAl69QgM.js';
1
+ import { q as UserContext, F as FieldPreset, H as HttpError, Z as CacheAdapter, y as SchemaBuilderOptions, z as CrudSchemas, V as ValidationResult, v as ParsedQuery } from './types-DDDYo18H.js';
2
+ import mongoose__default, { Schema } from 'mongoose';
2
3
 
3
4
  /**
4
5
  * Field Selection Utilities
@@ -139,6 +140,58 @@ declare function createError(status: number, message: string): HttpError;
139
140
  */
140
141
  declare function createMemoryCache(maxEntries?: number): CacheAdapter;
141
142
 
143
+ /**
144
+ * Mongoose to JSON Schema Converter with Field Rules
145
+ *
146
+ * Generates Fastify JSON schemas from Mongoose models with declarative field rules.
147
+ *
148
+ * Field Rules (options.fieldRules):
149
+ * - immutable: Field cannot be updated (omitted from update schema)
150
+ * - immutableAfterCreate: Alias for immutable
151
+ * - systemManaged: System-only field (omitted from create/update)
152
+ * - optional: Remove from required array
153
+ *
154
+ * Additional Options:
155
+ * - strictAdditionalProperties: Set to true to add "additionalProperties: false" to schemas
156
+ * This makes Fastify reject unknown fields at validation level (default: false for backward compatibility)
157
+ *
158
+ * @example
159
+ * buildCrudSchemasFromModel(Model, {
160
+ * strictAdditionalProperties: true, // Reject unknown fields
161
+ * fieldRules: {
162
+ * organizationId: { immutable: true },
163
+ * status: { systemManaged: true },
164
+ * },
165
+ * create: { omitFields: ['verifiedAt'] },
166
+ * update: { omitFields: ['customerId'] }
167
+ * })
168
+ */
169
+
170
+ /**
171
+ * Build CRUD schemas from Mongoose schema
172
+ */
173
+ declare function buildCrudSchemasFromMongooseSchema(mongooseSchema: Schema, options?: SchemaBuilderOptions): CrudSchemas;
174
+ /**
175
+ * Build CRUD schemas from Mongoose model
176
+ */
177
+ declare function buildCrudSchemasFromModel(mongooseModel: mongoose__default.Model<unknown>, options?: SchemaBuilderOptions): CrudSchemas;
178
+ /**
179
+ * Get fields that are immutable (cannot be updated)
180
+ */
181
+ declare function getImmutableFields(options?: SchemaBuilderOptions): string[];
182
+ /**
183
+ * Get fields that are system-managed (cannot be set by users)
184
+ */
185
+ declare function getSystemManagedFields(options?: SchemaBuilderOptions): string[];
186
+ /**
187
+ * Check if field is allowed in update
188
+ */
189
+ declare function isFieldUpdateAllowed(fieldName: string, options?: SchemaBuilderOptions): boolean;
190
+ /**
191
+ * Validate update body against field rules
192
+ */
193
+ declare function validateUpdateBody(body?: Record<string, unknown>, options?: SchemaBuilderOptions): ValidationResult;
194
+
142
195
  /**
143
196
  * Query Parser
144
197
  *
@@ -247,4 +300,4 @@ declare class QueryParser {
247
300
  /** Default query parser instance with standard options */
248
301
  declare const defaultQueryParser: QueryParser;
249
302
 
250
- export { type FilterValue as F, type OperatorMap as O, QueryParser as Q, getMongooseProjection as a, createError as b, createFieldPreset as c, createMemoryCache as d, defaultQueryParser as e, filterResponseData as f, getFieldsForUser as g, type QueryParserOptions as h };
303
+ export { type FilterValue as F, type OperatorMap as O, QueryParser as Q, getMongooseProjection as a, type QueryParserOptions as b, createFieldPreset as c, defaultQueryParser as d, buildCrudSchemasFromMongooseSchema as e, filterResponseData as f, getFieldsForUser as g, buildCrudSchemasFromModel as h, getImmutableFields as i, getSystemManagedFields as j, isFieldUpdateAllowed as k, createError as l, createMemoryCache as m, validateUpdateBody as v };
@@ -158,6 +158,13 @@ interface OperationOptions {
158
158
  /** Additional query filters (e.g., for soft delete) */
159
159
  query?: Record<string, unknown>;
160
160
  }
161
+ /** withTransaction options */
162
+ interface WithTransactionOptions {
163
+ /** Allow non-transactional fallback when transactions are unsupported */
164
+ allowFallback?: boolean;
165
+ /** Optional hook to observe fallback triggers */
166
+ onFallback?: (error: Error) => void;
167
+ }
161
168
  /** Create operation options */
162
169
  interface CreateOptions {
163
170
  /** MongoDB session for transactions */
@@ -348,30 +355,16 @@ interface JsonSchema {
348
355
  format?: string;
349
356
  pattern?: string;
350
357
  }
351
- /** CRUD schemas result */
358
+ /** CRUD schemas result - framework-agnostic JSON schemas */
352
359
  interface CrudSchemas {
360
+ /** JSON Schema for create request body */
353
361
  createBody: JsonSchema;
362
+ /** JSON Schema for update request body */
354
363
  updateBody: JsonSchema;
364
+ /** JSON Schema for route params (id validation) */
355
365
  params: JsonSchema;
366
+ /** JSON Schema for list/query parameters */
356
367
  listQuery: JsonSchema;
357
- crudSchemas: {
358
- create: {
359
- body: JsonSchema;
360
- };
361
- update: {
362
- body: JsonSchema;
363
- params: JsonSchema;
364
- };
365
- get: {
366
- params: JsonSchema;
367
- };
368
- list: {
369
- query: JsonSchema;
370
- };
371
- remove: {
372
- params: JsonSchema;
373
- };
374
- };
375
368
  }
376
369
  /** Decoded cursor */
377
370
  interface DecodedCursor {
@@ -580,4 +573,4 @@ interface HttpError extends Error {
580
573
  }>;
581
574
  }
582
575
 
583
- export type { CacheStats as $, AnyDocument as A, DecodedCursor as B, CreateOptions as C, DeleteResult as D, EventPayload as E, FieldPreset as F, ValidatorDefinition as G, HttpError as H, ValidationChainOptions as I, JsonSchema as J, KeysetPaginationOptions as K, Logger as L, SoftDeleteOptions as M, SoftDeleteFilterMode as N, OffsetPaginationOptions as O, PaginationConfig as P, SoftDeleteRepository as Q, RepositoryOptions as R, SelectSpec as S, LookupOptions as T, UpdateOptions as U, ValidationResult as V, GroupResult as W, MinMaxResult as X, CacheAdapter as Y, CacheOptions as Z, CacheOperationOptions as _, OffsetPaginationResult as a, CascadeRelation as a0, CascadeOptions as a1, KeysetPaginationResult as b, AggregatePaginationOptions as c, AggregatePaginationResult as d, PluginType as e, ObjectId as f, PopulateSpec as g, SortSpec as h, RepositoryContext as i, AnyModel as j, SortDirection as k, HookMode as l, PaginationResult as m, OperationOptions as n, UpdateManyResult as o, UpdateWithValidationResult as p, UserContext as q, Plugin as r, PluginFunction as s, RepositoryInstance as t, RepositoryEvent as u, ParsedQuery as v, FilterQuery as w, FieldRules as x, SchemaBuilderOptions as y, CrudSchemas as z };
576
+ export type { CacheOperationOptions as $, AnyDocument as A, DecodedCursor as B, CreateOptions as C, DeleteResult as D, EventPayload as E, FieldPreset as F, ValidatorDefinition as G, HttpError as H, ValidationChainOptions as I, JsonSchema as J, KeysetPaginationOptions as K, Logger as L, SoftDeleteOptions as M, SoftDeleteFilterMode as N, OffsetPaginationOptions as O, PaginationConfig as P, SoftDeleteRepository as Q, RepositoryOptions as R, SelectSpec as S, LookupOptions as T, UpdateOptions as U, ValidationResult as V, WithTransactionOptions as W, GroupResult as X, MinMaxResult as Y, CacheAdapter as Z, CacheOptions as _, OffsetPaginationResult as a, CacheStats as a0, CascadeRelation as a1, CascadeOptions as a2, KeysetPaginationResult as b, AggregatePaginationOptions as c, AggregatePaginationResult as d, PopulateSpec as e, SortSpec as f, PluginType as g, ObjectId as h, RepositoryContext as i, AnyModel as j, SortDirection as k, HookMode as l, PaginationResult as m, OperationOptions as n, UpdateManyResult as o, UpdateWithValidationResult as p, UserContext as q, Plugin as r, PluginFunction as s, RepositoryInstance as t, RepositoryEvent as u, ParsedQuery as v, FilterQuery as w, FieldRules as x, SchemaBuilderOptions as y, CrudSchemas as z };
@@ -1,58 +1,6 @@
1
- export { F as FilterValue, O as OperatorMap, Q as QueryParser, h as QueryParserOptions, b as createError, c as createFieldPreset, d as createMemoryCache, f as filterResponseData, g as getFieldsForUser, a as getMongooseProjection, e as queryParser } from '../queryParser-Bek4yy3x.js';
2
- import mongoose__default, { Schema } from 'mongoose';
3
- import { y as SchemaBuilderOptions, z as CrudSchemas, V as ValidationResult, S as SelectSpec, g as PopulateSpec, h as SortSpec } from '../types-DAl69QgM.js';
4
-
5
- /**
6
- * Mongoose to JSON Schema Converter with Field Rules
7
- *
8
- * Generates Fastify JSON schemas from Mongoose models with declarative field rules.
9
- *
10
- * Field Rules (options.fieldRules):
11
- * - immutable: Field cannot be updated (omitted from update schema)
12
- * - immutableAfterCreate: Alias for immutable
13
- * - systemManaged: System-only field (omitted from create/update)
14
- * - optional: Remove from required array
15
- *
16
- * Additional Options:
17
- * - strictAdditionalProperties: Set to true to add "additionalProperties: false" to schemas
18
- * This makes Fastify reject unknown fields at validation level (default: false for backward compatibility)
19
- *
20
- * @example
21
- * buildCrudSchemasFromModel(Model, {
22
- * strictAdditionalProperties: true, // Reject unknown fields
23
- * fieldRules: {
24
- * organizationId: { immutable: true },
25
- * status: { systemManaged: true },
26
- * },
27
- * create: { omitFields: ['verifiedAt'] },
28
- * update: { omitFields: ['customerId'] }
29
- * })
30
- */
31
-
32
- /**
33
- * Build CRUD schemas from Mongoose schema
34
- */
35
- declare function buildCrudSchemasFromMongooseSchema(mongooseSchema: Schema, options?: SchemaBuilderOptions): CrudSchemas;
36
- /**
37
- * Build CRUD schemas from Mongoose model
38
- */
39
- declare function buildCrudSchemasFromModel(mongooseModel: mongoose__default.Model<unknown>, options?: SchemaBuilderOptions): CrudSchemas;
40
- /**
41
- * Get fields that are immutable (cannot be updated)
42
- */
43
- declare function getImmutableFields(options?: SchemaBuilderOptions): string[];
44
- /**
45
- * Get fields that are system-managed (cannot be set by users)
46
- */
47
- declare function getSystemManagedFields(options?: SchemaBuilderOptions): string[];
48
- /**
49
- * Check if field is allowed in update
50
- */
51
- declare function isFieldUpdateAllowed(fieldName: string, options?: SchemaBuilderOptions): boolean;
52
- /**
53
- * Validate update body against field rules
54
- */
55
- declare function validateUpdateBody(body?: Record<string, unknown>, options?: SchemaBuilderOptions): ValidationResult;
1
+ export { F as FilterValue, O as OperatorMap, Q as QueryParser, b as QueryParserOptions, h as buildCrudSchemasFromModel, e as buildCrudSchemasFromMongooseSchema, l as createError, c as createFieldPreset, m as createMemoryCache, f as filterResponseData, g as getFieldsForUser, i as getImmutableFields, a as getMongooseProjection, j as getSystemManagedFields, k as isFieldUpdateAllowed, d as queryParser, v as validateUpdateBody } from '../queryParser-Do3SgsyJ.js';
2
+ import { S as SelectSpec, e as PopulateSpec, f as SortSpec } from '../types-DDDYo18H.js';
3
+ import 'mongoose';
56
4
 
57
5
  /**
58
6
  * Cache Key Utilities
@@ -131,4 +79,4 @@ declare function modelPattern(prefix: string, model: string): string;
131
79
  */
132
80
  declare function listPattern(prefix: string, model: string): string;
133
81
 
134
- export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, byIdKey, byQueryKey, getImmutableFields, getSystemManagedFields, isFieldUpdateAllowed, listPattern, listQueryKey, modelPattern, validateUpdateBody, versionKey };
82
+ export { byIdKey, byQueryKey, listPattern, listQueryKey, modelPattern, versionKey };
@@ -379,14 +379,7 @@ function buildCrudSchemasFromMongooseSchema(mongooseSchema, options = {}) {
379
379
  required: ["id"]
380
380
  };
381
381
  const jsonQuery = buildJsonSchemaForQuery(tree, options);
382
- const crudSchemas = {
383
- create: { body: jsonCreate },
384
- update: { body: jsonUpdate, params: jsonParams },
385
- get: { params: jsonParams },
386
- list: { query: jsonQuery },
387
- remove: { params: jsonParams }
388
- };
389
- return { createBody: jsonCreate, updateBody: jsonUpdate, params: jsonParams, listQuery: jsonQuery, crudSchemas };
382
+ return { createBody: jsonCreate, updateBody: jsonUpdate, params: jsonParams, listQuery: jsonQuery };
390
383
  }
391
384
  function buildCrudSchemasFromModel(mongooseModel, options = {}) {
392
385
  if (!mongooseModel || !mongooseModel.schema) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/mongokit",
3
- "version": "3.0.4",
3
+ "version": "3.0.6",
4
4
  "description": "Production-grade MongoDB repositories with zero dependencies - smart pagination, events, and plugins",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",