@classytic/mongokit 3.1.0 → 3.1.1
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/README.md +625 -465
- package/dist/actions/index.d.ts +2 -2
- package/dist/actions/index.js +3 -515
- package/dist/chunks/chunk-2ZN65ZOP.js +93 -0
- package/dist/chunks/chunk-CF6FLC2G.js +46 -0
- package/dist/chunks/chunk-CSLJ2PL2.js +1092 -0
- package/dist/chunks/chunk-IT7DCOKR.js +299 -0
- package/dist/chunks/chunk-M2XHQGZB.js +361 -0
- package/dist/chunks/chunk-SAKSLT47.js +470 -0
- package/dist/chunks/chunk-VJXDGP3C.js +14 -0
- package/dist/{index-3Nkm_Brq.d.ts → index-C2NCVxJK.d.ts} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +49 -2387
- package/dist/{mongooseToJsonSchema-CUQma8QK.d.ts → mongooseToJsonSchema-BKMxPbPp.d.ts} +1 -1
- package/dist/pagination/PaginationEngine.d.ts +1 -1
- package/dist/pagination/PaginationEngine.js +2 -368
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.js +4 -1170
- package/dist/{types-CrSoCuWu.d.ts → types-DA0rs2Jh.d.ts} +99 -5
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +3 -398
- package/package.json +8 -3
|
@@ -23,7 +23,63 @@ type PopulateSpec = string | string[] | PopulateOptions | PopulateOptions[];
|
|
|
23
23
|
/** Select specification */
|
|
24
24
|
type SelectSpec = string | string[] | Record<string, 0 | 1>;
|
|
25
25
|
/** Filter query type for MongoDB queries (compatible with Mongoose 8 & 9) */
|
|
26
|
-
type FilterQuery<
|
|
26
|
+
type FilterQuery<_T = unknown> = Record<string, unknown>;
|
|
27
|
+
/**
|
|
28
|
+
* Infer document type from a Mongoose Model
|
|
29
|
+
* @example
|
|
30
|
+
* type UserDoc = InferDocument<typeof UserModel>;
|
|
31
|
+
*/
|
|
32
|
+
type InferDocument<TModel> = TModel extends Model<infer TDoc> ? TDoc : never;
|
|
33
|
+
/**
|
|
34
|
+
* Infer raw document shape (without Mongoose Document methods)
|
|
35
|
+
* @example
|
|
36
|
+
* type User = InferRawDoc<typeof UserModel>;
|
|
37
|
+
*/
|
|
38
|
+
type InferRawDoc<TModel> = TModel extends Model<infer TDoc> ? TDoc extends Document ? Omit<TDoc, keyof Document> : TDoc : never;
|
|
39
|
+
/**
|
|
40
|
+
* Make specific fields optional
|
|
41
|
+
* @example
|
|
42
|
+
* type CreateUser = PartialBy<User, 'createdAt' | 'updatedAt'>;
|
|
43
|
+
*/
|
|
44
|
+
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
45
|
+
/**
|
|
46
|
+
* Make specific fields required
|
|
47
|
+
* @example
|
|
48
|
+
* type UserWithId = RequiredBy<User, '_id'>;
|
|
49
|
+
*/
|
|
50
|
+
type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
|
|
51
|
+
/**
|
|
52
|
+
* Extract keys of type T that have values of type V
|
|
53
|
+
* @example
|
|
54
|
+
* type StringFields = KeysOfType<User, string>; // 'name' | 'email'
|
|
55
|
+
*/
|
|
56
|
+
type KeysOfType<T, V> = {
|
|
57
|
+
[K in keyof T]: T[K] extends V ? K : never;
|
|
58
|
+
}[keyof T];
|
|
59
|
+
/**
|
|
60
|
+
* Deep partial - makes all nested properties optional
|
|
61
|
+
*/
|
|
62
|
+
type DeepPartial<T> = T extends object ? {
|
|
63
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
64
|
+
} : T;
|
|
65
|
+
/**
|
|
66
|
+
* Strict object type - prevents excess properties
|
|
67
|
+
* Use with `satisfies` for compile-time validation
|
|
68
|
+
*/
|
|
69
|
+
type Strict<T> = T & {
|
|
70
|
+
[K in Exclude<string, keyof T>]?: never;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* NonNullable fields extractor
|
|
74
|
+
*/
|
|
75
|
+
type NonNullableFields<T> = {
|
|
76
|
+
[K in keyof T]: NonNullable<T[K]>;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Create/Update input types from document
|
|
80
|
+
*/
|
|
81
|
+
type CreateInput<TDoc> = Omit<TDoc, '_id' | 'createdAt' | 'updatedAt' | '__v'>;
|
|
82
|
+
type UpdateInput<TDoc> = Partial<Omit<TDoc, '_id' | 'createdAt' | '__v'>>;
|
|
27
83
|
/** Hook execution mode */
|
|
28
84
|
type HookMode = 'sync' | 'async';
|
|
29
85
|
/** Repository options */
|
|
@@ -242,8 +298,30 @@ interface RepositoryContext {
|
|
|
242
298
|
lean?: boolean;
|
|
243
299
|
/** MongoDB session */
|
|
244
300
|
session?: ClientSession;
|
|
245
|
-
/**
|
|
301
|
+
/** Pagination filters */
|
|
302
|
+
filters?: Record<string, unknown>;
|
|
303
|
+
/** Sort specification */
|
|
304
|
+
sort?: SortSpec;
|
|
305
|
+
/** Page number (offset pagination) */
|
|
306
|
+
page?: number;
|
|
307
|
+
/** Items per page */
|
|
308
|
+
limit?: number;
|
|
309
|
+
/** Cursor for next page (keyset pagination) */
|
|
310
|
+
after?: string;
|
|
311
|
+
/** Whether this is a soft delete operation (set by softDeletePlugin) */
|
|
312
|
+
softDeleted?: boolean;
|
|
313
|
+
/** Include soft-deleted documents in queries */
|
|
246
314
|
includeDeleted?: boolean;
|
|
315
|
+
/** Skip cache for this operation */
|
|
316
|
+
skipCache?: boolean;
|
|
317
|
+
/** Custom TTL for this operation (seconds) */
|
|
318
|
+
cacheTtl?: number;
|
|
319
|
+
/** Whether result was served from cache (internal) */
|
|
320
|
+
_cacheHit?: boolean;
|
|
321
|
+
/** Cached result (internal) */
|
|
322
|
+
_cachedResult?: unknown;
|
|
323
|
+
/** IDs to cascade delete (internal) */
|
|
324
|
+
_cascadeIds?: unknown[];
|
|
247
325
|
/** Custom context data from plugins */
|
|
248
326
|
[key: string]: unknown;
|
|
249
327
|
}
|
|
@@ -272,8 +350,24 @@ interface RepositoryInstance {
|
|
|
272
350
|
hasMethod?(name: string): boolean;
|
|
273
351
|
[key: string]: unknown;
|
|
274
352
|
}
|
|
275
|
-
/** Repository
|
|
276
|
-
type
|
|
353
|
+
/** Repository operation names */
|
|
354
|
+
type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'delete' | 'deleteMany' | 'getById' | 'getByQuery' | 'getAll' | 'aggregatePaginate' | 'lookupPopulate';
|
|
355
|
+
/** Event lifecycle phases */
|
|
356
|
+
type EventPhase = 'before' | 'after' | 'error';
|
|
357
|
+
/** Repository event names (generated from template literals) */
|
|
358
|
+
type RepositoryEvent = `${EventPhase}:${RepositoryOperation}` | 'method:registered' | 'error:hook';
|
|
359
|
+
/** Type-safe event handler map */
|
|
360
|
+
type EventHandlers<TDoc = unknown> = {
|
|
361
|
+
[K in RepositoryEvent]?: K extends `after:${string}` ? (payload: {
|
|
362
|
+
context: RepositoryContext;
|
|
363
|
+
result: TDoc | TDoc[];
|
|
364
|
+
}) => void | Promise<void> : K extends `error:${string}` ? (payload: {
|
|
365
|
+
context: RepositoryContext;
|
|
366
|
+
error: Error;
|
|
367
|
+
}) => void | Promise<void> : (payload: {
|
|
368
|
+
context: RepositoryContext;
|
|
369
|
+
}) => void | Promise<void>;
|
|
370
|
+
};
|
|
277
371
|
/** Event payload */
|
|
278
372
|
interface EventPayload {
|
|
279
373
|
context: RepositoryContext;
|
|
@@ -553,4 +647,4 @@ interface HttpError extends Error {
|
|
|
553
647
|
}>;
|
|
554
648
|
}
|
|
555
649
|
|
|
556
|
-
export type {
|
|
650
|
+
export type { DecodedCursor as $, AnyDocument as A, UpdateManyResult as B, CacheAdapter as C, DeepPartial as D, UpdateWithValidationResult as E, FieldPreset as F, Plugin as G, HttpError as H, InferDocument as I, PluginFunction as J, KeysetPaginationOptions as K, RepositoryInstance as L, RepositoryOperation as M, NonNullableFields as N, OffsetPaginationOptions as O, PaginationConfig as P, EventPhase as Q, RepositoryOptions as R, SelectSpec as S, RepositoryEvent as T, UserContext as U, ValidationResult as V, WithTransactionOptions as W, EventHandlers as X, EventPayload as Y, FieldRules as Z, JsonSchema as _, OffsetPaginationResult as a, ValidatorDefinition as a0, ValidationChainOptions as a1, Logger as a2, SoftDeleteOptions as a3, SoftDeleteFilterMode as a4, SoftDeleteRepository as a5, GroupResult as a6, MinMaxResult as a7, CacheOptions as a8, CacheOperationOptions as a9, CacheStats as aa, CascadeRelation as ab, CascadeOptions as ac, KeysetPaginationResult as b, AggregatePaginationOptions as c, AggregatePaginationResult as d, PopulateSpec as e, SortSpec as f, SchemaBuilderOptions as g, CrudSchemas as h, PaginationResult as i, PluginType as j, ObjectId as k, UpdateOptions as l, RepositoryContext as m, AnyModel as n, SortDirection as o, HookMode as p, InferRawDoc as q, PartialBy as r, RequiredBy as s, KeysOfType as t, Strict as u, CreateInput as v, UpdateInput as w, OperationOptions as x, CreateOptions as y, DeleteResult as z };
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { d as buildCrudSchemasFromModel, b as buildCrudSchemasFromMongooseSchema, j as createError, c as createFieldPreset, k as createMemoryCache, f as filterResponseData, g as getFieldsForUser, e as getImmutableFields, a as getMongooseProjection, h as getSystemManagedFields, i as isFieldUpdateAllowed, v as validateUpdateBody } from '../mongooseToJsonSchema-
|
|
2
|
-
import { S as SelectSpec, e as PopulateSpec, f as SortSpec } from '../types-
|
|
1
|
+
export { d as buildCrudSchemasFromModel, b as buildCrudSchemasFromMongooseSchema, j as createError, c as createFieldPreset, k as createMemoryCache, f as filterResponseData, g as getFieldsForUser, e as getImmutableFields, a as getMongooseProjection, h as getSystemManagedFields, i as isFieldUpdateAllowed, v as validateUpdateBody } from '../mongooseToJsonSchema-BKMxPbPp.js';
|
|
2
|
+
import { S as SelectSpec, e as PopulateSpec, f as SortSpec } from '../types-DA0rs2Jh.js';
|
|
3
3
|
import 'mongoose';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/utils/index.js
CHANGED
|
@@ -1,398 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function getFieldsForUser(user, preset) {
|
|
5
|
-
if (!preset) {
|
|
6
|
-
throw new Error("Field preset is required");
|
|
7
|
-
}
|
|
8
|
-
const fields = [...preset.public || []];
|
|
9
|
-
if (user) {
|
|
10
|
-
fields.push(...preset.authenticated || []);
|
|
11
|
-
const roles = Array.isArray(user.roles) ? user.roles : user.roles ? [user.roles] : [];
|
|
12
|
-
if (roles.includes("admin") || roles.includes("superadmin")) {
|
|
13
|
-
fields.push(...preset.admin || []);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return [...new Set(fields)];
|
|
17
|
-
}
|
|
18
|
-
function getMongooseProjection(user, preset) {
|
|
19
|
-
const fields = getFieldsForUser(user, preset);
|
|
20
|
-
return fields.join(" ");
|
|
21
|
-
}
|
|
22
|
-
function filterObject(obj, allowedFields) {
|
|
23
|
-
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
24
|
-
return obj;
|
|
25
|
-
}
|
|
26
|
-
const filtered = {};
|
|
27
|
-
for (const field of allowedFields) {
|
|
28
|
-
if (field in obj) {
|
|
29
|
-
filtered[field] = obj[field];
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return filtered;
|
|
33
|
-
}
|
|
34
|
-
function filterResponseData(data, preset, user = null) {
|
|
35
|
-
const allowedFields = getFieldsForUser(user, preset);
|
|
36
|
-
if (Array.isArray(data)) {
|
|
37
|
-
return data.map((item) => filterObject(item, allowedFields));
|
|
38
|
-
}
|
|
39
|
-
return filterObject(data, allowedFields);
|
|
40
|
-
}
|
|
41
|
-
function createFieldPreset(config) {
|
|
42
|
-
return {
|
|
43
|
-
public: config.public || [],
|
|
44
|
-
authenticated: config.authenticated || [],
|
|
45
|
-
admin: config.admin || []
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function buildCrudSchemasFromMongooseSchema(mongooseSchema, options = {}) {
|
|
49
|
-
const jsonCreate = buildJsonSchemaFromPaths(mongooseSchema, options);
|
|
50
|
-
const jsonUpdate = buildJsonSchemaForUpdate(jsonCreate, options);
|
|
51
|
-
const jsonParams = {
|
|
52
|
-
type: "object",
|
|
53
|
-
properties: { id: { type: "string", pattern: "^[0-9a-fA-F]{24}$" } },
|
|
54
|
-
required: ["id"]
|
|
55
|
-
};
|
|
56
|
-
const tree = mongooseSchema?.obj || {};
|
|
57
|
-
const jsonQuery = buildJsonSchemaForQuery(tree, options);
|
|
58
|
-
return { createBody: jsonCreate, updateBody: jsonUpdate, params: jsonParams, listQuery: jsonQuery };
|
|
59
|
-
}
|
|
60
|
-
function buildCrudSchemasFromModel(mongooseModel, options = {}) {
|
|
61
|
-
if (!mongooseModel || !mongooseModel.schema) {
|
|
62
|
-
throw new Error("Invalid mongoose model");
|
|
63
|
-
}
|
|
64
|
-
return buildCrudSchemasFromMongooseSchema(mongooseModel.schema, options);
|
|
65
|
-
}
|
|
66
|
-
function getImmutableFields(options = {}) {
|
|
67
|
-
const immutable = [];
|
|
68
|
-
const fieldRules = options?.fieldRules || {};
|
|
69
|
-
Object.entries(fieldRules).forEach(([field, rules]) => {
|
|
70
|
-
if (rules.immutable || rules.immutableAfterCreate) {
|
|
71
|
-
immutable.push(field);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
(options?.update?.omitFields || []).forEach((f) => {
|
|
75
|
-
if (!immutable.includes(f)) immutable.push(f);
|
|
76
|
-
});
|
|
77
|
-
return immutable;
|
|
78
|
-
}
|
|
79
|
-
function getSystemManagedFields(options = {}) {
|
|
80
|
-
const systemManaged = [];
|
|
81
|
-
const fieldRules = options?.fieldRules || {};
|
|
82
|
-
Object.entries(fieldRules).forEach(([field, rules]) => {
|
|
83
|
-
if (rules.systemManaged) {
|
|
84
|
-
systemManaged.push(field);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
return systemManaged;
|
|
88
|
-
}
|
|
89
|
-
function isFieldUpdateAllowed(fieldName, options = {}) {
|
|
90
|
-
const immutableFields = getImmutableFields(options);
|
|
91
|
-
const systemManagedFields = getSystemManagedFields(options);
|
|
92
|
-
return !immutableFields.includes(fieldName) && !systemManagedFields.includes(fieldName);
|
|
93
|
-
}
|
|
94
|
-
function validateUpdateBody(body = {}, options = {}) {
|
|
95
|
-
const violations = [];
|
|
96
|
-
const immutableFields = getImmutableFields(options);
|
|
97
|
-
const systemManagedFields = getSystemManagedFields(options);
|
|
98
|
-
Object.keys(body).forEach((field) => {
|
|
99
|
-
if (immutableFields.includes(field)) {
|
|
100
|
-
violations.push({ field, reason: "Field is immutable" });
|
|
101
|
-
} else if (systemManagedFields.includes(field)) {
|
|
102
|
-
violations.push({ field, reason: "Field is system-managed" });
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
return {
|
|
106
|
-
valid: violations.length === 0,
|
|
107
|
-
violations
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
function buildJsonSchemaFromPaths(mongooseSchema, options) {
|
|
111
|
-
const properties = {};
|
|
112
|
-
const required = [];
|
|
113
|
-
const paths = mongooseSchema.paths;
|
|
114
|
-
const rootFields = /* @__PURE__ */ new Map();
|
|
115
|
-
for (const [path, schemaType] of Object.entries(paths)) {
|
|
116
|
-
if (path === "_id" || path === "__v") continue;
|
|
117
|
-
const parts = path.split(".");
|
|
118
|
-
const rootField = parts[0];
|
|
119
|
-
if (!rootFields.has(rootField)) {
|
|
120
|
-
rootFields.set(rootField, []);
|
|
121
|
-
}
|
|
122
|
-
rootFields.get(rootField).push({ path, schemaType });
|
|
123
|
-
}
|
|
124
|
-
for (const [rootField, fieldPaths] of rootFields.entries()) {
|
|
125
|
-
if (fieldPaths.length === 1 && fieldPaths[0].path === rootField) {
|
|
126
|
-
const schemaType = fieldPaths[0].schemaType;
|
|
127
|
-
properties[rootField] = schemaTypeToJsonSchema(schemaType);
|
|
128
|
-
if (schemaType.isRequired) {
|
|
129
|
-
required.push(rootField);
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
const nestedSchema = buildNestedJsonSchema(fieldPaths, rootField);
|
|
133
|
-
properties[rootField] = nestedSchema.schema;
|
|
134
|
-
if (nestedSchema.required) {
|
|
135
|
-
required.push(rootField);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
const schema = { type: "object", properties };
|
|
140
|
-
if (required.length) schema.required = required;
|
|
141
|
-
const fieldsToOmit = /* @__PURE__ */ new Set(["createdAt", "updatedAt", "__v"]);
|
|
142
|
-
(options?.create?.omitFields || []).forEach((f) => fieldsToOmit.add(f));
|
|
143
|
-
const fieldRules = options?.fieldRules || {};
|
|
144
|
-
Object.entries(fieldRules).forEach(([field, rules]) => {
|
|
145
|
-
if (rules.systemManaged) {
|
|
146
|
-
fieldsToOmit.add(field);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
fieldsToOmit.forEach((field) => {
|
|
150
|
-
if (schema.properties?.[field]) {
|
|
151
|
-
delete schema.properties[field];
|
|
152
|
-
}
|
|
153
|
-
if (schema.required) {
|
|
154
|
-
schema.required = schema.required.filter((k) => k !== field);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
const reqOv = options?.create?.requiredOverrides || {};
|
|
158
|
-
const optOv = options?.create?.optionalOverrides || {};
|
|
159
|
-
schema.required = schema.required || [];
|
|
160
|
-
for (const [k, v] of Object.entries(reqOv)) {
|
|
161
|
-
if (v && !schema.required.includes(k)) schema.required.push(k);
|
|
162
|
-
}
|
|
163
|
-
for (const [k, v] of Object.entries(optOv)) {
|
|
164
|
-
if (v && schema.required) schema.required = schema.required.filter((x) => x !== k);
|
|
165
|
-
}
|
|
166
|
-
Object.entries(fieldRules).forEach(([field, rules]) => {
|
|
167
|
-
if (rules.optional && schema.required) {
|
|
168
|
-
schema.required = schema.required.filter((x) => x !== field);
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
const schemaOverrides = options?.create?.schemaOverrides || {};
|
|
172
|
-
for (const [k, override] of Object.entries(schemaOverrides)) {
|
|
173
|
-
if (schema.properties?.[k]) {
|
|
174
|
-
schema.properties[k] = override;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
if (options?.strictAdditionalProperties === true) {
|
|
178
|
-
schema.additionalProperties = false;
|
|
179
|
-
}
|
|
180
|
-
return schema;
|
|
181
|
-
}
|
|
182
|
-
function buildNestedJsonSchema(fieldPaths, rootField) {
|
|
183
|
-
const properties = {};
|
|
184
|
-
const required = [];
|
|
185
|
-
let hasRequiredFields = false;
|
|
186
|
-
for (const { path, schemaType } of fieldPaths) {
|
|
187
|
-
const relativePath = path.substring(rootField.length + 1);
|
|
188
|
-
const parts = relativePath.split(".");
|
|
189
|
-
if (parts.length === 1) {
|
|
190
|
-
properties[parts[0]] = schemaTypeToJsonSchema(schemaType);
|
|
191
|
-
if (schemaType.isRequired) {
|
|
192
|
-
required.push(parts[0]);
|
|
193
|
-
hasRequiredFields = true;
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
const fieldName = parts[0];
|
|
197
|
-
if (!properties[fieldName]) {
|
|
198
|
-
properties[fieldName] = { type: "object", properties: {} };
|
|
199
|
-
}
|
|
200
|
-
const nestedObj = properties[fieldName];
|
|
201
|
-
if (!nestedObj.properties) nestedObj.properties = {};
|
|
202
|
-
const deepPath = parts.slice(1).join(".");
|
|
203
|
-
nestedObj.properties[deepPath] = schemaTypeToJsonSchema(schemaType);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
const schema = { type: "object", properties };
|
|
207
|
-
if (required.length) schema.required = required;
|
|
208
|
-
return { schema, required: hasRequiredFields };
|
|
209
|
-
}
|
|
210
|
-
function schemaTypeToJsonSchema(schemaType) {
|
|
211
|
-
const result = {};
|
|
212
|
-
const instance = schemaType.instance;
|
|
213
|
-
const options = schemaType.options || {};
|
|
214
|
-
if (instance === "String") {
|
|
215
|
-
result.type = "string";
|
|
216
|
-
if (typeof options.minlength === "number") result.minLength = options.minlength;
|
|
217
|
-
if (typeof options.maxlength === "number") result.maxLength = options.maxlength;
|
|
218
|
-
if (options.match instanceof RegExp) result.pattern = options.match.source;
|
|
219
|
-
if (options.enum && Array.isArray(options.enum)) result.enum = options.enum;
|
|
220
|
-
} else if (instance === "Number") {
|
|
221
|
-
result.type = "number";
|
|
222
|
-
if (typeof options.min === "number") result.minimum = options.min;
|
|
223
|
-
if (typeof options.max === "number") result.maximum = options.max;
|
|
224
|
-
} else if (instance === "Boolean") {
|
|
225
|
-
result.type = "boolean";
|
|
226
|
-
} else if (instance === "Date") {
|
|
227
|
-
result.type = "string";
|
|
228
|
-
result.format = "date-time";
|
|
229
|
-
} else if (instance === "ObjectId" || instance === "ObjectID") {
|
|
230
|
-
result.type = "string";
|
|
231
|
-
result.pattern = "^[0-9a-fA-F]{24}$";
|
|
232
|
-
} else if (instance === "Array") {
|
|
233
|
-
result.type = "array";
|
|
234
|
-
result.items = { type: "string" };
|
|
235
|
-
} else {
|
|
236
|
-
result.type = "object";
|
|
237
|
-
result.additionalProperties = true;
|
|
238
|
-
}
|
|
239
|
-
return result;
|
|
240
|
-
}
|
|
241
|
-
function buildJsonSchemaForUpdate(createJson, options) {
|
|
242
|
-
const clone = JSON.parse(JSON.stringify(createJson));
|
|
243
|
-
delete clone.required;
|
|
244
|
-
const fieldsToOmit = /* @__PURE__ */ new Set();
|
|
245
|
-
(options?.update?.omitFields || []).forEach((f) => fieldsToOmit.add(f));
|
|
246
|
-
const fieldRules = options?.fieldRules || {};
|
|
247
|
-
Object.entries(fieldRules).forEach(([field, rules]) => {
|
|
248
|
-
if (rules.immutable || rules.immutableAfterCreate) {
|
|
249
|
-
fieldsToOmit.add(field);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
fieldsToOmit.forEach((field) => {
|
|
253
|
-
if (clone.properties?.[field]) {
|
|
254
|
-
delete clone.properties[field];
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
if (options?.strictAdditionalProperties === true) {
|
|
258
|
-
clone.additionalProperties = false;
|
|
259
|
-
}
|
|
260
|
-
if (options?.update?.requireAtLeastOne === true) {
|
|
261
|
-
clone.minProperties = 1;
|
|
262
|
-
}
|
|
263
|
-
return clone;
|
|
264
|
-
}
|
|
265
|
-
function buildJsonSchemaForQuery(_tree, options) {
|
|
266
|
-
const basePagination = {
|
|
267
|
-
type: "object",
|
|
268
|
-
properties: {
|
|
269
|
-
page: { type: "string" },
|
|
270
|
-
limit: { type: "string" },
|
|
271
|
-
sort: { type: "string" },
|
|
272
|
-
populate: { type: "string" },
|
|
273
|
-
search: { type: "string" },
|
|
274
|
-
select: { type: "string" },
|
|
275
|
-
lean: { type: "string" },
|
|
276
|
-
includeDeleted: { type: "string" }
|
|
277
|
-
},
|
|
278
|
-
additionalProperties: true
|
|
279
|
-
};
|
|
280
|
-
const filterable = options?.query?.filterableFields || {};
|
|
281
|
-
for (const [k, v] of Object.entries(filterable)) {
|
|
282
|
-
if (basePagination.properties) {
|
|
283
|
-
basePagination.properties[k] = v && typeof v === "object" && "type" in v ? v : { type: "string" };
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
return basePagination;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// src/utils/error.ts
|
|
290
|
-
function createError(status, message) {
|
|
291
|
-
const error = new Error(message);
|
|
292
|
-
error.status = status;
|
|
293
|
-
return error;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// src/utils/memory-cache.ts
|
|
297
|
-
function createMemoryCache(maxEntries = 1e3) {
|
|
298
|
-
const cache = /* @__PURE__ */ new Map();
|
|
299
|
-
function cleanup() {
|
|
300
|
-
const now = Date.now();
|
|
301
|
-
for (const [key, entry] of cache) {
|
|
302
|
-
if (entry.expiresAt < now) {
|
|
303
|
-
cache.delete(key);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
function evictOldest() {
|
|
308
|
-
if (cache.size >= maxEntries) {
|
|
309
|
-
const firstKey = cache.keys().next().value;
|
|
310
|
-
if (firstKey) cache.delete(firstKey);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return {
|
|
314
|
-
async get(key) {
|
|
315
|
-
cleanup();
|
|
316
|
-
const entry = cache.get(key);
|
|
317
|
-
if (!entry) return null;
|
|
318
|
-
if (entry.expiresAt < Date.now()) {
|
|
319
|
-
cache.delete(key);
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
return entry.value;
|
|
323
|
-
},
|
|
324
|
-
async set(key, value, ttl) {
|
|
325
|
-
cleanup();
|
|
326
|
-
evictOldest();
|
|
327
|
-
cache.set(key, {
|
|
328
|
-
value,
|
|
329
|
-
expiresAt: Date.now() + ttl * 1e3
|
|
330
|
-
});
|
|
331
|
-
},
|
|
332
|
-
async del(key) {
|
|
333
|
-
cache.delete(key);
|
|
334
|
-
},
|
|
335
|
-
async clear(pattern) {
|
|
336
|
-
if (!pattern) {
|
|
337
|
-
cache.clear();
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
const regex = new RegExp(
|
|
341
|
-
"^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$"
|
|
342
|
-
);
|
|
343
|
-
for (const key of cache.keys()) {
|
|
344
|
-
if (regex.test(key)) {
|
|
345
|
-
cache.delete(key);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// src/utils/cache-keys.ts
|
|
353
|
-
function hashString(str) {
|
|
354
|
-
let hash = 5381;
|
|
355
|
-
for (let i = 0; i < str.length; i++) {
|
|
356
|
-
hash = (hash << 5) + hash ^ str.charCodeAt(i);
|
|
357
|
-
}
|
|
358
|
-
return (hash >>> 0).toString(16);
|
|
359
|
-
}
|
|
360
|
-
function stableStringify(obj) {
|
|
361
|
-
if (obj === null || obj === void 0) return "";
|
|
362
|
-
if (typeof obj !== "object") return String(obj);
|
|
363
|
-
if (Array.isArray(obj)) {
|
|
364
|
-
return "[" + obj.map(stableStringify).join(",") + "]";
|
|
365
|
-
}
|
|
366
|
-
const sorted = Object.keys(obj).sort().map((key) => `${key}:${stableStringify(obj[key])}`);
|
|
367
|
-
return "{" + sorted.join(",") + "}";
|
|
368
|
-
}
|
|
369
|
-
function byIdKey(prefix, model, id) {
|
|
370
|
-
return `${prefix}:id:${model}:${id}`;
|
|
371
|
-
}
|
|
372
|
-
function byQueryKey(prefix, model, query, options) {
|
|
373
|
-
const hashInput = stableStringify({ q: query, s: options?.select, p: options?.populate });
|
|
374
|
-
return `${prefix}:one:${model}:${hashString(hashInput)}`;
|
|
375
|
-
}
|
|
376
|
-
function listQueryKey(prefix, model, version, params) {
|
|
377
|
-
const hashInput = stableStringify({
|
|
378
|
-
f: params.filters,
|
|
379
|
-
s: params.sort,
|
|
380
|
-
pg: params.page,
|
|
381
|
-
lm: params.limit,
|
|
382
|
-
af: params.after,
|
|
383
|
-
sl: params.select,
|
|
384
|
-
pp: params.populate
|
|
385
|
-
});
|
|
386
|
-
return `${prefix}:list:${model}:${version}:${hashString(hashInput)}`;
|
|
387
|
-
}
|
|
388
|
-
function versionKey(prefix, model) {
|
|
389
|
-
return `${prefix}:ver:${model}`;
|
|
390
|
-
}
|
|
391
|
-
function modelPattern(prefix, model) {
|
|
392
|
-
return `${prefix}:*:${model}:*`;
|
|
393
|
-
}
|
|
394
|
-
function listPattern(prefix, model) {
|
|
395
|
-
return `${prefix}:list:${model}:*`;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, byIdKey, byQueryKey, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getSystemManagedFields, isFieldUpdateAllowed, listPattern, listQueryKey, modelPattern, validateUpdateBody, versionKey };
|
|
1
|
+
export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, createMemoryCache, getImmutableFields, getSystemManagedFields, isFieldUpdateAllowed, validateUpdateBody } from '../chunks/chunk-IT7DCOKR.js';
|
|
2
|
+
export { byIdKey, byQueryKey, createFieldPreset, filterResponseData, getFieldsForUser, getMongooseProjection, listPattern, listQueryKey, modelPattern, versionKey } from '../chunks/chunk-2ZN65ZOP.js';
|
|
3
|
+
export { createError } from '../chunks/chunk-VJXDGP3C.js';
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/mongokit",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Production-grade MongoDB repositories with zero dependencies - smart pagination, events, and plugins",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
6
7
|
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
7
9
|
"types": "./dist/index.d.ts",
|
|
8
10
|
"exports": {
|
|
9
11
|
".": {
|
|
@@ -57,7 +59,10 @@
|
|
|
57
59
|
"nextjs",
|
|
58
60
|
"typescript"
|
|
59
61
|
],
|
|
60
|
-
"author": "
|
|
62
|
+
"author": "Classytic <classytic.dev@gmail.com> (https://github.com/classytic)",
|
|
63
|
+
"contributors": [
|
|
64
|
+
"Sadman Chowdhury (https://github.com/siam923)"
|
|
65
|
+
],
|
|
61
66
|
"license": "MIT",
|
|
62
67
|
"repository": {
|
|
63
68
|
"type": "git",
|
|
@@ -92,7 +97,7 @@
|
|
|
92
97
|
"@types/node": "^22.0.0",
|
|
93
98
|
"@vitest/coverage-v8": "^3.2.4",
|
|
94
99
|
"mongodb-memory-server": "^10.2.3",
|
|
95
|
-
"mongoose": "^9.
|
|
100
|
+
"mongoose": "^9.1.3",
|
|
96
101
|
"tsup": "^8.0.0",
|
|
97
102
|
"typescript": "^5.7.0",
|
|
98
103
|
"vitest": "^3.0.0"
|