neopg 1.0.0 → 2.0.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/README.cn.md CHANGED
@@ -64,7 +64,7 @@ await db.close();
64
64
  创建一个模型文件(例如 `models/User.js`)。您的类应当继承自 `NeoPG.ModelChain`。
65
65
 
66
66
  ```javascript
67
- const { ModelChain, dataTypes } = require('neopg');
67
+ const { ModelChain, dataTypes } = require('neopg')
68
68
 
69
69
  class User extends ModelChain {
70
70
  static schema = {
@@ -106,29 +106,130 @@ class User extends ModelChain {
106
106
  index: ['email', 'age'],
107
107
  // 唯一索引定义
108
108
  unique: ['username']
109
- };
109
+ }
110
110
  }
111
111
 
112
- module.exports = User;
112
+ module.exports = User
113
+ ```
114
+
115
+ ## 🛠 CLI 模型生成器
116
+
117
+ NeoPG 内置了一个 CLI 工具,可以快速生成带有样板代码的模型文件。
118
+
119
+ ### 用法
120
+
121
+ 通过 `npx` 直接运行(无需全局安装):
122
+
123
+ ```bash
124
+ npx neopg-model [选项] [模型名称...]
125
+ ```
126
+
127
+ ### 选项
128
+
129
+ * `--dir=<path>`: 指定输出目录(默认:`./model`)。
130
+
131
+ ### 示例
132
+
133
+ **1. 基础生成**
134
+ ```bash
135
+ npx neopg-model user
136
+ # 创建文件: ./model/user.js
137
+ # 类名: User
138
+ # 表名: user
139
+ ```
140
+
141
+ **2. 命名规范(连字符处理)**
142
+ 输入带连字符的名称,NeoPG 会自动将类名转换为 **大驼峰(CamelCase)**,将表名转换为 **下划线(snake_case)**。
143
+
144
+ ```bash
145
+ npx neopg-model user-log
146
+ # 创建文件: ./model/user-log.js
147
+ # 类名: UserLog
148
+ # 表名: user_log
149
+ ```
150
+
151
+ **3. 批量生成与自定义目录**
152
+ ```bash
153
+ npx neopg-model --dir=./src/models product order-item
154
+ # 创建:
155
+ # ./src/models/product.js
156
+ # ./src/models/order-item.js
157
+ ```
158
+
159
+ **4. ES Modules (.mjs)**
160
+ 如果在名称后加上 `.mjs` 后缀,将生成 ESM 语法(`export default`)的文件。
161
+ ```bash
162
+ npx neopg-model config.mjs
113
163
  ```
114
164
 
115
165
  ---
116
166
 
117
167
  ## ⚙️ 注册与同步
118
168
 
119
- 初始化 NeoPG 并注册您的模型。您还可以将表结构定义同步到数据库中。
169
+ 初始化 NeoPG 并注册您的模型。您可以使用类(Class)或配置对象(Object)来定义模型。
170
+
171
+ ### 注册模型
172
+
173
+ NeoPG 提供了三种注册方法以应对不同场景:
174
+
175
+ * **`define(model)`**:标准注册方法。如果同名模型已存在,会抛出错误(`modelName conflict`),防止意外覆盖。
176
+ * **`add(model)`**:同 `define`,行为一致。
177
+ * **`set(model)`**:**强制覆盖/重置**。如果模型已存在,则更新其定义。适用于热重载或动态 Schema 场景。
120
178
 
121
179
  ```javascript
122
- const User = require('./models/User');
180
+ const User = require('./models/User')
181
+
182
+ // 1. 标准注册 (安全模式)
183
+ // 如果 'User' 已经被注册过,此处会报错
184
+ db.define(User)
185
+
186
+ // 2. 强制覆盖 (重置模式)
187
+ // 即使 'User' 已存在,也会使用新的定义覆盖它
188
+ db.set(User)
189
+
190
+ // 3. 使用纯对象注册 (快速原型)
191
+ db.define({
192
+ tableName: 'logs',
193
+ column: {
194
+ message: 'string',
195
+ level: 'int'
196
+ }
197
+ })
123
198
 
124
- // 1. 注册模型
125
- db.define(User);
199
+ ```
200
+
201
+ ### 同步数据库
202
+
203
+ 根据已注册的模型同步数据库表结构。
126
204
 
127
- // 2. 同步表结构 (DDL)
205
+ ```javascript
206
+ // 同步表结构 (DDL)
128
207
  // options: { force: true } 开启强制模式,会删除 Schema 中未定义的字段,请谨慎使用
129
- await db.sync({ force: false });
208
+ await db.sync({ force: false })
130
209
 
131
- console.log('数据库结构已同步!');
210
+ console.log('数据库结构已同步!')
211
+ ```
212
+
213
+ ---
214
+
215
+ ### 📂 自动加载模型
216
+
217
+ NeoPG 支持扫描指定目录并自动注册所有模型,无需手动逐个引入。
218
+
219
+ **加载规则:**
220
+ * 仅加载 `.js` 和 `.mjs` 后缀的文件。
221
+ * **忽略**以 `_` 开头的文件(可用作目录内的共享工具或基类)。
222
+ * **忽略**以 `!` 开头的文件(可用作临时禁用的模型)。
223
+
224
+ ```javascript
225
+ const db = new NeoPG(config)
226
+
227
+ // 自动加载 ./models 目录下的所有模型
228
+ // 注意:这是一个异步方法,因为它兼容 ESM (.mjs) 的动态导入
229
+ await db.loadModels('./models')
230
+
231
+ // 加载完成后即可同步或使用
232
+ await db.sync()
132
233
  ```
133
234
 
134
235
  ---
@@ -160,16 +261,13 @@ const page2 = await db.model('User').page(2, 20).find(); // 第 2 页,每页 2
160
261
 
161
262
  ```javascript
162
263
  await db.model('User')
163
- // 对象风格 (自动处理 AND)
164
264
  .where({
165
265
  age: 18,
166
266
  status: 'active'
167
267
  })
168
- // 操作符风格
169
268
  .where('create_time', '>', 1600000000)
170
- // SQL 片段风格 (强大且灵活!)
171
269
  .where('id IS NOT NULL')
172
- .find();
270
+ .find()
173
271
  ```
174
272
 
175
273
  ### 结合模板字符串的复杂查询
@@ -180,9 +278,9 @@ await db.model('User')
180
278
  // db.sql 是原生的 postgres 实例
181
279
  const { sql } = db;
182
280
 
281
+ // 通过模板字符串安全地注入参数
183
282
  await db.model('User')
184
283
  .where({ status: 'active' })
185
- // 通过模板字符串安全地注入参数
186
284
  .where(sql`age > ${20} AND email LIKE ${'%@gmail.com'}`)
187
285
  .find();
188
286
  ```
package/README.md CHANGED
@@ -54,7 +54,7 @@ const db = new NeoPG(config);
54
54
  ### Close Connection
55
55
 
56
56
  ```javascript
57
- await db.close();
57
+ await db.close()
58
58
  ```
59
59
 
60
60
  ---
@@ -64,7 +64,7 @@ await db.close();
64
64
  Create a model file (e.g., `models/User.js`). Your class should extend `NeoPG.ModelChain`.
65
65
 
66
66
  ```javascript
67
- const { ModelChain, dataTypes } = require('neopg');
67
+ const { ModelChain, dataTypes } = require('neopg')
68
68
 
69
69
  class User extends ModelChain {
70
70
  static schema = {
@@ -105,29 +105,130 @@ class User extends ModelChain {
105
105
  // Indexes
106
106
  index: ['email', 'age'],
107
107
  unique: ['username']
108
- };
108
+ }
109
109
  }
110
110
 
111
- module.exports = User;
111
+ module.exports = User
112
+ ```
113
+
114
+ ## 🛠 CLI Model Generator
115
+
116
+ NeoPG includes a built-in CLI tool to quickly generate model files with boilerplate code.
117
+
118
+ ### Usage
119
+
120
+ Run via `npx` (no global installation required):
121
+
122
+ ```bash
123
+ npx neopg-model [options] [model_names...]
124
+ ```
125
+
126
+ ### Options
127
+
128
+ * `--dir=<path>`: Specify the output directory (default: `./model`).
129
+
130
+ ### Examples
131
+
132
+ **1. Basic Generation**
133
+ ```bash
134
+ npx neopg-model user
135
+ # Creates: ./model/user.js
136
+ # Class: User
137
+ # Table: user
138
+ ```
139
+
140
+ **2. Naming Convention (Hyphenated)**
141
+ NeoPG automatically converts hyphenated names to **CamelCase** for the class and **snake_case** for the table.
142
+
143
+ ```bash
144
+ npx neopg-model user-log
145
+ # Creates: ./model/user-log.js
146
+ # Class: UserLog
147
+ # Table: user_log
148
+ ```
149
+
150
+ **3. Multiple Models & Custom Directory**
151
+ ```bash
152
+ npx neopg-model --dir=./src/models product order-item
153
+ # Creates:
154
+ # ./src/models/product.js
155
+ # ./src/models/order-item.js
156
+ ```
157
+
158
+ **4. ES Modules (.mjs)**
159
+ If you suffix the name with `.mjs`, it generates ESM syntax (`export default`).
160
+ ```bash
161
+ npx neopg-model config.mjs
112
162
  ```
113
163
 
114
164
  ---
115
165
 
116
166
  ## ⚙️ Registration & Sync
117
167
 
118
- Initialize NeoPG and register your models. You can also sync the table structure to the database.
168
+ Initialize NeoPG and register your models. You can define models using classes or configuration objects.
169
+
170
+ ### Registering Models
171
+
172
+ NeoPG provides three methods for registration:
173
+
174
+ * **`define(model)`**: The standard method. Throws an error if a model with the same name already exists.
175
+ * **`add(model)`**: Alias for `define`.
176
+ * **`set(model)`**: **Overwrites** the existing model if the name conflicts. Useful for hot-reloading or dynamic schema updates.
119
177
 
120
178
  ```javascript
121
- const User = require('./models/User');
179
+ const User = require('./models/User')
180
+
181
+ // 1. Standard Registration (Safe)
182
+ // Will throw error: "[NeoPG] modelName conflict: User" if registered twice
183
+ db.define(User)
184
+
185
+ // 2. Force Overwrite (Reset)
186
+ // Updates the definition for 'User' even if it exists
187
+ db.set(User)
188
+
189
+ // 3. Register using a plain object (Quick prototype)
190
+ db.define({
191
+ tableName: 'logs',
192
+ column: {
193
+ message: 'string',
194
+ level: 'int'
195
+ }
196
+ })
122
197
 
123
- // 1. Register Model
124
- db.define(User);
198
+ ```
199
+
200
+ ### Syncing Database
201
+
202
+ Sync the table structure to the database based on registered models.
125
203
 
126
- // 2. Sync Table Structure (DDL)
204
+ ```javascript
205
+ // Sync Table Structure (DDL)
127
206
  // options: { force: true } will drop columns not defined in schema
128
- await db.sync({ force: false });
207
+ await db.sync({ force: false })
129
208
 
130
- console.log('Database synced!');
209
+ console.log('Database synced!')
210
+ ```
211
+
212
+ ---
213
+
214
+ ### 📂 Auto-loading Models
215
+
216
+ Instead of manually importing and defining each model, you can load all models from a directory.
217
+
218
+ **Rules:**
219
+ * Only `.js` and `.mjs` files are loaded.
220
+ * Files starting with `_` are ignored (useful for utils/helpers).
221
+ * Files starting with `!` are ignored (useful for disabled models).
222
+
223
+ ```javascript
224
+ const db = new NeoPG(config)
225
+
226
+ // Load all models from the './models' directory
227
+ // This is asynchronous because it supports .mjs dynamic imports
228
+ await db.loadModels('./models')
229
+
230
+ // Now you can sync and use them
231
+ await db.sync()
131
232
  ```
132
233
 
133
234
  ---
@@ -159,16 +260,13 @@ const page2 = await db.model('User').page(2, 20).find(); // Page 2, Size 20
159
260
 
160
261
  ```javascript
161
262
  await db.model('User')
162
- // Object style (AND logic)
163
263
  .where({
164
264
  age: 18,
165
265
  status: 'active'
166
266
  })
167
- // Operator style
168
267
  .where('create_time', '>', 1600000000)
169
- // SQL Fragment style (Powerful!)
170
268
  .where('id IS NOT NULL')
171
- .find();
269
+ .find()
172
270
  ```
173
271
 
174
272
  ### Complex Where with Template Literals
@@ -221,14 +319,14 @@ const stats = await db.model('User')
221
319
  const newUser = await db.model('User').insert({
222
320
  username: 'neo',
223
321
  email: 'neo@matrix.com'
224
- });
322
+ })
225
323
  // ID and Timestamps are automatically generated if configured in Schema
226
324
 
227
325
  // Insert multiple (Batch)
228
326
  await db.model('User').insert([
229
327
  { username: 'a' },
230
328
  { username: 'b' }
231
- ]);
329
+ ])
232
330
  ```
233
331
 
234
332
  ### Update
@@ -305,16 +403,15 @@ const result = await db.transaction(async (tx) => {
305
403
  const user = await tx.model('User').insert({ username: 'alice' });
306
404
 
307
405
  // 2. Read operation within transaction
308
- const count = await tx.model('User').count();
406
+ const count = await tx.model('User').count()
309
407
 
310
408
  // 3. Throwing an error will automatically ROLLBACK
311
409
  if (count > 100) {
312
- throw new Error('Limit reached');
410
+ throw new Error('Limit reached')
313
411
  }
314
412
 
315
- return user;
316
- });
317
- // Automatically COMMITTED here if no error
413
+ return user
414
+ })
318
415
  ```
319
416
 
320
417
  ### Using Raw Postgres Transaction
@@ -323,7 +420,7 @@ const result = await db.transaction(async (tx) => {
323
420
  await db.sql.begin(async (sql) => {
324
421
  // sql is the transaction connection
325
422
  await sql`INSERT INTO users (name) VALUES ('bob')`;
326
- });
423
+ })
327
424
  ```
328
425
 
329
426
  ---
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const fs = require('node:fs');
6
+ const path = require('node:path');
7
+ const process = require('node:process');
8
+
9
+ // JS 关键字列表,防止类名冲突
10
+ const JS_KEYWORDS = new Set([
11
+ 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete',
12
+ 'do', 'else', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import',
13
+ 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true',
14
+ 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield', 'let', 'static', 'enum', 'await',
15
+ 'implements', 'interface', 'package', 'private', 'protected', 'public', 'arguments', 'eval'
16
+ ]);
17
+
18
+ // 帮助信息
19
+ function showHelp() {
20
+ console.log(`
21
+ Usage: neopg-model [options] [name1] [name2] ...
22
+
23
+ Options:
24
+ --dir=<path> 指定模型文件保存目录 (默认: ./model)
25
+
26
+ Example:
27
+ neopg-model user-log order_info
28
+ neopg-model --dir=./src/models Product
29
+ `);
30
+ }
31
+
32
+ // 解析参数
33
+ function parseArgs() {
34
+ const args = process.argv.slice(2);
35
+ const config = {
36
+ dir: './model',
37
+ names: []
38
+ };
39
+
40
+ if (args.length === 0) {
41
+ showHelp();
42
+ process.exit(0);
43
+ }
44
+
45
+ for (const arg of args) {
46
+ if (arg.startsWith('--dir=')) {
47
+ config.dir = arg.split('=')[1];
48
+ } else if (!arg.startsWith('-')) {
49
+ config.names.push(arg);
50
+ }
51
+ }
52
+
53
+ return config;
54
+ }
55
+
56
+ // 验证名称合法性: 字母开头,只允许字母、数字、_、-
57
+ function isValidName(name) {
58
+ return /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name);
59
+ }
60
+
61
+ // 处理命名规则
62
+ function processName(inputName) {
63
+ // 1. 移除扩展名,但记录是否是 .mjs
64
+ let isMjs = inputName.endsWith('.mjs');
65
+ const cleanName = inputName.replace(/(\.js|\.mjs)$/, '');
66
+
67
+ // 2. 验证合法性
68
+ if (!isValidName(cleanName)) {
69
+ console.error(`\x1b[31m[Error]\x1b[0m 名称 "${inputName}" 不合法。必须以字母开头,只能包含字母、数字、下划线和连字符。`);
70
+ return null;
71
+ }
72
+
73
+ // 3. 生成 tableName: 全小写,- 替换为 _
74
+ const tableName = cleanName.toLowerCase().replace(/-/g, '_');
75
+
76
+ // 4. 生成 modelName:
77
+ // - 去掉 '-'
78
+ // - '-' 后面的字母大写 (驼峰)
79
+ // - '_' 保留,_ 后面的字母不做特殊处理
80
+ // - 首字母大写
81
+ const parts = cleanName.split('-');
82
+ const modelName = parts.map((p, index) => {
83
+ // 每一部分的首字母大写
84
+ return p.charAt(0).toUpperCase() + p.slice(1);
85
+ }).join(''); // 直接连接,去掉了 -
86
+
87
+ // 检查关键字
88
+ if (JS_KEYWORDS.has(modelName)) {
89
+ console.warn(`\x1b[33m[Warning]\x1b[0m 生成的类名 "${modelName}" 是 JavaScript 关键字,建议修改名称。`);
90
+ }
91
+
92
+ return {
93
+ raw: cleanName,
94
+ isMjs,
95
+ tableName,
96
+ modelName
97
+ };
98
+ }
99
+
100
+ // 生成 CommonJS 模板
101
+ function generateCJS(info) {
102
+ return `'use strict'\n\nconst { dataTypes, ModelChain } = require('neopg')
103
+
104
+ class ${info.modelName} extends ModelChain {
105
+ static schema = {
106
+ tableName: '${info.tableName}',
107
+ modelName: '${info.modelName}',
108
+ primaryKey: 'id',
109
+ column: {
110
+ id: {
111
+ type: dataTypes.ID
112
+ },
113
+ name: {
114
+ type: dataTypes.STRING(30),
115
+ default: ''
116
+ }
117
+ },
118
+ index: [],
119
+ unique: []
120
+ }
121
+ }
122
+
123
+ module.exports = ${info.modelName}
124
+ `;
125
+ }
126
+
127
+ // 生成 ESM 模板
128
+ function generateESM(info) {
129
+ return `'use strict'\n\nimport { dataTypes, ModelChain } from 'neopg'
130
+
131
+ class ${info.modelName} extends ModelChain {
132
+ static schema = {
133
+ tableName: '${info.tableName}',
134
+ modelName: '${info.modelName}',
135
+ primaryKey: 'id',
136
+ column: {
137
+ id: {
138
+ type: dataTypes.ID
139
+ },
140
+ name: {
141
+ type: dataTypes.STRING(30),
142
+ default: ''
143
+ }
144
+ },
145
+ index: [],
146
+ unique: []
147
+ }
148
+ }
149
+
150
+ export default ${info.modelName}
151
+ `;
152
+ }
153
+
154
+ // 主逻辑
155
+ function main() {
156
+ const config = parseArgs();
157
+
158
+ // 1. 确保目录存在
159
+ const targetDir = path.resolve(process.cwd(), config.dir);
160
+ if (!fs.existsSync(targetDir)) {
161
+ try {
162
+ fs.mkdirSync(targetDir, { recursive: true });
163
+ console.log(`\x1b[32m[Info]\x1b[0m 创建目录: ${config.dir}`);
164
+ } catch (err) {
165
+ console.error(`\x1b[31m[Error]\x1b[0m 无法创建目录 ${targetDir}: ${err.message}`);
166
+ process.exit(1);
167
+ }
168
+ }
169
+
170
+ if (config.names.length === 0) {
171
+ console.error('\x1b[31m[Error]\x1b[0m 未指定模型名称。');
172
+ process.exit(1);
173
+ }
174
+
175
+ for (const name of config.names) {
176
+ const info = processName(name);
177
+ if (!info) continue;
178
+
179
+ const ext = info.isMjs ? '.mjs' : '.js';
180
+ const fileName = info.raw + ext;
181
+ const filePath = path.join(targetDir, fileName);
182
+
183
+ // 检查冲突:
184
+ // 1. 检查完全同名的文件
185
+ if (fs.existsSync(filePath)) {
186
+ console.error(`\x1b[31m[Skip]\x1b[0m 文件已存在: ${fileName}`);
187
+ continue;
188
+ }
189
+
190
+ // 2. 检查 ModelName 命名的文件 (可选,防止 user.js 和 User.js 在某些系统混淆)
191
+ const modelNamePath = path.join(targetDir, info.modelName + ext);
192
+ if (fs.existsSync(modelNamePath) && modelNamePath.toLowerCase() !== filePath.toLowerCase()) {
193
+ console.warn(`\x1b[33m[Warning]\x1b[0m 存在同类名文件: ${info.modelName}${ext},可能会导致混淆。`);
194
+ }
195
+
196
+ const content = info.isMjs ? generateESM(info) : generateCJS(info);
197
+
198
+ try {
199
+ fs.writeFileSync(filePath, content, 'utf8');
200
+ console.log(`\x1b[32m[Success]\x1b[0m 已创建模型: ${path.join(config.dir, fileName)} (Table: ${info.tableName}, Class: ${info.modelName})`);
201
+ } catch (err) {
202
+ console.error(`\x1b[31m[Error]\x1b[0m 写入文件失败 ${fileName}: ${err.message}`);
203
+ }
204
+ }
205
+ }
206
+
207
+ main();
package/lib/NeoPG.js CHANGED
@@ -7,6 +7,10 @@ const TransactionScope = require('./TransactionScope.js')
7
7
  const ModelChain = require('./ModelChain.js')
8
8
  const dataTypes = require('./dataTypes.js')
9
9
 
10
+ const path = require('node:path')
11
+ const fs = require('node:fs')
12
+ const { pathToFileURL } = require('node:url')
13
+
10
14
  class NeoPG {
11
15
  constructor(config) {
12
16
  this.driver = postgres(config)
@@ -33,7 +37,7 @@ class NeoPG {
33
37
 
34
38
  // --- 注册 ---
35
39
 
36
- add(input) {
40
+ add(input, is_reset=false) {
37
41
  let ModelClass
38
42
 
39
43
  if (typeof input === 'function') {
@@ -43,10 +47,16 @@ class NeoPG {
43
47
  }
44
48
 
45
49
  const rawSchema = ModelClass.schema
50
+
46
51
  if (!rawSchema) throw new Error(`[NeoPG] Missing static schema for ${ModelClass.name}`)
47
52
 
48
53
  const def = new ModelDef(rawSchema)
49
54
 
55
+ //已经存在又不是更新,则报错
56
+ if (!is_reset && this.registry.has(def.modelName)) {
57
+ throw new Error(`[NeoPG] modelName conflict: ${def.modelName}`)
58
+ }
59
+
50
60
  this.registry.set(def.modelName, {
51
61
  Class: ModelClass,
52
62
  def: def
@@ -59,6 +69,10 @@ class NeoPG {
59
69
  return this.add(model)
60
70
  }
61
71
 
72
+ set(model) {
73
+ return this.add(model, true)
74
+ }
75
+
62
76
  // --- 事务 ---
63
77
  async transaction(callback) {
64
78
  return await this.driver.begin(async (trxSql) => {
@@ -126,6 +140,64 @@ class NeoPG {
126
140
  async close() {
127
141
  await this.driver.end()
128
142
  }
143
+
144
+ /**
145
+ * 自动加载指定目录下的模型文件
146
+ * 规则:
147
+ * 1. 只加载 .js 和 .mjs 文件
148
+ * 2. 跳过以 '_' 开头的文件 (视为内部共享模块)
149
+ * 3. 跳过以 '!' 开头的文件 (视为保留或禁用文件)
150
+ *
151
+ * @param {string} dirPath - 目录路径 (相对 process.cwd() 或绝对路径)
152
+ */
153
+ async loadModels(dirPath) {
154
+ // 解析绝对路径
155
+ const absPath = path.isAbsolute(dirPath)
156
+ ? dirPath
157
+ : path.resolve(process.cwd(), dirPath)
158
+
159
+ if (!fs.existsSync(absPath)) {
160
+ throw new Error(`[NeoPG] Models directory not found: ${absPath}`)
161
+ }
162
+
163
+ const files = fs.readdirSync(absPath)
164
+
165
+ for (const file of files) {
166
+ // 过滤规则
167
+ if (file.startsWith('_') || file.startsWith('!')) continue
168
+
169
+ const ext = path.extname(file)
170
+
171
+ if (ext !== '.js' && ext !== '.mjs') continue
172
+
173
+ const fullFilePath = path.join(absPath, file)
174
+ let modelExport
175
+
176
+ try {
177
+ if (ext === '.mjs') {
178
+ // 处理 ESM 动态导入
179
+ // Windows 下 import() 需要 file:// 协议路径
180
+ const fileUrl = pathToFileURL(fullFilePath).href
181
+ const imported = await import(fileUrl)
182
+ modelExport = imported.default
183
+ } else {
184
+ // 处理 CommonJS
185
+ modelExport = require(fullFilePath)
186
+ }
187
+
188
+ // 注册模型 (如果导出为空或不是合法对象/类,add 方法内部会抛错或处理)
189
+ if (modelExport) {
190
+ this.add(modelExport)
191
+ }
192
+ } catch (err) {
193
+ console.error(`[NeoPG] Failed to load model: ${file}`)
194
+ throw err
195
+ }
196
+ }
197
+
198
+ return this
199
+ }
200
+
129
201
  }
130
202
 
131
203
  NeoPG.dataTypes = dataTypes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neopg",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "orm for postgres",
5
5
  "keywords": [
6
6
  "neopg",
@@ -18,6 +18,9 @@
18
18
  "bugs": {
19
19
  "url": "https://github.com/master-genius/neopg/issues"
20
20
  },
21
+ "bin": {
22
+ "neopg-model": "./bin/neopg-model.js"
23
+ },
21
24
  "repository": {
22
25
  "type": "git",
23
26
  "url": "git+https://github.com/master-genius/neopg.git"
package/test/test-db.js CHANGED
@@ -154,12 +154,21 @@ db.add(User);
154
154
  {
155
155
  username: 'Neo',
156
156
  email: '123@w.com',
157
+ sex: 1,
157
158
  level: Math.floor((Math.random() * 105))
158
159
  },
159
160
  {
160
161
  username: 'PG',
161
162
  email: '1234@w.com',
163
+ sex: 2,
162
164
  level: Math.floor((Math.random() * 100))
165
+ },
166
+
167
+ {
168
+ username: 'NPG',
169
+ email: '1235@w.com',
170
+ sex: 3,
171
+ level: 3
163
172
  }
164
173
  ])
165
174
  )
@@ -179,6 +188,12 @@ db.add(User);
179
188
  let result = await tx.model('User').where(tx.sql`level > 10`).returning('*').update(data)
180
189
  console.log(result)
181
190
 
191
+ let sex = 3
192
+ console.log(
193
+ 'test condition or',
194
+ await tx.model('User').where(tx.sql`(sex = ${sex} or level > 10)`).select(['id', 'level', 'username', 'sex']).find()
195
+ )
196
+
182
197
  console.log(
183
198
  'test avg',
184
199
  await tx.model('User').avg('level')