befly 2.1.0 → 2.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.
@@ -0,0 +1,471 @@
1
+ import { Env } from '../config/env.js';
2
+ import { Logger } from './logger.js';
3
+ import { createQueryBuilder } from './sqlBuilder.js';
4
+
5
+ // 数据库管理器(从 plugins/db.js 提取)
6
+ export class SqlManager {
7
+ // 私有属性
8
+ #sql;
9
+ #befly;
10
+
11
+ constructor(client, befly) {
12
+ this.#sql = client;
13
+ this.#befly = befly;
14
+ }
15
+
16
+ // 原始连接池访问(兼容占位)
17
+ get pool() {
18
+ return {
19
+ activeConnections: () => 0,
20
+ totalConnections: () => 0,
21
+ idleConnections: () => 0,
22
+ taskQueueSize: () => 0
23
+ };
24
+ }
25
+
26
+ // 创建查询构造器
27
+ query() {
28
+ return createQueryBuilder();
29
+ }
30
+
31
+ // 私有方法:通用数据处理函数 - 自动添加ID、时间戳和状态
32
+ async #processDataForInsert(data) {
33
+ const now = Date.now();
34
+ const genId = async () => (this.#befly?.redis?.genTimeID ? await this.#befly.redis.genTimeID() : now);
35
+
36
+ if (Array.isArray(data)) {
37
+ return await Promise.all(
38
+ data.map(async (item) => ({
39
+ ...item,
40
+ id: await genId(),
41
+ state: 0,
42
+ created_at: now,
43
+ updated_at: now
44
+ }))
45
+ );
46
+ } else {
47
+ return {
48
+ ...data,
49
+ id: await genId(),
50
+ state: 0,
51
+ created_at: now,
52
+ updated_at: now
53
+ };
54
+ }
55
+ }
56
+
57
+ // 私有方法:添加默认的state过滤条件
58
+ #addDefaultStateFilter(where = {}) {
59
+ const hasStateCondition = Object.keys(where).some((key) => key === 'state' || key.startsWith('state$'));
60
+ if (!hasStateCondition) return { ...where, state$ne: 2 };
61
+ return where;
62
+ }
63
+
64
+ // 私有方法:执行 SQL(支持传入事务连接对象)
65
+ async #executeWithConn(query, params = [], conn = null) {
66
+ if (!query || typeof query !== 'string') {
67
+ throw new Error('SQL 语句是必需的');
68
+ }
69
+
70
+ const isSelectLike = /^\s*(with|select|show|desc|explain)\b/i.test(query);
71
+ const isWriteLike = /^\s*(insert|update|delete|replace)\b/i.test(query);
72
+ const client = conn || this.#sql;
73
+ try {
74
+ if (Env.MYSQL_DEBUG === 1) {
75
+ Logger.debug('执行SQL:', { sql: query, params });
76
+ }
77
+ // 读查询
78
+ if (isSelectLike) {
79
+ if (params && params.length > 0) {
80
+ return await client.unsafe(query, params);
81
+ } else {
82
+ return await client.unsafe(query);
83
+ }
84
+ }
85
+
86
+ // 写查询
87
+ if (isWriteLike) {
88
+ const runOn = conn ? client : await this.#sql.reserve();
89
+ try {
90
+ if (params && params.length > 0) {
91
+ await runOn.unsafe(query, params);
92
+ } else {
93
+ await runOn.unsafe(query);
94
+ }
95
+ const [{ affectedRows }] = await runOn`SELECT ROW_COUNT() AS affectedRows`;
96
+ const [{ insertId }] = await runOn`SELECT LAST_INSERT_ID() AS insertId`;
97
+ return { affectedRows: Number(affectedRows) || 0, insertId: Number(insertId) || 0 };
98
+ } finally {
99
+ if (!conn && runOn && typeof runOn.release === 'function') runOn.release();
100
+ }
101
+ }
102
+
103
+ // 其他(DDL等)
104
+ if (params && params.length > 0) {
105
+ return await client.unsafe(query, params);
106
+ } else {
107
+ return await client.unsafe(query);
108
+ }
109
+ } catch (error) {
110
+ Logger.error('SQL 执行失败:', { sql: query, params, error: error.message });
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ // 私有方法:获取单条记录详情(支持传入连接对象)
116
+ async #getDetailWithConn(table, options = {}, conn = null) {
117
+ if (!table || typeof table !== 'string') {
118
+ throw new Error('表名是必需的');
119
+ }
120
+
121
+ const { where = {}, fields = '*', leftJoins = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
122
+
123
+ try {
124
+ const filteredWhere = this.#addDefaultStateFilter(where);
125
+ const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere).limit(1);
126
+
127
+ leftJoins.forEach((join) => {
128
+ if (typeof join === 'string') {
129
+ const parts = join.split(' ON ');
130
+ if (parts.length === 2) builder.leftJoin(parts[0].trim(), parts[1].trim());
131
+ } else if (join && typeof join === 'object' && join.table && join.on) {
132
+ builder.leftJoin(join.table, join.on);
133
+ }
134
+ });
135
+
136
+ const { sql: q, params } = builder.toSelectSql();
137
+ const result = await this.#executeWithConn(q, params, conn);
138
+ return result[0] || null;
139
+ } catch (error) {
140
+ Logger.error('getDetail 执行失败:', error);
141
+ throw error;
142
+ }
143
+ }
144
+
145
+ // 私有方法:获取列表(支持传入连接对象)
146
+ async #getListWithConn(table, options = {}, conn = null) {
147
+ if (!table || typeof table !== 'string') {
148
+ throw new Error('表名是必需的');
149
+ }
150
+
151
+ const { where = {}, fields = '*', leftJoins = [], orderBy = [], groupBy = [], having = [], page = 1, pageSize = 10 } = options;
152
+
153
+ try {
154
+ const filteredWhere = this.#addDefaultStateFilter(where);
155
+ const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere);
156
+
157
+ leftJoins.forEach((join) => {
158
+ if (typeof join === 'string') {
159
+ const parts = join.split(' ON ');
160
+ if (parts.length === 2) builder.leftJoin(parts[0].trim(), parts[1].trim());
161
+ } else if (join && typeof join === 'object' && join.table && join.on) {
162
+ builder.leftJoin(join.table, join.on);
163
+ }
164
+ });
165
+
166
+ if (Array.isArray(groupBy) && groupBy.length > 0) builder.groupBy(groupBy);
167
+ if (Array.isArray(having) && having.length > 0) having.forEach((h) => builder.having(h));
168
+ if (Array.isArray(orderBy) && orderBy.length > 0) builder.orderBy(orderBy);
169
+
170
+ const numPage = parseInt(page) || 1;
171
+ const numPageSize = parseInt(pageSize) || 10;
172
+ if (numPage > 0 && numPageSize > 0) {
173
+ const offset = (numPage - 1) * numPageSize;
174
+ builder.limit(numPageSize, offset);
175
+ }
176
+
177
+ const { sql: q, params } = builder.toSelectSql();
178
+ const rows = await this.#executeWithConn(q, params, conn);
179
+
180
+ let total = 0;
181
+ if (numPage > 0 && numPageSize > 0) {
182
+ const countBuilder = createQueryBuilder().from(table).where(filteredWhere);
183
+ leftJoins.forEach((join) => {
184
+ if (typeof join === 'string') {
185
+ const parts = join.split(' ON ');
186
+ if (parts.length === 2) countBuilder.leftJoin(parts[0].trim(), parts[1].trim());
187
+ } else if (join && typeof join === 'object' && join.table && join.on) {
188
+ countBuilder.leftJoin(join.table, join.on);
189
+ }
190
+ });
191
+
192
+ const { sql: countSql, params: countParams } = countBuilder.toCountSql();
193
+ const countResult = await this.#executeWithConn(countSql, countParams, conn);
194
+ total = countResult[0]?.total || 0;
195
+ }
196
+
197
+ return {
198
+ rows: Array.isArray(rows) ? rows : [],
199
+ total,
200
+ page: numPage,
201
+ pageSize: numPageSize
202
+ };
203
+ } catch (error) {
204
+ Logger.error('getList 执行失败:', error);
205
+ throw error;
206
+ }
207
+ }
208
+
209
+ // 私有方法:获取所有记录(支持传入连接对象)
210
+ async #getAllWithConn(table, options = {}, conn = null) {
211
+ if (!table || typeof table !== 'string') {
212
+ throw new Error('表名是必需的');
213
+ }
214
+
215
+ const { where = {}, fields = '*', leftJoins = [], orderBy = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
216
+
217
+ try {
218
+ const filteredWhere = this.#addDefaultStateFilter(where);
219
+ const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere);
220
+
221
+ leftJoins.forEach((join) => {
222
+ if (typeof join === 'string') {
223
+ const parts = join.split(' ON ');
224
+ if (parts.length === 2) builder.leftJoin(parts[0].trim(), parts[1].trim());
225
+ } else if (join && typeof join === 'object' && join.table && join.on) {
226
+ builder.leftJoin(join.table, join.on);
227
+ }
228
+ });
229
+
230
+ if (Array.isArray(orderBy) && orderBy.length > 0) builder.orderBy(orderBy);
231
+
232
+ const { sql: q, params } = builder.toSelectSql();
233
+ const result = await this.#executeWithConn(q, params, conn);
234
+ return Array.isArray(result) ? result : [];
235
+ } catch (error) {
236
+ Logger.error('getAll 执行失败:', error);
237
+ throw error;
238
+ }
239
+ }
240
+
241
+ // 私有方法:插入数据(支持传入连接对象)
242
+ async #insDataWithConn(table, data, conn = null) {
243
+ if (!table || typeof table !== 'string') {
244
+ throw new Error('表名是必需的');
245
+ }
246
+ if (!data) {
247
+ throw new Error('插入数据是必需的');
248
+ }
249
+ try {
250
+ const processedData = await this.#processDataForInsert(data);
251
+ const builder = createQueryBuilder();
252
+ const { sql: q, params } = builder.toInsertSql(table, processedData);
253
+ return await this.#executeWithConn(q, params, conn);
254
+ } catch (error) {
255
+ Logger.error('insData 执行失败:', error);
256
+ throw error;
257
+ }
258
+ }
259
+
260
+ // 私有方法:更新数据(支持传入连接对象)
261
+ async #updDataWithConn(table, data, where, conn = null) {
262
+ if (!table || typeof table !== 'string') {
263
+ throw new Error('表名是必需的');
264
+ }
265
+ if (!data || typeof data !== 'object') {
266
+ throw new Error('更新数据是必需的');
267
+ }
268
+ if (!where) {
269
+ throw new Error('更新操作需要 WHERE 条件');
270
+ }
271
+ try {
272
+ const filteredData = Object.fromEntries(Object.entries(data).filter(([key, value]) => value !== undefined && !['id', 'created_at', 'deleted_at'].includes(key)));
273
+ const updateData = { ...filteredData, updated_at: Date.now() };
274
+ const builder = createQueryBuilder().where(where);
275
+ const { sql: q, params } = builder.toUpdateSql(table, updateData);
276
+ return await this.#executeWithConn(q, params, conn);
277
+ } catch (error) {
278
+ Logger.error('updData 执行失败:', error);
279
+ throw error;
280
+ }
281
+ }
282
+
283
+ // 私有方法:删除数据(支持传入连接对象)
284
+ async #delDataWithConn(table, where, conn = null) {
285
+ if (!table || typeof table !== 'string') {
286
+ throw new Error('表名是必需的');
287
+ }
288
+ if (!where) {
289
+ throw new Error('删除操作需要 WHERE 条件');
290
+ }
291
+ try {
292
+ const builder = createQueryBuilder().where(where);
293
+ const { sql: q, params } = builder.toDeleteSql(table);
294
+ return await this.#executeWithConn(q, params, conn);
295
+ } catch (error) {
296
+ Logger.error('delData 执行失败:', error);
297
+ throw error;
298
+ }
299
+ }
300
+
301
+ // 私有方法:软删除数据(支持传入连接对象)
302
+ async #delData2WithConn(table, where, conn = null) {
303
+ if (!table || typeof table !== 'string') {
304
+ throw new Error('表名是必需的');
305
+ }
306
+ if (!where) {
307
+ throw new Error('软删除操作需要 WHERE 条件');
308
+ }
309
+ try {
310
+ const updateData = { state: 2, updated_at: Date.now() };
311
+ const builder = createQueryBuilder().where(where);
312
+ const { sql: q, params } = builder.toUpdateSql(table, updateData);
313
+ return await this.#executeWithConn(q, params, conn);
314
+ } catch (error) {
315
+ Logger.error('delData2 执行失败:', error);
316
+ throw error;
317
+ }
318
+ }
319
+
320
+ // 私有方法:批量插入(支持传入连接对象)
321
+ async #insBatchWithConn(table, dataArray, conn = null) {
322
+ if (!table || typeof table !== 'string') {
323
+ throw new Error('表名是必需的');
324
+ }
325
+ if (!Array.isArray(dataArray) || dataArray.length === 0) {
326
+ throw new Error('批量插入数据不能为空');
327
+ }
328
+ try {
329
+ const processedDataArray = await this.#processDataForInsert(dataArray);
330
+ const builder = createQueryBuilder();
331
+ const { sql: q, params } = builder.toInsertSql(table, processedDataArray);
332
+ return await this.#executeWithConn(q, params, conn);
333
+ } catch (error) {
334
+ Logger.error('insBatch 执行失败:', error);
335
+ throw error;
336
+ }
337
+ }
338
+
339
+ // 私有方法:获取记录总数(支持传入连接对象)
340
+ async #getCountWithConn(table, options = {}, conn = null) {
341
+ if (!table || typeof table !== 'string') {
342
+ throw new Error('表名是必需的');
343
+ }
344
+ const { where = {}, leftJoins = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
345
+ try {
346
+ const filteredWhere = this.#addDefaultStateFilter(where);
347
+ const builder = createQueryBuilder().from(table).where(filteredWhere);
348
+
349
+ leftJoins.forEach((join) => {
350
+ if (typeof join === 'string') {
351
+ const parts = join.split(' ON ');
352
+ if (parts.length === 2) builder.leftJoin(parts[0].trim(), parts[1].trim());
353
+ } else if (join && typeof join === 'object' && join.table && join.on) {
354
+ builder.leftJoin(join.table, join.on);
355
+ }
356
+ });
357
+
358
+ const { sql: q, params } = builder.toCountSql();
359
+ const result = await this.#executeWithConn(q, params, conn);
360
+ return result[0]?.total || 0;
361
+ } catch (error) {
362
+ Logger.error('getCount 执行失败:', error);
363
+ throw error;
364
+ }
365
+ }
366
+
367
+ // 执行原始 SQL - 核心方法
368
+ async execute(sql, params = []) {
369
+ return await this.#executeWithConn(sql, params);
370
+ }
371
+
372
+ // 获取单条记录详情
373
+ async getDetail(table, options = {}) {
374
+ return await this.#getDetailWithConn(table, options);
375
+ }
376
+
377
+ // 获取列表(支持分页)
378
+ async getList(table, options = {}) {
379
+ return await this.#getListWithConn(table, options);
380
+ }
381
+
382
+ // 获取所有记录
383
+ async getAll(table, options = {}) {
384
+ return await this.#getAllWithConn(table, options);
385
+ }
386
+
387
+ // 插入数据 - 增强版
388
+ async insData(table, data) {
389
+ return await this.#insDataWithConn(table, data);
390
+ }
391
+
392
+ // 更新数据 - 增强版
393
+ async updData(table, data, where) {
394
+ return await this.#updDataWithConn(table, data, where);
395
+ }
396
+
397
+ // 删除数据
398
+ async delData(table, where) {
399
+ return await this.#delDataWithConn(table, where);
400
+ }
401
+
402
+ // 软删除数据
403
+ async delData2(table, where) {
404
+ return await this.#delData2WithConn(table, where);
405
+ }
406
+
407
+ // 批量插入 - 增强版
408
+ async insBatch(table, dataArray) {
409
+ return await this.#insBatchWithConn(table, dataArray);
410
+ }
411
+
412
+ // 获取记录总数
413
+ async getCount(table, options = {}) {
414
+ return await this.#getCountWithConn(table, options);
415
+ }
416
+
417
+ // 事务处理
418
+ async trans(callback) {
419
+ if (typeof callback !== 'function') {
420
+ throw new Error('事务回调函数是必需的');
421
+ }
422
+ try {
423
+ const result = await this.#sql.begin(async (tx) => {
424
+ const txMethods = {
425
+ query: async (query, params = []) => this.#executeWithConn(query, params, tx),
426
+ execute: async (query, params = []) => this.#executeWithConn(query, params, tx),
427
+
428
+ getDetail: async (table, options = {}) => this.#getDetailWithConn(table, options, tx),
429
+ getList: async (table, options = {}) => this.#getListWithConn(table, options, tx),
430
+ getAll: async (table, options = {}) => this.#getAllWithConn(table, options, tx),
431
+ insData: async (table, data) => this.#insDataWithConn(table, data, tx),
432
+ updData: async (table, data, where) => this.#updDataWithConn(table, data, where, tx),
433
+ delData: async (table, where) => this.#delDataWithConn(table, where, tx),
434
+ delData2: async (table, where) => this.#delData2WithConn(table, where, tx),
435
+ getCount: async (table, options = {}) => this.#getCountWithConn(table, options, tx),
436
+ insBatch: async (table, dataArray) => this.#insBatchWithConn(table, dataArray, tx)
437
+ };
438
+ return await callback(txMethods);
439
+ });
440
+ return result;
441
+ } catch (error) {
442
+ Logger.info('事务已回滚');
443
+ throw error;
444
+ }
445
+ }
446
+
447
+ // 获取连接池状态
448
+ getPoolStatus() {
449
+ return {
450
+ activeConnections: 0,
451
+ totalConnections: 0,
452
+ idleConnections: 0,
453
+ taskQueueSize: 0
454
+ };
455
+ }
456
+
457
+ // 关闭连接池
458
+ async close() {
459
+ if (this.#sql) {
460
+ try {
461
+ await this.#sql.close();
462
+ Logger.info('数据库连接已关闭');
463
+ } catch (error) {
464
+ Logger.error('关闭数据库连接失败:', error);
465
+ throw error;
466
+ }
467
+ }
468
+ }
469
+ }
470
+
471
+ export default SqlManager;
package/utils/tool.js ADDED
@@ -0,0 +1,31 @@
1
+ import { omitFields } from './index.js';
2
+
3
+ // 工具类:通过构造函数注入 befly
4
+ export class Tool {
5
+ constructor(befly) {
6
+ this.befly = befly;
7
+ }
8
+
9
+ async updData(data, now = Date.now()) {
10
+ const cleaned = omitFields(data ?? {}, ['id', 'created_at', 'deleted_at'], [undefined]);
11
+ return { ...cleaned, updated_at: now };
12
+ }
13
+
14
+ async insData(data, now = Date.now()) {
15
+ const genId = async () => await this.befly.redis.genTimeID();
16
+
17
+ if (Array.isArray(data)) {
18
+ return await Promise.all(
19
+ data.map(async (item) => ({
20
+ ...omitFields(item ?? {}, [], [undefined]),
21
+ id: await genId(),
22
+ created_at: now,
23
+ updated_at: now
24
+ }))
25
+ );
26
+ } else {
27
+ const cleaned = omitFields(data ?? {}, [], [undefined]);
28
+ return { ...cleaned, id: await genId(), created_at: now, updated_at: now };
29
+ }
30
+ }
31
+ }
package/utils/validate.js CHANGED
@@ -1,96 +1,6 @@
1
- import { isType } from './util.js';
2
-
3
- // 验证字段名称是否为中文、数字、字母
4
- const validateFieldName = (name) => {
5
- const nameRegex = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/;
6
- return nameRegex.test(name);
7
- };
8
-
9
- // 验证字段类型是否为指定的四种类型之一
10
- const validateFieldType = (type) => {
11
- const validTypes = ['string', 'number', 'text', 'array'];
12
- return validTypes.includes(type);
13
- };
14
-
15
- // 验证最小值/最大值是否为null或数字
16
- const validateMinMax = (value) => {
17
- return value === 'null' || (!isNaN(parseFloat(value)) && isFinite(parseFloat(value)));
18
- };
19
-
20
- // 验证默认值是否为null、字符串或数字
21
- const validateDefaultValue = (value) => {
22
- if (value === 'null') return true;
23
- // 检查是否为数字
24
- if (!isNaN(parseFloat(value)) && isFinite(parseFloat(value))) return true;
25
- // 其他情况视为字符串,都是有效的
26
- return true;
27
- };
28
-
29
- // 验证索引标识是否为0或1
30
- const validateIndex = (value) => {
31
- return value === '0' || value === '1';
32
- };
33
-
34
- // 验证正则表达式是否有效
35
- const validateRegex = (value) => {
36
- if (value === 'null') return true;
37
- try {
38
- new RegExp(value);
39
- return true;
40
- } catch (e) {
41
- return false;
42
- }
43
- };
44
-
45
- // 专门用于处理⚡分隔的字段规则
46
- const parseFieldRule = (rule) => {
47
- const allParts = rule.split('⚡');
48
-
49
- // 必须包含7个部分:显示名⚡类型⚡最小值⚡最大值⚡默认值⚡是否索引⚡正则约束
50
- if (allParts.length !== 7) {
51
- throw new Error(`字段规则格式错误,必须包含7个部分,当前包含${allParts.length}个部分`);
52
- }
53
-
54
- // 验证各个部分的格式
55
- const [name, type, minValue, maxValue, defaultValue, isIndex, regexConstraint] = allParts;
56
-
57
- // 第1个值:名称必须为中文、数字、字母
58
- if (!validateFieldName(name)) {
59
- throw new Error(`字段名称 "${name}" 格式错误,必须为中文、数字、字母`);
60
- }
61
-
62
- // 第2个值:字段类型必须为string,number,text,array之一
63
- if (!validateFieldType(type)) {
64
- throw new Error(`字段类型 "${type}" 格式错误,必须为string、number、text、array之一`);
65
- }
66
-
67
- // 第3个值:最小值必须为null或数字
68
- if (!validateMinMax(minValue)) {
69
- throw new Error(`最小值 "${minValue}" 格式错误,必须为null或数字`);
70
- }
71
-
72
- // 第4个值:最大值必须为null或数字
73
- if (!validateMinMax(maxValue)) {
74
- throw new Error(`最大值 "${maxValue}" 格式错误,必须为null或数字`);
75
- }
76
-
77
- // 第5个值:默认值必须为null、字符串或数字
78
- if (!validateDefaultValue(defaultValue)) {
79
- throw new Error(`默认值 "${defaultValue}" 格式错误,必须为null、字符串或数字`);
80
- }
81
-
82
- // 第6个值:是否创建索引必须为0或1
83
- if (!validateIndex(isIndex)) {
84
- throw new Error(`索引标识 "${isIndex}" 格式错误,必须为0或1`);
85
- }
86
-
87
- // 第7个值:必须为null或正则表达式
88
- if (!validateRegex(regexConstraint)) {
89
- throw new Error(`正则约束 "${regexConstraint}" 格式错误,必须为null或有效的正则表达式`);
90
- }
1
+ import { isType, parseFieldRule } from './index.js';
91
2
 
92
- return allParts;
93
- };
3
+ // 移除本文件重复实现,统一复用 index.js 导出的校验函数与 parseFieldRule
94
4
 
95
5
  /**
96
6
  * 验证器类
package/.gitignore DELETED
@@ -1,94 +0,0 @@
1
- # Logs
2
- logs
3
- *.log
4
- npm-debug.log*
5
- esdata
6
- data
7
- .cache
8
- lab
9
- labs
10
- cache
11
- .VSCodeCounter
12
- oclif.manifest.json
13
- dist
14
- report.html
15
- dist-ssr
16
- *.local
17
- .vscode-test/
18
- *.vsix
19
- out
20
- CHANGELOG.md
21
- note.md
22
- .changeset
23
- addons2
24
- pnpm-lock.yaml
25
- bun.lock
26
- other
27
-
28
- # Editor directories and files
29
- .vscode/*
30
- .cache
31
- .yicode/auto-imports.d.ts
32
- !.vscode/extensions.json
33
- .idea
34
- .DS_Store
35
- *.suo
36
- *.ntvs*
37
- *.njsproj
38
- *.sln
39
- *.sw?
40
-
41
- # Runtime data
42
- pids
43
- *.pid
44
- *.seed
45
-
46
- # Directory for instrumented libs generated by jscoverage/JSCover
47
- lib-cov
48
-
49
- # Coverage directory used by tools like istanbul
50
- coverage
51
-
52
- # nyc test coverage
53
- .nyc_output
54
-
55
- # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
56
- .grunt
57
-
58
- # node-waf configuration
59
- .lock-wscript
60
-
61
- # Compiled binary addons (http://nodejs.org/api/addons.html)
62
- build/Release
63
-
64
- # Dependency directories
65
- node_modules
66
- jspm_packages
67
-
68
- # Optional npm cache directory
69
- .npm
70
-
71
- # Optional REPL history
72
- .node_repl_history
73
-
74
- # 0x
75
- profile-*
76
-
77
- # mac files
78
- .DS_Store
79
-
80
- # vim swap files
81
- *.swp
82
-
83
- # webstorm
84
- .idea
85
-
86
- # vscode
87
- .vscode
88
- *code-workspace
89
-
90
- # clinic
91
- profile*
92
- *clinic*
93
- *flamegraph*
94
- logs