@deessejs/collections 0.1.0 → 0.2.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/next/index.js +425 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/index.mjs +377 -0
- package/dist/next/index.mjs.map +1 -0
- package/package.json +11 -1
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc2) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/next/index.ts
|
|
31
|
+
var next_exports = {};
|
|
32
|
+
__export(next_exports, {
|
|
33
|
+
buildSchema: () => buildSchema,
|
|
34
|
+
collection: () => collection,
|
|
35
|
+
defaultWithCollectionsOptions: () => defaultWithCollectionsOptions,
|
|
36
|
+
defineConfig: () => defineConfig,
|
|
37
|
+
f: () => f,
|
|
38
|
+
field: () => field,
|
|
39
|
+
getCollectionsConfig: () => getCollectionsConfig,
|
|
40
|
+
getCollectionsFromConfig: () => getCollectionsFromConfig,
|
|
41
|
+
isCollectionsConfig: () => isCollectionsConfig,
|
|
42
|
+
isNextConfig: () => isNextConfig,
|
|
43
|
+
loadCollections: () => loadCollections,
|
|
44
|
+
pgAdapter: () => pgAdapter,
|
|
45
|
+
withCollections: () => withCollections,
|
|
46
|
+
withCollectionsSync: () => withCollectionsSync
|
|
47
|
+
});
|
|
48
|
+
module.exports = __toCommonJS(next_exports);
|
|
49
|
+
var import_path = require("path");
|
|
50
|
+
var import_process = __toESM(require("process"));
|
|
51
|
+
|
|
52
|
+
// src/field-type.ts
|
|
53
|
+
var fieldType = (config) => {
|
|
54
|
+
return () => ({
|
|
55
|
+
schema: config.schema,
|
|
56
|
+
database: config.database ?? {}
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/field.ts
|
|
61
|
+
var field = (config) => {
|
|
62
|
+
return {
|
|
63
|
+
fieldType: config.fieldType,
|
|
64
|
+
required: config.required ?? false,
|
|
65
|
+
unique: config.unique ?? false,
|
|
66
|
+
indexed: config.indexed ?? false,
|
|
67
|
+
default: config.default,
|
|
68
|
+
label: config.label,
|
|
69
|
+
description: config.description
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/fields/f.ts
|
|
74
|
+
var import_zod = require("zod");
|
|
75
|
+
var getItemType = (itemSchema) => {
|
|
76
|
+
if (itemSchema instanceof import_zod.z.ZodString) return "text";
|
|
77
|
+
if (itemSchema instanceof import_zod.z.ZodNumber) return "integer";
|
|
78
|
+
if (itemSchema instanceof import_zod.z.ZodBoolean) return "boolean";
|
|
79
|
+
if (itemSchema instanceof import_zod.z.ZodDate) return "timestamp";
|
|
80
|
+
if (itemSchema instanceof import_zod.z.ZodEnum) return "text";
|
|
81
|
+
if (itemSchema instanceof import_zod.z.ZodArray) return "array";
|
|
82
|
+
if (itemSchema instanceof import_zod.z.ZodObject) return "jsonb";
|
|
83
|
+
return "text";
|
|
84
|
+
};
|
|
85
|
+
var f = {
|
|
86
|
+
/**
|
|
87
|
+
* Text field type
|
|
88
|
+
*/
|
|
89
|
+
text: () => fieldType({
|
|
90
|
+
schema: import_zod.z.string(),
|
|
91
|
+
database: { type: "text" }
|
|
92
|
+
})(),
|
|
93
|
+
/**
|
|
94
|
+
* Email field type with built-in validation
|
|
95
|
+
*/
|
|
96
|
+
email: () => fieldType({
|
|
97
|
+
schema: import_zod.z.string().email(),
|
|
98
|
+
database: { type: "text" }
|
|
99
|
+
})(),
|
|
100
|
+
/**
|
|
101
|
+
* URL field type with built-in validation
|
|
102
|
+
*/
|
|
103
|
+
url: () => fieldType({
|
|
104
|
+
schema: import_zod.z.string().url(),
|
|
105
|
+
database: { type: "text" }
|
|
106
|
+
})(),
|
|
107
|
+
/**
|
|
108
|
+
* Number field type
|
|
109
|
+
*/
|
|
110
|
+
number: () => fieldType({
|
|
111
|
+
schema: import_zod.z.number(),
|
|
112
|
+
database: { type: "integer" }
|
|
113
|
+
})(),
|
|
114
|
+
/**
|
|
115
|
+
* Boolean field type
|
|
116
|
+
*/
|
|
117
|
+
boolean: () => fieldType({
|
|
118
|
+
schema: import_zod.z.boolean(),
|
|
119
|
+
database: { type: "boolean" }
|
|
120
|
+
})(),
|
|
121
|
+
/**
|
|
122
|
+
* Date field type (date only, no time)
|
|
123
|
+
*/
|
|
124
|
+
date: () => fieldType({
|
|
125
|
+
schema: import_zod.z.date(),
|
|
126
|
+
database: { type: "date" }
|
|
127
|
+
})(),
|
|
128
|
+
/**
|
|
129
|
+
* Timestamp field type (date with time)
|
|
130
|
+
*/
|
|
131
|
+
timestamp: () => fieldType({
|
|
132
|
+
schema: import_zod.z.date(),
|
|
133
|
+
database: { type: "timestamp" }
|
|
134
|
+
})(),
|
|
135
|
+
/**
|
|
136
|
+
* Creates a select field type
|
|
137
|
+
*/
|
|
138
|
+
select: (options) => fieldType({
|
|
139
|
+
schema: import_zod.z.enum(options),
|
|
140
|
+
database: { type: "text" }
|
|
141
|
+
})(),
|
|
142
|
+
/**
|
|
143
|
+
* JSON field type for storing JSON data
|
|
144
|
+
*/
|
|
145
|
+
json: (schema) => fieldType({
|
|
146
|
+
schema: schema ?? import_zod.z.any(),
|
|
147
|
+
database: { type: "jsonb" }
|
|
148
|
+
})(),
|
|
149
|
+
/**
|
|
150
|
+
* Array field type for storing lists
|
|
151
|
+
*/
|
|
152
|
+
array: (itemSchema) => fieldType({
|
|
153
|
+
schema: import_zod.z.array(itemSchema),
|
|
154
|
+
database: { type: "array", itemType: getItemType(itemSchema) }
|
|
155
|
+
})(),
|
|
156
|
+
/**
|
|
157
|
+
* Creates a relation field type for foreign key relationships
|
|
158
|
+
*/
|
|
159
|
+
relation: (options) => {
|
|
160
|
+
const isMany = options.many ?? false;
|
|
161
|
+
const isSingular = options.singular ?? false;
|
|
162
|
+
return fieldType({
|
|
163
|
+
schema: isMany ? import_zod.z.array(import_zod.z.string()) : import_zod.z.string(),
|
|
164
|
+
database: {
|
|
165
|
+
type: "integer",
|
|
166
|
+
references: options.collection,
|
|
167
|
+
through: options.through,
|
|
168
|
+
many: isMany,
|
|
169
|
+
singular: isSingular
|
|
170
|
+
}
|
|
171
|
+
})();
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var text = f.text;
|
|
175
|
+
var email = f.email;
|
|
176
|
+
var url = f.url;
|
|
177
|
+
var number = f.number;
|
|
178
|
+
var boolean = f.boolean;
|
|
179
|
+
var date = f.date;
|
|
180
|
+
var timestamp = f.timestamp;
|
|
181
|
+
var select = f.select;
|
|
182
|
+
var json = f.json;
|
|
183
|
+
var array = f.array;
|
|
184
|
+
var relation = f.relation;
|
|
185
|
+
|
|
186
|
+
// src/collection.ts
|
|
187
|
+
var collection = (config) => {
|
|
188
|
+
return {
|
|
189
|
+
slug: config.slug,
|
|
190
|
+
name: config.name,
|
|
191
|
+
fields: config.fields,
|
|
192
|
+
hooks: config.hooks,
|
|
193
|
+
dataType: config.dataType
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/operations/collection-operations.ts
|
|
198
|
+
var import_drizzle_orm = require("drizzle-orm");
|
|
199
|
+
|
|
200
|
+
// src/adapter.ts
|
|
201
|
+
var pgAdapter = (config) => {
|
|
202
|
+
return {
|
|
203
|
+
type: "postgres",
|
|
204
|
+
config: {
|
|
205
|
+
url: config.url,
|
|
206
|
+
migrationsPath: config.migrationsPath ?? "./migrations"
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/schema.ts
|
|
212
|
+
var import_pg_core = require("drizzle-orm/pg-core");
|
|
213
|
+
var buildTable = (collection2) => {
|
|
214
|
+
const columns = {
|
|
215
|
+
// Add default id column
|
|
216
|
+
id: (0, import_pg_core.serial)("id").primaryKey()
|
|
217
|
+
};
|
|
218
|
+
for (const [fieldName, fieldDef] of Object.entries(collection2.fields)) {
|
|
219
|
+
if (fieldName === "id") continue;
|
|
220
|
+
const fieldType2 = fieldDef.fieldType;
|
|
221
|
+
const fieldTypeName = fieldType2.name || fieldType2.type || "text";
|
|
222
|
+
switch (fieldTypeName) {
|
|
223
|
+
case "text":
|
|
224
|
+
columns[fieldName] = (0, import_pg_core.text)(fieldName);
|
|
225
|
+
break;
|
|
226
|
+
case "varchar":
|
|
227
|
+
columns[fieldName] = (0, import_pg_core.varchar)(fieldName, { length: 255 });
|
|
228
|
+
break;
|
|
229
|
+
case "number":
|
|
230
|
+
case "integer":
|
|
231
|
+
columns[fieldName] = (0, import_pg_core.integer)(fieldName);
|
|
232
|
+
break;
|
|
233
|
+
case "boolean":
|
|
234
|
+
columns[fieldName] = (0, import_pg_core.boolean)(fieldName);
|
|
235
|
+
break;
|
|
236
|
+
case "timestamp":
|
|
237
|
+
columns[fieldName] = (0, import_pg_core.timestamp)(fieldName);
|
|
238
|
+
break;
|
|
239
|
+
case "uuid":
|
|
240
|
+
columns[fieldName] = (0, import_pg_core.uuid)(fieldName);
|
|
241
|
+
break;
|
|
242
|
+
default:
|
|
243
|
+
columns[fieldName] = (0, import_pg_core.text)(fieldName);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return (0, import_pg_core.pgTable)(collection2.slug, columns);
|
|
247
|
+
};
|
|
248
|
+
var buildSchema = (collections) => {
|
|
249
|
+
const tables = {};
|
|
250
|
+
for (const coll of collections) {
|
|
251
|
+
tables[coll.slug] = buildTable(coll);
|
|
252
|
+
}
|
|
253
|
+
return tables;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/config.ts
|
|
257
|
+
var import_pg = require("pg");
|
|
258
|
+
var import_node_postgres = require("drizzle-orm/node-postgres");
|
|
259
|
+
var defineConfig = (options) => {
|
|
260
|
+
let pool = null;
|
|
261
|
+
let dbInstance = null;
|
|
262
|
+
let schema = {};
|
|
263
|
+
if (options.database.type === "postgres") {
|
|
264
|
+
pool = new import_pg.Pool({
|
|
265
|
+
connectionString: options.database.config.url
|
|
266
|
+
});
|
|
267
|
+
schema = buildSchema(options.collections);
|
|
268
|
+
dbInstance = (0, import_node_postgres.drizzle)(pool, { schema });
|
|
269
|
+
}
|
|
270
|
+
const collectionsMap = {};
|
|
271
|
+
const collectionNames = [];
|
|
272
|
+
for (const coll of options.collections) {
|
|
273
|
+
collectionsMap[coll.slug] = {
|
|
274
|
+
slug: coll.slug,
|
|
275
|
+
name: coll.name,
|
|
276
|
+
fields: coll.fields,
|
|
277
|
+
hooks: coll.hooks,
|
|
278
|
+
dataType: coll.dataType
|
|
279
|
+
};
|
|
280
|
+
collectionNames.push(coll.slug);
|
|
281
|
+
}
|
|
282
|
+
const pluginNames = [];
|
|
283
|
+
if (options.plugins) {
|
|
284
|
+
for (const plugin of options.plugins) {
|
|
285
|
+
pluginNames.push(plugin.name);
|
|
286
|
+
if (plugin.collections) {
|
|
287
|
+
for (const [name, coll] of Object.entries(plugin.collections)) {
|
|
288
|
+
collectionsMap[name] = {
|
|
289
|
+
slug: coll.slug,
|
|
290
|
+
name: coll.name,
|
|
291
|
+
fields: coll.fields,
|
|
292
|
+
hooks: coll.hooks,
|
|
293
|
+
dataType: coll.dataType
|
|
294
|
+
};
|
|
295
|
+
collectionNames.push(name);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
collections: collectionsMap,
|
|
302
|
+
db: dbInstance,
|
|
303
|
+
$meta: {
|
|
304
|
+
collections: collectionNames,
|
|
305
|
+
plugins: pluginNames
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// src/next/index.ts
|
|
311
|
+
function isDevelopment() {
|
|
312
|
+
return import_process.default.env.NODE_ENV === "development";
|
|
313
|
+
}
|
|
314
|
+
function isBuildTime() {
|
|
315
|
+
return import_process.default.env.__NEXT_BUILD === "1" || import_process.default.env.TURBO_BUILD === "1" || import_process.default.env.VERCEL === "1";
|
|
316
|
+
}
|
|
317
|
+
function debugLog(enabled, ...args) {
|
|
318
|
+
if (enabled) console.debug("[collections/next]", ...args);
|
|
319
|
+
}
|
|
320
|
+
function validateConfigPath(configPath, debug) {
|
|
321
|
+
const projectRoot = import_process.default.cwd();
|
|
322
|
+
const resolvedPath = (0, import_path.resolve)(projectRoot, configPath);
|
|
323
|
+
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
324
|
+
const normalizedRoot = projectRoot.replace(/\\/g, "/");
|
|
325
|
+
if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
|
|
326
|
+
throw new Error(`Config path must be within project directory: ${configPath}`);
|
|
327
|
+
}
|
|
328
|
+
debugLog(debug, "Validated config path:", resolvedPath);
|
|
329
|
+
return resolvedPath;
|
|
330
|
+
}
|
|
331
|
+
function extractCollectionsFromModule(module2) {
|
|
332
|
+
const defaultExport = module2.default;
|
|
333
|
+
const defaultCollections = defaultExport ? extractFromObject(defaultExport) : {};
|
|
334
|
+
const namedCollections = extractFromObject(module2);
|
|
335
|
+
return { ...defaultCollections, ...namedCollections };
|
|
336
|
+
}
|
|
337
|
+
function extractFromObject(obj) {
|
|
338
|
+
const collections = {};
|
|
339
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
340
|
+
if (key !== "default" && typeof value === "object" && value !== null && "slug" in value) {
|
|
341
|
+
collections[key] = value;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return collections;
|
|
345
|
+
}
|
|
346
|
+
async function loadCollectionsFromConfig(configPath, debug) {
|
|
347
|
+
try {
|
|
348
|
+
const validatedPath = validateConfigPath(configPath, debug);
|
|
349
|
+
const module2 = await import(
|
|
350
|
+
/* @vite-ignore */
|
|
351
|
+
validatedPath
|
|
352
|
+
);
|
|
353
|
+
const collections = extractCollectionsFromModule(module2);
|
|
354
|
+
debugLog(debug, `Loaded ${Object.keys(collections).length} collections`);
|
|
355
|
+
return collections;
|
|
356
|
+
} catch {
|
|
357
|
+
return {};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async function withCollections(nextConfig, options = {}) {
|
|
361
|
+
const { configPath = "./collections/config", hotReload = true, outputDir = "./drizzle", debug = false } = options;
|
|
362
|
+
const isProduction = !isDevelopment();
|
|
363
|
+
const shouldHotReload = hotReload && isDevelopment() && !isBuildTime();
|
|
364
|
+
const collections = await loadCollectionsFromConfig(configPath, debug);
|
|
365
|
+
let webpackConfig;
|
|
366
|
+
if (shouldHotReload && nextConfig.webpack) {
|
|
367
|
+
webpackConfig = (cfg, ctx) => nextConfig.webpack(cfg, ctx);
|
|
368
|
+
}
|
|
369
|
+
return { ...nextConfig, collections: { collections, outputDir, isProduction, configPath }, webpack: webpackConfig };
|
|
370
|
+
}
|
|
371
|
+
function withCollectionsSync(nextConfig, options = {}) {
|
|
372
|
+
const { configPath = "./collections/config", hotReload = true, outputDir = "./drizzle" } = options;
|
|
373
|
+
const isProduction = !isDevelopment();
|
|
374
|
+
const shouldHotReload = hotReload && isDevelopment() && !isBuildTime();
|
|
375
|
+
let webpackConfig;
|
|
376
|
+
if (shouldHotReload && nextConfig.webpack) {
|
|
377
|
+
webpackConfig = (cfg, ctx) => nextConfig.webpack(cfg, ctx);
|
|
378
|
+
}
|
|
379
|
+
return { ...nextConfig, collections: { collections: {}, outputDir, isProduction, configPath }, webpack: webpackConfig };
|
|
380
|
+
}
|
|
381
|
+
function getCollectionsConfig(nextConfig) {
|
|
382
|
+
return nextConfig.collections;
|
|
383
|
+
}
|
|
384
|
+
async function loadCollections(configPath) {
|
|
385
|
+
return loadCollectionsFromConfig(configPath, false);
|
|
386
|
+
}
|
|
387
|
+
function getCollectionsFromConfig(nextConfig) {
|
|
388
|
+
return nextConfig.collections?.collections ?? {};
|
|
389
|
+
}
|
|
390
|
+
function isNextConfig(config) {
|
|
391
|
+
if (!config || typeof config !== "object") return false;
|
|
392
|
+
const cfg = config;
|
|
393
|
+
if ("reactStrictMode" in cfg && typeof cfg.reactStrictMode !== "boolean") return false;
|
|
394
|
+
if ("swcMinify" in cfg && typeof cfg.swcMinify !== "boolean") return false;
|
|
395
|
+
const validProps = ["reactStrictMode", "swcMinify", "trailingSlash", "poweredByHeader", "compress"];
|
|
396
|
+
return validProps.some((prop) => prop in cfg);
|
|
397
|
+
}
|
|
398
|
+
function isCollectionsConfig(config) {
|
|
399
|
+
if (!config || typeof config !== "object") return false;
|
|
400
|
+
return config.collections !== void 0;
|
|
401
|
+
}
|
|
402
|
+
var defaultWithCollectionsOptions = {
|
|
403
|
+
configPath: "./collections/config",
|
|
404
|
+
hotReload: true,
|
|
405
|
+
outputDir: "./drizzle",
|
|
406
|
+
debug: false
|
|
407
|
+
};
|
|
408
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
409
|
+
0 && (module.exports = {
|
|
410
|
+
buildSchema,
|
|
411
|
+
collection,
|
|
412
|
+
defaultWithCollectionsOptions,
|
|
413
|
+
defineConfig,
|
|
414
|
+
f,
|
|
415
|
+
field,
|
|
416
|
+
getCollectionsConfig,
|
|
417
|
+
getCollectionsFromConfig,
|
|
418
|
+
isCollectionsConfig,
|
|
419
|
+
isNextConfig,
|
|
420
|
+
loadCollections,
|
|
421
|
+
pgAdapter,
|
|
422
|
+
withCollections,
|
|
423
|
+
withCollectionsSync
|
|
424
|
+
});
|
|
425
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/next/index.ts","../../src/field-type.ts","../../src/field.ts","../../src/fields/f.ts","../../src/collection.ts","../../src/operations/collection-operations.ts","../../src/adapter.ts","../../src/schema.ts","../../src/config.ts"],"sourcesContent":["/**\r\n * Next.js integration module for @deessejs/collections\r\n *\r\n * Provides hot reload support and Next.js plugin for collections\r\n */\r\n\r\nimport type { NextConfig } from 'next'\r\nimport type { Collection } from '../collection'\r\nimport { resolve } from 'path'\r\nimport process from 'process'\r\n\r\n// Re-export types from core for convenience\r\nexport type {\r\n Collection,\r\n CollectionConfig,\r\n CollectionHooks,\r\n OperationType,\r\n CreateHookContext,\r\n UpdateHookContext,\r\n DeleteHookContext,\r\n ReadHookContext,\r\n OperationHookContext\r\n} from '../collection'\r\n\r\n// Re-export field types\r\nexport type { FieldDefinition, FieldOptions } from '../field'\r\nexport type { FieldTypeInstance } from '../field-type'\r\n\r\n// Re-export field types and utilities\r\nexport { field, f, defineConfig, pgAdapter, buildSchema, collection } from '../index'\r\n\r\n/**\r\n * Type for withCollections configuration\r\n */\r\nexport interface WithCollectionsOptions {\r\n /**\r\n * Path to the collections config file\r\n * @default './collections/config'\r\n */\r\n configPath?: string\r\n\r\n /**\r\n * Enable hot reload in development\r\n * @default true\r\n */\r\n hotReload?: boolean\r\n\r\n /**\r\n * Output directory for generated schema\r\n * @default './drizzle'\r\n */\r\n outputDir?: string\r\n\r\n /**\r\n * Enable debug logging\r\n * @default false\r\n */\r\n debug?: boolean\r\n}\r\n\r\n/**\r\n * Type for the Next.js config returned by withCollections\r\n */\r\nexport type WithCollectionsConfig = NextConfig & {\r\n collections?: {\r\n collections: Record<string, Collection>\r\n outputDir: string\r\n isProduction: boolean\r\n configPath: string\r\n }\r\n}\r\n\r\n/**\r\n * Check if we're running in development mode\r\n */\r\nfunction isDevelopment(): boolean {\r\n return process.env.NODE_ENV === 'development'\r\n}\r\n\r\n/**\r\n * Check if we're running in a build-time context\r\n */\r\nfunction isBuildTime(): boolean {\r\n return process.env.__NEXT_BUILD === '1' || process.env.TURBO_BUILD === '1' || process.env.VERCEL === '1'\r\n}\r\n\r\n/**\r\n * Debug logging function\r\n */\r\nfunction debugLog(enabled: boolean, ...args: unknown[]): void {\r\n if (enabled) console.debug('[collections/next]', ...args)\r\n}\r\n\r\n/**\r\n * Validate that a path is within the project directory\r\n */\r\nfunction validateConfigPath(configPath: string, debug: boolean): string {\r\n const projectRoot = process.cwd()\r\n const resolvedPath = resolve(projectRoot, configPath)\r\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\r\n const normalizedRoot = projectRoot.replace(/\\\\/g, '/')\r\n\r\n if (!normalizedPath.startsWith(normalizedRoot + '/') && normalizedPath !== normalizedRoot) {\r\n throw new Error(`Config path must be within project directory: ${configPath}`)\r\n }\r\n\r\n debugLog(debug, 'Validated config path:', resolvedPath)\r\n return resolvedPath\r\n}\r\n\r\n/**\r\n * Extract collections from a loaded module\r\n */\r\nfunction extractCollectionsFromModule(module: Record<string, unknown>): Record<string, Collection> {\r\n const defaultExport = module.default as Record<string, unknown> | undefined\r\n const defaultCollections = defaultExport ? extractFromObject(defaultExport) : {}\r\n const namedCollections = extractFromObject(module)\r\n\r\n return { ...defaultCollections, ...namedCollections }\r\n}\r\n\r\nfunction extractFromObject(obj: Record<string, unknown>): Record<string, Collection> {\r\n const collections: Record<string, Collection> = {}\r\n for (const [key, value] of Object.entries(obj)) {\r\n if (key !== 'default' && typeof value === 'object' && value !== null && 'slug' in value) {\r\n collections[key] = value as Collection\r\n }\r\n }\r\n return collections\r\n}\r\n\r\n/**\r\n * Load collections from a config file using dynamic import\r\n */\r\nasync function loadCollectionsFromConfig(configPath: string, debug: boolean): Promise<Record<string, Collection>> {\r\n try {\r\n const validatedPath = validateConfigPath(configPath, debug)\r\n const module = await import(/* @vite-ignore */ validatedPath)\r\n const collections = extractCollectionsFromModule(module as Record<string, unknown>)\r\n debugLog(debug, `Loaded ${Object.keys(collections).length} collections`)\r\n return collections\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Create a Next.js plugin that provides collections integration\r\n *\r\n * @example\r\n * // next.config.mjs\r\n * import { withCollections } from '@deessejs/collections/next'\r\n *\r\n * export default withCollections({\r\n * // Next.js config\r\n * })\r\n */\r\nexport async function withCollections(nextConfig: NextConfig, options: WithCollectionsOptions = {}): Promise<WithCollectionsConfig> {\r\n const { configPath = './collections/config', hotReload = true, outputDir = './drizzle', debug = false } = options\r\n const isProduction = !isDevelopment()\r\n const shouldHotReload = hotReload && isDevelopment() && !isBuildTime()\r\n\r\n const collections = await loadCollectionsFromConfig(configPath, debug)\r\n let webpackConfig: NextConfig['webpack']\r\n\r\n if (shouldHotReload && nextConfig.webpack) {\r\n webpackConfig = (cfg, ctx) => nextConfig.webpack!(cfg, ctx)\r\n }\r\n\r\n return { ...nextConfig, collections: { collections, outputDir, isProduction, configPath }, webpack: webpackConfig }\r\n}\r\n\r\n/**\r\n * Synchronous version of withCollections\r\n * @deprecated Use withCollections() instead\r\n */\r\nexport function withCollectionsSync(nextConfig: NextConfig, options: WithCollectionsOptions = {}): WithCollectionsConfig {\r\n const { configPath = './collections/config', hotReload = true, outputDir = './drizzle' } = options\r\n const isProduction = !isDevelopment()\r\n const shouldHotReload = hotReload && isDevelopment() && !isBuildTime()\r\n let webpackConfig: NextConfig['webpack']\r\n\r\n if (shouldHotReload && nextConfig.webpack) {\r\n webpackConfig = (cfg, ctx) => nextConfig.webpack!(cfg, ctx)\r\n }\r\n\r\n return { ...nextConfig, collections: { collections: {}, outputDir, isProduction, configPath }, webpack: webpackConfig }\r\n}\r\n\r\n/**\r\n * Get the collections configuration from the Next.js config\r\n */\r\nexport function getCollectionsConfig(nextConfig: WithCollectionsConfig) {\r\n return nextConfig.collections\r\n}\r\n\r\n/**\r\n * Load collections from a config file\r\n */\r\nexport async function loadCollections(configPath: string): Promise<Record<string, Collection>> {\r\n return loadCollectionsFromConfig(configPath, false)\r\n}\r\n\r\n/**\r\n * Extract collections from Next.js config\r\n */\r\nexport function getCollectionsFromConfig(nextConfig: WithCollectionsConfig): Record<string, Collection> {\r\n return nextConfig.collections?.collections ?? {}\r\n}\r\n\r\n/**\r\n * Type guard to check if a value is a valid Next.js config\r\n */\r\nexport function isNextConfig(config: unknown): config is NextConfig {\r\n if (!config || typeof config !== 'object') return false\r\n const cfg = config as Record<string, unknown>\r\n if ('reactStrictMode' in cfg && typeof cfg.reactStrictMode !== 'boolean') return false\r\n if ('swcMinify' in cfg && typeof cfg.swcMinify !== 'boolean') return false\r\n const validProps = ['reactStrictMode', 'swcMinify', 'trailingSlash', 'poweredByHeader', 'compress']\r\n return validProps.some(prop => prop in cfg)\r\n}\r\n\r\n/**\r\n * Type guard to check if withCollections was applied\r\n */\r\nexport function isCollectionsConfig(config: unknown): config is WithCollectionsConfig {\r\n if (!config || typeof config !== 'object') return false\r\n return (config as Record<string, unknown>).collections !== undefined\r\n}\r\n\r\n/**\r\n * Default withCollections options\r\n */\r\nexport const defaultWithCollectionsOptions: Required<WithCollectionsOptions> = {\r\n configPath: './collections/config',\r\n hotReload: true,\r\n outputDir: './drizzle',\r\n debug: false\r\n}\r\n","import { z } from 'zod'\r\n\r\n/**\r\n * A field type instance (already configured)\r\n */\r\nexport type FieldTypeInstance = {\r\n schema: z.ZodType\r\n database: unknown\r\n}\r\n\r\n/**\r\n * A field type creator (needs to be called to get instance)\r\n */\r\nexport type FieldTypeCreator = () => FieldTypeInstance\r\n\r\n/**\r\n * Field type configuration\r\n */\r\nexport type FieldTypeConfig = {\r\n schema: z.ZodType\r\n database?: unknown\r\n}\r\n\r\n/**\r\n * Creates a new field type\r\n *\r\n * @example\r\n * const text = fieldType({\r\n * schema: z.string(),\r\n * database: { type: 'text' }\r\n * })\r\n *\r\n * const textField = text() // Get instance\r\n */\r\nexport const fieldType = (config: FieldTypeConfig): (() => FieldTypeInstance) => {\r\n return () => ({\r\n schema: config.schema,\r\n database: config.database ?? {}\r\n })\r\n}\r\n","import type { FieldTypeInstance } from './field-type'\r\n\r\n/**\r\n * Field configuration options\r\n */\r\nexport type FieldOptions = {\r\n fieldType: FieldTypeInstance\r\n required?: boolean\r\n unique?: boolean\r\n indexed?: boolean\r\n default?: unknown\r\n label?: string\r\n description?: string\r\n}\r\n\r\n/**\r\n * Creates a field definition\r\n *\r\n * @example\r\n * name: field({ fieldType: text() })\r\n * email: field({ fieldType: email(), unique: true })\r\n */\r\nexport const field = (config: FieldOptions): FieldDefinition => {\r\n return {\r\n fieldType: config.fieldType,\r\n required: config.required ?? false,\r\n unique: config.unique ?? false,\r\n indexed: config.indexed ?? false,\r\n default: config.default,\r\n label: config.label,\r\n description: config.description\r\n }\r\n}\r\n\r\n/**\r\n * A field definition\r\n */\r\nexport type FieldDefinition = {\r\n fieldType: FieldTypeInstance\r\n required: boolean\r\n unique: boolean\r\n indexed: boolean\r\n default?: unknown\r\n label?: string\r\n description?: string\r\n}\r\n","import { fieldType, type FieldTypeInstance } from '../field-type'\r\nimport { z } from 'zod'\r\n\r\n/**\r\n * Derive the database type string from a Zod schema\r\n */\r\nconst getItemType = (itemSchema: z.ZodType): string => {\r\n if (itemSchema instanceof z.ZodString) return 'text'\r\n if (itemSchema instanceof z.ZodNumber) return 'integer'\r\n if (itemSchema instanceof z.ZodBoolean) return 'boolean'\r\n if (itemSchema instanceof z.ZodDate) return 'timestamp'\r\n if (itemSchema instanceof z.ZodEnum) return 'text'\r\n if (itemSchema instanceof z.ZodArray) return 'array'\r\n if (itemSchema instanceof z.ZodObject) return 'jsonb'\r\n return 'text'\r\n}\r\n\r\n/**\r\n * Field types namespace (like zod's z)\r\n */\r\nexport const f = {\r\n /**\r\n * Text field type\r\n */\r\n text: (): FieldTypeInstance => fieldType({\r\n schema: z.string(),\r\n database: { type: 'text' }\r\n })(),\r\n\r\n /**\r\n * Email field type with built-in validation\r\n */\r\n email: (): FieldTypeInstance => fieldType({\r\n schema: z.string().email(),\r\n database: { type: 'text' }\r\n })(),\r\n\r\n /**\r\n * URL field type with built-in validation\r\n */\r\n url: (): FieldTypeInstance => fieldType({\r\n schema: z.string().url(),\r\n database: { type: 'text' }\r\n })(),\r\n\r\n /**\r\n * Number field type\r\n */\r\n number: (): FieldTypeInstance => fieldType({\r\n schema: z.number(),\r\n database: { type: 'integer' }\r\n })(),\r\n\r\n /**\r\n * Boolean field type\r\n */\r\n boolean: (): FieldTypeInstance => fieldType({\r\n schema: z.boolean(),\r\n database: { type: 'boolean' }\r\n })(),\r\n\r\n /**\r\n * Date field type (date only, no time)\r\n */\r\n date: (): FieldTypeInstance => fieldType({\r\n schema: z.date(),\r\n database: { type: 'date' }\r\n })(),\r\n\r\n /**\r\n * Timestamp field type (date with time)\r\n */\r\n timestamp: (): FieldTypeInstance => fieldType({\r\n schema: z.date(),\r\n database: { type: 'timestamp' }\r\n })(),\r\n\r\n /**\r\n * Creates a select field type\r\n */\r\n select: <T extends readonly [string, ...string[]]>(\r\n options: T\r\n ): FieldTypeInstance => fieldType({\r\n schema: z.enum(options),\r\n database: { type: 'text' }\r\n })(),\r\n\r\n /**\r\n * JSON field type for storing JSON data\r\n */\r\n json: (schema?: z.ZodType): FieldTypeInstance => fieldType({\r\n schema: schema ?? z.any(),\r\n database: { type: 'jsonb' }\r\n })(),\r\n\r\n /**\r\n * Array field type for storing lists\r\n */\r\n array: (itemSchema: z.ZodType): FieldTypeInstance => fieldType({\r\n schema: z.array(itemSchema),\r\n database: { type: 'array', itemType: getItemType(itemSchema) }\r\n })(),\r\n\r\n /**\r\n * Creates a relation field type for foreign key relationships\r\n */\r\n relation: (options: {\r\n collection: string\r\n singular?: boolean\r\n many?: boolean\r\n through?: string\r\n }): FieldTypeInstance => {\r\n const isMany = options.many ?? false\r\n const isSingular = options.singular ?? false\r\n\r\n return fieldType({\r\n schema: isMany ? z.array(z.string()) : z.string(),\r\n database: {\r\n type: 'integer',\r\n references: options.collection,\r\n through: options.through,\r\n many: isMany,\r\n singular: isSingular\r\n }\r\n })()\r\n }\r\n}\r\n\r\n/**\r\n * @deprecated Use f.text() instead\r\n */\r\nexport const text = f.text\r\n\r\n/**\r\n * @deprecated Use f.email() instead\r\n */\r\nexport const email = f.email\r\n\r\n/**\r\n * @deprecated Use f.url() instead\r\n */\r\nexport const url = f.url\r\n\r\n/**\r\n * @deprecated Use f.number() instead\r\n */\r\nexport const number = f.number\r\n\r\n/**\r\n * @deprecated Use f.boolean() instead\r\n */\r\nexport const boolean = f.boolean\r\n\r\n/**\r\n * @deprecated Use f.date() instead\r\n */\r\nexport const date = f.date\r\n\r\n/**\r\n * @deprecated Use f.timestamp() instead\r\n */\r\nexport const timestamp = f.timestamp\r\n\r\n/**\r\n * @deprecated Use f.select() instead\r\n */\r\nexport const select = f.select\r\n\r\n/**\r\n * @deprecated Use f.json() instead\r\n */\r\nexport const json = f.json\r\n\r\n/**\r\n * @deprecated Use f.array() instead\r\n */\r\nexport const array = f.array\r\n\r\n/**\r\n * @deprecated Use f.relation() instead\r\n */\r\nexport const relation = f.relation\r\n\r\n/**\r\n * @deprecated Use f.relation instead\r\n */\r\nexport type RelationOptions = {\r\n collection: string\r\n singular?: boolean\r\n many?: boolean\r\n through?: string\r\n}\r\n","import type { FieldDefinition } from './field'\r\n\r\n/**\r\n * Collection configuration\r\n */\r\nexport type CollectionConfig<T extends Record<string, unknown> = Record<string, unknown>> = {\r\n slug: string\r\n name?: string\r\n fields: Record<string, FieldDefinition>\r\n hooks?: CollectionHooks\r\n dataType?: T\r\n}\r\n\r\n/**\r\n * Operation types\r\n */\r\nexport type OperationType = 'create' | 'update' | 'delete' | 'read'\r\n\r\n/**\r\n * Hook context base\r\n */\r\nexport type HookContextBase = {\r\n collection: string\r\n operation: OperationType\r\n}\r\n\r\n/**\r\n * Before/After Operation context\r\n */\r\nexport type OperationHookContext = HookContextBase & {\r\n data?: Record<string, unknown>\r\n where?: Record<string, unknown>\r\n result?: unknown\r\n}\r\n\r\n/**\r\n * Before/After Create context\r\n */\r\nexport type CreateHookContext = HookContextBase & {\r\n operation: 'create'\r\n data: Record<string, unknown>\r\n result?: unknown\r\n db?: unknown\r\n}\r\n\r\n/**\r\n * Before/After Update context\r\n */\r\nexport type UpdateHookContext = HookContextBase & {\r\n operation: 'update'\r\n data: Record<string, unknown>\r\n where: Record<string, unknown>\r\n previousData?: Record<string, unknown>\r\n result?: unknown\r\n db?: unknown\r\n}\r\n\r\n/**\r\n * Before/After Delete context\r\n */\r\nexport type DeleteHookContext = HookContextBase & {\r\n operation: 'delete'\r\n where: Record<string, unknown>\r\n previousData?: Record<string, unknown>\r\n result?: unknown\r\n db?: unknown\r\n}\r\n\r\n/**\r\n * Before/After Read context\r\n */\r\nexport type ReadHookContext = HookContextBase & {\r\n operation: 'read'\r\n query?: Record<string, unknown>\r\n result?: unknown[]\r\n db?: unknown\r\n}\r\n\r\n/**\r\n * Generic hook function type\r\n */\r\nexport type GenericHookFunction = (context: OperationHookContext) => Promise<void> | void\r\nexport type CreateHookFunction = (context: CreateHookContext) => Promise<void> | void\r\nexport type UpdateHookFunction = (context: UpdateHookContext) => Promise<void> | void\r\nexport type DeleteHookFunction = (context: DeleteHookContext) => Promise<void> | void\r\nexport type ReadHookFunction = (context: ReadHookContext) => Promise<void> | void\r\n\r\n/**\r\n * Collection hooks\r\n */\r\nexport type CollectionHooks = {\r\n beforeOperation?: GenericHookFunction[]\r\n afterOperation?: GenericHookFunction[]\r\n beforeCreate?: CreateHookFunction[]\r\n afterCreate?: CreateHookFunction[]\r\n beforeUpdate?: UpdateHookFunction[]\r\n afterUpdate?: UpdateHookFunction[]\r\n beforeDelete?: DeleteHookFunction[]\r\n afterDelete?: DeleteHookFunction[]\r\n beforeRead?: ReadHookFunction[]\r\n afterRead?: ReadHookFunction[]\r\n}\r\n\r\n/**\r\n * A collection definition\r\n */\r\nexport type Collection<T extends Record<string, unknown> = Record<string, unknown>> = {\r\n slug: string\r\n name?: string\r\n fields: Record<string, FieldDefinition>\r\n hooks?: CollectionHooks\r\n dataType?: T\r\n}\r\n\r\n/**\r\n * Creates a new collection\r\n *\r\n * @example\r\n * export const users = collection({\r\n * slug: 'users',\r\n * name: 'Users',\r\n * fields: {\r\n * name: field({ fieldType: text }),\r\n * email: field({ fieldType: email, unique: true })\r\n * }\r\n * })\r\n */\r\nexport const collection = <T extends Record<string, unknown> = Record<string, unknown>>(\r\n config: CollectionConfig<T>\r\n): Collection<T> => {\r\n return {\r\n slug: config.slug,\r\n name: config.name,\r\n fields: config.fields,\r\n hooks: config.hooks,\r\n dataType: config.dataType\r\n }\r\n}\r\n","/* eslint-disable @typescript-eslint/no-explicit-any */\r\nimport { eq, and, like, gt, gte, lt, lte, isNull, inArray, not, desc, asc } from 'drizzle-orm'\r\n\r\nimport type { Collection, CollectionHooks, CreateHookContext, UpdateHookContext, DeleteHookContext, ReadHookContext, OperationHookContext } from '../collection'\r\nimport type {\r\n FindManyOptions,\r\n FindUniqueOptions,\r\n FindFirstOptions,\r\n CreateOptions,\r\n CreateManyOptions,\r\n UpdateOptions,\r\n UpdateManyOptions,\r\n DeleteOptions,\r\n DeleteManyOptions,\r\n CountOptions,\r\n ExistsOptions,\r\n WhereOperator\r\n} from './types'\r\n\r\n/**\r\n * Collection operations interface\r\n */\r\nexport interface CollectionOperations {\r\n findMany<T>(options?: FindManyOptions): Promise<T[]>\r\n findUnique<T>(options: FindUniqueOptions): Promise<T | undefined>\r\n findFirst<T>(options: FindFirstOptions): Promise<T | undefined>\r\n create<T>(options: CreateOptions<T>): Promise<T | undefined>\r\n createMany<T>(options: CreateManyOptions<T>): Promise<number>\r\n update<T>(options: UpdateOptions<T>): Promise<T | undefined>\r\n updateMany<T>(options: UpdateManyOptions<T>): Promise<number>\r\n delete<T>(options: DeleteOptions): Promise<T | undefined>\r\n deleteMany(options: DeleteManyOptions): Promise<number>\r\n count(options?: CountOptions): Promise<number>\r\n exists(options: ExistsOptions): Promise<boolean>\r\n}\r\n\r\n/**\r\n * Build where conditions from options\r\n */\r\nconst buildWhereClause = (\r\n tableColumns: Record<string, any>,\r\n where?: Record<string, unknown>\r\n): any => {\r\n if (!where) return undefined\r\n\r\n const conditions: any[] = []\r\n\r\n for (const [key, value] of Object.entries(where)) {\r\n const column = tableColumns[key]\r\n if (!column) continue\r\n\r\n if (value === null || typeof value !== 'object') {\r\n conditions.push(eq(column, value))\r\n } else {\r\n const operator = value as WhereOperator<unknown>\r\n if ('eq' in operator) {\r\n conditions.push(eq(column, operator.eq))\r\n } else if ('neq' in operator) {\r\n conditions.push(not(eq(column, operator.neq)))\r\n } else if ('gt' in operator) {\r\n conditions.push(gt(column, operator.gt as number))\r\n } else if ('gte' in operator) {\r\n conditions.push(gte(column, operator.gte as number))\r\n } else if ('lt' in operator) {\r\n conditions.push(lt(column, operator.lt as number))\r\n } else if ('lte' in operator) {\r\n conditions.push(lte(column, operator.lte as number))\r\n } else if ('in' in operator) {\r\n conditions.push(inArray(column, operator.in))\r\n } else if ('notIn' in operator) {\r\n conditions.push(not(inArray(column, operator.notIn)))\r\n } else if ('contains' in operator) {\r\n conditions.push(like(column, `%${operator.contains}%`))\r\n } else if ('startsWith' in operator) {\r\n conditions.push(like(column, `${operator.startsWith}%`))\r\n } else if ('endsWith' in operator) {\r\n conditions.push(like(column, `%${operator.endsWith}`))\r\n } else if ('isNull' in operator) {\r\n if (operator.isNull) {\r\n conditions.push(isNull(column))\r\n }\r\n } else if ('not' in operator) {\r\n conditions.push(not(eq(column, operator.not)))\r\n }\r\n }\r\n }\r\n\r\n if (conditions.length === 0) return undefined\r\n if (conditions.length === 1) return conditions[0]\r\n return and(...conditions)\r\n}\r\n\r\n/**\r\n * Build orderBy from options\r\n */\r\nconst buildOrderBy = (\r\n tableColumns: Record<string, any>,\r\n orderBy?: Record<string, unknown> | Record<string, unknown>[]\r\n): any[] => {\r\n if (!orderBy) return []\r\n\r\n const orders = Array.isArray(orderBy) ? orderBy : [orderBy]\r\n return orders.map((order) => {\r\n for (const [key, direction] of Object.entries(order)) {\r\n const column = tableColumns[key]\r\n if (!column) continue\r\n return direction === 'desc' ? desc(column) : asc(column)\r\n }\r\n return undefined\r\n }).filter(Boolean)\r\n}\r\n\r\n/**\r\n * Execute before operation hooks\r\n */\r\nconst executeBeforeOperationHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: OperationHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeOperation) return\r\n for (const hook of hooks.beforeOperation) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after operation hooks\r\n */\r\nconst executeAfterOperationHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: OperationHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterOperation) return\r\n for (const hook of hooks.afterOperation) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute before create hooks\r\n */\r\nconst executeBeforeCreateHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: CreateHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeCreate) return\r\n for (const hook of hooks.beforeCreate) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after create hooks\r\n */\r\nconst executeAfterCreateHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: CreateHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterCreate) return\r\n for (const hook of hooks.afterCreate) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute before update hooks\r\n */\r\nconst executeBeforeUpdateHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: UpdateHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeUpdate) return\r\n for (const hook of hooks.beforeUpdate) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after update hooks\r\n */\r\nconst executeAfterUpdateHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: UpdateHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterUpdate) return\r\n for (const hook of hooks.afterUpdate) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute before delete hooks\r\n */\r\nconst executeBeforeDeleteHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: DeleteHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeDelete) return\r\n for (const hook of hooks.beforeDelete) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after delete hooks\r\n */\r\nconst executeAfterDeleteHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: DeleteHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterDelete) return\r\n for (const hook of hooks.afterDelete) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute before read hooks\r\n */\r\nconst executeBeforeReadHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: ReadHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeRead) return\r\n for (const hook of hooks.beforeRead) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after read hooks\r\n */\r\nconst executeAfterReadHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: ReadHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterRead) return\r\n for (const hook of hooks.afterRead) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Creates collection operations with Drizzle\r\n */\r\nexport const createCollectionOperations = (\r\n _collection: Collection,\r\n _slug: string,\r\n _db: any,\r\n _table: any,\r\n _hooks?: CollectionHooks\r\n): CollectionOperations => {\r\n const tableColumns = _table as Record<string, any>\r\n const db = _db as any\r\n const hooks = _hooks as CollectionHooks | undefined\r\n\r\n // If no db instance, return placeholder operations\r\n if (!db) {\r\n return {\r\n findMany: async <T>(): Promise<T[]> => [],\r\n findUnique: async <T>(): Promise<T | undefined> => undefined,\r\n findFirst: async <T>(): Promise<T | undefined> => undefined,\r\n create: async <T>(): Promise<T | undefined> => undefined,\r\n createMany: async (): Promise<number> => 0,\r\n update: async <T>(): Promise<T | undefined> => undefined,\r\n updateMany: async (): Promise<number> => 0,\r\n delete: async <T>(): Promise<T | undefined> => undefined,\r\n deleteMany: async (): Promise<number> => 0,\r\n count: async (): Promise<number> => 0,\r\n exists: async (): Promise<boolean> => false\r\n }\r\n }\r\n\r\n return {\r\n findMany: async <T>(options?: FindManyOptions): Promise<T[]> => {\r\n const whereClause = buildWhereClause(tableColumns, options?.where)\r\n const orderByClause = buildOrderBy(tableColumns, options?.orderBy)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options?.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n let query = db.select().from(_table)\r\n\r\n if (whereClause) {\r\n query = query.where(whereClause)\r\n }\r\n\r\n if (orderByClause.length > 0) {\r\n query = query.orderBy(...orderByClause)\r\n }\r\n\r\n if (options?.offset) {\r\n query = query.offset(options.offset)\r\n }\r\n\r\n if (options?.limit) {\r\n query = query.limit(options.limit)\r\n }\r\n\r\n const result = await query\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result: result as unknown[],\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options?.where,\r\n result\r\n })\r\n\r\n return result as T[]\r\n },\r\n\r\n findUnique: async <T>(options: FindUniqueOptions): Promise<T | undefined> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return undefined\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n const result = await db.select().from(_table).where(whereClause).limit(1)\r\n const returnValue = result[0] as T | undefined\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result: returnValue ? [returnValue] : [],\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n findFirst: async <T>(options: FindFirstOptions): Promise<T | undefined> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n const orderByClause = buildOrderBy(tableColumns, options.orderBy)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n let query = db.select().from(_table)\r\n\r\n if (whereClause) {\r\n query = query.where(whereClause)\r\n }\r\n\r\n if (orderByClause.length > 0) {\r\n query = query.orderBy(...orderByClause)\r\n }\r\n\r\n const result = await query.limit(1)\r\n const returnValue = result[0] as T | undefined\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result: returnValue ? [returnValue] : [],\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n create: async <T>(options: CreateOptions<T>): Promise<T | undefined> => {\r\n const data = Array.isArray(options.data) ? options.data : [options.data]\r\n const firstData = data[0] as Record<string, unknown>\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: firstData,\r\n where: undefined\r\n })\r\n\r\n // Execute before create hooks\r\n await executeBeforeCreateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: firstData,\r\n db\r\n })\r\n\r\n const result = await db.insert(_table).values(data).returning()\r\n\r\n const returnValue = options.returning ? result[0] as T : undefined\r\n\r\n // Execute after create hooks\r\n await executeAfterCreateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: firstData,\r\n result: returnValue,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: firstData,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n createMany: async <T>(options: CreateManyOptions<T>): Promise<number> => {\r\n const dataArray = Array.isArray(options.data) ? options.data : [options.data]\r\n\r\n // Execute before operation hooks for each item\r\n for (const data of dataArray) {\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: data as Record<string, unknown>,\r\n where: undefined\r\n })\r\n\r\n await executeBeforeCreateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: data as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n const result = await db.insert(_table).values(options.data as any)\r\n\r\n // Execute after operation hooks for each item\r\n for (let i = 0; i < dataArray.length; i++) {\r\n await executeAfterCreateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: dataArray[i] as Record<string, unknown>,\r\n result: result[i],\r\n db\r\n })\r\n\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: dataArray[i] as Record<string, unknown>,\r\n result: result[i]\r\n })\r\n }\r\n\r\n return result.length || 0\r\n },\r\n\r\n update: async <T>(options: UpdateOptions<T>): Promise<T | undefined> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return undefined\r\n\r\n // Get previous data for hooks\r\n const previousResult = await db.select().from(_table).where(whereClause).limit(1)\r\n const previousData = previousResult[0] as Record<string, unknown> | undefined\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where\r\n })\r\n\r\n // Execute before update hooks\r\n await executeBeforeUpdateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n previousData,\r\n db\r\n })\r\n\r\n const result = await db.update(_table)\r\n .set(options.data as any)\r\n .where(whereClause)\r\n .returning()\r\n\r\n const returnValue = options.returning ? result[0] as T : undefined\r\n\r\n // Execute after update hooks\r\n await executeAfterUpdateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n previousData,\r\n result: returnValue,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n updateMany: async <T>(options: UpdateManyOptions<T>): Promise<number> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return 0\r\n\r\n // Get previous data for hooks\r\n const previousResults = await db.select().from(_table).where(whereClause)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where\r\n })\r\n\r\n // Execute before update hooks (for each previous record)\r\n for (const previousData of previousResults) {\r\n await executeBeforeUpdateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n previousData: previousData as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n const result = await db.update(_table)\r\n .set(options.data as any)\r\n .where(whereClause)\r\n\r\n // Execute after update hooks\r\n for (const previousData of previousResults) {\r\n await executeAfterUpdateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n previousData: previousData as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where\r\n })\r\n\r\n return result.length || 0\r\n },\r\n\r\n delete: async <T>(options: DeleteOptions): Promise<T | undefined> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return undefined\r\n\r\n // Get previous data for hooks\r\n const previousResult = await db.select().from(_table).where(whereClause).limit(1)\r\n const previousData = previousResult[0] as Record<string, unknown> | undefined\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where\r\n })\r\n\r\n // Execute before delete hooks\r\n await executeBeforeDeleteHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n previousData,\r\n db\r\n })\r\n\r\n const result = await db.delete(_table)\r\n .where(whereClause)\r\n .returning()\r\n\r\n const returnValue = options.returning ? result[0] as T : undefined\r\n\r\n // Execute after delete hooks\r\n await executeAfterDeleteHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n previousData,\r\n result: returnValue,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n deleteMany: async (options: DeleteManyOptions): Promise<number> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return 0\r\n\r\n // Get previous data for hooks\r\n const previousResults = await db.select().from(_table).where(whereClause)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where\r\n })\r\n\r\n // Execute before delete hooks\r\n for (const previousData of previousResults) {\r\n await executeBeforeDeleteHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n previousData: previousData as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n const result = await db.delete(_table).where(whereClause)\r\n\r\n // Execute after delete hooks\r\n for (const previousData of previousResults) {\r\n await executeAfterDeleteHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n previousData: previousData as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where\r\n })\r\n\r\n return result.length || 0\r\n },\r\n\r\n count: async (options?: CountOptions): Promise<number> => {\r\n const whereClause = buildWhereClause(tableColumns, options?.where)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options?.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n const result = whereClause\r\n ? await db.select().from(_table).where(whereClause)\r\n : await db.select().from(_table)\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options?.where,\r\n result\r\n })\r\n\r\n return result.length\r\n },\r\n\r\n exists: async (options: ExistsOptions): Promise<boolean> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return false\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n const result = await db.select().from(_table).where(whereClause).limit(1)\r\n const returnValue = result.length > 0\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result: result,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n }\r\n }\r\n}\r\n","/**\r\n * PostgreSQL adapter configuration\r\n */\r\nexport type PgAdapterConfig = {\r\n url: string\r\n migrationsPath?: string\r\n}\r\n\r\n/**\r\n * PostgreSQL adapter\r\n */\r\nexport interface PgAdapter {\r\n type: 'postgres'\r\n config: PgAdapterConfig\r\n}\r\n\r\n/**\r\n * Database adapter type\r\n */\r\nexport type DatabaseAdapter = PgAdapter\r\n\r\n/**\r\n * Creates a PostgreSQL adapter\r\n *\r\n * @example\r\n * const adapter = pgAdapter({\r\n * url: 'postgres://user:pass@localhost:5432/db'\r\n * })\r\n */\r\nexport const pgAdapter = (config: PgAdapterConfig): PgAdapter => {\r\n return {\r\n type: 'postgres',\r\n config: {\r\n url: config.url,\r\n migrationsPath: config.migrationsPath ?? './migrations'\r\n }\r\n }\r\n}\r\n","import { pgTable, serial, text, timestamp, uuid, varchar, boolean, integer } from 'drizzle-orm/pg-core'\r\n\r\nimport type { Collection } from './collection'\r\n\r\n/**\r\n * Build Drizzle table from collection definition\r\n */\r\nexport const buildTable = (collection: Collection) => {\r\n // Build columns object\r\n const columns: Record<string, unknown> = {\r\n // Add default id column\r\n id: serial('id').primaryKey()\r\n }\r\n\r\n // Build columns from fields\r\n for (const [fieldName, fieldDef] of Object.entries(collection.fields)) {\r\n // Skip the id field if explicitly defined in fields\r\n if (fieldName === 'id') continue\r\n\r\n const fieldType = fieldDef.fieldType as { name?: string; type?: string }\r\n const fieldTypeName = fieldType.name || fieldType.type || 'text'\r\n\r\n switch (fieldTypeName) {\r\n case 'text':\r\n columns[fieldName] = text(fieldName)\r\n break\r\n case 'varchar':\r\n columns[fieldName] = varchar(fieldName, { length: 255 })\r\n break\r\n case 'number':\r\n case 'integer':\r\n columns[fieldName] = integer(fieldName)\r\n break\r\n case 'boolean':\r\n columns[fieldName] = boolean(fieldName)\r\n break\r\n case 'timestamp':\r\n columns[fieldName] = timestamp(fieldName)\r\n break\r\n case 'uuid':\r\n columns[fieldName] = uuid(fieldName)\r\n break\r\n default:\r\n columns[fieldName] = text(fieldName)\r\n }\r\n }\r\n\r\n return pgTable(collection.slug, columns as Record<string, ReturnType<typeof text>>)\r\n}\r\n\r\n/**\r\n * Build all tables from collections\r\n */\r\nexport const buildSchema = (collections: Collection[]) => {\r\n const tables: Record<string, ReturnType<typeof pgTable>> = {}\r\n\r\n for (const coll of collections) {\r\n tables[coll.slug] = buildTable(coll)\r\n }\r\n\r\n return tables\r\n}\r\n","import { Pool, type Pool as PoolType } from 'pg'\r\nimport { drizzle } from 'drizzle-orm/node-postgres'\r\n\r\nimport type { Collection } from './collection'\r\nimport type { DatabaseAdapter } from './adapter'\r\nimport { buildSchema } from './schema'\r\n\r\n/**\r\n * Plugin interface\r\n */\r\nexport type Plugin = {\r\n name: string\r\n collections?: Record<string, Collection>\r\n hooks?: Record<string, unknown[]>\r\n}\r\n\r\n/**\r\n * Configuration options\r\n */\r\nexport type ConfigOptions<T extends Collection[] = []> = {\r\n database: DatabaseAdapter\r\n collections: T\r\n plugins?: Plugin[]\r\n}\r\n\r\n/**\r\n * Define config return type with inferred collection keys\r\n *\r\n * - collections: metadata only (slug, name, fields, hooks, dataType)\r\n * - db: Drizzle instance with operations (via schema tables)\r\n * - $meta: array of collection slugs and plugin names\r\n */\r\nexport type DefineConfigReturn<T extends Collection[] = []> = {\r\n collections: {\r\n [K in T[number] as K['slug']]: Collection\r\n }\r\n db: ReturnType<typeof drizzle<Record<string, unknown>>>\r\n $meta: {\r\n collections: T[number]['slug'][]\r\n plugins: string[]\r\n }\r\n}\r\n\r\n/**\r\n * Creates the configuration for the data layer\r\n *\r\n * @example\r\n * const adapter = pgAdapter({\r\n * url: process.env.DATABASE_URL!\r\n * })\r\n *\r\n * export const { collections, db } = defineConfig({\r\n * database: adapter,\r\n * collections: [users, posts],\r\n * plugins: [timestampsPlugin()]\r\n * })\r\n *\r\n * // collections: metadata only\r\n * collections.users.slug // 'users'\r\n * collections.users.fields // { name, email, ... }\r\n *\r\n * // db: Drizzle instance with operations\r\n * await db.users.findMany()\r\n * await db.users.insert(values)\r\n */\r\nexport const defineConfig = <T extends Collection[]>(\r\n options: ConfigOptions<T>\r\n): DefineConfigReturn<T> => {\r\n // Initialize the database connection based on adapter type\r\n let pool: PoolType | null = null\r\n let dbInstance: ReturnType<typeof drizzle<Record<string, unknown>>> | null = null\r\n\r\n let schema: Record<string, unknown> = {}\r\n\r\n if (options.database.type === 'postgres') {\r\n // Create pool from adapter config\r\n pool = new Pool({\r\n connectionString: options.database.config.url\r\n })\r\n\r\n // Build schema from collections\r\n schema = buildSchema(options.collections as Collection[])\r\n\r\n // Create Drizzle instance with schema\r\n dbInstance = drizzle(pool, { schema })\r\n }\r\n\r\n // Build collections map (metadata only)\r\n const collectionsMap: Record<string, Collection> = {}\r\n const collectionNames: string[] = []\r\n\r\n for (const coll of options.collections) {\r\n // Store only metadata (not operations)\r\n collectionsMap[coll.slug] = {\r\n slug: coll.slug,\r\n name: coll.name,\r\n fields: coll.fields,\r\n hooks: coll.hooks,\r\n dataType: coll.dataType\r\n }\r\n collectionNames.push(coll.slug)\r\n }\r\n\r\n // Build plugins map\r\n const pluginNames: string[] = []\r\n if (options.plugins) {\r\n for (const plugin of options.plugins) {\r\n pluginNames.push(plugin.name)\r\n\r\n // Register plugin collections (metadata only)\r\n if (plugin.collections) {\r\n for (const [name, coll] of Object.entries(plugin.collections)) {\r\n collectionsMap[name] = {\r\n slug: coll.slug,\r\n name: coll.name,\r\n fields: coll.fields,\r\n hooks: coll.hooks,\r\n dataType: coll.dataType\r\n }\r\n collectionNames.push(name)\r\n }\r\n }\r\n }\r\n }\r\n\r\n return {\r\n collections: collectionsMap as DefineConfigReturn<T>['collections'],\r\n db: dbInstance as DefineConfigReturn<T>['db'],\r\n $meta: {\r\n collections: collectionNames as DefineConfigReturn<T>['$meta']['collections'],\r\n plugins: pluginNames\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["collection","fieldType","process","module"]}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
// src/next/index.ts
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import process from "process";
|
|
4
|
+
|
|
5
|
+
// src/field-type.ts
|
|
6
|
+
var fieldType = (config) => {
|
|
7
|
+
return () => ({
|
|
8
|
+
schema: config.schema,
|
|
9
|
+
database: config.database ?? {}
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/field.ts
|
|
14
|
+
var field = (config) => {
|
|
15
|
+
return {
|
|
16
|
+
fieldType: config.fieldType,
|
|
17
|
+
required: config.required ?? false,
|
|
18
|
+
unique: config.unique ?? false,
|
|
19
|
+
indexed: config.indexed ?? false,
|
|
20
|
+
default: config.default,
|
|
21
|
+
label: config.label,
|
|
22
|
+
description: config.description
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// src/fields/f.ts
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
var getItemType = (itemSchema) => {
|
|
29
|
+
if (itemSchema instanceof z.ZodString) return "text";
|
|
30
|
+
if (itemSchema instanceof z.ZodNumber) return "integer";
|
|
31
|
+
if (itemSchema instanceof z.ZodBoolean) return "boolean";
|
|
32
|
+
if (itemSchema instanceof z.ZodDate) return "timestamp";
|
|
33
|
+
if (itemSchema instanceof z.ZodEnum) return "text";
|
|
34
|
+
if (itemSchema instanceof z.ZodArray) return "array";
|
|
35
|
+
if (itemSchema instanceof z.ZodObject) return "jsonb";
|
|
36
|
+
return "text";
|
|
37
|
+
};
|
|
38
|
+
var f = {
|
|
39
|
+
/**
|
|
40
|
+
* Text field type
|
|
41
|
+
*/
|
|
42
|
+
text: () => fieldType({
|
|
43
|
+
schema: z.string(),
|
|
44
|
+
database: { type: "text" }
|
|
45
|
+
})(),
|
|
46
|
+
/**
|
|
47
|
+
* Email field type with built-in validation
|
|
48
|
+
*/
|
|
49
|
+
email: () => fieldType({
|
|
50
|
+
schema: z.string().email(),
|
|
51
|
+
database: { type: "text" }
|
|
52
|
+
})(),
|
|
53
|
+
/**
|
|
54
|
+
* URL field type with built-in validation
|
|
55
|
+
*/
|
|
56
|
+
url: () => fieldType({
|
|
57
|
+
schema: z.string().url(),
|
|
58
|
+
database: { type: "text" }
|
|
59
|
+
})(),
|
|
60
|
+
/**
|
|
61
|
+
* Number field type
|
|
62
|
+
*/
|
|
63
|
+
number: () => fieldType({
|
|
64
|
+
schema: z.number(),
|
|
65
|
+
database: { type: "integer" }
|
|
66
|
+
})(),
|
|
67
|
+
/**
|
|
68
|
+
* Boolean field type
|
|
69
|
+
*/
|
|
70
|
+
boolean: () => fieldType({
|
|
71
|
+
schema: z.boolean(),
|
|
72
|
+
database: { type: "boolean" }
|
|
73
|
+
})(),
|
|
74
|
+
/**
|
|
75
|
+
* Date field type (date only, no time)
|
|
76
|
+
*/
|
|
77
|
+
date: () => fieldType({
|
|
78
|
+
schema: z.date(),
|
|
79
|
+
database: { type: "date" }
|
|
80
|
+
})(),
|
|
81
|
+
/**
|
|
82
|
+
* Timestamp field type (date with time)
|
|
83
|
+
*/
|
|
84
|
+
timestamp: () => fieldType({
|
|
85
|
+
schema: z.date(),
|
|
86
|
+
database: { type: "timestamp" }
|
|
87
|
+
})(),
|
|
88
|
+
/**
|
|
89
|
+
* Creates a select field type
|
|
90
|
+
*/
|
|
91
|
+
select: (options) => fieldType({
|
|
92
|
+
schema: z.enum(options),
|
|
93
|
+
database: { type: "text" }
|
|
94
|
+
})(),
|
|
95
|
+
/**
|
|
96
|
+
* JSON field type for storing JSON data
|
|
97
|
+
*/
|
|
98
|
+
json: (schema) => fieldType({
|
|
99
|
+
schema: schema ?? z.any(),
|
|
100
|
+
database: { type: "jsonb" }
|
|
101
|
+
})(),
|
|
102
|
+
/**
|
|
103
|
+
* Array field type for storing lists
|
|
104
|
+
*/
|
|
105
|
+
array: (itemSchema) => fieldType({
|
|
106
|
+
schema: z.array(itemSchema),
|
|
107
|
+
database: { type: "array", itemType: getItemType(itemSchema) }
|
|
108
|
+
})(),
|
|
109
|
+
/**
|
|
110
|
+
* Creates a relation field type for foreign key relationships
|
|
111
|
+
*/
|
|
112
|
+
relation: (options) => {
|
|
113
|
+
const isMany = options.many ?? false;
|
|
114
|
+
const isSingular = options.singular ?? false;
|
|
115
|
+
return fieldType({
|
|
116
|
+
schema: isMany ? z.array(z.string()) : z.string(),
|
|
117
|
+
database: {
|
|
118
|
+
type: "integer",
|
|
119
|
+
references: options.collection,
|
|
120
|
+
through: options.through,
|
|
121
|
+
many: isMany,
|
|
122
|
+
singular: isSingular
|
|
123
|
+
}
|
|
124
|
+
})();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var text = f.text;
|
|
128
|
+
var email = f.email;
|
|
129
|
+
var url = f.url;
|
|
130
|
+
var number = f.number;
|
|
131
|
+
var boolean = f.boolean;
|
|
132
|
+
var date = f.date;
|
|
133
|
+
var timestamp = f.timestamp;
|
|
134
|
+
var select = f.select;
|
|
135
|
+
var json = f.json;
|
|
136
|
+
var array = f.array;
|
|
137
|
+
var relation = f.relation;
|
|
138
|
+
|
|
139
|
+
// src/collection.ts
|
|
140
|
+
var collection = (config) => {
|
|
141
|
+
return {
|
|
142
|
+
slug: config.slug,
|
|
143
|
+
name: config.name,
|
|
144
|
+
fields: config.fields,
|
|
145
|
+
hooks: config.hooks,
|
|
146
|
+
dataType: config.dataType
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// src/operations/collection-operations.ts
|
|
151
|
+
import { eq, and, like, gt, gte, lt, lte, isNull, inArray, not, desc, asc } from "drizzle-orm";
|
|
152
|
+
|
|
153
|
+
// src/adapter.ts
|
|
154
|
+
var pgAdapter = (config) => {
|
|
155
|
+
return {
|
|
156
|
+
type: "postgres",
|
|
157
|
+
config: {
|
|
158
|
+
url: config.url,
|
|
159
|
+
migrationsPath: config.migrationsPath ?? "./migrations"
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/schema.ts
|
|
165
|
+
import { pgTable, serial, text as text2, timestamp as timestamp2, uuid, varchar, boolean as boolean2, integer } from "drizzle-orm/pg-core";
|
|
166
|
+
var buildTable = (collection2) => {
|
|
167
|
+
const columns = {
|
|
168
|
+
// Add default id column
|
|
169
|
+
id: serial("id").primaryKey()
|
|
170
|
+
};
|
|
171
|
+
for (const [fieldName, fieldDef] of Object.entries(collection2.fields)) {
|
|
172
|
+
if (fieldName === "id") continue;
|
|
173
|
+
const fieldType2 = fieldDef.fieldType;
|
|
174
|
+
const fieldTypeName = fieldType2.name || fieldType2.type || "text";
|
|
175
|
+
switch (fieldTypeName) {
|
|
176
|
+
case "text":
|
|
177
|
+
columns[fieldName] = text2(fieldName);
|
|
178
|
+
break;
|
|
179
|
+
case "varchar":
|
|
180
|
+
columns[fieldName] = varchar(fieldName, { length: 255 });
|
|
181
|
+
break;
|
|
182
|
+
case "number":
|
|
183
|
+
case "integer":
|
|
184
|
+
columns[fieldName] = integer(fieldName);
|
|
185
|
+
break;
|
|
186
|
+
case "boolean":
|
|
187
|
+
columns[fieldName] = boolean2(fieldName);
|
|
188
|
+
break;
|
|
189
|
+
case "timestamp":
|
|
190
|
+
columns[fieldName] = timestamp2(fieldName);
|
|
191
|
+
break;
|
|
192
|
+
case "uuid":
|
|
193
|
+
columns[fieldName] = uuid(fieldName);
|
|
194
|
+
break;
|
|
195
|
+
default:
|
|
196
|
+
columns[fieldName] = text2(fieldName);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return pgTable(collection2.slug, columns);
|
|
200
|
+
};
|
|
201
|
+
var buildSchema = (collections) => {
|
|
202
|
+
const tables = {};
|
|
203
|
+
for (const coll of collections) {
|
|
204
|
+
tables[coll.slug] = buildTable(coll);
|
|
205
|
+
}
|
|
206
|
+
return tables;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// src/config.ts
|
|
210
|
+
import { Pool } from "pg";
|
|
211
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
212
|
+
var defineConfig = (options) => {
|
|
213
|
+
let pool = null;
|
|
214
|
+
let dbInstance = null;
|
|
215
|
+
let schema = {};
|
|
216
|
+
if (options.database.type === "postgres") {
|
|
217
|
+
pool = new Pool({
|
|
218
|
+
connectionString: options.database.config.url
|
|
219
|
+
});
|
|
220
|
+
schema = buildSchema(options.collections);
|
|
221
|
+
dbInstance = drizzle(pool, { schema });
|
|
222
|
+
}
|
|
223
|
+
const collectionsMap = {};
|
|
224
|
+
const collectionNames = [];
|
|
225
|
+
for (const coll of options.collections) {
|
|
226
|
+
collectionsMap[coll.slug] = {
|
|
227
|
+
slug: coll.slug,
|
|
228
|
+
name: coll.name,
|
|
229
|
+
fields: coll.fields,
|
|
230
|
+
hooks: coll.hooks,
|
|
231
|
+
dataType: coll.dataType
|
|
232
|
+
};
|
|
233
|
+
collectionNames.push(coll.slug);
|
|
234
|
+
}
|
|
235
|
+
const pluginNames = [];
|
|
236
|
+
if (options.plugins) {
|
|
237
|
+
for (const plugin of options.plugins) {
|
|
238
|
+
pluginNames.push(plugin.name);
|
|
239
|
+
if (plugin.collections) {
|
|
240
|
+
for (const [name, coll] of Object.entries(plugin.collections)) {
|
|
241
|
+
collectionsMap[name] = {
|
|
242
|
+
slug: coll.slug,
|
|
243
|
+
name: coll.name,
|
|
244
|
+
fields: coll.fields,
|
|
245
|
+
hooks: coll.hooks,
|
|
246
|
+
dataType: coll.dataType
|
|
247
|
+
};
|
|
248
|
+
collectionNames.push(name);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
collections: collectionsMap,
|
|
255
|
+
db: dbInstance,
|
|
256
|
+
$meta: {
|
|
257
|
+
collections: collectionNames,
|
|
258
|
+
plugins: pluginNames
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/next/index.ts
|
|
264
|
+
function isDevelopment() {
|
|
265
|
+
return process.env.NODE_ENV === "development";
|
|
266
|
+
}
|
|
267
|
+
function isBuildTime() {
|
|
268
|
+
return process.env.__NEXT_BUILD === "1" || process.env.TURBO_BUILD === "1" || process.env.VERCEL === "1";
|
|
269
|
+
}
|
|
270
|
+
function debugLog(enabled, ...args) {
|
|
271
|
+
if (enabled) console.debug("[collections/next]", ...args);
|
|
272
|
+
}
|
|
273
|
+
function validateConfigPath(configPath, debug) {
|
|
274
|
+
const projectRoot = process.cwd();
|
|
275
|
+
const resolvedPath = resolve(projectRoot, configPath);
|
|
276
|
+
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
277
|
+
const normalizedRoot = projectRoot.replace(/\\/g, "/");
|
|
278
|
+
if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
|
|
279
|
+
throw new Error(`Config path must be within project directory: ${configPath}`);
|
|
280
|
+
}
|
|
281
|
+
debugLog(debug, "Validated config path:", resolvedPath);
|
|
282
|
+
return resolvedPath;
|
|
283
|
+
}
|
|
284
|
+
function extractCollectionsFromModule(module) {
|
|
285
|
+
const defaultExport = module.default;
|
|
286
|
+
const defaultCollections = defaultExport ? extractFromObject(defaultExport) : {};
|
|
287
|
+
const namedCollections = extractFromObject(module);
|
|
288
|
+
return { ...defaultCollections, ...namedCollections };
|
|
289
|
+
}
|
|
290
|
+
function extractFromObject(obj) {
|
|
291
|
+
const collections = {};
|
|
292
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
293
|
+
if (key !== "default" && typeof value === "object" && value !== null && "slug" in value) {
|
|
294
|
+
collections[key] = value;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return collections;
|
|
298
|
+
}
|
|
299
|
+
async function loadCollectionsFromConfig(configPath, debug) {
|
|
300
|
+
try {
|
|
301
|
+
const validatedPath = validateConfigPath(configPath, debug);
|
|
302
|
+
const module = await import(
|
|
303
|
+
/* @vite-ignore */
|
|
304
|
+
validatedPath
|
|
305
|
+
);
|
|
306
|
+
const collections = extractCollectionsFromModule(module);
|
|
307
|
+
debugLog(debug, `Loaded ${Object.keys(collections).length} collections`);
|
|
308
|
+
return collections;
|
|
309
|
+
} catch {
|
|
310
|
+
return {};
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async function withCollections(nextConfig, options = {}) {
|
|
314
|
+
const { configPath = "./collections/config", hotReload = true, outputDir = "./drizzle", debug = false } = options;
|
|
315
|
+
const isProduction = !isDevelopment();
|
|
316
|
+
const shouldHotReload = hotReload && isDevelopment() && !isBuildTime();
|
|
317
|
+
const collections = await loadCollectionsFromConfig(configPath, debug);
|
|
318
|
+
let webpackConfig;
|
|
319
|
+
if (shouldHotReload && nextConfig.webpack) {
|
|
320
|
+
webpackConfig = (cfg, ctx) => nextConfig.webpack(cfg, ctx);
|
|
321
|
+
}
|
|
322
|
+
return { ...nextConfig, collections: { collections, outputDir, isProduction, configPath }, webpack: webpackConfig };
|
|
323
|
+
}
|
|
324
|
+
function withCollectionsSync(nextConfig, options = {}) {
|
|
325
|
+
const { configPath = "./collections/config", hotReload = true, outputDir = "./drizzle" } = options;
|
|
326
|
+
const isProduction = !isDevelopment();
|
|
327
|
+
const shouldHotReload = hotReload && isDevelopment() && !isBuildTime();
|
|
328
|
+
let webpackConfig;
|
|
329
|
+
if (shouldHotReload && nextConfig.webpack) {
|
|
330
|
+
webpackConfig = (cfg, ctx) => nextConfig.webpack(cfg, ctx);
|
|
331
|
+
}
|
|
332
|
+
return { ...nextConfig, collections: { collections: {}, outputDir, isProduction, configPath }, webpack: webpackConfig };
|
|
333
|
+
}
|
|
334
|
+
function getCollectionsConfig(nextConfig) {
|
|
335
|
+
return nextConfig.collections;
|
|
336
|
+
}
|
|
337
|
+
async function loadCollections(configPath) {
|
|
338
|
+
return loadCollectionsFromConfig(configPath, false);
|
|
339
|
+
}
|
|
340
|
+
function getCollectionsFromConfig(nextConfig) {
|
|
341
|
+
return nextConfig.collections?.collections ?? {};
|
|
342
|
+
}
|
|
343
|
+
function isNextConfig(config) {
|
|
344
|
+
if (!config || typeof config !== "object") return false;
|
|
345
|
+
const cfg = config;
|
|
346
|
+
if ("reactStrictMode" in cfg && typeof cfg.reactStrictMode !== "boolean") return false;
|
|
347
|
+
if ("swcMinify" in cfg && typeof cfg.swcMinify !== "boolean") return false;
|
|
348
|
+
const validProps = ["reactStrictMode", "swcMinify", "trailingSlash", "poweredByHeader", "compress"];
|
|
349
|
+
return validProps.some((prop) => prop in cfg);
|
|
350
|
+
}
|
|
351
|
+
function isCollectionsConfig(config) {
|
|
352
|
+
if (!config || typeof config !== "object") return false;
|
|
353
|
+
return config.collections !== void 0;
|
|
354
|
+
}
|
|
355
|
+
var defaultWithCollectionsOptions = {
|
|
356
|
+
configPath: "./collections/config",
|
|
357
|
+
hotReload: true,
|
|
358
|
+
outputDir: "./drizzle",
|
|
359
|
+
debug: false
|
|
360
|
+
};
|
|
361
|
+
export {
|
|
362
|
+
buildSchema,
|
|
363
|
+
collection,
|
|
364
|
+
defaultWithCollectionsOptions,
|
|
365
|
+
defineConfig,
|
|
366
|
+
f,
|
|
367
|
+
field,
|
|
368
|
+
getCollectionsConfig,
|
|
369
|
+
getCollectionsFromConfig,
|
|
370
|
+
isCollectionsConfig,
|
|
371
|
+
isNextConfig,
|
|
372
|
+
loadCollections,
|
|
373
|
+
pgAdapter,
|
|
374
|
+
withCollections,
|
|
375
|
+
withCollectionsSync
|
|
376
|
+
};
|
|
377
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/next/index.ts","../../src/field-type.ts","../../src/field.ts","../../src/fields/f.ts","../../src/collection.ts","../../src/operations/collection-operations.ts","../../src/adapter.ts","../../src/schema.ts","../../src/config.ts"],"sourcesContent":["/**\r\n * Next.js integration module for @deessejs/collections\r\n *\r\n * Provides hot reload support and Next.js plugin for collections\r\n */\r\n\r\nimport type { NextConfig } from 'next'\r\nimport type { Collection } from '../collection'\r\nimport { resolve } from 'path'\r\nimport process from 'process'\r\n\r\n// Re-export types from core for convenience\r\nexport type {\r\n Collection,\r\n CollectionConfig,\r\n CollectionHooks,\r\n OperationType,\r\n CreateHookContext,\r\n UpdateHookContext,\r\n DeleteHookContext,\r\n ReadHookContext,\r\n OperationHookContext\r\n} from '../collection'\r\n\r\n// Re-export field types\r\nexport type { FieldDefinition, FieldOptions } from '../field'\r\nexport type { FieldTypeInstance } from '../field-type'\r\n\r\n// Re-export field types and utilities\r\nexport { field, f, defineConfig, pgAdapter, buildSchema, collection } from '../index'\r\n\r\n/**\r\n * Type for withCollections configuration\r\n */\r\nexport interface WithCollectionsOptions {\r\n /**\r\n * Path to the collections config file\r\n * @default './collections/config'\r\n */\r\n configPath?: string\r\n\r\n /**\r\n * Enable hot reload in development\r\n * @default true\r\n */\r\n hotReload?: boolean\r\n\r\n /**\r\n * Output directory for generated schema\r\n * @default './drizzle'\r\n */\r\n outputDir?: string\r\n\r\n /**\r\n * Enable debug logging\r\n * @default false\r\n */\r\n debug?: boolean\r\n}\r\n\r\n/**\r\n * Type for the Next.js config returned by withCollections\r\n */\r\nexport type WithCollectionsConfig = NextConfig & {\r\n collections?: {\r\n collections: Record<string, Collection>\r\n outputDir: string\r\n isProduction: boolean\r\n configPath: string\r\n }\r\n}\r\n\r\n/**\r\n * Check if we're running in development mode\r\n */\r\nfunction isDevelopment(): boolean {\r\n return process.env.NODE_ENV === 'development'\r\n}\r\n\r\n/**\r\n * Check if we're running in a build-time context\r\n */\r\nfunction isBuildTime(): boolean {\r\n return process.env.__NEXT_BUILD === '1' || process.env.TURBO_BUILD === '1' || process.env.VERCEL === '1'\r\n}\r\n\r\n/**\r\n * Debug logging function\r\n */\r\nfunction debugLog(enabled: boolean, ...args: unknown[]): void {\r\n if (enabled) console.debug('[collections/next]', ...args)\r\n}\r\n\r\n/**\r\n * Validate that a path is within the project directory\r\n */\r\nfunction validateConfigPath(configPath: string, debug: boolean): string {\r\n const projectRoot = process.cwd()\r\n const resolvedPath = resolve(projectRoot, configPath)\r\n const normalizedPath = resolvedPath.replace(/\\\\/g, '/')\r\n const normalizedRoot = projectRoot.replace(/\\\\/g, '/')\r\n\r\n if (!normalizedPath.startsWith(normalizedRoot + '/') && normalizedPath !== normalizedRoot) {\r\n throw new Error(`Config path must be within project directory: ${configPath}`)\r\n }\r\n\r\n debugLog(debug, 'Validated config path:', resolvedPath)\r\n return resolvedPath\r\n}\r\n\r\n/**\r\n * Extract collections from a loaded module\r\n */\r\nfunction extractCollectionsFromModule(module: Record<string, unknown>): Record<string, Collection> {\r\n const defaultExport = module.default as Record<string, unknown> | undefined\r\n const defaultCollections = defaultExport ? extractFromObject(defaultExport) : {}\r\n const namedCollections = extractFromObject(module)\r\n\r\n return { ...defaultCollections, ...namedCollections }\r\n}\r\n\r\nfunction extractFromObject(obj: Record<string, unknown>): Record<string, Collection> {\r\n const collections: Record<string, Collection> = {}\r\n for (const [key, value] of Object.entries(obj)) {\r\n if (key !== 'default' && typeof value === 'object' && value !== null && 'slug' in value) {\r\n collections[key] = value as Collection\r\n }\r\n }\r\n return collections\r\n}\r\n\r\n/**\r\n * Load collections from a config file using dynamic import\r\n */\r\nasync function loadCollectionsFromConfig(configPath: string, debug: boolean): Promise<Record<string, Collection>> {\r\n try {\r\n const validatedPath = validateConfigPath(configPath, debug)\r\n const module = await import(/* @vite-ignore */ validatedPath)\r\n const collections = extractCollectionsFromModule(module as Record<string, unknown>)\r\n debugLog(debug, `Loaded ${Object.keys(collections).length} collections`)\r\n return collections\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Create a Next.js plugin that provides collections integration\r\n *\r\n * @example\r\n * // next.config.mjs\r\n * import { withCollections } from '@deessejs/collections/next'\r\n *\r\n * export default withCollections({\r\n * // Next.js config\r\n * })\r\n */\r\nexport async function withCollections(nextConfig: NextConfig, options: WithCollectionsOptions = {}): Promise<WithCollectionsConfig> {\r\n const { configPath = './collections/config', hotReload = true, outputDir = './drizzle', debug = false } = options\r\n const isProduction = !isDevelopment()\r\n const shouldHotReload = hotReload && isDevelopment() && !isBuildTime()\r\n\r\n const collections = await loadCollectionsFromConfig(configPath, debug)\r\n let webpackConfig: NextConfig['webpack']\r\n\r\n if (shouldHotReload && nextConfig.webpack) {\r\n webpackConfig = (cfg, ctx) => nextConfig.webpack!(cfg, ctx)\r\n }\r\n\r\n return { ...nextConfig, collections: { collections, outputDir, isProduction, configPath }, webpack: webpackConfig }\r\n}\r\n\r\n/**\r\n * Synchronous version of withCollections\r\n * @deprecated Use withCollections() instead\r\n */\r\nexport function withCollectionsSync(nextConfig: NextConfig, options: WithCollectionsOptions = {}): WithCollectionsConfig {\r\n const { configPath = './collections/config', hotReload = true, outputDir = './drizzle' } = options\r\n const isProduction = !isDevelopment()\r\n const shouldHotReload = hotReload && isDevelopment() && !isBuildTime()\r\n let webpackConfig: NextConfig['webpack']\r\n\r\n if (shouldHotReload && nextConfig.webpack) {\r\n webpackConfig = (cfg, ctx) => nextConfig.webpack!(cfg, ctx)\r\n }\r\n\r\n return { ...nextConfig, collections: { collections: {}, outputDir, isProduction, configPath }, webpack: webpackConfig }\r\n}\r\n\r\n/**\r\n * Get the collections configuration from the Next.js config\r\n */\r\nexport function getCollectionsConfig(nextConfig: WithCollectionsConfig) {\r\n return nextConfig.collections\r\n}\r\n\r\n/**\r\n * Load collections from a config file\r\n */\r\nexport async function loadCollections(configPath: string): Promise<Record<string, Collection>> {\r\n return loadCollectionsFromConfig(configPath, false)\r\n}\r\n\r\n/**\r\n * Extract collections from Next.js config\r\n */\r\nexport function getCollectionsFromConfig(nextConfig: WithCollectionsConfig): Record<string, Collection> {\r\n return nextConfig.collections?.collections ?? {}\r\n}\r\n\r\n/**\r\n * Type guard to check if a value is a valid Next.js config\r\n */\r\nexport function isNextConfig(config: unknown): config is NextConfig {\r\n if (!config || typeof config !== 'object') return false\r\n const cfg = config as Record<string, unknown>\r\n if ('reactStrictMode' in cfg && typeof cfg.reactStrictMode !== 'boolean') return false\r\n if ('swcMinify' in cfg && typeof cfg.swcMinify !== 'boolean') return false\r\n const validProps = ['reactStrictMode', 'swcMinify', 'trailingSlash', 'poweredByHeader', 'compress']\r\n return validProps.some(prop => prop in cfg)\r\n}\r\n\r\n/**\r\n * Type guard to check if withCollections was applied\r\n */\r\nexport function isCollectionsConfig(config: unknown): config is WithCollectionsConfig {\r\n if (!config || typeof config !== 'object') return false\r\n return (config as Record<string, unknown>).collections !== undefined\r\n}\r\n\r\n/**\r\n * Default withCollections options\r\n */\r\nexport const defaultWithCollectionsOptions: Required<WithCollectionsOptions> = {\r\n configPath: './collections/config',\r\n hotReload: true,\r\n outputDir: './drizzle',\r\n debug: false\r\n}\r\n","import { z } from 'zod'\r\n\r\n/**\r\n * A field type instance (already configured)\r\n */\r\nexport type FieldTypeInstance = {\r\n schema: z.ZodType\r\n database: unknown\r\n}\r\n\r\n/**\r\n * A field type creator (needs to be called to get instance)\r\n */\r\nexport type FieldTypeCreator = () => FieldTypeInstance\r\n\r\n/**\r\n * Field type configuration\r\n */\r\nexport type FieldTypeConfig = {\r\n schema: z.ZodType\r\n database?: unknown\r\n}\r\n\r\n/**\r\n * Creates a new field type\r\n *\r\n * @example\r\n * const text = fieldType({\r\n * schema: z.string(),\r\n * database: { type: 'text' }\r\n * })\r\n *\r\n * const textField = text() // Get instance\r\n */\r\nexport const fieldType = (config: FieldTypeConfig): (() => FieldTypeInstance) => {\r\n return () => ({\r\n schema: config.schema,\r\n database: config.database ?? {}\r\n })\r\n}\r\n","import type { FieldTypeInstance } from './field-type'\r\n\r\n/**\r\n * Field configuration options\r\n */\r\nexport type FieldOptions = {\r\n fieldType: FieldTypeInstance\r\n required?: boolean\r\n unique?: boolean\r\n indexed?: boolean\r\n default?: unknown\r\n label?: string\r\n description?: string\r\n}\r\n\r\n/**\r\n * Creates a field definition\r\n *\r\n * @example\r\n * name: field({ fieldType: text() })\r\n * email: field({ fieldType: email(), unique: true })\r\n */\r\nexport const field = (config: FieldOptions): FieldDefinition => {\r\n return {\r\n fieldType: config.fieldType,\r\n required: config.required ?? false,\r\n unique: config.unique ?? false,\r\n indexed: config.indexed ?? false,\r\n default: config.default,\r\n label: config.label,\r\n description: config.description\r\n }\r\n}\r\n\r\n/**\r\n * A field definition\r\n */\r\nexport type FieldDefinition = {\r\n fieldType: FieldTypeInstance\r\n required: boolean\r\n unique: boolean\r\n indexed: boolean\r\n default?: unknown\r\n label?: string\r\n description?: string\r\n}\r\n","import { fieldType, type FieldTypeInstance } from '../field-type'\r\nimport { z } from 'zod'\r\n\r\n/**\r\n * Derive the database type string from a Zod schema\r\n */\r\nconst getItemType = (itemSchema: z.ZodType): string => {\r\n if (itemSchema instanceof z.ZodString) return 'text'\r\n if (itemSchema instanceof z.ZodNumber) return 'integer'\r\n if (itemSchema instanceof z.ZodBoolean) return 'boolean'\r\n if (itemSchema instanceof z.ZodDate) return 'timestamp'\r\n if (itemSchema instanceof z.ZodEnum) return 'text'\r\n if (itemSchema instanceof z.ZodArray) return 'array'\r\n if (itemSchema instanceof z.ZodObject) return 'jsonb'\r\n return 'text'\r\n}\r\n\r\n/**\r\n * Field types namespace (like zod's z)\r\n */\r\nexport const f = {\r\n /**\r\n * Text field type\r\n */\r\n text: (): FieldTypeInstance => fieldType({\r\n schema: z.string(),\r\n database: { type: 'text' }\r\n })(),\r\n\r\n /**\r\n * Email field type with built-in validation\r\n */\r\n email: (): FieldTypeInstance => fieldType({\r\n schema: z.string().email(),\r\n database: { type: 'text' }\r\n })(),\r\n\r\n /**\r\n * URL field type with built-in validation\r\n */\r\n url: (): FieldTypeInstance => fieldType({\r\n schema: z.string().url(),\r\n database: { type: 'text' }\r\n })(),\r\n\r\n /**\r\n * Number field type\r\n */\r\n number: (): FieldTypeInstance => fieldType({\r\n schema: z.number(),\r\n database: { type: 'integer' }\r\n })(),\r\n\r\n /**\r\n * Boolean field type\r\n */\r\n boolean: (): FieldTypeInstance => fieldType({\r\n schema: z.boolean(),\r\n database: { type: 'boolean' }\r\n })(),\r\n\r\n /**\r\n * Date field type (date only, no time)\r\n */\r\n date: (): FieldTypeInstance => fieldType({\r\n schema: z.date(),\r\n database: { type: 'date' }\r\n })(),\r\n\r\n /**\r\n * Timestamp field type (date with time)\r\n */\r\n timestamp: (): FieldTypeInstance => fieldType({\r\n schema: z.date(),\r\n database: { type: 'timestamp' }\r\n })(),\r\n\r\n /**\r\n * Creates a select field type\r\n */\r\n select: <T extends readonly [string, ...string[]]>(\r\n options: T\r\n ): FieldTypeInstance => fieldType({\r\n schema: z.enum(options),\r\n database: { type: 'text' }\r\n })(),\r\n\r\n /**\r\n * JSON field type for storing JSON data\r\n */\r\n json: (schema?: z.ZodType): FieldTypeInstance => fieldType({\r\n schema: schema ?? z.any(),\r\n database: { type: 'jsonb' }\r\n })(),\r\n\r\n /**\r\n * Array field type for storing lists\r\n */\r\n array: (itemSchema: z.ZodType): FieldTypeInstance => fieldType({\r\n schema: z.array(itemSchema),\r\n database: { type: 'array', itemType: getItemType(itemSchema) }\r\n })(),\r\n\r\n /**\r\n * Creates a relation field type for foreign key relationships\r\n */\r\n relation: (options: {\r\n collection: string\r\n singular?: boolean\r\n many?: boolean\r\n through?: string\r\n }): FieldTypeInstance => {\r\n const isMany = options.many ?? false\r\n const isSingular = options.singular ?? false\r\n\r\n return fieldType({\r\n schema: isMany ? z.array(z.string()) : z.string(),\r\n database: {\r\n type: 'integer',\r\n references: options.collection,\r\n through: options.through,\r\n many: isMany,\r\n singular: isSingular\r\n }\r\n })()\r\n }\r\n}\r\n\r\n/**\r\n * @deprecated Use f.text() instead\r\n */\r\nexport const text = f.text\r\n\r\n/**\r\n * @deprecated Use f.email() instead\r\n */\r\nexport const email = f.email\r\n\r\n/**\r\n * @deprecated Use f.url() instead\r\n */\r\nexport const url = f.url\r\n\r\n/**\r\n * @deprecated Use f.number() instead\r\n */\r\nexport const number = f.number\r\n\r\n/**\r\n * @deprecated Use f.boolean() instead\r\n */\r\nexport const boolean = f.boolean\r\n\r\n/**\r\n * @deprecated Use f.date() instead\r\n */\r\nexport const date = f.date\r\n\r\n/**\r\n * @deprecated Use f.timestamp() instead\r\n */\r\nexport const timestamp = f.timestamp\r\n\r\n/**\r\n * @deprecated Use f.select() instead\r\n */\r\nexport const select = f.select\r\n\r\n/**\r\n * @deprecated Use f.json() instead\r\n */\r\nexport const json = f.json\r\n\r\n/**\r\n * @deprecated Use f.array() instead\r\n */\r\nexport const array = f.array\r\n\r\n/**\r\n * @deprecated Use f.relation() instead\r\n */\r\nexport const relation = f.relation\r\n\r\n/**\r\n * @deprecated Use f.relation instead\r\n */\r\nexport type RelationOptions = {\r\n collection: string\r\n singular?: boolean\r\n many?: boolean\r\n through?: string\r\n}\r\n","import type { FieldDefinition } from './field'\r\n\r\n/**\r\n * Collection configuration\r\n */\r\nexport type CollectionConfig<T extends Record<string, unknown> = Record<string, unknown>> = {\r\n slug: string\r\n name?: string\r\n fields: Record<string, FieldDefinition>\r\n hooks?: CollectionHooks\r\n dataType?: T\r\n}\r\n\r\n/**\r\n * Operation types\r\n */\r\nexport type OperationType = 'create' | 'update' | 'delete' | 'read'\r\n\r\n/**\r\n * Hook context base\r\n */\r\nexport type HookContextBase = {\r\n collection: string\r\n operation: OperationType\r\n}\r\n\r\n/**\r\n * Before/After Operation context\r\n */\r\nexport type OperationHookContext = HookContextBase & {\r\n data?: Record<string, unknown>\r\n where?: Record<string, unknown>\r\n result?: unknown\r\n}\r\n\r\n/**\r\n * Before/After Create context\r\n */\r\nexport type CreateHookContext = HookContextBase & {\r\n operation: 'create'\r\n data: Record<string, unknown>\r\n result?: unknown\r\n db?: unknown\r\n}\r\n\r\n/**\r\n * Before/After Update context\r\n */\r\nexport type UpdateHookContext = HookContextBase & {\r\n operation: 'update'\r\n data: Record<string, unknown>\r\n where: Record<string, unknown>\r\n previousData?: Record<string, unknown>\r\n result?: unknown\r\n db?: unknown\r\n}\r\n\r\n/**\r\n * Before/After Delete context\r\n */\r\nexport type DeleteHookContext = HookContextBase & {\r\n operation: 'delete'\r\n where: Record<string, unknown>\r\n previousData?: Record<string, unknown>\r\n result?: unknown\r\n db?: unknown\r\n}\r\n\r\n/**\r\n * Before/After Read context\r\n */\r\nexport type ReadHookContext = HookContextBase & {\r\n operation: 'read'\r\n query?: Record<string, unknown>\r\n result?: unknown[]\r\n db?: unknown\r\n}\r\n\r\n/**\r\n * Generic hook function type\r\n */\r\nexport type GenericHookFunction = (context: OperationHookContext) => Promise<void> | void\r\nexport type CreateHookFunction = (context: CreateHookContext) => Promise<void> | void\r\nexport type UpdateHookFunction = (context: UpdateHookContext) => Promise<void> | void\r\nexport type DeleteHookFunction = (context: DeleteHookContext) => Promise<void> | void\r\nexport type ReadHookFunction = (context: ReadHookContext) => Promise<void> | void\r\n\r\n/**\r\n * Collection hooks\r\n */\r\nexport type CollectionHooks = {\r\n beforeOperation?: GenericHookFunction[]\r\n afterOperation?: GenericHookFunction[]\r\n beforeCreate?: CreateHookFunction[]\r\n afterCreate?: CreateHookFunction[]\r\n beforeUpdate?: UpdateHookFunction[]\r\n afterUpdate?: UpdateHookFunction[]\r\n beforeDelete?: DeleteHookFunction[]\r\n afterDelete?: DeleteHookFunction[]\r\n beforeRead?: ReadHookFunction[]\r\n afterRead?: ReadHookFunction[]\r\n}\r\n\r\n/**\r\n * A collection definition\r\n */\r\nexport type Collection<T extends Record<string, unknown> = Record<string, unknown>> = {\r\n slug: string\r\n name?: string\r\n fields: Record<string, FieldDefinition>\r\n hooks?: CollectionHooks\r\n dataType?: T\r\n}\r\n\r\n/**\r\n * Creates a new collection\r\n *\r\n * @example\r\n * export const users = collection({\r\n * slug: 'users',\r\n * name: 'Users',\r\n * fields: {\r\n * name: field({ fieldType: text }),\r\n * email: field({ fieldType: email, unique: true })\r\n * }\r\n * })\r\n */\r\nexport const collection = <T extends Record<string, unknown> = Record<string, unknown>>(\r\n config: CollectionConfig<T>\r\n): Collection<T> => {\r\n return {\r\n slug: config.slug,\r\n name: config.name,\r\n fields: config.fields,\r\n hooks: config.hooks,\r\n dataType: config.dataType\r\n }\r\n}\r\n","/* eslint-disable @typescript-eslint/no-explicit-any */\r\nimport { eq, and, like, gt, gte, lt, lte, isNull, inArray, not, desc, asc } from 'drizzle-orm'\r\n\r\nimport type { Collection, CollectionHooks, CreateHookContext, UpdateHookContext, DeleteHookContext, ReadHookContext, OperationHookContext } from '../collection'\r\nimport type {\r\n FindManyOptions,\r\n FindUniqueOptions,\r\n FindFirstOptions,\r\n CreateOptions,\r\n CreateManyOptions,\r\n UpdateOptions,\r\n UpdateManyOptions,\r\n DeleteOptions,\r\n DeleteManyOptions,\r\n CountOptions,\r\n ExistsOptions,\r\n WhereOperator\r\n} from './types'\r\n\r\n/**\r\n * Collection operations interface\r\n */\r\nexport interface CollectionOperations {\r\n findMany<T>(options?: FindManyOptions): Promise<T[]>\r\n findUnique<T>(options: FindUniqueOptions): Promise<T | undefined>\r\n findFirst<T>(options: FindFirstOptions): Promise<T | undefined>\r\n create<T>(options: CreateOptions<T>): Promise<T | undefined>\r\n createMany<T>(options: CreateManyOptions<T>): Promise<number>\r\n update<T>(options: UpdateOptions<T>): Promise<T | undefined>\r\n updateMany<T>(options: UpdateManyOptions<T>): Promise<number>\r\n delete<T>(options: DeleteOptions): Promise<T | undefined>\r\n deleteMany(options: DeleteManyOptions): Promise<number>\r\n count(options?: CountOptions): Promise<number>\r\n exists(options: ExistsOptions): Promise<boolean>\r\n}\r\n\r\n/**\r\n * Build where conditions from options\r\n */\r\nconst buildWhereClause = (\r\n tableColumns: Record<string, any>,\r\n where?: Record<string, unknown>\r\n): any => {\r\n if (!where) return undefined\r\n\r\n const conditions: any[] = []\r\n\r\n for (const [key, value] of Object.entries(where)) {\r\n const column = tableColumns[key]\r\n if (!column) continue\r\n\r\n if (value === null || typeof value !== 'object') {\r\n conditions.push(eq(column, value))\r\n } else {\r\n const operator = value as WhereOperator<unknown>\r\n if ('eq' in operator) {\r\n conditions.push(eq(column, operator.eq))\r\n } else if ('neq' in operator) {\r\n conditions.push(not(eq(column, operator.neq)))\r\n } else if ('gt' in operator) {\r\n conditions.push(gt(column, operator.gt as number))\r\n } else if ('gte' in operator) {\r\n conditions.push(gte(column, operator.gte as number))\r\n } else if ('lt' in operator) {\r\n conditions.push(lt(column, operator.lt as number))\r\n } else if ('lte' in operator) {\r\n conditions.push(lte(column, operator.lte as number))\r\n } else if ('in' in operator) {\r\n conditions.push(inArray(column, operator.in))\r\n } else if ('notIn' in operator) {\r\n conditions.push(not(inArray(column, operator.notIn)))\r\n } else if ('contains' in operator) {\r\n conditions.push(like(column, `%${operator.contains}%`))\r\n } else if ('startsWith' in operator) {\r\n conditions.push(like(column, `${operator.startsWith}%`))\r\n } else if ('endsWith' in operator) {\r\n conditions.push(like(column, `%${operator.endsWith}`))\r\n } else if ('isNull' in operator) {\r\n if (operator.isNull) {\r\n conditions.push(isNull(column))\r\n }\r\n } else if ('not' in operator) {\r\n conditions.push(not(eq(column, operator.not)))\r\n }\r\n }\r\n }\r\n\r\n if (conditions.length === 0) return undefined\r\n if (conditions.length === 1) return conditions[0]\r\n return and(...conditions)\r\n}\r\n\r\n/**\r\n * Build orderBy from options\r\n */\r\nconst buildOrderBy = (\r\n tableColumns: Record<string, any>,\r\n orderBy?: Record<string, unknown> | Record<string, unknown>[]\r\n): any[] => {\r\n if (!orderBy) return []\r\n\r\n const orders = Array.isArray(orderBy) ? orderBy : [orderBy]\r\n return orders.map((order) => {\r\n for (const [key, direction] of Object.entries(order)) {\r\n const column = tableColumns[key]\r\n if (!column) continue\r\n return direction === 'desc' ? desc(column) : asc(column)\r\n }\r\n return undefined\r\n }).filter(Boolean)\r\n}\r\n\r\n/**\r\n * Execute before operation hooks\r\n */\r\nconst executeBeforeOperationHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: OperationHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeOperation) return\r\n for (const hook of hooks.beforeOperation) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after operation hooks\r\n */\r\nconst executeAfterOperationHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: OperationHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterOperation) return\r\n for (const hook of hooks.afterOperation) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute before create hooks\r\n */\r\nconst executeBeforeCreateHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: CreateHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeCreate) return\r\n for (const hook of hooks.beforeCreate) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after create hooks\r\n */\r\nconst executeAfterCreateHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: CreateHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterCreate) return\r\n for (const hook of hooks.afterCreate) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute before update hooks\r\n */\r\nconst executeBeforeUpdateHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: UpdateHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeUpdate) return\r\n for (const hook of hooks.beforeUpdate) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after update hooks\r\n */\r\nconst executeAfterUpdateHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: UpdateHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterUpdate) return\r\n for (const hook of hooks.afterUpdate) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute before delete hooks\r\n */\r\nconst executeBeforeDeleteHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: DeleteHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeDelete) return\r\n for (const hook of hooks.beforeDelete) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after delete hooks\r\n */\r\nconst executeAfterDeleteHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: DeleteHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterDelete) return\r\n for (const hook of hooks.afterDelete) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute before read hooks\r\n */\r\nconst executeBeforeReadHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: ReadHookContext\r\n): Promise<void> => {\r\n if (!hooks?.beforeRead) return\r\n for (const hook of hooks.beforeRead) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Execute after read hooks\r\n */\r\nconst executeAfterReadHooks = async (\r\n hooks: CollectionHooks | undefined,\r\n context: ReadHookContext\r\n): Promise<void> => {\r\n if (!hooks?.afterRead) return\r\n for (const hook of hooks.afterRead) {\r\n await hook(context)\r\n }\r\n}\r\n\r\n/**\r\n * Creates collection operations with Drizzle\r\n */\r\nexport const createCollectionOperations = (\r\n _collection: Collection,\r\n _slug: string,\r\n _db: any,\r\n _table: any,\r\n _hooks?: CollectionHooks\r\n): CollectionOperations => {\r\n const tableColumns = _table as Record<string, any>\r\n const db = _db as any\r\n const hooks = _hooks as CollectionHooks | undefined\r\n\r\n // If no db instance, return placeholder operations\r\n if (!db) {\r\n return {\r\n findMany: async <T>(): Promise<T[]> => [],\r\n findUnique: async <T>(): Promise<T | undefined> => undefined,\r\n findFirst: async <T>(): Promise<T | undefined> => undefined,\r\n create: async <T>(): Promise<T | undefined> => undefined,\r\n createMany: async (): Promise<number> => 0,\r\n update: async <T>(): Promise<T | undefined> => undefined,\r\n updateMany: async (): Promise<number> => 0,\r\n delete: async <T>(): Promise<T | undefined> => undefined,\r\n deleteMany: async (): Promise<number> => 0,\r\n count: async (): Promise<number> => 0,\r\n exists: async (): Promise<boolean> => false\r\n }\r\n }\r\n\r\n return {\r\n findMany: async <T>(options?: FindManyOptions): Promise<T[]> => {\r\n const whereClause = buildWhereClause(tableColumns, options?.where)\r\n const orderByClause = buildOrderBy(tableColumns, options?.orderBy)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options?.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n let query = db.select().from(_table)\r\n\r\n if (whereClause) {\r\n query = query.where(whereClause)\r\n }\r\n\r\n if (orderByClause.length > 0) {\r\n query = query.orderBy(...orderByClause)\r\n }\r\n\r\n if (options?.offset) {\r\n query = query.offset(options.offset)\r\n }\r\n\r\n if (options?.limit) {\r\n query = query.limit(options.limit)\r\n }\r\n\r\n const result = await query\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result: result as unknown[],\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options?.where,\r\n result\r\n })\r\n\r\n return result as T[]\r\n },\r\n\r\n findUnique: async <T>(options: FindUniqueOptions): Promise<T | undefined> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return undefined\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n const result = await db.select().from(_table).where(whereClause).limit(1)\r\n const returnValue = result[0] as T | undefined\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result: returnValue ? [returnValue] : [],\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n findFirst: async <T>(options: FindFirstOptions): Promise<T | undefined> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n const orderByClause = buildOrderBy(tableColumns, options.orderBy)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n let query = db.select().from(_table)\r\n\r\n if (whereClause) {\r\n query = query.where(whereClause)\r\n }\r\n\r\n if (orderByClause.length > 0) {\r\n query = query.orderBy(...orderByClause)\r\n }\r\n\r\n const result = await query.limit(1)\r\n const returnValue = result[0] as T | undefined\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result: returnValue ? [returnValue] : [],\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n create: async <T>(options: CreateOptions<T>): Promise<T | undefined> => {\r\n const data = Array.isArray(options.data) ? options.data : [options.data]\r\n const firstData = data[0] as Record<string, unknown>\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: firstData,\r\n where: undefined\r\n })\r\n\r\n // Execute before create hooks\r\n await executeBeforeCreateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: firstData,\r\n db\r\n })\r\n\r\n const result = await db.insert(_table).values(data).returning()\r\n\r\n const returnValue = options.returning ? result[0] as T : undefined\r\n\r\n // Execute after create hooks\r\n await executeAfterCreateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: firstData,\r\n result: returnValue,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: firstData,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n createMany: async <T>(options: CreateManyOptions<T>): Promise<number> => {\r\n const dataArray = Array.isArray(options.data) ? options.data : [options.data]\r\n\r\n // Execute before operation hooks for each item\r\n for (const data of dataArray) {\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: data as Record<string, unknown>,\r\n where: undefined\r\n })\r\n\r\n await executeBeforeCreateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: data as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n const result = await db.insert(_table).values(options.data as any)\r\n\r\n // Execute after operation hooks for each item\r\n for (let i = 0; i < dataArray.length; i++) {\r\n await executeAfterCreateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: dataArray[i] as Record<string, unknown>,\r\n result: result[i],\r\n db\r\n })\r\n\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'create',\r\n data: dataArray[i] as Record<string, unknown>,\r\n result: result[i]\r\n })\r\n }\r\n\r\n return result.length || 0\r\n },\r\n\r\n update: async <T>(options: UpdateOptions<T>): Promise<T | undefined> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return undefined\r\n\r\n // Get previous data for hooks\r\n const previousResult = await db.select().from(_table).where(whereClause).limit(1)\r\n const previousData = previousResult[0] as Record<string, unknown> | undefined\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where\r\n })\r\n\r\n // Execute before update hooks\r\n await executeBeforeUpdateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n previousData,\r\n db\r\n })\r\n\r\n const result = await db.update(_table)\r\n .set(options.data as any)\r\n .where(whereClause)\r\n .returning()\r\n\r\n const returnValue = options.returning ? result[0] as T : undefined\r\n\r\n // Execute after update hooks\r\n await executeAfterUpdateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n previousData,\r\n result: returnValue,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n updateMany: async <T>(options: UpdateManyOptions<T>): Promise<number> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return 0\r\n\r\n // Get previous data for hooks\r\n const previousResults = await db.select().from(_table).where(whereClause)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where\r\n })\r\n\r\n // Execute before update hooks (for each previous record)\r\n for (const previousData of previousResults) {\r\n await executeBeforeUpdateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n previousData: previousData as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n const result = await db.update(_table)\r\n .set(options.data as any)\r\n .where(whereClause)\r\n\r\n // Execute after update hooks\r\n for (const previousData of previousResults) {\r\n await executeAfterUpdateHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where,\r\n previousData: previousData as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'update',\r\n data: options.data as Record<string, unknown>,\r\n where: options.where\r\n })\r\n\r\n return result.length || 0\r\n },\r\n\r\n delete: async <T>(options: DeleteOptions): Promise<T | undefined> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return undefined\r\n\r\n // Get previous data for hooks\r\n const previousResult = await db.select().from(_table).where(whereClause).limit(1)\r\n const previousData = previousResult[0] as Record<string, unknown> | undefined\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where\r\n })\r\n\r\n // Execute before delete hooks\r\n await executeBeforeDeleteHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n previousData,\r\n db\r\n })\r\n\r\n const result = await db.delete(_table)\r\n .where(whereClause)\r\n .returning()\r\n\r\n const returnValue = options.returning ? result[0] as T : undefined\r\n\r\n // Execute after delete hooks\r\n await executeAfterDeleteHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n previousData,\r\n result: returnValue,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n },\r\n\r\n deleteMany: async (options: DeleteManyOptions): Promise<number> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return 0\r\n\r\n // Get previous data for hooks\r\n const previousResults = await db.select().from(_table).where(whereClause)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where\r\n })\r\n\r\n // Execute before delete hooks\r\n for (const previousData of previousResults) {\r\n await executeBeforeDeleteHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n previousData: previousData as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n const result = await db.delete(_table).where(whereClause)\r\n\r\n // Execute after delete hooks\r\n for (const previousData of previousResults) {\r\n await executeAfterDeleteHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where,\r\n previousData: previousData as Record<string, unknown>,\r\n db\r\n })\r\n }\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'delete',\r\n where: options.where\r\n })\r\n\r\n return result.length || 0\r\n },\r\n\r\n count: async (options?: CountOptions): Promise<number> => {\r\n const whereClause = buildWhereClause(tableColumns, options?.where)\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options?.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n const result = whereClause\r\n ? await db.select().from(_table).where(whereClause)\r\n : await db.select().from(_table)\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options?.where,\r\n result\r\n })\r\n\r\n return result.length\r\n },\r\n\r\n exists: async (options: ExistsOptions): Promise<boolean> => {\r\n const whereClause = buildWhereClause(tableColumns, options.where)\r\n if (!whereClause) return false\r\n\r\n // Execute before operation hooks\r\n await executeBeforeOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where\r\n })\r\n\r\n // Execute before read hooks\r\n await executeBeforeReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n db\r\n })\r\n\r\n const result = await db.select().from(_table).where(whereClause).limit(1)\r\n const returnValue = result.length > 0\r\n\r\n // Execute after read hooks\r\n await executeAfterReadHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n query: options as unknown as Record<string, unknown>,\r\n result: result,\r\n db\r\n })\r\n\r\n // Execute after operation hooks\r\n await executeAfterOperationHooks(hooks, {\r\n collection: _slug,\r\n operation: 'read',\r\n where: options.where,\r\n result: returnValue\r\n })\r\n\r\n return returnValue\r\n }\r\n }\r\n}\r\n","/**\r\n * PostgreSQL adapter configuration\r\n */\r\nexport type PgAdapterConfig = {\r\n url: string\r\n migrationsPath?: string\r\n}\r\n\r\n/**\r\n * PostgreSQL adapter\r\n */\r\nexport interface PgAdapter {\r\n type: 'postgres'\r\n config: PgAdapterConfig\r\n}\r\n\r\n/**\r\n * Database adapter type\r\n */\r\nexport type DatabaseAdapter = PgAdapter\r\n\r\n/**\r\n * Creates a PostgreSQL adapter\r\n *\r\n * @example\r\n * const adapter = pgAdapter({\r\n * url: 'postgres://user:pass@localhost:5432/db'\r\n * })\r\n */\r\nexport const pgAdapter = (config: PgAdapterConfig): PgAdapter => {\r\n return {\r\n type: 'postgres',\r\n config: {\r\n url: config.url,\r\n migrationsPath: config.migrationsPath ?? './migrations'\r\n }\r\n }\r\n}\r\n","import { pgTable, serial, text, timestamp, uuid, varchar, boolean, integer } from 'drizzle-orm/pg-core'\r\n\r\nimport type { Collection } from './collection'\r\n\r\n/**\r\n * Build Drizzle table from collection definition\r\n */\r\nexport const buildTable = (collection: Collection) => {\r\n // Build columns object\r\n const columns: Record<string, unknown> = {\r\n // Add default id column\r\n id: serial('id').primaryKey()\r\n }\r\n\r\n // Build columns from fields\r\n for (const [fieldName, fieldDef] of Object.entries(collection.fields)) {\r\n // Skip the id field if explicitly defined in fields\r\n if (fieldName === 'id') continue\r\n\r\n const fieldType = fieldDef.fieldType as { name?: string; type?: string }\r\n const fieldTypeName = fieldType.name || fieldType.type || 'text'\r\n\r\n switch (fieldTypeName) {\r\n case 'text':\r\n columns[fieldName] = text(fieldName)\r\n break\r\n case 'varchar':\r\n columns[fieldName] = varchar(fieldName, { length: 255 })\r\n break\r\n case 'number':\r\n case 'integer':\r\n columns[fieldName] = integer(fieldName)\r\n break\r\n case 'boolean':\r\n columns[fieldName] = boolean(fieldName)\r\n break\r\n case 'timestamp':\r\n columns[fieldName] = timestamp(fieldName)\r\n break\r\n case 'uuid':\r\n columns[fieldName] = uuid(fieldName)\r\n break\r\n default:\r\n columns[fieldName] = text(fieldName)\r\n }\r\n }\r\n\r\n return pgTable(collection.slug, columns as Record<string, ReturnType<typeof text>>)\r\n}\r\n\r\n/**\r\n * Build all tables from collections\r\n */\r\nexport const buildSchema = (collections: Collection[]) => {\r\n const tables: Record<string, ReturnType<typeof pgTable>> = {}\r\n\r\n for (const coll of collections) {\r\n tables[coll.slug] = buildTable(coll)\r\n }\r\n\r\n return tables\r\n}\r\n","import { Pool, type Pool as PoolType } from 'pg'\r\nimport { drizzle } from 'drizzle-orm/node-postgres'\r\n\r\nimport type { Collection } from './collection'\r\nimport type { DatabaseAdapter } from './adapter'\r\nimport { buildSchema } from './schema'\r\n\r\n/**\r\n * Plugin interface\r\n */\r\nexport type Plugin = {\r\n name: string\r\n collections?: Record<string, Collection>\r\n hooks?: Record<string, unknown[]>\r\n}\r\n\r\n/**\r\n * Configuration options\r\n */\r\nexport type ConfigOptions<T extends Collection[] = []> = {\r\n database: DatabaseAdapter\r\n collections: T\r\n plugins?: Plugin[]\r\n}\r\n\r\n/**\r\n * Define config return type with inferred collection keys\r\n *\r\n * - collections: metadata only (slug, name, fields, hooks, dataType)\r\n * - db: Drizzle instance with operations (via schema tables)\r\n * - $meta: array of collection slugs and plugin names\r\n */\r\nexport type DefineConfigReturn<T extends Collection[] = []> = {\r\n collections: {\r\n [K in T[number] as K['slug']]: Collection\r\n }\r\n db: ReturnType<typeof drizzle<Record<string, unknown>>>\r\n $meta: {\r\n collections: T[number]['slug'][]\r\n plugins: string[]\r\n }\r\n}\r\n\r\n/**\r\n * Creates the configuration for the data layer\r\n *\r\n * @example\r\n * const adapter = pgAdapter({\r\n * url: process.env.DATABASE_URL!\r\n * })\r\n *\r\n * export const { collections, db } = defineConfig({\r\n * database: adapter,\r\n * collections: [users, posts],\r\n * plugins: [timestampsPlugin()]\r\n * })\r\n *\r\n * // collections: metadata only\r\n * collections.users.slug // 'users'\r\n * collections.users.fields // { name, email, ... }\r\n *\r\n * // db: Drizzle instance with operations\r\n * await db.users.findMany()\r\n * await db.users.insert(values)\r\n */\r\nexport const defineConfig = <T extends Collection[]>(\r\n options: ConfigOptions<T>\r\n): DefineConfigReturn<T> => {\r\n // Initialize the database connection based on adapter type\r\n let pool: PoolType | null = null\r\n let dbInstance: ReturnType<typeof drizzle<Record<string, unknown>>> | null = null\r\n\r\n let schema: Record<string, unknown> = {}\r\n\r\n if (options.database.type === 'postgres') {\r\n // Create pool from adapter config\r\n pool = new Pool({\r\n connectionString: options.database.config.url\r\n })\r\n\r\n // Build schema from collections\r\n schema = buildSchema(options.collections as Collection[])\r\n\r\n // Create Drizzle instance with schema\r\n dbInstance = drizzle(pool, { schema })\r\n }\r\n\r\n // Build collections map (metadata only)\r\n const collectionsMap: Record<string, Collection> = {}\r\n const collectionNames: string[] = []\r\n\r\n for (const coll of options.collections) {\r\n // Store only metadata (not operations)\r\n collectionsMap[coll.slug] = {\r\n slug: coll.slug,\r\n name: coll.name,\r\n fields: coll.fields,\r\n hooks: coll.hooks,\r\n dataType: coll.dataType\r\n }\r\n collectionNames.push(coll.slug)\r\n }\r\n\r\n // Build plugins map\r\n const pluginNames: string[] = []\r\n if (options.plugins) {\r\n for (const plugin of options.plugins) {\r\n pluginNames.push(plugin.name)\r\n\r\n // Register plugin collections (metadata only)\r\n if (plugin.collections) {\r\n for (const [name, coll] of Object.entries(plugin.collections)) {\r\n collectionsMap[name] = {\r\n slug: coll.slug,\r\n name: coll.name,\r\n fields: coll.fields,\r\n hooks: coll.hooks,\r\n dataType: coll.dataType\r\n }\r\n collectionNames.push(name)\r\n }\r\n }\r\n }\r\n }\r\n\r\n return {\r\n collections: collectionsMap as DefineConfigReturn<T>['collections'],\r\n db: dbInstance as DefineConfigReturn<T>['db'],\r\n $meta: {\r\n collections: collectionNames as DefineConfigReturn<T>['$meta']['collections'],\r\n plugins: pluginNames\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["text","timestamp","boolean","collection","fieldType"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deessejs/collections",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "High-level abstraction layer built on top of Drizzle ORM",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"files": [
|
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
"types": "./dist/plugins/index.d.ts",
|
|
27
27
|
"import": "./dist/plugins/index.mjs",
|
|
28
28
|
"require": "./dist/plugins/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./next": {
|
|
31
|
+
"types": "./dist/next/index.d.ts",
|
|
32
|
+
"import": "./dist/next/index.mjs",
|
|
33
|
+
"require": "./dist/next/index.js"
|
|
29
34
|
}
|
|
30
35
|
},
|
|
31
36
|
"scripts": {
|
|
@@ -49,6 +54,9 @@
|
|
|
49
54
|
"@neondatabase/serverless": "^1.0.2",
|
|
50
55
|
"@types/node": "^20.14.12",
|
|
51
56
|
"@types/pg": "^8.11.0",
|
|
57
|
+
"@types/react": "^18.3.0",
|
|
58
|
+
"next": "^14.2.0",
|
|
59
|
+
"react": "^18.3.0",
|
|
52
60
|
"eslint": "^8.57.0",
|
|
53
61
|
"neon-serverless": "^0.5.3",
|
|
54
62
|
"tsup": "^8.2.2",
|
|
@@ -57,6 +65,8 @@
|
|
|
57
65
|
"vitest": "^2.0.5"
|
|
58
66
|
},
|
|
59
67
|
"peerDependencies": {
|
|
68
|
+
"next": ">=14",
|
|
69
|
+
"react": ">=18",
|
|
60
70
|
"typescript": ">=5"
|
|
61
71
|
},
|
|
62
72
|
"publishConfig": {
|