@raclettejs/core 0.1.34 → 0.1.36-canary.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/dist/cli.js +86 -82
  3. package/dist/cli.js.map +3 -3
  4. package/package.json +5 -5
  5. package/services/.dockerignore +2 -0
  6. package/services/backend/.dockerignore +3 -1
  7. package/services/backend/src/core/payload/payloadTypes.ts +1 -0
  8. package/services/backend/src/core/pluginSystem/configGenerator/index.ts +12 -11
  9. package/services/backend/src/core/pluginSystem/pluginFastifyInstance.ts +28 -3
  10. package/services/backend/src/core/pluginSystem/pluginLoader.ts +11 -2
  11. package/services/backend/src/core/pluginSystem/pluginTypes.ts +39 -1
  12. package/services/backend/src/core/sockets/createSystemEventHandlers.ts +4 -4
  13. package/services/backend/src/core/sockets/socketManager.ts +80 -32
  14. package/services/backend/src/core/sockets/socketTypes.ts +6 -0
  15. package/services/backend/src/core/validation/schemaGenerator.ts +10 -3
  16. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts +1 -1
  17. package/services/backend/src/corePlugins/raclette__core/backend/index.ts +0 -2
  18. package/services/backend/src/modules/authentication/index.ts +15 -23
  19. package/services/backend/src/shared/helper/adminTag.ts +34 -0
  20. package/services/backend/src/shared/types/plugins/index.ts +1 -9
  21. package/services/backend/src/utils/request.utils.ts +1 -1
  22. package/services/frontend/.dockerignore +3 -1
  23. package/services/frontend/provision/entrypoint.sh +2 -2
  24. package/services/frontend/src/core/helpers/devMode.ts +1 -3
  25. package/services/frontend/src/core/lib/data/dataApi.ts +27 -16
  26. package/services/frontend/src/core/lib/data/queryApi.ts +17 -8
  27. package/services/frontend/src/core/lib/data/responseTypeHandler.ts +17 -8
  28. package/services/frontend/src/core/lib/data/writeDataHandler.ts +3 -1
  29. package/services/frontend/src/core/lib/dataHelper.ts +51 -12
  30. package/services/frontend/src/core/lib/eventWhitelist.ts +2 -0
  31. package/services/frontend/src/core/lib/httpClient.ts +16 -2
  32. package/services/frontend/src/core/lib/logger.ts +2 -3
  33. package/services/frontend/src/core/lib/userNotifier.ts +20 -0
  34. package/services/frontend/src/core/lib/workSessionUrl.ts +16 -0
  35. package/services/frontend/src/core/main.ts +82 -15
  36. package/services/frontend/src/core/setup/socketBootstrap.ts +83 -0
  37. package/services/frontend/src/core/setup/socketClient.ts +39 -39
  38. package/services/frontend/src/core/setup/socketEvents.ts +152 -158
  39. package/services/frontend/src/core/setup/socketRegistry.ts +19 -0
  40. package/services/frontend/src/core/store/index.ts +2 -4
  41. package/services/frontend/src/core/types/DataApi.ts +10 -2
  42. package/services/frontend/src/core/types/PluginRegistrar.ts +1 -1
  43. package/services/frontend/src/orchestrator/ProductOrchestrator.vue +15 -1
  44. package/services/frontend/src/orchestrator/assets/styles/layers.css +4 -1
  45. package/services/frontend/src/orchestrator/components/dataTable/BaseDataTable.vue +267 -35
  46. package/services/frontend/src/orchestrator/components/menu/ServerStatus.vue +11 -2
  47. package/services/frontend/src/orchestrator/composables/useVueQueryObservableHelper.ts +12 -7
  48. package/services/frontend/src/orchestrator/composables/useVueWriteOperationHelper.ts +12 -6
  49. package/services/frontend/src/orchestrator/composables/useWidgetLifecycle.ts +2 -4
  50. package/services/frontend/src/orchestrator/i18n/de-DE.json +1 -0
  51. package/services/frontend/src/orchestrator/i18n/en-EU.json +1 -0
  52. package/services/frontend/src/orchestrator/i18n/sk.json +1 -0
  53. package/services/frontend/src/orchestrator/i18n/tl-TL.json +1 -0
  54. package/services/frontend/src/orchestrator/index.ts +7 -24
  55. package/services/frontend/src/orchestrator/lib/queryParamsSource.ts +48 -0
  56. package/services/frontend/src/orchestrator/main.ts +2 -2
  57. package/services/frontend/src/orchestrator/router/index.ts +0 -19
  58. package/services/frontend/src/orchestrator/router/registerEventHandlers.ts +29 -0
  59. package/services/frontend/src/orchestrator/router/routeStore.ts +92 -17
  60. package/services/frontend/src/orchestrator/router/routes.ts +5 -0
  61. package/services/frontend/src/orchestrator/setup/application.ts +11 -2
  62. package/services/frontend/src/orchestrator/types/DataApi.ts +19 -0
  63. package/services/frontend/src/orchestrator/types/PluginApi.ts +4 -7
  64. package/services/frontend/src/orchestrator/types/Widgets.ts +2 -0
  65. package/services/frontend/src/orchestrator/types/index.ts +1 -0
  66. package/templates/docker-compose.yml +1 -4
  67. package/templates/fs.yml +1 -1
  68. package/package/dist/index.js +0 -452
  69. package/services/backend/.yarn/install-state.gz +0 -0
  70. package/services/backend/dist/core/eventBus/index.js +0 -7
  71. package/services/backend/dist/core/pluginSystem/configGenerator/index.js +0 -346
  72. package/services/backend/dist/core/sockets/index.js +0 -95
  73. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/account/events/index.js +0 -31
  74. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/account/index.js +0 -48
  75. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/account/routes/index.js +0 -15
  76. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/composition/events/index.js +0 -22
  77. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/composition/index.js +0 -51
  78. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/composition/routes/index.js +0 -19
  79. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/interactionLink/events/index.js +0 -22
  80. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/interactionLink/index.js +0 -48
  81. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/index.js +0 -19
  82. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/project/events/index.js +0 -31
  83. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/project/index.js +0 -49
  84. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/project/routes/index.js +0 -16
  85. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/tag/events/index.js +0 -39
  86. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/tag/index.js +0 -50
  87. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/tag/routes/index.js +0 -19
  88. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/user/events/index.js +0 -31
  89. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/user/index.js +0 -51
  90. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/user/routes/index.js +0 -21
  91. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/workSession/events/index.js +0 -31
  92. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/workSession/index.js +0 -48
  93. package/services/backend/dist/corePlugins/raclette__core/backend/datatypes/workSession/routes/index.js +0 -15
  94. package/services/backend/dist/corePlugins/raclette__core/backend/index.js +0 -34
  95. package/services/backend/dist/domains/index.js +0 -11
  96. package/services/backend/dist/domains/system/index.js +0 -11
  97. package/services/backend/dist/domains/system/routes/index.js +0 -17
  98. package/services/backend/dist/helpers/index.js +0 -14
  99. package/services/backend/dist/index.js +0 -3
  100. package/services/backend/dist/modules/authentication/index.js +0 -253
  101. package/services/backend/dist/shared/types/core/index.js +0 -8
  102. package/services/backend/dist/shared/types/dataTypes/index.js +0 -6
  103. package/services/backend/dist/shared/types/index.js +0 -8
  104. package/services/backend/dist/shared/types/plugins/index.js +0 -8
  105. package/services/backend/dist/types/index.js +0 -12
  106. package/services/backend/dist/utils/index.js +0 -2
  107. package/services/frontend/.yarn/install-state.gz +0 -0
@@ -3,59 +3,59 @@
3
3
  * Listens to the event bus and emits accordingly messages to the socket server.
4
4
  */
5
5
  import log from "@racletteCore/lib/logger"
6
+ import { $store } from "@racletteCore"
6
7
  import { getUserToken } from "./sessionHandler"
7
8
  import configService from "@racletteCore/lib/configService"
8
- import { validate } from "uuid"
9
+ import { extractSessionIdFromUrl } from "@racletteCore/lib/workSessionUrl"
10
+ import { getActiveSocket, setActiveSocket } from "./socketRegistry"
11
+ import type { Socket } from "socket.io-client"
9
12
 
10
- /**
11
- * Extract sessionId from URL if present
12
- * Supports racletteJS route format: /:workSession/id={uuid}
13
- */
14
- const extractSessionIdFromUrl = (): string | null => {
15
- const path = window.location.pathname + (window.location.hash || "")
16
- // Match /:workSession/id={uuid} pattern
17
- const workSessionMatch = path.match(/\/:workSession\/id=([^/&#]+)/)
18
-
19
- if (workSessionMatch && workSessionMatch[1]) {
20
- const sessionId = workSessionMatch[1]
21
- // Validate UUID format
22
- if (validate(sessionId)) {
23
- return sessionId
24
- }
25
- }
26
-
27
- return null
28
- }
13
+ let eventbusRegistered = false
29
14
 
30
- const registerSocketClient = ($socket, $eventbus) => {
31
- $eventbus.on("socket/join/room", (roomId?: string) => {
32
- const { requireAuthentication } = configService.getConfig()?.global ?? {
33
- requireAuthentication: true,
34
- }
15
+ const registerSocketClient = ($socket: Socket, $eventbus) => {
16
+ setActiveSocket($socket)
35
17
 
36
- let token: string | null = null
37
- const sessionId = extractSessionIdFromUrl()
18
+ if (eventbusRegistered) {
19
+ return
20
+ }
21
+ eventbusRegistered = true
38
22
 
39
- // Check if we're in a work session context (sessionId in URL indicates work session)
40
- const isWorkSession = !!sessionId
23
+ $eventbus.on(
24
+ "socket/join/room",
25
+ (roomId?: string, options?: { bootstrap?: boolean }) => {
26
+ const { requireAuthentication } = configService.getConfig()?.global ?? {
27
+ requireAuthentication: true,
28
+ }
41
29
 
42
- if (requireAuthentication) {
43
- token = getUserToken()
30
+ const sessionId = extractSessionIdFromUrl()
31
+ const isWorkSession = !!sessionId
44
32
 
45
- // For work sessions, we don't need a token - the sessionId is used for authentication
46
- // The sessionId was already sent during socket handshake in the auth object
47
- if (!token && !isWorkSession) {
33
+ if (requireAuthentication && !isWorkSession && !getUserToken()) {
48
34
  return log.socket("emitJoin", "no token and not a work session")
49
35
  }
50
- }
51
36
 
52
- log.socket("emitJoin", { roomId, token, sessionId, isWorkSession })
53
- $socket.emit("join", { roomId, token })
54
- })
37
+ // Light re-join: after a transport reconnect the project is still in store;
38
+ // skip full bootstrap and let the server use readMain (no roomId).
39
+ const projectId = $store.getState(["project", "_id"]) as string | null
40
+ const isLightRejoin =
41
+ options?.bootstrap === false ||
42
+ (!roomId && !!projectId && options?.bootstrap !== true)
43
+ const bootstrap =
44
+ options?.bootstrap ?? (isLightRejoin ? false : !roomId)
45
+
46
+ log.socket("emitJoin", {
47
+ roomId,
48
+ bootstrap,
49
+ isWorkSession,
50
+ lightRejoin: isLightRejoin,
51
+ })
52
+ getActiveSocket()?.emit("join", { roomId, bootstrap })
53
+ },
54
+ )
55
55
 
56
56
  $eventbus.on("socket/leave/room", (roomId) => {
57
57
  log.socket("emitLeave", { roomId })
58
- $socket.emit("leave", roomId)
58
+ getActiveSocket()?.emit("leave", roomId)
59
59
  })
60
60
  }
61
61
 
@@ -1,91 +1,85 @@
1
- /**
2
- * listen to socket events received from the backend
3
- *
4
- * @param {object} $socket - a socketClient instance
5
- * @param {object} $store - a minirx-store instance, so we can dispatch actions
6
- * @param {object} $eventbus - instance of our custom eventbus
7
- */
8
1
  import type { Composition } from "@shared/types/core/Composition.types"
9
2
  import type { InteractionLink } from "@shared/types/core/InteractionLink.types"
10
3
  import log from "@racletteCore/lib/logger"
11
- import { setupSpace } from "@racletteCore/lib/projectDataHelper"
12
4
  import { handleServerItem } from "@racletteCore/lib/data/dataApi"
13
5
  import * as userNotifier from "@racletteCore/lib/userNotifier"
14
6
  import type { ProjectConfiguration } from "../store/types"
15
- import { validate } from "uuid"
7
+ import type { Socket } from "socket.io-client"
8
+ import { extractSessionIdFromUrl } from "@racletteCore/lib/workSessionUrl"
16
9
  import { setHttpToken, setWorkSessionId } from "@racletteCore/lib/httpClient"
10
+ import { isActiveSocket, setActiveSocket } from "./socketRegistry"
11
+ import { startProjectBootstrap } from "./socketBootstrap"
17
12
 
18
13
  /**
19
- * Extract sessionId from URL if present
20
- * Supports racletteJS route format: /:workSession/id={uuid}
14
+ * Listen to socket events received from the backend.
15
+ *
16
+ * @param $socket - socket.io client instance
17
+ * @param $store - mini-rx store (dispatch actions)
18
+ * @param $eventbus - global event bus
19
+ *
20
+ * Project bootstrap after joinConfirmation is handled in ./socketBootstrap.ts.
21
21
  */
22
- const extractSessionIdFromUrl = (): string | null => {
23
- const path = window.location.pathname + (window.location.hash || "")
24
- // Match /:workSession/id={uuid} pattern
25
- const workSessionMatch = path.match(/\/:workSession\/id=([^/&#]+)/)
26
-
27
- if (workSessionMatch && workSessionMatch[1]) {
28
- const sessionId = workSessionMatch[1]
29
- // Validate UUID format
30
- if (validate(sessionId)) {
31
- return sessionId
32
- }
33
- }
22
+ const registerSocketEvents = ($socket: Socket, $store, $eventbus) => {
23
+ setActiveSocket($socket)
34
24
 
35
- return null
36
- }
25
+ /** Ignore events from a replaced socket instance (see socketRegistry + main.ts teardown). */
26
+ const guardActive = (socket: Socket) => isActiveSocket(socket)
37
27
 
38
- const registerSocketEvents = ($socket, $store, $eventbus) => {
39
- /**
40
- * connect
41
- * fired every time we have a new connection established
42
- */
43
- $socket.on("connect", (data) => {
28
+ // --- Connection lifecycle ---------------------------------------------------
29
+
30
+ /** Fired when a connection is established; triggers room join. */
31
+ $socket.on("connect", () => {
32
+ if (!guardActive($socket)) {
33
+ return
34
+ }
44
35
  log.socket("onConnect", "Socket connection established")
45
- $store.dispatch({
46
- type: "backend/socket/status",
47
- payload: { status: 1 },
48
- })
49
- // @TODO if we re-connect after a drop out we need to subscribe to our project without
50
- // completely setting up the project up, e.g. only re-join project room.
36
+ userNotifier.setServerConnected()
37
+ $eventbus.emit("socket/join/room")
51
38
  })
52
- /**
53
- * disconnect
54
- */
55
- $socket.on("disconnect", () => {
39
+
40
+ /** Enable offline mode here if project config disabled it. */
41
+ $socket.on("disconnect", (reason) => {
42
+ if (!guardActive($socket)) {
43
+ log.socket("onDisconnect", `ignored stale socket instance: ${reason}`)
44
+ return
45
+ }
56
46
  log.socket(
57
47
  "onDisconnect",
58
- "Socket disconnected, Enable offline mode here if project conf disabled it",
48
+ `Socket disconnected (${reason}). Enable offline mode here if project conf disabled it`,
59
49
  )
60
50
  userNotifier.setServerDisconnected()
61
51
  })
52
+
62
53
  $socket.on("connect_error", (err) => {
54
+ if (!guardActive($socket)) {
55
+ return
56
+ }
63
57
  log.socket("onConnectError", err)
64
58
  userNotifier.setServerDisconnected()
65
59
  })
66
- /**
67
- * close
68
- */
60
+
69
61
  $socket.on("close", () => {
62
+ if (!guardActive($socket)) {
63
+ return
64
+ }
70
65
  log.socket("onClose", "socket connection lost")
71
66
  userNotifier.setServerDisconnected()
72
67
  })
68
+
73
69
  /**
74
- * reconnect
70
+ * Reconnect attempt in progress (status 2).
71
+ * connect handler will set online status and re-join the room.
75
72
  */
76
- $socket.on("reconnect", () => {
77
- console.error("not properly tested yet")
78
- log.socket("onReconnect", "Socket reconnect")
79
- $store.dispatch({
80
- type: "backend/socket/status",
81
- payload: { status: 2 },
82
- })
73
+ $socket.on("reconnect", (attempt) => {
74
+ if (!guardActive($socket)) {
75
+ return
76
+ }
77
+ log.socket("onReconnect", `Socket reconnect (attempt ${attempt})`)
78
+ userNotifier.setServerConnecting()
83
79
  })
84
80
 
85
- /**
86
- * hello
87
- * const data = { id: 'you'session'id' }
88
- */
81
+ /** Server assigned this socket an id
82
+ * payload: { id: string } */
89
83
  $socket.on("socketEstablished", (data) => {
90
84
  log.socket("onSocketEstablished", data)
91
85
  $store.dispatch({
@@ -94,9 +88,8 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
94
88
  })
95
89
  })
96
90
 
97
- /**
98
- * tagDeleted
99
- */
91
+ // --- Entity sync (server → store / event bus) ------------------------------
92
+
100
93
  $socket.on("tagDeleted", (payload) => {
101
94
  const { appSessionId, body, type } = payload
102
95
 
@@ -112,9 +105,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
112
105
  body,
113
106
  })
114
107
  })
115
- /**
116
- * tagCreated
117
- */
108
+
118
109
  $socket.on("tagCreated", (payload) => {
119
110
  const { appSessionId, body, type } = payload
120
111
 
@@ -130,9 +121,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
130
121
  body,
131
122
  })
132
123
  })
133
- /**
134
- * userCreated
135
- */
124
+
136
125
  $socket.on("userCreated", (payload) => {
137
126
  const { appSessionId, body, type } = payload
138
127
 
@@ -143,9 +132,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
143
132
  })
144
133
  $eventbus.emit("socket_createdUser", { offline: false, body })
145
134
  })
146
- /**
147
- * userDeleted
148
- */
135
+
149
136
  $socket.on("userDeleted", (payload) => {
150
137
  const { appSessionId, body, type } = payload
151
138
  log.socket("onUserDeleted", {
@@ -155,9 +142,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
155
142
  })
156
143
  $eventbus.emit("socket_deletedUser", { offline: false, body })
157
144
  })
158
- /**
159
- * projectUpdated
160
- */
145
+
161
146
  $socket.on("projectUpdated", (payload) => {
162
147
  const { appSessionId, body, type } = payload
163
148
 
@@ -168,6 +153,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
168
153
  })
169
154
  handleServerItem(payload, true, false)
170
155
  })
156
+
171
157
  $socket.on("projectConfigUpdated", (payload) => {
172
158
  const { appSessionId, body, type } = payload
173
159
 
@@ -180,9 +166,8 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
180
166
  })
181
167
 
182
168
  /**
183
- * project/list
184
- * Reports or updates the list of available projects
185
- * This usually gets called after we requested a list of all projects which the user has access too
169
+ * Reports or updates the list of available projects.
170
+ * Usually sent after the client requested projects the user has access to.
186
171
  */
187
172
  $socket.on("getProjectList", (data) => {
188
173
  if (!data || data.length < 1) {
@@ -192,129 +177,141 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
192
177
  $store.dispatch({ type: "projects/add", payload: data })
193
178
  })
194
179
 
180
+ // --- Room join / leave ------------------------------------------------------
181
+
182
+ $socket.on("joinFailed", (payload: { roomId?: string; code?: string; message?: string }) => {
183
+ if (!guardActive($socket)) {
184
+ return
185
+ }
186
+ log.socket("onJoinFailed", payload)
187
+ // Room-scoped failure: transport may still work. A snackbar alone is not enough —
188
+ // surfacing this as a hard error would blank the page while HTTP/socket stay up.
189
+ // TODO: targeted UX once space-switch / permission flows are defined.
190
+ if (!$socket.connected) {
191
+ userNotifier.setServerDisconnected()
192
+ }
193
+ })
194
+
195
195
  /**
196
- * on joinConfirmation this means that we entered a new project with a new scope!
197
- * For work sessions, the payload structure is different: { session, composition }
196
+ * Entered a project room (new scope). bootstrap=false is a light re-join after reconnect.
197
+ * Work sessions: no project in payload, but compositions/interactionLinks (+ optional token).
198
198
  */
199
199
  $socket.on(
200
200
  "joinConfirmation",
201
201
  (payload: {
202
+ roomId?: string
203
+ bootstrap?: boolean
202
204
  project?: { _id: string; account: string }
203
205
  projectConfig?: ProjectConfiguration
204
206
  compositions?: Composition[]
205
207
  interactionLinks?: InteractionLink[]
206
- token?: string // Work session JWT token
208
+ token?: string // Work session JWT — replaces x-work-session-id header
207
209
  }) => {
208
- const { project, projectConfig, compositions, interactionLinks, token } = payload
210
+ if (!guardActive($socket)) {
211
+ return
212
+ }
213
+
214
+ const {
215
+ project,
216
+ projectConfig,
217
+ compositions,
218
+ interactionLinks,
219
+ token,
220
+ bootstrap = true,
221
+ } = payload
209
222
 
210
- // Check if this is a work session (no project but has compositions/interactionLinks)
211
223
  const isWorkSession = !project && compositions && compositions.length > 0
212
224
 
213
225
  if (isWorkSession) {
214
- // Work session: use session ID as room identifier
215
226
  const sessionId = extractSessionIdFromUrl() || "work_session"
216
227
  const resolveId = "join_session_" + sessionId
217
228
 
218
229
  log.socket("onJoinConfirmation", "Work session join", payload)
219
230
 
220
- // If token is provided, use it for authentication (replaces x-work-session-id header)
221
231
  if (token) {
222
232
  setHttpToken(token)
223
- // Clear the x-work-session-id header since we're using token now
224
233
  setWorkSessionId(null)
225
- log.socket("onJoinConfirmation", "Work session token set", { hasToken: !!token })
226
- }
227
-
228
- $eventbus.once("store_actionResolved_" + resolveId, async (action) => {
229
- $store.dispatch({
230
- type: "ui/update",
231
- payload: { loading: true },
234
+ log.socket("onJoinConfirmation", "Work session token set", {
235
+ hasToken: !!token,
232
236
  })
237
+ }
233
238
 
234
- // Use the same setupSpace function as logged-in users
235
- // This ensures all normal processing happens (landing page detection, etc.)
236
- await setupSpace(compositions || [], interactionLinks || [], projectConfig || {})
237
-
238
- // Don't navigate - let the default composition loading logic handle it
239
- // The URL stays as /:workSession/id={uuid} and the router will match
240
- // the composition from the store based on the default logic
241
-
242
- // Enable fullscreen mode (hide nav bars) for work sessions
243
- $store.dispatch({
244
- type: "ui/update",
245
- payload: {
246
- availableInterfaces: [], // Hide all UI interfaces for fullscreen
247
- navigationOpen: false,
239
+ startProjectBootstrap(
240
+ { store: $store, eventbus: $eventbus },
241
+ resolveId,
242
+ {
243
+ compositions,
244
+ interactionLinks,
245
+ projectConfig,
246
+ roomId: sessionId,
247
+ afterSetup: async () => {
248
+ // Fullscreen work session — hide chrome; URL stays /:workSession/id={uuid}
249
+ // and the router resolves composition from store.
250
+ $store.dispatch({
251
+ type: "ui/update",
252
+ payload: {
253
+ availableInterfaces: [],
254
+ navigationOpen: false,
255
+ },
256
+ })
248
257
  },
249
- })
258
+ },
259
+ )
250
260
 
251
- $store.dispatch({
252
- type: "ui/update",
253
- payload: { loading: false },
254
- })
261
+ return
262
+ }
255
263
 
256
- $eventbus.emit("ui/setupProject", sessionId)
257
- })
264
+ if (!project) {
265
+ log.socket("onJoinConfirmation", "no Room")
266
+ return
267
+ }
258
268
 
259
- // Reset store states for work session
260
- $store.dispatch({
261
- type: "projectInit",
262
- resolveId,
263
- })
269
+ const roomId = payload.roomId ?? project._id
264
270
 
271
+ if (!roomId) {
272
+ log.socket("onJoinConfirmation", "no Room id")
265
273
  return
266
274
  }
267
275
 
268
- // Regular project join confirmation (existing flow)
269
- const roomId = project?._id
270
-
271
- if (!project) {
272
- log.socket("onJoinConfirmation", "no Room")
276
+ if (bootstrap === false) {
277
+ log.socket(
278
+ "onJoinConfirmation",
279
+ `Joined room without bootstrap: ${roomId}`,
280
+ )
281
+ userNotifier.setServerConnected()
273
282
  return
274
283
  }
275
284
 
276
285
  log.socket("onJoinConfirmation", payload)
277
286
 
278
- const resolveId = "join_" + roomId
279
-
280
- $eventbus.once("store_actionResolved_" + resolveId, async (action) => {
281
- // start bootstraping of project
282
- // this event will be fired once the projectInit is finished which gets triggered down there
283
- $store.dispatch({
284
- type: "project/set",
285
- payload: project,
286
- })
287
- $store.dispatch({
288
- type: "ui/update",
289
- payload: { loading: true },
290
- })
291
- await setupSpace(compositions || [], interactionLinks || [], projectConfig || {})
292
-
293
- $eventbus.emit("ui/setupProject", roomId)
294
- })
295
- // Reset all store states which are project dependent.
296
- $store.dispatch({
297
- type: "projectInit",
298
- resolveId,
299
- })
287
+ startProjectBootstrap(
288
+ { store: $store, eventbus: $eventbus },
289
+ "join_" + roomId,
290
+ {
291
+ project,
292
+ compositions,
293
+ interactionLinks,
294
+ projectConfig,
295
+ roomId,
296
+ },
297
+ )
300
298
  },
301
299
  )
302
300
 
303
- /*
304
- * leaveConfirm
305
- * the backend reports when we leave a room, be it a resonse for our own
306
- * request or forcefully.
301
+ /**
302
+ * Backend reports leaving a room — response to our request or forced by server.
307
303
  */
308
304
  $socket.on("leaveConfirmation", (data) => {
309
305
  log.socket("onLeaveConfirmation", data)
310
306
  })
311
307
 
312
- /* login event
313
- * the backend has approved our token
314
- */
315
- $socket.on("login", (data) =>
308
+ /** Backend has accepted our auth token on the socket. */
309
+ $socket.on("login", () =>
316
310
  log.socket("onLogin", "Socket connection successfully authenticated"),
317
311
  )
312
+
313
+ // --- Generic data sync ------------------------------------------------------
314
+
318
315
  $socket.on("dataCreated", (payload) => {
319
316
  const { body, appSessionId } = payload
320
317
 
@@ -325,6 +322,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
325
322
  handleServerItem(payload, true, false)
326
323
  $eventbus.emit("socket_dataCreated", body)
327
324
  })
325
+
328
326
  $socket.on("dataDeleted", (payload) => {
329
327
  const { body, appSessionId } = payload
330
328
 
@@ -335,10 +333,8 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
335
333
  handleServerItem(payload, true, false)
336
334
  $eventbus.emit("socket_dataDeleted", body)
337
335
  })
338
- /**
339
- * data
340
- * We received a data object from the backend
341
- */
336
+
337
+ /** Incremental data update from the backend. */
342
338
  $socket.on("data", (payload) => {
343
339
  const { body, appSessionId } = payload
344
340
 
@@ -350,9 +346,7 @@ const registerSocketEvents = ($socket, $store, $eventbus) => {
350
346
  $eventbus.emit("socket/data/update", { offline: false, body })
351
347
  })
352
348
 
353
- /**
354
- * New deployed frontend version available
355
- */
349
+ /** New deployed frontend version available. */
356
350
  $socket.on("newVersionAvailable", () => {
357
351
  log.socket("onNewVersionAvailable", {})
358
352
  userNotifier.newVersionAvailable()
@@ -0,0 +1,19 @@
1
+ import type { Socket } from "socket.io-client"
2
+
3
+ let activeSocket: Socket | null = null
4
+
5
+ export const setActiveSocket = (socket: Socket) => {
6
+ activeSocket = socket
7
+ }
8
+
9
+ export const getActiveSocket = (): Socket | null => activeSocket
10
+
11
+ export const isActiveSocket = (socket: Socket): boolean =>
12
+ socket === activeSocket
13
+
14
+ export const teardownSocket = (socket: Socket) => {
15
+ socket.removeAllListeners()
16
+ if (socket.connected) {
17
+ socket.disconnect()
18
+ }
19
+ }
@@ -18,7 +18,7 @@ import {
18
18
  getValueFromLookup,
19
19
  deepFreeze,
20
20
  } from "../lib/dataHelper"
21
- import getEnvVariable from "../lib/getEnvVariable"
21
+ import { isDevelopmentMode } from "@racletteCore/helpers/devMode"
22
22
  import { applicationState } from "./state"
23
23
  /* import feature reducers */
24
24
  import metaReducers from "./reducers/metaReducer"
@@ -46,9 +46,7 @@ import log from "@racletteCore/lib/logger"
46
46
  import type { RacletteStore, StoreHelperGetState } from "@racletteCore"
47
47
 
48
48
  /* Apply different extensions depending on the environment */
49
- const extensions =
50
- getEnvVariable("RACLETTE_DEBUG_MODE") === "true" ||
51
- !getEnvVariable("RACLETTE_DEBUG_MODE")
49
+ const extensions = isDevelopmentMode()
52
50
  ? [
53
51
  new LoggerExtension(),
54
52
  new ReduxDevtoolsExtension({
@@ -1,5 +1,12 @@
1
1
  import type { QueryState } from "./Store"
2
- export type CustomStoreActionParams = Record<string, any>
2
+
3
+ export type CustomStoreActionParams = Record<string, unknown>
4
+
5
+ /** Callable params source — invoked on each query/write `execute()`. */
6
+ export type QueryParamsSubscriber = () => CustomStoreActionParams
7
+
8
+ /** Core query params: plain snapshot or subscriber (framework-agnostic). */
9
+ export type QueryParamsInput = CustomStoreActionParams | QueryParamsSubscriber
3
10
  export type CustomStoreActionOptions = {
4
11
  immediate?: boolean
5
12
  cb?: Function // function to call when call finised -> execute für getAll
@@ -12,7 +19,8 @@ export type CustomStoreActionOptions = {
12
19
 
13
20
  export type CustomStoreActionsPayload = {
14
21
  id?: String // if set this will used as the final namespace giver for the actionId, if not it will be a nanoId
15
- params?: CustomStoreActionParams
22
+ /** Plain object or subscriber function — resolved on each `execute()`. */
23
+ params?: QueryParamsInput
16
24
  options?: CustomStoreActionOptions
17
25
  config?: any
18
26
  }
@@ -12,7 +12,7 @@ import type {
12
12
  PluginApi,
13
13
  } from "@racletteOrchestrator"
14
14
  import type { DefineComponent } from "vue"
15
- import type { UIState, RouteState, WidgetBase } from "@racletteCore/store/types"
15
+ import type { UIState, WidgetBase } from "@racletteCore/store/types"
16
16
  import type { Query } from "@racletteCore/store/reducers/queries/reducers"
17
17
  export type BroadcastChannelDefinition = {
18
18
  channel: string