@classytic/mongokit 3.11.1 → 3.13.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.
Files changed (38) hide show
  1. package/README.md +75 -5
  2. package/dist/{PaginationEngine-CvDFfVXT.mjs → PaginationEngine-CfQRM1yz.mjs} +16 -16
  3. package/dist/Repository-BY40uhcv.mjs +3603 -0
  4. package/dist/actions/index.d.mts +1 -1
  5. package/dist/actions/index.mjs +1 -1
  6. package/dist/adapter/index.d.mts +190 -0
  7. package/dist/adapter/index.mjs +2 -0
  8. package/dist/adapter-DTDMnUOH.mjs +158 -0
  9. package/dist/ai/index.d.mts +1 -1
  10. package/dist/ai/index.mjs +3 -3
  11. package/dist/better-auth/index.d.mts +149 -0
  12. package/dist/better-auth/index.mjs +81 -0
  13. package/dist/field-selection-Br3WPOEb.mjs +99 -0
  14. package/dist/{index-dNTmpgK8.d.mts → index-BbHaKwi5.d.mts} +33 -13
  15. package/dist/index.d.mts +678 -36
  16. package/dist/index.mjs +240 -17
  17. package/dist/{logger-DJ7egMnd.mjs → logger-DprijP5h.mjs} +1 -5
  18. package/dist/{mongooseToJsonSchema-D1sdCOmj.mjs → mongooseToJsonSchema-BYbG894y.mjs} +68 -20
  19. package/dist/{mongooseToJsonSchema-DXxjFkLB.d.mts → mongooseToJsonSchema-BgRAaXX-.d.mts} +10 -2
  20. package/dist/pagination/PaginationEngine.d.mts +3 -2
  21. package/dist/pagination/PaginationEngine.mjs +1 -1
  22. package/dist/plugins/index.d.mts +2 -2
  23. package/dist/plugins/index.mjs +2 -2
  24. package/dist/query/primitives/coercion.d.mts +1 -1
  25. package/dist/query/primitives/coercion.mjs +1 -1
  26. package/dist/query/primitives/indexes.d.mts +1 -1
  27. package/dist/{types-Dc2aDVUi.d.mts → types-BbaPZleN.d.mts} +419 -205
  28. package/dist/{update-DEJYcgfV.mjs → update-DLcSSa5b.mjs} +184 -45
  29. package/dist/utils/index.d.mts +2 -91
  30. package/dist/utils/index.mjs +5 -5
  31. package/dist/{validation-chain.plugin-B0VSdcwa.d.mts → validation-chain.plugin-CAPYCj_T.d.mts} +228 -98
  32. package/dist/validation-chain.plugin-_-WuNml2.mjs +2399 -0
  33. package/package.json +25 -5
  34. package/dist/field-selection-dI0m4RBs.mjs +0 -221
  35. package/dist/validation-chain.plugin-Dllv-cml.mjs +0 -5122
  36. /package/dist/{coercion-oM8G4b6m.d.mts → coercion-TRMmR49h.d.mts} +0 -0
  37. /package/dist/{id-resolution-e4BT9zAV.mjs → id-resolution-B0Odvvou.mjs} +0 -0
  38. /package/dist/{indexes-C26GNZRU.d.mts → indexes-CgOF7Wjd.d.mts} +0 -0
@@ -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-dNTmpgK8.mjs";
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 };
@@ -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-DEJYcgfV.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-DLcSSa5b.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,2 @@
1
+ import { n as createMongooseAdapter, r as isMongooseModel, t as MongooseAdapter } from "../adapter-DTDMnUOH.mjs";
2
+ export { MongooseAdapter, 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 };
@@ -1,4 +1,4 @@
1
- import { q as Plugin } from "../types-Dc2aDVUi.mjs";
1
+ import { G as Plugin } from "../types-BbaPZleN.mjs";
2
2
  import { PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/ai/types.d.ts
package/dist/ai/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { r as warn } from "../logger-DJ7egMnd.mjs";
1
+ import { n as warn } from "../logger-DprijP5h.mjs";
2
2
  //#region src/ai/url-policy.ts
3
3
  /**
4
4
  * URL policy helpers for the vector plugin media pipeline.
@@ -45,7 +45,7 @@ function matchesOriginEntry(origin, entry, url) {
45
45
  if (wildcardMatch) {
46
46
  const [, entryProtocol, suffix] = wildcardMatch;
47
47
  if (url.protocol.toLowerCase() !== entryProtocol.toLowerCase()) return false;
48
- return url.hostname.toLowerCase().endsWith("." + suffix.toLowerCase());
48
+ return url.hostname.toLowerCase().endsWith(`.${suffix.toLowerCase()}`);
49
49
  }
50
50
  return false;
51
51
  }
@@ -137,7 +137,7 @@ async function withVectorErrorHints(op, ctx) {
137
137
  } catch (err) {
138
138
  const hint = classifyVectorSearchError(err, ctx);
139
139
  if (hint.code === "UNKNOWN") throw err;
140
- const message = `[mongokit:vector] ${hint.summary}\n` + hint.hints.map((h) => ` • ${h}`).join("\n");
140
+ const message = `[mongokit:vector] ${hint.summary}\n${hint.hints.map((h) => ` • ${h}`).join("\n")}`;
141
141
  const wrapped = new Error(message, { cause: err });
142
142
  wrapped.code = hint.code;
143
143
  throw wrapped;
@@ -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-BY40uhcv.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 };