@jayfong/x-server 2.12.14 → 2.12.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/lib/_cjs/cli/api_generator.js +20 -54
  2. package/lib/_cjs/cli/build_util.js +16 -45
  3. package/lib/_cjs/cli/cli.js +17 -29
  4. package/lib/_cjs/cli/deploy_util.js +0 -12
  5. package/lib/_cjs/cli/dev_util.js +18 -0
  6. package/lib/_cjs/cli/env_util.js +16 -49
  7. package/lib/_cjs/cli/{register.js → esbuild_register.js} +0 -1
  8. package/lib/_cjs/cli/template_util.js +26 -29
  9. package/lib/_cjs/core/define_bus.js +9 -6
  10. package/lib/_cjs/core/define_cron.js +0 -2
  11. package/lib/_cjs/core/define_handler.js +10 -12
  12. package/lib/_cjs/core/define_hook.js +0 -2
  13. package/lib/_cjs/core/define_server.js +0 -2
  14. package/lib/_cjs/core/define_task.js +7 -13
  15. package/lib/_cjs/core/get_handler_url.js +1 -2
  16. package/lib/_cjs/core/handler.js +8 -29
  17. package/lib/_cjs/core/http_error.js +0 -3
  18. package/lib/_cjs/core/http_header.js +8 -12
  19. package/lib/_cjs/core/server.js +8 -41
  20. package/lib/_cjs/core/types.js +0 -7
  21. package/lib/_cjs/index.js +0 -72
  22. package/lib/_cjs/plugins/cors.js +0 -7
  23. package/lib/_cjs/plugins/file_parser.js +2 -9
  24. package/lib/_cjs/plugins/form_body_parser.js +0 -6
  25. package/lib/_cjs/plugins/ws_parser.js +0 -6
  26. package/lib/_cjs/plugins/xml_parser.js +0 -12
  27. package/lib/_cjs/services/cache.js +18 -58
  28. package/lib/_cjs/services/captcha.js +0 -14
  29. package/lib/_cjs/services/dingtalk.js +0 -14
  30. package/lib/_cjs/services/dispose.js +0 -9
  31. package/lib/_cjs/services/emoji.js +2 -5
  32. package/lib/_cjs/services/jwt.js +0 -21
  33. package/lib/_cjs/services/log.js +0 -17
  34. package/lib/_cjs/services/mail.js +0 -9
  35. package/lib/_cjs/services/pay.js +9 -40
  36. package/lib/_cjs/services/rate_limit.js +0 -12
  37. package/lib/_cjs/services/redis.js +0 -4
  38. package/lib/_cjs/services/request.js +0 -5
  39. package/lib/_cjs/services/sensitive_words.js +13 -19
  40. package/lib/_cjs/x.js +0 -11
  41. package/lib/cli/api_generator.js +20 -42
  42. package/lib/cli/build_util.js +16 -19
  43. package/lib/cli/cli.js +17 -14
  44. package/lib/cli/deploy_util.js +0 -4
  45. package/lib/cli/dev_util.d.ts +8 -0
  46. package/lib/cli/dev_util.js +12 -0
  47. package/lib/cli/env_util.js +16 -37
  48. package/lib/cli/template_util.d.ts +1 -0
  49. package/lib/cli/template_util.js +26 -14
  50. package/lib/core/define_bus.js +7 -5
  51. package/lib/core/define_handler.js +10 -9
  52. package/lib/core/define_task.js +7 -6
  53. package/lib/core/get_handler_url.js +2 -1
  54. package/lib/core/handler.js +9 -22
  55. package/lib/core/http_header.js +6 -9
  56. package/lib/core/server.js +8 -31
  57. package/lib/core/types.js +0 -7
  58. package/lib/index.js +4 -3
  59. package/lib/plugins/cors.js +0 -3
  60. package/lib/plugins/file_parser.js +2 -5
  61. package/lib/plugins/form_body_parser.js +0 -3
  62. package/lib/plugins/ws_parser.js +0 -3
  63. package/lib/plugins/xml_parser.js +0 -10
  64. package/lib/services/cache.js +18 -52
  65. package/lib/services/captcha.js +0 -7
  66. package/lib/services/dingtalk.js +0 -7
  67. package/lib/services/dispose.js +0 -5
  68. package/lib/services/emoji.js +2 -3
  69. package/lib/services/jwt.js +0 -13
  70. package/lib/services/log.js +0 -8
  71. package/lib/services/mail.js +0 -3
  72. package/lib/services/pay.js +9 -30
  73. package/lib/services/rate_limit.js +0 -8
  74. package/lib/services/redis.js +0 -1
  75. package/lib/services/request.js +0 -1
  76. package/lib/services/sensitive_words.js +13 -15
  77. package/package.json +3 -3
  78. /package/lib/cli/{register.d.ts → esbuild_register.d.ts} +0 -0
  79. /package/lib/cli/{register.js → esbuild_register.js} +0 -0
@@ -8,7 +8,6 @@ import { dedent, uniq } from 'vtils';
8
8
  export class BuildUtil {
9
9
  static async build(options) {
10
10
  var _options$minify, _options$inlineEnvs;
11
-
12
11
  const mainFile = path.join(options.cwd, 'src/main.ts');
13
12
  const pkgFile = path.join(options.cwd, 'package.json');
14
13
  const pkgContent = await fs.readJson(pkgFile);
@@ -20,8 +19,9 @@ export class BuildUtil {
20
19
  const distNoInstallFile = path.join(distDir, 'no_install.lock');
21
20
  const distBundleFile = path.join(tempDir, `app.${pkgContent.version}.tgz`);
22
21
  await fs.ensureDir(distDir);
23
- await fs.ensureDir(tempDir); // 处理 external
22
+ await fs.ensureDir(tempDir);
24
23
 
24
+ // 处理 external
25
25
  const external = uniq(options.external || []);
26
26
  const externalWithVersions = (await Promise.all(external.map(async item => {
27
27
  try {
@@ -39,17 +39,17 @@ export class BuildUtil {
39
39
  version: ''
40
40
  };
41
41
  }
42
- }))).filter(item => !!item.version); // fix: hexoid 构建问题
42
+ }))).filter(item => !!item.version);
43
43
 
44
+ // fix: hexoid 构建问题
44
45
  try {
45
46
  const hexoidPackageJsonFile = require.resolve('hexoid/package.json');
46
-
47
47
  const pkg = await fs.readJson(hexoidPackageJsonFile);
48
48
  delete pkg.module;
49
49
  await fs.writeJson(hexoidPackageJsonFile, pkg);
50
- } catch {} // 构建
51
-
50
+ } catch {}
52
51
 
52
+ // 构建
53
53
  await fs.emptyDir(distDir);
54
54
  await esbuild.build({
55
55
  entryPoints: [mainFile],
@@ -75,7 +75,6 @@ export class BuildUtil {
75
75
  },
76
76
  plugins: [{
77
77
  name: 'extract-assets',
78
-
79
78
  setup(build) {
80
79
  build.onLoad({
81
80
  filter: /\/svg-captcha\/lib\/option-manager\.js$/
@@ -114,30 +113,29 @@ export class BuildUtil {
114
113
  };
115
114
  });
116
115
  }
117
-
118
116
  }]
119
- }); // 兼容 prisma
117
+ });
120
118
 
119
+ // 兼容 prisma
121
120
  const prismaSchemaFile = path.join(options.cwd, 'src/db/schema.prisma');
122
-
123
121
  if (await fs.pathExists(prismaSchemaFile)) {
124
122
  // 复制 schema.prisma
125
123
  const distPrismaSchemaFile = path.join(distDir, path.basename(prismaSchemaFile));
126
- await fs.copyFile(prismaSchemaFile, distPrismaSchemaFile); // 复制查询引擎
124
+ await fs.copyFile(prismaSchemaFile, distPrismaSchemaFile);
127
125
 
126
+ // 复制查询引擎
128
127
  const libqueryEngineFiles = await globby('libquery_engine-*', {
129
128
  cwd: path.join(options.cwd, 'node_modules/.prisma/client'),
130
129
  ignore: ['libquery_engine-{darwin,windows}*'],
131
130
  onlyFiles: true,
132
131
  absolute: true
133
132
  });
134
-
135
133
  for (const libqueryEngineFile of libqueryEngineFiles) {
136
134
  await fs.copyFile(libqueryEngineFile, path.join(distDir, path.basename(libqueryEngineFile)));
137
135
  }
138
- } // 写入 pkg
139
-
136
+ }
140
137
 
138
+ // 写入 pkg
141
139
  const deps = externalWithVersions.reduce((res, item) => {
142
140
  res[item.name] = item.version;
143
141
  return res;
@@ -147,8 +145,9 @@ export class BuildUtil {
147
145
  version: pkgContent.version,
148
146
  dependencies: deps
149
147
  };
150
- await fs.writeJSON(distPkgFile, distPkgContent); // 写入 pm2 配置
148
+ await fs.writeJSON(distPkgFile, distPkgContent);
151
149
 
150
+ // 写入 pm2 配置
152
151
  await fs.writeFile(distPm2File, dedent`
153
152
  module.exports = {
154
153
  apps: [
@@ -160,7 +159,6 @@ export class BuildUtil {
160
159
  ],
161
160
  }
162
161
  `);
163
-
164
162
  if (externalWithVersions.length) {
165
163
  if (options.noInstall) {
166
164
  await fs.writeFile(distNoInstallFile, '');
@@ -177,10 +175,9 @@ export class BuildUtil {
177
175
  }
178
176
  });
179
177
  }
180
- } // 打包为 tgz
181
-
178
+ }
182
179
 
180
+ // 打包为 tgz
183
181
  await compressing.tgz.compressDir(distDir, distBundleFile);
184
182
  }
185
-
186
183
  }
package/lib/cli/cli.js CHANGED
@@ -6,6 +6,7 @@ import { ApiGenerator } from "./api_generator";
6
6
  import { BuildUtil } from "./build_util";
7
7
  import { castArray, debounce } from 'vtils';
8
8
  import { DeployUtil } from "./deploy_util";
9
+ import { DevUtil } from "./dev_util";
9
10
  import { EnvUtil } from "./env_util";
10
11
  import { generateManyIndex } from 'vscode-generate-index-standalone';
11
12
  import { TemplateUtil } from "./template_util";
@@ -17,17 +18,19 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
17
18
  }).positional('npc', {
18
19
  describe: 'npc 地址及密钥,格式:地址@密钥',
19
20
  type: 'string'
21
+ }).positional('tsx', {
22
+ describe: '使用 tsx 运行',
23
+ type: 'boolean'
20
24
  }), async argv => {
21
25
  process.env.NODE_ENV = 'development';
22
26
  let lastRun;
23
-
24
27
  const run = () => {
25
- lastRun = execa('node', ['-r', require.resolve("./register"), 'src/main.ts'], {
28
+ lastRun = DevUtil.runFile({
29
+ file: 'src/main.ts',
26
30
  cwd: process.cwd(),
27
- stdio: 'inherit'
31
+ runner: argv.tsx ? 'tsx' : 'esbuild-register'
28
32
  });
29
33
  };
30
-
31
34
  if (argv.npc) {
32
35
  const [server, vkey] = argv.npc.split('@');
33
36
  execa('docker', ['run', '--rm', 'ffdfgdfg/npc:v0.26.9', `-server=${server}`, `-vkey=${vkey}`, '-type=tcp'], {
@@ -35,10 +38,10 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
35
38
  stdio: 'inherit'
36
39
  });
37
40
  }
38
-
39
41
  await TemplateUtil.init(process.cwd());
40
42
  const watcher = chokidar.watch(['src', '.env', '.env.*'], {
41
- cwd: process.cwd()
43
+ cwd: process.cwd(),
44
+ ignored: '**/*.test.*'
42
45
  });
43
46
  watcher.on('all', debounce(async () => {
44
47
  await EnvUtil.importFile({
@@ -58,7 +61,6 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
58
61
  console.log(`✔️ 索引文件已更新: ${filePath}`);
59
62
  }
60
63
  });
61
-
62
64
  if (lastRun) {
63
65
  console.log('==== 重启服务中 ====');
64
66
  lastRun.kill();
@@ -124,15 +126,14 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
124
126
  await EnvUtil.importFile({
125
127
  cwd: process.cwd(),
126
128
  file: '.env'
127
- }); // 之所以能成功是因为 Prisma 用的 dotenv 默认不会覆盖已经设置的环境变量
129
+ });
130
+ // 之所以能成功是因为 Prisma 用的 dotenv 默认不会覆盖已经设置的环境变量
128
131
  // https://github.com/motdotla/dotenv#override
129
-
130
132
  process.env.DATABASE_URL = process.env.DATABASE_ACTION_URL || process.env.DATABASE_URL;
131
133
  await execa('tnpx', ['prisma', ...argv._.slice(1), '--schema', 'src/db/schema.prisma'], {
132
134
  cwd: process.cwd(),
133
135
  stdio: 'inherit'
134
136
  });
135
-
136
137
  if (!argv.production) {
137
138
  await TemplateUtil.initModels(process.cwd());
138
139
  }
@@ -144,6 +145,9 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
144
145
  describe: '是否生产模式',
145
146
  type: 'boolean',
146
147
  default: false
148
+ }).positional('tsx', {
149
+ describe: '使用 tsx 运行',
150
+ type: 'boolean'
147
151
  }), async argv => {
148
152
  process.env.NODE_ENV = argv.production ? 'production' : 'development';
149
153
  await EnvUtil.importFile({
@@ -151,16 +155,16 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
151
155
  file: '.env'
152
156
  });
153
157
  process.env.DATABASE_URL = process.env.DATABASE_ACTION_URL || process.env.DATABASE_URL;
154
-
155
158
  if (!argv.script) {
156
159
  await execa('tnpx', ['select-run'], {
157
160
  cwd: process.cwd(),
158
161
  stdio: 'inherit'
159
162
  });
160
163
  } else {
161
- await execa('node', ['-r', require.resolve("./register"), argv.script], {
164
+ await DevUtil.runFile({
165
+ file: argv.script,
162
166
  cwd: process.cwd(),
163
- stdio: 'inherit'
167
+ runner: argv.tsx ? 'tsx' : 'esbuild-register'
164
168
  });
165
169
  }
166
170
  }).command('deploy [type]', '部署', _ => _.positional('type', {
@@ -172,7 +176,6 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
172
176
  cwd: process.cwd(),
173
177
  file: '.env.deploy'
174
178
  });
175
-
176
179
  if (argv.type === 'env') {
177
180
  const appEnv = await EnvUtil.parseFileAsMap({
178
181
  cwd: process.cwd(),
@@ -6,11 +6,9 @@ export class DeployUtil {
6
6
  static async deploy(options) {
7
7
  const pkgContent = await fs.readJson(path.join(options.cwd, 'package.json'));
8
8
  const appFile = path.join(options.cwd, `temp/app.${pkgContent.version}.tgz`);
9
-
10
9
  if (!(await fs.pathExists(appFile))) {
11
10
  throw new Error('请先构建');
12
11
  }
13
-
14
12
  const ssh = new NodeSSH();
15
13
  await ssh.connect({
16
14
  host: options.host,
@@ -44,7 +42,6 @@ export class DeployUtil {
44
42
  });
45
43
  ssh.dispose();
46
44
  }
47
-
48
45
  static async deployEnv(options) {
49
46
  const ssh = new NodeSSH();
50
47
  await ssh.connect({
@@ -68,5 +65,4 @@ export class DeployUtil {
68
65
  });
69
66
  ssh.dispose();
70
67
  }
71
-
72
68
  }
@@ -0,0 +1,8 @@
1
+ import execa from 'execa';
2
+ export declare class DevUtil {
3
+ static runFile(options: {
4
+ file: string;
5
+ cwd: string;
6
+ runner: 'esbuild-register' | 'tsx';
7
+ }): execa.ExecaChildProcess<string>;
8
+ }
@@ -0,0 +1,12 @@
1
+ import execa from 'execa';
2
+ export class DevUtil {
3
+ static runFile(options) {
4
+ return options.runner === 'tsx' ? execa('tsx', [options.file], {
5
+ cwd: options.cwd,
6
+ stdio: 'inherit'
7
+ }) : execa('node', ['-r', require.resolve("./esbuild_register"), options.file], {
8
+ cwd: options.cwd,
9
+ stdio: 'inherit'
10
+ });
11
+ }
12
+ }
@@ -9,27 +9,21 @@ export class EnvUtil {
9
9
  const [devValue, prodValue] = value.split(/\s+\|\|\s+/);
10
10
  return devOrProd(() => EnvUtil.normalizeValue(devValue), () => EnvUtil.normalizeValue(prodValue));
11
11
  }
12
-
13
12
  return value === 'true' ? true : value === 'false' ? false : value.startsWith('[') && value.endsWith(']') || value.startsWith('{') && value.endsWith('}') ? JSON.parse(value) : isNumeric(value) ? Number(value) : value;
14
13
  } else if (value && typeof value === 'object') {
15
14
  if (value.dev != null && value.prod != null) {
16
15
  return devOrProd(() => value.dev, () => value.prod);
17
16
  }
18
-
19
17
  return value;
20
18
  }
21
-
22
19
  return value;
23
20
  }
24
-
25
21
  static parseContent(src, isYaml = false) {
26
22
  const envs = [];
27
-
28
23
  if (isYaml) {
29
24
  const envObj = yamlParse(src);
30
25
  Object.keys(envObj).forEach(key => {
31
26
  var _src$match;
32
-
33
27
  envs.push({
34
28
  key: key,
35
29
  value: this.normalizeValue(envObj[key]),
@@ -38,56 +32,54 @@ export class EnvUtil {
38
32
  });
39
33
  } else {
40
34
  // https://github.com/andreialecu/dotenv/blob/feat-multiline/lib/main.js
41
- const multilineLineBreaks = true; // convert Buffers before splitting into lines and processing
35
+ const multilineLineBreaks = true;
42
36
 
37
+ // convert Buffers before splitting into lines and processing
43
38
  const lines = src.split(EnvUtil.NEWLINES_MATCH);
44
39
  let lastComment = '';
45
-
46
40
  for (let idx = 0; idx < lines.length; idx++) {
47
- let line = lines[idx]; // matching "KEY' and 'VAL' in 'KEY=VAL'
48
-
49
- const keyValueArr = line.match(EnvUtil.RE_INI_KEY_VAL); // matched?
41
+ let line = lines[idx];
50
42
 
43
+ // matching "KEY' and 'VAL' in 'KEY=VAL'
44
+ const keyValueArr = line.match(EnvUtil.RE_INI_KEY_VAL);
45
+ // matched?
51
46
  if (keyValueArr != null) {
52
- const key = keyValueArr[1]; // default undefined or missing values to empty string
53
-
47
+ const key = keyValueArr[1];
48
+ // default undefined or missing values to empty string
54
49
  let val = keyValueArr[2] || '';
55
50
  let end = val.length - 1;
56
51
  const isDoubleQuoted = val[0] === '"' && val[end] === '"';
57
52
  const isSingleQuoted = val[0] === "'" && val[end] === "'";
58
53
  const isMultilineDoubleQuoted = val[0] === '"' && (val.length === 1 || val[end] !== '"');
59
- const isMultilineSingleQuoted = val[0] === "'" && (val.length === 1 || val[end] !== "'"); // if parsing line breaks and the value starts with a quote
54
+ const isMultilineSingleQuoted = val[0] === "'" && (val.length === 1 || val[end] !== "'");
60
55
 
56
+ // if parsing line breaks and the value starts with a quote
61
57
  if (multilineLineBreaks && (isMultilineDoubleQuoted || isMultilineSingleQuoted)) {
62
58
  const quoteChar = isMultilineDoubleQuoted ? '"' : "'";
63
59
  val = val.substring(1);
64
-
65
60
  while (idx++ < lines.length - 1) {
66
61
  line = lines[idx];
67
62
  end = line.length - 1;
68
-
69
63
  if (line[end] === quoteChar) {
70
64
  val += EnvUtil.NEWLINE + line.substring(0, end);
71
65
  break;
72
66
  }
73
-
74
67
  val += EnvUtil.NEWLINE + line;
75
68
  }
76
-
77
- val = dedent(val); // if single or double quoted, remove quotes
69
+ val = dedent(val);
70
+ // if single or double quoted, remove quotes
78
71
  } else if (isSingleQuoted || isDoubleQuoted) {
79
- val = val.substring(1, end); // if double quoted, expand newlines
72
+ val = val.substring(1, end);
80
73
 
74
+ // if double quoted, expand newlines
81
75
  if (isDoubleQuoted) {
82
76
  val = val.replace(EnvUtil.RE_NEWLINES, EnvUtil.NEWLINE);
83
77
  }
84
-
85
78
  val = dedent(val);
86
79
  } else {
87
80
  // remove surrounding whitespace
88
81
  val = dedent(val).trim();
89
82
  }
90
-
91
83
  envs.push({
92
84
  key: key,
93
85
  value: EnvUtil.normalizeValue(val),
@@ -96,58 +88,47 @@ export class EnvUtil {
96
88
  lastComment = '';
97
89
  } else {
98
90
  const commentArr = line.match(EnvUtil.COMMENT_MATCH);
99
-
100
91
  if (commentArr) {
101
92
  lastComment = commentArr[1];
102
93
  }
103
94
  }
104
95
  }
105
96
  }
106
-
107
97
  return envs;
108
98
  }
109
-
110
99
  static async parseFile(options) {
111
100
  const envFile = path.join(options.cwd, options.file);
112
101
  const envYmlFile = path.join(options.cwd, `${options.file}.yml`);
113
102
  const envYamlFile = path.join(options.cwd, `${options.file}.yaml`);
114
103
  const file = (await fs.pathExists(envYmlFile)) ? envYmlFile : (await fs.pathExists(envYamlFile)) ? envYamlFile : (await fs.pathExists(envFile)) ? envFile : '';
115
-
116
104
  if (file) {
117
105
  const envContent = await fs.readFile(file, 'utf-8');
118
106
  const envs = EnvUtil.parseContent(envContent, /\.ya?ml$/i.test(file));
119
107
  return envs;
120
108
  }
121
-
122
109
  return [];
123
110
  }
124
-
125
111
  static async parseFileAsMap(options) {
126
112
  const envs = await EnvUtil.parseFile(options);
127
113
  const envsObj = {};
128
-
129
114
  for (const env of envs) {
130
115
  envsObj[env.key] = env.value;
131
116
  }
132
-
133
117
  return envsObj;
134
118
  }
135
-
136
119
  static async importFile(options) {
137
120
  const envs = await EnvUtil.parseFile(options);
138
121
  const envsObj = {};
139
-
140
122
  for (const env of envs) {
141
123
  envsObj[env.key] = env.value;
142
124
  process.env[env.key] = env.value;
143
125
  }
144
-
145
- process.env.X_SERVER_ENVS = JSON.stringify({ // @ts-ignore
126
+ process.env.X_SERVER_ENVS = JSON.stringify({
127
+ // @ts-ignore
146
128
  ...JSON.parse(process.env.X_SERVER_ENVS || '{}'),
147
129
  ...envsObj
148
130
  });
149
131
  }
150
-
151
132
  static makeTypes(envs) {
152
133
  return dedent`
153
134
  declare namespace NodeJS {
@@ -168,14 +149,12 @@ export class EnvUtil {
168
149
  }
169
150
  `;
170
151
  }
171
-
172
152
  static async outputTypes(options) {
173
153
  const envs = await EnvUtil.parseFile(options);
174
154
  const outFile = path.join(options.cwd, options.outFile);
175
155
  const types = EnvUtil.makeTypes(envs);
176
156
  await fs.outputFile(outFile, types);
177
157
  }
178
-
179
158
  }
180
159
  EnvUtil.NEWLINE = '\n';
181
160
  EnvUtil.RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/;
@@ -15,4 +15,5 @@ export declare class TemplateUtil {
15
15
  * 初始化模型。
16
16
  */
17
17
  static initModels(cwd: string): Promise<void>;
18
+ static getPrismaClientTypeFile(): string;
18
19
  }
@@ -1,4 +1,5 @@
1
1
  import fs from 'fs-extra';
2
+ import globby from 'globby';
2
3
  import path from 'path';
3
4
  import { camelCase, dedent, snakeCase, upperFirst } from 'vtils';
4
5
  import { generateManyIndex } from 'vscode-generate-index-standalone';
@@ -9,32 +10,39 @@ export class TemplateUtil {
9
10
  static async init(cwd) {
10
11
  await Promise.all([this.initHelperPackage(cwd), this.initTasks(cwd)]);
11
12
  }
13
+
12
14
  /**
13
15
  * 初始化辅助包。
14
16
  */
15
-
16
-
17
17
  static async initHelperPackage(cwd) {
18
+ const prismaClientFile = TemplateUtil.getPrismaClientTypeFile();
18
19
  const fromDir = path.join(__dirname, 'templates');
19
20
  const toDir = path.join(cwd, 'node_modules/.x');
20
- await fs.copy(fromDir, toDir, {
21
- overwrite: true
21
+ const fromFiles = await globby('*', {
22
+ cwd: fromDir,
23
+ onlyFiles: true,
24
+ absolute: true
22
25
  });
26
+ await fs.ensureDir(toDir);
27
+ await Promise.all(fromFiles.map(async fromFile => {
28
+ const toFile = fromFile.replace(fromDir, toDir);
29
+ let fromFileContent = await fs.readFile(fromFile, 'utf-8');
30
+ fromFileContent = fromFileContent.replaceAll('../.prisma/client/index.d.ts', path.relative(toDir, prismaClientFile)).replaceAll('../../src', path.relative(toDir, `${cwd}/src`));
31
+ await fs.writeFile(toFile, fromFileContent);
32
+ }));
23
33
  await generateManyIndex({
24
34
  cwd: cwd,
25
35
  patterns: ['node_modules/.x/*.ts'],
26
36
  replaceFile: true
27
37
  });
28
38
  }
39
+
29
40
  /**
30
41
  * 初始化任务。
31
42
  */
32
-
33
-
34
43
  static async initTasks(cwd) {
35
44
  const tasksDir = path.join(cwd, 'src/tasks');
36
45
  const tasksIndexFile = path.join(tasksDir, 'index.ts');
37
-
38
46
  if (!(await fs.pathExists(tasksIndexFile))) {
39
47
  await fs.outputFile(tasksIndexFile, dedent`
40
48
  // @index(['./**/*.ts', '!**/*.test.ts', '!**/_*'], f => \`export * from '\${f.path}'\`)
@@ -42,23 +50,21 @@ export class TemplateUtil {
42
50
  `);
43
51
  }
44
52
  }
53
+
45
54
  /**
46
55
  * 初始化模型。
47
56
  */
48
-
49
-
50
57
  static async initModels(cwd) {
51
58
  const modelsDir = path.join(cwd, 'src/models');
52
59
  const indexFile = path.join(modelsDir, 'index.ts');
53
60
  const indexFile2 = path.join(cwd, 'node_modules/.x/models.ts');
54
- const prismaClientFile = path.join(cwd, 'node_modules/.prisma/client/index.d.ts');
61
+ const prismaClientFile = TemplateUtil.getPrismaClientTypeFile();
55
62
  const prismaClientFileContent = await fs.readFile(prismaClientFile, 'utf8');
56
63
  const modelNames = [...prismaClientFileContent.match(/(?<=const ModelName:).+?(?=\})/s)[0].matchAll(/(\S+?):/g)].map(match => camelCase(match[1]));
57
64
  await Promise.all(modelNames.map(async modelName => {
58
65
  const model_name = snakeCase(modelName);
59
66
  const ModelName = upperFirst(modelName);
60
67
  const modelFile = path.join(modelsDir, `${model_name}.ts`);
61
-
62
68
  if (!(await fs.pathExists(modelFile))) {
63
69
  await fs.outputFile(modelFile, dedent`
64
70
  import { ${ModelName}BaseModel } from '@jayfong/x-server'
@@ -69,19 +75,25 @@ export class TemplateUtil {
69
75
  `);
70
76
  }
71
77
  }));
72
-
73
78
  if (!(await fs.pathExists(indexFile))) {
74
79
  await fs.outputFile(indexFile, dedent`
75
80
  // @index(['./**/*.ts', '!**/*.test.ts', '!**/_*'], f => \`export * from '\${f.path}'\`)
76
81
  // @endindex
77
82
  `);
78
83
  }
79
-
80
84
  await generateManyIndex({
81
85
  cwd: cwd,
82
86
  patterns: [indexFile, indexFile2],
83
87
  replaceFile: true
84
88
  });
85
89
  }
86
-
90
+ static getPrismaClientTypeFile() {
91
+ // const prismaClientFile = path.join(
92
+ // cwd,
93
+ // 'node_modules/.prisma/client/index.d.ts',
94
+ // )
95
+ // 用 require.resolve 以兼容 pnpm
96
+ const prismaClientFile = require.resolve('@prisma/client/package.json').replace('@prisma/client/package.json', '.prisma/client/index.d.ts');
97
+ return prismaClientFile;
98
+ }
87
99
  }
@@ -1,18 +1,18 @@
1
1
  import { EventEmitter } from 'node:events';
2
-
3
2
  class EventBus extends EventEmitter {
4
3
  onReturnOff(event, listener) {
5
4
  this.on(event, listener);
6
5
  return () => this.off(event, listener);
7
6
  }
8
-
9
7
  }
10
-
11
8
  export function defineBus() {
12
9
  const eventEmitter = new EventBus();
13
10
  return eventEmitter;
14
- } // strict-event-emitter-types
11
+ }
12
+
13
+ // strict-event-emitter-types
15
14
  // https://github.com/bterlson/strict-event-emitter-types/blob/9ed45fc68a69f0b7f85ce4e5fe11ff53cc6a009e/src/index.ts
15
+
16
16
  // the overridden signatures need to be assignment compatible, but
17
17
  // due to how tuple types work[0] it's not possible to be assignment
18
18
  // compatible anymore. This hack fixes it with a unique symbol that
@@ -20,4 +20,6 @@ export function defineBus() {
20
20
  //
21
21
  // Unfortunately, this has the result of giving a poor error message when
22
22
  // you mix up types.
23
- // 0: https://github.com/Microsoft/TypeScript/issues/26013)
23
+ // 0: https://github.com/Microsoft/TypeScript/issues/26013)
24
+ // TODO: Stash under a symbol key once TS compiler bug is fixed
25
+ // EventEmitter method overrides
@@ -1,35 +1,36 @@
1
1
  import { Handler } from "./handler";
2
2
  export function defineHandler(options) {
3
- const handler = new Handler({ ...options,
3
+ const handler = new Handler({
4
+ ...options,
4
5
  requestMethod: 'POST'
5
6
  });
6
7
  return handler;
7
8
  }
8
9
  defineHandler.POST = defineHandler;
9
-
10
10
  defineHandler.GET = options => {
11
- const handler = new Handler({ ...options,
11
+ const handler = new Handler({
12
+ ...options,
12
13
  requestMethod: 'GET'
13
14
  });
14
15
  return handler;
15
16
  };
16
-
17
17
  defineHandler.FILE = options => {
18
- const handler = new Handler({ ...options,
18
+ const handler = new Handler({
19
+ ...options,
19
20
  requestMethod: 'FILE'
20
21
  });
21
22
  return handler;
22
23
  };
23
-
24
24
  defineHandler.WS = options => {
25
- const handler = new Handler({ ...options,
25
+ const handler = new Handler({
26
+ ...options,
26
27
  requestMethod: 'WS'
27
28
  });
28
29
  return handler;
29
30
  };
30
-
31
31
  defineHandler.XML = options => {
32
- const handler = new Handler({ ...options,
32
+ const handler = new Handler({
33
+ ...options,
33
34
  requestMethod: 'XML'
34
35
  });
35
36
  return handler;
@@ -4,7 +4,8 @@ import { ms } from 'vtils';
4
4
  import { x } from "../x";
5
5
  export function defineTask(options) {
6
6
  const queue = new Queue(options.name, {
7
- redis: { ...x.redis.options,
7
+ redis: {
8
+ ...x.redis.options,
8
9
  // https://github.com/OptimalBits/bull/issues/2186
9
10
  maxRetriesPerRequest: null,
10
11
  enableReadyCheck: false
@@ -28,7 +29,6 @@ export function defineSliceTask(options) {
28
29
  handle: async data => {
29
30
  const res = data.count ? await x.redis.multi([['lrange', data.redisKey, `-${data.count}`, '-1'], ['ltrim', data.redisKey, '0', `-${data.count + 1}`]]).exec() : await x.redis.multi([['lrange', data.redisKey, '0', '-1'], ['del', data.redisKey]]).exec();
30
31
  const list = res[0][1].reverse().map(item => JSON.parse(item));
31
-
32
32
  if (list.length) {
33
33
  return options.handle(list, data.key);
34
34
  }
@@ -44,9 +44,10 @@ export function defineSliceTask(options) {
44
44
  assert(duration != null || threshold != null, '参数 threshold 和 duration 必须至少设置 1 个');
45
45
  const redisKey = !key ? redisKeyPrefix : `${redisKeyPrefix}_${key}`;
46
46
  const res = await x.redis.multi([['llen', redisKey], ['lpush', redisKey, JSON.stringify(data)]]).exec();
47
- const count = parseInt(res[0][1], 10) + 1; // 仅时段
48
- // 1分钟内的合并推送
47
+ const count = parseInt(res[0][1], 10) + 1;
49
48
 
49
+ // 仅时段
50
+ // 1分钟内的合并推送
50
51
  if (duration != null) {
51
52
  if (count === 1) {
52
53
  await task.add({
@@ -56,7 +57,8 @@ export function defineSliceTask(options) {
56
57
  delay: duration
57
58
  });
58
59
  }
59
- } // 仅阈值
60
+ }
61
+ // 仅阈值
60
62
  // 满10条推送
61
63
  else if (threshold != null) {
62
64
  if (thresholdTimeout) {
@@ -70,7 +72,6 @@ export function defineSliceTask(options) {
70
72
  delay: thresholdTimeout
71
73
  });
72
74
  }
73
-
74
75
  if (count === threshold) {
75
76
  await task.add({
76
77
  key: key,