mm_container 0.0.0-stage → 1.0.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/README.md +163 -2
- package/example/demo.js +74 -0
- package/index.js +27 -0
- package/lib/container.js +107 -0
- package/package.json +37 -4
package/README.md
CHANGED
|
@@ -1,3 +1,164 @@
|
|
|
1
|
-
#
|
|
1
|
+
# mm_container
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
容器模块。提供只允许存放实例对象的容器,按作用域缓存容器实例,用于在模块间共享对象(如 logger、服务实例、缓存数据)。
|
|
4
|
+
|
|
5
|
+
## 项目信息
|
|
6
|
+
|
|
7
|
+
- **当前版本**: 1.0.0
|
|
8
|
+
- **依赖**: mm_corejs(Admin 实例管理)
|
|
9
|
+
- **运行环境**: Node.js(使用私有字段 `#map`,需 Node.js 12+)
|
|
10
|
+
- **Gitee地址**: https://gitee.com/qiuwenwu91/mm_container
|
|
11
|
+
|
|
12
|
+
## 特点
|
|
13
|
+
|
|
14
|
+
- 容器内部用私有 `Map` 存储键值,不对外暴露 Map,避免绕过类型校验写入
|
|
15
|
+
- 只允许存入实例对象(数组、日期、普通对象、class 实例等),拒绝函数、class 本身、基础值及 `null`/`undefined`
|
|
16
|
+
- 校验在写入前执行,非法值不会写入,也不会覆盖同名键的旧值
|
|
17
|
+
- 提供语义化方法 `set` / `get` / `has` / `del` / `clear` / `list`,`set`、`clear` 支持链式调用
|
|
18
|
+
- 保留容器配置 `config` 与服务器实例 `server`
|
|
19
|
+
- `containerAdmin(scope, config?)` 按作用域返回容器,同一作用域始终是同一个实例
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install mm_container
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 基本使用
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
const { Container, containerAdmin } = require('mm_container');
|
|
31
|
+
|
|
32
|
+
// 1. 直接创建容器,可传入配置与服务器实例
|
|
33
|
+
const container = new Container({ name: 'demo' }, { tag: 'server' });
|
|
34
|
+
|
|
35
|
+
// 2. 存取实例对象
|
|
36
|
+
const logger = { level: 'info' };
|
|
37
|
+
container.set('logger', logger);
|
|
38
|
+
console.log(container.get('logger') === logger); // true
|
|
39
|
+
console.log(container.has('logger')); // true
|
|
40
|
+
console.log(container.size); // 1
|
|
41
|
+
console.log(container.list()); // [ { level: 'info' } ]
|
|
42
|
+
|
|
43
|
+
// 3. 链式调用
|
|
44
|
+
container.set('user', { id: 1 }).set('cache', { ttl: 60 });
|
|
45
|
+
|
|
46
|
+
// 4. 删除与清空
|
|
47
|
+
container.del('user'); // true
|
|
48
|
+
container.clear(); // 返回容器自身
|
|
49
|
+
console.log(container.size); // 0
|
|
50
|
+
|
|
51
|
+
// 5. 类型限制:函数、class、基础值都会被拒绝
|
|
52
|
+
try {
|
|
53
|
+
container.set('fn', () => {});
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.log(e.message); // 容器只能存储实例对象
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
按作用域共享容器:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
const { containerAdmin } = require('mm_container');
|
|
63
|
+
|
|
64
|
+
// 不传作用域时使用默认作用域 'os'
|
|
65
|
+
const os_container = containerAdmin();
|
|
66
|
+
os_container.set('logger', { level: 'info' });
|
|
67
|
+
|
|
68
|
+
// 同一作用域返回同一实例,配置仅在首次创建时生效
|
|
69
|
+
const same = containerAdmin('os', { debug: true });
|
|
70
|
+
console.log(same === os_container); // true
|
|
71
|
+
console.log(same.config); // { name: 'os' }
|
|
72
|
+
|
|
73
|
+
// 指定作用域,配置中会自动补充 name
|
|
74
|
+
const cache_container = containerAdmin('cache', { ttl: 60 });
|
|
75
|
+
console.log(cache_container.config); // { ttl: 60, name: 'cache' }
|
|
76
|
+
console.log(cache_container === os_container); // false
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API 说明
|
|
80
|
+
|
|
81
|
+
### 入口(index.js)
|
|
82
|
+
|
|
83
|
+
| 导出 | 签名 | 返回值 | 说明 |
|
|
84
|
+
| --- | --- | --- | --- |
|
|
85
|
+
| Container | `Container` | `Class` | 容器类,见下文 |
|
|
86
|
+
| admin | `admin` | `Admin` | mm_corejs 的实例管理器,已绑定 `Container` |
|
|
87
|
+
| containerAdmin | `containerAdmin(scope?, config?)` | `Container` | 获取指定作用域的容器实例,`scope` 为空时使用默认作用域 `'os'`。实例按作用域缓存,**配置仅在首次创建时生效**,`config.name` 始终等于作用域名 |
|
|
88
|
+
|
|
89
|
+
> 需要跨模块共享同一个容器时,统一使用 `containerAdmin('作用域名')`,避免各自 `new Container()` 造成数据不互通。
|
|
90
|
+
|
|
91
|
+
### 构造函数
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
const container = new Container(config?, server?);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
98
|
+
| --- | --- | --- | --- |
|
|
99
|
+
| config | `Object` | `{}` | 容器配置,原样保存在 `container.config` |
|
|
100
|
+
| server | `Object` | `undefined` | 服务器实例,保存在 `container.server` |
|
|
101
|
+
|
|
102
|
+
### 属性
|
|
103
|
+
|
|
104
|
+
| 属性 | 类型 | 说明 |
|
|
105
|
+
| --- | --- | --- |
|
|
106
|
+
| config | `Object` | 容器配置 |
|
|
107
|
+
| server | `Object \| undefined` | 服务器实例 |
|
|
108
|
+
| size | `number` | 只读,当前键值数量 |
|
|
109
|
+
|
|
110
|
+
### 方法
|
|
111
|
+
|
|
112
|
+
| 方法 | 签名 | 返回值 | 说明 |
|
|
113
|
+
| --- | --- | --- | --- |
|
|
114
|
+
| set | `set(key, value)` | `Container` | 存入实例对象,返回容器自身便于链式调用;`value` 不是实例对象时抛错 |
|
|
115
|
+
| get | `get(key)` | `*` | 获取键值,不存在时返回 `undefined` |
|
|
116
|
+
| has | `has(key)` | `boolean` | 判断键是否存在 |
|
|
117
|
+
| del | `del(key)` | `boolean` | 删除键值,删除成功返回 `true`,键不存在返回 `false` |
|
|
118
|
+
| clear | `clear()` | `Container` | 清空容器,返回容器自身 |
|
|
119
|
+
| list | `list()` | `Array` | 按插入顺序返回全部键值 |
|
|
120
|
+
|
|
121
|
+
- 键名 `key` 不限制类型,与 `Map` 的键规则一致
|
|
122
|
+
- 同名键再次 `set` 会覆盖旧值
|
|
123
|
+
|
|
124
|
+
## 类型限制
|
|
125
|
+
|
|
126
|
+
`set` 写入前会校验 `value`,仅 `typeof value === 'object' && value !== null` 的值可以存入。
|
|
127
|
+
|
|
128
|
+
| 值 | 是否允许 | 说明 |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| `{}`、`new Date()`、`[1, 2]` | 允许 | 实例对象 |
|
|
131
|
+
| class 的实例(如 `new Logger()`) | 允许 | 实例对象 |
|
|
132
|
+
| 函数(含箭头函数、async 函数) | 拒绝 | 抛 `Error('容器只能存储实例对象')` |
|
|
133
|
+
| class 本身(如 `Container`) | 拒绝 | class 的本质是函数 |
|
|
134
|
+
| `number`、`string`、`boolean`、`bigint`、`symbol` | 拒绝 | 基础值 |
|
|
135
|
+
| `null`、`undefined` | 拒绝 | 不属于对象 |
|
|
136
|
+
|
|
137
|
+
> 校验失败时不会写入数据,同名键的旧值与 `size` 保持不变。
|
|
138
|
+
|
|
139
|
+
## 示例
|
|
140
|
+
|
|
141
|
+
仓库内提供可运行示例:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm run demo
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
示例代码见 `example/demo.js`,会依次演示基础寄存取、链式调用、删除清空、类型限制(函数 / class / 数字 / 字符串 / null)以及按作用域共享容器。
|
|
148
|
+
|
|
149
|
+
## 测试
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm test
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
使用 jest 运行,覆盖构造、全部容器方法、类型限制、作用域缓存与默认作用域等场景。覆盖率阈值为 100%(语句、分支、函数、行),当前 37 个用例全部通过。
|
|
156
|
+
|
|
157
|
+
## 更新日志
|
|
158
|
+
|
|
159
|
+
### 1.0.0
|
|
160
|
+
|
|
161
|
+
- 提供 `Container`、`admin`、`containerAdmin` 三个导出
|
|
162
|
+
- 容器内部使用私有 `Map` 存储,提供 `set` / `get` / `has` / `del` / `clear` / `list` 及 `size`
|
|
163
|
+
- 限制容器只能存储实例对象,拒绝函数、class 与基础值
|
|
164
|
+
- 新增测试与示例,测试覆盖率 100%
|
package/example/demo.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Container, containerAdmin } = require('../index');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 演示容器只接收实例对象,拒绝函数、class 与基础值
|
|
7
|
+
* @param {Container} container 容器实例
|
|
8
|
+
*/
|
|
9
|
+
function showTypeLimit(container) {
|
|
10
|
+
console.log('=== 类型限制 ===');
|
|
11
|
+
console.log(' 存入函数: ' + getRejectMessage(container, 'fn', () => {}));
|
|
12
|
+
console.log(' 存入 class: ' + getRejectMessage(container, 'cls', Container));
|
|
13
|
+
console.log(' 存入数字: ' + getRejectMessage(container, 'num', 1));
|
|
14
|
+
console.log(' 存入字符串: ' + getRejectMessage(container, 'str', 'text'));
|
|
15
|
+
console.log(' 存入 null: ' + getRejectMessage(container, 'nil', null));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 获取容器拒绝值时的错误信息
|
|
20
|
+
* @param {Container} container 容器实例
|
|
21
|
+
* @param {string} key 键名
|
|
22
|
+
* @param {*} value 待存入的值
|
|
23
|
+
* @returns {string} 错误信息,未被拒绝时返回“未拒绝”
|
|
24
|
+
*/
|
|
25
|
+
function getRejectMessage(container, key, value) {
|
|
26
|
+
try {
|
|
27
|
+
container.set(key, value);
|
|
28
|
+
return '未拒绝';
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return e.message;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 使用示例:演示 Container 的键值存取、类型限制与 containerAdmin 的作用域管理
|
|
36
|
+
*/
|
|
37
|
+
function runDemo() {
|
|
38
|
+
const container = new Container({ name: 'demo' }, { tag: 'server' });
|
|
39
|
+
|
|
40
|
+
console.log('=== 基础容器 ===');
|
|
41
|
+
console.log(' 配置: ' + JSON.stringify(container.config));
|
|
42
|
+
console.log(' 服务器: ' + JSON.stringify(container.server));
|
|
43
|
+
|
|
44
|
+
const item_a = { id: 1 };
|
|
45
|
+
const item_b = { id: 2 };
|
|
46
|
+
container.set('a', item_a).set('b', item_b);
|
|
47
|
+
console.log(' set 后 size: ' + container.size);
|
|
48
|
+
console.log(' get a: ' + JSON.stringify(container.get('a')));
|
|
49
|
+
console.log(' has b: ' + container.has('b'));
|
|
50
|
+
console.log(' list: ' + JSON.stringify(container.list()));
|
|
51
|
+
|
|
52
|
+
console.log(' del a: ' + container.del('a'));
|
|
53
|
+
console.log(' has a: ' + container.has('a'));
|
|
54
|
+
|
|
55
|
+
showTypeLimit(container);
|
|
56
|
+
|
|
57
|
+
container.clear();
|
|
58
|
+
console.log(' clear 后 size: ' + container.size);
|
|
59
|
+
|
|
60
|
+
console.log('=== 作用域容器 ===');
|
|
61
|
+
const os_container = containerAdmin();
|
|
62
|
+
os_container.set('logger', { level: 'info' });
|
|
63
|
+
console.log(' os 容器配置: ' + JSON.stringify(os_container.config));
|
|
64
|
+
console.log(' os 容器内容: ' + JSON.stringify(os_container.list()));
|
|
65
|
+
console.log(' 同一作用域返回同一实例: ' + (containerAdmin('os') === os_container));
|
|
66
|
+
|
|
67
|
+
const cache_container = containerAdmin('cache', { ttl: 60 });
|
|
68
|
+
cache_container.set('user:1', { name: '张三' });
|
|
69
|
+
console.log(' cache 容器配置: ' + JSON.stringify(cache_container.config));
|
|
70
|
+
console.log(' cache 容器内容: ' + JSON.stringify(cache_container.list()));
|
|
71
|
+
console.log(' 不同作用域为不同实例: ' + (cache_container !== os_container));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
runDemo();
|
package/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Container = require('./lib/container');
|
|
4
|
+
const { Admin } = require('mm_corejs');
|
|
5
|
+
|
|
6
|
+
const admin = new Admin();
|
|
7
|
+
admin.setClass(Container);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 获取容器管理器
|
|
11
|
+
* 未指定作用域时使用默认作用域;同一作用域返回同一个容器实例。
|
|
12
|
+
* @param {string} scope 作用域名称
|
|
13
|
+
* @param {Object} config 容器配置
|
|
14
|
+
* @returns {Container} 容器实例
|
|
15
|
+
*/
|
|
16
|
+
function containerAdmin(scope, config) {
|
|
17
|
+
if (!scope) {
|
|
18
|
+
scope = 'os';
|
|
19
|
+
}
|
|
20
|
+
return admin.get(scope, config);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
Container,
|
|
25
|
+
admin,
|
|
26
|
+
containerAdmin
|
|
27
|
+
};
|
package/lib/container.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 判断是否为实例对象
|
|
3
|
+
* 数组、日期等对象均属于实例对象;函数、class、基础值均不属于实例对象。
|
|
4
|
+
* @param {*} value 待判断的值
|
|
5
|
+
* @returns {boolean} 是否为实例对象
|
|
6
|
+
*/
|
|
7
|
+
function _isInstanceObject(value) {
|
|
8
|
+
return typeof value === 'object' && value !== null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 校验容器存储的值
|
|
13
|
+
* @param {*} value 待校验的值
|
|
14
|
+
* @throws {Error} 值不是实例对象时抛错
|
|
15
|
+
*/
|
|
16
|
+
function _checkValue(value) {
|
|
17
|
+
if (!_isInstanceObject(value)) {
|
|
18
|
+
throw new Error('容器只能存储实例对象');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 容器类
|
|
24
|
+
* 内部使用私有 Map 存储键值,提供语义化的存取方法,并保存容器配置与服务器实例。
|
|
25
|
+
* 容器只允许存储实例对象,不允许存储函数、class 和基础值。
|
|
26
|
+
*/
|
|
27
|
+
class Container {
|
|
28
|
+
#map;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 构造函数
|
|
32
|
+
* @param {Object} config 容器配置
|
|
33
|
+
* @param {Object} server 服务器实例
|
|
34
|
+
*/
|
|
35
|
+
constructor(config = {}, server) {
|
|
36
|
+
this.#map = new Map();
|
|
37
|
+
this.config = config;
|
|
38
|
+
this.server = server;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 获取键值数量
|
|
43
|
+
* @returns {number} 键值数量
|
|
44
|
+
*/
|
|
45
|
+
get size() {
|
|
46
|
+
return this.#map.size;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 设置键值
|
|
51
|
+
* @param {*} key 键名
|
|
52
|
+
* @param {Object} value 键值,必须是实例对象
|
|
53
|
+
* @returns {Container} 容器自身,便于链式调用
|
|
54
|
+
* @throws {Error} 值不是实例对象时抛错
|
|
55
|
+
*/
|
|
56
|
+
set(key, value) {
|
|
57
|
+
_checkValue(value);
|
|
58
|
+
this.#map.set(key, value);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 获取键值
|
|
64
|
+
* @param {*} key 键名
|
|
65
|
+
* @returns {*} 键值,不存在时返回 undefined
|
|
66
|
+
*/
|
|
67
|
+
get(key) {
|
|
68
|
+
return this.#map.get(key);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 判断键是否存在
|
|
73
|
+
* @param {*} key 键名
|
|
74
|
+
* @returns {boolean} 是否存在
|
|
75
|
+
*/
|
|
76
|
+
has(key) {
|
|
77
|
+
return this.#map.has(key);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 删除键值
|
|
82
|
+
* @param {*} key 键名
|
|
83
|
+
* @returns {boolean} 是否删除成功
|
|
84
|
+
*/
|
|
85
|
+
del(key) {
|
|
86
|
+
return this.#map.delete(key);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 清空容器
|
|
91
|
+
* @returns {Container} 容器自身,便于链式调用
|
|
92
|
+
*/
|
|
93
|
+
clear() {
|
|
94
|
+
this.#map.clear();
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 列出全部键值
|
|
100
|
+
* @returns {Array} 键值数组
|
|
101
|
+
*/
|
|
102
|
+
list() {
|
|
103
|
+
return Array.from(this.#map.values());
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = Container;
|
package/package.json
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_container",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "容器工具,基于 Map 提供语义化的键值存取(set/get/has/del/clear/list),并保存容器配置与服务器实例。",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mm_mod",
|
|
7
|
+
"mm_infra",
|
|
8
|
+
"mm_common",
|
|
9
|
+
"超级美眉",
|
|
10
|
+
"容器",
|
|
11
|
+
"键值存储",
|
|
12
|
+
"container"
|
|
13
|
+
],
|
|
14
|
+
"layer": "infra",
|
|
15
|
+
"group": "common",
|
|
16
|
+
"author": "邱文武",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"main": "index.js",
|
|
19
|
+
"files": [
|
|
20
|
+
"index.js",
|
|
21
|
+
"lib",
|
|
22
|
+
"example"
|
|
23
|
+
],
|
|
24
|
+
"directories": {
|
|
25
|
+
"example": "example",
|
|
26
|
+
"lib": "lib"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "jest --coverage",
|
|
30
|
+
"demo": "node example/demo.js",
|
|
31
|
+
"prepublishOnly": "npm test"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"mm_corejs": "^1.1.5"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"jest": "^30.5.2"
|
|
38
|
+
}
|
|
39
|
+
}
|