alemonjs 2.1.79 → 2.1.81

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.
@@ -96,29 +96,41 @@ const finishCurrentTrace = (reason) => {
96
96
  const getTargetEvent = (event) => {
97
97
  return event ?? eventStore.getStore()?.event;
98
98
  };
99
+ const setSendAttempted = (target) => {
100
+ target._sendAttempted = true;
101
+ target._has_send_attempt = true;
102
+ };
103
+ const setSendSucceeded = (target) => {
104
+ target._sendSucceeded = true;
105
+ target._has_send_success = true;
106
+ };
107
+ const setLastSendError = (target, error) => {
108
+ target._lastSendError = error;
109
+ target._last_send_error = error;
110
+ };
99
111
  const markEventSendAttempt = (event) => {
100
112
  const target = getTargetEvent(event);
101
113
  if (!target) {
102
114
  return;
103
115
  }
104
- target._has_send_attempt = true;
116
+ setSendAttempted(target);
105
117
  };
106
118
  const markEventSendSuccess = (event) => {
107
119
  const target = getTargetEvent(event);
108
120
  if (!target) {
109
121
  return;
110
122
  }
111
- target._has_send_attempt = true;
112
- target._has_send_success = true;
113
- target._last_send_error = null;
123
+ setSendAttempted(target);
124
+ setSendSucceeded(target);
125
+ setLastSendError(target, null);
114
126
  };
115
127
  const markEventSendFailure = (error, event) => {
116
128
  const target = getTargetEvent(event);
117
129
  if (!target) {
118
130
  return;
119
131
  }
120
- target._has_send_attempt = true;
121
- target._last_send_error = error instanceof Error ? error.message : typeof error === 'string' ? error : 'Unknown send error';
132
+ setSendAttempted(target);
133
+ setLastSendError(target, error instanceof Error ? error.message : typeof error === 'string' ? error : 'Unknown send error');
122
134
  };
123
135
  const recordEventSendResults = (results, event) => {
124
136
  markEventSendAttempt(event);
@@ -12,6 +12,7 @@ export * from './permission';
12
12
  export * from './reaction';
13
13
  export * from './request';
14
14
  export * from './role';
15
+ export * from './route';
15
16
  export * from './user';
16
17
  export * from './subscribe';
17
18
  export * from './event';
@@ -12,6 +12,7 @@ export { usePermission } from './permission.js';
12
12
  export { useReaction } from './reaction.js';
13
13
  export { useRequest } from './request.js';
14
14
  export { useRole } from './role.js';
15
+ export { useRoute } from './route.js';
15
16
  export { useUser } from './user.js';
16
17
  export { useObserver, useSubscribe } from './subscribe.js';
17
18
  export { createEvent, useEvent } from './event.js';
@@ -0,0 +1,32 @@
1
+ import type { EventKeys, Events } from '../../types';
2
+ import type { RouteParams } from '../router/types';
3
+ import type { RouteSchemaValue } from '../router/validator';
4
+ type RouteReader = {
5
+ param: (name: string) => RouteSchemaValue | undefined;
6
+ hasParam: (name: string) => boolean;
7
+ };
8
+ export type UseRouteUnmatched = RouteReader & {
9
+ matched: false;
10
+ key: undefined;
11
+ text: undefined;
12
+ sourceText: undefined;
13
+ rewrittenText: undefined;
14
+ rawArgs: [];
15
+ parsedArgs: [];
16
+ params: {
17
+ [key: string]: any;
18
+ };
19
+ };
20
+ export type UseRouteMatched = RouteReader & {
21
+ matched: true;
22
+ key: string;
23
+ text: string;
24
+ sourceText?: string;
25
+ rewrittenText: string;
26
+ rawArgs: string[];
27
+ parsedArgs: RouteSchemaValue[];
28
+ params: RouteParams;
29
+ };
30
+ export type UseRouteResult = UseRouteUnmatched | UseRouteMatched;
31
+ export declare const useRoute: <T extends EventKeys>(event?: Events[T]) => readonly [UseRouteResult];
32
+ export {};
@@ -0,0 +1,39 @@
1
+ import { getEventOrThrow } from './common.js';
2
+
3
+ const createRouteReader = (params) => ({
4
+ param: (name) => params[name],
5
+ hasParam: (name) => Object.prototype.hasOwnProperty.call(params, name) && params[name] !== undefined
6
+ });
7
+ const createEmptyRoute = () => ({
8
+ matched: false,
9
+ key: undefined,
10
+ text: undefined,
11
+ sourceText: undefined,
12
+ rewrittenText: undefined,
13
+ rawArgs: [],
14
+ parsedArgs: [],
15
+ params: {},
16
+ ...createRouteReader({})
17
+ });
18
+ const useRoute = (event) => {
19
+ const currentEvent = getEventOrThrow(event);
20
+ const route = currentEvent.__route;
21
+ if (!route) {
22
+ return [createEmptyRoute()];
23
+ }
24
+ return [
25
+ {
26
+ matched: true,
27
+ key: route.key,
28
+ text: route.text,
29
+ sourceText: route.sourceText,
30
+ rewrittenText: route.rewrittenText ?? route.text,
31
+ rawArgs: [...route.rawArgs],
32
+ parsedArgs: [...route.parsedArgs],
33
+ params: { ...route.params },
34
+ ...createRouteReader(route.params)
35
+ }
36
+ ];
37
+ };
38
+
39
+ export { useRoute };
package/lib/app/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, MiddlewareTree, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, ResponseTree, 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, clearRuntimeAppKoaRouters, core, disposeAllRuntimeApps, disposeRuntimeApp, getRuntimeApp, getRuntimeAppKoaRouters, getSubscribeList, hasRuntimeAppCapability, listRuntimeAppKoaRouters, listRuntimeApps, logger, registerRuntimeApp, setRuntimeAppKoaRouters, toRuntimeAppSnapshot, updateRuntimeAppCapabilities, updateRuntimeAppStatus } from './store.js';
2
2
  export { Expose, clearAllExpose, disposeExpose, registerExpose } from './expose.js';
3
3
  export { loadModels, run } from './load_modules/load.js';
4
4
  export { loadChildren, loadChildrenFile } from './load_modules/loadChild.js';
@@ -30,6 +30,7 @@ export { usePermission } from './hook-use/permission.js';
30
30
  export { useReaction } from './hook-use/reaction.js';
31
31
  export { useRequest } from './hook-use/request.js';
32
32
  export { useRole } from './hook-use/role.js';
33
+ export { useRoute } from './hook-use/route.js';
33
34
  export { useUser } from './hook-use/user.js';
34
35
  export { useObserver, useSubscribe } from './hook-use/subscribe.js';
35
36
  export { createEvent, useEvent } from './hook-use/event.js';
@@ -1,21 +1,36 @@
1
1
  import { getConfig } from '../../core/config.js';
2
2
  import { loadChildren, loadChildrenFile } from './loadChild.js';
3
- import { join } from 'path';
3
+ import { join, dirname } from 'path';
4
4
  import { existsSync } from 'fs';
5
5
  import { ResultCode } from '../../core/variable.js';
6
+ import { registerRuntimeApp } from '../store.js';
6
7
 
7
8
  const loadApps = () => {
8
9
  const cfg = getConfig();
9
- const apps = Array.isArray(cfg.value?.apps) ? cfg.value.apps : Object.keys(cfg.value?.apps ?? {}).filter(Boolean);
10
+ const apps = Array.isArray(cfg.value?.apps)
11
+ ? cfg.value.apps.filter(Boolean)
12
+ : Object.entries(cfg.value?.apps ?? {})
13
+ .filter(([, enabled]) => Boolean(enabled))
14
+ .map(([name]) => name);
10
15
  const uniqueApps = Array.from(new Set(apps));
11
- uniqueApps.forEach(app => void loadChildrenFile(app));
16
+ uniqueApps.forEach(app => {
17
+ registerRuntimeApp({
18
+ name: app,
19
+ kind: 'plugin',
20
+ enabled: true,
21
+ status: 'discovered',
22
+ rootDir: '',
23
+ mainPath: ''
24
+ });
25
+ void loadChildrenFile(app);
26
+ });
12
27
  };
13
28
  const run = (input) => {
14
29
  if (!input) {
15
30
  return;
16
31
  }
17
32
  const mainPath = join(process.cwd(), input);
18
- if (!existsSync(input)) {
33
+ if (!existsSync(mainPath)) {
19
34
  logger.warn({
20
35
  code: ResultCode.Warn,
21
36
  message: '未找到主要入口文件',
@@ -23,6 +38,14 @@ const run = (input) => {
23
38
  });
24
39
  return;
25
40
  }
41
+ registerRuntimeApp({
42
+ name: 'main',
43
+ kind: 'main',
44
+ enabled: true,
45
+ status: 'discovered',
46
+ rootDir: join(process.cwd(), dirname(input)),
47
+ mainPath
48
+ });
26
49
  void loadChildren(mainPath, 'main');
27
50
  };
28
51
  function loadModels() {
@@ -1,7 +1,7 @@
1
1
  import { dirname, join } from 'path';
2
2
  import { existsSync } from 'fs';
3
3
  import { showErrorModule, getRecursiveDirFiles, createEventName } from '../../core/utils.js';
4
- import { ChildrenApp } from '../store.js';
4
+ import { registerRuntimeApp, updateRuntimeAppStatus, ChildrenApp, clearRuntimeAppKoaRouters, setRuntimeAppKoaRouters, updateRuntimeAppCapabilities } from '../store.js';
5
5
  import { registerExpose } from '../expose.js';
6
6
  import { ResultCode, fileSuffixMiddleware } from '../../core/variable.js';
7
7
  import { registerAppDir, scheduleCancelByApp, unregisterAppDir } from '../schedule-store.js';
@@ -10,6 +10,33 @@ import module$1 from 'module';
10
10
  const initRequire = () => { };
11
11
  initRequire.resolve = () => '';
12
12
  const require$1 = module$1?.createRequire?.(import.meta.url) ?? initRequire;
13
+ const resolvePackageRoot = (startDir) => {
14
+ let currentDir = startDir;
15
+ while (currentDir && currentDir !== dirname(currentDir)) {
16
+ if (existsSync(join(currentDir, 'package.json'))) {
17
+ return currentDir;
18
+ }
19
+ currentDir = dirname(currentDir);
20
+ }
21
+ return startDir;
22
+ };
23
+ const detectWebCapability = (startDir) => {
24
+ const packageRoot = resolvePackageRoot(startDir);
25
+ const packageJsonPath = join(packageRoot, 'package.json');
26
+ if (!existsSync(packageJsonPath)) {
27
+ return existsSync(join(packageRoot, 'index.html'));
28
+ }
29
+ try {
30
+ const pkg = require$1(packageJsonPath) ?? {};
31
+ const root = pkg?.alemonjs?.web?.root;
32
+ if (typeof root === 'string' && root.trim()) {
33
+ return existsSync(join(packageRoot, root));
34
+ }
35
+ }
36
+ catch {
37
+ }
38
+ return existsSync(join(packageRoot, 'index.html'));
39
+ };
13
40
  const loadChildren = async (mainPath, appName) => {
14
41
  if (!mainPath || typeof mainPath !== 'string') {
15
42
  logger.error({
@@ -21,6 +48,24 @@ const loadChildren = async (mainPath, appName) => {
21
48
  }
22
49
  const mainDir = dirname(mainPath);
23
50
  const App = new ChildrenApp(appName);
51
+ const kind = appName === 'main' ? 'main' : 'plugin';
52
+ const baseCapabilities = {
53
+ event: false,
54
+ httpApi: existsSync(join(mainDir, 'route', 'api')),
55
+ web: detectWebCapability(mainDir),
56
+ schedule: false,
57
+ expose: false
58
+ };
59
+ registerRuntimeApp({
60
+ name: appName,
61
+ kind,
62
+ enabled: true,
63
+ status: 'discovered',
64
+ rootDir: mainDir,
65
+ mainPath,
66
+ capabilities: baseCapabilities
67
+ });
68
+ updateRuntimeAppStatus(appName, 'loading');
24
69
  registerAppDir(appName, mainDir);
25
70
  try {
26
71
  const moduleApp = await import(`file://${mainPath}`);
@@ -46,6 +91,8 @@ const loadChildren = async (mainPath, appName) => {
46
91
  App.pushCycle(app);
47
92
  const unMounted = async (e) => {
48
93
  showErrorModule(e);
94
+ clearRuntimeAppKoaRouters(appName);
95
+ updateRuntimeAppStatus(appName, 'failed', e);
49
96
  scheduleCancelByApp(appName);
50
97
  unregisterAppDir(appName);
51
98
  App.un();
@@ -69,17 +116,28 @@ const loadChildren = async (mainPath, appName) => {
69
116
  }
70
117
  const registerMounted = async () => {
71
118
  const res = await app?.register();
119
+ const hasEventCapability = Boolean(res && (res?.response || res?.middleware || res?.responseRouter || res?.middlewareRouter));
120
+ const hasExposeCapability = Boolean(res?.expose);
121
+ const hasKoaRouterCapability = Boolean(res?.koaRouter);
72
122
  if (res && (res?.response || res?.middleware || res?.responseRouter || res?.middlewareRouter)) {
73
123
  App.register(res);
74
124
  }
125
+ setRuntimeAppKoaRouters(appName, res?.koaRouter);
75
126
  if (res?.expose) {
76
127
  registerExpose(appName, res.expose.getConfigs());
77
128
  }
129
+ updateRuntimeAppCapabilities(appName, {
130
+ ...baseCapabilities,
131
+ httpApi: baseCapabilities.httpApi || hasKoaRouterCapability,
132
+ event: hasEventCapability,
133
+ expose: hasExposeCapability
134
+ });
78
135
  App.on();
79
136
  try {
80
137
  if (app?.onMounted) {
81
138
  await app.onMounted({ response: [], responseMiddleware: {}, middleware: [] });
82
139
  }
140
+ updateRuntimeAppStatus(appName, 'ready');
83
141
  }
84
142
  catch (e) {
85
143
  void unMounted(e);
@@ -139,11 +197,16 @@ const loadChildren = async (mainPath, appName) => {
139
197
  mwData.push(middleware);
140
198
  }
141
199
  App.pushMiddleware(mwData);
200
+ updateRuntimeAppCapabilities(appName, {
201
+ ...baseCapabilities,
202
+ event: resData.length > 0 || Object.keys(resAndMwData).length > 0 || mwData.length > 0
203
+ });
142
204
  App.on();
143
205
  try {
144
206
  if (app?.onMounted) {
145
207
  await app.onMounted({ response: resData, responseMiddleware: resAndMwData, middleware: mwData });
146
208
  }
209
+ updateRuntimeAppStatus(appName, 'ready');
147
210
  }
148
211
  catch (e) {
149
212
  void unMounted(e);
@@ -163,6 +226,8 @@ const loadChildren = async (mainPath, appName) => {
163
226
  }
164
227
  catch (e) {
165
228
  showErrorModule(e);
229
+ clearRuntimeAppKoaRouters(appName);
230
+ updateRuntimeAppStatus(appName, 'failed', e);
166
231
  App.un();
167
232
  }
168
233
  };
@@ -178,6 +243,7 @@ const loadChildrenFile = (appName) => {
178
243
  try {
179
244
  const mainPath = require$1.resolve(appName);
180
245
  if (!existsSync(mainPath)) {
246
+ updateRuntimeAppStatus(appName, 'failed', new Error('The main file does not exist,' + mainPath));
181
247
  logger.error({
182
248
  code: ResultCode.FailParams,
183
249
  message: 'The main file does not exist,' + mainPath,
@@ -185,9 +251,18 @@ const loadChildrenFile = (appName) => {
185
251
  });
186
252
  return;
187
253
  }
254
+ registerRuntimeApp({
255
+ name: appName,
256
+ kind: 'plugin',
257
+ enabled: true,
258
+ status: 'discovered',
259
+ rootDir: dirname(mainPath),
260
+ mainPath
261
+ });
188
262
  void loadChildren(mainPath, appName);
189
263
  }
190
264
  catch (e) {
265
+ updateRuntimeAppStatus(appName, 'failed', e);
191
266
  showErrorModule(e);
192
267
  }
193
268
  };
@@ -545,6 +545,8 @@ class Router {
545
545
  attachRouteContext(event, {
546
546
  key: match.route.config.path,
547
547
  text: match.normalizedCommand,
548
+ sourceText: typeof event.MessageText === 'string' ? event.MessageText : undefined,
549
+ rewrittenText: match.normalizedCommand,
548
550
  rawArgs: match.rawArgs,
549
551
  parsedArgs: [],
550
552
  params: {}
@@ -570,6 +572,8 @@ class Router {
570
572
  attachRouteContext(event, {
571
573
  key: match.route.config.path,
572
574
  text: match.normalizedCommand,
575
+ sourceText: initialMessageText,
576
+ rewrittenText: match.normalizedCommand,
573
577
  rawArgs: match.rawArgs,
574
578
  parsedArgs: match.parsedArgs,
575
579
  params
@@ -606,6 +610,8 @@ class Router {
606
610
  attachRouteContext(event, {
607
611
  key: '',
608
612
  text: scopedText,
613
+ sourceText: typeof event.MessageText === 'string' ? event.MessageText : undefined,
614
+ rewrittenText: scopedText,
609
615
  rawArgs: [],
610
616
  parsedArgs: [],
611
617
  params: {}
@@ -3,6 +3,8 @@ export type RouteParams = Record<string, RouteSchemaValue | undefined>;
3
3
  export type RouteContext = {
4
4
  key: string;
5
5
  text: string;
6
+ sourceText?: string;
7
+ rewrittenText?: string;
6
8
  rawArgs: string[];
7
9
  parsedArgs: RouteSchemaValue[];
8
10
  params: RouteParams;
@@ -1,5 +1,31 @@
1
1
  import { SinglyLinkedList } from './SinglyLinkedList';
2
2
  import { childrenCallbackRes, ChildrenCycle, EventCycleEnum, EventKeys, FileTreeNode, StoreMiddlewareItem, StoreResponseItem, SubscribeValue } from '../types';
3
+ import type KoaRouter from 'koa-router';
4
+ export type RuntimeAppStatus = 'discovered' | 'loading' | 'ready' | 'failed' | 'disposed';
5
+ export type RuntimeAppCapability = {
6
+ event: boolean;
7
+ httpApi: boolean;
8
+ web: boolean;
9
+ schedule: boolean;
10
+ expose: boolean;
11
+ };
12
+ export type RuntimeAppError = {
13
+ message: string;
14
+ stack?: string;
15
+ time: number;
16
+ };
17
+ export type RuntimeAppRecord = {
18
+ name: string;
19
+ kind: 'main' | 'plugin';
20
+ enabled: boolean;
21
+ status: RuntimeAppStatus;
22
+ rootDir: string;
23
+ mainPath: string;
24
+ error?: RuntimeAppError;
25
+ capabilities: RuntimeAppCapability;
26
+ createdAt: number;
27
+ updatedAt: number;
28
+ };
3
29
  export declare class Logger {
4
30
  #private;
5
31
  constructor();
@@ -14,8 +40,92 @@ export declare class Core {
14
40
  storeChildrenApp: {
15
41
  [key: string]: import("../types").StoreChildrenApp;
16
42
  };
43
+ runtimeApps?: {
44
+ [key: string]: RuntimeAppRecord;
45
+ };
46
+ runtimeAppKoaRouters?: {
47
+ [key: string]: KoaRouter[];
48
+ };
17
49
  };
18
50
  }
51
+ export declare const registerRuntimeApp: (record: Omit<RuntimeAppRecord, "createdAt" | "updatedAt" | "capabilities"> & {
52
+ capabilities?: Partial<RuntimeAppCapability>;
53
+ }) => RuntimeAppRecord;
54
+ export declare const updateRuntimeAppStatus: (name: string, status: RuntimeAppStatus, error?: unknown) => RuntimeAppRecord;
55
+ export declare const updateRuntimeAppCapabilities: (name: string, capabilities: Partial<RuntimeAppCapability>) => RuntimeAppRecord;
56
+ export declare const setRuntimeAppKoaRouters: (name: string, koaRouters?: KoaRouter | KoaRouter[]) => KoaRouter<any, {}>[];
57
+ export declare const getRuntimeAppKoaRouters: (name: string) => KoaRouter<any, {}>[];
58
+ export declare const clearRuntimeAppKoaRouters: (name: string) => void;
59
+ export declare const listRuntimeAppKoaRouters: () => {
60
+ name: string;
61
+ routers: KoaRouter<any, {}>[];
62
+ }[];
63
+ export declare const getRuntimeApp: (name: string) => RuntimeAppRecord;
64
+ export declare const toRuntimeAppSnapshot: (item: RuntimeAppRecord) => {
65
+ capabilities: {
66
+ event: boolean;
67
+ httpApi: boolean;
68
+ web: boolean;
69
+ schedule: boolean;
70
+ expose: boolean;
71
+ };
72
+ error: {
73
+ message: string;
74
+ time: number;
75
+ };
76
+ name: string;
77
+ kind: "main" | "plugin";
78
+ enabled: boolean;
79
+ status: RuntimeAppStatus;
80
+ rootDir: string;
81
+ mainPath: string;
82
+ createdAt: number;
83
+ updatedAt: number;
84
+ };
85
+ export declare const listRuntimeApps: () => {
86
+ capabilities: {
87
+ event: boolean;
88
+ httpApi: boolean;
89
+ web: boolean;
90
+ schedule: boolean;
91
+ expose: boolean;
92
+ };
93
+ error: {
94
+ message: string;
95
+ time: number;
96
+ };
97
+ name: string;
98
+ kind: "main" | "plugin";
99
+ enabled: boolean;
100
+ status: RuntimeAppStatus;
101
+ rootDir: string;
102
+ mainPath: string;
103
+ createdAt: number;
104
+ updatedAt: number;
105
+ }[];
106
+ export declare const disposeRuntimeApp: (name: string) => RuntimeAppRecord;
107
+ export declare const disposeAllRuntimeApps: () => {
108
+ capabilities: {
109
+ event: boolean;
110
+ httpApi: boolean;
111
+ web: boolean;
112
+ schedule: boolean;
113
+ expose: boolean;
114
+ };
115
+ error: {
116
+ message: string;
117
+ time: number;
118
+ };
119
+ name: string;
120
+ kind: "main" | "plugin";
121
+ enabled: boolean;
122
+ status: RuntimeAppStatus;
123
+ rootDir: string;
124
+ mainPath: string;
125
+ createdAt: number;
126
+ updatedAt: number;
127
+ }[];
128
+ export declare const hasRuntimeAppCapability: (name: string, capability: keyof RuntimeAppCapability) => boolean;
19
129
  export declare const bumpStoreVersion: () => void;
20
130
  export declare class Response {
21
131
  #private;
@@ -87,4 +197,10 @@ export declare const core: {
87
197
  storeChildrenApp: {
88
198
  [key: string]: import("../types").StoreChildrenApp;
89
199
  };
200
+ runtimeApps?: {
201
+ [key: string]: RuntimeAppRecord;
202
+ };
203
+ runtimeAppKoaRouters?: {
204
+ [key: string]: KoaRouter[];
205
+ };
90
206
  };
package/lib/app/store.js CHANGED
@@ -107,7 +107,9 @@ class Core {
107
107
  mount: new Map(),
108
108
  unmount: new Map()
109
109
  },
110
- storeChildrenApp: {}
110
+ storeChildrenApp: {},
111
+ runtimeApps: {},
112
+ runtimeAppKoaRouters: {}
111
113
  };
112
114
  }
113
115
  }
@@ -116,6 +118,203 @@ class Core {
116
118
  }
117
119
  }
118
120
  let _storeVersion = 0;
121
+ const createEmptyRuntimeCapabilities = () => ({
122
+ event: false,
123
+ httpApi: false,
124
+ web: false,
125
+ schedule: false,
126
+ expose: false
127
+ });
128
+ const logRuntimeAppStatus = (level, record) => {
129
+ if (!global.logger?.[level]) {
130
+ return;
131
+ }
132
+ global.logger[level]({
133
+ message: 'runtime app status',
134
+ data: {
135
+ app: record.name,
136
+ kind: record.kind,
137
+ status: record.status,
138
+ capabilities: record.capabilities,
139
+ error: record.error?.message ?? null
140
+ }
141
+ });
142
+ };
143
+ const normalizeRuntimeAppError = (error) => {
144
+ if (!error) {
145
+ return undefined;
146
+ }
147
+ if (error instanceof Error) {
148
+ return {
149
+ message: error.message,
150
+ stack: error.stack,
151
+ time: Date.now()
152
+ };
153
+ }
154
+ return {
155
+ message: typeof error === 'string' ? error : 'Unknown runtime app error',
156
+ time: Date.now()
157
+ };
158
+ };
159
+ const sameRuntimeAppCapabilities = (left, right) => {
160
+ return (left.event === right.event &&
161
+ left.httpApi === right.httpApi &&
162
+ left.web === right.web &&
163
+ left.schedule === right.schedule &&
164
+ left.expose === right.expose);
165
+ };
166
+ const sameRuntimeAppError = (left, right) => {
167
+ if (!left && !right) {
168
+ return true;
169
+ }
170
+ if (!left || !right) {
171
+ return false;
172
+ }
173
+ return left.message === right.message && left.stack === right.stack;
174
+ };
175
+ const getRuntimeAppStore = () => {
176
+ if (!global.alemonjsCore.runtimeApps) {
177
+ global.alemonjsCore.runtimeApps = {};
178
+ }
179
+ return global.alemonjsCore.runtimeApps;
180
+ };
181
+ const getRuntimeAppKoaRouterStore = () => {
182
+ if (!global.alemonjsCore.runtimeAppKoaRouters) {
183
+ global.alemonjsCore.runtimeAppKoaRouters = {};
184
+ }
185
+ return global.alemonjsCore.runtimeAppKoaRouters;
186
+ };
187
+ const registerRuntimeApp = (record) => {
188
+ const runtimeApps = getRuntimeAppStore();
189
+ const current = runtimeApps[record.name];
190
+ const now = Date.now();
191
+ const nextCapabilities = {
192
+ ...(current?.capabilities ?? createEmptyRuntimeCapabilities()),
193
+ ...(record.capabilities ?? {})
194
+ };
195
+ runtimeApps[record.name] = {
196
+ name: record.name,
197
+ kind: record.kind,
198
+ enabled: record.enabled,
199
+ status: record.status,
200
+ rootDir: record.rootDir,
201
+ mainPath: record.mainPath,
202
+ error: record.error,
203
+ capabilities: nextCapabilities,
204
+ createdAt: current?.createdAt ?? now,
205
+ updatedAt: now
206
+ };
207
+ if (!current || current.status !== record.status) {
208
+ logRuntimeAppStatus('info', runtimeApps[record.name]);
209
+ }
210
+ return runtimeApps[record.name];
211
+ };
212
+ const updateRuntimeAppStatus = (name, status, error) => {
213
+ const runtimeApps = getRuntimeAppStore();
214
+ const current = runtimeApps[name];
215
+ if (!current) {
216
+ return;
217
+ }
218
+ const normalizedError = normalizeRuntimeAppError(error);
219
+ if (current.status === status && sameRuntimeAppError(current.error, normalizedError)) {
220
+ return current;
221
+ }
222
+ current.status = status;
223
+ current.updatedAt = Date.now();
224
+ current.error = normalizedError;
225
+ logRuntimeAppStatus(status === 'failed' ? 'warn' : 'info', current);
226
+ return current;
227
+ };
228
+ const updateRuntimeAppCapabilities = (name, capabilities) => {
229
+ const runtimeApps = getRuntimeAppStore();
230
+ const current = runtimeApps[name];
231
+ if (!current) {
232
+ return;
233
+ }
234
+ const nextCapabilities = {
235
+ ...current.capabilities,
236
+ ...capabilities
237
+ };
238
+ if (sameRuntimeAppCapabilities(current.capabilities, nextCapabilities)) {
239
+ return current;
240
+ }
241
+ current.capabilities = nextCapabilities;
242
+ current.updatedAt = Date.now();
243
+ return current;
244
+ };
245
+ const setRuntimeAppKoaRouters = (name, koaRouters) => {
246
+ const koaRouterStore = getRuntimeAppKoaRouterStore();
247
+ if (!koaRouters) {
248
+ delete koaRouterStore[name];
249
+ return [];
250
+ }
251
+ const normalizedRouters = (Array.isArray(koaRouters) ? koaRouters : [koaRouters]).filter(Boolean);
252
+ koaRouterStore[name] = normalizedRouters;
253
+ return normalizedRouters;
254
+ };
255
+ const getRuntimeAppKoaRouters = (name) => {
256
+ return getRuntimeAppKoaRouterStore()[name] ?? [];
257
+ };
258
+ const clearRuntimeAppKoaRouters = (name) => {
259
+ const koaRouterStore = getRuntimeAppKoaRouterStore();
260
+ delete koaRouterStore[name];
261
+ };
262
+ const listRuntimeAppKoaRouters = () => {
263
+ return Object.entries(getRuntimeAppKoaRouterStore())
264
+ .sort(([left], [right]) => {
265
+ if (left === 'main' && right !== 'main') {
266
+ return -1;
267
+ }
268
+ if (left !== 'main' && right === 'main') {
269
+ return 1;
270
+ }
271
+ return left.localeCompare(right);
272
+ })
273
+ .map(([name, routers]) => ({
274
+ name,
275
+ routers: [...routers]
276
+ }));
277
+ };
278
+ const getRuntimeApp = (name) => {
279
+ return getRuntimeAppStore()[name];
280
+ };
281
+ const toRuntimeAppSnapshot = (item) => ({
282
+ ...item,
283
+ capabilities: { ...item.capabilities },
284
+ error: item.error
285
+ ? {
286
+ message: item.error.message,
287
+ time: item.error.time
288
+ }
289
+ : undefined
290
+ });
291
+ const listRuntimeApps = () => {
292
+ return Object.values(getRuntimeAppStore())
293
+ .sort((left, right) => {
294
+ if (left.name === 'main' && right.name !== 'main') {
295
+ return -1;
296
+ }
297
+ if (left.name !== 'main' && right.name === 'main') {
298
+ return 1;
299
+ }
300
+ return left.name.localeCompare(right.name);
301
+ })
302
+ .map(toRuntimeAppSnapshot);
303
+ };
304
+ const disposeRuntimeApp = (name) => {
305
+ clearRuntimeAppKoaRouters(name);
306
+ return updateRuntimeAppStatus(name, 'disposed');
307
+ };
308
+ const disposeAllRuntimeApps = () => {
309
+ const runtimeApps = listRuntimeApps();
310
+ runtimeApps.forEach(app => {
311
+ disposeRuntimeApp(app.name);
312
+ });
313
+ return runtimeApps;
314
+ };
315
+ const hasRuntimeAppCapability = (name, capability) => {
316
+ return Boolean(getRuntimeApp(name)?.capabilities?.[capability]);
317
+ };
119
318
  const bumpStoreVersion = () => {
120
319
  _storeVersion++;
121
320
  };
@@ -449,4 +648,4 @@ process?.on?.('exit', code => {
449
648
  logger.info?.(`[alemonjs][exit] 进程退出,code=${code}`);
450
649
  });
451
650
 
452
- export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, MiddlewareTree, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, ResponseTree, State, StateSubscribe, SubscribeList, bumpStoreVersion, core, getSubscribeList, logger };
651
+ export { ChildrenApp, Core, Logger, Middleware, MiddlewareRouter, MiddlewareTree, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, ResponseTree, State, StateSubscribe, SubscribeList, bumpStoreVersion, clearRuntimeAppKoaRouters, core, disposeAllRuntimeApps, disposeRuntimeApp, getRuntimeApp, getRuntimeAppKoaRouters, getSubscribeList, hasRuntimeAppCapability, listRuntimeAppKoaRouters, listRuntimeApps, logger, registerRuntimeApp, setRuntimeAppKoaRouters, toRuntimeAppSnapshot, updateRuntimeAppCapabilities, updateRuntimeAppStatus };
package/lib/client.js CHANGED
@@ -16,7 +16,7 @@ import '@koa/cors';
16
16
  import './cbp/routers/router.js';
17
17
  import 'lodash';
18
18
  import 'fs/promises';
19
- import './app/store.js';
19
+ import { disposeAllRuntimeApps } from './app/store.js';
20
20
  import { loadModels } from './app/load_modules/load.js';
21
21
  import './app/load_modules/loadChild.js';
22
22
  import './app/define-children.js';
@@ -28,7 +28,7 @@ import './app/event-processor.js';
28
28
  import './app/event-response.js';
29
29
  import './app/hook-event-context.js';
30
30
  import './app/message-format-old.js';
31
- import 'cron';
31
+ import { scheduleCancelByApp, unregisterAppDir } from './app/schedule-store.js';
32
32
  import './app/event-utils.js';
33
33
  import './app/message-api.js';
34
34
  import './process/platform.js';
@@ -36,6 +36,18 @@ import './process/module.js';
36
36
  import { createServer } from './server/main.js';
37
37
 
38
38
  global.__client_loaded = true;
39
+ let runtimeDisposed = false;
40
+ const disposeRuntime = () => {
41
+ if (runtimeDisposed) {
42
+ return;
43
+ }
44
+ runtimeDisposed = true;
45
+ const apps = disposeAllRuntimeApps();
46
+ apps.forEach(app => {
47
+ scheduleCancelByApp(app.name);
48
+ unregisterAppDir(app.name);
49
+ });
50
+ };
39
51
  const mainServer = () => {
40
52
  const port = process.env.serverPort;
41
53
  if (!port) {
@@ -72,10 +84,12 @@ const mainProcess = () => {
72
84
  ['SIGINT', 'SIGTERM', 'SIGQUIT', 'disconnect'].forEach(sig => {
73
85
  process?.on?.(sig, () => {
74
86
  logger.info?.(`[alemonjs][${sig}] 收到信号,正在关闭...`);
87
+ disposeRuntime();
75
88
  setImmediate(() => process.exit(0));
76
89
  });
77
90
  });
78
91
  process?.on?.('exit', code => {
92
+ disposeRuntime();
79
93
  logger.info?.(`[alemonjs][exit] 进程退出,code=${code}`);
80
94
  });
81
95
  process.on('message', msg => {
@@ -86,6 +100,7 @@ const mainProcess = () => {
86
100
  mainServer();
87
101
  }
88
102
  else if (data?.type === 'stop') {
103
+ disposeRuntime();
89
104
  process.exit(0);
90
105
  }
91
106
  }
package/lib/global.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { DefineChildrenFunc, OnResponseReversalFunc, OnMiddlewareReversalFunc, OnSelectsFunc, OnDataFormatFunc, OnResponseReversalFuncBack, OnGroupFunc, OnMiddlewareReversalFuncBack, DefineResponseFunc, defineMiddlewareFunc, StoreChildrenApp, StateSubscribeMap, SubscribeKeysMap, LoggerUtils, ResponseState, StartOptions } from './types';
2
2
  import WebSocket, { Server } from 'ws';
3
3
  import { IncomingMessage } from 'http';
4
+ import type KoaRouter from 'koa-router';
5
+ import type { RuntimeAppRecord } from './app/store.js';
4
6
  declare global {
5
7
  var __config: any;
6
8
  var __options: StartOptions;
@@ -12,6 +14,12 @@ declare global {
12
14
  storeChildrenApp: {
13
15
  [key: string]: StoreChildrenApp;
14
16
  };
17
+ runtimeApps?: {
18
+ [key: string]: RuntimeAppRecord;
19
+ };
20
+ runtimeAppKoaRouters?: {
21
+ [key: string]: KoaRouter[];
22
+ };
15
23
  };
16
24
  var chatbotServer: Server<typeof WebSocket, typeof IncomingMessage>;
17
25
  var chatbotPlatform: WebSocket;
package/lib/index.js CHANGED
@@ -5,7 +5,7 @@ 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, MiddlewareTree, ProcessorEventAutoClearMap, ProcessorEventUserAutoClearMap, Response, ResponseMiddleware, ResponseRouter, ResponseTree, 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, clearRuntimeAppKoaRouters, core, disposeAllRuntimeApps, disposeRuntimeApp, getRuntimeApp, getRuntimeAppKoaRouters, getSubscribeList, hasRuntimeAppCapability, listRuntimeAppKoaRouters, listRuntimeApps, logger, registerRuntimeApp, setRuntimeAppKoaRouters, toRuntimeAppSnapshot, updateRuntimeAppCapabilities, updateRuntimeAppStatus } from './app/store.js';
9
9
  export { Expose, clearAllExpose, disposeExpose, registerExpose } from './app/expose.js';
10
10
  export { loadModels, run } from './app/load_modules/load.js';
11
11
  export { loadChildren, loadChildrenFile } from './app/load_modules/loadChild.js';
@@ -37,6 +37,7 @@ export { usePermission } from './app/hook-use/permission.js';
37
37
  export { useReaction } from './app/hook-use/reaction.js';
38
38
  export { useRequest } from './app/hook-use/request.js';
39
39
  export { useRole } from './app/hook-use/role.js';
40
+ export { useRoute } from './app/hook-use/route.js';
40
41
  export { useUser } from './app/hook-use/user.js';
41
42
  export { useObserver, useSubscribe } from './app/hook-use/subscribe.js';
42
43
  export { createEvent, useEvent } from './app/hook-use/event.js';
package/lib/main.js CHANGED
@@ -58,8 +58,8 @@ const start = (options = {}) => {
58
58
  cbpServer(port, () => {
59
59
  const httpURL = `http://127.0.0.1:${port}`;
60
60
  const wsURL = `ws://127.0.0.1:${port}`;
61
- logger.info(`[CBP server started at ${httpURL}]`);
62
- logger.info(`[CBP server started at ${wsURL}]`);
61
+ logger.info(`[CBP-Server] ${httpURL}`);
62
+ logger.info(`[CBP-Server] ${wsURL}]`);
63
63
  startClient(options);
64
64
  startPlatform(options);
65
65
  });
@@ -67,7 +67,7 @@ const start = (options = {}) => {
67
67
  else {
68
68
  const sockPath = generateSocketPath();
69
69
  process.env.__ALEMON_DIRECT_SOCK = sockPath;
70
- logger.info('[Direct-IPC mode] 平台↔客户端直连通道,无主进程桥接');
70
+ logger.info('[Direct-IPC] 平台↔客户端直连');
71
71
  startClient(options);
72
72
  startPlatform(options);
73
73
  }
@@ -9,14 +9,113 @@ import module$1 from 'module';
9
9
  import { ResultCode } from '../../core/variable.js';
10
10
  import 'yaml';
11
11
  import '../../core/utils.js';
12
+ import { listRuntimeApps, getRuntimeApp, toRuntimeAppSnapshot, listRuntimeAppKoaRouters, hasRuntimeAppCapability, getRuntimeAppKoaRouters } from '../../app/store.js';
12
13
 
13
14
  const initRequire = () => { };
14
15
  initRequire.resolve = () => '';
15
16
  const require$1 = module$1?.createRequire?.(import.meta.url) ?? initRequire;
16
- const mainDirMap = new Map();
17
17
  const router = new KoaRouter({
18
18
  prefix: '/'
19
19
  });
20
+ const resolvePackageRoot = (startDir) => {
21
+ let currentDir = startDir;
22
+ while (currentDir && currentDir !== path__default.dirname(currentDir)) {
23
+ if (existsSync(path__default.join(currentDir, 'package.json'))) {
24
+ return currentDir;
25
+ }
26
+ currentDir = path__default.dirname(currentDir);
27
+ }
28
+ return startDir;
29
+ };
30
+ const readWebRootConfig = (packageRoot) => {
31
+ const packageJsonPath = path__default.join(packageRoot, 'package.json');
32
+ if (!existsSync(packageJsonPath)) {
33
+ return '';
34
+ }
35
+ const pkg = require$1(packageJsonPath) ?? {};
36
+ return pkg?.alemonjs?.web?.root ?? '';
37
+ };
38
+ const denyRuntimeAppAccess = (ctx, appName, capability) => {
39
+ const runtimeApp = getRuntimeApp(appName);
40
+ if (!runtimeApp || !runtimeApp.enabled) {
41
+ ctx.status = 404;
42
+ ctx.body = {
43
+ code: 404,
44
+ message: '应用未注册或未启用',
45
+ data: null
46
+ };
47
+ return null;
48
+ }
49
+ if (runtimeApp.status === 'discovered' || runtimeApp.status === 'loading') {
50
+ ctx.status = 503;
51
+ ctx.body = {
52
+ code: 503,
53
+ message: '应用正在初始化',
54
+ data: {
55
+ app: appName,
56
+ status: runtimeApp.status
57
+ }
58
+ };
59
+ return null;
60
+ }
61
+ if (runtimeApp.status === 'failed') {
62
+ ctx.status = 500;
63
+ ctx.body = {
64
+ code: 500,
65
+ message: '应用生命周期失败',
66
+ data: {
67
+ app: appName,
68
+ status: runtimeApp.status,
69
+ lifecycle: 'failed'
70
+ }
71
+ };
72
+ return null;
73
+ }
74
+ if (runtimeApp.status === 'disposed') {
75
+ ctx.status = 410;
76
+ ctx.body = {
77
+ code: 410,
78
+ message: '应用已卸载或已结束服务',
79
+ data: {
80
+ app: appName,
81
+ status: runtimeApp.status,
82
+ lifecycle: 'disposed'
83
+ }
84
+ };
85
+ return null;
86
+ }
87
+ if (runtimeApp.status !== 'ready' || !hasRuntimeAppCapability(appName, capability)) {
88
+ ctx.status = 404;
89
+ ctx.body = {
90
+ code: 404,
91
+ message: '应用未提供对应服务能力',
92
+ data: null
93
+ };
94
+ return null;
95
+ }
96
+ return runtimeApp;
97
+ };
98
+ const dispatchRegisteredKoaRouters = async (ctx) => {
99
+ const registeredRouters = listRuntimeAppKoaRouters();
100
+ for (const item of registeredRouters) {
101
+ const runtimeApp = getRuntimeApp(item.name);
102
+ if (!runtimeApp || !runtimeApp.enabled || runtimeApp.status !== 'ready' || !hasRuntimeAppCapability(item.name, 'httpApi')) {
103
+ continue;
104
+ }
105
+ const routers = getRuntimeAppKoaRouters(item.name);
106
+ for (const koaRouter of routers) {
107
+ const beforeMatched = Array.isArray(ctx.matched) ? ctx.matched.length : 0;
108
+ await koaRouter.routes()(ctx, async () => { });
109
+ const afterMatched = Array.isArray(ctx.matched) ? ctx.matched.length : 0;
110
+ if (afterMatched <= beforeMatched) {
111
+ continue;
112
+ }
113
+ await koaRouter.allowedMethods()(ctx, async () => { });
114
+ return true;
115
+ }
116
+ }
117
+ return false;
118
+ };
20
119
  router.get('/', ctx => {
21
120
  ctx.status = 200;
22
121
  ctx.set('Content-Type', 'text/html; charset=utf-8');
@@ -30,6 +129,47 @@ router.get('api/online', ctx => {
30
129
  data: null
31
130
  };
32
131
  });
132
+ router.get('api/runtime/apps', ctx => {
133
+ const status = String(ctx.query?.status ?? '').trim();
134
+ const data = listRuntimeApps().filter(item => {
135
+ if (!status) {
136
+ return true;
137
+ }
138
+ return item.status === status;
139
+ });
140
+ ctx.status = 200;
141
+ ctx.body = {
142
+ code: 200,
143
+ message: 'runtime apps',
144
+ data
145
+ };
146
+ });
147
+ router.get('api/runtime/apps/:app', ctx => {
148
+ const appName = ctx.params.app;
149
+ const runtimeApp = getRuntimeApp(appName);
150
+ if (!runtimeApp) {
151
+ ctx.status = 404;
152
+ ctx.body = {
153
+ code: 404,
154
+ message: '应用未注册',
155
+ data: null
156
+ };
157
+ return;
158
+ }
159
+ ctx.status = 200;
160
+ ctx.body = {
161
+ code: 200,
162
+ message: 'runtime app',
163
+ data: toRuntimeAppSnapshot(runtimeApp)
164
+ };
165
+ });
166
+ router.use(async (ctx, next) => {
167
+ const handled = await dispatchRegisteredKoaRouters(ctx);
168
+ if (handled) {
169
+ return;
170
+ }
171
+ await next();
172
+ });
33
173
  router.all('app/{*path}', async (ctx) => {
34
174
  if (!process.env.input) {
35
175
  ctx.status = 400;
@@ -43,6 +183,10 @@ router.all('app/{*path}', async (ctx) => {
43
183
  const rootPath = process.cwd();
44
184
  const apiPath = '/app/api';
45
185
  if (ctx.path.startsWith(apiPath)) {
186
+ const runtimeApp = denyRuntimeAppAccess(ctx, 'main', 'httpApi');
187
+ if (!runtimeApp) {
188
+ return;
189
+ }
46
190
  const mainPath = join(rootPath, process.env.input);
47
191
  if (!existsSync(mainPath)) {
48
192
  ctx.status = 400;
@@ -111,9 +255,13 @@ router.all('app/{*path}', async (ctx) => {
111
255
  }
112
256
  let root = '';
113
257
  const resourcePath = formatPath(ctx.params?.path);
258
+ const runtimeApp = denyRuntimeAppAccess(ctx, 'main', 'web');
259
+ if (!runtimeApp) {
260
+ return;
261
+ }
262
+ const packageRoot = resolvePackageRoot(runtimeApp.rootDir);
114
263
  try {
115
- const pkg = require$1(path__default.join(rootPath, 'package.json')) ?? {};
116
- root = pkg.alemonjs?.web?.root ?? '';
264
+ root = readWebRootConfig(packageRoot);
117
265
  }
118
266
  catch (err) {
119
267
  ctx.status = 500;
@@ -124,7 +272,7 @@ router.all('app/{*path}', async (ctx) => {
124
272
  };
125
273
  return;
126
274
  }
127
- const webRoot = root ? path__default.join(rootPath, root) : rootPath;
275
+ const webRoot = root ? path__default.join(packageRoot, root) : packageRoot;
128
276
  const fullPath = safePath(webRoot, resourcePath);
129
277
  if (!fullPath) {
130
278
  ctx.status = 403;
@@ -177,22 +325,12 @@ router.all('apps/:app/{*path}', async (ctx) => {
177
325
  }
178
326
  const apiPath = `/apps/${appName}/api`;
179
327
  if (ctx.path.startsWith(apiPath)) {
328
+ const runtimeApp = denyRuntimeAppAccess(ctx, appName, 'httpApi');
329
+ if (!runtimeApp) {
330
+ return;
331
+ }
180
332
  try {
181
- if (!mainDirMap.has(appName)) {
182
- const mainPath = require$1.resolve(appName);
183
- if (!existsSync(mainPath)) {
184
- ctx.status = 400;
185
- ctx.body = {
186
- code: 400,
187
- message: '未找到主要入口文件',
188
- data: null
189
- };
190
- return;
191
- }
192
- const mainDir = dirname(mainPath);
193
- mainDirMap.set(appName, mainDir);
194
- }
195
- const routeBase = join(mainDirMap.get(appName), 'route');
333
+ const routeBase = join(runtimeApp.rootDir, 'route');
196
334
  const dir = safePath(routeBase, ctx.path?.replace(apiPath, '/api') || '');
197
335
  if (!dir) {
198
336
  ctx.status = 403;
@@ -250,12 +388,15 @@ router.all('apps/:app/{*path}', async (ctx) => {
250
388
  ctx.status = 405;
251
389
  return;
252
390
  }
253
- const rootPath = path__default.join(process.cwd(), 'node_modules', appName);
391
+ const runtimeApp = denyRuntimeAppAccess(ctx, appName, 'web');
392
+ if (!runtimeApp) {
393
+ return;
394
+ }
395
+ const packageRoot = resolvePackageRoot(runtimeApp.rootDir);
254
396
  const resourcePath = formatPath(ctx.params?.path);
255
397
  let root = '';
256
398
  try {
257
- const pkg = require$1(`${appName}/package`) ?? {};
258
- root = pkg?.alemonjs?.web?.root ?? '';
399
+ root = readWebRootConfig(packageRoot);
259
400
  }
260
401
  catch (err) {
261
402
  ctx.status = 500;
@@ -266,7 +407,7 @@ router.all('apps/:app/{*path}', async (ctx) => {
266
407
  };
267
408
  return;
268
409
  }
269
- const webRoot = root ? path__default.join(rootPath, root) : rootPath;
410
+ const webRoot = root ? path__default.join(packageRoot, root) : packageRoot;
270
411
  const fullPath = safePath(webRoot, resourcePath);
271
412
  if (!fullPath) {
272
413
  ctx.status = 403;
@@ -1,8 +1,11 @@
1
1
  export type Expansion = {
2
2
  IsAtMe?: boolean;
3
3
  IsPrivate?: boolean;
4
+ _sendAttempted?: boolean;
4
5
  _has_send_attempt?: boolean;
6
+ _sendSucceeded?: boolean;
5
7
  _has_send_success?: boolean;
8
+ _lastSendError?: string | null;
6
9
  _last_send_error?: string | null;
7
10
  [key: string]: any;
8
11
  };
@@ -3,6 +3,7 @@ import { ClientAPI } from '../client';
3
3
  import { EventKeys, Events } from './map';
4
4
  import { DataEnums } from '../message';
5
5
  import { Expose } from '../../app/expose';
6
+ import type KoaRouter from 'koa-router';
6
7
  export type Current<T extends EventKeys> = (event: Events[T], next: Next) => Promise<boolean | void | undefined> | boolean | void | undefined;
7
8
  export type OnResponseValue<C, T extends EventKeys> = {
8
9
  current: C;
@@ -56,6 +57,7 @@ export type childrenCallbackRes = {
56
57
  middleware?: ReturnType<defineMiddlewareFunc>;
57
58
  responseRouter?: ReturnType<DefineRouterFunc>;
58
59
  middlewareRouter?: ReturnType<DefineRouterFunc>;
60
+ koaRouter?: KoaRouter | KoaRouter[];
59
61
  expose?: Expose;
60
62
  } | undefined;
61
63
  export type childrenCallback = ChildrenCycle & {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alemonjs",
3
- "version": "2.1.79",
3
+ "version": "2.1.81",
4
4
  "description": "bot script",
5
5
  "author": "lemonade",
6
6
  "license": "MIT",
@@ -34,7 +34,6 @@
34
34
  "cron": "^4.4.0",
35
35
  "file-type": "21.0.0",
36
36
  "flatted": "^3.3.3",
37
- "https-proxy-agent": "^9.0.0",
38
37
  "koa": "^3.0.1",
39
38
  "koa-router": "^14.0.0",
40
39
  "koa-static": "^5.0.0",
@@ -44,6 +43,7 @@
44
43
  "qrcode": "^1.5.4",
45
44
  "uuid": "11.1.0",
46
45
  "ws": "^8.18.0",
46
+ "https-proxy-agent": "^9.0.0",
47
47
  "yaml": "^2.5.1"
48
48
  },
49
49
  "devDependencies": {
@@ -71,5 +71,5 @@
71
71
  "type": "git",
72
72
  "url": "https://github.com/lemonade-lab/alemonjs.git"
73
73
  },
74
- "gitHead": "7dab16a2167bbdf5706931b67e7eab2fc67835d6"
75
- }
74
+ "gitHead": "c6aa5616afe091a37610dad22fbb2d2618d943b8"
75
+ }
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.