@zhin.js/command 1.1.12 → 1.1.14

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,5 +1,6 @@
1
1
  import { type CapabilityId, type CapabilitySlot, type PluginId, type RuntimeSnapshot, type Locale } from '@zhin.js/plugin-runtime';
2
2
  import type { UserInteractionFactory } from '@zhin.js/interaction';
3
+ import { type CapabilityOperationFactory } from '@zhin.js/feature-kit';
3
4
  import { type CommandValidationIssue } from './arguments.js';
4
5
  import { type CommandMenuEntry, type CommandMenuAppearance } from './command-menu.js';
5
6
  export type { CommandMenuEntry, CommandMenuAppearance } from './command-menu.js';
@@ -32,6 +33,7 @@ export interface CommandDispatchRequest {
32
33
  readonly interactionFactory?: UserInteractionFactory;
33
34
  readonly commandPrefix?: string;
34
35
  readonly snapshot?: RuntimeSnapshot;
36
+ readonly operation?: CapabilityOperationFactory;
35
37
  readonly locale?: Locale;
36
38
  }
37
39
  export interface CommandDispatchResult {
@@ -81,7 +83,7 @@ export declare class CommandIndex {
81
83
  formatMenu(prefix: string | undefined, locale: Locale): string | undefined;
82
84
  has(name: string): boolean;
83
85
  execute(name: string, args?: readonly string[], operationSnapshot?: RuntimeSnapshot): Promise<unknown>;
84
- dispatch({ input, source, interactionFactory, commandPrefix, snapshot: operationSnapshot, locale: localeOverride }: CommandDispatchRequest): Promise<CommandDispatchResult>;
86
+ dispatch({ input, source, interactionFactory, commandPrefix, snapshot: operationSnapshot, locale: localeOverride, operation: operationFactory }: CommandDispatchRequest): Promise<CommandDispatchResult>;
85
87
  }
86
88
  export declare function isCommandIndex(value: unknown): value is CommandIndex;
87
89
  /**
@@ -195,7 +195,7 @@ export class CommandIndex {
195
195
  admission.release();
196
196
  }
197
197
  }
198
- async dispatch({ input, source, interactionFactory, commandPrefix, snapshot: operationSnapshot = this.snapshot, locale: localeOverride }) {
198
+ async dispatch({ input, source, interactionFactory, commandPrefix, snapshot: operationSnapshot = this.snapshot, locale: localeOverride, operation: operationFactory }) {
199
199
  const locale = localeOverride ?? commandLocale(source, operationSnapshot);
200
200
  const prefix = commandPrefix ?? '';
201
201
  if (this.#menu) {
@@ -247,12 +247,22 @@ export class CommandIndex {
247
247
  const limit = admitCommandExecution(authorSource, match.command.slot.id, match.command.slot.owner, match.command.execution, operationSnapshot);
248
248
  if (limit)
249
249
  return Object.freeze({ matched: true, status: 'limited', command: match.command.name, owner: match.command.slot.owner, limit });
250
- const value = await definition.execute(createCommandContext({
251
- snapshot: operationSnapshot, ownerId: match.command.slot.owner, input: authorSource,
252
- interaction: interactionFactory?.(authorSource), adapter: definition.adapter,
253
- presentation: { locale, formatCommand: (localPath, owner = match.command.owner) => this.formatCommand(owner, localPath, source && typeof source === 'object' && 'conversation' in source ? commandPrefix : undefined, locale) },
254
- }, { args: textArgs(parsed.remaining), params: parsed.params, segments: parsed.remaining, options: parsed.options }));
255
- return Object.freeze({ matched: true, status: 'executed', command: match.command.name, owner: match.command.slot.owner, value });
250
+ const operation = operationFactory?.create({ owner: match.command.slot.owner, signal: operationFactory.signal });
251
+ try {
252
+ const value = await definition.execute(createCommandContext({
253
+ snapshot: operationSnapshot, ownerId: match.command.slot.owner, input: authorSource,
254
+ operation,
255
+ interaction: interactionFactory?.(authorSource), adapter: definition.adapter,
256
+ presentation: { locale, formatCommand: (localPath, owner = match.command.owner) => this.formatCommand(owner, localPath, source && typeof source === 'object' && 'conversation' in source ? commandPrefix : undefined, locale) },
257
+ }, { args: textArgs(parsed.remaining), params: parsed.params, segments: parsed.remaining, options: parsed.options }));
258
+ return Object.freeze({ matched: true, status: 'executed', command: match.command.name, owner: match.command.slot.owner, value });
259
+ }
260
+ finally {
261
+ try {
262
+ await operation?.resources?.close();
263
+ }
264
+ catch { /* Cleanup never replaces the command's actual outcome. */ }
265
+ }
256
266
  }
257
267
  #denied(record) {
258
268
  return Object.freeze({ matched: true, status: 'denied', command: record.name, owner: record.slot.owner });
@@ -3,7 +3,7 @@
3
3
  * @module zhin.js/command
4
4
  */
5
5
  import { type Locale, type PluginId, type RuntimeSnapshot } from '@zhin.js/plugin-runtime';
6
- import { type OperationSession, type AdapterClient, type RegisteredAdapterName, type CapabilityContext } from '@zhin.js/feature-kit';
6
+ import { type CapabilityOperation, type OperationSession, type AdapterClient, type RegisteredAdapterName, type CapabilityContext } from '@zhin.js/feature-kit';
7
7
  import type { UserInteraction } from '@zhin.js/interaction';
8
8
  import { type CommandExecutionPolicy } from './execution-policy.js';
9
9
  export type { CommandExecutionPolicy, CommandExecutionLimit } from './execution-policy.js';
@@ -346,6 +346,7 @@ export interface CommandInvocationArguments {
346
346
  export interface CommandInvocationOperation {
347
347
  readonly snapshot: RuntimeSnapshot;
348
348
  readonly ownerId: PluginId;
349
+ readonly operation?: CapabilityOperation;
349
350
  readonly input?: unknown;
350
351
  readonly interaction?: UserInteraction;
351
352
  readonly adapter?: string;
package/lib/definition.js CHANGED
@@ -177,7 +177,7 @@ export function resolveDynamicParams(params, source) {
177
177
  export function createCommandContext(operation, invocation) {
178
178
  const { snapshot, ownerId, input, interaction, adapter, presentation } = operation;
179
179
  const { args, params = {}, options = {}, segments = [] } = invocation;
180
- const context = createCapabilityContext(snapshot, ownerId, undefined, readOperationInputSignal(input));
180
+ const context = createCapabilityContext({ snapshot, ownerId, signal: readOperationInputSignal(input), operation: operation.operation });
181
181
  const session = resolveCommandSession(input);
182
182
  const parameterValues = { __proto__: null, ...params };
183
183
  const optionValues = { __proto__: null, ...options };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/command",
3
- "version": "1.1.12",
3
+ "version": "1.1.14",
4
4
  "description": "Convention-based Command Feature for Zhin Plugin Runtime",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -21,10 +21,10 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "segment-matcher": "^1.0.5",
24
- "@zhin.js/feature-kit": "1.1.10",
24
+ "@zhin.js/feature-kit": "1.1.12",
25
25
  "@zhin.js/interaction": "1.1.2",
26
- "@zhin.js/permission": "1.1.10",
27
- "@zhin.js/plugin-runtime": "1.1.18"
26
+ "@zhin.js/permission": "1.1.12",
27
+ "@zhin.js/plugin-runtime": "1.1.20"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^26.1.2",
@@ -13,7 +13,7 @@ import {
13
13
  type Locale,
14
14
  } from '@zhin.js/plugin-runtime';
15
15
  import type { UserInteractionFactory } from '@zhin.js/interaction';
16
- import { bindOperationInput } from '@zhin.js/feature-kit';
16
+ import { bindOperationInput, type CapabilityOperationFactory } from '@zhin.js/feature-kit';
17
17
  import {
18
18
  permissionHostToken,
19
19
  type PermissionHost,
@@ -65,6 +65,7 @@ export interface CommandDispatchRequest {
65
65
  readonly interactionFactory?: UserInteractionFactory;
66
66
  readonly commandPrefix?: string;
67
67
  readonly snapshot?: RuntimeSnapshot;
68
+ readonly operation?: CapabilityOperationFactory;
68
69
  readonly locale?: Locale;
69
70
  }
70
71
  /** Presentation and generation shared across one menu lookup/render operation. */
@@ -351,7 +352,7 @@ export class CommandIndex {
351
352
  }
352
353
 
353
354
  async dispatch({ input, source, interactionFactory, commandPrefix, snapshot: operationSnapshot = this.snapshot,
354
- locale: localeOverride }: CommandDispatchRequest): Promise<CommandDispatchResult> {
355
+ locale: localeOverride, operation: operationFactory }: CommandDispatchRequest): Promise<CommandDispatchResult> {
355
356
  const locale = localeOverride ?? commandLocale(source, operationSnapshot);
356
357
  const prefix = commandPrefix ?? '';
357
358
  if (this.#menu) {
@@ -395,13 +396,19 @@ export class CommandIndex {
395
396
  owner: match.command.slot.owner, issues: parsed.issues, help });
396
397
  const limit = admitCommandExecution(authorSource, match.command.slot.id, match.command.slot.owner, match.command.execution, operationSnapshot);
397
398
  if (limit) return Object.freeze({ matched: true, status: 'limited', command: match.command.name, owner: match.command.slot.owner, limit });
398
- const value = await definition.execute(createCommandContext({
399
- snapshot: operationSnapshot, ownerId: match.command.slot.owner, input: authorSource,
400
- interaction: interactionFactory?.(authorSource), adapter: definition.adapter,
401
- presentation: { locale, formatCommand: (localPath, owner = match.command.owner) => this.formatCommand(owner, localPath,
402
- source && typeof source === 'object' && 'conversation' in source ? commandPrefix : undefined, locale) },
403
- }, { args: textArgs(parsed.remaining), params: parsed.params, segments: parsed.remaining, options: parsed.options }));
404
- return Object.freeze({ matched: true, status: 'executed', command: match.command.name, owner: match.command.slot.owner, value });
399
+ const operation = operationFactory?.create({ owner: match.command.slot.owner, signal: operationFactory.signal });
400
+ try {
401
+ const value = await definition.execute(createCommandContext({
402
+ snapshot: operationSnapshot, ownerId: match.command.slot.owner, input: authorSource,
403
+ operation,
404
+ interaction: interactionFactory?.(authorSource), adapter: definition.adapter,
405
+ presentation: { locale, formatCommand: (localPath, owner = match.command.owner) => this.formatCommand(owner, localPath,
406
+ source && typeof source === 'object' && 'conversation' in source ? commandPrefix : undefined, locale) },
407
+ }, { args: textArgs(parsed.remaining), params: parsed.params, segments: parsed.remaining, options: parsed.options }));
408
+ return Object.freeze({ matched: true, status: 'executed', command: match.command.name, owner: match.command.slot.owner, value });
409
+ } finally {
410
+ try { await operation?.resources?.close(); } catch { /* Cleanup never replaces the command's actual outcome. */ }
411
+ }
405
412
  }
406
413
 
407
414
  #denied(record: CommandRecord): CommandDispatchResult {
package/src/definition.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  import { resolveLocale, type Locale, type PluginId, type RuntimeSnapshot } from '@zhin.js/plugin-runtime';
6
6
  import {
7
7
  createCapabilityContext,
8
+ type CapabilityOperation,
8
9
  readOperationClient,
9
10
  readOperationSession,
10
11
  type OperationSession,
@@ -598,6 +599,7 @@ export interface CommandInvocationArguments {
598
599
  export interface CommandInvocationOperation {
599
600
  readonly snapshot: RuntimeSnapshot;
600
601
  readonly ownerId: PluginId;
602
+ readonly operation?: CapabilityOperation;
601
603
  readonly input?: unknown;
602
604
  readonly interaction?: UserInteraction;
603
605
  readonly adapter?: string;
@@ -611,7 +613,7 @@ export interface CommandInvocationOperation {
611
613
  export function createCommandContext(operation: CommandInvocationOperation, invocation: CommandInvocationArguments): CommandContext {
612
614
  const { snapshot, ownerId, input, interaction, adapter, presentation } = operation;
613
615
  const { args, params = {}, options = {}, segments = [] } = invocation;
614
- const context = createCapabilityContext(snapshot, ownerId, undefined, readOperationInputSignal(input));
616
+ const context = createCapabilityContext({ snapshot, ownerId, signal: readOperationInputSignal(input), operation: operation.operation });
615
617
  const session = resolveCommandSession(input);
616
618
  const parameterValues: Record<string, CommandParameterValue> = { __proto__: null, ...params };
617
619
  const optionValues: Record<string, CommandParameterValue> = { __proto__: null, ...options };