@classytic/mongokit 3.7.0 → 3.10.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/dist/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  import { r as warn, t as configureLogger } from "./logger-DJ7egMnd.mjs";
2
- import { y as LookupBuilder } from "./update-BDsQ3cdy.mjs";
3
- import { n as parseDuplicateKeyError, t as createError } from "./error-Lw9GEAro.mjs";
2
+ import { b as LookupBuilder } from "./update-CWXfFC6O.mjs";
3
+ import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-CzYwrfUP.mjs";
4
4
  import { t as actions_exports } from "./actions/index.mjs";
5
- import { t as PaginationEngine } from "./PaginationEngine-CPmZv7Ds.mjs";
6
- import { A as AuditTrailQuery, C as cachePlugin, D as withTransaction, E as isTransactionUnsupported, M as auditLogPlugin, N as aggregateHelpersPlugin, O as AggregationBuilder, S as cascadePlugin, T as Repository, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, w as HOOK_PRIORITY, x as sequentialId, y as getNextSequence } from "./validation-chain.plugin-CEyJssZl.mjs";
5
+ import { A as AuditTrailQuery, C as cachePlugin, D as withTransaction, E as isTransactionUnsupported, F as MUTATING_OPERATIONS, I as OP_REGISTRY, L as READ_OPERATIONS, M as auditLogPlugin, N as aggregateHelpersPlugin, O as AggregationBuilder, P as ALL_OPERATIONS, R as operationsByPolicyKey, S as cascadePlugin, T as Repository, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, w as HOOK_PRIORITY, x as sequentialId, y as getNextSequence } from "./validation-chain.plugin-Ccu6irtx.mjs";
6
+ import { t as PaginationEngine } from "./PaginationEngine-B93fLCcO.mjs";
7
7
  import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
8
8
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-dI0m4RBs.mjs";
9
9
  import { buildFieldTypeMap, coerceFieldValue, coerceHeuristic } from "./query/primitives/coercion.mjs";
10
10
  import { extractSchemaIndexes } from "./query/primitives/indexes.mjs";
11
- import { a as isFieldUpdateAllowed, i as getSystemManagedFields, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-DUwXPStK.mjs";
11
+ import { n as buildCrudSchemasFromMongooseSchema, r as createMemoryCache, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-D1sdCOmj.mjs";
12
12
  //#region src/query/QueryParser.ts
13
13
  /**
14
14
  * Modern Query Parser
@@ -986,4 +986,4 @@ function createRepository(Model, plugins = [], paginationConfig = {}, options =
986
986
  }
987
987
  var src_default = Repository;
988
988
  //#endregion
989
- export { AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, PaginationEngine, QueryParser, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, createTenantContext, customIdPlugin, dateSequentialId, src_default as default, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getNextSequence, getSystemManagedFields, immutableField, isFieldUpdateAllowed, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin, withTransaction };
989
+ export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MUTATING_OPERATIONS, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, createTenantContext, customIdPlugin, dateSequentialId, src_default as default, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, immutableField, isDuplicateKeyError, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin, withTransaction };
@@ -1,5 +1,6 @@
1
1
  import { n as isObjectIdInstance } from "./id-resolution-e4BT9zAV.mjs";
2
2
  import mongoose from "mongoose";
3
+ import { applyFieldRules, collectFieldsToOmit } from "@classytic/repo-core/schema";
3
4
  //#region src/utils/memory-cache.ts
4
5
  /**
5
6
  * Creates an in-memory cache adapter
@@ -51,7 +52,7 @@ function createMemoryCache(maxEntries = 1e3) {
51
52
  expiresAt: Date.now() + ttl * 1e3
52
53
  });
53
54
  },
54
- async del(key) {
55
+ async delete(key) {
55
56
  cache.delete(key);
56
57
  },
57
58
  async clear(pattern) {
@@ -68,25 +69,22 @@ function createMemoryCache(maxEntries = 1e3) {
68
69
  //#endregion
69
70
  //#region src/utils/mongooseToJsonSchema.ts
70
71
  /**
71
- * Mongoose to JSON Schema Converter with Field Rules
72
+ * Mongoose → JSON Schema converter.
72
73
  *
73
- * Generates Fastify JSON schemas from Mongoose models with declarative field rules.
74
+ * The **mongoose-specific** half of mongokit's CRUD-schema pipeline. Walks
75
+ * `Model.schema.paths` (accurate type info, including subdocuments, arrays,
76
+ * Maps, enums, length/min/max validators) and emits the four portable
77
+ * `CrudSchemas` fragments defined at `@classytic/repo-core/schema`.
74
78
  *
75
- * Field Rules (options.fieldRules):
76
- * - immutable: Field cannot be updated (omitted from update schema)
77
- * - immutableAfterCreate: Alias for immutable
78
- * - systemManaged: System-only field (omitted from create/update)
79
- * - optional: Remove from required array
80
- *
81
- * Additional Options:
82
- * - strictAdditionalProperties: Set to true to add "additionalProperties: false" to schemas
83
- * This makes Fastify reject unknown fields at validation level (default: false for backward compatibility)
84
- * - update.requireAtLeastOne: Set to true to add "minProperties: 1" to update schema
85
- * This prevents empty update payloads (default: false)
79
+ * Policy — `fieldRules` (immutable / systemManaged / optional) and the
80
+ * `create.omitFields` / `update.omitFields` lists — lives in repo-core and
81
+ * is shared across every kit. Every other kit (sqlitekit's
82
+ * `drizzleToJsonSchema`, prismakit's `prismaToJsonSchema`, ...) reuses the
83
+ * same helpers so a switch of storage backend is a one-line import swap.
86
84
  *
87
85
  * @example
88
86
  * buildCrudSchemasFromModel(Model, {
89
- * strictAdditionalProperties: true, // Reject unknown fields
87
+ * strictAdditionalProperties: true,
90
88
  * fieldRules: {
91
89
  * organizationId: { immutable: true },
92
90
  * status: { systemManaged: true },
@@ -94,9 +92,9 @@ function createMemoryCache(maxEntries = 1e3) {
94
92
  * create: { omitFields: ['verifiedAt'] },
95
93
  * update: {
96
94
  * omitFields: ['customerId'],
97
- * requireAtLeastOne: true // Reject empty updates
98
- * }
99
- * })
95
+ * requireAtLeastOne: true,
96
+ * },
97
+ * });
100
98
  */
101
99
  function isMongooseSchema(value) {
102
100
  return value instanceof mongoose.Schema;
@@ -134,89 +132,6 @@ function buildCrudSchemasFromModel(mongooseModel, options = {}) {
134
132
  return buildCrudSchemasFromMongooseSchema(mongooseModel.schema, options);
135
133
  }
136
134
  /**
137
- * Collect fields to omit from a generated schema based on field rules and
138
- * explicit omit lists. This is the single source of truth for "which fields
139
- * should NOT appear in the create/update body" — called by both the
140
- * path-based and tree-based schema builders.
141
- */
142
- function collectFieldsToOmit(options, purpose) {
143
- const result = new Set([
144
- "createdAt",
145
- "updatedAt",
146
- "__v"
147
- ]);
148
- const rules = options?.fieldRules || {};
149
- for (const [field, rule] of Object.entries(rules)) {
150
- if (rule.systemManaged) result.add(field);
151
- if (purpose === "update" && (rule.immutable || rule.immutableAfterCreate)) result.add(field);
152
- }
153
- const explicit = purpose === "create" ? options?.create?.omitFields : options?.update?.omitFields;
154
- if (explicit) for (const f of explicit) result.add(f);
155
- return result;
156
- }
157
- /**
158
- * Apply omissions + optional-overrides to a built JSON schema in-place.
159
- * Removes properties and adjusts the `required` array.
160
- */
161
- function applyFieldRules(schema, fieldsToOmit, options) {
162
- for (const field of fieldsToOmit) {
163
- if (schema.properties?.[field]) delete schema.properties[field];
164
- if (schema.required) schema.required = schema.required.filter((k) => k !== field);
165
- }
166
- const rules = options?.fieldRules || {};
167
- for (const [field, rule] of Object.entries(rules)) if (rule.optional && schema.required) schema.required = schema.required.filter((k) => k !== field);
168
- }
169
- /**
170
- * Get fields that are immutable (cannot be updated).
171
- * Delegates to `collectFieldsToOmit` internally.
172
- */
173
- function getImmutableFields(options = {}) {
174
- const immutable = [];
175
- const rules = options?.fieldRules || {};
176
- for (const [field, rule] of Object.entries(rules)) if (rule.immutable || rule.immutableAfterCreate) immutable.push(field);
177
- for (const f of options?.update?.omitFields || []) if (!immutable.includes(f)) immutable.push(f);
178
- return immutable;
179
- }
180
- /**
181
- * Get fields that are system-managed (cannot be set by users).
182
- */
183
- function getSystemManagedFields(options = {}) {
184
- const systemManaged = [];
185
- const rules = options?.fieldRules || {};
186
- for (const [field, rule] of Object.entries(rules)) if (rule.systemManaged) systemManaged.push(field);
187
- return systemManaged;
188
- }
189
- /**
190
- * Check if field is allowed in update
191
- */
192
- function isFieldUpdateAllowed(fieldName, options = {}) {
193
- const immutableFields = getImmutableFields(options);
194
- const systemManagedFields = getSystemManagedFields(options);
195
- return !immutableFields.includes(fieldName) && !systemManagedFields.includes(fieldName);
196
- }
197
- /**
198
- * Validate update body against field rules
199
- */
200
- function validateUpdateBody(body = {}, options = {}) {
201
- const violations = [];
202
- const immutableFields = getImmutableFields(options);
203
- const systemManagedFields = getSystemManagedFields(options);
204
- Object.keys(body).forEach((field) => {
205
- if (immutableFields.includes(field)) violations.push({
206
- field,
207
- reason: "Field is immutable"
208
- });
209
- else if (systemManagedFields.includes(field)) violations.push({
210
- field,
211
- reason: "Field is system-managed"
212
- });
213
- });
214
- return {
215
- valid: violations.length === 0,
216
- violations
217
- };
218
- }
219
- /**
220
135
  * Build JSON schema from Mongoose schema.paths (accurate type information)
221
136
  */
222
137
  function buildJsonSchemaFromPaths(mongooseSchema, options) {
@@ -228,6 +143,9 @@ function buildJsonSchemaFromPaths(mongooseSchema, options) {
228
143
  if (softRequiredSet.has(name)) return true;
229
144
  return schemaType?.options?.softRequired === true;
230
145
  };
146
+ const hasDefault = (schemaType) => {
147
+ return schemaType?.options?.default !== void 0;
148
+ };
231
149
  const rootFields = /* @__PURE__ */ new Map();
232
150
  for (const [path, schemaType] of Object.entries(paths)) {
233
151
  if (path === "__v") continue;
@@ -246,7 +164,7 @@ function buildJsonSchemaFromPaths(mongooseSchema, options) {
246
164
  for (const [rootField, fieldPaths] of rootFields.entries()) if (fieldPaths.length === 1 && fieldPaths[0].path === rootField) {
247
165
  const schemaType = fieldPaths[0].schemaType;
248
166
  properties[rootField] = schemaTypeToJsonSchema(schemaType, options);
249
- if (schemaType.isRequired && !isSoftRequired(rootField, schemaType)) required.push(rootField);
167
+ if (schemaType.isRequired && !isSoftRequired(rootField, schemaType) && !hasDefault(schemaType)) required.push(rootField);
250
168
  } else {
251
169
  const nestedSchema = buildNestedJsonSchema(fieldPaths, rootField, options);
252
170
  properties[rootField] = nestedSchema.schema;
@@ -631,4 +549,4 @@ function buildJsonSchemaForQuery(_tree, options) {
631
549
  return basePagination;
632
550
  }
633
551
  //#endregion
634
- export { isFieldUpdateAllowed as a, getSystemManagedFields as i, buildCrudSchemasFromMongooseSchema as n, validateUpdateBody as o, getImmutableFields as r, createMemoryCache as s, buildCrudSchemasFromModel as t };
552
+ export { buildCrudSchemasFromMongooseSchema as n, createMemoryCache as r, buildCrudSchemasFromModel as t };
@@ -1,5 +1,6 @@
1
- import { T as FieldPreset, _ as CrudSchemas, k as HttpError, ot as SchemaBuilderOptions, s as CacheAdapter, xt as ValidationResult, yt as UserContext } from "./types-BgdeBXI1.mjs";
1
+ import { O as HttpError, _t as UserContext, s as CacheAdapter, w as FieldPreset } from "./types-BUS0Zj8y.mjs";
2
2
  import mongoose, { Schema } from "mongoose";
3
+ import { CrudSchemas, SchemaBuilderOptions } from "@classytic/repo-core/schema";
3
4
 
4
5
  //#region src/utils/error.d.ts
5
6
  /**
@@ -15,6 +16,23 @@ import mongoose, { Schema } from "mongoose";
15
16
  * throw createError(403, 'Access denied');
16
17
  */
17
18
  declare function createError(status: number, message: string): HttpError;
19
+ /**
20
+ * Boolean classifier: true when `err` is an authoritative MongoDB duplicate
21
+ * key error — and ONLY then. Used by arc's outbox / idempotency adapters to
22
+ * distinguish "write already landed (idempotent no-op)" from "transient DB
23
+ * error (must retry)".
24
+ *
25
+ * Deliberately narrow — matches only:
26
+ * - `code === 11000`
27
+ * - `codeName === 'DuplicateKey'`
28
+ *
29
+ * Does NOT match `err.name === 'MongoServerError'`, which is also true for
30
+ * WriteConflict (112), NotWritablePrimary (10107), ExceededTimeLimit, and
31
+ * every other server-side error. Treating those as duplicate keys would
32
+ * cause arc's outbox to silently swallow transactional retries and lose
33
+ * events.
34
+ */
35
+ declare function isDuplicateKeyError(err: unknown): boolean;
18
36
  /** Options for parseDuplicateKeyError */
19
37
  interface ParseDuplicateKeyOptions {
20
38
  /**
@@ -161,22 +179,5 @@ declare function buildCrudSchemasFromMongooseSchema(mongooseSchema: Schema, opti
161
179
  * Build CRUD schemas from Mongoose model
162
180
  */
163
181
  declare function buildCrudSchemasFromModel(mongooseModel: mongoose.Model<unknown>, options?: SchemaBuilderOptions): CrudSchemas;
164
- /**
165
- * Get fields that are immutable (cannot be updated).
166
- * Delegates to `collectFieldsToOmit` internally.
167
- */
168
- declare function getImmutableFields(options?: SchemaBuilderOptions): string[];
169
- /**
170
- * Get fields that are system-managed (cannot be set by users).
171
- */
172
- declare function getSystemManagedFields(options?: SchemaBuilderOptions): string[];
173
- /**
174
- * Check if field is allowed in update
175
- */
176
- declare function isFieldUpdateAllowed(fieldName: string, options?: SchemaBuilderOptions): boolean;
177
- /**
178
- * Validate update body against field rules
179
- */
180
- declare function validateUpdateBody(body?: Record<string, unknown>, options?: SchemaBuilderOptions): ValidationResult;
181
182
  //#endregion
182
- export { isFieldUpdateAllowed as a, configureLogger as c, getFieldsForUser as d, getMongooseProjection as f, getSystemManagedFields as i, createFieldPreset as l, parseDuplicateKeyError as m, buildCrudSchemasFromMongooseSchema as n, validateUpdateBody as o, createError as p, getImmutableFields as r, createMemoryCache as s, buildCrudSchemasFromModel as t, filterResponseData as u };
183
+ export { createFieldPreset as a, getMongooseProjection as c, parseDuplicateKeyError as d, configureLogger as i, createError as l, buildCrudSchemasFromMongooseSchema as n, filterResponseData as o, createMemoryCache as r, getFieldsForUser as s, buildCrudSchemasFromModel as t, isDuplicateKeyError as u };
@@ -1,4 +1,4 @@
1
- import { F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, P as KeysetPaginationOptions, U as OffsetPaginationResult, a as AnyDocument, n as AggregatePaginationOptions, r as AggregatePaginationResult } from "../types-BgdeBXI1.mjs";
1
+ import { B as OffsetPaginationOptions, M as KeysetPaginationOptions, N as KeysetPaginationResult, U as PaginationConfig, V as OffsetPaginationResult, a as AnyDocument, n as AggregatePaginationOptions, r as AggregatePaginationResult } from "../types-BUS0Zj8y.mjs";
2
2
  import { Model } from "mongoose";
3
3
 
4
4
  //#region src/pagination/PaginationEngine.d.ts
@@ -1,2 +1,2 @@
1
- import { t as PaginationEngine } from "../PaginationEngine-CPmZv7Ds.mjs";
1
+ import { t as PaginationEngine } from "../PaginationEngine-B93fLCcO.mjs";
2
2
  export { PaginationEngine };
@@ -1,2 +1,2 @@
1
- import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, S as MethodRegistryRepository, T as ElasticSearchOptions, U as AuditEntry, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "../validation-chain.plugin-Be9rG04B.mjs";
1
+ import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, S as MethodRegistryRepository, T as ElasticSearchOptions, U as AuditEntry, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "../validation-chain.plugin-JkipjuOR.mjs";
2
2
  export { type AggregateHelpersMethods, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type CacheMethods, type CustomIdOptions, type DateSequentialIdOptions, type ElasticSearchOptions, type IdGenerator, type MethodRegistryRepository, type MongoOperationsMethods, type MultiTenantOptions, type ObservabilityOptions, type OperationMetric, type PrefixedIdOptions, type SequentialIdOptions, type SoftDeleteMethods, type SubdocumentMethods, type TenantContext, type TenantStore, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -1,2 +1,2 @@
1
- import { A as AuditTrailQuery, C as cachePlugin, M as auditLogPlugin, N as aggregateHelpersPlugin, S as cascadePlugin, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, x as sequentialId, y as getNextSequence } from "../validation-chain.plugin-CEyJssZl.mjs";
1
+ import { A as AuditTrailQuery, C as cachePlugin, M as auditLogPlugin, N as aggregateHelpersPlugin, S as cascadePlugin, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, x as sequentialId, y as getNextSequence } from "../validation-chain.plugin-Ccu6irtx.mjs";
2
2
  export { AuditTrailQuery, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -1,5 +1,8 @@
1
1
  import * as _$mongoose from "mongoose";
2
2
  import { ClientSession, Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
3
+ import * as _$_classytic_repo_core_repository0 from "@classytic/repo-core/repository";
4
+ import { DeleteResult, LookupPopulateResult, UpdateManyResult } from "@classytic/repo-core/repository";
5
+ import { ValidationResult } from "@classytic/repo-core/schema";
3
6
 
4
7
  //#region src/query/LookupBuilder.d.ts
5
8
  interface LookupOptions {
@@ -822,6 +825,31 @@ interface UpdateOptions extends OperationOptions {
822
825
  /** Array filters for positional operator $[<identifier>] updates */
823
826
  arrayFilters?: Record<string, unknown>[];
824
827
  }
828
+ /**
829
+ * Options for atomic findOneAndUpdate (compare-and-set primitive).
830
+ *
831
+ * Designed for outbox/lock/semaphore patterns that need a single round-trip
832
+ * match-and-mutate. Goes through the full hook pipeline (multi-tenant scope,
833
+ * soft-delete, audit) the same way `update()` does.
834
+ */
835
+ interface FindOneAndUpdateOptions extends OperationOptions {
836
+ /** Sort to disambiguate when filter matches multiple docs (e.g. FIFO claim). */
837
+ sort?: SortSpec;
838
+ /** Return doc state before or after the update. Default: 'after'. */
839
+ returnDocument?: 'before' | 'after';
840
+ /** Insert if no doc matches. Default: false. */
841
+ upsert?: boolean;
842
+ /** Array filters for positional `$[<identifier>]` updates. */
843
+ arrayFilters?: Record<string, unknown>[];
844
+ /** Allow aggregation-pipeline updates (array form). Default: false. */
845
+ updatePipeline?: boolean;
846
+ /** Run mongoose schema validators on the update. Default: true. */
847
+ runValidators?: boolean;
848
+ /** Collation for locale-aware string comparison. */
849
+ collation?: CollationOptions;
850
+ /** Maximum execution time in milliseconds. */
851
+ maxTimeMS?: number;
852
+ }
825
853
  /** Aggregate operation options */
826
854
  interface AggregateOptions extends ReadOptions {
827
855
  /** Allow aggregation to use disk for large sorts/groups */
@@ -840,11 +868,30 @@ interface AggregateOptions extends ReadOptions {
840
868
  maxPipelineStages?: number;
841
869
  }
842
870
  /** Lookup populate options */
871
+ /**
872
+ * Mongokit's `lookupPopulate` options — kit-native superset of the
873
+ * portable `LookupPopulateOptions` from repo-core. Widens two slots:
874
+ *
875
+ * - `lookups`: accepts either the portable `LookupSpec[]` (works on
876
+ * every kit) or mongokit's `LookupOptions[]` (adds `pipeline` /
877
+ * `let` / `sanitize` for mongo-correlated joins).
878
+ * - `select`: accepts mongoose's space-separated string form
879
+ * (`'name email'`) in addition to the portable array / inclusion
880
+ * map. Kept for arc + existing controller convenience.
881
+ *
882
+ * For cross-kit code: type the variable as
883
+ * `import('@classytic/repo-core/repository').LookupPopulateOptions<TDoc>`
884
+ * — every kit's `lookupPopulate` accepts that shape.
885
+ */
843
886
  interface LookupPopulateOptions extends ReadOptions {
844
887
  /** MongoDB query filters */
845
888
  filters?: Record<string, unknown>;
846
- /** Lookup configurations for $lookup joins */
847
- lookups: LookupOptions[];
889
+ /**
890
+ * Portable `LookupSpec[]` (cross-kit) or mongokit-native
891
+ * `LookupOptions[]` (with `pipeline` / `let` / `sanitize`). The
892
+ * portable shape is a structural subset, so callers can mix.
893
+ */
894
+ lookups: readonly _$_classytic_repo_core_repository0.LookupSpec[] | LookupOptions[];
848
895
  /** Sort specification */
849
896
  sort?: SortSpec | string;
850
897
  /** Page number (offset mode, 1-indexed) */
@@ -860,38 +907,13 @@ interface LookupPopulateOptions extends ReadOptions {
860
907
  /** Count strategy for offset pagination */
861
908
  countStrategy?: 'exact' | 'estimated' | 'none';
862
909
  }
863
- /** Lookup populate result */
864
- interface LookupPopulateResult<T = unknown> {
865
- data: T[];
866
- total: number;
867
- page?: number;
868
- limit: number;
869
- next?: string | null;
870
- hasMore?: boolean;
871
- }
872
- /** Delete result */
873
- interface DeleteResult {
874
- success: boolean;
875
- message: string;
876
- id?: string;
877
- soft?: boolean;
878
- count?: number;
879
- }
880
- /** Update many result */
881
- interface UpdateManyResult {
882
- matchedCount: number;
883
- modifiedCount: number;
884
- }
885
- /** Validation result */
886
- interface ValidationResult {
887
- valid: boolean;
888
- violations?: Array<{
889
- field: string;
890
- reason: string;
891
- }>;
892
- message?: string;
893
- }
894
- /** Update with validation result */
910
+ /**
911
+ * Union emitted by the validation-chain plugin. Keeps the violation shape
912
+ * from `ValidationResult` without re-exporting the type — consumers that
913
+ * need `ValidationResult`, `CrudSchemas`, `JsonSchema`, `SchemaBuilderOptions`,
914
+ * `FieldRule` or `FieldRules` import them straight from
915
+ * `@classytic/repo-core/schema`.
916
+ */
895
917
  type UpdateWithValidationResult<T> = {
896
918
  success: true;
897
919
  data: T;
@@ -1021,13 +1043,13 @@ interface RepositoryInstance {
1021
1043
  _buildContext(operation: string, options: Record<string, unknown> | object): Promise<RepositoryContext>;
1022
1044
  _handleError(error: Error): HttpError;
1023
1045
  update(id: string | ObjectId, data: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
1024
- aggregate(pipeline: PipelineStage[], options?: Record<string, unknown>): Promise<unknown[]>;
1046
+ aggregatePipeline(pipeline: PipelineStage[], options?: Record<string, unknown>): Promise<unknown[]>;
1025
1047
  getByQuery(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
1026
1048
  _executeQuery<T>(buildQuery: (Model: Model<any>) => Promise<T>): Promise<T>;
1027
1049
  [key: string]: unknown;
1028
1050
  }
1029
1051
  /** Repository operation names */
1030
- type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'delete' | 'deleteMany' | 'restore' | 'getById' | 'getByQuery' | 'getOne' | 'getAll' | 'findAll' | 'getOrCreate' | 'count' | 'exists' | 'distinct' | 'aggregate' | 'aggregatePaginate' | 'lookupPopulate' | 'bulkWrite';
1052
+ type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'findOneAndUpdate' | 'delete' | 'deleteMany' | 'restore' | 'getById' | 'getByQuery' | 'getOne' | 'getAll' | 'findAll' | 'getOrCreate' | 'count' | 'exists' | 'distinct' | 'aggregate' | 'aggregatePaginate' | 'aggregatePipeline' | 'aggregatePipelinePaginate' | 'lookupPopulate' | 'bulkWrite';
1031
1053
  /** Event lifecycle phases */
1032
1054
  type EventPhase = 'before' | 'after' | 'error';
1033
1055
  /** Repository event names (generated from template literals) */
@@ -1065,96 +1087,6 @@ interface FieldPreset {
1065
1087
  /** Additional fields for admins */
1066
1088
  admin?: string[];
1067
1089
  }
1068
- /** Field rules for schema building */
1069
- interface FieldRules {
1070
- [fieldName: string]: {
1071
- /** Field cannot be updated */immutable?: boolean; /** Alias for immutable */
1072
- immutableAfterCreate?: boolean; /** System-only field (omitted from create/update) */
1073
- systemManaged?: boolean; /** Remove from required array */
1074
- optional?: boolean;
1075
- };
1076
- }
1077
- /** Schema builder options */
1078
- interface SchemaBuilderOptions {
1079
- /** Field rules for create/update */
1080
- fieldRules?: FieldRules;
1081
- /** Strict additional properties (default: false) */
1082
- strictAdditionalProperties?: boolean;
1083
- /** Date format: 'date' | 'datetime' */
1084
- dateAs?: 'date' | 'datetime';
1085
- /** Create schema options */
1086
- create?: {
1087
- /** Fields to omit from create schema */omitFields?: string[]; /** Override required status */
1088
- requiredOverrides?: Record<string, boolean>; /** Override optional status */
1089
- optionalOverrides?: Record<string, boolean>; /** Schema overrides */
1090
- schemaOverrides?: Record<string, unknown>;
1091
- };
1092
- /**
1093
- * Field names to mark as soft-required: they remain in the generated body
1094
- * schema's `properties` (still validated when present) but are excluded
1095
- * from the `required[]` array so the client may omit them.
1096
- *
1097
- * Combines with per-path `softRequired: true` Mongoose schema option.
1098
- * Use this when the Mongoose model is owned by an upstream package and
1099
- * you can't annotate the schema directly.
1100
- *
1101
- * The DB-level `required: true` invariant is unaffected — Mongoose still
1102
- * rejects null on save. This flag only affects HTTP body validation.
1103
- */
1104
- softRequiredFields?: string[];
1105
- /** Update schema options */
1106
- update?: {
1107
- /** Fields to omit from update schema */omitFields?: string[]; /** Require at least one field to be provided (default: false) */
1108
- requireAtLeastOne?: boolean;
1109
- };
1110
- /** Query schema options */
1111
- query?: {
1112
- /** Filterable fields */filterableFields?: Record<string, {
1113
- type: string;
1114
- } | unknown>;
1115
- };
1116
- /**
1117
- * Emit OpenAPI vendor extensions (`x-*` keywords like `x-ref` for populated
1118
- * ObjectId fields). Default: `false`.
1119
- *
1120
- * Why opt-in: Ajv's strict mode THROWS on any `x-*` keyword it doesn't
1121
- * recognize (`strict mode: unknown keyword: "x-ref"`). Consumers validating
1122
- * request bodies with Ajv strict need a keyword-clean schema; consumers
1123
- * generating OpenAPI documentation want the vendor extensions. Default is
1124
- * OFF so Ajv-strict-mode consumers are safe out of the box. Turn ON to
1125
- * include `x-ref` etc. in the output (typically when feeding the schema
1126
- * into a docgen tool like @fastify/swagger or redocly).
1127
- */
1128
- openApiExtensions?: boolean;
1129
- }
1130
- /** JSON Schema type */
1131
- interface JsonSchema {
1132
- type: string;
1133
- properties?: Record<string, unknown>;
1134
- required?: string[];
1135
- additionalProperties?: boolean | unknown;
1136
- items?: unknown;
1137
- enum?: string[];
1138
- format?: string;
1139
- pattern?: string;
1140
- minProperties?: number;
1141
- maxProperties?: number;
1142
- minLength?: number;
1143
- maxLength?: number;
1144
- minimum?: number;
1145
- maximum?: number;
1146
- }
1147
- /** CRUD schemas result - framework-agnostic JSON schemas */
1148
- interface CrudSchemas {
1149
- /** JSON Schema for create request body */
1150
- createBody: JsonSchema;
1151
- /** JSON Schema for update request body */
1152
- updateBody: JsonSchema;
1153
- /** JSON Schema for route params (id validation) */
1154
- params: JsonSchema;
1155
- /** JSON Schema for list/query parameters */
1156
- listQuery: JsonSchema;
1157
- }
1158
1090
  /** Decoded cursor */
1159
1091
  interface DecodedCursor {
1160
1092
  /** Primary sort field value (rehydrated) — legacy compat */
@@ -1173,7 +1105,7 @@ interface ValidatorDefinition {
1173
1105
  /** Validator name */
1174
1106
  name: string;
1175
1107
  /** Operations to apply validator to */
1176
- operations?: Array<'create' | 'createMany' | 'update' | 'delete'>;
1108
+ operations?: Array<'create' | 'createMany' | 'update' | 'findOneAndUpdate' | 'delete'>;
1177
1109
  /** Validation function */
1178
1110
  validate: (context: RepositoryContext, repo?: RepositoryInstance) => void | Promise<void>;
1179
1111
  }
@@ -1256,15 +1188,20 @@ interface MinMaxResult {
1256
1188
  max: unknown;
1257
1189
  }
1258
1190
  /**
1259
- * Cache adapter interface - bring your own cache implementation
1260
- * Works with Redis, Memcached, in-memory, or any key-value store
1191
+ * Cache adapter interface — bring your own cache implementation.
1192
+ * Works with Redis, Memcached, in-memory, or any key-value store.
1193
+ *
1194
+ * Shape is aligned with `@classytic/repo-core/cache#CacheAdapter` and arc's
1195
+ * `CacheStore` so one adapter implementation satisfies every consumer. The
1196
+ * `delete` method is named for consistency with `Map.delete` / `Set.delete` /
1197
+ * `MinimalRepo.delete(id)` — your Redis wrapper translates to `redis.del`.
1261
1198
  *
1262
1199
  * @example Redis implementation:
1263
1200
  * ```typescript
1264
1201
  * const redisCache: CacheAdapter = {
1265
1202
  * async get(key) { return JSON.parse(await redis.get(key) || 'null'); },
1266
1203
  * async set(key, value, ttl) { await redis.setex(key, ttl, JSON.stringify(value)); },
1267
- * async del(key) { await redis.del(key); },
1204
+ * async delete(key) { await redis.del(key); },
1268
1205
  * async clear(pattern) {
1269
1206
  * const keys = await redis.keys(pattern || '*');
1270
1207
  * if (keys.length) await redis.del(...keys);
@@ -1277,8 +1214,8 @@ interface CacheAdapter {
1277
1214
  get<T = unknown>(key: string): Promise<T | null>;
1278
1215
  /** Set value with TTL in seconds */
1279
1216
  set<T = unknown>(key: string, value: T, ttl: number): Promise<void>;
1280
- /** Delete single key */
1281
- del(key: string): Promise<void>;
1217
+ /** Delete a single key. No-op when the key doesn't exist. */
1218
+ delete(key: string): Promise<void>;
1282
1219
  /** Clear keys matching pattern (optional, used for bulk invalidation) */
1283
1220
  clear?(pattern?: string): Promise<void>;
1284
1221
  }
@@ -1525,4 +1462,4 @@ type AllPluginMethods<TDoc> = {
1525
1462
  */
1526
1463
  type WithPlugins<TDoc, TRepo extends RepositoryInstance = RepositoryInstance> = TRepo & AllPluginMethods<TDoc>;
1527
1464
  //#endregion
1528
- export { ReadPreferenceType as $, InferDocument as A, LookupOptions as At, NonNullableFields as B, EventPayload as C, WithPlugins as Ct, GroupResult as D, IRequestContext as Dt, FieldRules as E, IControllerResponse as Et, KeysetPaginationResult as F, PaginationConfig as G, OffsetPaginationOptions as H, Logger as I, Plugin as J, PaginationResult as K, LookupPopulateOptions as L, JsonSchema as M, KeysOfType as N, HookMode as O, IResponseFormatter as Ot, KeysetPaginationOptions as P, ReadOptions as Q, LookupPopulateResult as R, EventHandlers as S, ValidatorDefinition as St, FieldPreset as T, IController as Tt, OffsetPaginationResult as U, ObjectId as V, OperationOptions as W, PluginType as X, PluginFunction as Y, PopulateSpec as Z, CrudSchemas as _, UpdateOptions as _t, AnyDocument as a, RequiredBy as at, DeleteResult as b, ValidationChainOptions as bt, CacheOperationOptions as c, SessionOptions as ct, CacheableOptions as d, SoftDeleteRepository as dt, RepositoryContext as et, CascadeOptions as f, SortDirection as ft, CreateOptions as g, UpdateManyResult as gt, CreateInput as h, UpdateInput as ht, AllPluginMethods as i, RepositoryOptions as it, InferRawDoc as j, HttpError as k, LookupBuilder as kt, CacheOptions as l, SoftDeleteFilterMode as lt, CollationOptions as m, Strict as mt, AggregatePaginationOptions as n, RepositoryInstance as nt, AnyModel as o, SchemaBuilderOptions as ot, CascadeRelation as p, SortSpec as pt, PartialBy as q, AggregatePaginationResult as r, RepositoryOperation as rt, CacheAdapter as s, SelectSpec as st, AggregateOptions as t, RepositoryEvent as tt, CacheStats as u, SoftDeleteOptions as ut, DecodedCursor as v, UpdateWithValidationResult as vt, EventPhase as w, WithTransactionOptions as wt, DocField as x, ValidationResult as xt, DeepPartial as y, UserContext as yt, MinMaxResult as z };
1465
+ export { RepositoryContext as $, InferRawDoc as A, OffsetPaginationOptions as B, EventPhase as C, IControllerResponse as Ct, HookMode as D, LookupOptions as Dt, GroupResult as E, LookupBuilder as Et, LookupPopulateOptions as F, PartialBy as G, OperationOptions as H, LookupPopulateResult as I, PluginType as J, Plugin as K, MinMaxResult as L, KeysetPaginationOptions as M, KeysetPaginationResult as N, HttpError as O, Logger as P, ReadPreferenceType as Q, NonNullableFields as R, EventPayload as S, IController as St, FindOneAndUpdateOptions as T, IResponseFormatter as Tt, PaginationConfig as U, OffsetPaginationResult as V, PaginationResult as W, PrioritizedHook as X, PopulateSpec as Y, ReadOptions as Z, DecodedCursor as _, UserContext as _t, AnyDocument as a, SelectSpec as at, DocField as b, WithPlugins as bt, CacheOperationOptions as c, SoftDeleteOptions as ct, CacheableOptions as d, SortSpec as dt, RepositoryEvent as et, CascadeOptions as f, Strict as ft, CreateOptions as g, UpdateWithValidationResult as gt, CreateInput as h, UpdateOptions as ht, AllPluginMethods as i, RequiredBy as it, KeysOfType as j, InferDocument as k, CacheOptions as l, SoftDeleteRepository as lt, CollationOptions as m, UpdateManyResult as mt, AggregatePaginationOptions as n, RepositoryOperation as nt, AnyModel as o, SessionOptions as ot, CascadeRelation as p, UpdateInput as pt, PluginFunction as q, AggregatePaginationResult as r, RepositoryOptions as rt, CacheAdapter as s, SoftDeleteFilterMode as st, AggregateOptions as t, RepositoryInstance as tt, CacheStats as u, SortDirection as ut, DeepPartial as v, ValidationChainOptions as vt, FieldPreset as w, IRequestContext as wt, EventHandlers as x, WithTransactionOptions as xt, DeleteResult as y, ValidatorDefinition as yt, ObjectId as z };