@ai-matrx/summon 0.1.15 → 0.1.17
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/CHANGELOG.md +24 -1
- package/dist/handles.cjs +63 -50
- package/dist/handles.cjs.map +1 -1
- package/dist/handles.d.cts +2 -5
- package/dist/handles.d.ts +2 -5
- package/dist/handles.js +63 -50
- package/dist/handles.js.map +1 -1
- package/dist/{helpers-2NY1ha0V.d.cts → ids-83h05Ed-.d.cts} +4 -1
- package/dist/{helpers-2NY1ha0V.d.ts → ids-83h05Ed-.d.ts} +4 -1
- package/dist/index.cjs +63 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +63 -50
- package/dist/index.js.map +1 -1
- package/dist/open.cjs +158 -2
- package/dist/open.cjs.map +1 -1
- package/dist/open.d.cts +41 -2
- package/dist/open.d.ts +41 -2
- package/dist/open.js +158 -2
- package/dist/open.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,15 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
- **Light handle entries on `./open`: `registerHandleEntry(slot, { id?, groupId?, owner?, context? })`, `unregisterHandleEntry(id)`, `removeHandleGroup(groupId)`, `newHandleId()`** — register a callback, payload group or object handle NOW without loading `./handles`. The registry's state, insertion and removal moved to `src/open/handleEntries.ts` (and `newHandleId` to `src/open/ids.ts`); `callbackManager` delegates to them, so both paths share one slot and one removal path. Why: an opener that registers on open kept `callbackManager` (~4.7 KB min) in every route's first load. Triggers, subscribe, requests and channels stay on `./handles` (load it where you emit). Guard: `src/open/handleEntries.test.ts`; `importCost` keeps `./open` free of `handles/*` except the slot. Consumer action: none (additive).
|
|
6
|
+
- **`lazyOpener(overlayId, load, { instanceId })` on `./open`**: an opener whose code loads on its first call. It returns the real opener's signature, hands back a stand-in handle at once (its `instanceId` minted — `"mint"`, written into the options the real opener receives — or derived by a host rule), loads once (share `load` across a set: one import()), then opens with the same arguments; `update`/`close` made meanwhile replay in order, a window closed before the chunk arrives never opens, a failed load is announced and retried on the next call. Once loaded every call is the real opener, synchronously. `mintInstanceId(overlayId)` is the `<id>-<time>-<rand>` shape hand-written openers mint. Why: a host's opener registry (chat's `windows.openers`) put every opener module in first load. Guard: `src/open/lazyOpener.test.ts`. Consumer action: none (additive).
|
|
5
7
|
- **`defineOverlay` moves to its own entry, `@ai-matrx/summon/define`** (breaking: `./root` no longer exports it or `CALLBACK_GROUP_KEY`; the types stay on `./root`). Why: a bundler keeps every export of a module that anything uses, and each entry is one module — definitions are read only by a host's lazy registry, yet `defineOverlay` sat in `./root` and rode into every route's first load. Consumer action: import `defineOverlay` from `@ai-matrx/summon/define`, only in definition modules (never in a file callers import for its openers).
|
|
6
8
|
- **`peekHandle(id)` on `./open`**: `callbackManager.get(id)` without loading the handles registry (it reads the registry's global slot; `undefined` until something registers). For boot-time code that only resolves ids. Guard: `src/open/peek.test.ts`.
|
|
7
9
|
- **The `?panels=` grammar's READ side moves to `./url`** (`parseLinkToken`, `parseLinkParam`, `decodeLinkPart`, `readPanelsParam`, `withPanelsParam`); `./open` keeps the WRITE side (`formatLinkToken`, `encodeLinkId`, `encodeLinkArg`, `LinkToken`, `LinkEntry`). Parsing happens only in the lazy link runtime. Consumer action: import parsers from `@ai-matrx/summon/url`. Guard: `src/importCost.test.ts` ("first-load entries export nothing only the lazy runtime needs").
|
|
8
10
|
|
|
9
|
-
## 0.1.
|
|
11
|
+
## 0.1.17
|
|
10
12
|
|
|
11
13
|
UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
|
|
12
14
|
Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/summon/src`.
|
|
13
15
|
|
|
16
|
+
## 0.1.15
|
|
17
|
+
|
|
18
|
+
SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
|
|
19
|
+
so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
|
|
20
|
+
Diff: `git diff 4d394e1dd8 290ebde895 -- apps/shared/summon/src`
|
|
21
|
+
|
|
22
|
+
Changed shipped source files (13):
|
|
23
|
+
- `apps/shared/summon/src/define.ts`
|
|
24
|
+
- `apps/shared/summon/src/define/defineOverlay.tsx`
|
|
25
|
+
- `apps/shared/summon/src/handles/callbackManager.ts`
|
|
26
|
+
- `apps/shared/summon/src/handles/slot.ts`
|
|
27
|
+
- `apps/shared/summon/src/open.ts`
|
|
28
|
+
- `apps/shared/summon/src/open/grammar.ts`
|
|
29
|
+
- `apps/shared/summon/src/open/peek.ts`
|
|
30
|
+
- `apps/shared/summon/src/react.ts`
|
|
31
|
+
- `apps/shared/summon/src/root.ts`
|
|
32
|
+
- `apps/shared/summon/src/url.ts`
|
|
33
|
+
- `apps/shared/summon/src/url/OverlayLinkManager.tsx`
|
|
34
|
+
- `apps/shared/summon/src/url/door.ts`
|
|
35
|
+
- ...and 1 more
|
|
36
|
+
|
|
14
37
|
## 0.1.14
|
|
15
38
|
### Unreleased
|
|
16
39
|
|
package/dist/handles.cjs
CHANGED
|
@@ -36,10 +36,7 @@ __export(handles_exports, {
|
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(handles_exports);
|
|
38
38
|
|
|
39
|
-
// src/
|
|
40
|
-
var import_global_slot = require("@ai-matrx/kit/global-slot");
|
|
41
|
-
|
|
42
|
-
// src/handles/ids.ts
|
|
39
|
+
// src/open/ids.ts
|
|
43
40
|
function newHandleId() {
|
|
44
41
|
const c = typeof globalThis.crypto === "undefined" ? void 0 : globalThis.crypto;
|
|
45
42
|
if (c && typeof c.randomUUID === "function") return c.randomUUID();
|
|
@@ -58,13 +55,69 @@ function newHandleId() {
|
|
|
58
55
|
// src/handles/slot.ts
|
|
59
56
|
var HANDLES_SLOT = "ai-matrx.summon.handles";
|
|
60
57
|
|
|
61
|
-
// src/
|
|
58
|
+
// src/open/handleEntries.ts
|
|
59
|
+
var import_global_slot = require("@ai-matrx/kit/global-slot");
|
|
62
60
|
var handlesState = () => (0, import_global_slot.globalSlot)(HANDLES_SLOT, () => ({
|
|
63
61
|
callbacks: /* @__PURE__ */ new Map(),
|
|
64
62
|
groups: /* @__PURE__ */ new Map(),
|
|
65
63
|
owners: /* @__PURE__ */ new Map(),
|
|
66
64
|
registryListeners: /* @__PURE__ */ new Set()
|
|
67
65
|
}));
|
|
66
|
+
function notifyHandlesRegistry() {
|
|
67
|
+
for (const listener of [...handlesState().registryListeners]) listener();
|
|
68
|
+
}
|
|
69
|
+
function deleteHandleEntry(id, notify = true) {
|
|
70
|
+
const s = handlesState();
|
|
71
|
+
const entry = s.callbacks.get(id);
|
|
72
|
+
if (!entry) return;
|
|
73
|
+
s.callbacks.delete(id);
|
|
74
|
+
if (entry.groupId !== void 0) {
|
|
75
|
+
const group = s.groups.get(entry.groupId);
|
|
76
|
+
group?.delete(id);
|
|
77
|
+
if (group?.size === 0) s.groups.delete(entry.groupId);
|
|
78
|
+
}
|
|
79
|
+
if (entry.owner !== void 0) {
|
|
80
|
+
const owned = s.owners.get(entry.owner);
|
|
81
|
+
owned?.delete(id);
|
|
82
|
+
if (owned?.size === 0) s.owners.delete(entry.owner);
|
|
83
|
+
}
|
|
84
|
+
if (notify) notifyHandlesRegistry();
|
|
85
|
+
entry.onRemove?.();
|
|
86
|
+
}
|
|
87
|
+
function registerHandleEntry(slot, options = {}) {
|
|
88
|
+
const s = handlesState();
|
|
89
|
+
const id = options.id ?? newHandleId();
|
|
90
|
+
if (s.callbacks.has(id)) deleteHandleEntry(id, false);
|
|
91
|
+
const entry = { callback: slot };
|
|
92
|
+
if (options.context !== void 0) entry.context = options.context;
|
|
93
|
+
if (options.groupId !== void 0) entry.groupId = options.groupId;
|
|
94
|
+
if (options.owner !== void 0) entry.owner = options.owner;
|
|
95
|
+
if (options.onRemove !== void 0) entry.onRemove = options.onRemove;
|
|
96
|
+
s.callbacks.set(id, entry);
|
|
97
|
+
if (options.groupId !== void 0) {
|
|
98
|
+
const group = s.groups.get(options.groupId) ?? /* @__PURE__ */ new Set();
|
|
99
|
+
group.add(id);
|
|
100
|
+
s.groups.set(options.groupId, group);
|
|
101
|
+
}
|
|
102
|
+
if (options.owner !== void 0) {
|
|
103
|
+
const owned = s.owners.get(options.owner) ?? /* @__PURE__ */ new Set();
|
|
104
|
+
owned.add(id);
|
|
105
|
+
s.owners.set(options.owner, owned);
|
|
106
|
+
}
|
|
107
|
+
notifyHandlesRegistry();
|
|
108
|
+
return id;
|
|
109
|
+
}
|
|
110
|
+
function removeHandleGroup(groupId) {
|
|
111
|
+
const s = handlesState();
|
|
112
|
+
const group = s.groups.get(groupId);
|
|
113
|
+
if (!group) return;
|
|
114
|
+
s.groups.delete(groupId);
|
|
115
|
+
for (const id of [...group]) deleteHandleEntry(id, false);
|
|
116
|
+
notifyHandlesRegistry();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/handles/callbackManager.ts
|
|
120
|
+
var handlesState2 = () => handlesState();
|
|
68
121
|
function invoke(slot, data, context) {
|
|
69
122
|
if (typeof slot !== "function") return void 0;
|
|
70
123
|
return slot(data, context);
|
|
@@ -74,7 +127,7 @@ function invokeListener(listener, data, context) {
|
|
|
74
127
|
}
|
|
75
128
|
var CallbackManager = class {
|
|
76
129
|
get s() {
|
|
77
|
-
return
|
|
130
|
+
return handlesState2();
|
|
78
131
|
}
|
|
79
132
|
/**
|
|
80
133
|
* Observe the registry itself: `listener` runs after any entry is added or removed. This lets a
|
|
@@ -88,7 +141,7 @@ var CallbackManager = class {
|
|
|
88
141
|
};
|
|
89
142
|
}
|
|
90
143
|
notifyRegistry() {
|
|
91
|
-
|
|
144
|
+
notifyHandlesRegistry();
|
|
92
145
|
}
|
|
93
146
|
/** Register a callback; returns its id. */
|
|
94
147
|
register(callback) {
|
|
@@ -100,27 +153,7 @@ var CallbackManager = class {
|
|
|
100
153
|
}
|
|
101
154
|
/** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */
|
|
102
155
|
registerEntry(slot, options) {
|
|
103
|
-
|
|
104
|
-
const callbackId = options.id ?? newHandleId();
|
|
105
|
-
if (s.callbacks.has(callbackId)) this.deleteEntry(callbackId, false);
|
|
106
|
-
const entry = { callback: slot };
|
|
107
|
-
if (options.context !== void 0) entry.context = options.context;
|
|
108
|
-
if (options.groupId !== void 0) entry.groupId = options.groupId;
|
|
109
|
-
if (options.owner !== void 0) entry.owner = options.owner;
|
|
110
|
-
if (options.onRemove !== void 0) entry.onRemove = options.onRemove;
|
|
111
|
-
s.callbacks.set(callbackId, entry);
|
|
112
|
-
if (options.groupId !== void 0) {
|
|
113
|
-
const group = s.groups.get(options.groupId) ?? /* @__PURE__ */ new Set();
|
|
114
|
-
group.add(callbackId);
|
|
115
|
-
s.groups.set(options.groupId, group);
|
|
116
|
-
}
|
|
117
|
-
if (options.owner !== void 0) {
|
|
118
|
-
const owned = s.owners.get(options.owner) ?? /* @__PURE__ */ new Set();
|
|
119
|
-
owned.add(callbackId);
|
|
120
|
-
s.owners.set(options.owner, owned);
|
|
121
|
-
}
|
|
122
|
-
this.notifyRegistry();
|
|
123
|
-
return callbackId;
|
|
156
|
+
return registerHandleEntry(slot, options);
|
|
124
157
|
}
|
|
125
158
|
/** Subscribe to results for a callback id. */
|
|
126
159
|
subscribe(callbackId, listener) {
|
|
@@ -238,12 +271,7 @@ var CallbackManager = class {
|
|
|
238
271
|
}
|
|
239
272
|
/** Remove a group and all its callbacks. */
|
|
240
273
|
removeGroup(groupId) {
|
|
241
|
-
|
|
242
|
-
const group = s.groups.get(groupId);
|
|
243
|
-
if (!group) return;
|
|
244
|
-
s.groups.delete(groupId);
|
|
245
|
-
for (const callbackId of [...group]) this.deleteEntry(callbackId, false);
|
|
246
|
-
this.notifyRegistry();
|
|
274
|
+
removeHandleGroup(groupId);
|
|
247
275
|
}
|
|
248
276
|
/** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */
|
|
249
277
|
disposeOwner(owner) {
|
|
@@ -273,22 +301,7 @@ var CallbackManager = class {
|
|
|
273
301
|
* (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.
|
|
274
302
|
*/
|
|
275
303
|
deleteEntry(callbackId, notify) {
|
|
276
|
-
|
|
277
|
-
const entry = s.callbacks.get(callbackId);
|
|
278
|
-
if (!entry) return;
|
|
279
|
-
s.callbacks.delete(callbackId);
|
|
280
|
-
if (entry.groupId !== void 0) {
|
|
281
|
-
const group = s.groups.get(entry.groupId);
|
|
282
|
-
group?.delete(callbackId);
|
|
283
|
-
if (group?.size === 0) s.groups.delete(entry.groupId);
|
|
284
|
-
}
|
|
285
|
-
if (entry.owner !== void 0) {
|
|
286
|
-
const owned = s.owners.get(entry.owner);
|
|
287
|
-
owned?.delete(callbackId);
|
|
288
|
-
if (owned?.size === 0) s.owners.delete(entry.owner);
|
|
289
|
-
}
|
|
290
|
-
if (notify) this.notifyRegistry();
|
|
291
|
-
entry.onRemove?.();
|
|
304
|
+
deleteHandleEntry(callbackId, notify);
|
|
292
305
|
}
|
|
293
306
|
};
|
|
294
307
|
var callbackManager = new CallbackManager();
|
package/dist/handles.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/handles.ts","../src/handles/callbackManager.ts","../src/handles/ids.ts","../src/handles/slot.ts","../src/handles/helpers.ts"],"sourcesContent":["// @ai-matrx/summon/handles — the handles registry: callbacks, event channels, one-shot requests\n// and imperative object handles, owner-scoped to open overlay instances. Framework-free: importing\n// this section never loads React or Redux.\nexport {\n CallbackManager,\n callbackManager,\n disposeOwner,\n HANDLES_SLOT,\n type Callback,\n type CallbackContext,\n type HandleSlot,\n type ProgressInfo,\n type RegisterOptions,\n type TriggerOptions,\n} from \"./handles/callbackManager\";\nexport {\n createEventChannel,\n createRequest,\n getEventChannel,\n getHandle,\n getRequest,\n HandleDisposedError,\n registerHandle,\n removeHandle,\n type EventChannel,\n type OwnerOption,\n type PendingRequest,\n type RequestHandle,\n} from \"./handles/helpers\";\nexport { newHandleId } from \"./handles/ids\";\n","/**\n * The handles registry — everything that cannot travel through Redux (callbacks, event channels,\n * one-shot requests, imperative object handles) lives here under a string id; only the id goes\n * into overlay open data.\n *\n * Successor of `@ai-matrx/chat` `utils/callbackManager.ts` with the SAME public verbs and types, so\n * moving a caller is an import-path swap. Changes (CHANGELOG 0.1.0):\n * 1. State lives in the global slot \"ai-matrx.summon.handles\", never in module scope: a\n * duplicated module graph (ESM + CJS, unsplit bundles, two packages) sees one registry.\n * 2. Removal bug fixed: `trigger` / `triggerWithContext` / `remove` deleted the entry BEFORE\n * `removeFromGroups` read it, so group membership never shrank and emptied groups were never\n * dropped. Removal now reads the entry first, in one place (`deleteEntry`).\n * 3. Owner scoping: `registerWithContext(cb, { owner })` (an InstanceKey `overlayId:instanceId`)\n * ties the entry to an open overlay instance; `disposeOwner(owner)` removes everything it\n * owns. The overlays close middleware calls it, so no opener disposes by hand.\n * 4. The object slot is handle-agnostic: `registerWidgetHandle<H extends object>(handle)` stores\n * any object (no chat `WidgetHandle` import).\n * 5. Ids come from `crypto.randomUUID` (no `uuid` dependency), with a fallback for insecure\n * contexts where `randomUUID` is absent.\n *\n * Framework-free: no React, no Redux.\n */\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { newHandleId } from \"./ids\";\nimport { HANDLES_SLOT } from \"./slot\";\n\nexport type CallbackContext = Record<string, unknown>;\n\nexport interface ProgressInfo {\n progress?: number;\n status?: \"pending\" | \"running\" | \"completed\" | \"error\";\n error?: Error;\n}\n\nexport type Callback<T = unknown, C extends CallbackContext = CallbackContext> = (\n data: T,\n context?: C,\n) => void | Promise<void>;\n\n/** What an entry's slot holds: a callback, or an object handle retrieved with `get`. */\nexport type HandleSlot = Callback<never, never> | object;\n\nexport interface RegisterOptions<C extends CallbackContext = CallbackContext> {\n context?: C;\n groupId?: string;\n /** The InstanceKey (`overlayId:instanceId`) that owns this entry; disposed when it closes. */\n owner?: string;\n}\n\nexport interface TriggerOptions<C extends CallbackContext = CallbackContext> {\n context?: C;\n progress?: ProgressInfo;\n removeAfterTrigger?: boolean;\n}\n\n/** A registry entry. `callback` is the heterogeneous slot (function or object handle). */\ninterface CallbackEntry {\n callback: HandleSlot;\n context?: CallbackContext;\n groupId?: string;\n owner?: string;\n listeners?: Array<Callback<never, never>>;\n /** Runs once when the entry leaves the registry for any reason (requests reject here). */\n onRemove?: () => void;\n}\n\ninterface HandlesState {\n readonly callbacks: Map<string, CallbackEntry>;\n readonly groups: Map<string, Set<string>>;\n readonly owners: Map<string, Set<string>>;\n readonly registryListeners: Set<() => void>;\n}\n\nexport { HANDLES_SLOT };\n\nconst handlesState = (): HandlesState =>\n globalSlot<HandlesState>(HANDLES_SLOT, () => ({\n callbacks: new Map(),\n groups: new Map(),\n owners: new Map(),\n registryListeners: new Set(),\n }));\n\n/** Invoke a slot as a callback (a function slot only; object handles are never invoked). */\nfunction invoke(slot: HandleSlot, data: unknown, context?: CallbackContext): void | Promise<void> {\n if (typeof slot !== \"function\") return undefined;\n return (slot as Callback<unknown, CallbackContext>)(data, context);\n}\n\nfunction invokeListener(listener: Callback<never, never>, data: unknown, context?: CallbackContext): void | Promise<void> {\n return (listener as Callback<unknown, CallbackContext>)(data, context);\n}\n\n/** Internal registration options beyond the public ones (used by the typed helpers). */\nexport interface InternalRegisterOptions extends RegisterOptions {\n id?: string;\n onRemove?: () => void;\n}\n\nexport class CallbackManager {\n private get s(): HandlesState {\n return handlesState();\n }\n\n /**\n * Observe the registry itself: `listener` runs after any entry is added or removed. This lets a\n * view that resolves ids with `get` re-render when an id goes away (`useSyncExternalStore`).\n */\n subscribeToRegistry(listener: () => void): () => void {\n const listeners = this.s.registryListeners;\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n }\n\n private notifyRegistry(): void {\n for (const listener of [...this.s.registryListeners]) listener();\n }\n\n /** Register a callback; returns its id. */\n register<T>(callback: Callback<T>): string {\n return this.registerEntry(callback as HandleSlot, {});\n }\n\n /** Register with optional context, group and owner. */\n registerWithContext<T, C extends CallbackContext = CallbackContext>(\n callback: Callback<T, C>,\n options: RegisterOptions<C> = {},\n ): string {\n return this.registerEntry(callback as HandleSlot, options);\n }\n\n /** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */\n registerEntry(slot: HandleSlot, options: InternalRegisterOptions): string {\n const s = this.s;\n const callbackId = options.id ?? newHandleId();\n if (s.callbacks.has(callbackId)) this.deleteEntry(callbackId, false);\n const entry: CallbackEntry = { callback: slot };\n if (options.context !== undefined) entry.context = options.context;\n if (options.groupId !== undefined) entry.groupId = options.groupId;\n if (options.owner !== undefined) entry.owner = options.owner;\n if (options.onRemove !== undefined) entry.onRemove = options.onRemove;\n s.callbacks.set(callbackId, entry);\n\n if (options.groupId !== undefined) {\n const group = s.groups.get(options.groupId) ?? new Set<string>();\n group.add(callbackId);\n s.groups.set(options.groupId, group);\n }\n if (options.owner !== undefined) {\n const owned = s.owners.get(options.owner) ?? new Set<string>();\n owned.add(callbackId);\n s.owners.set(options.owner, owned);\n }\n this.notifyRegistry();\n return callbackId;\n }\n\n /** Subscribe to results for a callback id. */\n subscribe<T, C extends CallbackContext = CallbackContext>(callbackId: string, listener: Callback<T, C>): boolean {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return false;\n (entry.listeners ??= []).push(listener as Callback<never, never>);\n return true;\n }\n\n /** Unsubscribe a listener from a callback id. */\n unsubscribe<T, C extends CallbackContext = CallbackContext>(callbackId: string, listener: Callback<T, C>): boolean {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry || !entry.listeners) return false;\n const index = entry.listeners.indexOf(listener as Callback<never, never>);\n if (index === -1) return false;\n entry.listeners.splice(index, 1);\n return true;\n }\n\n /** Run a callback once, notify its listeners, then remove it. */\n trigger<T>(callbackId: string, data: T): void {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return;\n void invoke(entry.callback, data);\n if (entry.listeners && entry.listeners.length > 0) {\n for (const listener of [...entry.listeners]) void invokeListener(listener, data);\n }\n this.deleteEntry(callbackId, true);\n }\n\n /** Trigger with merged context and progress; removes unless `removeAfterTrigger: false`. */\n triggerWithContext<T, C extends CallbackContext = CallbackContext>(\n callbackId: string,\n data: T,\n options?: TriggerOptions<C>,\n ): void {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return;\n const mergedContext: CallbackContext = {\n ...entry.context,\n ...options?.context,\n ...(options?.progress && { progress: options.progress }),\n };\n void invoke(entry.callback, data, mergedContext);\n if (entry.listeners && entry.listeners.length > 0) {\n for (const listener of [...entry.listeners]) void invokeListener(listener, data, mergedContext);\n }\n if (options?.removeAfterTrigger !== false) this.deleteEntry(callbackId, true);\n }\n\n /** Trigger every callback in a group. */\n triggerGroup<T, C extends CallbackContext = CallbackContext>(groupId: string, data: T, options?: TriggerOptions<C>): void {\n const group = this.s.groups.get(groupId);\n if (!group) return;\n for (const callbackId of [...group]) this.triggerWithContext(callbackId, data, options);\n if (options?.removeAfterTrigger !== false) this.s.groups.delete(groupId);\n }\n\n /**\n * Run the one durable command registered for a group. A command is retained on rejection so its\n * caller can retry, and is removed only after its promise settles successfully.\n */\n async triggerGroupCommand<T, C extends CallbackContext = CallbackContext>(\n groupId: string,\n data: T,\n options?: { context?: C; removeAfterSuccess?: boolean },\n ): Promise<void> {\n const group = this.s.groups.get(groupId);\n const callbackIds = group ? [...group] : [];\n const callbackId = callbackIds[0];\n if (callbackIds.length !== 1 || callbackId === undefined) {\n throw new Error(`Expected exactly one command callback for group \"${groupId}\", found ${callbackIds.length}`);\n }\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) throw new Error(`Command callback \"${callbackId}\" is no longer available`);\n\n const mergedContext: CallbackContext = { ...entry.context, ...options?.context };\n await invoke(entry.callback, data, mergedContext);\n\n // Observers never control command settlement: the command already acknowledged.\n for (const listener of entry.listeners ?? []) {\n try {\n await invokeListener(listener, data, mergedContext);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"[overlays/handles] command observer failed\", error);\n }\n }\n\n if (options?.removeAfterSuccess !== false) {\n this.deleteEntry(callbackId, true);\n this.s.groups.delete(groupId);\n }\n }\n\n /** Report progress to a callback (or every callback of a group) without removing it. */\n updateProgress(\n id: string,\n progress: number,\n options?: { status?: ProgressInfo[\"status\"]; error?: Error; groupId?: boolean },\n ): void {\n const progressInfo: ProgressInfo = { progress, status: options?.status || \"running\" };\n if (options?.error !== undefined) progressInfo.error = options.error;\n if (options?.groupId) {\n this.triggerGroup(id, null, { progress: progressInfo, removeAfterTrigger: false });\n } else {\n this.triggerWithContext(id, null, { progress: progressInfo, removeAfterTrigger: false });\n }\n }\n\n /** Create a new, empty group and return its id. */\n createGroup(): string {\n const groupId = newHandleId();\n this.s.groups.set(groupId, new Set());\n return groupId;\n }\n\n /** Remove one entry (and its group/owner membership). */\n remove(callbackId: string): void {\n this.deleteEntry(callbackId, true);\n }\n\n /** Alias of `remove` — the preferred name when unregistering a handle on unmount. */\n unregister(callbackId: string): void {\n this.remove(callbackId);\n }\n\n /** The stored callback or object handle for an id, without invoking it. */\n get<T = unknown>(callbackId: string): T | undefined {\n return this.s.callbacks.get(callbackId)?.callback as T | undefined;\n }\n\n /** True when the id is registered. */\n has(callbackId: string): boolean {\n return this.s.callbacks.has(callbackId);\n }\n\n /**\n * Register an object handle (a widget handle, an editor API, …). The object occupies the slot;\n * it is never invoked by `trigger`, only retrieved with `get`.\n */\n registerWidgetHandle<H extends object>(handle: H, options: { owner?: string } = {}): string {\n return this.registerEntry(handle, options.owner === undefined ? {} : { owner: options.owner });\n }\n\n /** Remove a group and all its callbacks. */\n removeGroup(groupId: string): void {\n const s = this.s;\n const group = s.groups.get(groupId);\n if (!group) return;\n s.groups.delete(groupId);\n for (const callbackId of [...group]) this.deleteEntry(callbackId, false);\n this.notifyRegistry();\n }\n\n /** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */\n disposeOwner(owner: string): number {\n const s = this.s;\n const owned = s.owners.get(owner);\n if (!owned) return 0;\n s.owners.delete(owner);\n const ids = [...owned];\n for (const callbackId of ids) this.deleteEntry(callbackId, false);\n if (ids.length > 0) this.notifyRegistry();\n return ids.length;\n }\n\n /** Ids currently owned by `owner`. */\n ownedBy(owner: string): readonly string[] {\n return [...(this.s.owners.get(owner) ?? [])];\n }\n\n /** The member ids of a group (empty when unknown). */\n groupMembers(groupId: string): readonly string[] {\n return [...(this.s.groups.get(groupId) ?? [])];\n }\n\n /** Total registered entries (diagnostics, leak tests). */\n size(): number {\n return this.s.callbacks.size;\n }\n\n /**\n * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink\n * (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.\n */\n private deleteEntry(callbackId: string, notify: boolean): void {\n const s = this.s;\n const entry = s.callbacks.get(callbackId);\n if (!entry) return;\n s.callbacks.delete(callbackId);\n if (entry.groupId !== undefined) {\n const group = s.groups.get(entry.groupId);\n group?.delete(callbackId);\n if (group?.size === 0) s.groups.delete(entry.groupId);\n }\n if (entry.owner !== undefined) {\n const owned = s.owners.get(entry.owner);\n owned?.delete(callbackId);\n if (owned?.size === 0) s.owners.delete(entry.owner);\n }\n if (notify) this.notifyRegistry();\n entry.onRemove?.();\n }\n}\n\n/** The process-wide handles registry (state in the global slot; every instance shares it). */\nexport const callbackManager = new CallbackManager();\n\n/** Remove everything owned by an open instance (`overlayId:instanceId`). */\nexport function disposeOwner(owner: string): number {\n return callbackManager.disposeOwner(owner);\n}\n","/** A fresh handle id: `crypto.randomUUID` when present, else a v4 UUID from `getRandomValues`. */\nexport function newHandleId(): string {\n const c: Crypto | undefined = typeof globalThis.crypto === \"undefined\" ? undefined : globalThis.crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const bytes = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n }\n bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/** The global slot that holds the handles registry (one registry per realm, whatever the bundling). */\nexport const HANDLES_SLOT = \"ai-matrx.summon.handles\";\n","/**\n * Typed helpers over the handles registry: imperative object handles, event channels and one-shot\n * requests. Each is stored in the same registry (so its id can ride overlay open data and `get`\n * still resolves it) and accepts an `owner` InstanceKey so closing the owning overlay disposes it.\n */\nimport { callbackManager } from \"./callbackManager\";\n\nexport interface OwnerOption {\n /** The InstanceKey (`overlayId:instanceId`) that owns this handle; disposed when it closes. */\n owner?: string;\n}\n\n/** Thrown into a request's promise when it is disposed before being answered. */\nexport class HandleDisposedError extends Error {\n readonly handleId: string;\n constructor(handleId: string, message = `[overlays/handles] \"${handleId}\" was disposed before it settled`) {\n super(message);\n this.name = \"HandleDisposedError\";\n this.handleId = handleId;\n }\n}\n\nfunction ownerOptions(options: OwnerOption | undefined): { owner?: string } {\n return options?.owner === undefined ? {} : { owner: options.owner };\n}\n\n// ── Imperative object handles ────────────────────────────────────────────────────────────────\n\n/**\n * Register an imperative object handle under a caller-chosen id (an editor API, a widget handle,\n * a ref's methods). Re-registering an id replaces the previous handle. Returns a disposer that\n * removes the handle only if it is still the one registered (safe for React effect cleanup).\n */\nexport function registerHandle<T extends object>(id: string, handle: T, options?: OwnerOption): () => void {\n callbackManager.registerEntry(handle, { id, ...ownerOptions(options) });\n return () => {\n if (callbackManager.get<T>(id) === handle) callbackManager.remove(id);\n };\n}\n\n/** The object handle registered under `id`, or undefined. */\nexport function getHandle<T extends object>(id: string): T | undefined {\n const value = callbackManager.get<unknown>(id);\n return typeof value === \"object\" && value !== null ? (value as T) : undefined;\n}\n\n/** Remove the handle registered under `id`. */\nexport function removeHandle(id: string): void {\n callbackManager.remove(id);\n}\n\n// ── Event channels ───────────────────────────────────────────────────────────────────────────\n\nexport interface EventChannel<Events> {\n readonly id: string;\n /** Deliver an event to every subscriber (no-op after dispose). */\n emit(event: Events): void;\n /** Listen; returns the unsubscribe. */\n subscribe(listener: (event: Events) => void): () => void;\n /** Remove the channel and drop every subscriber. */\n dispose(): void;\n readonly disposed: boolean;\n}\n\n/**\n * A many-shot event stream between an opener and an overlay. Pass `channel.id` in open data; the\n * overlay resolves it with `getEventChannel(id)`.\n */\nexport function createEventChannel<Events>(options?: OwnerOption & { readonly id?: string }): EventChannel<Events> {\n const listeners = new Set<(event: Events) => void>();\n let disposed = false;\n let id = \"\";\n const channel: EventChannel<Events> = {\n get id() {\n return id;\n },\n get disposed() {\n return disposed;\n },\n emit(event) {\n if (disposed) return;\n for (const listener of [...listeners]) listener(event);\n },\n subscribe(listener) {\n if (disposed) return () => {};\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n if (disposed) return;\n callbackManager.remove(id);\n },\n };\n id = callbackManager.registerEntry(channel, {\n ...(options?.id === undefined ? {} : { id: options.id }),\n ...ownerOptions(options),\n onRemove: () => {\n disposed = true;\n listeners.clear();\n },\n });\n return channel;\n}\n\n/** The channel registered under `id`, or undefined once disposed. */\nexport function getEventChannel<Events>(id: string): EventChannel<Events> | undefined {\n return getHandle<EventChannel<Events>>(id);\n}\n\n// ── One-shot requests ────────────────────────────────────────────────────────────────────────\n\nexport interface PendingRequest<Req, Res> {\n readonly id: string;\n /** What the asker sent. */\n readonly request: Req;\n /** Answer the request (first answer wins; later calls are ignored). */\n resolve(response: Res): void;\n /** Fail the request. */\n reject(reason: unknown): void;\n readonly settled: boolean;\n}\n\nexport interface RequestHandle<Req, Res> extends PendingRequest<Req, Res> {\n /** Settles with the answer; rejects with `HandleDisposedError` when disposed unanswered. */\n readonly promise: Promise<Res>;\n /** Remove the request; an unanswered one rejects. */\n dispose(): void;\n}\n\n/**\n * A one-shot promise bridge: the asker awaits `promise`, the overlay answers through\n * `getRequest(id).resolve(…)`. Disposing it unanswered (or closing its owner) rejects.\n */\nexport function createRequest<Req, Res>(request: Req, options?: OwnerOption): RequestHandle<Req, Res> {\n let settle!: (response: Res) => void;\n let fail!: (reason: unknown) => void;\n const promise = new Promise<Res>((resolve, reject) => {\n settle = resolve;\n fail = reject;\n });\n // A disposed request nobody awaited must not surface as an unhandled rejection.\n promise.catch(() => {});\n let settled = false;\n let id = \"\";\n const handle: RequestHandle<Req, Res> = {\n get id() {\n return id;\n },\n request,\n promise,\n get settled() {\n return settled;\n },\n resolve(response) {\n if (settled) return;\n settled = true;\n settle(response);\n callbackManager.remove(id);\n },\n reject(reason) {\n if (settled) return;\n settled = true;\n fail(reason);\n callbackManager.remove(id);\n },\n dispose() {\n callbackManager.remove(id);\n },\n };\n id = callbackManager.registerEntry(handle, {\n ...ownerOptions(options),\n onRemove: () => {\n if (settled) return;\n settled = true;\n fail(new HandleDisposedError(id));\n },\n });\n return handle;\n}\n\n/** The pending request registered under `id`, or undefined once answered or disposed. */\nexport function getRequest<Req, Res>(id: string): PendingRequest<Req, Res> | undefined {\n return getHandle<RequestHandle<Req, Res>>(id);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBA,yBAA2B;;;ACrBpB,SAAS,cAAsB;AACpC,QAAM,IAAwB,OAAO,WAAW,WAAW,cAAc,SAAY,WAAW;AAChG,MAAI,KAAK,OAAO,EAAE,eAAe,WAAY,QAAO,EAAE,WAAW;AACjE,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,MAAI,KAAK,OAAO,EAAE,oBAAoB,YAAY;AAChD,MAAE,gBAAgB,KAAK;AAAA,EACzB,OAAO;AACL,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK,EAAG,OAAM,CAAC,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG;AAAA,EAC3E;AACA,QAAM,CAAC,KAAM,MAAM,CAAC,KAAK,KAAK,KAAQ;AACtC,QAAM,CAAC,KAAM,MAAM,CAAC,KAAK,KAAK,KAAQ;AACtC,QAAM,MAAM,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC7E,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC;AAC1G;;;ACbO,IAAM,eAAe;;;AF0E5B,IAAM,eAAe,UACnB,+BAAyB,cAAc,OAAO;AAAA,EAC5C,WAAW,oBAAI,IAAI;AAAA,EACnB,QAAQ,oBAAI,IAAI;AAAA,EAChB,QAAQ,oBAAI,IAAI;AAAA,EAChB,mBAAmB,oBAAI,IAAI;AAC7B,EAAE;AAGJ,SAAS,OAAO,MAAkB,MAAe,SAAiD;AAChG,MAAI,OAAO,SAAS,WAAY,QAAO;AACvC,SAAQ,KAA4C,MAAM,OAAO;AACnE;AAEA,SAAS,eAAe,UAAkC,MAAe,SAAiD;AACxH,SAAQ,SAAgD,MAAM,OAAO;AACvE;AAQO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,IAAY,IAAkB;AAC5B,WAAO,aAAa;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,oBAAoB,UAAkC;AACpD,UAAM,YAAY,KAAK,EAAE;AACzB,cAAU,IAAI,QAAQ;AACtB,WAAO,MAAM;AACX,gBAAU,OAAO,QAAQ;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,iBAAuB;AAC7B,eAAW,YAAY,CAAC,GAAG,KAAK,EAAE,iBAAiB,EAAG,UAAS;AAAA,EACjE;AAAA;AAAA,EAGA,SAAY,UAA+B;AACzC,WAAO,KAAK,cAAc,UAAwB,CAAC,CAAC;AAAA,EACtD;AAAA;AAAA,EAGA,oBACE,UACA,UAA8B,CAAC,GACvB;AACR,WAAO,KAAK,cAAc,UAAwB,OAAO;AAAA,EAC3D;AAAA;AAAA,EAGA,cAAc,MAAkB,SAA0C;AACxE,UAAM,IAAI,KAAK;AACf,UAAM,aAAa,QAAQ,MAAM,YAAY;AAC7C,QAAI,EAAE,UAAU,IAAI,UAAU,EAAG,MAAK,YAAY,YAAY,KAAK;AACnE,UAAM,QAAuB,EAAE,UAAU,KAAK;AAC9C,QAAI,QAAQ,YAAY,OAAW,OAAM,UAAU,QAAQ;AAC3D,QAAI,QAAQ,YAAY,OAAW,OAAM,UAAU,QAAQ;AAC3D,QAAI,QAAQ,UAAU,OAAW,OAAM,QAAQ,QAAQ;AACvD,QAAI,QAAQ,aAAa,OAAW,OAAM,WAAW,QAAQ;AAC7D,MAAE,UAAU,IAAI,YAAY,KAAK;AAEjC,QAAI,QAAQ,YAAY,QAAW;AACjC,YAAM,QAAQ,EAAE,OAAO,IAAI,QAAQ,OAAO,KAAK,oBAAI,IAAY;AAC/D,YAAM,IAAI,UAAU;AACpB,QAAE,OAAO,IAAI,QAAQ,SAAS,KAAK;AAAA,IACrC;AACA,QAAI,QAAQ,UAAU,QAAW;AAC/B,YAAM,QAAQ,EAAE,OAAO,IAAI,QAAQ,KAAK,KAAK,oBAAI,IAAY;AAC7D,YAAM,IAAI,UAAU;AACpB,QAAE,OAAO,IAAI,QAAQ,OAAO,KAAK;AAAA,IACnC;AACA,SAAK,eAAe;AACpB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAA0D,YAAoB,UAAmC;AAC/G,UAAM,QAAQ,KAAK,EAAE,UAAU,IAAI,UAAU;AAC7C,QAAI,CAAC,MAAO,QAAO;AACnB,KAAC,MAAM,cAAc,CAAC,GAAG,KAAK,QAAkC;AAChE,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAA4D,YAAoB,UAAmC;AACjH,UAAM,QAAQ,KAAK,EAAE,UAAU,IAAI,UAAU;AAC7C,QAAI,CAAC,SAAS,CAAC,MAAM,UAAW,QAAO;AACvC,UAAM,QAAQ,MAAM,UAAU,QAAQ,QAAkC;AACxE,QAAI,UAAU,GAAI,QAAO;AACzB,UAAM,UAAU,OAAO,OAAO,CAAC;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAW,YAAoB,MAAe;AAC5C,UAAM,QAAQ,KAAK,EAAE,UAAU,IAAI,UAAU;AAC7C,QAAI,CAAC,MAAO;AACZ,SAAK,OAAO,MAAM,UAAU,IAAI;AAChC,QAAI,MAAM,aAAa,MAAM,UAAU,SAAS,GAAG;AACjD,iBAAW,YAAY,CAAC,GAAG,MAAM,SAAS,EAAG,MAAK,eAAe,UAAU,IAAI;AAAA,IACjF;AACA,SAAK,YAAY,YAAY,IAAI;AAAA,EACnC;AAAA;AAAA,EAGA,mBACE,YACA,MACA,SACM;AACN,UAAM,QAAQ,KAAK,EAAE,UAAU,IAAI,UAAU;AAC7C,QAAI,CAAC,MAAO;AACZ,UAAM,gBAAiC;AAAA,MACrC,GAAG,MAAM;AAAA,MACT,GAAG,SAAS;AAAA,MACZ,GAAI,SAAS,YAAY,EAAE,UAAU,QAAQ,SAAS;AAAA,IACxD;AACA,SAAK,OAAO,MAAM,UAAU,MAAM,aAAa;AAC/C,QAAI,MAAM,aAAa,MAAM,UAAU,SAAS,GAAG;AACjD,iBAAW,YAAY,CAAC,GAAG,MAAM,SAAS,EAAG,MAAK,eAAe,UAAU,MAAM,aAAa;AAAA,IAChG;AACA,QAAI,SAAS,uBAAuB,MAAO,MAAK,YAAY,YAAY,IAAI;AAAA,EAC9E;AAAA;AAAA,EAGA,aAA6D,SAAiB,MAAS,SAAmC;AACxH,UAAM,QAAQ,KAAK,EAAE,OAAO,IAAI,OAAO;AACvC,QAAI,CAAC,MAAO;AACZ,eAAW,cAAc,CAAC,GAAG,KAAK,EAAG,MAAK,mBAAmB,YAAY,MAAM,OAAO;AACtF,QAAI,SAAS,uBAAuB,MAAO,MAAK,EAAE,OAAO,OAAO,OAAO;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,oBACJ,SACA,MACA,SACe;AACf,UAAM,QAAQ,KAAK,EAAE,OAAO,IAAI,OAAO;AACvC,UAAM,cAAc,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC;AAC1C,UAAM,aAAa,YAAY,CAAC;AAChC,QAAI,YAAY,WAAW,KAAK,eAAe,QAAW;AACxD,YAAM,IAAI,MAAM,oDAAoD,OAAO,YAAY,YAAY,MAAM,EAAE;AAAA,IAC7G;AACA,UAAM,QAAQ,KAAK,EAAE,UAAU,IAAI,UAAU;AAC7C,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,qBAAqB,UAAU,0BAA0B;AAErF,UAAM,gBAAiC,EAAE,GAAG,MAAM,SAAS,GAAG,SAAS,QAAQ;AAC/E,UAAM,OAAO,MAAM,UAAU,MAAM,aAAa;AAGhD,eAAW,YAAY,MAAM,aAAa,CAAC,GAAG;AAC5C,UAAI;AACF,cAAM,eAAe,UAAU,MAAM,aAAa;AAAA,MACpD,SAAS,OAAO;AAEd,gBAAQ,MAAM,8CAA8C,KAAK;AAAA,MACnE;AAAA,IACF;AAEA,QAAI,SAAS,uBAAuB,OAAO;AACzC,WAAK,YAAY,YAAY,IAAI;AACjC,WAAK,EAAE,OAAO,OAAO,OAAO;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA,EAGA,eACE,IACA,UACA,SACM;AACN,UAAM,eAA6B,EAAE,UAAU,QAAQ,SAAS,UAAU,UAAU;AACpF,QAAI,SAAS,UAAU,OAAW,cAAa,QAAQ,QAAQ;AAC/D,QAAI,SAAS,SAAS;AACpB,WAAK,aAAa,IAAI,MAAM,EAAE,UAAU,cAAc,oBAAoB,MAAM,CAAC;AAAA,IACnF,OAAO;AACL,WAAK,mBAAmB,IAAI,MAAM,EAAE,UAAU,cAAc,oBAAoB,MAAM,CAAC;AAAA,IACzF;AAAA,EACF;AAAA;AAAA,EAGA,cAAsB;AACpB,UAAM,UAAU,YAAY;AAC5B,SAAK,EAAE,OAAO,IAAI,SAAS,oBAAI,IAAI,CAAC;AACpC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAO,YAA0B;AAC/B,SAAK,YAAY,YAAY,IAAI;AAAA,EACnC;AAAA;AAAA,EAGA,WAAW,YAA0B;AACnC,SAAK,OAAO,UAAU;AAAA,EACxB;AAAA;AAAA,EAGA,IAAiB,YAAmC;AAClD,WAAO,KAAK,EAAE,UAAU,IAAI,UAAU,GAAG;AAAA,EAC3C;AAAA;AAAA,EAGA,IAAI,YAA6B;AAC/B,WAAO,KAAK,EAAE,UAAU,IAAI,UAAU;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAuC,QAAW,UAA8B,CAAC,GAAW;AAC1F,WAAO,KAAK,cAAc,QAAQ,QAAQ,UAAU,SAAY,CAAC,IAAI,EAAE,OAAO,QAAQ,MAAM,CAAC;AAAA,EAC/F;AAAA;AAAA,EAGA,YAAY,SAAuB;AACjC,UAAM,IAAI,KAAK;AACf,UAAM,QAAQ,EAAE,OAAO,IAAI,OAAO;AAClC,QAAI,CAAC,MAAO;AACZ,MAAE,OAAO,OAAO,OAAO;AACvB,eAAW,cAAc,CAAC,GAAG,KAAK,EAAG,MAAK,YAAY,YAAY,KAAK;AACvE,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA,EAGA,aAAa,OAAuB;AAClC,UAAM,IAAI,KAAK;AACf,UAAM,QAAQ,EAAE,OAAO,IAAI,KAAK;AAChC,QAAI,CAAC,MAAO,QAAO;AACnB,MAAE,OAAO,OAAO,KAAK;AACrB,UAAM,MAAM,CAAC,GAAG,KAAK;AACrB,eAAW,cAAc,IAAK,MAAK,YAAY,YAAY,KAAK;AAChE,QAAI,IAAI,SAAS,EAAG,MAAK,eAAe;AACxC,WAAO,IAAI;AAAA,EACb;AAAA;AAAA,EAGA,QAAQ,OAAkC;AACxC,WAAO,CAAC,GAAI,KAAK,EAAE,OAAO,IAAI,KAAK,KAAK,CAAC,CAAE;AAAA,EAC7C;AAAA;AAAA,EAGA,aAAa,SAAoC;AAC/C,WAAO,CAAC,GAAI,KAAK,EAAE,OAAO,IAAI,OAAO,KAAK,CAAC,CAAE;AAAA,EAC/C;AAAA;AAAA,EAGA,OAAe;AACb,WAAO,KAAK,EAAE,UAAU;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAY,YAAoB,QAAuB;AAC7D,UAAM,IAAI,KAAK;AACf,UAAM,QAAQ,EAAE,UAAU,IAAI,UAAU;AACxC,QAAI,CAAC,MAAO;AACZ,MAAE,UAAU,OAAO,UAAU;AAC7B,QAAI,MAAM,YAAY,QAAW;AAC/B,YAAM,QAAQ,EAAE,OAAO,IAAI,MAAM,OAAO;AACxC,aAAO,OAAO,UAAU;AACxB,UAAI,OAAO,SAAS,EAAG,GAAE,OAAO,OAAO,MAAM,OAAO;AAAA,IACtD;AACA,QAAI,MAAM,UAAU,QAAW;AAC7B,YAAM,QAAQ,EAAE,OAAO,IAAI,MAAM,KAAK;AACtC,aAAO,OAAO,UAAU;AACxB,UAAI,OAAO,SAAS,EAAG,GAAE,OAAO,OAAO,MAAM,KAAK;AAAA,IACpD;AACA,QAAI,OAAQ,MAAK,eAAe;AAChC,UAAM,WAAW;AAAA,EACnB;AACF;AAGO,IAAM,kBAAkB,IAAI,gBAAgB;AAG5C,SAAS,aAAa,OAAuB;AAClD,SAAO,gBAAgB,aAAa,KAAK;AAC3C;;;AGrWO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EACpC;AAAA,EACT,YAAY,UAAkB,UAAU,uBAAuB,QAAQ,oCAAoC;AACzG,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,WAAW;AAAA,EAClB;AACF;AAEA,SAAS,aAAa,SAAsD;AAC1E,SAAO,SAAS,UAAU,SAAY,CAAC,IAAI,EAAE,OAAO,QAAQ,MAAM;AACpE;AASO,SAAS,eAAiC,IAAY,QAAW,SAAmC;AACzG,kBAAgB,cAAc,QAAQ,EAAE,IAAI,GAAG,aAAa,OAAO,EAAE,CAAC;AACtE,SAAO,MAAM;AACX,QAAI,gBAAgB,IAAO,EAAE,MAAM,OAAQ,iBAAgB,OAAO,EAAE;AAAA,EACtE;AACF;AAGO,SAAS,UAA4B,IAA2B;AACrE,QAAM,QAAQ,gBAAgB,IAAa,EAAE;AAC7C,SAAO,OAAO,UAAU,YAAY,UAAU,OAAQ,QAAc;AACtE;AAGO,SAAS,aAAa,IAAkB;AAC7C,kBAAgB,OAAO,EAAE;AAC3B;AAmBO,SAAS,mBAA2B,SAAwE;AACjH,QAAM,YAAY,oBAAI,IAA6B;AACnD,MAAI,WAAW;AACf,MAAI,KAAK;AACT,QAAM,UAAgC;AAAA,IACpC,IAAI,KAAK;AACP,aAAO;AAAA,IACT;AAAA,IACA,IAAI,WAAW;AACb,aAAO;AAAA,IACT;AAAA,IACA,KAAK,OAAO;AACV,UAAI,SAAU;AACd,iBAAW,YAAY,CAAC,GAAG,SAAS,EAAG,UAAS,KAAK;AAAA,IACvD;AAAA,IACA,UAAU,UAAU;AAClB,UAAI,SAAU,QAAO,MAAM;AAAA,MAAC;AAC5B,gBAAU,IAAI,QAAQ;AACtB,aAAO,MAAM;AACX,kBAAU,OAAO,QAAQ;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,UAAU;AACR,UAAI,SAAU;AACd,sBAAgB,OAAO,EAAE;AAAA,IAC3B;AAAA,EACF;AACA,OAAK,gBAAgB,cAAc,SAAS;AAAA,IAC1C,GAAI,SAAS,OAAO,SAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;AAAA,IACtD,GAAG,aAAa,OAAO;AAAA,IACvB,UAAU,MAAM;AACd,iBAAW;AACX,gBAAU,MAAM;AAAA,IAClB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAGO,SAAS,gBAAwB,IAA8C;AACpF,SAAO,UAAgC,EAAE;AAC3C;AA0BO,SAAS,cAAwB,SAAc,SAAgD;AACpG,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,IAAI,QAAa,CAAC,SAAS,WAAW;AACpD,aAAS;AACT,WAAO;AAAA,EACT,CAAC;AAED,UAAQ,MAAM,MAAM;AAAA,EAAC,CAAC;AACtB,MAAI,UAAU;AACd,MAAI,KAAK;AACT,QAAM,SAAkC;AAAA,IACtC,IAAI,KAAK;AACP,aAAO;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAAA,IACA,QAAQ,UAAU;AAChB,UAAI,QAAS;AACb,gBAAU;AACV,aAAO,QAAQ;AACf,sBAAgB,OAAO,EAAE;AAAA,IAC3B;AAAA,IACA,OAAO,QAAQ;AACb,UAAI,QAAS;AACb,gBAAU;AACV,WAAK,MAAM;AACX,sBAAgB,OAAO,EAAE;AAAA,IAC3B;AAAA,IACA,UAAU;AACR,sBAAgB,OAAO,EAAE;AAAA,IAC3B;AAAA,EACF;AACA,OAAK,gBAAgB,cAAc,QAAQ;AAAA,IACzC,GAAG,aAAa,OAAO;AAAA,IACvB,UAAU,MAAM;AACd,UAAI,QAAS;AACb,gBAAU;AACV,WAAK,IAAI,oBAAoB,EAAE,CAAC;AAAA,IAClC;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAGO,SAAS,WAAqB,IAAkD;AACrF,SAAO,UAAmC,EAAE;AAC9C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/handles.ts","../src/open/ids.ts","../src/handles/slot.ts","../src/open/handleEntries.ts","../src/handles/callbackManager.ts","../src/handles/helpers.ts"],"sourcesContent":["// @ai-matrx/summon/handles — the handles registry: callbacks, event channels, one-shot requests\n// and imperative object handles, owner-scoped to open overlay instances. Framework-free: importing\n// this section never loads React or Redux.\nexport {\n CallbackManager,\n callbackManager,\n disposeOwner,\n HANDLES_SLOT,\n type Callback,\n type CallbackContext,\n type HandleSlot,\n type ProgressInfo,\n type RegisterOptions,\n type TriggerOptions,\n} from \"./handles/callbackManager\";\nexport {\n createEventChannel,\n createRequest,\n getEventChannel,\n getHandle,\n getRequest,\n HandleDisposedError,\n registerHandle,\n removeHandle,\n type EventChannel,\n type OwnerOption,\n type PendingRequest,\n type RequestHandle,\n} from \"./handles/helpers\";\nexport { newHandleId } from \"./handles/ids\";\n","/** A fresh handle id: `crypto.randomUUID` when present, else a v4 UUID from `getRandomValues`. */\nexport function newHandleId(): string {\n const c: Crypto | undefined = typeof globalThis.crypto === \"undefined\" ? undefined : globalThis.crypto;\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n const bytes = new Uint8Array(16);\n if (c && typeof c.getRandomValues === \"function\") {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n }\n bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","/** The global slot that holds the handles registry (one registry per realm, whatever the bundling). */\nexport const HANDLES_SLOT = \"ai-matrx.summon.handles\";\n","// The handles registry's STATE and its insert/remove path, without the registry's verbs (trigger,\n// subscribe, requests, channels). A boot-time opener that only needs an id NOW — register a\n// callback, a payload group, an object handle; drop it on close — writes through here and never\n// puts `@ai-matrx/summon/handles` in first load; whoever later triggers or reads loads the full\n// registry, which works on the same global slot through these same functions (one removal path).\nimport { globalSlot } from \"@ai-matrx/kit/global-slot\";\nimport { HANDLES_SLOT } from \"../handles/slot\";\nimport { newHandleId } from \"./ids\";\n\n/** A registry slot: a callback (invoked by triggers) or an object handle (only ever read). */\nexport type HandleEntrySlot = ((...args: never[]) => unknown) | object;\n\n/** @internal A registry entry (the handles section adds `listeners`). */\nexport interface HandleEntry {\n callback: HandleEntrySlot;\n context?: Record<string, unknown>;\n groupId?: string;\n owner?: string;\n listeners?: Array<(...args: never[]) => unknown>;\n /** Runs once when the entry leaves the registry for any reason (requests reject here). */\n onRemove?: () => void;\n}\n\n/** @internal The registry state in its global slot (one per realm, whatever the bundling). */\nexport interface HandlesState {\n readonly callbacks: Map<string, HandleEntry>;\n readonly groups: Map<string, Set<string>>;\n readonly owners: Map<string, Set<string>>;\n readonly registryListeners: Set<() => void>;\n}\n\nexport interface HandleEntryOptions {\n /** A fixed id (default: a fresh `newHandleId()`); an existing entry with it is replaced. */\n id?: string;\n context?: Record<string, unknown>;\n /** Join (or start) this group; `removeHandleGroup` drops every member. */\n groupId?: string;\n /** The InstanceKey (`overlayId:instanceId`) that owns this entry; disposed when it closes. */\n owner?: string;\n onRemove?: () => void;\n}\n\n/** @internal */\nexport const handlesState = (): HandlesState =>\n globalSlot<HandlesState>(HANDLES_SLOT, () => ({\n callbacks: new Map(),\n groups: new Map(),\n owners: new Map(),\n registryListeners: new Set(),\n }));\n\n/** @internal */\nexport function notifyHandlesRegistry(): void {\n for (const listener of [...handlesState().registryListeners]) listener();\n}\n\n/**\n * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink,\n * then runs the entry's removal hook.\n */\nexport function deleteHandleEntry(id: string, notify = true): void {\n const s = handlesState();\n const entry = s.callbacks.get(id);\n if (!entry) return;\n s.callbacks.delete(id);\n if (entry.groupId !== undefined) {\n const group = s.groups.get(entry.groupId);\n group?.delete(id);\n if (group?.size === 0) s.groups.delete(entry.groupId);\n }\n if (entry.owner !== undefined) {\n const owned = s.owners.get(entry.owner);\n owned?.delete(id);\n if (owned?.size === 0) s.owners.delete(entry.owner);\n }\n if (notify) notifyHandlesRegistry();\n entry.onRemove?.();\n}\n\n/** Register a callback or object handle NOW (no registry load); returns its id. */\nexport function registerHandleEntry(slot: HandleEntrySlot, options: HandleEntryOptions = {}): string {\n const s = handlesState();\n const id = options.id ?? newHandleId();\n if (s.callbacks.has(id)) deleteHandleEntry(id, false);\n const entry: HandleEntry = { callback: slot };\n if (options.context !== undefined) entry.context = options.context;\n if (options.groupId !== undefined) entry.groupId = options.groupId;\n if (options.owner !== undefined) entry.owner = options.owner;\n if (options.onRemove !== undefined) entry.onRemove = options.onRemove;\n s.callbacks.set(id, entry);\n if (options.groupId !== undefined) {\n const group = s.groups.get(options.groupId) ?? new Set<string>();\n group.add(id);\n s.groups.set(options.groupId, group);\n }\n if (options.owner !== undefined) {\n const owned = s.owners.get(options.owner) ?? new Set<string>();\n owned.add(id);\n s.owners.set(options.owner, owned);\n }\n notifyHandlesRegistry();\n return id;\n}\n\n/** `callbackManager.unregister(id)` without loading the registry. */\nexport function unregisterHandleEntry(id: string): void {\n deleteHandleEntry(id, true);\n}\n\n/** `callbackManager.removeGroup(groupId)` without loading the registry. */\nexport function removeHandleGroup(groupId: string): void {\n const s = handlesState();\n const group = s.groups.get(groupId);\n if (!group) return;\n s.groups.delete(groupId);\n for (const id of [...group]) deleteHandleEntry(id, false);\n notifyHandlesRegistry();\n}\n","/**\n * The handles registry — everything that cannot travel through Redux (callbacks, event channels,\n * one-shot requests, imperative object handles) lives here under a string id; only the id goes\n * into overlay open data.\n *\n * Successor of `@ai-matrx/chat` `utils/callbackManager.ts` with the SAME public verbs and types, so\n * moving a caller is an import-path swap. Changes (CHANGELOG 0.1.0):\n * 1. State lives in the global slot \"ai-matrx.summon.handles\", never in module scope: a\n * duplicated module graph (ESM + CJS, unsplit bundles, two packages) sees one registry.\n * 2. Removal bug fixed: `trigger` / `triggerWithContext` / `remove` deleted the entry BEFORE\n * `removeFromGroups` read it, so group membership never shrank and emptied groups were never\n * dropped. Removal now reads the entry first, in one place (`deleteEntry`).\n * 3. Owner scoping: `registerWithContext(cb, { owner })` (an InstanceKey `overlayId:instanceId`)\n * ties the entry to an open overlay instance; `disposeOwner(owner)` removes everything it\n * owns. The overlays close middleware calls it, so no opener disposes by hand.\n * 4. The object slot is handle-agnostic: `registerWidgetHandle<H extends object>(handle)` stores\n * any object (no chat `WidgetHandle` import).\n * 5. Ids come from `crypto.randomUUID` (no `uuid` dependency), with a fallback for insecure\n * contexts where `randomUUID` is absent.\n *\n * Framework-free: no React, no Redux.\n */\nimport { newHandleId } from \"./ids\";\nimport { HANDLES_SLOT } from \"./slot\";\nimport {\n deleteHandleEntry,\n handlesState as sharedHandlesState,\n notifyHandlesRegistry,\n registerHandleEntry,\n removeHandleGroup,\n type HandlesState as SharedHandlesState,\n} from \"../open/handleEntries\";\n\nexport type CallbackContext = Record<string, unknown>;\n\nexport interface ProgressInfo {\n progress?: number;\n status?: \"pending\" | \"running\" | \"completed\" | \"error\";\n error?: Error;\n}\n\nexport type Callback<T = unknown, C extends CallbackContext = CallbackContext> = (\n data: T,\n context?: C,\n) => void | Promise<void>;\n\n/** What an entry's slot holds: a callback, or an object handle retrieved with `get`. */\nexport type HandleSlot = Callback<never, never> | object;\n\nexport interface RegisterOptions<C extends CallbackContext = CallbackContext> {\n context?: C;\n groupId?: string;\n /** The InstanceKey (`overlayId:instanceId`) that owns this entry; disposed when it closes. */\n owner?: string;\n}\n\nexport interface TriggerOptions<C extends CallbackContext = CallbackContext> {\n context?: C;\n progress?: ProgressInfo;\n removeAfterTrigger?: boolean;\n}\n\n/** A registry entry. `callback` is the heterogeneous slot (function or object handle). */\ninterface CallbackEntry {\n callback: HandleSlot;\n context?: CallbackContext;\n groupId?: string;\n owner?: string;\n listeners?: Array<Callback<never, never>>;\n /** Runs once when the entry leaves the registry for any reason (requests reject here). */\n onRemove?: () => void;\n}\n\ninterface HandlesState {\n readonly callbacks: Map<string, CallbackEntry>;\n readonly groups: Map<string, Set<string>>;\n readonly owners: Map<string, Set<string>>;\n readonly registryListeners: Set<() => void>;\n}\n\nexport { HANDLES_SLOT };\n\n// State, insertion and removal are the light path on `./open` (`open/handleEntries.ts`), so a\n// boot-time registrant and this registry share one slot and one removal path.\nconst handlesState = (): HandlesState => sharedHandlesState() as SharedHandlesState as HandlesState;\n\n/** Invoke a slot as a callback (a function slot only; object handles are never invoked). */\nfunction invoke(slot: HandleSlot, data: unknown, context?: CallbackContext): void | Promise<void> {\n if (typeof slot !== \"function\") return undefined;\n return (slot as Callback<unknown, CallbackContext>)(data, context);\n}\n\nfunction invokeListener(listener: Callback<never, never>, data: unknown, context?: CallbackContext): void | Promise<void> {\n return (listener as Callback<unknown, CallbackContext>)(data, context);\n}\n\n/** Internal registration options beyond the public ones (used by the typed helpers). */\nexport interface InternalRegisterOptions extends RegisterOptions {\n id?: string;\n onRemove?: () => void;\n}\n\nexport class CallbackManager {\n private get s(): HandlesState {\n return handlesState();\n }\n\n /**\n * Observe the registry itself: `listener` runs after any entry is added or removed. This lets a\n * view that resolves ids with `get` re-render when an id goes away (`useSyncExternalStore`).\n */\n subscribeToRegistry(listener: () => void): () => void {\n const listeners = this.s.registryListeners;\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n }\n\n private notifyRegistry(): void {\n notifyHandlesRegistry();\n }\n\n /** Register a callback; returns its id. */\n register<T>(callback: Callback<T>): string {\n return this.registerEntry(callback as HandleSlot, {});\n }\n\n /** Register with optional context, group and owner. */\n registerWithContext<T, C extends CallbackContext = CallbackContext>(\n callback: Callback<T, C>,\n options: RegisterOptions<C> = {},\n ): string {\n return this.registerEntry(callback as HandleSlot, options);\n }\n\n /** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */\n registerEntry(slot: HandleSlot, options: InternalRegisterOptions): string {\n return registerHandleEntry(slot, options);\n }\n\n /** Subscribe to results for a callback id. */\n subscribe<T, C extends CallbackContext = CallbackContext>(callbackId: string, listener: Callback<T, C>): boolean {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return false;\n (entry.listeners ??= []).push(listener as Callback<never, never>);\n return true;\n }\n\n /** Unsubscribe a listener from a callback id. */\n unsubscribe<T, C extends CallbackContext = CallbackContext>(callbackId: string, listener: Callback<T, C>): boolean {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry || !entry.listeners) return false;\n const index = entry.listeners.indexOf(listener as Callback<never, never>);\n if (index === -1) return false;\n entry.listeners.splice(index, 1);\n return true;\n }\n\n /** Run a callback once, notify its listeners, then remove it. */\n trigger<T>(callbackId: string, data: T): void {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return;\n void invoke(entry.callback, data);\n if (entry.listeners && entry.listeners.length > 0) {\n for (const listener of [...entry.listeners]) void invokeListener(listener, data);\n }\n this.deleteEntry(callbackId, true);\n }\n\n /** Trigger with merged context and progress; removes unless `removeAfterTrigger: false`. */\n triggerWithContext<T, C extends CallbackContext = CallbackContext>(\n callbackId: string,\n data: T,\n options?: TriggerOptions<C>,\n ): void {\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) return;\n const mergedContext: CallbackContext = {\n ...entry.context,\n ...options?.context,\n ...(options?.progress && { progress: options.progress }),\n };\n void invoke(entry.callback, data, mergedContext);\n if (entry.listeners && entry.listeners.length > 0) {\n for (const listener of [...entry.listeners]) void invokeListener(listener, data, mergedContext);\n }\n if (options?.removeAfterTrigger !== false) this.deleteEntry(callbackId, true);\n }\n\n /** Trigger every callback in a group. */\n triggerGroup<T, C extends CallbackContext = CallbackContext>(groupId: string, data: T, options?: TriggerOptions<C>): void {\n const group = this.s.groups.get(groupId);\n if (!group) return;\n for (const callbackId of [...group]) this.triggerWithContext(callbackId, data, options);\n if (options?.removeAfterTrigger !== false) this.s.groups.delete(groupId);\n }\n\n /**\n * Run the one durable command registered for a group. A command is retained on rejection so its\n * caller can retry, and is removed only after its promise settles successfully.\n */\n async triggerGroupCommand<T, C extends CallbackContext = CallbackContext>(\n groupId: string,\n data: T,\n options?: { context?: C; removeAfterSuccess?: boolean },\n ): Promise<void> {\n const group = this.s.groups.get(groupId);\n const callbackIds = group ? [...group] : [];\n const callbackId = callbackIds[0];\n if (callbackIds.length !== 1 || callbackId === undefined) {\n throw new Error(`Expected exactly one command callback for group \"${groupId}\", found ${callbackIds.length}`);\n }\n const entry = this.s.callbacks.get(callbackId);\n if (!entry) throw new Error(`Command callback \"${callbackId}\" is no longer available`);\n\n const mergedContext: CallbackContext = { ...entry.context, ...options?.context };\n await invoke(entry.callback, data, mergedContext);\n\n // Observers never control command settlement: the command already acknowledged.\n for (const listener of entry.listeners ?? []) {\n try {\n await invokeListener(listener, data, mergedContext);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"[overlays/handles] command observer failed\", error);\n }\n }\n\n if (options?.removeAfterSuccess !== false) {\n this.deleteEntry(callbackId, true);\n this.s.groups.delete(groupId);\n }\n }\n\n /** Report progress to a callback (or every callback of a group) without removing it. */\n updateProgress(\n id: string,\n progress: number,\n options?: { status?: ProgressInfo[\"status\"]; error?: Error; groupId?: boolean },\n ): void {\n const progressInfo: ProgressInfo = { progress, status: options?.status || \"running\" };\n if (options?.error !== undefined) progressInfo.error = options.error;\n if (options?.groupId) {\n this.triggerGroup(id, null, { progress: progressInfo, removeAfterTrigger: false });\n } else {\n this.triggerWithContext(id, null, { progress: progressInfo, removeAfterTrigger: false });\n }\n }\n\n /** Create a new, empty group and return its id. */\n createGroup(): string {\n const groupId = newHandleId();\n this.s.groups.set(groupId, new Set());\n return groupId;\n }\n\n /** Remove one entry (and its group/owner membership). */\n remove(callbackId: string): void {\n this.deleteEntry(callbackId, true);\n }\n\n /** Alias of `remove` — the preferred name when unregistering a handle on unmount. */\n unregister(callbackId: string): void {\n this.remove(callbackId);\n }\n\n /** The stored callback or object handle for an id, without invoking it. */\n get<T = unknown>(callbackId: string): T | undefined {\n return this.s.callbacks.get(callbackId)?.callback as T | undefined;\n }\n\n /** True when the id is registered. */\n has(callbackId: string): boolean {\n return this.s.callbacks.has(callbackId);\n }\n\n /**\n * Register an object handle (a widget handle, an editor API, …). The object occupies the slot;\n * it is never invoked by `trigger`, only retrieved with `get`.\n */\n registerWidgetHandle<H extends object>(handle: H, options: { owner?: string } = {}): string {\n return this.registerEntry(handle, options.owner === undefined ? {} : { owner: options.owner });\n }\n\n /** Remove a group and all its callbacks. */\n removeGroup(groupId: string): void {\n removeHandleGroup(groupId);\n }\n\n /** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */\n disposeOwner(owner: string): number {\n const s = this.s;\n const owned = s.owners.get(owner);\n if (!owned) return 0;\n s.owners.delete(owner);\n const ids = [...owned];\n for (const callbackId of ids) this.deleteEntry(callbackId, false);\n if (ids.length > 0) this.notifyRegistry();\n return ids.length;\n }\n\n /** Ids currently owned by `owner`. */\n ownedBy(owner: string): readonly string[] {\n return [...(this.s.owners.get(owner) ?? [])];\n }\n\n /** The member ids of a group (empty when unknown). */\n groupMembers(groupId: string): readonly string[] {\n return [...(this.s.groups.get(groupId) ?? [])];\n }\n\n /** Total registered entries (diagnostics, leak tests). */\n size(): number {\n return this.s.callbacks.size;\n }\n\n /**\n * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink\n * (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.\n */\n private deleteEntry(callbackId: string, notify: boolean): void {\n deleteHandleEntry(callbackId, notify);\n }\n}\n\n/** The process-wide handles registry (state in the global slot; every instance shares it). */\nexport const callbackManager = new CallbackManager();\n\n/** Remove everything owned by an open instance (`overlayId:instanceId`). */\nexport function disposeOwner(owner: string): number {\n return callbackManager.disposeOwner(owner);\n}\n","/**\n * Typed helpers over the handles registry: imperative object handles, event channels and one-shot\n * requests. Each is stored in the same registry (so its id can ride overlay open data and `get`\n * still resolves it) and accepts an `owner` InstanceKey so closing the owning overlay disposes it.\n */\nimport { callbackManager } from \"./callbackManager\";\n\nexport interface OwnerOption {\n /** The InstanceKey (`overlayId:instanceId`) that owns this handle; disposed when it closes. */\n owner?: string;\n}\n\n/** Thrown into a request's promise when it is disposed before being answered. */\nexport class HandleDisposedError extends Error {\n readonly handleId: string;\n constructor(handleId: string, message = `[overlays/handles] \"${handleId}\" was disposed before it settled`) {\n super(message);\n this.name = \"HandleDisposedError\";\n this.handleId = handleId;\n }\n}\n\nfunction ownerOptions(options: OwnerOption | undefined): { owner?: string } {\n return options?.owner === undefined ? {} : { owner: options.owner };\n}\n\n// ── Imperative object handles ────────────────────────────────────────────────────────────────\n\n/**\n * Register an imperative object handle under a caller-chosen id (an editor API, a widget handle,\n * a ref's methods). Re-registering an id replaces the previous handle. Returns a disposer that\n * removes the handle only if it is still the one registered (safe for React effect cleanup).\n */\nexport function registerHandle<T extends object>(id: string, handle: T, options?: OwnerOption): () => void {\n callbackManager.registerEntry(handle, { id, ...ownerOptions(options) });\n return () => {\n if (callbackManager.get<T>(id) === handle) callbackManager.remove(id);\n };\n}\n\n/** The object handle registered under `id`, or undefined. */\nexport function getHandle<T extends object>(id: string): T | undefined {\n const value = callbackManager.get<unknown>(id);\n return typeof value === \"object\" && value !== null ? (value as T) : undefined;\n}\n\n/** Remove the handle registered under `id`. */\nexport function removeHandle(id: string): void {\n callbackManager.remove(id);\n}\n\n// ── Event channels ───────────────────────────────────────────────────────────────────────────\n\nexport interface EventChannel<Events> {\n readonly id: string;\n /** Deliver an event to every subscriber (no-op after dispose). */\n emit(event: Events): void;\n /** Listen; returns the unsubscribe. */\n subscribe(listener: (event: Events) => void): () => void;\n /** Remove the channel and drop every subscriber. */\n dispose(): void;\n readonly disposed: boolean;\n}\n\n/**\n * A many-shot event stream between an opener and an overlay. Pass `channel.id` in open data; the\n * overlay resolves it with `getEventChannel(id)`.\n */\nexport function createEventChannel<Events>(options?: OwnerOption & { readonly id?: string }): EventChannel<Events> {\n const listeners = new Set<(event: Events) => void>();\n let disposed = false;\n let id = \"\";\n const channel: EventChannel<Events> = {\n get id() {\n return id;\n },\n get disposed() {\n return disposed;\n },\n emit(event) {\n if (disposed) return;\n for (const listener of [...listeners]) listener(event);\n },\n subscribe(listener) {\n if (disposed) return () => {};\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n if (disposed) return;\n callbackManager.remove(id);\n },\n };\n id = callbackManager.registerEntry(channel, {\n ...(options?.id === undefined ? {} : { id: options.id }),\n ...ownerOptions(options),\n onRemove: () => {\n disposed = true;\n listeners.clear();\n },\n });\n return channel;\n}\n\n/** The channel registered under `id`, or undefined once disposed. */\nexport function getEventChannel<Events>(id: string): EventChannel<Events> | undefined {\n return getHandle<EventChannel<Events>>(id);\n}\n\n// ── One-shot requests ────────────────────────────────────────────────────────────────────────\n\nexport interface PendingRequest<Req, Res> {\n readonly id: string;\n /** What the asker sent. */\n readonly request: Req;\n /** Answer the request (first answer wins; later calls are ignored). */\n resolve(response: Res): void;\n /** Fail the request. */\n reject(reason: unknown): void;\n readonly settled: boolean;\n}\n\nexport interface RequestHandle<Req, Res> extends PendingRequest<Req, Res> {\n /** Settles with the answer; rejects with `HandleDisposedError` when disposed unanswered. */\n readonly promise: Promise<Res>;\n /** Remove the request; an unanswered one rejects. */\n dispose(): void;\n}\n\n/**\n * A one-shot promise bridge: the asker awaits `promise`, the overlay answers through\n * `getRequest(id).resolve(…)`. Disposing it unanswered (or closing its owner) rejects.\n */\nexport function createRequest<Req, Res>(request: Req, options?: OwnerOption): RequestHandle<Req, Res> {\n let settle!: (response: Res) => void;\n let fail!: (reason: unknown) => void;\n const promise = new Promise<Res>((resolve, reject) => {\n settle = resolve;\n fail = reject;\n });\n // A disposed request nobody awaited must not surface as an unhandled rejection.\n promise.catch(() => {});\n let settled = false;\n let id = \"\";\n const handle: RequestHandle<Req, Res> = {\n get id() {\n return id;\n },\n request,\n promise,\n get settled() {\n return settled;\n },\n resolve(response) {\n if (settled) return;\n settled = true;\n settle(response);\n callbackManager.remove(id);\n },\n reject(reason) {\n if (settled) return;\n settled = true;\n fail(reason);\n callbackManager.remove(id);\n },\n dispose() {\n callbackManager.remove(id);\n },\n };\n id = callbackManager.registerEntry(handle, {\n ...ownerOptions(options),\n onRemove: () => {\n if (settled) return;\n settled = true;\n fail(new HandleDisposedError(id));\n },\n });\n return handle;\n}\n\n/** The pending request registered under `id`, or undefined once answered or disposed. */\nexport function getRequest<Req, Res>(id: string): PendingRequest<Req, Res> | undefined {\n return getHandle<RequestHandle<Req, Res>>(id);\n}\n"],"mappings":"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","names":["handlesState"]}
|
package/dist/handles.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { E as EventChannel, H as HandleDisposedError, O as OwnerOption, P as PendingRequest, R as RequestHandle, c as createEventChannel, a as createRequest, g as getEventChannel, b as getHandle, d as getRequest, r as registerHandle, e as removeHandle } from './
|
|
1
|
+
export { E as EventChannel, H as HandleDisposedError, O as OwnerOption, P as PendingRequest, R as RequestHandle, c as createEventChannel, a as createRequest, g as getEventChannel, b as getHandle, d as getRequest, n as newHandleId, r as registerHandle, e as removeHandle } from './ids-83h05Ed-.cjs';
|
|
2
2
|
|
|
3
3
|
/** The global slot that holds the handles registry (one registry per realm, whatever the bundling). */
|
|
4
4
|
declare const HANDLES_SLOT = "ai-matrx.summon.handles";
|
|
@@ -105,7 +105,4 @@ declare const callbackManager: CallbackManager;
|
|
|
105
105
|
/** Remove everything owned by an open instance (`overlayId:instanceId`). */
|
|
106
106
|
declare function disposeOwner(owner: string): number;
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
declare function newHandleId(): string;
|
|
110
|
-
|
|
111
|
-
export { type Callback, type CallbackContext, CallbackManager, HANDLES_SLOT, type HandleSlot, type ProgressInfo, type RegisterOptions, type TriggerOptions, callbackManager, disposeOwner, newHandleId };
|
|
108
|
+
export { type Callback, type CallbackContext, CallbackManager, HANDLES_SLOT, type HandleSlot, type ProgressInfo, type RegisterOptions, type TriggerOptions, callbackManager, disposeOwner };
|
package/dist/handles.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { E as EventChannel, H as HandleDisposedError, O as OwnerOption, P as PendingRequest, R as RequestHandle, c as createEventChannel, a as createRequest, g as getEventChannel, b as getHandle, d as getRequest, r as registerHandle, e as removeHandle } from './
|
|
1
|
+
export { E as EventChannel, H as HandleDisposedError, O as OwnerOption, P as PendingRequest, R as RequestHandle, c as createEventChannel, a as createRequest, g as getEventChannel, b as getHandle, d as getRequest, n as newHandleId, r as registerHandle, e as removeHandle } from './ids-83h05Ed-.js';
|
|
2
2
|
|
|
3
3
|
/** The global slot that holds the handles registry (one registry per realm, whatever the bundling). */
|
|
4
4
|
declare const HANDLES_SLOT = "ai-matrx.summon.handles";
|
|
@@ -105,7 +105,4 @@ declare const callbackManager: CallbackManager;
|
|
|
105
105
|
/** Remove everything owned by an open instance (`overlayId:instanceId`). */
|
|
106
106
|
declare function disposeOwner(owner: string): number;
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
declare function newHandleId(): string;
|
|
110
|
-
|
|
111
|
-
export { type Callback, type CallbackContext, CallbackManager, HANDLES_SLOT, type HandleSlot, type ProgressInfo, type RegisterOptions, type TriggerOptions, callbackManager, disposeOwner, newHandleId };
|
|
108
|
+
export { type Callback, type CallbackContext, CallbackManager, HANDLES_SLOT, type HandleSlot, type ProgressInfo, type RegisterOptions, type TriggerOptions, callbackManager, disposeOwner };
|
package/dist/handles.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import { globalSlot } from "@ai-matrx/kit/global-slot";
|
|
3
|
-
|
|
4
|
-
// src/handles/ids.ts
|
|
1
|
+
// src/open/ids.ts
|
|
5
2
|
function newHandleId() {
|
|
6
3
|
const c = typeof globalThis.crypto === "undefined" ? void 0 : globalThis.crypto;
|
|
7
4
|
if (c && typeof c.randomUUID === "function") return c.randomUUID();
|
|
@@ -20,13 +17,69 @@ function newHandleId() {
|
|
|
20
17
|
// src/handles/slot.ts
|
|
21
18
|
var HANDLES_SLOT = "ai-matrx.summon.handles";
|
|
22
19
|
|
|
23
|
-
// src/
|
|
20
|
+
// src/open/handleEntries.ts
|
|
21
|
+
import { globalSlot } from "@ai-matrx/kit/global-slot";
|
|
24
22
|
var handlesState = () => globalSlot(HANDLES_SLOT, () => ({
|
|
25
23
|
callbacks: /* @__PURE__ */ new Map(),
|
|
26
24
|
groups: /* @__PURE__ */ new Map(),
|
|
27
25
|
owners: /* @__PURE__ */ new Map(),
|
|
28
26
|
registryListeners: /* @__PURE__ */ new Set()
|
|
29
27
|
}));
|
|
28
|
+
function notifyHandlesRegistry() {
|
|
29
|
+
for (const listener of [...handlesState().registryListeners]) listener();
|
|
30
|
+
}
|
|
31
|
+
function deleteHandleEntry(id, notify = true) {
|
|
32
|
+
const s = handlesState();
|
|
33
|
+
const entry = s.callbacks.get(id);
|
|
34
|
+
if (!entry) return;
|
|
35
|
+
s.callbacks.delete(id);
|
|
36
|
+
if (entry.groupId !== void 0) {
|
|
37
|
+
const group = s.groups.get(entry.groupId);
|
|
38
|
+
group?.delete(id);
|
|
39
|
+
if (group?.size === 0) s.groups.delete(entry.groupId);
|
|
40
|
+
}
|
|
41
|
+
if (entry.owner !== void 0) {
|
|
42
|
+
const owned = s.owners.get(entry.owner);
|
|
43
|
+
owned?.delete(id);
|
|
44
|
+
if (owned?.size === 0) s.owners.delete(entry.owner);
|
|
45
|
+
}
|
|
46
|
+
if (notify) notifyHandlesRegistry();
|
|
47
|
+
entry.onRemove?.();
|
|
48
|
+
}
|
|
49
|
+
function registerHandleEntry(slot, options = {}) {
|
|
50
|
+
const s = handlesState();
|
|
51
|
+
const id = options.id ?? newHandleId();
|
|
52
|
+
if (s.callbacks.has(id)) deleteHandleEntry(id, false);
|
|
53
|
+
const entry = { callback: slot };
|
|
54
|
+
if (options.context !== void 0) entry.context = options.context;
|
|
55
|
+
if (options.groupId !== void 0) entry.groupId = options.groupId;
|
|
56
|
+
if (options.owner !== void 0) entry.owner = options.owner;
|
|
57
|
+
if (options.onRemove !== void 0) entry.onRemove = options.onRemove;
|
|
58
|
+
s.callbacks.set(id, entry);
|
|
59
|
+
if (options.groupId !== void 0) {
|
|
60
|
+
const group = s.groups.get(options.groupId) ?? /* @__PURE__ */ new Set();
|
|
61
|
+
group.add(id);
|
|
62
|
+
s.groups.set(options.groupId, group);
|
|
63
|
+
}
|
|
64
|
+
if (options.owner !== void 0) {
|
|
65
|
+
const owned = s.owners.get(options.owner) ?? /* @__PURE__ */ new Set();
|
|
66
|
+
owned.add(id);
|
|
67
|
+
s.owners.set(options.owner, owned);
|
|
68
|
+
}
|
|
69
|
+
notifyHandlesRegistry();
|
|
70
|
+
return id;
|
|
71
|
+
}
|
|
72
|
+
function removeHandleGroup(groupId) {
|
|
73
|
+
const s = handlesState();
|
|
74
|
+
const group = s.groups.get(groupId);
|
|
75
|
+
if (!group) return;
|
|
76
|
+
s.groups.delete(groupId);
|
|
77
|
+
for (const id of [...group]) deleteHandleEntry(id, false);
|
|
78
|
+
notifyHandlesRegistry();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/handles/callbackManager.ts
|
|
82
|
+
var handlesState2 = () => handlesState();
|
|
30
83
|
function invoke(slot, data, context) {
|
|
31
84
|
if (typeof slot !== "function") return void 0;
|
|
32
85
|
return slot(data, context);
|
|
@@ -36,7 +89,7 @@ function invokeListener(listener, data, context) {
|
|
|
36
89
|
}
|
|
37
90
|
var CallbackManager = class {
|
|
38
91
|
get s() {
|
|
39
|
-
return
|
|
92
|
+
return handlesState2();
|
|
40
93
|
}
|
|
41
94
|
/**
|
|
42
95
|
* Observe the registry itself: `listener` runs after any entry is added or removed. This lets a
|
|
@@ -50,7 +103,7 @@ var CallbackManager = class {
|
|
|
50
103
|
};
|
|
51
104
|
}
|
|
52
105
|
notifyRegistry() {
|
|
53
|
-
|
|
106
|
+
notifyHandlesRegistry();
|
|
54
107
|
}
|
|
55
108
|
/** Register a callback; returns its id. */
|
|
56
109
|
register(callback) {
|
|
@@ -62,27 +115,7 @@ var CallbackManager = class {
|
|
|
62
115
|
}
|
|
63
116
|
/** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */
|
|
64
117
|
registerEntry(slot, options) {
|
|
65
|
-
|
|
66
|
-
const callbackId = options.id ?? newHandleId();
|
|
67
|
-
if (s.callbacks.has(callbackId)) this.deleteEntry(callbackId, false);
|
|
68
|
-
const entry = { callback: slot };
|
|
69
|
-
if (options.context !== void 0) entry.context = options.context;
|
|
70
|
-
if (options.groupId !== void 0) entry.groupId = options.groupId;
|
|
71
|
-
if (options.owner !== void 0) entry.owner = options.owner;
|
|
72
|
-
if (options.onRemove !== void 0) entry.onRemove = options.onRemove;
|
|
73
|
-
s.callbacks.set(callbackId, entry);
|
|
74
|
-
if (options.groupId !== void 0) {
|
|
75
|
-
const group = s.groups.get(options.groupId) ?? /* @__PURE__ */ new Set();
|
|
76
|
-
group.add(callbackId);
|
|
77
|
-
s.groups.set(options.groupId, group);
|
|
78
|
-
}
|
|
79
|
-
if (options.owner !== void 0) {
|
|
80
|
-
const owned = s.owners.get(options.owner) ?? /* @__PURE__ */ new Set();
|
|
81
|
-
owned.add(callbackId);
|
|
82
|
-
s.owners.set(options.owner, owned);
|
|
83
|
-
}
|
|
84
|
-
this.notifyRegistry();
|
|
85
|
-
return callbackId;
|
|
118
|
+
return registerHandleEntry(slot, options);
|
|
86
119
|
}
|
|
87
120
|
/** Subscribe to results for a callback id. */
|
|
88
121
|
subscribe(callbackId, listener) {
|
|
@@ -200,12 +233,7 @@ var CallbackManager = class {
|
|
|
200
233
|
}
|
|
201
234
|
/** Remove a group and all its callbacks. */
|
|
202
235
|
removeGroup(groupId) {
|
|
203
|
-
|
|
204
|
-
const group = s.groups.get(groupId);
|
|
205
|
-
if (!group) return;
|
|
206
|
-
s.groups.delete(groupId);
|
|
207
|
-
for (const callbackId of [...group]) this.deleteEntry(callbackId, false);
|
|
208
|
-
this.notifyRegistry();
|
|
236
|
+
removeHandleGroup(groupId);
|
|
209
237
|
}
|
|
210
238
|
/** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */
|
|
211
239
|
disposeOwner(owner) {
|
|
@@ -235,22 +263,7 @@ var CallbackManager = class {
|
|
|
235
263
|
* (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.
|
|
236
264
|
*/
|
|
237
265
|
deleteEntry(callbackId, notify) {
|
|
238
|
-
|
|
239
|
-
const entry = s.callbacks.get(callbackId);
|
|
240
|
-
if (!entry) return;
|
|
241
|
-
s.callbacks.delete(callbackId);
|
|
242
|
-
if (entry.groupId !== void 0) {
|
|
243
|
-
const group = s.groups.get(entry.groupId);
|
|
244
|
-
group?.delete(callbackId);
|
|
245
|
-
if (group?.size === 0) s.groups.delete(entry.groupId);
|
|
246
|
-
}
|
|
247
|
-
if (entry.owner !== void 0) {
|
|
248
|
-
const owned = s.owners.get(entry.owner);
|
|
249
|
-
owned?.delete(callbackId);
|
|
250
|
-
if (owned?.size === 0) s.owners.delete(entry.owner);
|
|
251
|
-
}
|
|
252
|
-
if (notify) this.notifyRegistry();
|
|
253
|
-
entry.onRemove?.();
|
|
266
|
+
deleteHandleEntry(callbackId, notify);
|
|
254
267
|
}
|
|
255
268
|
};
|
|
256
269
|
var callbackManager = new CallbackManager();
|