@zhin.js/command 1.1.3 → 1.1.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.
@@ -32,8 +32,8 @@ export declare class CommandIndex {
32
32
  constructor(slots: readonly Readonly<CapabilitySlot<CommandDefinition>>[], snapshot: RuntimeSnapshot, menu?: CommandMenuConfig);
33
33
  list(): readonly CommandDescriptor[];
34
34
  has(name: string): boolean;
35
- execute(name: string, args?: readonly string[]): Promise<unknown>;
36
- dispatch(input: CommandMatchInput, source?: unknown, interactionFactory?: UserInteractionFactory, commandPrefix?: string): Promise<CommandDispatchResult>;
35
+ execute(name: string, args?: readonly string[], operationSnapshot?: RuntimeSnapshot): Promise<unknown>;
36
+ dispatch(input: CommandMatchInput, source?: unknown, interactionFactory?: UserInteractionFactory, commandPrefix?: string, operationSnapshot?: RuntimeSnapshot): Promise<CommandDispatchResult>;
37
37
  }
38
38
  export declare function isCommandIndex(value: unknown): value is CommandIndex;
39
39
  export declare class CommandParameterValueError extends TypeError {
@@ -1,5 +1,5 @@
1
1
  import { SegmentMatcher, TypeMatcherRegistry, } from 'segment-matcher';
2
- import { operationClientAdapter } from '@zhin.js/feature-kit';
2
+ import { bindOperationInput, operationClientAdapter } from '@zhin.js/feature-kit';
3
3
  import { permissionHostToken, toPermissionSubject, } from '@zhin.js/permission';
4
4
  import { createCommandContext, resolveCommandSession, resolveDynamicParams, } from './definition.js';
5
5
  const segmentFields = {
@@ -112,37 +112,37 @@ export class CommandIndex {
112
112
  throw error;
113
113
  }
114
114
  }
115
- async execute(name, args = []) {
115
+ async execute(name, args = [], operationSnapshot = this.snapshot) {
116
116
  const match = this.#match(name, true);
117
117
  if (!match) {
118
118
  this.#diagnoseParameter(name);
119
119
  throw new Error(`Unknown Command: ${name}`);
120
120
  }
121
121
  // Host / 无 session:跳过 permit;无 source 时函数默认值得到空 session。
122
- return match.command.slot.definition.execute(createCommandContext(this.snapshot, match.command.slot.owner, args, resolveDynamicParams(match.params, undefined), undefined, Object.freeze([]), undefined, match.command.slot.definition.adapter));
122
+ return match.command.slot.definition.execute(createCommandContext(operationSnapshot, match.command.slot.owner, args, resolveDynamicParams(match.params, undefined), undefined, Object.freeze([]), undefined, match.command.slot.definition.adapter));
123
123
  }
124
- async dispatch(input, source = undefined, interactionFactory, commandPrefix = '') {
124
+ async dispatch(input, source = undefined, interactionFactory, commandPrefix = '', operationSnapshot = this.snapshot) {
125
125
  if (this.#menu) {
126
126
  const menuValue = this.#dispatchMenu(input, commandPrefix);
127
127
  if (menuValue !== undefined) {
128
128
  return Object.freeze({
129
129
  matched: true,
130
130
  command: this.#menu.keyword,
131
- owner: this.snapshot.root,
131
+ owner: operationSnapshot.root,
132
132
  value: menuValue,
133
133
  });
134
134
  }
135
135
  }
136
- const interaction = interactionFactory?.(source);
137
136
  const shortcut = this.#matchShortcut(input);
138
137
  if (shortcut) {
139
138
  if (!commandAdapterMatches(shortcut.record, source)) {
140
139
  return Object.freeze({ matched: false });
141
140
  }
142
- if (!(await this.#permitAllows(shortcut.record, source))) {
141
+ if (!(await this.#permitAllows(shortcut.record, source, operationSnapshot))) {
143
142
  return Object.freeze({ matched: false });
144
143
  }
145
- const value = await shortcut.record.slot.definition.execute(createCommandContext(this.snapshot, shortcut.record.slot.owner, Object.freeze([]), resolveDynamicParams(shortcut.params, source), source, Object.freeze([]), interaction, shortcut.record.slot.definition.adapter));
144
+ const authorSource = bindOperationInput(source, shortcut.record.slot.owner, operationSnapshot);
145
+ const value = await shortcut.record.slot.definition.execute(createCommandContext(operationSnapshot, shortcut.record.slot.owner, Object.freeze([]), resolveDynamicParams(shortcut.params, authorSource), authorSource, Object.freeze([]), interactionFactory?.(authorSource), shortcut.record.slot.definition.adapter));
146
146
  return Object.freeze({
147
147
  matched: true,
148
148
  command: shortcut.record.name,
@@ -156,11 +156,12 @@ export class CommandIndex {
156
156
  if (!commandAdapterMatches(match.command, source)) {
157
157
  return Object.freeze({ matched: false });
158
158
  }
159
- if (!(await this.#permitAllows(match.command, source))) {
159
+ if (!(await this.#permitAllows(match.command, source, operationSnapshot))) {
160
160
  return Object.freeze({ matched: false });
161
161
  }
162
162
  const args = textArgs(match.remaining);
163
- const value = await match.command.slot.definition.execute(createCommandContext(this.snapshot, match.command.slot.owner, args, resolveDynamicParams(match.params, source), source, match.remaining, interaction, match.command.slot.definition.adapter));
163
+ const authorSource = bindOperationInput(source, match.command.slot.owner, operationSnapshot);
164
+ const value = await match.command.slot.definition.execute(createCommandContext(operationSnapshot, match.command.slot.owner, args, resolveDynamicParams(match.params, authorSource), authorSource, match.remaining, interactionFactory?.(authorSource), match.command.slot.definition.adapter));
164
165
  return Object.freeze({
165
166
  matched: true,
166
167
  command: match.command.name,
@@ -168,21 +169,21 @@ export class CommandIndex {
168
169
  value,
169
170
  });
170
171
  }
171
- async #permitAllows(record, source) {
172
+ async #permitAllows(record, source, operationSnapshot) {
172
173
  const permits = record.permit;
173
174
  if (!permits || permits.length === 0)
174
175
  return true;
175
176
  if (!hasImSession(source))
176
177
  return true;
177
- const host = this.#resolveHost();
178
+ const host = this.#resolveHost(operationSnapshot);
178
179
  if (!host)
179
180
  return false;
180
181
  const subject = toPermissionSubject(resolveCommandSession(source));
181
182
  return host.checkAll(permits, subject);
182
183
  }
183
- #resolveHost() {
184
+ #resolveHost(operationSnapshot) {
184
185
  try {
185
- const resources = this.snapshot.resources.get(this.snapshot.root);
186
+ const resources = operationSnapshot.resources.get(operationSnapshot.root);
186
187
  if (!resources)
187
188
  return undefined;
188
189
  const host = resources.get(permissionHostToken.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/command",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Convention-based Command Feature for Zhin Plugin Runtime",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -18,10 +18,10 @@
18
18
  ],
19
19
  "dependencies": {
20
20
  "segment-matcher": "^1.0.5",
21
- "@zhin.js/feature-kit": "1.1.2",
21
+ "@zhin.js/feature-kit": "1.1.3",
22
22
  "@zhin.js/interaction": "1.1.0",
23
- "@zhin.js/permission": "1.1.3",
24
- "@zhin.js/plugin-runtime": "1.1.11"
23
+ "@zhin.js/permission": "1.1.4",
24
+ "@zhin.js/plugin-runtime": "1.1.12"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^26.1.2",
@@ -9,7 +9,7 @@ import type {
9
9
  RuntimeSnapshot,
10
10
  } from '@zhin.js/plugin-runtime';
11
11
  import type { UserInteractionFactory } from '@zhin.js/interaction';
12
- import { operationClientAdapter } from '@zhin.js/feature-kit';
12
+ import { bindOperationInput, operationClientAdapter } from '@zhin.js/feature-kit';
13
13
  import {
14
14
  permissionHostToken,
15
15
  toPermissionSubject,
@@ -210,7 +210,7 @@ export class CommandIndex {
210
210
  }
211
211
  }
212
212
 
213
- async execute(name: string, args: readonly string[] = []): Promise<unknown> {
213
+ async execute(name: string, args: readonly string[] = [], operationSnapshot: RuntimeSnapshot = this.snapshot): Promise<unknown> {
214
214
  const match = this.#match(name, true);
215
215
  if (!match) {
216
216
  this.#diagnoseParameter(name);
@@ -219,7 +219,7 @@ export class CommandIndex {
219
219
  // Host / 无 session:跳过 permit;无 source 时函数默认值得到空 session。
220
220
  return match.command.slot.definition.execute(
221
221
  createCommandContext(
222
- this.snapshot,
222
+ operationSnapshot,
223
223
  match.command.slot.owner,
224
224
  args,
225
225
  resolveDynamicParams(match.params, undefined),
@@ -236,6 +236,7 @@ export class CommandIndex {
236
236
  source: unknown = undefined,
237
237
  interactionFactory?: UserInteractionFactory,
238
238
  commandPrefix = '',
239
+ operationSnapshot: RuntimeSnapshot = this.snapshot,
239
240
  ): Promise<CommandDispatchResult> {
240
241
  if (this.#menu) {
241
242
  const menuValue = this.#dispatchMenu(input, commandPrefix);
@@ -243,29 +244,29 @@ export class CommandIndex {
243
244
  return Object.freeze({
244
245
  matched: true,
245
246
  command: this.#menu.keyword,
246
- owner: this.snapshot.root,
247
+ owner: operationSnapshot.root,
247
248
  value: menuValue,
248
249
  });
249
250
  }
250
251
  }
251
- const interaction = interactionFactory?.(source);
252
252
  const shortcut = this.#matchShortcut(input);
253
253
  if (shortcut) {
254
254
  if (!commandAdapterMatches(shortcut.record, source)) {
255
255
  return Object.freeze({ matched: false });
256
256
  }
257
- if (!(await this.#permitAllows(shortcut.record, source))) {
257
+ if (!(await this.#permitAllows(shortcut.record, source, operationSnapshot))) {
258
258
  return Object.freeze({ matched: false });
259
259
  }
260
+ const authorSource = bindOperationInput(source, shortcut.record.slot.owner, operationSnapshot);
260
261
  const value = await shortcut.record.slot.definition.execute(
261
262
  createCommandContext(
262
- this.snapshot,
263
+ operationSnapshot,
263
264
  shortcut.record.slot.owner,
264
265
  Object.freeze([]),
265
- resolveDynamicParams(shortcut.params, source),
266
- source,
266
+ resolveDynamicParams(shortcut.params, authorSource),
267
+ authorSource,
267
268
  Object.freeze([]),
268
- interaction,
269
+ interactionFactory?.(authorSource),
269
270
  shortcut.record.slot.definition.adapter,
270
271
  ),
271
272
  );
@@ -282,19 +283,20 @@ export class CommandIndex {
282
283
  if (!commandAdapterMatches(match.command, source)) {
283
284
  return Object.freeze({ matched: false });
284
285
  }
285
- if (!(await this.#permitAllows(match.command, source))) {
286
+ if (!(await this.#permitAllows(match.command, source, operationSnapshot))) {
286
287
  return Object.freeze({ matched: false });
287
288
  }
288
289
  const args = textArgs(match.remaining);
290
+ const authorSource = bindOperationInput(source, match.command.slot.owner, operationSnapshot);
289
291
  const value = await match.command.slot.definition.execute(
290
292
  createCommandContext(
291
- this.snapshot,
293
+ operationSnapshot,
292
294
  match.command.slot.owner,
293
295
  args,
294
- resolveDynamicParams(match.params, source),
295
- source,
296
+ resolveDynamicParams(match.params, authorSource),
297
+ authorSource,
296
298
  match.remaining,
297
- interaction,
299
+ interactionFactory?.(authorSource),
298
300
  match.command.slot.definition.adapter,
299
301
  ),
300
302
  );
@@ -306,19 +308,19 @@ export class CommandIndex {
306
308
  });
307
309
  }
308
310
 
309
- async #permitAllows(record: CommandRecord, source: unknown): Promise<boolean> {
311
+ async #permitAllows(record: CommandRecord, source: unknown, operationSnapshot: RuntimeSnapshot): Promise<boolean> {
310
312
  const permits = record.permit;
311
313
  if (!permits || permits.length === 0) return true;
312
314
  if (!hasImSession(source)) return true;
313
- const host = this.#resolveHost();
315
+ const host = this.#resolveHost(operationSnapshot);
314
316
  if (!host) return false;
315
317
  const subject = toPermissionSubject(resolveCommandSession(source));
316
318
  return host.checkAll(permits, subject);
317
319
  }
318
320
 
319
- #resolveHost(): PermissionHost | undefined {
321
+ #resolveHost(operationSnapshot: RuntimeSnapshot): PermissionHost | undefined {
320
322
  try {
321
- const resources = this.snapshot.resources.get(this.snapshot.root);
323
+ const resources = operationSnapshot.resources.get(operationSnapshot.root);
322
324
  if (!resources) return undefined;
323
325
  const host = resources.get(permissionHostToken.id);
324
326
  return host && typeof (host as PermissionHost).check === 'function'