@jskit-ai/realtime 0.1.156 → 0.1.158
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/package.json +25 -69
- package/patterns/realtime-application/PATTERN.md +69 -0
- package/patterns/realtime-application/example/.env.example +1 -0
- package/patterns/realtime-application/example/package.json +8 -0
- package/patterns/realtime-application/example/src/placement.js +17 -0
- package/src/client/RealtimeClientProvider.js +81 -203
- package/src/server/RealtimeProvider.js +98 -0
- package/src/server/realtimeAudience.js +220 -0
- package/src/server/realtimeDelivery.js +41 -0
- package/test/entrypoints.boundary.test.js +6 -12
- package/test/providerRuntime.test.js +187 -730
- package/test/realtimeApplicationPattern.test.js +16 -0
- package/src/client/listeners.js +0 -68
- package/src/server/RealtimeServiceProvider.js +0 -764
- package/test/clientListeners.test.js +0 -66
package/package.json
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/realtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.158",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
7
7
|
},
|
|
8
8
|
"exports": {
|
|
9
|
-
"./server/
|
|
9
|
+
"./server/RealtimeProvider": "./src/server/RealtimeProvider.js",
|
|
10
10
|
"./server/runtime": "./src/server/runtime.js",
|
|
11
11
|
"./client": "./src/client/RealtimeClientProvider.js",
|
|
12
12
|
"./client/RealtimeClientProvider": "./src/client/RealtimeClientProvider.js",
|
|
13
|
-
"./client/listeners": "./src/client/listeners.js",
|
|
14
13
|
"./client/composables/useRealtimeEvent": "./src/client/composables/useRealtimeEvent.js",
|
|
15
14
|
"./client/runtime": "./src/client/runtime.js"
|
|
16
15
|
},
|
|
17
16
|
"dependencies": {
|
|
18
|
-
"@jskit-ai/kernel": "0.1.
|
|
19
|
-
"@jskit-ai/shell-web": "0.1.
|
|
17
|
+
"@jskit-ai/kernel": "0.1.161",
|
|
18
|
+
"@jskit-ai/shell-web": "0.1.165",
|
|
20
19
|
"@socket.io/redis-adapter": "^8.3.0",
|
|
21
20
|
"redis": "^5.8.2",
|
|
22
21
|
"socket.io": "^4.8.3",
|
|
@@ -28,30 +27,25 @@
|
|
|
28
27
|
"description": "Thin, generic realtime runtime wrappers for socket.io server and client.",
|
|
29
28
|
"jskit": {
|
|
30
29
|
"kind": "runtime",
|
|
31
|
-
"options": {
|
|
32
|
-
"realtime-redis-url": {
|
|
33
|
-
"required": true,
|
|
34
|
-
"allowEmpty": true,
|
|
35
|
-
"values": [],
|
|
36
|
-
"defaultValue": "",
|
|
37
|
-
"promptLabel": "Realtime Redis URL",
|
|
38
|
-
"promptHint": "Leave empty to use in-memory socket adapter."
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
30
|
"capabilities": {
|
|
42
31
|
"provides": [
|
|
43
32
|
"runtime.realtime",
|
|
44
33
|
"runtime.realtime.client"
|
|
45
34
|
],
|
|
46
|
-
"requires": [
|
|
35
|
+
"requires": [
|
|
36
|
+
"runtime.config",
|
|
37
|
+
"runtime.env",
|
|
38
|
+
"runtime.events",
|
|
39
|
+
"runtime.fastify",
|
|
40
|
+
"runtime.logger"
|
|
41
|
+
]
|
|
47
42
|
},
|
|
48
43
|
"runtime": {
|
|
49
44
|
"server": {
|
|
50
|
-
"providerEntrypoint": "src/server/RealtimeServiceProvider.js",
|
|
51
45
|
"providers": [
|
|
52
46
|
{
|
|
53
|
-
"entrypoint": "src/server/
|
|
54
|
-
"export": "
|
|
47
|
+
"entrypoint": "src/server/RealtimeProvider.js",
|
|
48
|
+
"export": "RealtimeProvider"
|
|
55
49
|
}
|
|
56
50
|
]
|
|
57
51
|
},
|
|
@@ -65,33 +59,34 @@
|
|
|
65
59
|
}
|
|
66
60
|
},
|
|
67
61
|
"metadata": {
|
|
62
|
+
"client": {
|
|
63
|
+
"optimizeDeps": {
|
|
64
|
+
"include": [
|
|
65
|
+
"socket.io-client"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
},
|
|
68
69
|
"apiSummary": {
|
|
69
70
|
"surfaces": [
|
|
70
71
|
{
|
|
71
72
|
"subpath": "./server",
|
|
72
|
-
"summary": "Exports
|
|
73
|
+
"summary": "Exports the explicit RealtimeProvider capability provider."
|
|
73
74
|
},
|
|
74
75
|
{
|
|
75
76
|
"subpath": "./client",
|
|
76
77
|
"summary": "Exports RealtimeClientProvider only."
|
|
77
78
|
},
|
|
78
|
-
{
|
|
79
|
-
"subpath": "./client/listeners",
|
|
80
|
-
"summary": "Exports client listener registration helpers for provider-level realtime subscriptions."
|
|
81
|
-
},
|
|
82
79
|
{
|
|
83
80
|
"subpath": "./client/composables/*",
|
|
84
81
|
"summary": "Exports component-level realtime socket composables."
|
|
85
82
|
}
|
|
86
83
|
],
|
|
87
|
-
"
|
|
84
|
+
"capabilities": {
|
|
88
85
|
"server": [
|
|
89
|
-
"runtime.realtime"
|
|
90
|
-
"runtime.realtime.io"
|
|
86
|
+
"runtime.realtime"
|
|
91
87
|
],
|
|
92
88
|
"client": [
|
|
93
|
-
"
|
|
94
|
-
"runtime.realtime.client.socket",
|
|
89
|
+
"client.realtime",
|
|
95
90
|
"realtime.web.connection.indicator"
|
|
96
91
|
]
|
|
97
92
|
}
|
|
@@ -109,7 +104,7 @@
|
|
|
109
104
|
],
|
|
110
105
|
"order": 950,
|
|
111
106
|
"componentToken": "realtime.web.connection.indicator",
|
|
112
|
-
"source": "
|
|
107
|
+
"source": "patterns/realtime-application/example/src/placement.js"
|
|
113
108
|
}
|
|
114
109
|
]
|
|
115
110
|
}
|
|
@@ -122,45 +117,6 @@
|
|
|
122
117
|
"ws": true
|
|
123
118
|
}
|
|
124
119
|
}
|
|
125
|
-
},
|
|
126
|
-
"mutations": {
|
|
127
|
-
"dependencies": {
|
|
128
|
-
"runtime": {
|
|
129
|
-
"@jskit-ai/kernel": "0.1.159",
|
|
130
|
-
"@jskit-ai/shell-web": "0.1.163",
|
|
131
|
-
"@socket.io/redis-adapter": "^8.3.0",
|
|
132
|
-
"redis": "^5.8.2",
|
|
133
|
-
"socket.io": "^4.8.3",
|
|
134
|
-
"socket.io-client": "^4.8.3"
|
|
135
|
-
},
|
|
136
|
-
"dev": {}
|
|
137
|
-
},
|
|
138
|
-
"packageJson": {
|
|
139
|
-
"scripts": {}
|
|
140
|
-
},
|
|
141
|
-
"procfile": {},
|
|
142
|
-
"files": [],
|
|
143
|
-
"text": [
|
|
144
|
-
{
|
|
145
|
-
"file": ".env",
|
|
146
|
-
"op": "upsert-env",
|
|
147
|
-
"key": "REALTIME_REDIS_URL",
|
|
148
|
-
"value": "${option:realtime-redis-url}",
|
|
149
|
-
"reason": "Configure optional Redis backplane URL for realtime socket adapter.",
|
|
150
|
-
"category": "runtime-config",
|
|
151
|
-
"id": "realtime-redis-url"
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
"op": "append-text",
|
|
155
|
-
"file": "src/placement.js",
|
|
156
|
-
"position": "bottom",
|
|
157
|
-
"skipIfContains": "id: \"realtime.connection.indicator\"",
|
|
158
|
-
"value": "\naddPlacement({\n id: \"realtime.connection.indicator\",\n target: \"shell.status\",\n kind: \"component\",\n surfaces: [\"*\"],\n order: 950,\n componentToken: \"realtime.web.connection.indicator\"\n});\n",
|
|
159
|
-
"reason": "Append realtime connection indicator placement into app-owned placement registry.",
|
|
160
|
-
"category": "realtime-web",
|
|
161
|
-
"id": "realtime-placement-indicator"
|
|
162
|
-
}
|
|
163
|
-
]
|
|
164
120
|
}
|
|
165
121
|
}
|
|
166
122
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: realtime/realtime-application
|
|
3
|
+
title: Realtime application
|
|
4
|
+
summary: Add JSKIT realtime events with an optional Redis backplane and an explicit shell status contribution.
|
|
5
|
+
keywords: redis, realtime, socket.io, sockets, status, websocket
|
|
6
|
+
requires: @jskit-ai/realtime, @jskit-ai/shell-web
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Realtime application
|
|
10
|
+
|
|
11
|
+
## Use when
|
|
12
|
+
|
|
13
|
+
Use this pattern when the product needs server-to-client events or live query
|
|
14
|
+
refresh. Install the realtime package normally, then add only the product's
|
|
15
|
+
listeners and desired shell contribution.
|
|
16
|
+
|
|
17
|
+
## Do not use when
|
|
18
|
+
|
|
19
|
+
Do not add realtime merely to poll less often, or before event ownership and
|
|
20
|
+
delivery semantics are clear. A single-process application does not need Redis.
|
|
21
|
+
|
|
22
|
+
## Product decisions
|
|
23
|
+
|
|
24
|
+
Decide which events are public contracts, their scopes and payloads, reconnect
|
|
25
|
+
behavior, whether a connection indicator belongs in the UI, and whether the
|
|
26
|
+
deployment needs a Redis backplane.
|
|
27
|
+
|
|
28
|
+
## Invariants
|
|
29
|
+
|
|
30
|
+
- Empty `REALTIME_REDIS_URL` means the in-process adapter.
|
|
31
|
+
- Redis credentials stay outside Git.
|
|
32
|
+
- Listeners are registered through the public provider seams.
|
|
33
|
+
- The status indicator is an explicit app placement, not a source mutation.
|
|
34
|
+
- Event payloads do not become an undocumented second API.
|
|
35
|
+
|
|
36
|
+
## Framework APIs
|
|
37
|
+
|
|
38
|
+
Server features declare their successful domain events on their actions. The
|
|
39
|
+
installed `RealtimeProvider` delivers only events with an explicit realtime
|
|
40
|
+
name and audience. Client features use `RealtimeClientProvider` and the public
|
|
41
|
+
listener registration helpers from `@jskit-ai/realtime`. Use the normal shell
|
|
42
|
+
placement registry for the optional status component.
|
|
43
|
+
|
|
44
|
+
## Example files
|
|
45
|
+
|
|
46
|
+
`example/package.json` declares realtime and shell runtime packages.
|
|
47
|
+
`example/.env.example` documents the optional Redis input.
|
|
48
|
+
`example/src/placement.js` adds the standard connection indicator explicitly.
|
|
49
|
+
|
|
50
|
+
## Variation points
|
|
51
|
+
|
|
52
|
+
Omit the placement when the product does not need a visible connection state.
|
|
53
|
+
Change event listeners, Redis provisioning, and client invalidation behavior to
|
|
54
|
+
match the product. Keep transport retry policy in the realtime runtime.
|
|
55
|
+
|
|
56
|
+
## Verification
|
|
57
|
+
|
|
58
|
+
- Test in-process delivery without Redis.
|
|
59
|
+
- When Redis is selected, test delivery across two server processes.
|
|
60
|
+
- Disconnect and reconnect a browser and verify recovery behavior.
|
|
61
|
+
- Confirm the status contribution renders in compact and expanded shells.
|
|
62
|
+
|
|
63
|
+
## Avoid
|
|
64
|
+
|
|
65
|
+
- prompting for optional Redis configuration during package installation
|
|
66
|
+
- appending source into placement files
|
|
67
|
+
- leaking Redis values to client config
|
|
68
|
+
- generic “data changed” payloads with no scope
|
|
69
|
+
- receipts, provenance, or mutation history
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
REALTIME_REDIS_URL=
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createPlacementRegistry } from "@jskit-ai/shell-web/client/placement";
|
|
2
|
+
|
|
3
|
+
const registry = createPlacementRegistry();
|
|
4
|
+
const { addPlacement } = registry;
|
|
5
|
+
|
|
6
|
+
addPlacement({
|
|
7
|
+
id: "realtime.connection.indicator",
|
|
8
|
+
target: "shell.status",
|
|
9
|
+
kind: "component",
|
|
10
|
+
surfaces: ["*"],
|
|
11
|
+
order: 950,
|
|
12
|
+
componentToken: "realtime.web.connection.indicator"
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default function getPlacements() {
|
|
16
|
+
return registry.build();
|
|
17
|
+
}
|
|
@@ -1,37 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
|
|
2
2
|
import { normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
-
import { createProviderLogger
|
|
3
|
+
import { createProviderLogger } from "@jskit-ai/kernel/shared/support/providerLogger";
|
|
4
4
|
import { getClientAppConfig, resolveClientBootstrapDebugEnabled, resolveMobileConfig } from "@jskit-ai/kernel/client";
|
|
5
5
|
import RealtimeConnectionIndicator from "./components/RealtimeConnectionIndicator.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createSocketIoClient, disconnectSocketIoClient } from "./runtime.js";
|
|
7
7
|
|
|
8
8
|
const REALTIME_RUNTIME_CLIENT_API = Object.freeze({
|
|
9
9
|
createSocketIoClient,
|
|
10
10
|
disconnectSocketIoClient
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
function
|
|
14
|
-
if (!app || typeof app.has !== "function" || typeof app.make !== "function") {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
if (app.has("mobile.capacitor.adapter.client") !== true) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const adapter = app.make("mobile.capacitor.adapter.client");
|
|
22
|
-
return adapter?.available === true;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolveRealtimeClientConfig(app) {
|
|
13
|
+
function resolveRealtimeClientConfig({ env = {}, mobile = null } = {}) {
|
|
26
14
|
const appConfig = normalizeObject(getClientAppConfig());
|
|
27
|
-
const env = app && typeof app.has === "function" && app.has("jskit.client.env") ? normalizeObject(app.make("jskit.client.env")) : {};
|
|
28
15
|
const realtime = normalizeObject(appConfig.realtime);
|
|
29
16
|
const realtimeClient = normalizeObject(appConfig.realtimeClient);
|
|
30
|
-
const mobileConfig = resolveMobileConfig({
|
|
31
|
-
mobile: normalizeObject(appConfig.mobile)
|
|
32
|
-
});
|
|
17
|
+
const mobileConfig = resolveMobileConfig({ mobile: normalizeObject(appConfig.mobile) });
|
|
33
18
|
const url = normalizeText(
|
|
34
|
-
realtimeClient.url || (mobileConfig.enabled === true &&
|
|
19
|
+
realtimeClient.url || (mobileConfig.enabled === true && mobile?.adapter?.available === true ? mobileConfig.apiBaseUrl : "")
|
|
35
20
|
);
|
|
36
21
|
const options = normalizeObject(realtimeClient.options);
|
|
37
22
|
const explicitDebugEnabled =
|
|
@@ -40,7 +25,7 @@ function resolveRealtimeClientConfig(app) {
|
|
|
40
25
|
: typeof realtime.debug === "boolean"
|
|
41
26
|
? realtime.debug
|
|
42
27
|
: undefined;
|
|
43
|
-
const hasRealtimeDebugEnvOverride = Object.hasOwn(env, "VITE_REALTIME_DEBUG");
|
|
28
|
+
const hasRealtimeDebugEnvOverride = Object.hasOwn(normalizeObject(env), "VITE_REALTIME_DEBUG");
|
|
44
29
|
const debugEnabled = hasRealtimeDebugEnvOverride
|
|
45
30
|
? resolveClientBootstrapDebugEnabled({
|
|
46
31
|
env,
|
|
@@ -53,221 +38,114 @@ function resolveRealtimeClientConfig(app) {
|
|
|
53
38
|
debugEnvKey: "VITE_REALTIME_DEBUG"
|
|
54
39
|
});
|
|
55
40
|
|
|
56
|
-
return Object.freeze({
|
|
57
|
-
url,
|
|
58
|
-
options,
|
|
59
|
-
debugEnabled
|
|
60
|
-
});
|
|
41
|
+
return Object.freeze({ url, options, debugEnabled });
|
|
61
42
|
}
|
|
62
43
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
app.singleton("runtime.realtime.client.socket", (scope) => {
|
|
74
|
-
const realtimeRuntime = scope.make("runtime.realtime.client");
|
|
75
|
-
const realtimeClientConfig = resolveRealtimeClientConfig(scope);
|
|
76
|
-
return realtimeRuntime.createSocketIoClient({
|
|
77
|
-
url: realtimeClientConfig.url,
|
|
78
|
-
options: realtimeClientConfig.options
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
}
|
|
44
|
+
function createRealtimeClient({ config, loggerInput } = {}) {
|
|
45
|
+
const logger = createProviderLogger(loggerInput, {
|
|
46
|
+
debugEnabled: config.debugEnabled
|
|
47
|
+
});
|
|
48
|
+
const socket = createSocketIoClient({
|
|
49
|
+
url: config.url,
|
|
50
|
+
options: config.options
|
|
51
|
+
});
|
|
52
|
+
const detach = [];
|
|
53
|
+
let initialized = false;
|
|
82
54
|
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
|
|
55
|
+
function initialize(vueApp = null) {
|
|
56
|
+
if (initialized) {
|
|
57
|
+
return;
|
|
86
58
|
}
|
|
87
|
-
|
|
88
|
-
const realtimeClientConfig = resolveRealtimeClientConfig(app);
|
|
89
|
-
const logger = createSharedProviderLogger(app, {
|
|
90
|
-
debugEnabled: realtimeClientConfig.debugEnabled
|
|
91
|
-
});
|
|
92
|
-
const socket = app.make("runtime.realtime.client.socket");
|
|
93
|
-
const listeners = resolveRealtimeClientListeners(app);
|
|
94
|
-
const detach = [];
|
|
95
|
-
|
|
96
|
-
logger.debug(
|
|
97
|
-
{
|
|
98
|
-
providerId: RealtimeClientProvider.id,
|
|
99
|
-
listenerCount: listeners.length,
|
|
100
|
-
listeners: listeners.map((listener) => ({
|
|
101
|
-
listenerId: listener.listenerId,
|
|
102
|
-
event: listener.event
|
|
103
|
-
}))
|
|
104
|
-
},
|
|
105
|
-
"Realtime client booted listeners."
|
|
106
|
-
);
|
|
59
|
+
initialized = true;
|
|
107
60
|
|
|
108
61
|
if (typeof socket.on === "function") {
|
|
109
|
-
const onConnect = () => {
|
|
110
|
-
|
|
111
|
-
{
|
|
112
|
-
providerId: RealtimeClientProvider.id,
|
|
113
|
-
socketConnected: true
|
|
114
|
-
},
|
|
115
|
-
"Realtime client socket connected."
|
|
116
|
-
);
|
|
117
|
-
};
|
|
118
|
-
const onDisconnect = (reason) => {
|
|
62
|
+
const onConnect = () => logger.debug({ socketConnected: true }, "Realtime client socket connected.");
|
|
63
|
+
const onDisconnect = (reason) =>
|
|
119
64
|
logger.debug(
|
|
120
|
-
{
|
|
121
|
-
providerId: RealtimeClientProvider.id,
|
|
122
|
-
socketConnected: false,
|
|
123
|
-
reason: String(reason || "")
|
|
124
|
-
},
|
|
65
|
+
{ socketConnected: false, reason: String(reason || "") },
|
|
125
66
|
"Realtime client socket disconnected."
|
|
126
67
|
);
|
|
127
|
-
|
|
128
|
-
const onConnectError = (error) => {
|
|
68
|
+
const onConnectError = (error) =>
|
|
129
69
|
logger.warn(
|
|
130
|
-
{
|
|
131
|
-
providerId: RealtimeClientProvider.id,
|
|
132
|
-
error: String(error?.message || error || "unknown error")
|
|
133
|
-
},
|
|
70
|
+
{ error: String(error?.message || error || "unknown error") },
|
|
134
71
|
"Realtime client socket connect error."
|
|
135
72
|
);
|
|
136
|
-
};
|
|
137
73
|
|
|
138
74
|
socket.on("connect", onConnect);
|
|
139
75
|
socket.on("disconnect", onDisconnect);
|
|
140
76
|
socket.on("connect_error", onConnectError);
|
|
141
77
|
detach.push(() => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
socket.off("connect_error", onConnectError);
|
|
146
|
-
}
|
|
78
|
+
socket.off?.("connect", onConnect);
|
|
79
|
+
socket.off?.("disconnect", onDisconnect);
|
|
80
|
+
socket.off?.("connect_error", onConnectError);
|
|
147
81
|
});
|
|
148
82
|
|
|
149
|
-
if (
|
|
150
|
-
const
|
|
83
|
+
if (config.debugEnabled === true && typeof socket.onAny === "function") {
|
|
84
|
+
const onAny = (eventName, payload) =>
|
|
151
85
|
logger.debug(
|
|
152
86
|
{
|
|
153
|
-
providerId: RealtimeClientProvider.id,
|
|
154
87
|
event: String(eventName || ""),
|
|
155
88
|
payloadScope: payload?.scope || null,
|
|
156
89
|
payloadEntityId: payload?.entityId || null
|
|
157
90
|
},
|
|
158
91
|
"Realtime client received socket event."
|
|
159
92
|
);
|
|
160
|
-
|
|
161
|
-
socket.onAny
|
|
162
|
-
detach.push(() => {
|
|
163
|
-
if (typeof socket.offAny === "function") {
|
|
164
|
-
socket.offAny(onAnyDebug);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
for (const listener of listeners) {
|
|
171
|
-
const invoke = (eventName, payload) => {
|
|
172
|
-
const context = Object.freeze({
|
|
173
|
-
event: eventName,
|
|
174
|
-
payload,
|
|
175
|
-
socket,
|
|
176
|
-
app
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
if (listener.matches && listener.matches(context) !== true) {
|
|
180
|
-
logger.debug(
|
|
181
|
-
{
|
|
182
|
-
listenerId: listener.listenerId,
|
|
183
|
-
event: eventName
|
|
184
|
-
},
|
|
185
|
-
"Realtime client listener skipped event by matches()."
|
|
186
|
-
);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
logger.debug(
|
|
191
|
-
{
|
|
192
|
-
listenerId: listener.listenerId,
|
|
193
|
-
event: eventName,
|
|
194
|
-
payloadScope: payload?.scope || null,
|
|
195
|
-
payloadEntityId: payload?.entityId || null
|
|
196
|
-
},
|
|
197
|
-
"Realtime client listener handling event."
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
Promise.resolve(listener.handle(context)).catch((error) => {
|
|
201
|
-
logger.error(
|
|
202
|
-
{
|
|
203
|
-
listenerId: listener.listenerId,
|
|
204
|
-
event: eventName,
|
|
205
|
-
error: String(error?.message || error || "unknown error")
|
|
206
|
-
},
|
|
207
|
-
"Realtime client listener failed."
|
|
208
|
-
);
|
|
209
|
-
});
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
if (listener.event === "*") {
|
|
213
|
-
if (typeof socket.onAny === "function") {
|
|
214
|
-
const onAny = (eventName, payload) => invoke(eventName, payload);
|
|
215
|
-
socket.onAny(onAny);
|
|
216
|
-
detach.push(() => {
|
|
217
|
-
if (typeof socket.offAny === "function") {
|
|
218
|
-
socket.offAny(onAny);
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
continue;
|
|
93
|
+
socket.onAny(onAny);
|
|
94
|
+
detach.push(() => socket.offAny?.(onAny));
|
|
223
95
|
}
|
|
224
|
-
|
|
225
|
-
if (typeof socket.on === "function") {
|
|
226
|
-
const onEvent = (payload) => invoke(listener.event, payload);
|
|
227
|
-
socket.on(listener.event, onEvent);
|
|
228
|
-
detach.push(() => {
|
|
229
|
-
if (typeof socket.off === "function") {
|
|
230
|
-
socket.off(listener.event, onEvent);
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
this.socket = socket;
|
|
237
|
-
this.detach = detach;
|
|
238
|
-
|
|
239
|
-
if (!app.has("jskit.client.vue.app")) {
|
|
240
|
-
return;
|
|
241
96
|
}
|
|
242
97
|
|
|
243
|
-
|
|
244
|
-
if (!vueApp || typeof vueApp.provide !== "function") {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
vueApp.provide("jskit.realtime.runtime.client.socket", socket);
|
|
98
|
+
vueApp?.provide?.("jskit.realtime.runtime.client.socket", socket);
|
|
248
99
|
}
|
|
249
100
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
release();
|
|
256
|
-
} catch {}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
101
|
+
function dispose() {
|
|
102
|
+
for (const release of detach.splice(0, detach.length)) {
|
|
103
|
+
try {
|
|
104
|
+
release();
|
|
105
|
+
} catch {}
|
|
259
106
|
}
|
|
260
|
-
|
|
107
|
+
disconnectSocketIoClient(socket);
|
|
108
|
+
initialized = false;
|
|
109
|
+
}
|
|
261
110
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
111
|
+
return Object.freeze({
|
|
112
|
+
...REALTIME_RUNTIME_CLIENT_API,
|
|
113
|
+
config,
|
|
114
|
+
dispose,
|
|
115
|
+
initialize,
|
|
116
|
+
socket
|
|
117
|
+
});
|
|
118
|
+
}
|
|
265
119
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
120
|
+
const RealtimeClientProvider = defineProvider({
|
|
121
|
+
id: "runtime.realtime.client",
|
|
122
|
+
requires: {
|
|
123
|
+
components: "client.components",
|
|
124
|
+
env: "client.env",
|
|
125
|
+
logger: "client.logger",
|
|
126
|
+
vueApp: "client.vue"
|
|
127
|
+
},
|
|
128
|
+
optional: {
|
|
129
|
+
mobile: "client.mobile"
|
|
130
|
+
},
|
|
131
|
+
provides: {
|
|
132
|
+
realtime: "client.realtime"
|
|
133
|
+
},
|
|
134
|
+
setup({ components, env, logger, mobile }) {
|
|
135
|
+
components.register("realtime.web.connection.indicator", RealtimeConnectionIndicator);
|
|
136
|
+
return {
|
|
137
|
+
realtime: createRealtimeClient({
|
|
138
|
+
config: resolveRealtimeClientConfig({ env, mobile }),
|
|
139
|
+
loggerInput: logger
|
|
140
|
+
})
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
boot({ vueApp }, { outputs }) {
|
|
144
|
+
outputs.realtime.initialize(vueApp);
|
|
145
|
+
},
|
|
146
|
+
shutdown(_dependencies, { outputs }) {
|
|
147
|
+
outputs.realtime.dispose();
|
|
270
148
|
}
|
|
271
|
-
}
|
|
149
|
+
});
|
|
272
150
|
|
|
273
|
-
export { RealtimeClientProvider };
|
|
151
|
+
export { RealtimeClientProvider, resolveRealtimeClientConfig };
|