yeow-api 0.3.0 → 0.3.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeow-api",
3
- "version": "0.3.0",
3
+ "version": "0.3.5",
4
4
  "description": "Yeow API",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
package/src/command.ts CHANGED
@@ -79,23 +79,13 @@ export function registerCommand(name: string, options: CommandOptions, taskOptio
79
79
  } else {
80
80
  const result = completerFn(sender, data.args);
81
81
  if (result && typeof result.then === 'function') {
82
- // 异步 completer:await 结果后再回传(原实现立即发空补全、Promise 结果被丢弃)
83
- (result as Promise<string[]>).then(
84
- (list) => {
85
- $send('task', {
86
- type: 'command.tabComplete',
87
- params: { callbackId: compCbId, completions: list || [] },
88
- cb: '',
89
- });
90
- },
91
- () => {
92
- $send('task', {
93
- type: 'command.tabComplete',
94
- params: { callbackId: compCbId, completions: [] },
95
- cb: '',
96
- });
97
- },
98
- );
82
+ // 自动模式不等待 Promise(与事件处理一致):收到 Promise 视为无补全,
83
+ // 立即释放(空补全);异步补全请使用手动模式 complete(res)
84
+ $send('task', {
85
+ type: 'command.tabComplete',
86
+ params: { callbackId: compCbId, completions: [] },
87
+ cb: '',
88
+ });
99
89
  } else {
100
90
  $send('task', {
101
91
  type: 'command.tabComplete',
package/src/core.ts CHANGED
@@ -103,3 +103,9 @@ export { registerPermission } from './permission.js';
103
103
  export type { Permission, PermissionOptions, PermissionDefault } from './permission.js';
104
104
  export { createWorker, Worker, onMessage, postMessage } from './worker.js';
105
105
  export type { WorkerOptions } from './worker.js';
106
+ export {
107
+ stringToBytes, stringToBytesAsync,
108
+ bytesToString, bytesToStringAsync,
109
+ gzipCompress, gzipCompressSync,
110
+ gzipDecompress, gzipDecompressSync,
111
+ } from './util.js';
package/src/event.ts CHANGED
@@ -358,11 +358,12 @@ function loc(raw: Record<string, unknown> | null): Location | null {
358
358
  );
359
359
  }
360
360
 
361
- function adaptEvent<K extends keyof EventMap>(type: K, data: RawEvent): EventMap[K] {
362
- const wrap: Record<string, unknown> = {};
361
+ function adaptEvent<K extends keyof EventMap>(type: K, data: RawEvent): { event: EventMap[K]; mods: Record<string, unknown> } {
362
+ // 初始值:原始数据(跳过 _ 前缀内部字段),player/from/to/respawnLocation 适配为对象
363
+ const initial: Record<string, unknown> = {};
363
364
  for (const key of Object.keys(data)) {
364
365
  if (key.startsWith('_')) continue;
365
- wrap[key] = data[key];
366
+ initial[key] = data[key];
366
367
  }
367
368
  const hasPlayer = [
368
369
  'playerJoin', 'playerQuit', 'playerChat', 'playerMove',
@@ -375,13 +376,34 @@ function adaptEvent<K extends keyof EventMap>(type: K, data: RawEvent): EventMap
375
376
  'playerAdvancementDone', 'playerToggleSneak', 'playerToggleFlight',
376
377
  'inventoryClick', 'playerResourcePackStatus',
377
378
  ] as K[];
378
- if (hasPlayer.includes(type) && data.player) {
379
- wrap.player = Player.getSync(data.player as string);
379
+ // 直接以 uuid 构造 Player(零往返);name 在首次访问时惰性获取(见 Player.name)
380
+ if (hasPlayer.includes(type) && data.player) initial.player = new Player(data.player as string);
381
+ if (data.from) initial.from = loc(data.from as Record<string, unknown>);
382
+ if (data.to) initial.to = loc(data.to as Record<string, unknown>);
383
+ if (data.respawnLocation) initial.respawnLocation = loc(data.respawnLocation as Record<string, unknown>);
384
+
385
+ // 修改收集:所有字段经 getter/setter——handler 直接赋值(e.xxx = ...)即记录为回写 mods。
386
+ // cancelled 单独处理(仅可取消事件暴露;读取语义保持原状:未设置时返回 false)。
387
+ const mods: Record<string, unknown> = {};
388
+ const wrap: Record<string, unknown> = {};
389
+ for (const key of Object.keys(initial)) {
390
+ if (key === 'cancelled') continue;
391
+ Object.defineProperty(wrap, key, {
392
+ get: () => (key in mods ? mods[key] : initial[key]),
393
+ set: (v: unknown) => { mods[key] = v; },
394
+ enumerable: true,
395
+ configurable: true,
396
+ });
380
397
  }
381
- if (data.from) wrap.from = loc(data.from as Record<string, unknown>);
382
- if (data.to) wrap.to = loc(data.to as Record<string, unknown>);
383
- if (data.respawnLocation) wrap.respawnLocation = loc(data.respawnLocation as Record<string, unknown>);
384
- return wrap as unknown as EventMap[K];
398
+ if (data._cancellable) {
399
+ Object.defineProperty(wrap, 'cancelled', {
400
+ get: () => (mods.cancelled as boolean) || false,
401
+ set: (v: boolean) => { mods.cancelled = v; },
402
+ enumerable: true,
403
+ configurable: true,
404
+ });
405
+ }
406
+ return { event: wrap as unknown as EventMap[K], mods };
385
407
  }
386
408
 
387
409
  // ── Event Subscription ─────────────────────────────────────────────
@@ -411,18 +433,8 @@ export function eventOn<K extends keyof EventMap>(
411
433
  const pluginName = __plugin?.name || 'unknown';
412
434
 
413
435
  const cbId = _registerCallback((data: RawEvent) => {
414
- const wrapped = adaptEvent(eventType, data);
436
+ const { event: wrapped, mods: mutatedMods } = adaptEvent(eventType, data);
415
437
  const eventId = data?._eventId;
416
- const cancellable = data?._cancellable;
417
- const localMods: { cancelled?: boolean } = {};
418
-
419
- if (cancellable) {
420
- Object.defineProperty(wrapped, 'cancelled', {
421
- get: () => localMods.cancelled || false,
422
- set: (v: boolean) => { localMods.cancelled = v; },
423
- enumerable: true,
424
- });
425
- }
426
438
 
427
439
  if (manualRelease) {
428
440
  const complete = (result?: Record<string, unknown>) => {
@@ -437,10 +449,12 @@ export function eventOn<K extends keyof EventMap>(
437
449
  }
438
450
 
439
451
  const result = (handler as EventHandler<typeof eventType>)(wrapped);
452
+ // 回写合并:返回值(mods)优先合并,事件参数直接赋值(e.xxx = ...)覆盖之——
453
+ // 返回 Promise 时视为无修改(Promise 不展开),事件立即释放
440
454
  const mods: Record<string, unknown> = {
441
455
  ...(result && typeof result === 'object' ? result : {}),
456
+ ...mutatedMods,
442
457
  };
443
- if (localMods.cancelled) mods.cancelled = true;
444
458
  $send('task', { type: 'event.complete', params: { eventId, mods }, cb: '' });
445
459
  }, { persistent: true });
446
460
 
package/src/player.ts CHANGED
@@ -32,7 +32,15 @@ export class Player {
32
32
 
33
33
  constructor(public readonly uuid: string, private _name?: string) {}
34
34
 
35
- get name(): string { return this._name ?? ''; }
35
+ /** 玩家名。未提供时首次访问惰性同步获取并缓存(仅一次往返)。 */
36
+ get name(): string {
37
+ if (this._name === undefined) {
38
+ let n = '';
39
+ try { n = call<PlayerData>('player.get', { identifier: this.uuid })?.name ?? ''; } catch { /* 离线/异常 → '' */ }
40
+ this._name = n;
41
+ }
42
+ return this._name;
43
+ }
36
44
 
37
45
  /** 玩家物品栏(统一 Inventory 容器抽象)。 */
38
46
  get inventory(): Inventory {
package/src/util.ts ADDED
@@ -0,0 +1,77 @@
1
+ // util 通道:gzip 压缩/解压 + UTF-8 ↔ 字节转换。
2
+ //
3
+ // 协议层字节数据以 base64 字符串承载(引擎原生 Uint8Array.toBase64()/fromBase64()
4
+ // 负责转换)——本模块输入输出一律 Uint8Array / string,**不暴露 base64**。
5
+ // encode/decode 的语义是 buffer ↔ 字符串;无流式接口(一次性整体处理)。
6
+
7
+ /** util 通道同步调用:err → 抛 Error。 */
8
+ function send<T>(t: string, p: Record<string, unknown>): T {
9
+ const r = $send('util', { t, p }) as T | { err?: string } | null;
10
+ if (r == null) return undefined as T;
11
+ if ((r as { err?: string }).err) throw new Error((r as { err?: string }).err);
12
+ return r as T;
13
+ }
14
+
15
+ /** util 通道异步调用(ioExecutor 上执行,不阻塞 JS 线程)。 */
16
+ function sendAsync<T>(t: string, p: Record<string, unknown>): Promise<T> {
17
+ return new Promise<T>((resolve, reject) => {
18
+ const cbId = _registerCallback((r: T | { err?: string }) => {
19
+ if ((r as { err?: string })?.err) reject(new Error((r as { err?: string }).err));
20
+ else resolve(r as T);
21
+ });
22
+ $send('util', { t, p, cb: cbId });
23
+ });
24
+ }
25
+
26
+ /** 输入规范化:string 视为 UTF-8 文本(经 encode.utf8 字节化);Uint8Array 直接转换。 */
27
+ function toB64(data: Uint8Array | string): string {
28
+ return typeof data === 'string'
29
+ ? send<{ data: string }>('encode.utf8', { data }).data
30
+ : data.toBase64();
31
+ }
32
+
33
+ // ── 字符串 ↔ 字节(UTF-8)────────────────────────────────────────
34
+
35
+ /** UTF-8 字符串 → 字节(同步)。 */
36
+ export function stringToBytes(text: string): Uint8Array {
37
+ return Uint8Array.fromBase64(send<{ data: string }>('encode.utf8', { data: text }).data);
38
+ }
39
+
40
+ /** 字节 → UTF-8 字符串(同步;非法序列替换为 U+FFFD,不抛错)。 */
41
+ export function bytesToString(bytes: Uint8Array): string {
42
+ return send<{ data: string }>('decode.utf8', { data: bytes.toBase64() }).data;
43
+ }
44
+
45
+ /** UTF-8 字符串 → 字节(异步,ioExecutor 执行)。 */
46
+ export function stringToBytesAsync(text: string): Promise<Uint8Array> {
47
+ return sendAsync<{ data: string }>('encode.utf8', { data: text }).then((r) => Uint8Array.fromBase64(r.data));
48
+ }
49
+
50
+ /** 字节 → UTF-8 字符串(异步)。 */
51
+ export function bytesToStringAsync(bytes: Uint8Array): Promise<string> {
52
+ return sendAsync<{ data: string }>('decode.utf8', { data: bytes.toBase64() }).then((r) => r.data);
53
+ }
54
+
55
+ // ── gzip ──────────────────────────────────────────────────────────
56
+
57
+ /** gzip 压缩(level 0-9,默认引擎默认级别)。输入 string 视为 UTF-8 文本。 */
58
+ export function gzipCompress(data: Uint8Array | string, level?: number): Promise<Uint8Array> {
59
+ return sendAsync<{ data: string }>('gzip.compress', { data: toB64(data), level })
60
+ .then((r) => Uint8Array.fromBase64(r.data));
61
+ }
62
+
63
+ /** gzip 压缩(同步版)。 */
64
+ export function gzipCompressSync(data: Uint8Array | string, level?: number): Uint8Array {
65
+ return Uint8Array.fromBase64(send<{ data: string }>('gzip.compress', { data: toB64(data), level }).data);
66
+ }
67
+
68
+ /** gzip 解压(输出上限 256 MiB,超限报错——防压缩炸弹)。 */
69
+ export function gzipDecompress(data: Uint8Array | string): Promise<Uint8Array> {
70
+ return sendAsync<{ data: string }>('gzip.decompress', { data: toB64(data) })
71
+ .then((r) => Uint8Array.fromBase64(r.data));
72
+ }
73
+
74
+ /** gzip 解压(同步版)。 */
75
+ export function gzipDecompressSync(data: Uint8Array | string): Uint8Array {
76
+ return Uint8Array.fromBase64(send<{ data: string }>('gzip.decompress', { data: toB64(data) }).data);
77
+ }