@memberjunction/ng-realtime-channels 0.0.0 → 6.2.0-edge.4
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/LICENSE +183 -0
- package/README.md +42 -28
- package/dist/lib/angular-component-channel.d.ts +244 -0
- package/dist/lib/angular-component-channel.d.ts.map +1 -0
- package/dist/lib/angular-component-channel.js +412 -0
- package/dist/lib/angular-component-channel.js.map +1 -0
- package/dist/lib/channel-frame-capture.d.ts +33 -0
- package/dist/lib/channel-frame-capture.d.ts.map +1 -0
- package/dist/lib/channel-frame-capture.js +36 -0
- package/dist/lib/channel-frame-capture.js.map +1 -0
- package/dist/lib/dom-frame-capture.d.ts +73 -0
- package/dist/lib/dom-frame-capture.d.ts.map +1 -0
- package/dist/lib/dom-frame-capture.js +135 -0
- package/dist/lib/dom-frame-capture.js.map +1 -0
- package/dist/public-api.d.ts +18 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +18 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +48 -7
package/LICENSE
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
License text copyright (c) 2024 MariaDB plc, All Rights Reserved.
|
|
4
|
+
"Business Source License" is a trademark of MariaDB plc.
|
|
5
|
+
|
|
6
|
+
-----------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
|
|
10
|
+
Licensor: Blue Cypress, Inc.
|
|
11
|
+
|
|
12
|
+
Licensed Work: MemberJunction.
|
|
13
|
+
The Licensed Work is (c) 2023-2026 Blue Cypress, Inc.
|
|
14
|
+
|
|
15
|
+
Additional Use Grant: Subject to the terms of this License, Licensor grants
|
|
16
|
+
you the following additional rights to make Production
|
|
17
|
+
Use of the Licensed Work.
|
|
18
|
+
|
|
19
|
+
1. Internal Use
|
|
20
|
+
|
|
21
|
+
You may make production use of the Licensed Work for
|
|
22
|
+
your own internal business or organizational operations.
|
|
23
|
+
|
|
24
|
+
2. Nonprofit Use
|
|
25
|
+
|
|
26
|
+
If you are a Nonprofit, you may make production use of
|
|
27
|
+
the Licensed Work for the operations and activities of
|
|
28
|
+
your Organizational Family.
|
|
29
|
+
|
|
30
|
+
3. MemberJunction Certified Program Use
|
|
31
|
+
|
|
32
|
+
If you are authorized by Licensor under the
|
|
33
|
+
MemberJunction Certified Program to provide professional
|
|
34
|
+
services using the Licensed Work, you may make
|
|
35
|
+
production use of the Licensed Work in providing such
|
|
36
|
+
professional services to a client, provided that:
|
|
37
|
+
|
|
38
|
+
(a) the Licensed Work is deployed in, and the applicable
|
|
39
|
+
production use occurs within, an environment owned,
|
|
40
|
+
leased, licensed, subscribed to, or otherwise controlled
|
|
41
|
+
by that client; and
|
|
42
|
+
|
|
43
|
+
(b) the production use is for that client's own internal
|
|
44
|
+
business or organizational operations or is otherwise
|
|
45
|
+
independently permitted to that client under this
|
|
46
|
+
Additional Use Grant.
|
|
47
|
+
|
|
48
|
+
4. Definitions Applicable to the Additional Use Grant
|
|
49
|
+
|
|
50
|
+
"Affiliate" means, with respect to a specified Person,
|
|
51
|
+
any other Person that directly or indirectly Controls,
|
|
52
|
+
is Controlled by, or is under common Control with such
|
|
53
|
+
specified Person.
|
|
54
|
+
|
|
55
|
+
"Control" (including the terms "Controls," "Controlled
|
|
56
|
+
by," and "under common Control with") means the direct
|
|
57
|
+
or indirect possession of the power to direct or cause
|
|
58
|
+
the direction of the management and policies of a
|
|
59
|
+
Person, whether through ownership of voting interests,
|
|
60
|
+
by contract, or otherwise.
|
|
61
|
+
|
|
62
|
+
"Organizational Family" means, with respect to a Person,
|
|
63
|
+
(a) such Person and its Affiliates, and (b) any
|
|
64
|
+
nonprofit organization, governmental entity, chapter,
|
|
65
|
+
division, local affiliate, regional affiliate, state
|
|
66
|
+
affiliate, national affiliate, or other entity that is
|
|
67
|
+
formally affiliated with such Person through governing
|
|
68
|
+
documents, a charter, bylaws, a membership agreement, or
|
|
69
|
+
another written organizational instrument, and is
|
|
70
|
+
recognized under such documents as part of the same
|
|
71
|
+
organizational structure.
|
|
72
|
+
|
|
73
|
+
"Nonprofit" means a Person recognized by the Internal
|
|
74
|
+
Revenue Service as exempt from federal income taxation
|
|
75
|
+
under Section 501(c)(3), 501(c)(4), 501(c)(5), or
|
|
76
|
+
501(c)(6) of the Internal Revenue Code, or a foreign
|
|
77
|
+
organization recognized under substantially equivalent
|
|
78
|
+
laws.
|
|
79
|
+
|
|
80
|
+
A Person claiming eligibility as a Nonprofit shall, upon
|
|
81
|
+
Licensor's reasonable request, provide documentation
|
|
82
|
+
reasonably sufficient to demonstrate that it qualifies
|
|
83
|
+
as a Nonprofit. If such Person materially misrepresents,
|
|
84
|
+
or is unable to demonstrate, its qualification as a
|
|
85
|
+
Nonprofit, the rights granted to such Person under
|
|
86
|
+
Section 2 of this Additional Use Grant shall terminate.
|
|
87
|
+
|
|
88
|
+
"MemberJunction Certified Program" means Licensor's
|
|
89
|
+
then-current program for certifying and authorizing a
|
|
90
|
+
Person to provide professional services using the
|
|
91
|
+
Licensed Work.
|
|
92
|
+
|
|
93
|
+
"Person" means any individual, corporation, limited
|
|
94
|
+
liability company, partnership, association, nonprofit
|
|
95
|
+
organization, governmental entity, or other legal or
|
|
96
|
+
organizational entity.
|
|
97
|
+
|
|
98
|
+
Change Date: Four (4) years from the date the Licensed Work is first
|
|
99
|
+
made available.
|
|
100
|
+
|
|
101
|
+
Change License: MIT License.
|
|
102
|
+
|
|
103
|
+
For information about alternative licensing arrangements for the Licensed
|
|
104
|
+
Work, please contact Blue Cypress, Inc.
|
|
105
|
+
|
|
106
|
+
-----------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
Terms
|
|
109
|
+
|
|
110
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
111
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
112
|
+
Licensor may make an Additional Use Grant, above, permitting limited
|
|
113
|
+
production use.
|
|
114
|
+
|
|
115
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
116
|
+
available distribution of a specific version of the Licensed Work under this
|
|
117
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
118
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
119
|
+
above terminate.
|
|
120
|
+
|
|
121
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
122
|
+
currently in effect as described in this License, you must purchase a
|
|
123
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
124
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
125
|
+
|
|
126
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
127
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
128
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
129
|
+
for each version of the Licensed Work released by Licensor.
|
|
130
|
+
|
|
131
|
+
You must conspicuously display this License on each original or modified copy
|
|
132
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
133
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
134
|
+
License apply to your use of that work.
|
|
135
|
+
|
|
136
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
137
|
+
terminate your rights under this License for the current and all other
|
|
138
|
+
versions of the Licensed Work.
|
|
139
|
+
|
|
140
|
+
This License does not grant you any right in any trademark or logo of
|
|
141
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
142
|
+
Licensor as expressly required by this License).
|
|
143
|
+
|
|
144
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
145
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
146
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
147
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
148
|
+
TITLE.
|
|
149
|
+
|
|
150
|
+
MariaDB hereby grants you permission to use this License's text to license
|
|
151
|
+
your works, and to refer to it using the trademark "Business Source License",
|
|
152
|
+
as long as you comply with the Covenants of Licensor below.
|
|
153
|
+
|
|
154
|
+
-----------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
Covenants of Licensor
|
|
157
|
+
|
|
158
|
+
In consideration of the right to use this License's text and the "Business
|
|
159
|
+
Source License" name and trademark, Licensor covenants to MariaDB, and to all
|
|
160
|
+
other recipients of the licensed work to be provided by Licensor:
|
|
161
|
+
|
|
162
|
+
1. To specify as the Change License the GPL Version 2.0 or any later version,
|
|
163
|
+
or a license that is compatible with GPL Version 2.0 or a later version,
|
|
164
|
+
where "compatible" means that software provided under the Change License
|
|
165
|
+
can be included in a program with software provided under GPL Version 2.0
|
|
166
|
+
or a later version. Licensor may specify additional Change Licenses without
|
|
167
|
+
limitation.
|
|
168
|
+
|
|
169
|
+
2. To either: (a) specify an additional grant of rights to use that does not
|
|
170
|
+
impose any additional restriction on the right granted in this License, as
|
|
171
|
+
the Additional Use Grant; or (b) insert the text "None".
|
|
172
|
+
|
|
173
|
+
3. To specify a Change Date.
|
|
174
|
+
|
|
175
|
+
4. Not to modify this License in any other way.
|
|
176
|
+
|
|
177
|
+
-----------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
Notice
|
|
180
|
+
|
|
181
|
+
The Business Source License (this document, or the "License") is not an Open
|
|
182
|
+
Source license. However, the Licensed Work will eventually be made available
|
|
183
|
+
under an Open Source License, as stated in this License.
|
package/README.md
CHANGED
|
@@ -1,45 +1,59 @@
|
|
|
1
1
|
# @memberjunction/ng-realtime-channels
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Build realtime-agent channels in Angular without the call overlay. A small package on purpose: it depends on the channel contract (`@memberjunction/realtime-runtime`, `@memberjunction/ai-core-plus`) and Angular's core, and nothing else of MemberJunction's UI (no overlay, no Explorer, no router), so an embed or an Open App can use it without pulling in the conversations stack.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The how-to is [`guides/REALTIME_CHANNELS_GUIDE.md`](../../../../guides/REALTIME_CHANNELS_GUIDE.md); a complete worked example is [`@memberjunction/ng-realtime-channel-examples`](../realtime-channel-examples/README.md).
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## What is in it
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
| Export | Purpose |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `AngularComponentChannel<TComponent>` | A base class that turns an **existing** Angular component into a realtime channel surface without rewriting it. You write a descriptor and a few binding functions; the adapter owns binding, perception, verbs, completion, persistence and pictures. |
|
|
12
|
+
| `ChannelSurfaceEvent` | What a component's output becomes: `{ Name, Payload?, Author?, Perceive? }`. |
|
|
13
|
+
| `EnableChannelFrameCapture(options?)` | Opts a host in to showing the model pictures of channel surfaces: registers a bounded `html-to-image` based rasterizer. Returns a function that removes it. |
|
|
14
|
+
| `CreateDomFrameCapturer(options?)`, `DomFrameCaptureOptions`, `DomRasterizer` | Build a capturer with different limits or a different rasterizer. |
|
|
15
|
+
| `ChannelFrameCapture`, `ElementFrameCapturer` | The singleton that holds the host's capturer (`Instance.Register(fn \| null)`, `Instance.Capturer`). |
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@memberjunction/ng-realtime-channels`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
17
|
+
## `AngularComponentChannel`
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
```ts
|
|
20
|
+
@RegisterClass(BaseRealtimeChannelClient, 'SudokuChannel')
|
|
21
|
+
export class SudokuChannel extends AngularComponentChannel<SudokuComponent> {
|
|
22
|
+
protected readonly ComponentClass: Type<SudokuComponent> = SudokuComponent;
|
|
23
|
+
protected readonly Descriptor: RealtimeChannelDescriptor = { Key: 'Sudoku', /* ... for the model ... */ };
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
protected ReadSurfaceState(c: SudokuComponent): JSONObject { return { board: c.Board, moves: c.MoveCount }; }
|
|
26
|
+
protected ApplySurfaceVerb(c: SudokuComponent, verb: string, args: JSONObject, actor: RealtimeChannelActor): RealtimeChannelVerbResult { /* ... */ }
|
|
27
|
+
protected SurfaceEvents(c: SudokuComponent): Observable<ChannelSurfaceEvent> { return c.CellChanged.pipe(map(/* ... */)); }
|
|
28
|
+
}
|
|
29
|
+
export function LoadSudokuChannel(): void {}
|
|
30
|
+
```
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
Required members: `ComponentClass`, `Descriptor`, `ReadSurfaceState`, `ApplySurfaceVerb`, `SurfaceEvents`. Optional: `OnSurfaceOpen` (apply `open` inputs), `SurfaceCompletion` (a stream that becomes `Complete(output)`), `OnSurfaceBound` (restore a component created again from the last state), `SurfaceElement` (the root element, so the model can be shown a picture). A subclass that overrides `OnInitialize` must call `super.OnInitialize()`.
|
|
21
33
|
|
|
22
|
-
|
|
34
|
+
What the adapter does:
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
* **Binding.** `BindSurface` subscribes to `SurfaceEvents` and `SurfaceCompletion`, restores the last state through `OnSurfaceBound`, applies `open` inputs that arrived early and releases waiting verbs. `UnbindSurface` keeps the last state and lets go. A collapsed-then-expanded panel creates a new component, so durable state belongs to the channel.
|
|
37
|
+
* **Perception.** Every event, and every successful verb, is recorded as a change: observers get a typed event and the model gets one coalesced delta note, gated by exposure policy. An event emitted while the adapter is running the agent's verb is attributed to the agent.
|
|
38
|
+
* **Verbs.** Resolved against the descriptor (case-insensitive), refused for the agent when exposure is below the verb's `ReturnsChannelData`, validated against the parameter schema, serialized, run on the component. A throw becomes a `verb_failed` result the model can read, and is logged.
|
|
39
|
+
* **A verb with no surface.** It waits, bounded (`SurfaceBindTimeoutMs`, 5 s), for the component to bind and runs against the real one; if it never binds the call fails with `surface_unavailable` and a message telling the agent to ask the user to open it. Not a hidden headless instance (it would hold state nobody is looking at) and not queue-and-acknowledge (the agent needs the real outcome).
|
|
40
|
+
* **A verb that never answers.** Each verb has a bounded execution time (`VerbTimeoutMs`, 15 s, overridable; a non-positive value turns it off). On timeout the call fails with `verb_timeout` ("The X didn't respond to \"play\" within 15s; try again or ask the user."), the queue is released and the timeout is logged once with the channel and verb. The adapter cannot cancel the promise: a late result is never delivered, a late failure is logged, and a late success (the component changed after the agent was told it had not) is recorded as a change so the next perception note shows the real state. Nothing is rolled back, so the component's own rules must reject a duplicate retry.
|
|
41
|
+
* **Persistence.** `SerializeState()` and `RestoreState()` carry the last state as JSON (a non-object is refused).
|
|
42
|
+
* **Pictures.** If `SurfaceElement` returns an element and the host called `EnableChannelFrameCapture`, the channel sources the model's video track and a frame is pushed after each user change, only while the effective exposure is `'pixels'` and a video track is established.
|
|
28
43
|
|
|
29
|
-
##
|
|
44
|
+
## The rasterizer
|
|
30
45
|
|
|
31
|
-
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
46
|
+
Core ships no rasterizer: it is a heavy dependency every embed would pay for. A host that wants the model to see surfaces calls `EnableChannelFrameCapture()` once at startup, **before a session is minted** (Explorer does this in `ExplorerAppComponent`). The capturer is bounded (3000 nodes, 4 s, one capture at a time, a 1024 px longest edge, no web fonts) and fails soft: it costs the model a frame, never a call, and after three consecutive failures (or at once on one that cannot recover) turns itself off for the session and logs one reason.
|
|
36
47
|
|
|
37
|
-
##
|
|
48
|
+
## Layering
|
|
38
49
|
|
|
39
|
-
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
50
|
+
Layer **L1 (widgets)**: no router, no Explorer, no overlay. See [`guides/UI_LAYERING_GUIDE.md`](../../../../guides/UI_LAYERING_GUIDE.md).
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
## Testing
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
```bash
|
|
55
|
+
cd packages/Angular/Generic/realtime-channels
|
|
56
|
+
pnpm test # node preset (adapter) + jsdom preset (rasterizer)
|
|
57
|
+
pnpm run test:types # typecheck the specs
|
|
58
|
+
pnpm run build
|
|
59
|
+
```
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview {@link AngularComponentChannel}: turns an EXISTING Angular component into a realtime channel surface
|
|
3
|
+
* without rewriting it.
|
|
4
|
+
*
|
|
5
|
+
* A channel is what an agent and a user both operate during a call: the agent acts through verbs, perceives through
|
|
6
|
+
* state notes (and, if allowed, pictures), and the user acts directly on the surface. Writing one from scratch means
|
|
7
|
+
* writing the whole contract (descriptor, perception, verbs, binding the surface, completion). This base class owns the
|
|
8
|
+
* mechanics so a channel over a component you already have is a descriptor plus a few binding functions:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* @RegisterClass(BaseRealtimeChannelClient, 'SudokuChannel')
|
|
12
|
+
* export class SudokuChannel extends AngularComponentChannel<SudokuComponent> {
|
|
13
|
+
* protected readonly ComponentClass = SudokuComponent;
|
|
14
|
+
* protected readonly Descriptor: RealtimeChannelDescriptor = { Key: 'Sudoku', ... };
|
|
15
|
+
* protected ReadSurfaceState(c: SudokuComponent): JSONObject { return { board: c.Board, moves: c.MoveCount }; }
|
|
16
|
+
* protected ApplySurfaceVerb(c: SudokuComponent, verb: string, args: JSONObject): RealtimeChannelVerbResult { ... }
|
|
17
|
+
* protected SurfaceEvents(c: SudokuComponent): Observable<ChannelSurfaceEvent> { return c.CellChanged.pipe(map(...)); }
|
|
18
|
+
* }
|
|
19
|
+
* export function LoadSudokuChannel(): void {}
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ## What the adapter does for you
|
|
23
|
+
*
|
|
24
|
+
* - **Binding.** The overlay creates the component and hands it to {@link BindSurface}; the adapter subscribes to
|
|
25
|
+
* {@link SurfaceEvents}, applies anything that arrived early, and lets go cleanly on {@link UnbindSurface} (a host may
|
|
26
|
+
* create a NEW component instance for the same channel, e.g. when the overlay is recreated, so durable state belongs
|
|
27
|
+
* to the channel, see below).
|
|
28
|
+
* - **Perception.** Every event the component emits, and every verb the agent runs, is recorded as a change: observers
|
|
29
|
+
* see a typed event, and the model gets ONE coalesced structured note carrying a delta of {@link ReadSurfaceState}
|
|
30
|
+
* (see `RecordChange` in the base class). Exposure policy gates it like any channel.
|
|
31
|
+
* - **Verbs.** Parameters are validated against the descriptor's schema before your code runs; a verb the agent may not
|
|
32
|
+
* have (exposure) is refused by the dispatcher; the outcome is a structured result the model can recover from.
|
|
33
|
+
* - **Completion.** An optional {@link SurfaceCompletion} stream becomes `Complete(output)`.
|
|
34
|
+
* - **Pictures.** If you can name the component's root element ({@link SurfaceElement}) and the host registered a
|
|
35
|
+
* rasterizer (`EnableChannelFrameCapture`), the channel shows the model what the component looks like.
|
|
36
|
+
* - **Persistence.** The last state is saved as the channel's state of record and handed back to
|
|
37
|
+
* {@link OnSurfaceBound} when the surface is created again (collapse/expand, or a resumed session).
|
|
38
|
+
*
|
|
39
|
+
* ## When there is no surface
|
|
40
|
+
*
|
|
41
|
+
* A verb can arrive while no component is bound (the panel was just opened and the overlay is still creating the
|
|
42
|
+
* component). The adapter does NOT run the verb against a hidden second instance, and it does NOT acknowledge it and apply
|
|
43
|
+
* it later: the agent needs the real outcome (a move can be refused), and a second instance would hold state the user is
|
|
44
|
+
* not looking at. Instead the call WAITS, bounded ({@link SurfaceBindTimeoutMs}, 5 s), for the surface, runs once it
|
|
45
|
+
* binds (calls are serialized, so order is preserved), and fails with `'surface_unavailable'` and a message the agent can
|
|
46
|
+
* act on ("ask the user to open it") if it never binds. Seed inputs of `open` are different: there is nothing to report back
|
|
47
|
+
* but success, so they are applied when the surface binds. (A channel with no surface at all, a pure wire, does not need this
|
|
48
|
+
* class.)
|
|
49
|
+
*
|
|
50
|
+
* ## A verb that never answers
|
|
51
|
+
*
|
|
52
|
+
* Verbs run one at a time, so one that never settles would stall every later call. Each verb therefore has a bounded execution
|
|
53
|
+
* time ({@link VerbTimeoutMs}, 15 s). On timeout the call fails with `'verb_timeout'` and a message the agent can act on ("try
|
|
54
|
+
* again or ask the user"), the queue is released, and the timeout is logged once with the channel and verb. The adapter cannot
|
|
55
|
+
* cancel the promise your {@link ApplySurfaceVerb} returned, so what happens next is deliberately defined:
|
|
56
|
+
*
|
|
57
|
+
* - A **late result is never delivered**: the agent was already told the verb failed, and it is not told otherwise.
|
|
58
|
+
* - A late **failure** is logged and dropped.
|
|
59
|
+
* - A late **success** means the component DID change after the agent was told it had not. Nothing is rolled back (the
|
|
60
|
+
* component's own rules decide whether a retry is accepted, which is why they must live in the component), but the change is
|
|
61
|
+
* recorded like any other so the next perception note shows the model the real state.
|
|
62
|
+
* - The next queued verb may run while the late one is still pending, so a verb should not assume exclusive access to the
|
|
63
|
+
* component for longer than it takes to settle.
|
|
64
|
+
*
|
|
65
|
+
* @module @memberjunction/ng-realtime-channels
|
|
66
|
+
*/
|
|
67
|
+
import type { Type } from '@angular/core';
|
|
68
|
+
import { Observable } from 'rxjs';
|
|
69
|
+
import type { JSONObject } from '@memberjunction/ai';
|
|
70
|
+
import { type RealtimeChannelActor, type RealtimeChannelDescriptor } from '@memberjunction/ai-core-plus';
|
|
71
|
+
import { BaseRealtimeChannelClient, type RealtimeChannelVerbResult } from '@memberjunction/realtime-runtime';
|
|
72
|
+
/**
|
|
73
|
+
* Something the wrapped component did that the channel should know about: a user's move, a selection, a state
|
|
74
|
+
* change worth telling the model about. Return these from {@link AngularComponentChannel.SurfaceEvents}.
|
|
75
|
+
*/
|
|
76
|
+
export interface ChannelSurfaceEvent {
|
|
77
|
+
/** The event's name; declare it in the descriptor's `Events` so the agent knows it exists. */
|
|
78
|
+
Name: string;
|
|
79
|
+
/** The event's payload (JSON). */
|
|
80
|
+
Payload?: JSONObject;
|
|
81
|
+
/**
|
|
82
|
+
* Who caused it. Default `'user'`, except that an event emitted while the adapter is running an agent's verb is
|
|
83
|
+
* attributed to the agent (the component echoes the agent's own move through the same outputs the user's use).
|
|
84
|
+
*/
|
|
85
|
+
Author?: RealtimeChannelActor | 'system';
|
|
86
|
+
/** Set `false` for an event observers should see but the model should not be told about. Default `true`. */
|
|
87
|
+
Perceive?: boolean;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Base class for a realtime channel whose surface is an existing Angular component. See the file comment for the model.
|
|
91
|
+
*
|
|
92
|
+
* @typeParam TComponent The component class the channel shows and operates.
|
|
93
|
+
*/
|
|
94
|
+
export declare abstract class AngularComponentChannel<TComponent extends object> extends BaseRealtimeChannelClient<TComponent> {
|
|
95
|
+
/** The existing component to show as the channel's surface. The overlay creates it; the adapter binds to it. */
|
|
96
|
+
protected abstract readonly ComponentClass: Type<TComponent>;
|
|
97
|
+
/**
|
|
98
|
+
* The channel's self-description: what it is, what it holds (nouns), what it accepts (verbs with parameter schemas and
|
|
99
|
+
* `InvokableBy`), what it emits (events), how it opens (inputs) and what it hands back (output). Pure data, and the only
|
|
100
|
+
* thing an agent has to go on, so write `Instructions` and every description for the MODEL.
|
|
101
|
+
*/
|
|
102
|
+
protected abstract readonly Descriptor: RealtimeChannelDescriptor;
|
|
103
|
+
/**
|
|
104
|
+
* The channel's state as the model should perceive it: one entry per noun in the descriptor, JSON only. Called whenever
|
|
105
|
+
* a perception note is built, so it must be a pure, cheap read of the component.
|
|
106
|
+
*
|
|
107
|
+
* @param component The bound component.
|
|
108
|
+
*/
|
|
109
|
+
protected abstract ReadSurfaceState(component: TComponent): JSONObject;
|
|
110
|
+
/**
|
|
111
|
+
* Runs one verb on the component. The adapter has already resolved the verb against the descriptor and validated
|
|
112
|
+
* `args` against its schema, so trust the shape; check the GAME or DOCUMENT rules (is it the agent's turn? is the cell
|
|
113
|
+
* free?) and return a failure result with a message the model can act on rather than throwing.
|
|
114
|
+
*
|
|
115
|
+
* @param component The bound component.
|
|
116
|
+
* @param verb The verb's name, as declared in the descriptor.
|
|
117
|
+
* @param args The validated parameters.
|
|
118
|
+
* @param actor Who is acting: the agent, or the user acting through the same verb.
|
|
119
|
+
*/
|
|
120
|
+
protected abstract ApplySurfaceVerb(component: TComponent, verb: string, args: JSONObject, actor: RealtimeChannelActor): RealtimeChannelVerbResult | Promise<RealtimeChannelVerbResult>;
|
|
121
|
+
/**
|
|
122
|
+
* What the component emits that the channel should react to, as a stream. Subscribed when the surface binds and
|
|
123
|
+
* unsubscribed when it unbinds. Merge the component's outputs here; map each to a {@link ChannelSurfaceEvent}.
|
|
124
|
+
*
|
|
125
|
+
* @param component The bound component.
|
|
126
|
+
*/
|
|
127
|
+
protected abstract SurfaceEvents(component: TComponent): Observable<ChannelSurfaceEvent>;
|
|
128
|
+
/**
|
|
129
|
+
* Applies the seed inputs of `open` to the component (optional). Return a failure to refuse the open. Called when the
|
|
130
|
+
* surface is bound, immediately if it already is.
|
|
131
|
+
*
|
|
132
|
+
* @param component The bound component.
|
|
133
|
+
* @param inputs The validated open inputs.
|
|
134
|
+
*/
|
|
135
|
+
protected OnSurfaceOpen?(component: TComponent, inputs: JSONObject): void | RealtimeChannelVerbResult | Promise<void | RealtimeChannelVerbResult>;
|
|
136
|
+
/**
|
|
137
|
+
* Emits when the component's job is done and it has something to hand back (a finished game, a submitted form):
|
|
138
|
+
* becomes `Complete(output)`, validated against the descriptor's `Output`. Optional.
|
|
139
|
+
*
|
|
140
|
+
* @param component The bound component.
|
|
141
|
+
*/
|
|
142
|
+
protected SurfaceCompletion?(component: TComponent): Observable<JSONObject>;
|
|
143
|
+
/**
|
|
144
|
+
* Called each time a component is bound, with the last state the channel knew (this session's, or the saved one on a
|
|
145
|
+
* resumed session), so a component created fresh (a panel expanded again, a session resumed) can be restored to it.
|
|
146
|
+
* Optional; a component that keeps its own state elsewhere does not need it.
|
|
147
|
+
*
|
|
148
|
+
* @param component The newly bound component.
|
|
149
|
+
* @param lastState The last known state, or `null` when there is none.
|
|
150
|
+
*/
|
|
151
|
+
protected OnSurfaceBound?(component: TComponent, lastState: JSONObject | null): void;
|
|
152
|
+
/**
|
|
153
|
+
* The component's root element, so the channel can show the model a picture of it. Optional: without it (or without a
|
|
154
|
+
* rasterizer registered by the host) the channel offers state only. A component can expose its host element by
|
|
155
|
+
* injecting `ElementRef`.
|
|
156
|
+
*
|
|
157
|
+
* @param component The bound component.
|
|
158
|
+
*/
|
|
159
|
+
protected SurfaceElement?(component: TComponent): HTMLElement | null;
|
|
160
|
+
/** How long a verb waits for the surface to bind before failing with `'surface_unavailable'`, in ms. Override to change it. */
|
|
161
|
+
protected SurfaceBindTimeoutMs: number;
|
|
162
|
+
/**
|
|
163
|
+
* How long {@link ApplySurfaceVerb} may run before the call fails with `'verb_timeout'` and the queue is released, in ms
|
|
164
|
+
* (15 s by default; the surface bind wait is separate, see {@link SurfaceBindTimeoutMs}). Override it for a component whose
|
|
165
|
+
* verbs are legitimately slow; a value that is not a positive number turns the limit off, which is only safe for a verb that
|
|
166
|
+
* is guaranteed to settle. See the file comment for what happens to a verb that settles after the timeout.
|
|
167
|
+
*/
|
|
168
|
+
protected VerbTimeoutMs: number;
|
|
169
|
+
get ChannelName(): string;
|
|
170
|
+
get TabTitle(): string;
|
|
171
|
+
GetDescriptor(): RealtimeChannelDescriptor;
|
|
172
|
+
/** The channel has no native tools: its verbs are reached through the `ContextTool` proxy. */
|
|
173
|
+
GetToolDefinitions(): never[];
|
|
174
|
+
GetSurfaceComponent(): Type<TComponent>;
|
|
175
|
+
private surface;
|
|
176
|
+
private surfaceSubscriptions;
|
|
177
|
+
private bindWaiters;
|
|
178
|
+
private lastState;
|
|
179
|
+
private pendingOpenInputs;
|
|
180
|
+
private verbChain;
|
|
181
|
+
private applyingActor;
|
|
182
|
+
/** The bound component, or `null` while no surface is up. */
|
|
183
|
+
protected get Surface(): TComponent | null;
|
|
184
|
+
/**
|
|
185
|
+
* Opts into pictures when the subclass can name the component's element and the host registered a rasterizer. Subclasses
|
|
186
|
+
* that override this MUST call `super.OnInitialize()`.
|
|
187
|
+
*/
|
|
188
|
+
protected OnInitialize(): void;
|
|
189
|
+
/**
|
|
190
|
+
* The overlay created the component: bind to it. Subscribes to its events and completion, restores the last known
|
|
191
|
+
* state, applies any seed inputs that arrived before it existed, and releases verbs that were waiting for it.
|
|
192
|
+
*/
|
|
193
|
+
BindSurface(component: TComponent): void;
|
|
194
|
+
/** The component is going away (the panel collapsed, or the session ended): keep its last state, let go of it. */
|
|
195
|
+
UnbindSurface(): void;
|
|
196
|
+
private unbindSubscriptions;
|
|
197
|
+
/** Resolves with the bound component, waiting (bounded) for it to bind; `null` when it never does. */
|
|
198
|
+
private whenSurfaceBound;
|
|
199
|
+
/**
|
|
200
|
+
* The channel's state: the live component's, or the last known when no component is bound (so a note built while the
|
|
201
|
+
* panel is collapsed still describes the channel truthfully).
|
|
202
|
+
*/
|
|
203
|
+
GetState(): JSONObject;
|
|
204
|
+
/** Reads the component's state, logging and returning `null` if the subclass's reader throws (a perception note must never break a call). */
|
|
205
|
+
private safeReadState;
|
|
206
|
+
/** The state of record: the last known state as JSON, or `null` before there is any. */
|
|
207
|
+
SerializeState(): string | null;
|
|
208
|
+
/**
|
|
209
|
+
* A saved state came back (a resumed session): hold it, and hand it to {@link OnSurfaceBound} when the component is
|
|
210
|
+
* created. Returns `false` for anything that is not a JSON object.
|
|
211
|
+
*/
|
|
212
|
+
RestoreState(stateJson: string): boolean;
|
|
213
|
+
/**
|
|
214
|
+
* Seeds the surface with the open inputs: now if the component is bound, otherwise when it binds (there is nothing to
|
|
215
|
+
* report back but success, so there is nothing to wait for).
|
|
216
|
+
*/
|
|
217
|
+
protected OnOpen(inputs: JSONObject): Promise<void | RealtimeChannelVerbResult>;
|
|
218
|
+
private applyPendingOpen;
|
|
219
|
+
/**
|
|
220
|
+
* Runs one verb. Verbs are serialized (a second call waits for the first), wait (bounded) for the surface, and run
|
|
221
|
+
* against the real component, so the outcome the agent hears is the outcome the user sees.
|
|
222
|
+
*/
|
|
223
|
+
ApplyVerb(verb: string, args: JSONObject, actor: RealtimeChannelActor): Promise<RealtimeChannelVerbResult>;
|
|
224
|
+
private runVerb;
|
|
225
|
+
private applyOnSurface;
|
|
226
|
+
/**
|
|
227
|
+
* Runs {@link ApplySurfaceVerb} with a bounded execution time. Settles with the verb's result, with `TimedOut` when it did
|
|
228
|
+
* not answer in time, or rejects with what it threw. Once it has timed out, whatever it later does is handled by
|
|
229
|
+
* {@link onLateVerbSettle} and never reaches the caller: the promise is already settled, so there is no second result.
|
|
230
|
+
*/
|
|
231
|
+
private runWithTimeout;
|
|
232
|
+
/**
|
|
233
|
+
* A verb settled AFTER it timed out. The agent already has its answer (failure), so the late result is never delivered. A
|
|
234
|
+
* late failure is logged. A late success means the component changed after the agent was told it had not: it is recorded as
|
|
235
|
+
* a change (when the same component is still on screen) so the next perception note shows the model the real state.
|
|
236
|
+
*/
|
|
237
|
+
private onLateVerbSettle;
|
|
238
|
+
/** The component emitted something: tell observers, and (unless told not to) record it as a change the model perceives. */
|
|
239
|
+
private onSurfaceEvent;
|
|
240
|
+
/** A picture of the component, or `null` when there is none to send (see the rules in the Interactive Component channel). */
|
|
241
|
+
private captureFrame;
|
|
242
|
+
Dispose(): void;
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=angular-component-channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"angular-component-channel.d.ts","sourceRoot":"","sources":["../../src/lib/angular-component-channel.ts"],"names":[],"mappings":"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"}
|