@domain.js/main 0.2.5 → 0.3.2

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/cli/index.js CHANGED
@@ -62,7 +62,7 @@ const makeDefineFile = async (modules, targetFile, isTS) => {
62
62
  else {
63
63
  content.push(`const ${variable} = require("./${name}")`);
64
64
  }
65
- _exports.push(`"${file2Module(name)}": ${variable},`);
65
+ _exports.push(`"${file2Module(name).replace(/[/]/, ".")}": ${variable},`);
66
66
  }
67
67
  // 处理导出
68
68
  content.push("\n");
@@ -116,28 +116,33 @@ const loadDeps = async (rootDir, ext = "js") => {
116
116
  const targetFile = path.resolve(rootDir, `./defines.${ext}`);
117
117
  await makeDefineFile(modules.sort(), targetFile, isTS);
118
118
  };
119
- const checkService = (_dir) => {
120
- const TSFile = path.resolve(_dir, "index.ts");
121
- const JSFile = path.resolve(_dir, "index.js");
122
- if (!fs.existsSync(TSFile) && !fs.existsSync(JSFile)) {
123
- throw Error("目录下缺少index.ts 或 index.js 文件");
124
- }
125
- };
126
- const loadServices = async (rootDir = process.cwd(), ext = "js") => {
119
+ /**
120
+ * 自动加载领域方法
121
+ * @param rootDir 项目根目录
122
+ * @param ext 文件后缀
123
+ */
124
+ const loadDomain = async (rootDir = process.cwd(), ext = "js") => {
127
125
  const isTS = ext === "ts";
128
126
  const modules = [];
129
127
  const dir = path.resolve(rootDir, "src/domain/services/");
130
- for (const x of fs.readdirSync(dir)) {
128
+ for (const domain of fs.readdirSync(dir)) {
131
129
  // 忽略隐藏目录, 忽略私有目录
132
- if (x[0] === "." || x[0] === "_")
130
+ if (domain[0] === "." || domain[0] === "_")
133
131
  continue;
134
- const _dir = path.resolve(dir, x);
132
+ const _dir = path.resolve(dir, domain);
135
133
  const stat = fs.statSync(_dir);
136
134
  // 非目录忽略,模块必须是目录
137
135
  if (!stat.isDirectory())
138
136
  continue;
139
- checkService(_dir);
140
- modules.push(path.join(dir, x));
137
+ for (const name of fs.readdirSync(_dir)) {
138
+ // 忽略隐藏目录, 忽略私有目录
139
+ if (name[0] === "." || name[0] === "_")
140
+ continue;
141
+ const extname = path.extname(name);
142
+ if (extname.toLowerCase() !== `.${ext}`)
143
+ continue;
144
+ modules.push(path.join(dir, domain, path.basename(name, extname)));
145
+ }
141
146
  }
142
147
  // 按字典排序,后续有变动的时候不容易冲突
143
148
  const targetFile = path.resolve(rootDir, `src/domain/services/defines.${ext}`);
@@ -185,7 +190,7 @@ const loadSchemas = async (rootDir = process.cwd(), ext = "js") => {
185
190
  const targetFile = path.resolve(rootDir, `src/domain/services/schemas.${ext}`);
186
191
  await makeDefineFile(modules.sort(), targetFile, isTS);
187
192
  };
188
- const actions = { loadDeps, loadServices, loadSchemas };
193
+ const actions = { loadDeps, loadSchemas, loadDomain };
189
194
  const main = async (command) => {
190
195
  const action = actions[command];
191
196
  if (!action) {
@@ -18,6 +18,12 @@ export declare function Main(cnf: Cnf, deps: Deps): {
18
18
  [propName: string]: Sequelize;
19
19
  };
20
20
  export declare const Deps: string[];
21
+ /** Model 上的 sort 设定类型 */
22
+ export interface ModelSort<Fields extends string> {
23
+ default: Fields;
24
+ allow: Fields[];
25
+ defaultDirection?: "DESC" | "ASC";
26
+ }
21
27
  /**
22
28
  * Model 基类
23
29
  */
@@ -49,11 +55,7 @@ export declare class ModelBase<Attrs extends {} = any, Attrs4Create extends {} =
49
55
  maxResultsLimit: number;
50
56
  };
51
57
  /** 列表查询时候排序控制参数 */
52
- static sort?: {
53
- default: string;
54
- allow: string[];
55
- defaultDirection?: "DESC" | "ASC";
56
- };
58
+ static sort?: ModelSort<string>;
57
59
  /** 关联资源设定, 除非要关联过滤,否则不要设置资源之间的关联关系 */
58
60
  static includes?: {
59
61
  [k: string]: {
@@ -33,9 +33,15 @@ export interface HttpCodes {
33
33
  [propName: string]: number;
34
34
  }
35
35
  export interface Domain {
36
- [propName: string]: (profile: Profile, params: any) => any | Domain;
36
+ [propName: string]: {
37
+ /** 领域方法第一个参数 schema 定义 */
38
+ profile: any;
39
+ /** 领域方法第二个参数 schema 定义 */
40
+ params: any;
41
+ /** 领域方法 */
42
+ method: (profile: any, params?: any) => any;
43
+ };
37
44
  }
38
- export declare type GetSchemaByPath = (methodPath: string) => [any, any];
39
45
  export interface Err {
40
46
  message: string;
41
47
  code?: number | string;
@@ -1,12 +1,8 @@
1
1
  import * as restify from "restify";
2
- import { Cnf, Domain, GetSchemaByPath, HttpCodes, Profile } from "./defines";
3
- import { Router } from "./router";
4
- interface Deps {
5
- routers: (r: ReturnType<typeof Router>) => void;
2
+ import { Cnf, Domain, HttpCodes, Profile } from "./defines";
3
+ export declare function Main(cnf: Cnf, deps: {
4
+ routers: (r: any) => void;
6
5
  domain: Domain;
7
6
  httpCodes: HttpCodes;
8
- getSchemaByPath: GetSchemaByPath;
9
7
  makeProfileHook?: (obj: Profile, req: restify.Request) => any;
10
- }
11
- export declare function Main(cnf: Cnf, deps: Deps): () => restify.Server;
12
- export {};
8
+ }): () => restify.Server;
@@ -27,7 +27,7 @@ const socket_1 = require("./socket");
27
27
  const utils_1 = require("./utils");
28
28
  function Main(cnf, deps) {
29
29
  const utils = (0, utils_1.Utils)(cnf);
30
- const { routers, getSchemaByPath, domain, httpCodes, makeProfileHook } = deps;
30
+ const { routers, domain, httpCodes, makeProfileHook } = deps;
31
31
  const server = restify.createServer();
32
32
  server.use(restify.plugins.queryParser());
33
33
  server.use(restify.plugins.bodyParser({
@@ -39,7 +39,6 @@ function Main(cnf, deps) {
39
39
  server,
40
40
  httpCodes,
41
41
  makeProfileHook,
42
- getSchemaByPath,
43
42
  domain,
44
43
  apisRoute: cnf.apisRoute,
45
44
  });
@@ -1,9 +1,8 @@
1
1
  import * as restify from "restify";
2
- import { Domain, GetSchemaByPath, HttpCodes, Profile } from "./defines";
2
+ import { Domain, HttpCodes, Profile } from "./defines";
3
3
  import { Utils } from "./utils";
4
4
  interface Deps {
5
5
  domain: Domain;
6
- getSchemaByPath: GetSchemaByPath;
7
6
  utils: ReturnType<typeof Utils>;
8
7
  server: restify.Server;
9
8
  httpCodes: HttpCodes;
@@ -25,4 +24,22 @@ export declare function Router(deps: Deps): {
25
24
  model: (res: string, routePath?: string) => void;
26
25
  resource: (res: string, routePath?: string) => void;
27
26
  };
27
+ declare type TRouter = ReturnType<typeof Router>;
28
+ export declare type ReplaceArrayItem<T extends any[], index extends number, R, S extends any[] = [], L extends number = S["length"]> = T extends [infer A, ...infer rest] ? L extends index ? [...S, R, ...rest] : ReplaceArrayItem<rest, index, R, [...S, A]> : never;
29
+ /** 普通 route 方法名称 */
30
+ declare type Keys = "get" | "post" | "put" | "patch" | "del";
31
+ /** 从servers 路径字符串中提取可用作model的名称,目前还不严谨,聊胜于无 */
32
+ declare type PickModelNames<paths extends string> = paths extends string ? paths extends `${infer F}.${string}` ? F : never : never;
33
+ /** 替换函数的某个参数类型定义 */
34
+ export declare type ParameterReplace<T extends (...args: any[]) => any, Index extends number, TR> = (...args: ReplaceArrayItem<Parameters<T>, Index, TR>) => ReturnType<T>;
35
+ /**
36
+ * 利用领域方法路径类型集合,收窄 methodPath, 同时可以自动提示
37
+ */
38
+ export declare type NarrowDomainPaths<Paths extends string, ModelNames = PickModelNames<Paths>> = Omit<TRouter, Keys> & {
39
+ [k in Keys]: ParameterReplace<TRouter["get"], 1, Paths>;
40
+ } & {
41
+ model: ParameterReplace<TRouter["model"], 0, ModelNames>;
42
+ collection: ParameterReplace<ParameterReplace<TRouter["collection"], 0, ModelNames>, 2, ModelNames>;
43
+ resource: ParameterReplace<TRouter["resource"], 0, ModelNames>;
44
+ };
28
45
  export {};
@@ -23,7 +23,7 @@ exports.Router = void 0;
23
23
  const _ = __importStar(require("lodash"));
24
24
  const errors = __importStar(require("restify-errors"));
25
25
  function Router(deps) {
26
- const { domain, apisRoute, getSchemaByPath, utils, server, httpCodes = {}, makeProfileHook, } = deps;
26
+ const { domain, apisRoute, utils, server, httpCodes = {}, makeProfileHook } = deps;
27
27
  const { ucwords, makeParams, makeProfile, outputCSV } = utils;
28
28
  // 改写 HttpErrorToJSON 处理 data
29
29
  const HttpErrorToJSON = errors.HttpError.prototype.toJSON;
@@ -61,8 +61,9 @@ function Router(deps) {
61
61
  const { path } = req.query;
62
62
  try {
63
63
  const { all } = req.query;
64
- const schema = getSchemaByPath(path);
65
- res.send(all === undefined ? schema[1] : schema);
64
+ const profile = domain[path]["profile"];
65
+ const params = domain[path]["params"];
66
+ res.send(all === undefined ? params : [profile, params]);
66
67
  }
67
68
  catch (e) {
68
69
  next(error2httpError(e));
@@ -79,7 +80,9 @@ function Router(deps) {
79
80
  */
80
81
  apis.push(`[${verb.toUpperCase()}] ${route} Domain: ${methodPath}`);
81
82
  apisHTML += `\n<li><a href="./${apisRoute}/_schema?path=${methodPath}">[${verb.toUpperCase()}] ${route} Domain: ${methodPath}</a></li>`;
82
- const method = _.get(domain, methodPath);
83
+ if (!domain[methodPath])
84
+ throw Error(`Missing domain method: ${methodPath}`);
85
+ const { method } = domain[methodPath];
83
86
  /** 如果都没有则抛出异常 */
84
87
  if (!method || !_.isFunction(method)) {
85
88
  throw Error(`Missing domain method: ${methodPath}`);
@@ -1,10 +1,6 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.BridgeSocket = void 0;
7
- const lodash_1 = __importDefault(require("lodash"));
8
4
  const proxyIps = new Set(["127.0.0.1"]);
9
5
  class MyError extends Error {
10
6
  constructor(code, message, data) {
@@ -72,9 +68,9 @@ const makeProfile = (client, type = "user", auth, extra = {}) => {
72
68
  return obj;
73
69
  };
74
70
  function BridgeSocket(io, domain) {
75
- const subscribe = lodash_1.default.get(domain, "message.subscribe");
76
- const unsubscribe = lodash_1.default.get(domain, "message.unsubscribe");
77
- const entrance = lodash_1.default.get(domain, "message.entrance");
71
+ const { method: subscribe } = domain["message.subscribe"];
72
+ const { method: unsubscribe } = domain["message.unsubscribe"];
73
+ const { method: entrance } = domain["message.entrance"];
78
74
  if (!subscribe)
79
75
  throw Error("要启用 socket 服务,必须要要有 message.subscribe 方法,用来处理 socket 订阅");
80
76
  if (!unsubscribe)
@@ -138,7 +134,7 @@ function BridgeSocket(io, domain) {
138
134
  client.use(async ([name, params, responseId], next) => {
139
135
  if (name === "init" || name === "entrance")
140
136
  return next();
141
- const method = domain[name];
137
+ const { method } = domain[name];
142
138
  try {
143
139
  if (!method)
144
140
  throw new MyError("notFound", "不存在该领域方法");
@@ -1 +1,22 @@
1
+ /** 将 readonly string[] 转换成联合类型 */
1
2
  export declare type ReadonlyArray2union<T extends ReadonlyArray<any>> = T extends ReadonlyArray<infer A> ? A : never;
3
+ /** 接口参数 schema 定义的类型 */
4
+ export interface ParamsSchema<Params extends {}, Keys extends string | number | symbol = keyof Params> {
5
+ /** 接口整体描述信息 */
6
+ description: string;
7
+ /** 固定为 object params 必然是一个对象, 可以是一个空对象 */
8
+ type: "object";
9
+ /** 必填选项 */
10
+ required?: (keyof Params)[];
11
+ /** 多选1的设定 */
12
+ oneOf?: any[];
13
+ /** 是否允许添加额外的属性 */
14
+ additionalProperties?: boolean;
15
+ /** params 参数每一个属性的描述 */
16
+ properties: Record<Keys, Record<string, any> & {
17
+ /** 当前参数的描述信息 */
18
+ description: string;
19
+ /** 当前参数的类型 */
20
+ type: "string" | "number" | "integer" | "object" | "array";
21
+ }>;
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@domain.js/main",
3
- "version": "0.2.5",
3
+ "version": "0.3.2",
4
4
  "description": "DDD framework",
5
5
  "main": "dist/index.js",
6
6
  "bin": {