floating-live 0.17.0-alpha.2 → 0.17.0-alpha.4

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.
@@ -1,10 +1,13 @@
1
1
  import { App } from "../app.js";
2
2
  import { CommandOptions, CommandCallOptions, AppCommandMap, CommandFunction } from "../command/index.js";
3
- import { AppError, ErrorOptions } from "../error/index.js";
4
3
  import { AppEventListener, AppEventEmitOptions, AppEventDetailMap } from "../event/index.js";
5
4
  import { AppHookMap, HookCallOptions, HookContext, HookFunction, HookUseOptions } from "../hook/index.js";
6
5
  import { AppValueMap, ValueContext, ValueOptions } from "../value/index.js";
7
- import { WhenRegisterCallback } from "./manager.js";
6
+ /** 插件注册监听回调
7
+ * 当目标插件已经注册时,回调会被立即调用,否则将在目标插件注册时调用。
8
+ * 回调可以返回一个函数作为注销时的回调。
9
+ */
10
+ export type WhenRegisterCallback = () => (() => void) | void;
8
11
  /** 插件对象 */
9
12
  export interface PluginItem {
10
13
  /** 插件名称 */
@@ -39,6 +42,16 @@ export interface PluginRegisterOptions {
39
42
  /** 插件类型 */
40
43
  pluginType?: "core" | "framework" | "";
41
44
  }
45
+ export interface PluginData<T> {
46
+ plugin: PluginItem;
47
+ context: PluginContext;
48
+ exposes?: T;
49
+ unremovable: boolean;
50
+ /** 可访问App实例 */
51
+ accessApp: boolean;
52
+ /** 插件类型 */
53
+ pluginType: "core" | "framework" | "";
54
+ }
42
55
  /** 插件上下文 */
43
56
  export interface PluginContext {
44
57
  /** 安全插件上下文 */
@@ -53,10 +66,6 @@ export interface PluginContext {
53
66
  off<K extends keyof AppEventDetailMap>(type: K, listener: AppEventListener<AppEventDetailMap[K]>): void;
54
67
  /** 发送事件 */
55
68
  emit<K extends keyof AppEventDetailMap>(type: K, detail: AppEventDetailMap[K], options?: AppEventEmitOptions & EventInit): void;
56
- /** 抛出错误 */
57
- throw(err?: Error): void;
58
- /** 生成错误 @deprecated */
59
- error(id: string, options: ErrorOptions): AppError;
60
69
  /** 注册插件 */
61
70
  register<P extends PluginItem>(plugin: PluginConstructor<P>, options?: PluginInitOptions): Promise<P>;
62
71
  /** 注销插件 */
@@ -1,13 +1,11 @@
1
- import { BasePlugin } from "../../index.js";
1
+ import { appError, BasePlugin } from "../../index.js";
2
2
  export class Platform extends BasePlugin {
3
- constructor() {
4
- super(...arguments);
5
- this.list = new Map();
6
- }
3
+ static pluginName = "platform";
4
+ list = new Map();
7
5
  /** 注册直播平台信息 */
8
6
  register(name, info, signal) {
9
7
  if (this.list.has(name)) {
10
- throw new this.Error("platform:register_duplicated", {
8
+ throw appError("platform:register_duplicated", {
11
9
  message: "平台信息重复注册",
12
10
  });
13
11
  }
@@ -21,7 +19,7 @@ export class Platform extends BasePlugin {
21
19
  /** 解除注册直播平台信息 */
22
20
  unregister(name) {
23
21
  if (!this.list.has(name)) {
24
- throw new this.Error("platform:unregister_unexisted", {
22
+ throw appError("platform:unregister_unexisted", {
25
23
  message: "无法解除不存在的平台信息注册",
26
24
  });
27
25
  }
@@ -44,5 +42,4 @@ export class Platform extends BasePlugin {
44
42
  return [...this.list].map(([name, info]) => ({ name, info }));
45
43
  }
46
44
  }
47
- Platform.pluginName = "platform";
48
45
  export default Platform;
@@ -1,17 +1,22 @@
1
1
  import { LiveMessage } from "../../live/message.js";
2
- import { LiveRoom, LiveRoomDetailInfo, LiveRoomData, LiveRoomStatsInfo, LiveRoomStatus } from "../../live/room.js";
2
+ import { LiveRoom, LiveRoomDetailInfo, LiveRoomData, LiveRoomStatsInfo, LiveRoomStatus, InvalidLiveRoom } from "../../live/room.js";
3
3
  import { BasePlugin } from "../../plugin/index.js";
4
4
  import { PluginContext } from "../../plugin/types.js";
5
+ export interface RoomCreateOptions {
6
+ allowInvalidRoom?: boolean;
7
+ open?: boolean;
8
+ [option: string]: any;
9
+ }
5
10
  interface PluginExposes {
6
11
  add(platform: string, id: number, options?: boolean | Record<string, any>): void;
7
12
  remove(key: string): void;
8
- get(key: string): LiveRoom | undefined;
13
+ get(key: string): LiveRoom | InvalidLiveRoom | undefined;
9
14
  has(key: string): boolean;
10
15
  data(key: string): LiveRoomData | undefined;
11
16
  update(key: string): void;
12
17
  open(key: string): void;
13
18
  close(key: string): void;
14
- getList(): LiveRoom[];
19
+ getList(): (LiveRoom | InvalidLiveRoom)[];
15
20
  }
16
21
  declare module "../../index.js" {
17
22
  interface AppCommandMap {
@@ -22,19 +27,22 @@ declare module "../../index.js" {
22
27
  close: (key: string) => void;
23
28
  update: (key: string) => void;
24
29
  "room.snapshot": () => LiveRoomData[];
30
+ "room.validate": (key: string) => void;
31
+ "room.invalidate": (key: string) => void;
25
32
  [name: `${string}.room.create`]: (id: string | number, options: Record<string, any>) => LiveRoom | Promise<LiveRoom>;
26
33
  [name: `${string}.room.data`]: (id: string | number) => LiveRoomData | Promise<LiveRoomData>;
27
34
  }
28
35
  interface AppEventDetailMap {
29
36
  "live:message": {
37
+ key: string;
38
+ platform: string;
39
+ roomId: string | number;
30
40
  message: LiveMessage.All;
31
41
  };
32
42
  "live:raw": {
33
- /** 平台 */
43
+ key: string;
34
44
  platform: string;
35
45
  roomId: string | number;
36
- /** 服务来源 */
37
- service?: string;
38
46
  data: any;
39
47
  };
40
48
  "room:connecting": {
@@ -60,6 +68,14 @@ declare module "../../index.js" {
60
68
  key: string;
61
69
  position: number;
62
70
  };
71
+ "room:validate": {
72
+ key: string;
73
+ room: LiveRoomData;
74
+ };
75
+ "room:invalidate": {
76
+ key: string;
77
+ room: LiveRoomData;
78
+ };
63
79
  "room:open": {
64
80
  key: string;
65
81
  room: LiveRoomData;
@@ -89,10 +105,13 @@ declare module "../../index.js" {
89
105
  }
90
106
  interface AppHookMap {
91
107
  "live.message": {
108
+ key: string;
109
+ platform: string;
110
+ roomId: string | number;
92
111
  message: LiveMessage.All;
93
112
  };
94
- "room.add": {
95
- platform: string;
113
+ "room.create": {
114
+ serviceId: string;
96
115
  id: string | number;
97
116
  options: Record<string, any>;
98
117
  };
@@ -107,17 +126,30 @@ declare module "../../index.js" {
107
126
  export declare class Room extends BasePlugin {
108
127
  static pluginName: string;
109
128
  private map;
129
+ /** AbortController 属性 symbol
130
+ * 与 room 插件实例绑定,为绑定的
131
+ */
110
132
  private symbolAbortController;
111
133
  init(ctx: PluginContext): void;
112
134
  expose(): PluginExposes;
113
135
  /** 添加房间监听实例 */
114
- attach(room: LiveRoom, open?: boolean): void;
136
+ attach(room: LiveRoom | InvalidLiveRoom, open?: boolean): void;
137
+ /** 移除房间监听实例 */
138
+ detach(key: string): LiveRoom | InvalidLiveRoom;
139
+ /** 绑定房间事件监听 */
140
+ private bindEvent;
115
141
  /** 添加房间 */
116
- add(platform: string, id: number, options?: boolean | Record<string, any>): Promise<void>;
117
- /** 移除房间 */
142
+ add(serviceId: string, id: number, options?: boolean | RoomCreateOptions): Promise<void>;
143
+ /** 创建房间实例 */
144
+ create(serviceId: string, id: number | string, options?: RoomCreateOptions): Promise<LiveRoom | InvalidLiveRoom>;
145
+ /** 移除并销毁房间 */
118
146
  remove(key: string): void;
147
+ /** 激活无效房间 */
148
+ validate(key: string, options: RoomCreateOptions): Promise<void>;
149
+ /** 使有效的房间设为无效 */
150
+ invalidate(key: string): void;
119
151
  /** 获取房间 */
120
- get(key: string): LiveRoom | undefined;
152
+ get(key: string): LiveRoom | InvalidLiveRoom | undefined;
121
153
  has(key: string): boolean;
122
154
  /** 获取房间信息 */
123
155
  data(key: string): LiveRoomData | undefined;
@@ -126,7 +158,7 @@ export declare class Room extends BasePlugin {
126
158
  /** 获取房间key列表 */
127
159
  get keys(): string[];
128
160
  /** 获取房间列表 */
129
- getList(): LiveRoom[];
161
+ getList(): (LiveRoom | InvalidLiveRoom)[];
130
162
  /** 打开房间连接 */
131
163
  open(key: string): void;
132
164
  /** 关闭房间连接 */
@@ -1,12 +1,14 @@
1
- import { AppError } from "../../error/index.js";
1
+ import { appError } from "../../error/index.js";
2
+ import { InvalidLiveRoom, } from "../../live/room.js";
2
3
  import { BasePlugin } from "../../plugin/index.js";
3
4
  import { bindCommand } from "../../utils/index.js";
4
5
  export class Room extends BasePlugin {
5
- constructor() {
6
- super(...arguments);
7
- this.map = new Map();
8
- this.symbolAbortController = Symbol("room.abortController");
9
- }
6
+ static pluginName = "room";
7
+ map = new Map();
8
+ /** AbortController 属性 symbol
9
+ * 与 room 插件实例绑定,为绑定的
10
+ */
11
+ symbolAbortController = Symbol("room.abortController");
10
12
  init(ctx) {
11
13
  ctx.registerCommand("add", bindCommand(this.add, this));
12
14
  ctx.registerCommand("remove", bindCommand(this.remove, this));
@@ -31,14 +33,44 @@ export class Room extends BasePlugin {
31
33
  }
32
34
  /** 添加房间监听实例 */
33
35
  attach(room, open) {
34
- let key = room.key;
36
+ const key = room.key;
35
37
  if (this.map.has(key)) {
36
- this.throw(new AppError("room:add_exist", {
37
- message: "房间已存在",
38
- }));
39
- return;
38
+ throw appError("room:add_exist", {
39
+ message: `房间已存在[${key}]`,
40
+ target: `room/${key}`,
41
+ });
40
42
  }
41
43
  this.map.set(key, room);
44
+ // 如果房间有效,则绑定事件
45
+ if (room.valid) {
46
+ this.bindEvent(room);
47
+ if (open) {
48
+ room.open();
49
+ }
50
+ this.ctx.emit("room:add", { key, room: room.toData() });
51
+ }
52
+ }
53
+ /** 移除房间监听实例 */
54
+ detach(key) {
55
+ const room = this.map.get(key);
56
+ if (!room) {
57
+ throw appError("room:remove_unexist", {
58
+ message: `房间不存在[${key}]`,
59
+ target: `room/${key}`,
60
+ });
61
+ }
62
+ this.map.delete(key); // 从表中删除房间
63
+ // 移除插件实例对该房间的所有监听
64
+ room[this.symbolAbortController]?.abort();
65
+ room[this.symbolAbortController] = undefined;
66
+ this.ctx.emit("room:remove", { key });
67
+ return room;
68
+ }
69
+ /** 绑定房间事件监听 */
70
+ bindEvent(room) {
71
+ const key = room.key;
72
+ const platform = room.platform;
73
+ const roomId = room.id;
42
74
  const abortController = new AbortController();
43
75
  room[this.symbolAbortController] =
44
76
  abortController;
@@ -48,15 +80,15 @@ export class Room extends BasePlugin {
48
80
  // 添加监听事件
49
81
  // 直播消息
50
82
  setHandler("message", ({ message }) => {
51
- const ctx = { message };
83
+ const ctx = { key, platform, roomId, message };
52
84
  this.ctx.callHook("live.message", ctx).then((ctx) => {
53
85
  !ctx.defaultPrevented &&
54
- this.ctx.emit("live:message", { message: ctx.message });
86
+ this.ctx.emit("live:message", { key, platform, roomId, message: ctx.message });
55
87
  });
56
88
  });
57
89
  // 直播消息源数据
58
90
  setHandler("raw", ({ platform, roomId, data }) => {
59
- this.ctx.emit("live:raw", { platform, roomId, data });
91
+ this.ctx.emit("live:raw", { key, platform, roomId, data });
60
92
  });
61
93
  // 正在连接服务器
62
94
  setHandler("connecting", () => {
@@ -98,48 +130,117 @@ export class Room extends BasePlugin {
98
130
  setHandler("close", () => {
99
131
  this.ctx.emit("room:close", { key, room: room.toData() });
100
132
  });
101
- if (open) {
102
- room.open();
103
- }
104
- this.ctx.emit("room:add", { key, room: room.toData() });
105
133
  }
106
134
  /** 添加房间 */
107
- async add(platform, id, options) {
135
+ async add(serviceId, id, options) {
108
136
  const opt = typeof options == "boolean" ? { open: options } : options || {};
109
- const ctx = { platform, id, options: opt };
110
- const res = await this.ctx.callHook("room.add", ctx);
137
+ const room = await this.create(serviceId, id, opt);
138
+ this.attach(room);
139
+ }
140
+ /** 创建房间实例 */
141
+ async create(serviceId, id, options = {}) {
142
+ const ctx = { serviceId, id, options };
143
+ const res = await this.ctx.callHook("room.create", ctx).catch((reason) => {
144
+ throw appError("room:create_hook_error", {
145
+ message: `无法创建房间监听实例[${serviceId}:${id}]:调用钩子函数出错`,
146
+ target: `room/${serviceId}:${id}`,
147
+ });
148
+ });
111
149
  if (res.defaultPrevented) {
112
- this.throw(new this.Error("room:add_hook_prevented", {
113
- message: "无法添加房间",
114
- cause: "房间添加被钩子函数阻止",
115
- target: `room/${platform}:${id}`,
116
- }));
150
+ throw appError("room:create_hook_prevented", {
151
+ message: `无法创建房间监听实例[${serviceId}:${id}]:房间添加被钩子函数阻止`,
152
+ target: `room/${serviceId}:${id}`,
153
+ });
117
154
  }
118
155
  let room;
119
156
  try {
120
- room = await this.ctx.call(`${platform}.room.create`, id, opt);
157
+ room = await this.ctx.call(`${serviceId}.room.create`, id, options);
121
158
  }
122
159
  catch (e) {
123
- this.throw(new this.Error("room:create_fail", {
124
- message: "无法创建房间",
125
- cause: e,
126
- target: `room/${platform}:${id}`,
127
- }));
160
+ if (options.allowInvalidRoom) {
161
+ room = new InvalidLiveRoom(serviceId, id);
162
+ }
163
+ else {
164
+ throw appError("room:create_fail", {
165
+ message: `创建房间监听实例失败[${serviceId}:${id}]`,
166
+ reason: e,
167
+ target: `room/${serviceId}:${id}`,
168
+ });
169
+ }
128
170
  }
129
- this.attach(room);
171
+ return room;
130
172
  }
131
- /** 移除房间 */
173
+ /** 移除并销毁房间 */
132
174
  remove(key) {
175
+ const room = this.detach(key);
176
+ if (room.valid) {
177
+ try {
178
+ room.destroy?.();
179
+ }
180
+ catch (e) {
181
+ throw appError("room:destroy_fail", {
182
+ message: `销毁房间实例失败[${key}]`,
183
+ reason: e,
184
+ target: `room/${key}`,
185
+ });
186
+ }
187
+ }
188
+ }
189
+ /** 激活无效房间 */
190
+ async validate(key, options) {
191
+ let invalidRoom = this.map.get(key);
192
+ if (!invalidRoom) {
193
+ throw appError("room:validate_unexist", {
194
+ message: `房间不存在[${key}]`,
195
+ target: `room/${key}`,
196
+ });
197
+ }
198
+ if (invalidRoom.valid == true) {
199
+ throw appError("room:validate_valid", {
200
+ message: `无法将已经有效的房间再次设为有效[${key}]`,
201
+ target: `room/${key}`,
202
+ });
203
+ }
204
+ const { serviceId, id } = invalidRoom;
205
+ const room = await this.create(serviceId, id, { ...options, allowInvalidRoom: false });
206
+ this.map.set(key, room);
207
+ if (room.valid) {
208
+ this.ctx.emit("room:validate", { key, room: room.toData() });
209
+ }
210
+ }
211
+ /** 使有效的房间设为无效 */
212
+ invalidate(key) {
133
213
  let room = this.map.get(key);
134
214
  if (!room) {
135
- this.throw(new this.Error("room:remove_unexist", {
136
- message: "房间不存在",
215
+ throw appError("room:invalidate_unexist", {
216
+ message: `房间不存在[${key}]`,
137
217
  target: `room/${key}`,
138
- }));
218
+ });
139
219
  }
140
- this.map.delete(key); // 从表中删除房间
141
- room[this.symbolAbortController]?.abort(); // 移除房间监听实例
220
+ if (room.valid == true) {
221
+ throw appError("room:invalidate_invalid", {
222
+ message: `无法将已经无效的房间再次设为无效[${key}]`,
223
+ target: `room/${key}`,
224
+ });
225
+ }
226
+ // 用无效房间替换掉原房间
227
+ const { serviceId, id } = room;
228
+ const invalidRoom = new InvalidLiveRoom(serviceId, id);
229
+ this.map.set(key, invalidRoom);
230
+ // 移除插件实例对原房间的所有监听
231
+ room[this.symbolAbortController]?.abort();
232
+ room[this.symbolAbortController] = undefined;
142
233
  this.ctx.emit("room:remove", { key });
234
+ try {
235
+ room.destroy?.();
236
+ }
237
+ catch (e) {
238
+ throw appError("room:destroy_fail", {
239
+ message: `销毁房间实例失败[${key}]`,
240
+ reason: e,
241
+ target: `room/${key}`,
242
+ });
243
+ }
143
244
  }
144
245
  /** 获取房间 */
145
246
  get(key) {
@@ -156,7 +257,7 @@ export class Room extends BasePlugin {
156
257
  /** 更新房间信息 */
157
258
  update(key) {
158
259
  let room = this.map.get(key);
159
- room && room.update();
260
+ room && room.valid && room.update();
160
261
  }
161
262
  /** 获取房间key列表 */
162
263
  get keys() {
@@ -170,21 +271,33 @@ export class Room extends BasePlugin {
170
271
  open(key) {
171
272
  let room = this.map.get(key);
172
273
  if (!room) {
173
- this.throw(new this.Error("room:open_unexist", {
174
- message: "打开的房间不存在",
274
+ throw (appError("room:open_unexist", {
275
+ message: "要打开的房间不存在",
175
276
  target: `room/${key}`,
176
277
  }));
177
278
  }
279
+ if (!room.valid) {
280
+ throw appError("room:open_invalid", {
281
+ message: "要打开的房间无效",
282
+ target: `room/${key}`,
283
+ });
284
+ }
178
285
  room.open();
179
286
  }
180
287
  /** 关闭房间连接 */
181
288
  close(key) {
182
289
  let room = this.map.get(key);
183
290
  if (!room) {
184
- this.throw(new this.Error("room:close_unexist", {
291
+ throw appError("room:close_unexist", {
185
292
  message: "关闭的房间不存在",
186
293
  target: `room/${key}`,
187
- }));
294
+ });
295
+ }
296
+ if (!room.valid) {
297
+ throw appError("room:close_invalid", {
298
+ message: "要关闭的房间无效",
299
+ target: `room/${key}`,
300
+ });
188
301
  }
189
302
  room.close();
190
303
  }
@@ -193,10 +306,10 @@ export class Room extends BasePlugin {
193
306
  const arr = [...this.map];
194
307
  const index = arr.findIndex(([k]) => k == key);
195
308
  if (index == -1) {
196
- this.throw(new this.Error("room:move_unexist", {
309
+ throw appError("room:move_unexist", {
197
310
  message: "要移动的房间不存在",
198
311
  target: `room/${key}`,
199
- }));
312
+ });
200
313
  }
201
314
  const [item] = arr.splice(index, 1);
202
315
  arr.splice(position, 0, item);
@@ -208,5 +321,4 @@ export class Room extends BasePlugin {
208
321
  return this.getList().map((room) => room.toData());
209
322
  }
210
323
  }
211
- Room.pluginName = "room";
212
324
  export default Room;
@@ -1,8 +1,9 @@
1
- import { AppError } from "../error/index.js";
1
+ import { AppErrorLegacy } from "../error/index.js";
2
2
  import { bindCommand } from "../utils/index.js";
3
3
  export class ValueManager {
4
+ list = new Map();
5
+ app;
4
6
  constructor(app) {
5
- this.list = new Map();
6
7
  this.app = app;
7
8
  app.registerCommand("value.snapshot", bindCommand(this.toSnapshot, this));
8
9
  }
@@ -25,7 +26,7 @@ export class ValueManager {
25
26
  get(name, options) {
26
27
  const item = this.list.get(name);
27
28
  if (!item)
28
- throw new AppError("value:get_unexist", {
29
+ throw new AppErrorLegacy("value:get_unexist", {
29
30
  message: "无法获取未注册的值",
30
31
  });
31
32
  return item.get(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "floating-live",
3
- "version": "0.17.0-alpha.2",
3
+ "version": "0.17.0-alpha.4",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,13 +11,17 @@
11
11
  "rimraf": "^3.0.2",
12
12
  "ts-node": "^10.9.1",
13
13
  "tsc-alias": "^1.8.16",
14
- "typescript": "^5.6.3"
14
+ "typescript": "^6.0.3"
15
15
  },
16
16
  "scripts": {
17
17
  "tsc": "tsc -b",
18
+ "check": "tsc --noEmit",
18
19
  "clear": "rimraf dist",
19
20
  "build": "npm run clear && tsc -b && tsc-alias",
20
21
  "prepack": "npm run build",
21
22
  "postpack": "npm run clear"
22
- }
23
+ },
24
+ "files": [
25
+ "./dist/"
26
+ ]
23
27
  }