ultravisor-beacon-capability 1.0.0
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/README.md +106 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +103 -0
- package/docs/_brand.json +18 -0
- package/docs/_cover.md +13 -0
- package/docs/_sidebar.md +31 -0
- package/docs/_topbar.md +5 -0
- package/docs/_version.json +7 -0
- package/docs/api/README.md +44 -0
- package/docs/api/action-convention.md +148 -0
- package/docs/api/add-action.md +68 -0
- package/docs/api/beacon-capability.md +89 -0
- package/docs/api/build-action-map.md +88 -0
- package/docs/api/connect.md +81 -0
- package/docs/api/disconnect.md +50 -0
- package/docs/api/is-connected.md +33 -0
- package/docs/api/lifecycle-hooks.md +115 -0
- package/docs/architecture.md +237 -0
- package/docs/css/docuserve.css +327 -0
- package/docs/examples/README.md +58 -0
- package/docs/examples/certificate-expiry-monitor.md +212 -0
- package/docs/examples/docker-container-management.md +265 -0
- package/docs/examples/log-archive-and-upload.md +214 -0
- package/docs/examples/log-file-cleanup.md +199 -0
- package/docs/examples/mysql-maintenance.md +253 -0
- package/docs/examples/postgresql-aggregation.md +247 -0
- package/docs/examples/rest-api-health-check.md +213 -0
- package/docs/examples/rest-endpoint-sync.md +240 -0
- package/docs/examples/server-metrics-collection.md +199 -0
- package/docs/examples/shell-commands.md +176 -0
- package/docs/index.html +39 -0
- package/docs/quickstart.md +199 -0
- package/docs/retold-catalog.json +85 -0
- package/docs/retold-keyword-index.json +10642 -0
- package/package.json +33 -0
- package/source/Ultravisor-Beacon-Capability-ActionMap.cjs +132 -0
- package/source/Ultravisor-Beacon-Capability.cjs +276 -0
- package/test/Ultravisor-Beacon-Capability_tests.js +744 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# buildActionMap
|
|
2
|
+
|
|
3
|
+
Discovers action methods on a capability instance by walking its prototype chain. This is an internal function used by `UltravisorBeaconCapability.connect()`, but it is exported for testing and advanced use cases.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
const { buildActionMap } = require('ultravisor-beacon-capability/source/Ultravisor-Beacon-Capability-ActionMap.cjs');
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Signature
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
buildActionMap(pInstance) => object
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Parameters
|
|
18
|
+
|
|
19
|
+
| Parameter | Type | Description |
|
|
20
|
+
|-----------|------|-------------|
|
|
21
|
+
| `pInstance` | `object` | A capability instance to inspect |
|
|
22
|
+
|
|
23
|
+
### Returns
|
|
24
|
+
|
|
25
|
+
An object mapping action names to action definitions:
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
{
|
|
29
|
+
'ActionName':
|
|
30
|
+
{
|
|
31
|
+
Description: 'Human-readable description',
|
|
32
|
+
SettingsSchema: [{ Name: 'Param', DataType: 'String', Required: true }],
|
|
33
|
+
Handler: function (pWorkItem, pContext, fCallback, fReportProgress) { ... }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The `Handler` function uses the raw `ultravisor-beacon` signature (not the convention signature). It wraps the bound action method with Settings pre-extraction.
|
|
39
|
+
|
|
40
|
+
## Example
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
const libFable = require('fable');
|
|
44
|
+
const libBeaconCapability = require('ultravisor-beacon-capability');
|
|
45
|
+
const { buildActionMap } = require('ultravisor-beacon-capability/source/Ultravisor-Beacon-Capability-ActionMap.cjs');
|
|
46
|
+
|
|
47
|
+
class MyCapability extends libBeaconCapability
|
|
48
|
+
{
|
|
49
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
50
|
+
{
|
|
51
|
+
super(pFable, pOptions, pServiceHash);
|
|
52
|
+
this.capabilityName = 'Test';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get actionPing_Description() { return 'Respond with pong'; }
|
|
56
|
+
actionPing(pSettings, pWorkItem, fCallback)
|
|
57
|
+
{
|
|
58
|
+
return fCallback(null, { Outputs: { Response: 'pong' } });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let tmpFable = new libFable({});
|
|
63
|
+
let tmpCap = new MyCapability(tmpFable, {}, 'test');
|
|
64
|
+
let tmpMap = buildActionMap(tmpCap);
|
|
65
|
+
|
|
66
|
+
console.log(Object.keys(tmpMap));
|
|
67
|
+
// ['Ping']
|
|
68
|
+
|
|
69
|
+
console.log(tmpMap.Ping.Description);
|
|
70
|
+
// 'Respond with pong'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Algorithm
|
|
74
|
+
|
|
75
|
+
1. Get the first prototype of the instance
|
|
76
|
+
2. While the prototype is not `Object.prototype`:
|
|
77
|
+
a. For each own property name on the prototype:
|
|
78
|
+
- Skip if already visited (subclass overrides win)
|
|
79
|
+
- Skip if not starting with `action` or too short
|
|
80
|
+
- Skip if ending with `_Schema` or `_Description`
|
|
81
|
+
- Skip if not a function (via `Object.getOwnPropertyDescriptor`)
|
|
82
|
+
- Strip `action` prefix to get the action name
|
|
83
|
+
- Resolve `_Schema` companion (getter, value, or method)
|
|
84
|
+
- Resolve `_Description` companion
|
|
85
|
+
- Create bound handler with Settings extraction wrapper
|
|
86
|
+
- Add to the action map
|
|
87
|
+
b. Move to the next prototype in the chain
|
|
88
|
+
3. Return the action map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# connect()
|
|
2
|
+
|
|
3
|
+
Connect to an Ultravisor server, discover and register actions, and begin accepting work items.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
connect(pBeaconConfig, fCallback)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Description |
|
|
14
|
+
|-----------|------|-------------|
|
|
15
|
+
| `pBeaconConfig` | `object` | Connection configuration (see below) |
|
|
16
|
+
| `fCallback` | `function` | `function(pError, pBeaconInfo)` -- called when connection completes or fails |
|
|
17
|
+
|
|
18
|
+
### Configuration
|
|
19
|
+
|
|
20
|
+
| Property | Type | Default | Required | Description |
|
|
21
|
+
|----------|------|---------|----------|-------------|
|
|
22
|
+
| `ServerURL` | `string` | -- | Yes | Ultravisor server URL (e.g. `http://localhost:54321`) |
|
|
23
|
+
| `Name` | `string` | `capabilityName` | No | Beacon worker name for registration |
|
|
24
|
+
| `Password` | `string` | `''` | No | Authentication password |
|
|
25
|
+
| `MaxConcurrent` | `number` | `1` | No | Maximum number of work items to execute in parallel |
|
|
26
|
+
| `StagingPath` | `string` | `process.cwd()` | No | Directory for file transfer staging |
|
|
27
|
+
| `Tags` | `object` | `{}` | No | Metadata tags sent to the coordinator |
|
|
28
|
+
| `BindAddresses` | `array` | `[]` | No | Network addresses to advertise for direct access |
|
|
29
|
+
|
|
30
|
+
### Callback
|
|
31
|
+
|
|
32
|
+
On success, `fCallback` receives `(null, pBeaconInfo)` where `pBeaconInfo` contains:
|
|
33
|
+
|
|
34
|
+
| Property | Type | Description |
|
|
35
|
+
|----------|------|-------------|
|
|
36
|
+
| `BeaconID` | `string` | Unique identifier assigned by the server |
|
|
37
|
+
|
|
38
|
+
On failure, `fCallback` receives `(pError)`.
|
|
39
|
+
|
|
40
|
+
## Behavior
|
|
41
|
+
|
|
42
|
+
1. Validates that `ServerURL` is present in the config
|
|
43
|
+
2. Validates that `capabilityName` is set on the instance
|
|
44
|
+
3. Discovers action methods via `buildActionMap()`
|
|
45
|
+
4. Merges any explicitly registered actions (from `addAction()`)
|
|
46
|
+
5. Builds a capability descriptor
|
|
47
|
+
6. Registers the `UltravisorBeacon` service type with Fable (if not already registered)
|
|
48
|
+
7. Instantiates a beacon service with the provided config
|
|
49
|
+
8. Registers the capability descriptor with the beacon service
|
|
50
|
+
9. Enables the beacon (authenticates, registers with server, begins polling)
|
|
51
|
+
|
|
52
|
+
## Example
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
tmpCapability.connect(
|
|
56
|
+
{
|
|
57
|
+
ServerURL: 'http://ultravisor.local:54321',
|
|
58
|
+
Name: 'my-worker',
|
|
59
|
+
Password: 'secret',
|
|
60
|
+
MaxConcurrent: 4,
|
|
61
|
+
Tags: { environment: 'production', host: require('os').hostname() }
|
|
62
|
+
},
|
|
63
|
+
(pError, pBeaconInfo) =>
|
|
64
|
+
{
|
|
65
|
+
if (pError)
|
|
66
|
+
{
|
|
67
|
+
console.error('Failed to connect:', pError.message);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
console.log('Beacon online:', pBeaconInfo.BeaconID);
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Error Cases
|
|
75
|
+
|
|
76
|
+
| Error | Cause |
|
|
77
|
+
|-------|-------|
|
|
78
|
+
| `ServerURL is required` | `pBeaconConfig.ServerURL` is missing or falsy |
|
|
79
|
+
| `capabilityName must be set` | `this.capabilityName` is empty |
|
|
80
|
+
| Authentication failure | Server rejected credentials |
|
|
81
|
+
| Network error | Server unreachable (beacon will auto-reconnect) |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# disconnect()
|
|
2
|
+
|
|
3
|
+
Disconnect from the Ultravisor server. Deregisters the beacon and cleans up the connection.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
disconnect(fCallback)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Description |
|
|
14
|
+
|-----------|------|-------------|
|
|
15
|
+
| `fCallback` | `function` | `function(pError)` -- called when disconnection completes |
|
|
16
|
+
|
|
17
|
+
## Behavior
|
|
18
|
+
|
|
19
|
+
1. If not currently connected, calls `fCallback(null)` immediately
|
|
20
|
+
2. Calls `disable()` on the underlying beacon service, which:
|
|
21
|
+
- Sends a deregistration message to the server
|
|
22
|
+
- Closes the WebSocket connection (if open)
|
|
23
|
+
- Stops HTTP polling (if active)
|
|
24
|
+
- Calls `onShutdown()` on the capability
|
|
25
|
+
3. Clears the internal beacon service reference
|
|
26
|
+
4. Calls `fCallback`
|
|
27
|
+
|
|
28
|
+
## Example
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
// Graceful shutdown on SIGTERM
|
|
32
|
+
process.on('SIGTERM', () =>
|
|
33
|
+
{
|
|
34
|
+
tmpCapability.disconnect((pError) =>
|
|
35
|
+
{
|
|
36
|
+
if (pError)
|
|
37
|
+
{
|
|
38
|
+
console.error('Disconnect error:', pError.message);
|
|
39
|
+
}
|
|
40
|
+
console.log('Beacon disconnected.');
|
|
41
|
+
process.exit(0);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Notes
|
|
47
|
+
|
|
48
|
+
- Safe to call when not connected -- it is a no-op
|
|
49
|
+
- Safe to call without a callback -- errors are silently ignored
|
|
50
|
+
- After disconnecting, you can call `connect()` again to reconnect
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# isConnected()
|
|
2
|
+
|
|
3
|
+
Check whether the beacon is currently connected to an Ultravisor server.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
isConnected() => boolean
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Returns
|
|
12
|
+
|
|
13
|
+
`true` if the beacon service exists and is enabled; `false` otherwise.
|
|
14
|
+
|
|
15
|
+
## Example
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
if (tmpCapability.isConnected())
|
|
19
|
+
{
|
|
20
|
+
console.log('Beacon is online and accepting work items');
|
|
21
|
+
}
|
|
22
|
+
else
|
|
23
|
+
{
|
|
24
|
+
console.log('Beacon is offline');
|
|
25
|
+
tmpCapability.connect({ ServerURL: 'http://ultravisor:54321' }, (pError) => { });
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Notes
|
|
30
|
+
|
|
31
|
+
- Returns `false` before `connect()` is called
|
|
32
|
+
- Returns `false` after `disconnect()` completes
|
|
33
|
+
- During auto-reconnection (after a connection drop), the beacon service remains enabled internally, so this may return `true` even while reconnecting
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# onInitialize / onShutdown
|
|
2
|
+
|
|
3
|
+
Lifecycle hooks for resource management. Override these methods in your subclass to set up and tear down resources that your actions depend on.
|
|
4
|
+
|
|
5
|
+
## onInitialize
|
|
6
|
+
|
|
7
|
+
Called after the beacon connects to the Ultravisor server, before it begins accepting work items.
|
|
8
|
+
|
|
9
|
+
### Signature
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
onInitialize(fCallback)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Parameter | Type | Description |
|
|
16
|
+
|-----------|------|-------------|
|
|
17
|
+
| `fCallback` | `function` | `function(pError)` -- call with `null` on success, or an error to abort |
|
|
18
|
+
|
|
19
|
+
### Default
|
|
20
|
+
|
|
21
|
+
No-op -- calls `fCallback(null)` immediately.
|
|
22
|
+
|
|
23
|
+
### Example
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
onInitialize(fCallback)
|
|
27
|
+
{
|
|
28
|
+
// Create a database connection pool
|
|
29
|
+
this._Pool = require('mysql2').createPool({
|
|
30
|
+
host: this.fable.settings.DatabaseHost || 'localhost',
|
|
31
|
+
user: this.fable.settings.DatabaseUser || 'root',
|
|
32
|
+
database: this.fable.settings.DatabaseName || 'mydb'
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Verify the connection
|
|
36
|
+
this._Pool.query('SELECT 1', (pError) =>
|
|
37
|
+
{
|
|
38
|
+
if (pError)
|
|
39
|
+
{
|
|
40
|
+
this.log.error('Database connection failed:', pError.message);
|
|
41
|
+
return fCallback(pError);
|
|
42
|
+
}
|
|
43
|
+
this.log.info('Database connection pool ready');
|
|
44
|
+
return fCallback(null);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## onShutdown
|
|
50
|
+
|
|
51
|
+
Called when the beacon disconnects from the Ultravisor server.
|
|
52
|
+
|
|
53
|
+
### Signature
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
onShutdown(fCallback)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Description |
|
|
60
|
+
|-----------|------|-------------|
|
|
61
|
+
| `fCallback` | `function` | `function(pError)` -- call with `null` on success |
|
|
62
|
+
|
|
63
|
+
### Default
|
|
64
|
+
|
|
65
|
+
No-op -- calls `fCallback(null)` immediately.
|
|
66
|
+
|
|
67
|
+
### Example
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
onShutdown(fCallback)
|
|
71
|
+
{
|
|
72
|
+
if (this._Pool)
|
|
73
|
+
{
|
|
74
|
+
this._Pool.end((pError) =>
|
|
75
|
+
{
|
|
76
|
+
if (pError)
|
|
77
|
+
{
|
|
78
|
+
this.log.warn('Error closing database pool:', pError.message);
|
|
79
|
+
}
|
|
80
|
+
this.log.info('Database connection pool closed');
|
|
81
|
+
return fCallback(null);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
{
|
|
86
|
+
return fCallback(null);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Lifecycle Sequence
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
connect() called
|
|
95
|
+
-> Beacon authenticates with Ultravisor server
|
|
96
|
+
-> Beacon registers capabilities
|
|
97
|
+
-> onInitialize() called
|
|
98
|
+
-> Beacon begins accepting work items
|
|
99
|
+
-> ... actions execute ...
|
|
100
|
+
disconnect() called
|
|
101
|
+
-> Beacon stops accepting work items
|
|
102
|
+
-> onShutdown() called
|
|
103
|
+
-> Beacon deregisters from server
|
|
104
|
+
-> Connection closed
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Common Use Cases
|
|
108
|
+
|
|
109
|
+
| Resource | onInitialize | onShutdown |
|
|
110
|
+
|----------|-------------|------------|
|
|
111
|
+
| Database connection pool | Create pool, test connection | Close pool |
|
|
112
|
+
| HTTP client / API token | Fetch auth token, create client | Revoke token |
|
|
113
|
+
| Temporary directory | Create staging directory | Remove directory |
|
|
114
|
+
| File handle / stream | Open file | Close file |
|
|
115
|
+
| External service handle | Connect to service | Disconnect |
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Ultravisor Beacon Capability is a thin convention layer on top of `ultravisor-beacon`. It discovers action methods on your subclass, builds the capability descriptor that `ultravisor-beacon` expects, and delegates all transport and execution to the underlying beacon service.
|
|
4
|
+
|
|
5
|
+
## Class Hierarchy
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
graph TD
|
|
9
|
+
FSPB[fable-serviceproviderbase<br/><small>Fable service base class</small>]
|
|
10
|
+
UBC[UltravisorBeaconCapability<br/><small>Convention-based base class</small>]
|
|
11
|
+
YOUR[YourCapability<br/><small>Your subclass with action methods</small>]
|
|
12
|
+
|
|
13
|
+
AM[ActionMap<br/><small>Prototype chain walker</small>]
|
|
14
|
+
UBS[UltravisorBeaconService<br/><small>ultravisor-beacon</small>]
|
|
15
|
+
|
|
16
|
+
CM[CapabilityManager<br/><small>Descriptor store</small>]
|
|
17
|
+
BC[BeaconClient<br/><small>Transport + polling</small>]
|
|
18
|
+
PR[ProviderRegistry<br/><small>Provider index</small>]
|
|
19
|
+
CA[CapabilityAdapter<br/><small>Descriptor to provider bridge</small>]
|
|
20
|
+
|
|
21
|
+
FSPB -->|extends| UBC
|
|
22
|
+
UBC -->|extends| YOUR
|
|
23
|
+
UBC -->|uses| AM
|
|
24
|
+
UBC -->|composes| UBS
|
|
25
|
+
|
|
26
|
+
UBS --> CM
|
|
27
|
+
UBS --> BC
|
|
28
|
+
CM -->|buildProviderDescriptors| CA
|
|
29
|
+
CA -->|registerProvider| PR
|
|
30
|
+
BC --> PR
|
|
31
|
+
|
|
32
|
+
style UBC fill:#e1f5fe
|
|
33
|
+
style YOUR fill:#c8e6c9
|
|
34
|
+
style AM fill:#fff3e0
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Module Composition
|
|
38
|
+
|
|
39
|
+
The capability base class composes three internal concerns:
|
|
40
|
+
|
|
41
|
+
| Component | Source | Responsibility |
|
|
42
|
+
|-----------|--------|----------------|
|
|
43
|
+
| **ActionMap** | `Ultravisor-Beacon-Capability-ActionMap.cjs` | Discovers `action*` methods on the prototype chain; resolves companion `_Schema` and `_Description` properties; builds bound handlers |
|
|
44
|
+
| **UltravisorBeaconCapability** | `Ultravisor-Beacon-Capability.cjs` | Base class; merges discovered and explicit actions; builds the capability descriptor; manages beacon lifecycle |
|
|
45
|
+
| **UltravisorBeaconService** | `ultravisor-beacon` (external) | Handles authentication, transport negotiation, polling, heartbeat, work item execution |
|
|
46
|
+
|
|
47
|
+
## Connect Flow
|
|
48
|
+
|
|
49
|
+
When you call `connect()`, the following sequence executes:
|
|
50
|
+
|
|
51
|
+
```mermaid
|
|
52
|
+
sequenceDiagram
|
|
53
|
+
participant Dev as Your Code
|
|
54
|
+
participant Cap as BeaconCapability
|
|
55
|
+
participant AM as ActionMap
|
|
56
|
+
participant BS as BeaconService
|
|
57
|
+
participant UV as Ultravisor Server
|
|
58
|
+
|
|
59
|
+
Dev->>Cap: connect({ ServerURL, Name, ... })
|
|
60
|
+
Cap->>AM: buildActionMap(this)
|
|
61
|
+
AM-->>Cap: { ActionName: { Handler, Schema, Description } }
|
|
62
|
+
Cap->>Cap: Merge explicit actions (addAction)
|
|
63
|
+
Cap->>Cap: Build capability descriptor
|
|
64
|
+
Cap->>BS: new UltravisorBeaconService(config)
|
|
65
|
+
Cap->>BS: registerCapability(descriptor)
|
|
66
|
+
Cap->>BS: enable()
|
|
67
|
+
BS->>UV: POST /1.0/Authenticate
|
|
68
|
+
UV-->>BS: session cookie
|
|
69
|
+
BS->>UV: POST /Beacon/Register
|
|
70
|
+
UV-->>BS: { BeaconID }
|
|
71
|
+
UV->>UV: Create task types for each action
|
|
72
|
+
BS-->>Cap: callback(null, beaconInfo)
|
|
73
|
+
Cap-->>Dev: callback(null, beaconInfo)
|
|
74
|
+
|
|
75
|
+
Note over UV: Task types now available:<br/>beacon-{capability}-{action}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Action Discovery
|
|
79
|
+
|
|
80
|
+
The `buildActionMap()` function walks the prototype chain of your capability instance to find action methods:
|
|
81
|
+
|
|
82
|
+
```mermaid
|
|
83
|
+
flowchart TD
|
|
84
|
+
START([Start]) --> PROTO[Get prototype of instance]
|
|
85
|
+
PROTO --> CHECK{prototype !== Object.prototype?}
|
|
86
|
+
CHECK -->|No| DONE([Return action map])
|
|
87
|
+
CHECK -->|Yes| PROPS[Get own property names]
|
|
88
|
+
PROPS --> EACH{Next property name}
|
|
89
|
+
EACH -->|None left| NEXT[Get parent prototype]
|
|
90
|
+
NEXT --> CHECK
|
|
91
|
+
|
|
92
|
+
EACH -->|Has name| VISITED{Already visited?}
|
|
93
|
+
VISITED -->|Yes| EACH
|
|
94
|
+
VISITED -->|No| PREFIX{Starts with 'action'?}
|
|
95
|
+
PREFIX -->|No| EACH
|
|
96
|
+
PREFIX -->|Yes| SUFFIX{Ends with '_Schema' or '_Description'?}
|
|
97
|
+
SUFFIX -->|Yes| EACH
|
|
98
|
+
SUFFIX -->|No| FUNC{Is a function?}
|
|
99
|
+
FUNC -->|No| EACH
|
|
100
|
+
FUNC -->|Yes| EXTRACT[Strip 'action' prefix to get action name]
|
|
101
|
+
EXTRACT --> SCHEMA[Resolve companion _Schema]
|
|
102
|
+
SCHEMA --> DESC[Resolve companion _Description]
|
|
103
|
+
DESC --> HANDLER[Create bound handler with Settings extraction]
|
|
104
|
+
HANDLER --> ADD[Add to action map]
|
|
105
|
+
ADD --> EACH
|
|
106
|
+
|
|
107
|
+
style EXTRACT fill:#c8e6c9
|
|
108
|
+
style HANDLER fill:#c8e6c9
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Key behaviors:
|
|
112
|
+
- **Subclass wins** -- If a method name is seen on a derived class, the base class version is skipped
|
|
113
|
+
- **Getters supported** -- `_Schema` and `_Description` companions can be ES class getters, plain properties, or methods
|
|
114
|
+
- **Bound handlers** -- Each handler is bound to the instance, preserving `this` context for access to services, connections, and state
|
|
115
|
+
|
|
116
|
+
## Handler Wrapping
|
|
117
|
+
|
|
118
|
+
The convention-based handler signature differs from the raw `ultravisor-beacon` handler signature. The ActionMap creates a wrapper that bridges the two:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
Convention (your code):
|
|
122
|
+
actionDoWork(pSettings, pWorkItem, fCallback, fReportProgress)
|
|
123
|
+
|
|
124
|
+
Raw beacon (what ultravisor-beacon expects):
|
|
125
|
+
Handler(pWorkItem, pContext, fCallback, fReportProgress)
|
|
126
|
+
|
|
127
|
+
Wrapper (created by ActionMap):
|
|
128
|
+
function(pWorkItem, pContext, fCallback, fReportProgress)
|
|
129
|
+
{
|
|
130
|
+
let tmpSettings = (pWorkItem && pWorkItem.Settings) ? pWorkItem.Settings : {};
|
|
131
|
+
return boundMethod(tmpSettings, pWorkItem, fCallback, fReportProgress);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The `pContext` parameter (containing `{ StagingPath }`) is not forwarded because it is unused in practice. If needed, it is available via `this.options.StagingPath` on the capability instance.
|
|
136
|
+
|
|
137
|
+
## Capability Descriptor
|
|
138
|
+
|
|
139
|
+
The base class produces a descriptor matching the shape documented in `ultravisor-beacon`'s `CapabilityManager`:
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
{
|
|
143
|
+
Capability: 'YourCapabilityName',
|
|
144
|
+
Name: 'YourCapabilityNameProvider',
|
|
145
|
+
actions:
|
|
146
|
+
{
|
|
147
|
+
'ActionOne':
|
|
148
|
+
{
|
|
149
|
+
Description: 'What it does',
|
|
150
|
+
SettingsSchema: [{ Name: 'Param', DataType: 'String', Required: true }],
|
|
151
|
+
Handler: function (pWorkItem, pContext, fCallback, fReportProgress) { ... }
|
|
152
|
+
},
|
|
153
|
+
'ActionTwo': { ... }
|
|
154
|
+
},
|
|
155
|
+
initialize: function (fCallback) { /* delegates to onInitialize */ },
|
|
156
|
+
shutdown: function (fCallback) { /* delegates to onShutdown */ }
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The `initialize` and `shutdown` functions delegate to `onInitialize()` and `onShutdown()` on your subclass.
|
|
161
|
+
|
|
162
|
+
## Server-Side Task Registration
|
|
163
|
+
|
|
164
|
+
When the Ultravisor server receives the beacon registration, its coordinator automatically creates task types for each action:
|
|
165
|
+
|
|
166
|
+
```mermaid
|
|
167
|
+
graph LR
|
|
168
|
+
REG[Beacon registers<br/>Capability: DBMaint<br/>Actions: PurgeOld, Vacuum] --> COORD[Coordinator]
|
|
169
|
+
COORD --> T1[Task Type:<br/>beacon-dbmaint-purgeold]
|
|
170
|
+
COORD --> T2[Task Type:<br/>beacon-dbmaint-vacuum]
|
|
171
|
+
|
|
172
|
+
T1 --> UI[Ultravisor Dashboard]
|
|
173
|
+
T2 --> UI
|
|
174
|
+
T1 --> SCHED[Scheduler]
|
|
175
|
+
T2 --> SCHED
|
|
176
|
+
T1 --> GRAPH[Operation Graphs]
|
|
177
|
+
T2 --> GRAPH
|
|
178
|
+
|
|
179
|
+
style T1 fill:#e1f5fe
|
|
180
|
+
style T2 fill:#e1f5fe
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Each task type includes:
|
|
184
|
+
- **SettingsInputs** derived from the action's `SettingsSchema`
|
|
185
|
+
- **EventInputs/Outputs** for graph wiring (Trigger, Complete, Error)
|
|
186
|
+
- **StateOutputs** for capturing results (Result, StdOut)
|
|
187
|
+
|
|
188
|
+
## Lifecycle
|
|
189
|
+
|
|
190
|
+
```mermaid
|
|
191
|
+
stateDiagram-v2
|
|
192
|
+
[*] --> Created: new YourCapability(fable, options)
|
|
193
|
+
Created --> Connecting: connect(config)
|
|
194
|
+
Connecting --> Initializing: onInitialize()
|
|
195
|
+
Initializing --> Connected: Beacon enabled
|
|
196
|
+
Connected --> Executing: Work item received
|
|
197
|
+
Executing --> Connected: Work item complete
|
|
198
|
+
Connected --> ShuttingDown: disconnect()
|
|
199
|
+
ShuttingDown --> Disconnected: onShutdown()
|
|
200
|
+
Disconnected --> [*]
|
|
201
|
+
|
|
202
|
+
Connected --> Reconnecting: Connection lost
|
|
203
|
+
Reconnecting --> Connected: Auto-reconnect
|
|
204
|
+
|
|
205
|
+
note right of Initializing
|
|
206
|
+
Override onInitialize() to set up
|
|
207
|
+
database connections, service handles,
|
|
208
|
+
or other resources your actions need
|
|
209
|
+
end note
|
|
210
|
+
|
|
211
|
+
note right of ShuttingDown
|
|
212
|
+
Override onShutdown() to close
|
|
213
|
+
connections and release resources
|
|
214
|
+
end note
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## File Layout
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
ultravisor-beacon-capability/
|
|
221
|
+
package.json
|
|
222
|
+
README.md
|
|
223
|
+
source/
|
|
224
|
+
Ultravisor-Beacon-Capability.cjs # Base class (main export)
|
|
225
|
+
Ultravisor-Beacon-Capability-ActionMap.cjs # Action discovery helper
|
|
226
|
+
test/
|
|
227
|
+
Ultravisor-Beacon-Capability_tests.js # Mocha TDD tests
|
|
228
|
+
docs/
|
|
229
|
+
README.md # Overview
|
|
230
|
+
_cover.md # Landing page
|
|
231
|
+
_sidebar.md # Navigation
|
|
232
|
+
_topbar.md # Top bar
|
|
233
|
+
quickstart.md # Step-by-step guide
|
|
234
|
+
architecture.md # This file
|
|
235
|
+
api/ # API reference
|
|
236
|
+
examples/ # Real-world examples
|
|
237
|
+
```
|