@classytic/mongokit 3.12.0 → 3.13.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 +126 -4
- package/dist/{PaginationEngine-BDaAsyPn.mjs → PaginationEngine-CfQRM1yz.mjs} +16 -16
- package/dist/Repository-d5gWSROf.mjs +3603 -0
- package/dist/actions/index.d.mts +1 -1
- package/dist/actions/index.mjs +1 -1
- package/dist/adapter/index.d.mts +190 -0
- package/dist/adapter/index.mjs +2 -0
- package/dist/adapter-DTDMnUOH.mjs +158 -0
- package/dist/ai/index.d.mts +1 -1
- package/dist/ai/index.mjs +1 -1
- package/dist/better-auth/index.d.mts +149 -0
- package/dist/better-auth/index.mjs +81 -0
- package/dist/field-selection-Br3WPOEb.mjs +99 -0
- package/dist/{index-D9cORR-o.d.mts → index-BbHaKwi5.d.mts} +33 -13
- package/dist/index.d.mts +690 -30
- package/dist/index.mjs +240 -17
- package/dist/{logger-8-VS1za6.mjs → logger-DprijP5h.mjs} +1 -5
- package/dist/{mongooseToJsonSchema-B187ufOP.mjs → mongooseToJsonSchema-BYbG894y.mjs} +1 -1
- package/dist/{mongooseToJsonSchema-rtkdbBhf.d.mts → mongooseToJsonSchema-BgRAaXX-.d.mts} +2 -1
- package/dist/pagination/PaginationEngine.d.mts +2 -2
- package/dist/pagination/PaginationEngine.mjs +1 -1
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/index.mjs +2 -2
- package/dist/query/primitives/coercion.d.mts +1 -1
- package/dist/query/primitives/coercion.mjs +1 -1
- package/dist/query/primitives/indexes.d.mts +1 -1
- package/dist/{types-DXscZnLu.d.mts → types-BbaPZleN.d.mts} +371 -112
- package/dist/{update-CDxPtpsk.mjs → update-C-3viiw1.mjs} +112 -28
- package/dist/utils/index.d.mts +2 -91
- package/dist/utils/index.mjs +5 -5
- package/dist/validation-chain.plugin-BLVp_Pw2.mjs +2399 -0
- package/dist/{validation-chain.plugin-CH-xPoKM.d.mts → validation-chain.plugin-CAPYCj_T.d.mts} +212 -80
- package/package.json +20 -3
- package/dist/field-selection-dI0m4RBs.mjs +0 -221
- package/dist/validation-chain.plugin-BicIgzzt.mjs +0 -4970
- /package/dist/{coercion-oM8G4b6m.d.mts → coercion-TRMmR49h.d.mts} +0 -0
- /package/dist/{id-resolution-e4BT9zAV.mjs → id-resolution-B0Odvvou.mjs} +0 -0
- /package/dist/{indexes-C26GNZRU.d.mts → indexes-CgOF7Wjd.d.mts} +0 -0
package/dist/actions/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-
|
|
1
|
+
import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-BbHaKwi5.mjs";
|
|
2
2
|
export { aggregate_d_exports as aggregate, create_d_exports as create, delete_d_exports as deleteActions, read_d_exports as read, update_d_exports as update };
|
package/dist/actions/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "../chunk-CfYAbeIz.mjs";
|
|
2
|
-
import { g as create_exports, i as update_exports, p as delete_exports, u as read_exports, v as aggregate_exports } from "../update-
|
|
2
|
+
import { g as create_exports, i as update_exports, p as delete_exports, u as read_exports, v as aggregate_exports } from "../update-C-3viiw1.mjs";
|
|
3
3
|
//#region src/actions/index.ts
|
|
4
4
|
var actions_exports = /* @__PURE__ */ __exportAll({
|
|
5
5
|
aggregate: () => aggregate_exports,
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Document, Model } from "mongoose";
|
|
2
|
+
import { StandardRepo } from "@classytic/repo-core/repository";
|
|
3
|
+
import { SchemaBuilderOptions, SchemaGenerator } from "@classytic/repo-core/schema";
|
|
4
|
+
import { AdapterRepositoryInput, AdapterSchemaContext, AdapterValidationResult, DataAdapter, OpenApiSchemas, RepositoryLike, SchemaMetadata } from "@classytic/repo-core/adapter";
|
|
5
|
+
|
|
6
|
+
//#region src/adapter/mongoose-adapter.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Options for creating a Mongoose adapter.
|
|
9
|
+
*
|
|
10
|
+
* `TDoc` is auto-inferred from the Mongoose model — no explicit type
|
|
11
|
+
* needed in most call sites.
|
|
12
|
+
*/
|
|
13
|
+
interface MongooseAdapterOptions<TDoc = unknown> {
|
|
14
|
+
/** Mongoose model instance — preserves document type for type safety. */
|
|
15
|
+
model: Model<TDoc>;
|
|
16
|
+
/**
|
|
17
|
+
* Repository implementing CRUD operations.
|
|
18
|
+
*
|
|
19
|
+
* Typed as `AdapterRepositoryInput<TDoc>` (permissive structural shape)
|
|
20
|
+
* so kit-native repository classes plug in directly. See
|
|
21
|
+
* `AdapterRepositoryInput` JSDoc in `@classytic/repo-core/adapter` for
|
|
22
|
+
* why the wider input exists at the boundary.
|
|
23
|
+
*/
|
|
24
|
+
repository: AdapterRepositoryInput<TDoc>;
|
|
25
|
+
/**
|
|
26
|
+
* External schema generator plugin for OpenAPI / introspection docs.
|
|
27
|
+
* When provided, the adapter delegates schema generation to it.
|
|
28
|
+
*
|
|
29
|
+
* **Model type is intentionally `Model<unknown>`, not `Model<TDoc>`**:
|
|
30
|
+
* schema generators introspect `.schema.paths` — they read metadata,
|
|
31
|
+
* not document types. Typing as `Model<TDoc>` would force every host
|
|
32
|
+
* to cast `m as unknown as Model<unknown>` when handing the model to
|
|
33
|
+
* `buildCrudSchemasFromModel`, because `Model<T>` is invariant in T.
|
|
34
|
+
* Widening here at the callback boundary trades one documented internal
|
|
35
|
+
* cast for zero host-side casts.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { buildCrudSchemasFromModel, createMongooseAdapter } from '@classytic/mongokit';
|
|
40
|
+
* import { createMongooseAdapter as fromAdapterSubpath } from '@classytic/mongokit/adapter';
|
|
41
|
+
*
|
|
42
|
+
* createMongooseAdapter({
|
|
43
|
+
* model: ProductModel,
|
|
44
|
+
* repository: productRepository,
|
|
45
|
+
* schemaGenerator: buildCrudSchemasFromModel,
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
schemaGenerator?: SchemaGenerator<Model<unknown>>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Mongoose data adapter — implements the `DataAdapter<TDoc>` contract from
|
|
53
|
+
* `@classytic/repo-core/adapter`.
|
|
54
|
+
*/
|
|
55
|
+
declare class MongooseAdapter<TDoc = unknown> implements DataAdapter<TDoc> {
|
|
56
|
+
readonly type: "mongoose";
|
|
57
|
+
readonly name: string;
|
|
58
|
+
readonly model: Model<TDoc>;
|
|
59
|
+
readonly repository: RepositoryLike<TDoc>;
|
|
60
|
+
private readonly schemaGenerator?;
|
|
61
|
+
constructor(options: MongooseAdapterOptions<TDoc>);
|
|
62
|
+
/**
|
|
63
|
+
* Lightweight path-existence check for hosts that infer absent fields
|
|
64
|
+
* (e.g. arc's `defineResource()` infers `tenantField: false` when the
|
|
65
|
+
* configured tenant field doesn't exist on the model). Reads
|
|
66
|
+
* `model.schema.paths[name]` directly — no allocation, no metadata walk.
|
|
67
|
+
*/
|
|
68
|
+
hasFieldPath(name: string): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Get schema metadata from a Mongoose model.
|
|
71
|
+
*/
|
|
72
|
+
getSchemaMetadata(): SchemaMetadata;
|
|
73
|
+
/**
|
|
74
|
+
* Generate OpenAPI-shaped schemas from the model.
|
|
75
|
+
*
|
|
76
|
+
* Delegates to the `schemaGenerator` callback supplied at construction.
|
|
77
|
+
* Returns `null` when no callback is configured — hosts treat that as
|
|
78
|
+
* "no schemas available" and skip OpenAPI generation for the resource.
|
|
79
|
+
*
|
|
80
|
+
* Most call sites pass mongokit's own `buildCrudSchemasFromModel`:
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* createMongooseAdapter({
|
|
84
|
+
* model,
|
|
85
|
+
* repository,
|
|
86
|
+
* schemaGenerator: buildCrudSchemasFromModel,
|
|
87
|
+
* });
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
generateSchemas(schemaOptions?: SchemaBuilderOptions, context?: AdapterSchemaContext): OpenApiSchemas | Record<string, unknown> | null;
|
|
91
|
+
/**
|
|
92
|
+
* Default `validate` — kits typically override or hosts skip this since
|
|
93
|
+
* Mongoose enforces validation at save time. Provided for adapter-
|
|
94
|
+
* contract completeness so consumers that branch on `adapter.validate`
|
|
95
|
+
* find a no-op success path.
|
|
96
|
+
*/
|
|
97
|
+
validate(_data: unknown): AdapterValidationResult;
|
|
98
|
+
/**
|
|
99
|
+
* Extract relation metadata from Mongoose ref paths.
|
|
100
|
+
*
|
|
101
|
+
* Cardinality rules:
|
|
102
|
+
* - `field: { type: ObjectId, ref }` → one-to-one (single ref)
|
|
103
|
+
* - `field: [{ type: ObjectId, ref }]` → one-to-many (array of refs)
|
|
104
|
+
*
|
|
105
|
+
* Mongoose surfaces array paths with `instance === 'Array'` and a
|
|
106
|
+
* `caster` SchemaType holding the element-side metadata (including the
|
|
107
|
+
* `ref` and inner instance). Reading the caster lets us distinguish
|
|
108
|
+
* one-to-many from a scalar-array path that just happens to declare
|
|
109
|
+
* something else. Pre-3.12.x this method always returned `'one-to-one'`
|
|
110
|
+
* regardless of cardinality — wrong metadata for OpenAPI / docs hosts.
|
|
111
|
+
*/
|
|
112
|
+
private extractRelations;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Create a Mongoose adapter — produces a framework-agnostic
|
|
116
|
+
* `DataAdapter<TDoc>` that any host consuming
|
|
117
|
+
* `@classytic/repo-core/adapter` can wire in.
|
|
118
|
+
*
|
|
119
|
+
* Two call shapes:
|
|
120
|
+
*
|
|
121
|
+
* ```ts
|
|
122
|
+
* // Object form (explicit)
|
|
123
|
+
* const adapter = createMongooseAdapter({
|
|
124
|
+
* model: ProductModel,
|
|
125
|
+
* repository: productRepository,
|
|
126
|
+
* schemaGenerator: buildCrudSchemasFromModel,
|
|
127
|
+
* });
|
|
128
|
+
*
|
|
129
|
+
* // Shorthand form (2-arg) — most common path
|
|
130
|
+
* const adapter = createMongooseAdapter(ProductModel, productRepository);
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
declare function createMongooseAdapter<TDoc = unknown>(model: Model<TDoc>, repository: AdapterRepositoryInput<TDoc>): DataAdapter<TDoc>;
|
|
134
|
+
declare function createMongooseAdapter<TDoc = unknown>(options: MongooseAdapterOptions<TDoc>): DataAdapter<TDoc>;
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/adapter/types.d.ts
|
|
137
|
+
/**
|
|
138
|
+
* Infer document type from a Mongoose model.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* const ProductModel = mongoose.model('Product', productSchema);
|
|
142
|
+
* type ProductDoc = InferMongooseDoc<typeof ProductModel>;
|
|
143
|
+
*/
|
|
144
|
+
type InferMongooseDoc<M> = M extends Model<infer D> ? D : never;
|
|
145
|
+
/**
|
|
146
|
+
* Infer document type from a `Repository<TDoc>` / `StandardRepo<TDoc>`.
|
|
147
|
+
*/
|
|
148
|
+
type InferRepoDoc<R> = R extends StandardRepo<infer D> ? D : never;
|
|
149
|
+
/**
|
|
150
|
+
* Infer document type from a `DataAdapter`.
|
|
151
|
+
*/
|
|
152
|
+
type InferAdapterDoc<A> = A extends {
|
|
153
|
+
repository: StandardRepo<infer D>;
|
|
154
|
+
} ? D : never;
|
|
155
|
+
/**
|
|
156
|
+
* Strip Mongoose-specific surface from a hydrated document type.
|
|
157
|
+
*/
|
|
158
|
+
type CleanDoc<T> = T extends Document ? Omit<T, keyof Document | '_id' | '__v' | '$__' | '$isNew' | 'save' | 'remove'> : T;
|
|
159
|
+
/**
|
|
160
|
+
* Mongoose document with index signature — usable as a generic constraint.
|
|
161
|
+
*/
|
|
162
|
+
type MongooseDocument = Document & Record<string, unknown>;
|
|
163
|
+
/**
|
|
164
|
+
* Pin a Mongoose model's document type to match a repository's document type.
|
|
165
|
+
*/
|
|
166
|
+
type MatchingModel<TDoc> = Model<TDoc & Document>;
|
|
167
|
+
/**
|
|
168
|
+
* Runtime guard: is `value` a Mongoose model?
|
|
169
|
+
*/
|
|
170
|
+
declare function isMongooseModel(value: unknown): value is Model<Document>;
|
|
171
|
+
/**
|
|
172
|
+
* Mongoose SchemaType internal shape (not fully exposed by `@types/mongoose`).
|
|
173
|
+
* Used to extract field metadata from schema paths.
|
|
174
|
+
*/
|
|
175
|
+
interface MongooseSchemaType {
|
|
176
|
+
instance: string;
|
|
177
|
+
isRequired?: boolean;
|
|
178
|
+
options?: {
|
|
179
|
+
ref?: string;
|
|
180
|
+
enum?: Array<string | number>;
|
|
181
|
+
minlength?: number;
|
|
182
|
+
maxlength?: number;
|
|
183
|
+
min?: number;
|
|
184
|
+
max?: number;
|
|
185
|
+
[key: string]: unknown;
|
|
186
|
+
};
|
|
187
|
+
[key: string]: unknown;
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
export { type CleanDoc, type InferAdapterDoc, type InferMongooseDoc, type InferRepoDoc, type MatchingModel, MongooseAdapter, type MongooseAdapterOptions, type MongooseDocument, type MongooseSchemaType, createMongooseAdapter, isMongooseModel };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { n as warn } from "./logger-DprijP5h.mjs";
|
|
2
|
+
import { mergeFieldRuleConstraints } from "@classytic/repo-core/schema";
|
|
3
|
+
import { asRepositoryLike, isRepository } from "@classytic/repo-core/adapter";
|
|
4
|
+
//#region src/adapter/types.ts
|
|
5
|
+
/**
|
|
6
|
+
* Runtime guard: is `value` a Mongoose model?
|
|
7
|
+
*/
|
|
8
|
+
function isMongooseModel(value) {
|
|
9
|
+
return typeof value === "function" && value.prototype && "modelName" in value && "schema" in value;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/adapter/mongoose-adapter.ts
|
|
13
|
+
/**
|
|
14
|
+
* Mongoose data adapter — implements the `DataAdapter<TDoc>` contract from
|
|
15
|
+
* `@classytic/repo-core/adapter`.
|
|
16
|
+
*/
|
|
17
|
+
var MongooseAdapter = class {
|
|
18
|
+
type = "mongoose";
|
|
19
|
+
name;
|
|
20
|
+
model;
|
|
21
|
+
repository;
|
|
22
|
+
schemaGenerator;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
if (!isMongooseModel(options.model)) throw new TypeError("MongooseAdapter: Invalid model. Expected Mongoose Model instance.\nUsage: createMongooseAdapter({ model: YourModel, repository: yourRepo })");
|
|
25
|
+
if (!isRepository(options.repository)) throw new TypeError("MongooseAdapter: Invalid repository. Expected MinimalRepo instance.\nUsage: createMongooseAdapter({ model: YourModel, repository: yourRepo })");
|
|
26
|
+
this.model = options.model;
|
|
27
|
+
this.repository = asRepositoryLike(options.repository);
|
|
28
|
+
this.schemaGenerator = options.schemaGenerator;
|
|
29
|
+
this.name = `MongooseAdapter<${options.model.modelName}>`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Lightweight path-existence check for hosts that infer absent fields
|
|
33
|
+
* (e.g. arc's `defineResource()` infers `tenantField: false` when the
|
|
34
|
+
* configured tenant field doesn't exist on the model). Reads
|
|
35
|
+
* `model.schema.paths[name]` directly — no allocation, no metadata walk.
|
|
36
|
+
*/
|
|
37
|
+
hasFieldPath(name) {
|
|
38
|
+
return Boolean(this.model.schema?.paths?.[name]);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get schema metadata from a Mongoose model.
|
|
42
|
+
*/
|
|
43
|
+
getSchemaMetadata() {
|
|
44
|
+
const paths = this.model.schema.paths;
|
|
45
|
+
const fields = {};
|
|
46
|
+
for (const [fieldName, schemaType] of Object.entries(paths)) {
|
|
47
|
+
if (fieldName.startsWith("_") && fieldName !== "_id") continue;
|
|
48
|
+
const typeInfo = schemaType;
|
|
49
|
+
fields[fieldName] = {
|
|
50
|
+
type: {
|
|
51
|
+
String: "string",
|
|
52
|
+
Number: "number",
|
|
53
|
+
Boolean: "boolean",
|
|
54
|
+
Date: "date",
|
|
55
|
+
ObjectID: "objectId",
|
|
56
|
+
ObjectId: "objectId",
|
|
57
|
+
Array: "array",
|
|
58
|
+
Mixed: "object",
|
|
59
|
+
Buffer: "object",
|
|
60
|
+
Embedded: "object"
|
|
61
|
+
}[typeInfo.instance || "Mixed"] ?? "object",
|
|
62
|
+
required: !!typeInfo.isRequired,
|
|
63
|
+
ref: typeInfo.options?.ref
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
name: this.model.modelName,
|
|
68
|
+
fields,
|
|
69
|
+
relations: this.extractRelations(paths)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Generate OpenAPI-shaped schemas from the model.
|
|
74
|
+
*
|
|
75
|
+
* Delegates to the `schemaGenerator` callback supplied at construction.
|
|
76
|
+
* Returns `null` when no callback is configured — hosts treat that as
|
|
77
|
+
* "no schemas available" and skip OpenAPI generation for the resource.
|
|
78
|
+
*
|
|
79
|
+
* Most call sites pass mongokit's own `buildCrudSchemasFromModel`:
|
|
80
|
+
*
|
|
81
|
+
* ```ts
|
|
82
|
+
* createMongooseAdapter({
|
|
83
|
+
* model,
|
|
84
|
+
* repository,
|
|
85
|
+
* schemaGenerator: buildCrudSchemasFromModel,
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
generateSchemas(schemaOptions, context) {
|
|
90
|
+
if (!this.schemaGenerator) return null;
|
|
91
|
+
try {
|
|
92
|
+
const generated = this.schemaGenerator(this.model, schemaOptions, context);
|
|
93
|
+
mergeFieldRuleConstraints(generated, schemaOptions);
|
|
94
|
+
return generated;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
97
|
+
warn(`[MongooseAdapter:${this.model.modelName}] schemaGenerator threw — schema generation skipped: ${message}`);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Default `validate` — kits typically override or hosts skip this since
|
|
103
|
+
* Mongoose enforces validation at save time. Provided for adapter-
|
|
104
|
+
* contract completeness so consumers that branch on `adapter.validate`
|
|
105
|
+
* find a no-op success path.
|
|
106
|
+
*/
|
|
107
|
+
validate(_data) {
|
|
108
|
+
return { valid: true };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract relation metadata from Mongoose ref paths.
|
|
112
|
+
*
|
|
113
|
+
* Cardinality rules:
|
|
114
|
+
* - `field: { type: ObjectId, ref }` → one-to-one (single ref)
|
|
115
|
+
* - `field: [{ type: ObjectId, ref }]` → one-to-many (array of refs)
|
|
116
|
+
*
|
|
117
|
+
* Mongoose surfaces array paths with `instance === 'Array'` and a
|
|
118
|
+
* `caster` SchemaType holding the element-side metadata (including the
|
|
119
|
+
* `ref` and inner instance). Reading the caster lets us distinguish
|
|
120
|
+
* one-to-many from a scalar-array path that just happens to declare
|
|
121
|
+
* something else. Pre-3.12.x this method always returned `'one-to-one'`
|
|
122
|
+
* regardless of cardinality — wrong metadata for OpenAPI / docs hosts.
|
|
123
|
+
*/
|
|
124
|
+
extractRelations(paths) {
|
|
125
|
+
const relations = {};
|
|
126
|
+
for (const [fieldName, schemaType] of Object.entries(paths)) {
|
|
127
|
+
const path = schemaType;
|
|
128
|
+
if (path.instance === "Array") {
|
|
129
|
+
const elementRef = path.embeddedSchemaType?.options?.ref ?? path.caster?.options?.ref ?? path.options?.ref;
|
|
130
|
+
if (elementRef) relations[fieldName] = {
|
|
131
|
+
type: "one-to-many",
|
|
132
|
+
target: elementRef,
|
|
133
|
+
foreignKey: fieldName
|
|
134
|
+
};
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const ref = path.options?.ref;
|
|
138
|
+
if (ref) relations[fieldName] = {
|
|
139
|
+
type: "one-to-one",
|
|
140
|
+
target: ref,
|
|
141
|
+
foreignKey: fieldName
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return Object.keys(relations).length > 0 ? relations : void 0;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
function createMongooseAdapter(modelOrOptions, repository) {
|
|
148
|
+
if (isMongooseModel(modelOrOptions)) {
|
|
149
|
+
if (!repository) throw new TypeError("createMongooseAdapter: repository is required when using 2-arg form.\nUsage: createMongooseAdapter(Model, repository)");
|
|
150
|
+
return new MongooseAdapter({
|
|
151
|
+
model: modelOrOptions,
|
|
152
|
+
repository
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return new MongooseAdapter(modelOrOptions);
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
export { createMongooseAdapter as n, isMongooseModel as r, MongooseAdapter as t };
|
package/dist/ai/index.d.mts
CHANGED
package/dist/ai/index.mjs
CHANGED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { DataAdapter, RepositoryLike } from "@classytic/repo-core/adapter";
|
|
2
|
+
import { BetterAuthPluginKey, BetterAuthPluginKey as BetterAuthPluginKey$1 } from "@classytic/repo-core/better-auth";
|
|
3
|
+
|
|
4
|
+
//#region src/better-auth/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Minimal structural type for the Mongoose surface this module touches.
|
|
7
|
+
* Declared structurally so the file has zero runtime `import` of mongoose —
|
|
8
|
+
* mongoose stays a peer dep and is never bundled with mongokit.
|
|
9
|
+
*/
|
|
10
|
+
interface MongooseLike {
|
|
11
|
+
models: Record<string, unknown>;
|
|
12
|
+
Schema: new (definition?: Record<string, unknown>, options?: Record<string, unknown>) => MongooseSchemaLike;
|
|
13
|
+
model: (name: string, schema?: unknown) => MongooseModelLike;
|
|
14
|
+
}
|
|
15
|
+
interface MongooseSchemaLike {
|
|
16
|
+
index(fields: Record<string, 1 | -1>, options?: Record<string, unknown>): MongooseSchemaLike;
|
|
17
|
+
}
|
|
18
|
+
interface MongooseModelLike {
|
|
19
|
+
modelName: string;
|
|
20
|
+
schema: {
|
|
21
|
+
paths: Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Minimal structural type for a `betterAuth()` instance. We only need
|
|
26
|
+
* `$context` (resolved tables map). Avoids a runtime peer dep on
|
|
27
|
+
* `better-auth` from this module — declared as an *optional* peer in
|
|
28
|
+
* package.json for hosts that import this subpath.
|
|
29
|
+
*/
|
|
30
|
+
interface BetterAuthInstance {
|
|
31
|
+
$context: Promise<{
|
|
32
|
+
tables: Record<string, BATableConfig>;
|
|
33
|
+
}> | {
|
|
34
|
+
tables: Record<string, BATableConfig>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
interface BATableConfig {
|
|
38
|
+
modelName: string;
|
|
39
|
+
fields: Record<string, BAFieldAttribute>;
|
|
40
|
+
}
|
|
41
|
+
interface BAFieldAttribute {
|
|
42
|
+
type: 'string' | 'number' | 'boolean' | 'date' | 'string[]' | 'number[]';
|
|
43
|
+
required?: boolean;
|
|
44
|
+
unique?: boolean;
|
|
45
|
+
fieldName?: string;
|
|
46
|
+
references?: {
|
|
47
|
+
model: string;
|
|
48
|
+
field: string;
|
|
49
|
+
onDelete?: string;
|
|
50
|
+
};
|
|
51
|
+
defaultValue?: unknown;
|
|
52
|
+
}
|
|
53
|
+
interface RegisterBetterAuthStubsOptions {
|
|
54
|
+
/** Plugin sets to include. `core` is always implied. */
|
|
55
|
+
plugins?: BetterAuthPluginKey$1[];
|
|
56
|
+
/** Additional collection names beyond the plugin set. */
|
|
57
|
+
extraCollections?: string[];
|
|
58
|
+
/** Mirror BA's `usePlural` flag — appends `s` to every collection name. */
|
|
59
|
+
usePlural?: boolean;
|
|
60
|
+
/** Per-collection model name override (mirrors BA's `user.modelName`). */
|
|
61
|
+
modelOverrides?: Partial<Record<string, string>>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Register stub Mongoose models for Better Auth's collections so that
|
|
65
|
+
* Mongoose-based resources can `.populate()` references to BA-owned
|
|
66
|
+
* documents. Idempotent — safe to call multiple times.
|
|
67
|
+
*
|
|
68
|
+
* Schemas are `strict: false` and `_id: false` so BA's writes flow through
|
|
69
|
+
* unchanged and Mongoose hydrates whatever BA wrote. Use this when you have
|
|
70
|
+
* resources that *reference* BA collections (`createdBy: { ref: 'user' }`)
|
|
71
|
+
* but don't expose CRUD on the BA collection itself.
|
|
72
|
+
*
|
|
73
|
+
* For full CRUD on a BA collection, use `createBetterAuthOverlay` instead —
|
|
74
|
+
* it registers the model AND wires it into a `DataAdapter<TDoc>`.
|
|
75
|
+
*
|
|
76
|
+
* @returns the model names that were newly registered (excluding any that
|
|
77
|
+
* already existed on `mongoose.models`).
|
|
78
|
+
*/
|
|
79
|
+
declare function registerBetterAuthStubs(mongoose: MongooseLike, options?: RegisterBetterAuthStubsOptions): string[];
|
|
80
|
+
interface BetterAuthOverlayOptions<TDoc = Record<string, unknown>> {
|
|
81
|
+
/** A `betterAuth()` instance. Used to read the resolved schema (`auth.$context.tables`). */
|
|
82
|
+
auth: BetterAuthInstance;
|
|
83
|
+
/** Mongoose instance — passed in to keep mongoose a peer dep. */
|
|
84
|
+
mongoose: MongooseLike;
|
|
85
|
+
/**
|
|
86
|
+
* Canonical BA collection name (`'user'`, `'organization'`, `'member'`, ...).
|
|
87
|
+
* The factory looks up `auth.$context.tables[collection]` to derive the
|
|
88
|
+
* actual model name (honoring BA's `user.modelName` overrides) and the
|
|
89
|
+
* field set (honoring `additionalFields` and plugin schema additions).
|
|
90
|
+
*/
|
|
91
|
+
collection: string;
|
|
92
|
+
/**
|
|
93
|
+
* Additional fields to declare on the Mongoose schema. Use this when:
|
|
94
|
+
* - You added them via `betterAuth({ user: { additionalFields: { ... } } })`
|
|
95
|
+
* and want typed access from your repository
|
|
96
|
+
* - You want to declare validators / defaults / indexes on those fields
|
|
97
|
+
*
|
|
98
|
+
* Schema stays `strict: false`, so any field BA writes that you DON'T
|
|
99
|
+
* declare here still round-trips — declaring is about typed access and
|
|
100
|
+
* Mongoose-side concerns (validators, indexes, defaults).
|
|
101
|
+
*/
|
|
102
|
+
additionalFields?: Record<string, unknown>;
|
|
103
|
+
/**
|
|
104
|
+
* Mongoose schema indexes to attach to the overlay model.
|
|
105
|
+
*
|
|
106
|
+
* @example [{ fields: { code: 1 }, options: { unique: true, sparse: true } }]
|
|
107
|
+
*/
|
|
108
|
+
indexes?: Array<{
|
|
109
|
+
fields: Record<string, 1 | -1>;
|
|
110
|
+
options?: Record<string, unknown>;
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* Mirror BA's `usePlural: true` flag. Only used as a fallback when
|
|
114
|
+
* `auth.$context.tables[collection].modelName` doesn't already include
|
|
115
|
+
* the pluralization (rare — BA's resolver normally bakes it in).
|
|
116
|
+
*/
|
|
117
|
+
usePlural?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Subclass `Repository<TDoc>` to add domain methods (e.g. `getByEmail`,
|
|
120
|
+
* `deactivate`, `getAdmins`). When omitted, the default `Repository<TDoc>`
|
|
121
|
+
* is used — the standard CRUD surface is already complete.
|
|
122
|
+
*/
|
|
123
|
+
RepositoryClass?: new (Model: ReturnType<MongooseLike['model']>) => RepositoryLike<TDoc>;
|
|
124
|
+
/**
|
|
125
|
+
* Optional schema generator. Pass `buildCrudSchemasFromModel` from
|
|
126
|
+
* `@classytic/mongokit` to wire OpenAPI auto-gen.
|
|
127
|
+
*/
|
|
128
|
+
schemaGenerator?: any;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create a `DataAdapter<TDoc>` over a Better Auth collection.
|
|
132
|
+
*
|
|
133
|
+
* Reads `auth.$context.tables[collection]` to resolve the actual model
|
|
134
|
+
* name and any BA-declared fields. Registers a `strict: false` Mongoose
|
|
135
|
+
* model, wraps it in a `Repository`, and returns the framework-agnostic
|
|
136
|
+
* adapter ready for `defineResource({ adapter })`.
|
|
137
|
+
*
|
|
138
|
+
* Async because we await BA's resolved schema. Resolves once at boot —
|
|
139
|
+
* there's no per-request cost. Symmetric with
|
|
140
|
+
* `@classytic/sqlitekit/better-auth.createBetterAuthOverlay`.
|
|
141
|
+
*
|
|
142
|
+
* @throws if `collection` doesn't match any table BA knows about (typo or
|
|
143
|
+
* missing plugin), or if a model with the resolved name is already
|
|
144
|
+
* registered on `mongoose.models` from a prior call (re-registration
|
|
145
|
+
* would silently drop this call's `additionalFields` / `indexes`).
|
|
146
|
+
*/
|
|
147
|
+
declare function createBetterAuthOverlay<TDoc = Record<string, unknown>>(options: BetterAuthOverlayOptions<TDoc>): Promise<DataAdapter<TDoc>>;
|
|
148
|
+
//#endregion
|
|
149
|
+
export { BetterAuthInstance, BetterAuthOverlayOptions, type BetterAuthPluginKey, MongooseLike, RegisterBetterAuthStubsOptions, createBetterAuthOverlay, registerBetterAuthStubs };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { n as Repository } from "../Repository-d5gWSROf.mjs";
|
|
2
|
+
import { n as createMongooseAdapter } from "../adapter-DTDMnUOH.mjs";
|
|
3
|
+
import { pluralizeBetterAuthCollection, resolveBetterAuthCollections } from "@classytic/repo-core/better-auth";
|
|
4
|
+
//#region src/better-auth/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Register stub Mongoose models for Better Auth's collections so that
|
|
7
|
+
* Mongoose-based resources can `.populate()` references to BA-owned
|
|
8
|
+
* documents. Idempotent — safe to call multiple times.
|
|
9
|
+
*
|
|
10
|
+
* Schemas are `strict: false` and `_id: false` so BA's writes flow through
|
|
11
|
+
* unchanged and Mongoose hydrates whatever BA wrote. Use this when you have
|
|
12
|
+
* resources that *reference* BA collections (`createdBy: { ref: 'user' }`)
|
|
13
|
+
* but don't expose CRUD on the BA collection itself.
|
|
14
|
+
*
|
|
15
|
+
* For full CRUD on a BA collection, use `createBetterAuthOverlay` instead —
|
|
16
|
+
* it registers the model AND wires it into a `DataAdapter<TDoc>`.
|
|
17
|
+
*
|
|
18
|
+
* @returns the model names that were newly registered (excluding any that
|
|
19
|
+
* already existed on `mongoose.models`).
|
|
20
|
+
*/
|
|
21
|
+
function registerBetterAuthStubs(mongoose, options = {}) {
|
|
22
|
+
const names = resolveBetterAuthCollections(options);
|
|
23
|
+
const registered = [];
|
|
24
|
+
for (const finalName of names) {
|
|
25
|
+
if (mongoose.models[finalName]) continue;
|
|
26
|
+
const schema = new mongoose.Schema({}, {
|
|
27
|
+
strict: false,
|
|
28
|
+
collection: finalName,
|
|
29
|
+
_id: false,
|
|
30
|
+
timestamps: false
|
|
31
|
+
});
|
|
32
|
+
mongoose.model(finalName, schema);
|
|
33
|
+
registered.push(finalName);
|
|
34
|
+
}
|
|
35
|
+
return registered;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a `DataAdapter<TDoc>` over a Better Auth collection.
|
|
39
|
+
*
|
|
40
|
+
* Reads `auth.$context.tables[collection]` to resolve the actual model
|
|
41
|
+
* name and any BA-declared fields. Registers a `strict: false` Mongoose
|
|
42
|
+
* model, wraps it in a `Repository`, and returns the framework-agnostic
|
|
43
|
+
* adapter ready for `defineResource({ adapter })`.
|
|
44
|
+
*
|
|
45
|
+
* Async because we await BA's resolved schema. Resolves once at boot —
|
|
46
|
+
* there's no per-request cost. Symmetric with
|
|
47
|
+
* `@classytic/sqlitekit/better-auth.createBetterAuthOverlay`.
|
|
48
|
+
*
|
|
49
|
+
* @throws if `collection` doesn't match any table BA knows about (typo or
|
|
50
|
+
* missing plugin), or if a model with the resolved name is already
|
|
51
|
+
* registered on `mongoose.models` from a prior call (re-registration
|
|
52
|
+
* would silently drop this call's `additionalFields` / `indexes`).
|
|
53
|
+
*/
|
|
54
|
+
async function createBetterAuthOverlay(options) {
|
|
55
|
+
const { auth, mongoose, collection, additionalFields = {}, indexes = [], usePlural = false, RepositoryClass, schemaGenerator } = options;
|
|
56
|
+
const ctx = await auth.$context;
|
|
57
|
+
const tableConfig = ctx.tables[collection];
|
|
58
|
+
if (!tableConfig) throw new Error(`[mongokit:better-auth] Better Auth has no table named '${collection}'. Available: ${Object.keys(ctx.tables).join(", ")}. Did you enable the right plugin in your betterAuth() config?`);
|
|
59
|
+
const finalName = usePlural ? pluralizeBetterAuthCollection(tableConfig.modelName) : tableConfig.modelName;
|
|
60
|
+
if (mongoose.models[finalName]) {
|
|
61
|
+
if (Object.keys(additionalFields).length > 0 || indexes.length > 0) throw new Error(`[mongokit:better-auth] '${finalName}' already registered on mongoose.models. Cannot apply additionalFields / indexes from this createBetterAuthOverlay() call — mongoose locks schema on first model() call. Either: (a) drop the prior registerBetterAuthStubs() call for '${finalName}', or (b) move additionalFields / indexes into the existing schema definition.`);
|
|
62
|
+
}
|
|
63
|
+
let Model = mongoose.models[finalName];
|
|
64
|
+
if (!Model) {
|
|
65
|
+
const schema = new mongoose.Schema(additionalFields, {
|
|
66
|
+
strict: false,
|
|
67
|
+
collection: finalName,
|
|
68
|
+
timestamps: false
|
|
69
|
+
});
|
|
70
|
+
for (const idx of indexes) schema.index(idx.fields, idx.options);
|
|
71
|
+
Model = mongoose.model(finalName, schema);
|
|
72
|
+
}
|
|
73
|
+
const repository = new (RepositoryClass ?? Repository)(Model);
|
|
74
|
+
return createMongooseAdapter({
|
|
75
|
+
model: Model,
|
|
76
|
+
repository,
|
|
77
|
+
schemaGenerator
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { createBetterAuthOverlay, registerBetterAuthStubs };
|