@zhin.js/adapter 1.1.5 → 1.1.8

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.
@@ -16,7 +16,7 @@
16
16
  * 接入步骤(以 telegram 为例):
17
17
  * 1. plugin.ts setup 里 `context.resources.provide(telegramRuntimeStateToken, createEndpointRuntimeState())`,
18
18
  * token 由 `defineEndpointRuntimeStateToken('telegram')` 创建。
19
- * 2. adapters/telegram.ts create() 里 `context.use(token).endpoints.set(config.name, { name, mode })`。
19
+ * 2. adapters/telegram.ts create() 里 `context.use(token).endpoints.set(config.id, { id, mode })`。
20
20
  * 3. src 下 `export const telegramEndpointCommands = createEndpointCommands({ adapterKey: 'telegram', ... }, defineCommand)`
21
21
  * (defineCommand 由调用方从 @zhin.js/command 传入——provider 包之间禁止互相 import,
22
22
  * 见 scripts/check-architecture-layers.mjs,故 defineCommand 走依赖注入)。
@@ -28,7 +28,13 @@
28
28
  */
29
29
  import fs from 'node:fs';
30
30
  import path from 'node:path';
31
- import { createToken, type Token } from '@zhin.js/plugin-runtime';
31
+ import {
32
+ createToken,
33
+ outboundHostToken,
34
+ type OutboundHost,
35
+ type OutboundSendInput,
36
+ type Token,
37
+ } from '@zhin.js/plugin-runtime';
32
38
  import { isMap, isSeq, parseDocument, type YAMLSeq } from 'yaml';
33
39
 
34
40
  // ---------------------------------------------------------------------------
@@ -54,8 +60,9 @@ export function isEndpointOperator(config: unknown, input: unknown): boolean {
54
60
  }
55
61
  }
56
62
  if (masters.size === 0) return true;
57
- const sender = String((input as { sender?: unknown } | null | undefined)?.sender ?? '').trim();
58
- return !!sender && masters.has(sender);
63
+ const senderRaw = (input as { sender?: { id?: string } | null } | null | undefined)?.sender;
64
+ const senderId = (typeof senderRaw === 'object' && senderRaw?.id) ? senderRaw.id.trim() : '';
65
+ return !!senderId && masters.has(senderId);
59
66
  }
60
67
 
61
68
  /** add/remove 的拒绝文案(list 只读,不校验)。 */
@@ -80,18 +87,83 @@ export function extractEndpointCommandReply(input: unknown): EndpointCommandRepl
80
87
  return async () => undefined;
81
88
  }
82
89
 
90
+ /**
91
+ * bindFlow 后续状态推送:优先走 OutboundHost(不受 inbound Message reply scope 限制)。
92
+ * 扫码绑定等长流程会在命令结果已送达、`$reply` 已冻结后继续 notify,必须用 durable 出站。
93
+ */
94
+ export function createDurableEndpointCommandReply(
95
+ input: unknown,
96
+ use: EndpointCommandUse,
97
+ ): EndpointCommandReply {
98
+ const scoped = extractEndpointCommandReply(input);
99
+ const target = readOutboundSendTarget(input);
100
+ if (!target) return scoped;
101
+
102
+ return async (text) => {
103
+ let outbound: OutboundHost | undefined;
104
+ try {
105
+ outbound = use(outboundHostToken);
106
+ } catch {
107
+ outbound = undefined;
108
+ }
109
+ if (outbound) {
110
+ await outbound.send({ ...target, content: text });
111
+ return;
112
+ }
113
+ await scoped(text);
114
+ };
115
+ }
116
+
117
+ function readOutboundSendTarget(input: unknown): Omit<OutboundSendInput, 'content'> | undefined {
118
+ const message = input as {
119
+ conversation?: {
120
+ endpoint?: { id?: unknown; adapter?: unknown };
121
+ kind?: unknown;
122
+ id?: unknown;
123
+ parent?: OutboundSendInput['conversation']['parent'];
124
+ threadId?: unknown;
125
+ };
126
+ metadata?: Readonly<Record<string, unknown>>;
127
+ } | null | undefined;
128
+ const conversation = message?.conversation;
129
+ const endpointKeyRaw = conversation?.endpoint?.id;
130
+ const adapterRaw = conversation?.endpoint?.adapter;
131
+ const kind = conversation?.kind;
132
+ const id = conversation?.id;
133
+ if (
134
+ endpointKeyRaw == null
135
+ || adapterRaw == null
136
+ || (kind !== 'private' && kind !== 'group' && kind !== 'channel')
137
+ || typeof id !== 'string'
138
+ || !id
139
+ ) {
140
+ return undefined;
141
+ }
142
+ const live = String(message?.metadata?.endpoint ?? message?.metadata?.endpointKey ?? '').trim();
143
+ return {
144
+ adapter: String(adapterRaw),
145
+ endpointKey: live || String(endpointKeyRaw),
146
+ conversation: {
147
+ kind,
148
+ id,
149
+ parent: conversation.parent,
150
+ threadId: typeof conversation.threadId === 'string' ? conversation.threadId : undefined,
151
+ },
152
+ };
153
+ }
154
+
83
155
  // ---------------------------------------------------------------------------
84
156
  // 运行时状态:adapter create() 注册的 running endpoints
85
157
  // ---------------------------------------------------------------------------
86
158
 
87
159
  export interface EndpointRunningInfo {
88
- readonly name: string;
160
+ readonly id: string;
89
161
  /** 连接模式(ws / wss / polling / socket-mode …),仅用于 list 展示。 */
90
162
  readonly mode?: string;
91
163
  }
92
164
 
93
165
  export interface EndpointRuntimeState {
94
- /** 当前 generation 已成功创建的 endpoint(name → 描述) */
166
+ /** 当前 generation 已成功创建的 endpoint(id → 描述) */
95
167
  readonly endpoints: Map<string, EndpointRunningInfo>;
96
168
  }
97
169
 
@@ -127,10 +199,10 @@ function envSlug(text: string): string {
127
199
  /** 派生 endpoint 凭据的 env 键:`${ADAPTER}_${NAME}_${FIELD}`(如 `TELEGRAM_MY_BOT_TOKEN`) */
128
200
  export function buildEndpointEnvKey(
129
201
  adapterKey: string,
130
- endpointName: string,
202
+ endpointId: string,
131
203
  fieldKey: string,
132
204
  ): string {
133
- return `${envSlug(adapterKey)}_${envSlug(endpointName)}_${envSlug(fieldKey)}`;
205
+ return `${envSlug(adapterKey)}_${envSlug(endpointId)}_${envSlug(fieldKey)}`;
134
206
  }
135
207
 
136
208
  function escapeRegExp(text: string): string {
@@ -170,7 +242,7 @@ export function persistEndpointEnvValues(
170
242
  // ---------------------------------------------------------------------------
171
243
 
172
244
  export interface ConfiguredEndpointEntry {
173
- name: string;
245
+ id: string;
174
246
  [key: string]: unknown;
175
247
  }
176
248
 
@@ -223,14 +295,14 @@ export function listConfiguredEndpoints(
223
295
  if (!Array.isArray(endpoints)) return [];
224
296
  return endpoints.filter(
225
297
  (entry): entry is ConfiguredEndpointEntry =>
226
- !!entry && typeof entry === 'object' && typeof (entry as { name?: unknown }).name === 'string',
298
+ !!entry && typeof entry === 'object' && typeof (entry as { id?: unknown }).id === 'string',
227
299
  );
228
300
  }
229
301
 
230
- function entryName(item: unknown): string | undefined {
302
+ function entryId(item: unknown): string | undefined {
231
303
  if (!isMap(item)) return undefined;
232
- const name = item.get('name');
233
- return typeof name === 'string' && name ? name : undefined;
304
+ const id = item.get('id');
305
+ return typeof id === 'string' && id ? id : undefined;
234
306
  }
235
307
 
236
308
  /**
@@ -269,7 +341,7 @@ function ensureEndpointsSeq(
269
341
  return doc.getIn(['plugins', adapterKey, 'endpoints']) as YAMLSeq;
270
342
  }
271
343
 
272
- /** 追加 endpoint 到 plugins.<adapterKey>.endpoints;name 已存在时报错 */
344
+ /** 追加 endpoint 到 plugins.<adapterKey>.endpoints;id 已存在时报错 */
273
345
  export function addEndpointToConfig(
274
346
  adapterKey: string,
275
347
  entry: ConfiguredEndpointEntry,
@@ -277,23 +349,23 @@ export function addEndpointToConfig(
277
349
  ): string {
278
350
  const document = readConfigDocument(adapterKey, projectRoot);
279
351
  const seq = ensureEndpointsSeq(document.doc, adapterKey);
280
- if (seq.items.some((item) => entryName(item) === entry.name)) {
281
- throw new Error(`配置中已存在 ${adapterKey} endpoint「${entry.name}」,可先 ${adapterKey}.endpoint remove ${entry.name} 再重新添加`);
352
+ if (seq.items.some((item) => entryId(item) === entry.id)) {
353
+ throw new Error(`配置中已存在 ${adapterKey} endpoint「${entry.id}」,可先 ${adapterKey}.endpoint remove ${entry.id} 再重新添加`);
282
354
  }
283
355
  seq.items.push(document.doc.createNode(entry));
284
356
  writeConfigDocument(document);
285
357
  return document.filePath;
286
358
  }
287
359
 
288
- /** 按 name 移除 plugins.<adapterKey>.endpoints 项;不存在返回 false */
360
+ /** 按 id 移除 plugins.<adapterKey>.endpoints 项;不存在返回 false */
289
361
  export function removeEndpointFromConfig(
290
362
  adapterKey: string,
291
- name: string,
363
+ id: string,
292
364
  projectRoot?: string,
293
365
  ): { removed: boolean; filePath: string } {
294
366
  const document = readConfigDocument(adapterKey, projectRoot);
295
367
  const seq = ensureEndpointsSeq(document.doc, adapterKey);
296
- const next = seq.items.filter((item) => entryName(item) !== name);
368
+ const next = seq.items.filter((item) => entryId(item) !== id);
297
369
  if (next.length === seq.items.length) {
298
370
  return { removed: false, filePath: document.filePath };
299
371
  }
@@ -322,9 +394,9 @@ export type EndpointCommandUse = <T>(token: Token<T>) => T;
322
394
 
323
395
  /** bindFlow 钩子上下文:接管 add 命令的自定义绑定流程(如 QQ 扫码)。 */
324
396
  export interface EndpointBindFlowContext {
325
- /** 命令参数 name(未指定时为 undefined,流程可自行决定终名) */
326
- readonly name?: string;
327
- /** 向当前会话推送后续状态(二维码刷新 / 成功 / 失败) */
397
+ /** 命令参数 id(未指定时为 undefined,流程可自行决定终名) */
398
+ readonly id?: string;
399
+ /** 向当前会话推送后续状态(二维码刷新 / 成功 / 失败;走 durable OutboundHost,可在命令 reply scope 结束后调用) */
328
400
  readonly reply: EndpointCommandReply;
329
401
  readonly config: unknown;
330
402
  readonly input: unknown;
@@ -406,9 +478,9 @@ export interface EndpointCommands<TCommand = EndpointCommandDefinition> {
406
478
  readonly remove: TCommand;
407
479
  }
408
480
 
409
- function endpointNameParam(params: Readonly<Record<string, unknown>>): string | undefined {
410
- const name = params.name;
411
- return typeof name === 'string' && name.trim() ? name.trim() : undefined;
481
+ function endpointIdParam(params: Readonly<Record<string, unknown>>): string | undefined {
482
+ const id = params.id;
483
+ return typeof id === 'string' && id.trim() ? id.trim() : undefined;
412
484
  }
413
485
 
414
486
  /** list 文案:运行中 + 配置中两段,footer 可选。 */
@@ -427,7 +499,7 @@ export function formatEndpointList(
427
499
  lines.push(' (无)');
428
500
  } else {
429
501
  for (const endpoint of running) {
430
- lines.push(endpoint.mode ? ` - ${endpoint.name}(${endpoint.mode})` : ` - ${endpoint.name}`);
502
+ lines.push(endpoint.mode ? ` - ${endpoint.id}(${endpoint.mode})` : ` - ${endpoint.id}`);
431
503
  }
432
504
  }
433
505
  lines.push(`【配置中的 ${spec.adapterDisplayName} endpoints】(zhin.config.yml → plugins.${spec.adapterKey}.endpoints)`);
@@ -436,7 +508,7 @@ export function formatEndpointList(
436
508
  } else {
437
509
  for (const entry of source.configured) {
438
510
  const detail = spec.describeEntry?.(entry);
439
- lines.push(detail ? ` - ${entry.name}(${detail})` : ` - ${entry.name}`);
511
+ lines.push(detail ? ` - ${entry.id}(${detail})` : ` - ${entry.id}`);
440
512
  }
441
513
  }
442
514
  if (source.footer) lines.push(source.footer);
@@ -455,13 +527,13 @@ function addUsage(spec: EndpointCommandsSpec): string {
455
527
  ].filter(Boolean).join(',');
456
528
  return marks ? `${field.key}(${marks})` : field.key;
457
529
  }).join('、')}`;
458
- return `用法:${spec.adapterKey}.endpoint add <name> <key=value...>${fieldText}`;
530
+ return `用法:${spec.adapterKey}.endpoint add <id> <key=value...>${fieldText}`;
459
531
  }
460
532
 
461
533
  /** add(kv 模式)的完整业务逻辑:解析 kv → 凭据写 .env → 追加 yaml;返回回复文本。 */
462
534
  export function addEndpointFromKeyValues(
463
535
  spec: EndpointCommandsSpec,
464
- name: string,
536
+ id: string,
465
537
  args: readonly string[],
466
538
  projectRoot?: string,
467
539
  ): string {
@@ -484,13 +556,13 @@ export function addEndpointFromKeyValues(
484
556
  if (missing.length > 0) {
485
557
  return `缺少必填字段:${missing.map((field) => field.key).join('、')}。${addUsage(spec)}`;
486
558
  }
487
- const entry: ConfiguredEndpointEntry = { name };
559
+ const entry: ConfiguredEndpointEntry = { id };
488
560
  const envValues: Record<string, string> = {};
489
561
  for (const field of fields) {
490
562
  const value = values.get(field.key);
491
563
  if (value === undefined) continue;
492
564
  if (field.env) {
493
- const envKey = buildEndpointEnvKey(spec.adapterKey, name, field.key);
565
+ const envKey = buildEndpointEnvKey(spec.adapterKey, id, field.key);
494
566
  envValues[envKey] = value;
495
567
  entry[field.key] = `\${${envKey}}`;
496
568
  } else {
@@ -498,11 +570,10 @@ export function addEndpointFromKeyValues(
498
570
  }
499
571
  }
500
572
  try {
501
- // 先写配置(重名等校验失败时不留孤儿 .env 键),再落 .env 凭据
502
573
  const filePath = addEndpointToConfig(spec.adapterKey, entry, projectRoot);
503
574
  if (Object.keys(envValues).length > 0) persistEndpointEnvValues(envValues, projectRoot);
504
575
  return (
505
- `✅ endpoint「${name}」已追加到 ${filePath} 的 plugins.${spec.adapterKey}.endpoints` +
576
+ `✅ endpoint「${id}」已追加到 ${filePath} 的 plugins.${spec.adapterKey}.endpoints` +
506
577
  `${Object.keys(envValues).length > 0 ? '(凭据已写入 .env)' : ''}。\n` +
507
578
  '⚠️ 需重启 zhin 后新 endpoint 才会生效。'
508
579
  );
@@ -512,13 +583,13 @@ export function addEndpointFromKeyValues(
512
583
  }
513
584
 
514
585
  /** remove 的完整业务逻辑:从 yaml 移除;返回回复文本。 */
515
- export function removeEndpointByName(
586
+ export function removeEndpointById(
516
587
  spec: Pick<EndpointCommandsSpec, 'adapterKey'>,
517
- name: string,
588
+ id: string,
518
589
  projectRoot?: string,
519
590
  ): string {
520
- const trimmed = name.trim();
521
- if (!trimmed) return `用法:${spec.adapterKey}.endpoint remove <name>`;
591
+ const trimmed = id.trim();
592
+ if (!trimmed) return `用法:${spec.adapterKey}.endpoint remove <id>`;
522
593
  try {
523
594
  const { removed, filePath } = removeEndpointFromConfig(spec.adapterKey, trimmed, projectRoot);
524
595
  if (!removed) {
@@ -553,29 +624,29 @@ export function createEndpointCommands<TCommand>(
553
624
  add: defineCommand({
554
625
  description: spec.addDescription
555
626
  ?? `手动添加 ${spec.adapterDisplayName} endpoint(凭据写入 .env 并追加到 zhin.config.yml,重启生效)`,
556
- params: { name: { type: 'string', description: 'endpoint 名称' } },
627
+ params: { id: { type: 'string', description: 'endpoint ID' } },
557
628
  execute({ config, input, params, args, use }) {
558
629
  if (!isEndpointOperator(config, input)) return forbidden;
559
- const name = endpointNameParam(params);
630
+ const id = endpointIdParam(params);
560
631
  if (spec.bindFlow) {
561
632
  return spec.bindFlow({
562
- name,
563
- reply: extractEndpointCommandReply(input),
633
+ id,
634
+ reply: createDurableEndpointCommandReply(input, use),
564
635
  config,
565
636
  input,
566
637
  use,
567
638
  });
568
639
  }
569
- if (!name) return addUsage(spec);
570
- return addEndpointFromKeyValues(spec, name, args);
640
+ if (!id) return addUsage(spec);
641
+ return addEndpointFromKeyValues(spec, id, args);
571
642
  },
572
643
  }),
573
644
  remove: defineCommand({
574
645
  description: `从 zhin.config.yml 的 plugins.${spec.adapterKey}.endpoints 移除指定 endpoint(重启生效)`,
575
- params: { name: { type: 'string', description: 'endpoint 名称' } },
646
+ params: { id: { type: 'string', description: 'endpoint ID' } },
576
647
  execute({ config, input, params }) {
577
648
  if (!isEndpointOperator(config, input)) return forbidden;
578
- return removeEndpointByName(spec, String(params.name ?? ''));
649
+ return removeEndpointById(spec, String(params.id ?? ''));
579
650
  },
580
651
  }),
581
652
  });
@@ -1,9 +1,4 @@
1
- import {
2
- formatLegacyConversationRef,
3
- formatLegacyMessageRef,
4
- type ConversationTarget,
5
- type MessageTarget,
6
- } from '@zhin.js/im-contract';
1
+ import type { ConversationRef, MessageRef } from '@zhin.js/im-contract';
7
2
 
8
3
  /**
9
4
  * Transport-neutral control plane for a live endpoint.
@@ -13,100 +8,26 @@ import {
13
8
  * to know a protocol's method names or identifier layout.
14
9
  */
15
10
  export interface EndpointControl {
16
- recall?(message: MessageTarget): Promise<void>;
17
- edit?(message: MessageTarget, content: unknown): Promise<string | null>;
11
+ recall?(message: MessageRef): Promise<void>;
12
+ edit?(message: MessageRef, content: unknown): Promise<string | null>;
18
13
  addReaction?(
19
- message: MessageTarget,
14
+ message: MessageRef,
20
15
  emoji: string,
21
16
  hint?: { readonly sceneType?: string; readonly channelId?: string },
22
17
  ): Promise<string | null>;
23
- removeReaction?(message: MessageTarget, reactionId: string): Promise<void>;
24
- typing?(conversation: ConversationTarget, active?: boolean): Promise<void>;
18
+ removeReaction?(message: MessageRef, reactionId: string): Promise<void>;
19
+ typing?(conversation: ConversationRef, active?: boolean): Promise<void>;
25
20
  }
26
21
 
27
22
  export interface EndpointWithControl {
28
23
  readonly control?: EndpointControl;
29
24
  }
30
25
 
31
- interface LegacyEndpointControlSurface {
32
- recallMessage?(messageId: string): Promise<void>;
33
- $recallMessage?(messageId: string): Promise<void>;
34
- editMessage?(messageId: string, content: unknown): Promise<string | null>;
35
- $editMessage?(messageId: string, content: unknown): Promise<string | null>;
36
- addReaction?(
37
- messageId: string,
38
- emoji: string,
39
- hint?: { readonly sceneType?: string; readonly channelId?: string },
40
- ): Promise<string | null>;
41
- $addReaction?(
42
- messageId: string,
43
- emoji: string,
44
- hint?: { readonly sceneType?: string; readonly channelId?: string },
45
- ): Promise<string | null>;
46
- removeReaction?(messageId: string, reactionId: string): Promise<void>;
47
- $removeReaction?(messageId: string, reactionId: string): Promise<void>;
48
- typing?(target: string, active?: boolean): Promise<void>;
49
- $typing?(target: string, active?: boolean): Promise<void>;
50
- }
51
-
52
- /**
53
- * Resolves the public control port. The legacy branch is deliberately kept in
54
- * Adapter only: it is a migration bridge for existing protocol endpoints, not
55
- * an IM Core extension point. New adapters must expose `control` directly.
56
- */
57
- export function resolveEndpointControl(endpoint: unknown): EndpointControl | undefined {
26
+ /** Reads the canonical control port without probing protocol-specific methods. */
27
+ export function endpointControlOf(endpoint: unknown): EndpointControl | undefined {
58
28
  if (!endpoint || typeof endpoint !== 'object') return undefined;
59
29
  const explicit = (endpoint as EndpointWithControl).control;
60
- if (explicit && typeof explicit === 'object') return explicit;
61
-
62
- const legacy = endpoint as LegacyEndpointControlSurface;
63
- const recall = legacy.recallMessage ?? legacy.$recallMessage;
64
- const edit = legacy.editMessage ?? legacy.$editMessage;
65
- const addReaction = legacy.addReaction ?? legacy.$addReaction;
66
- const removeReaction = legacy.removeReaction ?? legacy.$removeReaction;
67
- const typing = legacy.typing ?? legacy.$typing;
68
- if (!recall && !edit && !addReaction && !removeReaction && !typing) return undefined;
69
-
70
- return Object.freeze({
71
- ...(recall
72
- ? { recall: (message: MessageTarget) => recall.call(endpoint, legacyMessageId(message)) }
73
- : {}),
74
- ...(edit
75
- ? {
76
- edit: (message: MessageTarget, content: unknown) =>
77
- edit.call(endpoint, legacyMessageId(message), content),
78
- }
79
- : {}),
80
- ...(addReaction
81
- ? {
82
- addReaction: (
83
- message: MessageTarget,
84
- emoji: string,
85
- hint?: { readonly sceneType?: string; readonly channelId?: string },
86
- ) => addReaction.call(endpoint, legacyMessageId(message), emoji, hint),
87
- }
88
- : {}),
89
- ...(removeReaction
90
- ? {
91
- removeReaction: (message: MessageTarget, reactionId: string) =>
92
- removeReaction.call(endpoint, legacyMessageId(message), reactionId),
93
- }
94
- : {}),
95
- ...(typing
96
- ? {
97
- typing: (conversation: ConversationTarget, active?: boolean) =>
98
- typing.call(endpoint, legacyConversationTarget(conversation), active),
99
- }
100
- : {}),
101
- });
102
- }
103
-
104
- function legacyMessageId(message: MessageTarget): string {
105
- return typeof message === 'string' ? message : formatLegacyMessageRef(message);
106
- }
107
-
108
- function legacyConversationTarget(conversation: ConversationTarget): string {
109
- return typeof conversation === 'string' ? conversation : formatLegacyConversationRef(conversation);
30
+ return explicit && typeof explicit === 'object' ? explicit : undefined;
110
31
  }
111
32
 
112
33
  /** Checks only an Endpoint's explicit `control` port, never the legacy bridge. */
@@ -19,7 +19,7 @@
19
19
  *
20
20
  * 迁移指引(以 napcat/milky/onebot WS endpoint 为例):
21
21
  * 1. 删除 #started / #stopping / #reconnectTimer / #heartbeatTimer / opened 旗标,
22
- * 构造器里 `this.#lifecycle = createEndpointLifecycle({ name: config.name, reconnect, heartbeat })`。
22
+ * 构造器里 `this.#lifecycle = createEndpointLifecycle({ name: config.id, reconnect, heartbeat })`。
23
23
  * 2. `start()` 改为:
24
24
  * ```ts
25
25
  * this.#unregisterAgent = registerXxxAgentEndpoint(name, this); // agent 注册仍在适配器侧
package/src/provider.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { featureId, type RuntimeSnapshot } from '@zhin.js/plugin-runtime';
1
+ import { featureId } from '@zhin.js/plugin-runtime';
2
2
  import { defineFeatureProvider, typeScriptModules } from '@zhin.js/feature-kit';
3
3
  import { AdapterIndex } from './adapter-index.js';
4
4
  import { parseAdapterDefinition } from './definition.js';
@@ -18,31 +18,18 @@ const adapterFeature = defineFeatureProvider({
18
18
  },
19
19
  runtime: {
20
20
  async project(slots, context) {
21
- const index = await AdapterIndex.create(slots, context.snapshot);
22
- let previousIndex: AdapterIndex | undefined;
21
+ const index = await AdapterIndex.create(slots, context.snapshot, context.signal);
23
22
  return {
24
23
  value: index,
25
24
  dispose: () => index.stop(),
26
25
  handoff: {
27
- quiescePrevious(previous) {
28
- previousIndex = previousAdapterIndex(previous);
29
- return previousIndex?.close();
30
- },
31
- activateNext: () => index.start(),
26
+ activateNext: (signal) => index.activate(signal),
32
27
  deactivateNext: () => index.stop(),
33
- resumePrevious() {
34
- previousIndex?.open();
35
- },
36
- openNext: () => index.open(),
37
28
  },
38
29
  };
39
30
  },
40
31
  },
41
32
  });
42
33
 
43
- function previousAdapterIndex(snapshot: RuntimeSnapshot): AdapterIndex | undefined {
44
- return snapshot.projections.get(adapterFeatureId) as AdapterIndex | undefined;
45
- }
46
-
47
34
  export { adapterFeature };
48
35
  export default adapterFeature;