alemonjs 2.1.52 → 2.1.54

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/bin/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## alemonc
2
2
 
3
- AlemonJS 项目 CLI 工具,用于配置管理、平台管理、版本更新等。
3
+ ALemonJS 项目 CLI 工具,用于配置管理、平台管理、版本更新等。
4
4
 
5
5
  ### 帮助
6
6
 
package/bin/info.js CHANGED
@@ -8,7 +8,7 @@ import YAML from 'yaml';
8
8
  * 输出项目诊断信息
9
9
  */
10
10
  export function info() {
11
- console.log('=== AlemonJS 项目信息 ===\n');
11
+ console.log('=== ALemonJS 项目信息 ===\n');
12
12
 
13
13
  // Node 版本
14
14
  console.log(`Node.js: ${process.version}`);
@@ -2,4 +2,5 @@ import { DefineRouterFunc } from '../types';
2
2
  export declare const lazy: <T extends {
3
3
  default: any;
4
4
  }>(fnc: () => Promise<T>) => (() => Promise<T["default"]>);
5
+ export declare function runHandler(loader: () => Promise<any>, args: any[]): Promise<any>;
5
6
  export declare const defineRouter: DefineRouterFunc;
@@ -11,10 +11,15 @@ const lazy = (fnc) => {
11
11
  };
12
12
  return back;
13
13
  };
14
+ async function runHandler(loader, args) {
15
+ const mod = await loader();
16
+ const handler = mod.default ?? mod;
17
+ return handler(...args);
18
+ }
14
19
  const defineRouter = routes => {
15
20
  return {
16
21
  current: routes
17
22
  };
18
23
  };
19
24
 
20
- export { defineRouter, lazy };
25
+ export { defineRouter, lazy, runHandler };
@@ -1,3 +1,4 @@
1
- import { Next, Events, EventKeys, StoreResponseItem } from '../types';
1
+ import { Next, Events, EventKeys, FileTreeNode, StoreResponseItem } from '../types';
2
2
  export declare const clearModuleCache: (path?: string) => void;
3
3
  export declare const createNextStep: <T extends EventKeys>(valueEvent: Events[T], select: T, next: Next, files: StoreResponseItem[], callHandler: (currents: any, nextEvent: any) => void) => Next;
4
+ export declare const createFileTreeStep: <T extends EventKeys>(valueEvent: Events[T], select: T, next: Next, root: FileTreeNode, callHandler: (currents: any, nextEvent: any) => void) => Next;
@@ -1,10 +1,26 @@
1
- import { useState } from './event-utils.js';
2
1
  import { getCachedRegExp, showErrorModule } from '../core/utils.js';
3
2
  import { EventMessageText } from '../core/variable.js';
4
- import { ResponseMiddleware } from './store.js';
5
3
 
6
- const responseMiddlewareSingleton = new ResponseMiddleware();
7
4
  const moduleCache = new Map();
5
+ const shouldSkipFile = (file, select, valueEvent) => {
6
+ if (!moduleCache.has(file.path)) {
7
+ return false;
8
+ }
9
+ const app = moduleCache.get(file.path);
10
+ if (!app?.default?.current || !app?.default?.select) {
11
+ return true;
12
+ }
13
+ const selects = Array.isArray(app.default.select) ? app.default.select : [app.default.select];
14
+ if (!selects.includes(select)) {
15
+ return true;
16
+ }
17
+ if (EventMessageText.includes(select) && app?.regular) {
18
+ if (!getCachedRegExp(app.regular).test(valueEvent['MessageText'])) {
19
+ return true;
20
+ }
21
+ }
22
+ return false;
23
+ };
8
24
  const clearModuleCache = (path) => {
9
25
  if (path) {
10
26
  moduleCache.delete(path);
@@ -28,13 +44,6 @@ const callHandlerFile = async (valueEvent, select, file, nextStep, callback) =>
28
44
  nextStep();
29
45
  return;
30
46
  }
31
- if (file?.stateKey) {
32
- const [state] = useState(file?.stateKey);
33
- if (state === false) {
34
- nextStep();
35
- return;
36
- }
37
- }
38
47
  if (EventMessageText.includes(select)) {
39
48
  if (app?.regular) {
40
49
  if (!getCachedRegExp(app.regular).test(valueEvent['MessageText'])) {
@@ -52,11 +61,11 @@ const callHandlerFile = async (valueEvent, select, file, nextStep, callback) =>
52
61
  }
53
62
  catch (err) {
54
63
  showErrorModule(err);
64
+ nextStep();
55
65
  }
56
66
  };
57
67
  const createNextStep = (valueEvent, select, next, files, callHandler) => {
58
68
  let valueI = 0;
59
- const recordCloseMw = new Set();
60
69
  const nextStep = (cn, ...cns) => {
61
70
  if (cn) {
62
71
  next(...cns);
@@ -79,30 +88,125 @@ const createNextStep = (valueEvent, select, next, files, callHandler) => {
79
88
  nextStep();
80
89
  return;
81
90
  }
82
- const currentsAndMiddleware = responseMiddlewareSingleton.find(file.appName, file.stateKey);
83
91
  const currents = [];
84
- const iterItems = currentsAndMiddleware.length > 0 ? currentsAndMiddleware.concat(file) : [file];
85
- for (const cm of iterItems) {
86
- let isBreak = false;
87
- if (recordCloseMw.has(cm.stateKey)) {
92
+ await callHandlerFile(valueEvent, select, file, () => {
93
+ nextStep();
94
+ }, app => {
95
+ const currentsItem = Array.isArray(app.default.current) ? app.default.current : [app.default.current];
96
+ currents.push(...currentsItem);
97
+ });
98
+ if (currents.length > 0) {
99
+ callHandler(currents, nextStep);
100
+ }
101
+ };
102
+ return nextStep;
103
+ };
104
+ const createFileTreeStep = (valueEvent, select, next, root, callHandler) => {
105
+ const processNode = (node, done) => {
106
+ if (node.middleware?.path) {
107
+ void checkMiddleware(node, done);
108
+ }
109
+ else {
110
+ void processContent(node, done);
111
+ }
112
+ };
113
+ const checkMiddleware = async (node, done) => {
114
+ if (shouldSkipFile(node.middleware, select, valueEvent)) {
115
+ void processContent(node, done);
116
+ return;
117
+ }
118
+ let matched = false;
119
+ const mwCurrents = [];
120
+ await callHandlerFile(valueEvent, select, node.middleware, () => {
121
+ void processContent(node, done);
122
+ }, app => {
123
+ matched = true;
124
+ const items = Array.isArray(app.default.current) ? app.default.current : [app.default.current];
125
+ mwCurrents.push(...items);
126
+ });
127
+ if (matched) {
128
+ if (mwCurrents.length === 0) {
129
+ void processContent(node, done);
88
130
  return;
89
131
  }
90
- const close = () => {
91
- isBreak = true;
92
- recordCloseMw.add(cm.stateKey);
93
- };
94
- await callHandlerFile(valueEvent, select, cm, close, app => {
95
- const currentsItem = Array.isArray(app.default.current) ? app.default.current : [app.default.current];
96
- currents.push(...currentsItem);
132
+ const gateCurrents = mwCurrents.concat([
133
+ (_event, gateNext) => {
134
+ gateNext();
135
+ return true;
136
+ }
137
+ ]);
138
+ callHandler(gateCurrents, (cn, ...cns) => {
139
+ if (cn) {
140
+ done(true, ...cns);
141
+ return;
142
+ }
143
+ void processContent(node, done);
97
144
  });
98
- if (isBreak) {
99
- nextStep();
145
+ }
146
+ };
147
+ const processContent = (node, done) => {
148
+ processFiles(node, 0, () => {
149
+ processChildNodes(node, done);
150
+ }, done);
151
+ };
152
+ const processFiles = (node, idx, filesDone, treeDone) => {
153
+ if (idx >= node.files.length) {
154
+ filesDone();
155
+ return;
156
+ }
157
+ const file = node.files[idx];
158
+ if (!file?.path) {
159
+ processFiles(node, idx + 1, filesDone, treeDone);
160
+ return;
161
+ }
162
+ if (shouldSkipFile(file, select, valueEvent)) {
163
+ processFiles(node, idx + 1, filesDone, treeDone);
164
+ return;
165
+ }
166
+ void callHandlerFile(valueEvent, select, file, () => {
167
+ processFiles(node, idx + 1, filesDone, treeDone);
168
+ }, app => {
169
+ const fileCurrents = Array.isArray(app.default.current) ? app.default.current : [app.default.current];
170
+ callHandler(fileCurrents, (cn, ...cns) => {
171
+ if (cn) {
172
+ treeDone(true, ...cns);
173
+ }
174
+ else {
175
+ processFiles(node, idx + 1, filesDone, treeDone);
176
+ }
177
+ });
178
+ });
179
+ };
180
+ const processChildNodes = (node, done) => {
181
+ const childKeys = Array.from(node.children.keys());
182
+ let childIdx = 0;
183
+ const nextChild = (...cns) => {
184
+ if (cns.length > 0 && cns[0]) {
185
+ done(...cns);
186
+ return;
187
+ }
188
+ if (childIdx >= childKeys.length) {
189
+ done();
100
190
  return;
101
191
  }
192
+ const childNode = node.children.get(childKeys[childIdx++]);
193
+ if (childNode) {
194
+ processNode(childNode, nextChild);
195
+ }
196
+ else {
197
+ nextChild();
198
+ }
199
+ };
200
+ nextChild();
201
+ };
202
+ const startStep = (cn, ...cns) => {
203
+ if (cn) {
204
+ next(...cns);
205
+ return;
102
206
  }
103
- callHandler(currents, nextStep);
207
+ processNode(root, next);
104
208
  };
105
- return nextStep;
209
+ return startStep;
106
210
  };
107
211
 
108
- export { clearModuleCache, createNextStep };
212
+ export { clearModuleCache, createFileTreeStep, createNextStep };
@@ -1,2 +1,2 @@
1
1
  import { Next, Events, EventKeys, ResponseRoute } from '../types';
2
- export declare const createRouteProcessChildren: <T extends EventKeys>(valueEvent: Events[T], select: T, nextCycle: Next, callHandler: (currents: any, nextEvent: any) => void) => (nodes: ResponseRoute[], middleware: any, next: () => Promise<void> | void) => void;
2
+ export declare const createRouteProcessChildren: <T extends EventKeys>(valueEvent: Events[T], select: T, nextCycle: Next, callHandler: (currents: any, nextEvent: any) => void) => (nodes: ResponseRoute[], _pending: any[], next: () => Promise<void> | void) => void;
@@ -12,7 +12,40 @@ function isFunction(value) {
12
12
  return isAsyncFunction(value) || typeof value === 'function' || value instanceof Function;
13
13
  }
14
14
  const createRouteProcessChildren = (valueEvent, select, nextCycle, callHandler) => {
15
- const processChildren = (nodes, middleware, next) => {
15
+ const handlerResultCache = new Map();
16
+ const collectHandlers = (tail) => {
17
+ const result = [];
18
+ let node = tail;
19
+ while (node) {
20
+ result.push(node.handler);
21
+ node = node.prev;
22
+ }
23
+ result.reverse();
24
+ return result;
25
+ };
26
+ const resolveHandler = async (handler) => {
27
+ if (handlerResultCache.has(handler)) {
28
+ return handlerResultCache.get(handler);
29
+ }
30
+ const app = await handler();
31
+ const result = { matched: true, currents: [] };
32
+ if (isFunction(app)) {
33
+ result.currents.push(app);
34
+ }
35
+ else {
36
+ const selects = Array.isArray(app.select) ? app.select : [app.select];
37
+ if (!selects.includes(select)) {
38
+ result.matched = false;
39
+ }
40
+ else {
41
+ const items = Array.isArray(app.current) ? app.current : [app.current];
42
+ result.currents.push(...items);
43
+ }
44
+ }
45
+ handlerResultCache.set(handler, result);
46
+ return result;
47
+ };
48
+ const processChildren = (nodes, pendingTail, next) => {
16
49
  if (!nodes || nodes.length === 0) {
17
50
  void next();
18
51
  return;
@@ -57,35 +90,25 @@ const createRouteProcessChildren = (valueEvent, select, nextCycle, callHandler)
57
90
  void nextNode();
58
91
  return;
59
92
  }
93
+ const currentNode = { handler: node.handler, prev: pendingTail };
60
94
  if (node.children && node.children.length > 0) {
61
- middleware.push(node.handler);
62
- processChildren(node.children, middleware, () => {
63
- middleware.pop();
95
+ processChildren(node.children, currentNode, () => {
64
96
  void nextNode();
65
97
  });
66
98
  return;
67
99
  }
68
- middleware.push(node.handler);
69
- const currentsAndMiddleware = middleware;
70
100
  try {
71
- const currents = [];
72
- for (const item of currentsAndMiddleware) {
73
- const app = await item();
74
- if (isFunction(app)) {
75
- currents.push(app);
76
- continue;
77
- }
78
- const selects = Array.isArray(app.select) ? app.select : [app.select];
79
- if (!selects.includes(select)) {
80
- middleware.pop();
101
+ const allHandlers = collectHandlers(currentNode);
102
+ const allCurrents = [];
103
+ for (const h of allHandlers) {
104
+ const result = await resolveHandler(h);
105
+ if (!result.matched) {
81
106
  void nextNode();
82
107
  return;
83
108
  }
84
- const currentsItem = Array.isArray(app.current) ? app.current : [app.current];
85
- currents.push(...currentsItem);
109
+ allCurrents.push(...result.currents);
86
110
  }
87
- middleware.pop();
88
- callHandler(currents, (cn, ...cns) => {
111
+ callHandler(allCurrents, (cn, ...cns) => {
89
112
  if (cn) {
90
113
  nextCycle(true, ...cns);
91
114
  return;
@@ -94,13 +117,14 @@ const createRouteProcessChildren = (valueEvent, select, nextCycle, callHandler)
94
117
  });
95
118
  }
96
119
  catch (err) {
97
- middleware.pop();
98
120
  showErrorModule(err);
99
121
  }
100
122
  };
101
123
  void nextNode();
102
124
  };
103
- return processChildren;
125
+ return (nodes, _pending, next) => {
126
+ processChildren(nodes, null, next);
127
+ };
104
128
  };
105
129
 
106
130
  export { createRouteProcessChildren };
@@ -1,14 +1,14 @@
1
- import { Response, ResponseRouter } from './store.js';
1
+ import { ResponseTree, ResponseRouter } from './store.js';
2
2
  import { createCallHandler } from './event-processor-callHandler.js';
3
- import { createNextStep } from './event-processor-cycleFiles.js';
3
+ import { createFileTreeStep } from './event-processor-cycleFiles.js';
4
4
  import { createRouteProcessChildren } from './event-processor-cycleRoute.js';
5
5
 
6
- const responseSingleton = new Response();
6
+ const responseTreeSingleton = new ResponseTree();
7
7
  const responseRouterSingleton = new ResponseRouter();
8
8
  const expendEvent = (valueEvent, select, next) => {
9
- const StoreResponse = responseSingleton.value;
9
+ const root = responseTreeSingleton.value;
10
10
  const callHandler = createCallHandler(valueEvent);
11
- const nextEvent = createNextStep(valueEvent, select, next, StoreResponse, callHandler);
11
+ const nextEvent = createFileTreeStep(valueEvent, select, next, root, callHandler);
12
12
  const routes = responseRouterSingleton.value;
13
13
  const callRouteHandler = createCallHandler(valueEvent);
14
14
  const processChildren = createRouteProcessChildren(valueEvent, select, nextEvent, callRouteHandler);
@@ -1,14 +1,14 @@
1
- import { Middleware, MiddlewareRouter } from './store.js';
1
+ import { MiddlewareTree, MiddlewareRouter } from './store.js';
2
2
  import { createCallHandler } from './event-processor-callHandler.js';
3
- import { createNextStep } from './event-processor-cycleFiles.js';
3
+ import { createFileTreeStep } from './event-processor-cycleFiles.js';
4
4
  import { createRouteProcessChildren } from './event-processor-cycleRoute.js';
5
5
 
6
- const middlewareSingleton = new Middleware();
6
+ const middlewareTreeSingleton = new MiddlewareTree();
7
7
  const middlewareRouterSingleton = new MiddlewareRouter();
8
8
  const expendMiddleware = (valueEvent, select, next) => {
9
- const mwFiles = middlewareSingleton.value;
9
+ const root = middlewareTreeSingleton.value;
10
10
  const callHandler = createCallHandler(valueEvent);
11
- const nextMiddleware = createNextStep(valueEvent, select, next, mwFiles, callHandler);
11
+ const nextMiddleware = createFileTreeStep(valueEvent, select, next, root, callHandler);
12
12
  const routes = middlewareRouterSingleton.value;
13
13
  const callRouteHandler = createCallHandler(valueEvent);
14
14
  const processChildren = createRouteProcessChildren(valueEvent, select, nextMiddleware, callRouteHandler);
@@ -98,6 +98,9 @@ const onProcessor = (name, event, data) => {
98
98
  }
99
99
  }
100
100
  }
101
+ else {
102
+ event['MessageText'] = '';
103
+ }
101
104
  const masterId = value?.master_id;
102
105
  const masterKey = value?.master_key;
103
106
  if (event['UserId'] && matchIn(masterId, event['UserId'])) {
@@ -0,0 +1,16 @@
1
+ import { ExposeProvideConfig, ExposeListItem, ExposeWatchEvent, ExposeSchemaItem } from '../types';
2
+ type WatchCallback = (event: ExposeWatchEvent) => void;
3
+ export declare function registerExpose(appName: string, configs: ExposeProvideConfig[]): void;
4
+ export declare class Expose {
5
+ #private;
6
+ static create(): Expose;
7
+ provide(config: ExposeProvideConfig): this;
8
+ getConfigs(): ExposeProvideConfig[];
9
+ list(protocol?: string): ExposeListItem[];
10
+ invoke(protocol: string, appName: string, action: string, value?: any): any;
11
+ schema(): ExposeSchemaItem[];
12
+ watch(protocolOrCallback: string | WatchCallback, callback?: WatchCallback): () => void;
13
+ }
14
+ export declare function disposeExpose(appName: string): void;
15
+ export declare function clearAllExpose(): void;
16
+ export {};
@@ -0,0 +1,190 @@
1
+ const storeExpose = new Map();
2
+ const protocolWatchers = new Map();
3
+ const globalWatchers = new Set();
4
+ function emitWatch(event) {
5
+ const watchers = protocolWatchers.get(event.protocol);
6
+ if (watchers) {
7
+ for (const cb of watchers) {
8
+ try {
9
+ cb(event);
10
+ }
11
+ catch {
12
+ }
13
+ }
14
+ }
15
+ for (const cb of globalWatchers) {
16
+ try {
17
+ cb(event);
18
+ }
19
+ catch {
20
+ }
21
+ }
22
+ }
23
+ function registerExpose(appName, configs) {
24
+ for (const config of configs) {
25
+ const protocol = config.name;
26
+ if (!storeExpose.has(protocol)) {
27
+ storeExpose.set(protocol, new Map());
28
+ }
29
+ const providers = storeExpose.get(protocol);
30
+ if (providers.has(appName)) {
31
+ console.warn(`[expose] ${appName} already provided "${protocol}", overwriting.`);
32
+ }
33
+ providers.set(appName, { appName, config });
34
+ }
35
+ }
36
+ class Expose {
37
+ #configs = [];
38
+ static create() {
39
+ return new Expose();
40
+ }
41
+ provide(config) {
42
+ this.#configs.push(config);
43
+ return this;
44
+ }
45
+ getConfigs() {
46
+ return this.#configs;
47
+ }
48
+ list(protocol) {
49
+ const result = [];
50
+ if (protocol) {
51
+ const providers = storeExpose.get(protocol);
52
+ if (!providers) {
53
+ return result;
54
+ }
55
+ for (const [, entry] of providers) {
56
+ result.push(createListItem(entry, protocol));
57
+ }
58
+ }
59
+ else {
60
+ for (const [proto, providers] of storeExpose) {
61
+ for (const [, entry] of providers) {
62
+ result.push(createListItem(entry, proto));
63
+ }
64
+ }
65
+ }
66
+ return result;
67
+ }
68
+ invoke(protocol, appName, action, value) {
69
+ const providers = storeExpose.get(protocol);
70
+ if (!providers) {
71
+ throw new Error(`[expose] protocol "${protocol}" not found`);
72
+ }
73
+ const entry = providers.get(appName);
74
+ if (!entry) {
75
+ throw new Error(`[expose] provider "${appName}" not found in protocol "${protocol}"`);
76
+ }
77
+ const actionMeta = entry.config.actions[action];
78
+ if (!actionMeta) {
79
+ throw new Error(`[expose] action "${action}" not found in "${protocol}" of "${appName}"`);
80
+ }
81
+ const result = actionMeta.handler(value);
82
+ if (action !== 'read') {
83
+ emitWatch({
84
+ name: appName,
85
+ protocol,
86
+ action,
87
+ value
88
+ });
89
+ }
90
+ return result;
91
+ }
92
+ schema() {
93
+ const result = [];
94
+ for (const [protocol, providers] of storeExpose) {
95
+ const firstEntry = providers.values().next().value;
96
+ if (!firstEntry) {
97
+ continue;
98
+ }
99
+ const actions = {};
100
+ for (const [actionName, meta] of Object.entries(firstEntry.config.actions)) {
101
+ actions[actionName] = {
102
+ description: meta.description,
103
+ ...(meta.params ? { params: meta.params } : {}),
104
+ ...(meta.returns ? { returns: meta.returns } : {})
105
+ };
106
+ }
107
+ result.push({
108
+ protocol,
109
+ description: firstEntry.config.description,
110
+ providers: Array.from(providers.keys()).map(name => ({ name })),
111
+ actions,
112
+ invoke: 'Expose.invoke(protocol, appName, action, value?)'
113
+ });
114
+ }
115
+ return result;
116
+ }
117
+ watch(protocolOrCallback, callback) {
118
+ if (typeof protocolOrCallback === 'function') {
119
+ const cb = protocolOrCallback;
120
+ globalWatchers.add(cb);
121
+ return () => {
122
+ globalWatchers.delete(cb);
123
+ };
124
+ }
125
+ const protocol = protocolOrCallback;
126
+ if (!callback) {
127
+ throw new Error('[expose] watch(protocol, callback) requires a callback');
128
+ }
129
+ if (!protocolWatchers.has(protocol)) {
130
+ protocolWatchers.set(protocol, new Set());
131
+ }
132
+ protocolWatchers.get(protocol).add(callback);
133
+ return () => {
134
+ const set = protocolWatchers.get(protocol);
135
+ if (set) {
136
+ set.delete(callback);
137
+ if (set.size === 0) {
138
+ protocolWatchers.delete(protocol);
139
+ }
140
+ }
141
+ };
142
+ }
143
+ }
144
+ function createListItem(entry, protocol) {
145
+ const actions = {};
146
+ for (const [actionName, meta] of Object.entries(entry.config.actions)) {
147
+ actions[actionName] = {
148
+ description: meta.description,
149
+ ...(meta.params ? { params: meta.params } : {}),
150
+ ...(meta.returns ? { returns: meta.returns } : {})
151
+ };
152
+ }
153
+ return {
154
+ name: entry.appName,
155
+ protocol,
156
+ description: entry.config.description,
157
+ actions,
158
+ invoke(action, value) {
159
+ const actionMeta = entry.config.actions[action];
160
+ if (!actionMeta) {
161
+ throw new Error(`[expose] action "${action}" not found in "${protocol}" of "${entry.appName}"`);
162
+ }
163
+ const result = actionMeta.handler(value);
164
+ if (action !== 'read') {
165
+ emitWatch({
166
+ name: entry.appName,
167
+ protocol,
168
+ action,
169
+ value
170
+ });
171
+ }
172
+ return result;
173
+ }
174
+ };
175
+ }
176
+ function disposeExpose(appName) {
177
+ for (const [protocol, providers] of storeExpose) {
178
+ providers.delete(appName);
179
+ if (providers.size === 0) {
180
+ storeExpose.delete(protocol);
181
+ }
182
+ }
183
+ }
184
+ function clearAllExpose() {
185
+ storeExpose.clear();
186
+ protocolWatchers.clear();
187
+ globalWatchers.clear();
188
+ }
189
+
190
+ export { Expose, clearAllExpose, disposeExpose, registerExpose };
@@ -1,4 +1,5 @@
1
1
  export * from './store.js';
2
+ export * from './expose.js';
2
3
  export * from './load_modules/index.js';
3
4
  export * from './define-children.js';
4
5
  export * from './define-platform.js';
package/lib/app/index.js CHANGED
@@ -1,11 +1,12 @@
1
- export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, State, StateSubscribe, SubscribeList, bumpStoreVersion, core, getSubscribeList, logger } from './store.js';
1
+ export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, MiddlewareTree, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, ResponseTree, State, StateSubscribe, SubscribeList, bumpStoreVersion, core, getSubscribeList, logger } from './store.js';
2
+ export { Expose, clearAllExpose, disposeExpose, registerExpose } from './expose.js';
2
3
  export { loadModels, run } from './load_modules/load.js';
3
4
  export { loadChildren, loadChildrenFile } from './load_modules/loadChild.js';
4
5
  export { defineChildren } from './define-children.js';
5
6
  export { definePlatform } from './define-platform.js';
6
7
  export { defineResponse } from './define-response.js';
7
8
  export { defineMiddleware } from './define-middleware.js';
8
- export { defineRouter, lazy } from './define-router.js';
9
+ export { defineRouter, lazy, runHandler } from './define-router.js';
9
10
  export { FormatEvent, wrapEvent } from './event-format.js';
10
11
  export { onGroup } from './event-group.js';
11
12
  export { OnMiddleware, onMiddleware } from './event-middleware.js';
@@ -2,6 +2,7 @@ import { dirname, join } from 'path';
2
2
  import { existsSync } from 'fs';
3
3
  import { showErrorModule, getRecursiveDirFiles, createEventName } from '../../core/utils.js';
4
4
  import { ChildrenApp } from '../store.js';
5
+ import { registerExpose } from '../expose.js';
5
6
  import { ResultCode, fileSuffixMiddleware } from '../../core/variable.js';
6
7
  import module$1 from 'module';
7
8
 
@@ -67,6 +68,9 @@ const loadChildren = async (mainPath, appName) => {
67
68
  if (res && (res?.response || res?.middleware || res?.responseRouter || res?.middlewareRouter)) {
68
69
  App.register(res);
69
70
  }
71
+ if (res?.expose) {
72
+ registerExpose(appName, res.expose.getConfigs());
73
+ }
70
74
  App.on();
71
75
  try {
72
76
  if (app?.onMounted) {
@@ -1,5 +1,5 @@
1
1
  import { SinglyLinkedList } from './SinglyLinkedList';
2
- import { childrenCallbackRes, ChildrenCycle, EventCycleEnum, EventKeys, StoreMiddlewareItem, StoreResponseItem, SubscribeValue } from '../types';
2
+ import { childrenCallbackRes, ChildrenCycle, EventCycleEnum, EventKeys, FileTreeNode, StoreMiddlewareItem, StoreResponseItem, SubscribeValue } from '../types';
3
3
  export declare class Logger {
4
4
  #private;
5
5
  constructor();
@@ -8,11 +8,11 @@ export declare class Logger {
8
8
  export declare class Core {
9
9
  constructor();
10
10
  get value(): {
11
- storeState: import("..").ResponseState;
12
- storeStateSubscribe: import("..").StateSubscribeMap;
13
- storeSubscribeList: import("..").SubscribeKeysMap;
11
+ storeState: import("../types").ResponseState;
12
+ storeStateSubscribe: import("../types").StateSubscribeMap;
13
+ storeSubscribeList: import("../types").SubscribeKeysMap;
14
14
  storeChildrenApp: {
15
- [key: string]: import("..").StoreChildrenApp;
15
+ [key: string]: import("../types").StoreChildrenApp;
16
16
  };
17
17
  };
18
18
  }
@@ -24,6 +24,14 @@ export declare class Response {
24
24
  export declare class ResponseMiddleware {
25
25
  find(name: string, stateKey: string): StoreResponseItem[];
26
26
  }
27
+ export declare class MiddlewareTree {
28
+ #private;
29
+ get value(): FileTreeNode;
30
+ }
31
+ export declare class ResponseTree {
32
+ #private;
33
+ get value(): FileTreeNode;
34
+ }
27
35
  export declare class ResponseRouter {
28
36
  #private;
29
37
  get value(): any[];
@@ -67,16 +75,16 @@ export declare class ChildrenApp {
67
75
  pushCycle(data: ChildrenCycle): void;
68
76
  on(): void;
69
77
  un(): void;
70
- get value(): import("..").StoreChildrenApp;
78
+ get value(): import("../types").StoreChildrenApp;
71
79
  }
72
80
  export declare const ProcessorEventAutoClearMap: Map<any, any>;
73
81
  export declare const ProcessorEventUserAutoClearMap: Map<any, any>;
74
82
  export declare const logger: any;
75
83
  export declare const core: {
76
- storeState: import("..").ResponseState;
77
- storeStateSubscribe: import("..").StateSubscribeMap;
78
- storeSubscribeList: import("..").SubscribeKeysMap;
84
+ storeState: import("../types").ResponseState;
85
+ storeStateSubscribe: import("../types").StateSubscribeMap;
86
+ storeSubscribeList: import("../types").SubscribeKeysMap;
79
87
  storeChildrenApp: {
80
- [key: string]: import("..").StoreChildrenApp;
88
+ [key: string]: import("../types").StoreChildrenApp;
81
89
  };
82
90
  };
package/lib/app/store.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { SinglyLinkedList } from './SinglyLinkedList.js';
2
2
  import { mkdirSync } from 'node:fs';
3
3
  import log4js from 'log4js';
4
+ import { disposeExpose } from './expose.js';
4
5
 
5
6
  const createLogger = () => {
6
7
  if (process.env.BROWSER_ENV === 'browser') {
@@ -157,6 +158,96 @@ class ResponseMiddleware {
157
158
  return mr;
158
159
  }
159
160
  }
161
+ function createTreeNode() {
162
+ return { files: [], children: new Map() };
163
+ }
164
+ function buildFileTree(files, middlewareResponse) {
165
+ const root = createTreeNode();
166
+ for (const file of files) {
167
+ if (!file.stateKey) {
168
+ root.files.push(file);
169
+ continue;
170
+ }
171
+ const parts = file.stateKey.split(':');
172
+ let node = root;
173
+ for (const part of parts) {
174
+ if (!node.children.has(part)) {
175
+ node.children.set(part, createTreeNode());
176
+ }
177
+ node = node.children.get(part);
178
+ }
179
+ node.files.push(file);
180
+ }
181
+ if (middlewareResponse) {
182
+ for (const [key, mw] of Object.entries(middlewareResponse)) {
183
+ const parts = key.split(':');
184
+ let node = root;
185
+ for (const part of parts) {
186
+ if (!node.children.has(part)) {
187
+ node.children.set(part, createTreeNode());
188
+ }
189
+ node = node.children.get(part);
190
+ }
191
+ node.middleware = mw;
192
+ }
193
+ }
194
+ return root;
195
+ }
196
+ function mergeFileTree(target, source) {
197
+ target.files.push(...source.files);
198
+ if (source.middleware) {
199
+ if (!target.middleware) {
200
+ target.middleware = source.middleware;
201
+ }
202
+ else {
203
+ console.warn(`[mergeFileTree] middleware conflict at same stateKey, keeping first (${target.middleware.path}), discarding (${source.middleware.path})`);
204
+ }
205
+ }
206
+ for (const [key, child] of source.children) {
207
+ if (target.children.has(key)) {
208
+ mergeFileTree(target.children.get(key), child);
209
+ }
210
+ else {
211
+ target.children.set(key, child);
212
+ }
213
+ }
214
+ }
215
+ class MiddlewareTree {
216
+ #cache = null;
217
+ #cacheVersion = -1;
218
+ get value() {
219
+ if (this.#cacheVersion === _storeVersion && this.#cache !== null) {
220
+ return this.#cache;
221
+ }
222
+ const root = createTreeNode();
223
+ for (const appKey of Object.keys(alemonjsCore.storeChildrenApp)) {
224
+ const app = alemonjsCore.storeChildrenApp[appKey];
225
+ const subTree = buildFileTree(app.middleware ?? [], undefined);
226
+ mergeFileTree(root, subTree);
227
+ }
228
+ this.#cache = root;
229
+ this.#cacheVersion = _storeVersion;
230
+ return this.#cache;
231
+ }
232
+ }
233
+ class ResponseTree {
234
+ #cache = null;
235
+ #cacheVersion = -1;
236
+ get value() {
237
+ if (this.#cacheVersion === _storeVersion && this.#cache !== null) {
238
+ return this.#cache;
239
+ }
240
+ const root = createTreeNode();
241
+ for (const appKey of Object.keys(alemonjsCore.storeChildrenApp)) {
242
+ const app = alemonjsCore.storeChildrenApp[appKey];
243
+ const subTree = buildFileTree(app.response ?? [], app.middlewareResponse);
244
+ mergeFileTree(root, subTree);
245
+ }
246
+ this.#cache = root;
247
+ this.#cacheVersion = _storeVersion;
248
+ return this.#cache;
249
+ }
250
+ }
160
251
  class ResponseRouter {
161
252
  #cache = null;
162
253
  #cacheVersion = -1;
@@ -334,6 +425,7 @@ class ChildrenApp {
334
425
  bumpStoreVersion();
335
426
  }
336
427
  un() {
428
+ disposeExpose(this.#name);
337
429
  delete alemonjsCore.storeChildrenApp[this.#name];
338
430
  bumpStoreVersion();
339
431
  }
@@ -357,4 +449,4 @@ process?.on?.('exit', code => {
357
449
  logger.info?.(`[alemonjs][exit] 进程退出,code=${code}`);
358
450
  });
359
451
 
360
- export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, State, StateSubscribe, SubscribeList, bumpStoreVersion, core, getSubscribeList, logger };
452
+ export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, MiddlewareTree, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, ResponseTree, State, StateSubscribe, SubscribeList, bumpStoreVersion, core, getSubscribeList, logger };
@@ -14,10 +14,10 @@ import { onProcessor } from '../../app/event-processor.js';
14
14
  import '../../app/event-response.js';
15
15
  import { createResult } from '../../core/utils.js';
16
16
  import '../../app/hook-event-context.js';
17
- import '../../app/event-utils.js';
18
17
  import { apiResolves, apiTimeouts, actionResolves, actionTimeouts, FULL_RECEIVE_HEADER } from '../processor/config.js';
19
18
  import { setDirectSend } from '../processor/transport.js';
20
19
  import '../../app/message-format-old.js';
20
+ import '../../app/event-utils.js';
21
21
  import '../../app/message-api.js';
22
22
  import { createWSConnector } from './base.js';
23
23
  import { createDirectServer } from '../../process/direct-channel.js';
@@ -21,8 +21,8 @@ class App extends Component {
21
21
  }
22
22
  a { color: #0099cc; }
23
23
  `;
24
- const head = Head(null, Title('欢迎使用 AlemonJS!'), Style(style));
25
- const body = Body(null, H1('AlemonJS 启动成功!'), P(null, '已成功通过 ', A({ href: 'https://alemonjs.com', target: '_blank' }, 'AlemonJS 框架'), ' 启动。'), Div({ className: 'footer' }, '— 感谢选择 AlemonJS。'));
24
+ const head = Head(null, Title('欢迎使用 ALemonJS!'), Style(style));
25
+ const body = Body(null, H1('ALemonJS 启动成功!'), P(null, '已成功通过 ', A({ href: 'https://alemonjs.com', target: '_blank' }, 'ALemonJS 框架'), ' 启动。'), Div({ className: 'footer' }, '— 感谢选择 ALemonJS。'));
26
26
  return Html(null, head, body);
27
27
  }
28
28
  }
package/lib/client.js CHANGED
@@ -27,8 +27,8 @@ import './app/event-middleware.js';
27
27
  import './app/event-processor.js';
28
28
  import './app/event-response.js';
29
29
  import './app/hook-event-context.js';
30
- import './app/event-utils.js';
31
30
  import './app/message-format-old.js';
31
+ import './app/event-utils.js';
32
32
  import './app/message-api.js';
33
33
  import './process/platform.js';
34
34
  import './process/module.js';
package/lib/index.js CHANGED
@@ -5,14 +5,15 @@ export { createEventName, createHash, createResult, createUserHashKey, fastHash,
5
5
  export { cbpClient } from './cbp/connects/client.js';
6
6
  export { cbpPlatform } from './cbp/connects/platform.js';
7
7
  export { cbpServer } from './cbp/server/main.js';
8
- export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, State, StateSubscribe, SubscribeList, bumpStoreVersion, core, getSubscribeList, logger } from './app/store.js';
8
+ export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, MiddlewareTree, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, ResponseTree, State, StateSubscribe, SubscribeList, bumpStoreVersion, core, getSubscribeList, logger } from './app/store.js';
9
+ export { Expose, clearAllExpose, disposeExpose, registerExpose } from './app/expose.js';
9
10
  export { loadModels, run } from './app/load_modules/load.js';
10
11
  export { loadChildren, loadChildrenFile } from './app/load_modules/loadChild.js';
11
12
  export { defineChildren } from './app/define-children.js';
12
13
  export { definePlatform } from './app/define-platform.js';
13
14
  export { defineResponse } from './app/define-response.js';
14
15
  export { defineMiddleware } from './app/define-middleware.js';
15
- export { defineRouter, lazy } from './app/define-router.js';
16
+ export { defineRouter, lazy, runHandler } from './app/define-router.js';
16
17
  export { FormatEvent, wrapEvent } from './app/event-format.js';
17
18
  export { onGroup } from './app/event-group.js';
18
19
  export { OnMiddleware, onMiddleware } from './app/event-middleware.js';
@@ -21,8 +21,8 @@ class App extends Component {
21
21
  }
22
22
  a { color: #0099cc; }
23
23
  `;
24
- const head = Head(null, Title('欢迎使用 AlemonJS!'), Style(style));
25
- const body = Body(null, H1('AlemonJS 启动成功!'), P(null, '已成功通过 ', A({ href: 'https://alemonjs.com', target: '_blank' }, 'AlemonJS 框架'), ' 启动。'), P(null, '如果想访问主应用,请访问, ', A({ href: '/app', target: '_blank' }, '/app'), '(对应根目录index.html)'), P(null, '如果想访问其他应用,请访问 ', A({ href: '/apps/[package-name]', target: '_blank' }, '/apps/[package-name]'), '。(对应/packages/[package-name]/index.html)'), Div({ className: 'footer' }, '— 感谢选择 AlemonJS。'));
24
+ const head = Head(null, Title('欢迎使用 ALemonJS!'), Style(style));
25
+ const body = Body(null, H1('ALemonJS 启动成功!'), P(null, '已成功通过 ', A({ href: 'https://alemonjs.com', target: '_blank' }, 'ALemonJS 框架'), ' 启动。'), P(null, '如果想访问主应用,请访问, ', A({ href: '/app', target: '_blank' }, '/app'), '(对应根目录index.html)'), P(null, '如果想访问其他应用,请访问 ', A({ href: '/apps/[package-name]', target: '_blank' }, '/apps/[package-name]'), '。(对应/packages/[package-name]/index.html)'), Div({ className: 'footer' }, '— 感谢选择 ALemonJS。'));
26
26
  return Html(null, head, body);
27
27
  }
28
28
  }
@@ -2,6 +2,7 @@ import { ChildrenCycle, Next } from '../cycle';
2
2
  import { ClientAPI } from '../client';
3
3
  import { EventKeys, Events } from './map';
4
4
  import { DataEnums } from '../message';
5
+ import { Expose } from '../../app/expose';
5
6
  export type Current<T extends EventKeys> = (event: Events[T], next: Next) => Promise<boolean | void | undefined> | boolean | void | undefined;
6
7
  export type OnResponseValue<C, T extends EventKeys> = {
7
8
  current: C;
@@ -54,6 +55,7 @@ export type childrenCallbackRes = {
54
55
  middleware?: ReturnType<defineMiddlewareFunc>;
55
56
  responseRouter?: ReturnType<DefineRouterFunc>;
56
57
  middlewareRouter?: ReturnType<DefineRouterFunc>;
58
+ expose?: Expose;
57
59
  } | undefined;
58
60
  export type childrenCallback = ChildrenCycle & {
59
61
  register?: () => (childrenCallbackRes | undefined) | Promise<childrenCallbackRes | undefined>;
@@ -0,0 +1,41 @@
1
+ export interface ExposeActionMeta {
2
+ description: string;
3
+ params?: Record<string, string>;
4
+ returns?: Record<string, string>;
5
+ handler: (...args: any[]) => any;
6
+ }
7
+ export interface ExposeProvideConfig {
8
+ name: string;
9
+ description: string;
10
+ actions: Record<string, ExposeActionMeta>;
11
+ }
12
+ export interface ExposeListItem {
13
+ name: string;
14
+ protocol: string;
15
+ description: string;
16
+ actions: Record<string, {
17
+ description: string;
18
+ params?: Record<string, string>;
19
+ returns?: Record<string, string>;
20
+ }>;
21
+ invoke: (action: string, value?: any) => any;
22
+ }
23
+ export interface ExposeWatchEvent {
24
+ name: string;
25
+ protocol: string;
26
+ action: string;
27
+ value?: any;
28
+ }
29
+ export interface ExposeSchemaItem {
30
+ protocol: string;
31
+ description: string;
32
+ providers: {
33
+ name: string;
34
+ }[];
35
+ actions: Record<string, {
36
+ description: string;
37
+ params?: Record<string, string>;
38
+ returns?: Record<string, string>;
39
+ }>;
40
+ invoke: string;
41
+ }
@@ -0,0 +1 @@
1
+
@@ -23,6 +23,7 @@ export * from './message/index';
23
23
  export * from './package/index';
24
24
  export * from './state/index';
25
25
  export * from './store/res';
26
+ export * from './expose/index';
26
27
  export * from './subscribe';
27
28
  export * from './standard';
28
29
  export * from './actions';
@@ -23,6 +23,11 @@ export type StoreMiddlewareItem = {
23
23
  select: string;
24
24
  } | null;
25
25
  };
26
+ export type FileTreeNode = {
27
+ middleware?: StoreResponseItem;
28
+ files: StoreResponseItem[];
29
+ children: Map<string, FileTreeNode>;
30
+ };
26
31
  export type StoreMiddleware = {
27
32
  [key in EventKeys]: StoreResponseItem[];
28
33
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alemonjs",
3
- "version": "2.1.52",
3
+ "version": "2.1.54",
4
4
  "description": "bot script",
5
5
  "author": "lemonade",
6
6
  "license": "MIT",
@@ -69,5 +69,5 @@
69
69
  "type": "git",
70
70
  "url": "https://github.com/lemonade-lab/alemonjs.git"
71
71
  },
72
- "gitHead": "90cafaf98cefa01152764922f58e543f0c513811"
73
- }
72
+ "gitHead": "c6aa5616afe091a37610dad22fbb2d2618d943b8"
73
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2013-present, Yuxi (Evan) You
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIdED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.