prisma-flare 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/core/extendedPrismaClient.ts
2
- import { PrismaClient } from "@prisma/client";
2
+ import { PrismaClient as PrismaClient2 } from "@prisma/client";
3
3
 
4
4
  // src/core/modelRegistry.ts
5
5
  var ModelRegistry = class {
@@ -657,53 +657,6 @@ var FlareBuilder = class _FlareBuilder {
657
657
  }
658
658
  };
659
659
 
660
- // src/core/extendedPrismaClient.ts
661
- var FlareClient = class extends PrismaClient {
662
- constructor(options = {}) {
663
- super(options);
664
- }
665
- /**
666
- * Creates a new FlareBuilder instance for the specified model.
667
- * @param modelName - The name of the model.
668
- * @returns FlareBuilder instance
669
- */
670
- from(modelName) {
671
- const key = modelName.charAt(0).toLowerCase() + modelName.slice(1);
672
- const model = this[key];
673
- if (!model) {
674
- throw new Error(`Model ${modelName} does not exist on PrismaClient.`);
675
- }
676
- return new FlareBuilder(model);
677
- }
678
- /**
679
- * Executes a transaction with the FlareClient capabilities.
680
- * @param fn - The transaction function.
681
- * @param options - Transaction options.
682
- * @returns The result of the transaction.
683
- */
684
- async transaction(fn, options) {
685
- return super.$transaction(async (tx) => {
686
- const extendedTx = new Proxy(tx, {
687
- get: (target, prop, receiver) => {
688
- if (prop === "from") {
689
- return (modelName) => {
690
- const key = modelName.charAt(0).toLowerCase() + modelName.slice(1);
691
- const model = target[key];
692
- if (!model) {
693
- throw new Error(`Model ${modelName} does not exist on TransactionClient.`);
694
- }
695
- return new FlareBuilder(model);
696
- };
697
- }
698
- return Reflect.get(target, prop, receiver);
699
- }
700
- });
701
- return fn(extendedTx);
702
- }, options);
703
- }
704
- };
705
- var ExtendedPrismaClient = FlareClient;
706
-
707
660
  // src/core/hookRegistry.ts
708
661
  function valuesEqual(a, b) {
709
662
  if (a == null && b == null) return true;
@@ -875,76 +828,10 @@ var HookRegistry = class {
875
828
  var hookRegistry = new HookRegistry();
876
829
  var hookRegistry_default = hookRegistry;
877
830
 
878
- // src/core/hooks.ts
879
- function normalizeModelName(model) {
880
- return model.toLowerCase();
881
- }
882
- function beforeCreate(model, callback) {
883
- hookRegistry_default.addHook(normalizeModelName(model), "create", "before", callback);
884
- }
885
- function beforeDelete(model, callback) {
886
- hookRegistry_default.addHook(normalizeModelName(model), "delete", "before", callback);
887
- }
888
- function afterCreate(model, callback) {
889
- hookRegistry_default.addHook(normalizeModelName(model), "create", "after", callback);
890
- }
891
- function afterDelete(model, callback) {
892
- hookRegistry_default.addHook(normalizeModelName(model), "delete", "after", callback);
893
- }
894
- function beforeUpdate(model, callback) {
895
- hookRegistry_default.addHook(normalizeModelName(model), "update", "before", callback);
896
- }
897
- function afterUpdate(model, callback) {
898
- hookRegistry_default.addHook(normalizeModelName(model), "update", "after", callback);
899
- }
900
- function afterChange(model, column, callback) {
901
- hookRegistry_default.addColumnHook(normalizeModelName(model), column, callback);
902
- }
903
- function afterUpsert(model, callback) {
904
- hookRegistry_default.addHook(normalizeModelName(model), "upsert", "after", callback);
905
- }
906
-
907
831
  // src/core/hookMiddleware.ts
908
832
  import { Prisma } from "@prisma/client";
909
-
910
- // src/cli/config.ts
911
- import * as fs from "fs";
912
- import * as path from "path";
913
- function findProjectRoot(currentDir) {
914
- if (fs.existsSync(path.join(currentDir, "package.json"))) {
915
- return currentDir;
916
- }
917
- const parentDir = path.dirname(currentDir);
918
- if (parentDir === currentDir) {
919
- throw new Error("Could not find package.json");
920
- }
921
- return findProjectRoot(parentDir);
922
- }
923
- function loadConfig(rootDir) {
924
- const projectRoot = rootDir || findProjectRoot(process.cwd());
925
- const configPath = path.join(projectRoot, "prisma-flare.config.json");
926
- let config = {
927
- modelsPath: "prisma/models",
928
- dbPath: "prisma/db",
929
- callbacksPath: "prisma/callbacks"
930
- };
931
- if (fs.existsSync(configPath)) {
932
- try {
933
- const configFile = fs.readFileSync(configPath, "utf-8");
934
- const userConfig = JSON.parse(configFile);
935
- config = { ...config, ...userConfig };
936
- } catch {
937
- console.warn("\u26A0\uFE0F Could not read prisma-flare.config.json, using defaults.");
938
- }
939
- }
940
- return {
941
- ...config
942
- };
943
- }
944
-
945
- // src/core/hookMiddleware.ts
946
- import fs2 from "fs";
947
- import path2 from "path";
833
+ import fs from "fs";
834
+ import path from "path";
948
835
  function supportsTypeScriptImports() {
949
836
  if (process.env.TS_NODE || /* @__PURE__ */ Symbol.for("ts-node.register.instance") in process) {
950
837
  return true;
@@ -962,16 +849,16 @@ function supportsTypeScriptImports() {
962
849
  }
963
850
  async function loadCallbacks(callbacksDir) {
964
851
  if (!callbacksDir) {
965
- callbacksDir = path2.join(process.cwd(), "prisma", "callbacks");
852
+ callbacksDir = path.join(process.cwd(), "prisma", "callbacks");
966
853
  }
967
- if (!fs2.existsSync(callbacksDir)) {
854
+ if (!fs.existsSync(callbacksDir)) {
968
855
  console.warn(`Callbacks directory not found: ${callbacksDir}`);
969
856
  return;
970
857
  }
971
858
  const canImportTs = supportsTypeScriptImports();
972
- const files = fs2.readdirSync(callbacksDir);
859
+ const files = fs.readdirSync(callbacksDir);
973
860
  for (const file of files) {
974
- const filePath = path2.join(callbacksDir, file);
861
+ const filePath = path.join(callbacksDir, file);
975
862
  if (file.endsWith(".js")) {
976
863
  await import(filePath);
977
864
  } else if (file.endsWith(".ts") && canImportTs) {
@@ -1013,7 +900,7 @@ async function executeHookLogic(prisma, model, action, args, next) {
1013
900
  const isUpdateAction = action === "update" || action === "updateMany";
1014
901
  if (hasColumnHooks && isUpdateAction) {
1015
902
  fields = hookRegistry_default.getRelevantFields(modelName);
1016
- prevData = await fetchAffectedRecords(prisma, modelName, args.where, fields);
903
+ prevData = await fetchAffectedRecords(prisma, model, args.where, fields);
1017
904
  shouldRunColumnHooks = hookRegistry_default.shouldRunColumnHooks(modelName, prevData.length, { __flare: flareOptions });
1018
905
  }
1019
906
  await hookRegistry_default.runHooks("before", modelName, action, [args], prisma);
@@ -1021,7 +908,7 @@ async function executeHookLogic(prisma, model, action, args, next) {
1021
908
  if (shouldRunColumnHooks && prevData.length > 0) {
1022
909
  let newData = [];
1023
910
  const ids = prevData.map((r) => r.id);
1024
- newData = await fetchAffectedRecords(prisma, modelName, { id: { in: ids } }, fields);
911
+ newData = await fetchAffectedRecords(prisma, model, { id: { in: ids } }, fields);
1025
912
  for (let i = 0; i < prevData.length; i++) {
1026
913
  const prevRecord = prevData[i];
1027
914
  const newRecord = newData.find((record) => record.id === prevRecord.id);
@@ -1064,23 +951,102 @@ function registerHooksLegacy(prisma) {
1064
951
  return executeHookLogic(prisma, model, action, args, () => next(params));
1065
952
  });
1066
953
  }
1067
- async function registerHooks(prisma) {
1068
- let client;
954
+ function registerHooks(prisma) {
1069
955
  if (supportsPrisma6Middleware(prisma)) {
1070
956
  registerHooksLegacy(prisma);
1071
- client = prisma;
957
+ return prisma;
1072
958
  } else {
1073
959
  const extension = createHooksExtension(prisma);
1074
- client = prisma.$extends(extension);
960
+ return prisma.$extends(extension);
1075
961
  }
1076
- try {
1077
- const config = loadConfig();
1078
- const projectRoot = findProjectRoot(process.cwd());
1079
- const callbacksPath = path2.join(projectRoot, config.callbacksPath);
1080
- await loadCallbacks(callbacksPath);
1081
- } catch {
962
+ }
963
+
964
+ // src/core/extendedPrismaClient.ts
965
+ function supportsPrisma6Middleware2(prisma) {
966
+ return typeof prisma.$use === "function";
967
+ }
968
+ var FlareClient = class extends PrismaClient2 {
969
+ constructor(options = {}) {
970
+ const { callbacks = true, ...prismaOptions } = options;
971
+ super(prismaOptions);
972
+ if (callbacks) {
973
+ if (supportsPrisma6Middleware2(this)) {
974
+ registerHooksLegacy(this);
975
+ } else {
976
+ const extension = createHooksExtension(this);
977
+ return this.$extends(extension);
978
+ }
979
+ }
980
+ }
981
+ /**
982
+ * Creates a new FlareBuilder instance for the specified model.
983
+ * @param modelName - The name of the model.
984
+ * @returns FlareBuilder instance
985
+ */
986
+ from(modelName) {
987
+ const key = modelName.charAt(0).toLowerCase() + modelName.slice(1);
988
+ const model = this[key];
989
+ if (!model) {
990
+ throw new Error(`Model ${modelName} does not exist on PrismaClient.`);
991
+ }
992
+ return new FlareBuilder(model);
993
+ }
994
+ /**
995
+ * Executes a transaction with the FlareClient capabilities.
996
+ * @param fn - The transaction function.
997
+ * @param options - Transaction options.
998
+ * @returns The result of the transaction.
999
+ */
1000
+ async transaction(fn, options) {
1001
+ return super.$transaction(async (tx) => {
1002
+ const extendedTx = new Proxy(tx, {
1003
+ get: (target, prop, receiver) => {
1004
+ if (prop === "from") {
1005
+ return (modelName) => {
1006
+ const key = modelName.charAt(0).toLowerCase() + modelName.slice(1);
1007
+ const model = target[key];
1008
+ if (!model) {
1009
+ throw new Error(`Model ${modelName} does not exist on TransactionClient.`);
1010
+ }
1011
+ return new FlareBuilder(model);
1012
+ };
1013
+ }
1014
+ return Reflect.get(target, prop, receiver);
1015
+ }
1016
+ });
1017
+ return fn(extendedTx);
1018
+ }, options);
1082
1019
  }
1083
- return client;
1020
+ };
1021
+ var ExtendedPrismaClient = FlareClient;
1022
+
1023
+ // src/core/hooks.ts
1024
+ function normalizeModelName(model) {
1025
+ return model.toLowerCase();
1026
+ }
1027
+ function beforeCreate(model, callback) {
1028
+ hookRegistry_default.addHook(normalizeModelName(model), "create", "before", callback);
1029
+ }
1030
+ function beforeDelete(model, callback) {
1031
+ hookRegistry_default.addHook(normalizeModelName(model), "delete", "before", callback);
1032
+ }
1033
+ function afterCreate(model, callback) {
1034
+ hookRegistry_default.addHook(normalizeModelName(model), "create", "after", callback);
1035
+ }
1036
+ function afterDelete(model, callback) {
1037
+ hookRegistry_default.addHook(normalizeModelName(model), "delete", "after", callback);
1038
+ }
1039
+ function beforeUpdate(model, callback) {
1040
+ hookRegistry_default.addHook(normalizeModelName(model), "update", "before", callback);
1041
+ }
1042
+ function afterUpdate(model, callback) {
1043
+ hookRegistry_default.addHook(normalizeModelName(model), "update", "after", callback);
1044
+ }
1045
+ function afterChange(model, column, callback) {
1046
+ hookRegistry_default.addColumnHook(normalizeModelName(model), column, callback);
1047
+ }
1048
+ function afterUpsert(model, callback) {
1049
+ hookRegistry_default.addHook(normalizeModelName(model), "upsert", "after", callback);
1084
1050
  }
1085
1051
 
1086
1052
  // src/core/adapters/postgres.ts
@@ -1163,8 +1129,8 @@ function parseDatabaseUrl(url) {
1163
1129
  }
1164
1130
 
1165
1131
  // src/core/adapters/sqlite.ts
1166
- import * as fs3 from "fs";
1167
- import * as path3 from "path";
1132
+ import * as fs2 from "fs";
1133
+ import * as path2 from "path";
1168
1134
  var SqliteAdapter = {
1169
1135
  name: "sqlite",
1170
1136
  matches(url) {
@@ -1172,13 +1138,13 @@ var SqliteAdapter = {
1172
1138
  },
1173
1139
  async create(url) {
1174
1140
  const filePath = parseSqliteUrl(url);
1175
- const dir = path3.dirname(filePath);
1141
+ const dir = path2.dirname(filePath);
1176
1142
  try {
1177
- if (!fs3.existsSync(dir)) {
1178
- fs3.mkdirSync(dir, { recursive: true });
1143
+ if (!fs2.existsSync(dir)) {
1144
+ fs2.mkdirSync(dir, { recursive: true });
1179
1145
  }
1180
- if (!fs3.existsSync(filePath)) {
1181
- fs3.writeFileSync(filePath, "");
1146
+ if (!fs2.existsSync(filePath)) {
1147
+ fs2.writeFileSync(filePath, "");
1182
1148
  console.log(`\u2705 SQLite database created at "${filePath}"`);
1183
1149
  } else {
1184
1150
  console.log(`\u26A0\uFE0F SQLite database already exists at "${filePath}"`);
@@ -1191,20 +1157,20 @@ var SqliteAdapter = {
1191
1157
  async drop(url) {
1192
1158
  const filePath = parseSqliteUrl(url);
1193
1159
  try {
1194
- if (fs3.existsSync(filePath)) {
1195
- fs3.unlinkSync(filePath);
1160
+ if (fs2.existsSync(filePath)) {
1161
+ fs2.unlinkSync(filePath);
1196
1162
  console.log(`\u2705 SQLite database at "${filePath}" dropped successfully.`);
1197
1163
  } else {
1198
1164
  console.log(`\u26A0\uFE0F SQLite database does not exist at "${filePath}"`);
1199
1165
  }
1200
- if (fs3.existsSync(`${filePath}-journal`)) {
1201
- fs3.unlinkSync(`${filePath}-journal`);
1166
+ if (fs2.existsSync(`${filePath}-journal`)) {
1167
+ fs2.unlinkSync(`${filePath}-journal`);
1202
1168
  }
1203
- if (fs3.existsSync(`${filePath}-wal`)) {
1204
- fs3.unlinkSync(`${filePath}-wal`);
1169
+ if (fs2.existsSync(`${filePath}-wal`)) {
1170
+ fs2.unlinkSync(`${filePath}-wal`);
1205
1171
  }
1206
- if (fs3.existsSync(`${filePath}-shm`)) {
1207
- fs3.unlinkSync(`${filePath}-shm`);
1172
+ if (fs2.existsSync(`${filePath}-shm`)) {
1173
+ fs2.unlinkSync(`${filePath}-shm`);
1208
1174
  }
1209
1175
  } catch (error) {
1210
1176
  console.error("\u274C Error dropping SQLite database:", error);
@@ -1214,8 +1180,8 @@ var SqliteAdapter = {
1214
1180
  };
1215
1181
  function parseSqliteUrl(url) {
1216
1182
  let cleanPath = url.replace(/^file:/, "");
1217
- if (!path3.isAbsolute(cleanPath)) {
1218
- cleanPath = path3.resolve(process.cwd(), cleanPath);
1183
+ if (!path2.isAbsolute(cleanPath)) {
1184
+ cleanPath = path2.resolve(process.cwd(), cleanPath);
1219
1185
  }
1220
1186
  return cleanPath;
1221
1187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-flare",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Prisma utilities package with callback system and query builder for chained operations",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -52,8 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "pg": "^8.16.3",
55
- "pluralize": "^8.0.0",
56
- "dotenv": "^17.2.3"
55
+ "pluralize": "^8.0.0"
57
56
  },
58
57
  "devDependencies": {
59
58
  "@prisma/client": "^5.18.0",
package/readme.md CHANGED
@@ -73,27 +73,49 @@ prisma-flare automatically detects your Prisma version at runtime and uses the a
73
73
  Replace your standard `PrismaClient` with `FlareClient` in your database setup file (e.g., `src/db.ts` or `src/lib/prisma.ts`).
74
74
 
75
75
  ```typescript
76
- // src/db.ts
77
- import { FlareClient, registerHooks } from 'prisma-flare';
76
+ // prisma/db.ts
77
+ import './callbacks'; // Import generated index to register all hooks
78
+ import { FlareClient } from 'prisma-flare';
78
79
 
79
- // Initialize hooks middleware and auto-load callbacks
80
- export const db = await registerHooks(new FlareClient());
80
+ export const db = new FlareClient();
81
81
  ```
82
82
 
83
- `registerHooks()` is async and:
84
- - Registers the hooks middleware (using the appropriate API for your Prisma version)
85
- - Automatically loads all callback files from `prisma/callbacks` (or your configured path)
86
- - Returns the extended client instance
83
+ `FlareClient` automatically attaches the callbacks middleware (using the appropriate API for your Prisma version). The callbacks import loads a generated barrel file that registers all your hooks - this pattern works in all environments (bundlers, Node.js, serverless, etc.).
87
84
 
88
- ### 2. Generate Query Classes
85
+ **With Prisma adapters:**
89
86
 
90
- Run the generator to create type-safe query classes for your specific schema.
87
+ ```typescript
88
+ import './callbacks';
89
+ import { PrismaPg } from '@prisma/adapter-pg';
90
+ import { FlareClient } from 'prisma-flare';
91
+
92
+ const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
93
+
94
+ export const db = new FlareClient({ adapter });
95
+ ```
96
+
97
+ **Disable callbacks middleware:**
98
+
99
+ ```typescript
100
+ import { FlareClient } from 'prisma-flare';
101
+
102
+ // If you don't use callbacks, disable the middleware for slightly less overhead
103
+ export const db = new FlareClient({ callbacks: false });
104
+ ```
105
+
106
+ ### 2. Generate Query Classes & Callbacks Index
107
+
108
+ Run the generator to create type-safe query classes and the callbacks barrel file.
91
109
 
92
110
  ```bash
93
111
  npx prisma-flare generate
94
112
  ```
95
113
 
96
- By default, this will look for your `db` instance in `src/db` and output queries to `src/models`.
114
+ This command:
115
+ - Generates query classes based on your `schema.prisma`
116
+ - Generates `prisma/callbacks/index.ts` that imports all your callback files
117
+
118
+ **Important:** Re-run this command after adding new callback files to update the index.
97
119
 
98
120
  ### 3. Configuration (Optional)
99
121
 
@@ -110,7 +132,7 @@ If your project structure is different, create a `prisma-flare.config.json` in y
110
132
 
111
133
  - `modelsPath`: Where to generate the query classes (defaults to `prisma/models`).
112
134
  - `dbPath`: Path to the file exporting your `db` instance (relative to project root, defaults to `prisma/db`).
113
- - `callbacksPath`: Directory containing your callback/hook files (defaults to `prisma/callbacks`). All `.ts`/`.js` files in this directory are automatically loaded when `registerHooks()` is called.
135
+ - `callbacksPath`: Directory containing your callback/hook files (defaults to `prisma/callbacks`). The generator creates an `index.ts` barrel file in this directory.
114
136
  - `envPath`: Path to your environment file (optional, defaults to `.env`).
115
137
  - `plurals`: Custom pluralization for model names (optional).
116
138
 
@@ -203,7 +225,7 @@ await DB.instance.transaction(async (tx) => {
203
225
 
204
226
  ### Callhooks & Middleware
205
227
 
206
- Define hooks to run logic before or after database operations. Create callback files in your callbacks directory (default: `prisma/callbacks`) and they'll be automatically loaded.
228
+ Define hooks to run logic before or after database operations. Create callback files in your callbacks directory (default: `prisma/callbacks`), then run `npx prisma-flare generate` to update the index.
207
229
 
208
230
  ```typescript
209
231
  // prisma/callbacks/user.ts
@@ -235,7 +257,7 @@ afterChange('post', 'published', async (oldValue, newValue, record) => {
235
257
  });
236
258
  ```
237
259
 
238
- All files in the callbacks directory are automatically imported when `registerHooks()` is called. No manual loading required.
260
+ After creating callback files, run `npx prisma-flare generate` to update the index. The generated `index.ts` imports all callbacks, which you then import in your db setup file.
239
261
 
240
262
  #### Hook Configuration
241
263
 
@@ -306,25 +328,22 @@ This prevents false positives when:
306
328
 
307
329
  #### Advanced Hook Registration
308
330
 
309
- For more control over hook registration, prisma-flare exports additional utilities:
331
+ For advanced use cases, prisma-flare exports lower-level utilities:
310
332
 
311
333
  ```typescript
312
334
  import {
313
- registerHooks, // Auto-detects Prisma version + auto-loads callbacks (recommended)
314
- registerHooksLegacy, // Force legacy $use API (Prisma ≤6 only, no auto-load)
335
+ registerHooksLegacy, // Force legacy $use API (Prisma ≤6 only)
315
336
  createHooksExtension, // Get raw extension for manual use
316
- loadCallbacks // Manually load callbacks from a custom path
337
+ loadCallbacks // Manually load callbacks at runtime (dev only)
317
338
  } from 'prisma-flare';
318
339
 
319
- // Option 1: Auto-detect with auto-loading (recommended)
320
- const db = await registerHooks(new FlareClient());
321
-
322
- // Option 2: Manual callback loading from custom path
340
+ // Manual extension on raw PrismaClient (advanced)
323
341
  import { PrismaClient } from '@prisma/client';
324
342
  const prisma = new PrismaClient().$extends(createHooksExtension(new PrismaClient()));
325
- await loadCallbacks('/custom/path/to/callbacks');
326
343
  ```
327
344
 
345
+ For most use cases, just use `new FlareClient()` which handles everything automatically.
346
+
328
347
  ## CLI Utilities
329
348
 
330
349
  Prisma Flare comes with a suite of CLI tools to manage your database workflow. It supports **PostgreSQL** and **SQLite** out of the box, and is extensible for other databases.