@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
@@ -1,5 +1,5 @@
1
1
  import { ref, type Ref } from "vue"
2
- import getEnvVariable from "@racletteCore/lib/getEnvVariable"
2
+ import { isDevelopmentMode } from "@racletteCore/helpers/devMode"
3
3
  import log from "@racletteCore/lib/logger"
4
4
 
5
5
  export type OnAllWidgetsReady = (callback: () => void) => void
@@ -24,9 +24,7 @@ export function createWidgetLifecycleManager(
24
24
  const readyCallbacks = new Set<() => void>()
25
25
  const observers = new Map<string, MutationObserver>()
26
26
 
27
- const DEV_MODE =
28
- getEnvVariable("RACLETTE_DEBUG_MODE") === "true" ||
29
- !getEnvVariable("RACLETTE_DEBUG_MODE")
27
+ const DEV_MODE = isDevelopmentMode()
30
28
 
31
29
  const checkAllReady = () => {
32
30
  if (renderedWidgets.value.size === totalWidgets && !allWidgetsReady.value) {
@@ -140,6 +140,7 @@
140
140
  "socket_status_0": "Server nicht verbunden",
141
141
  "socket_status_1": "Server verbunden",
142
142
  "socket_status_2": "Verbindung zum Server wird hergestellt...",
143
+ "app_loading": "Anwendungsinhalt wird geladen",
143
144
  "dataSynced$displayName$type": "{type} \"{displayName}\" wurde synchronisiert",
144
145
  "dataCreate$displayName$type": "{type} \"{displayName}\" wurde erstellt",
145
146
  "dataUpdate$displayName$type": "{type} \"{displayName}\" wurde aktualisiert",
@@ -140,6 +140,7 @@
140
140
  "socket_status_0": "Server not connected",
141
141
  "socket_status_1": "Server connected",
142
142
  "socket_status_2": "Connecting to server...",
143
+ "app_loading": "Loading application content",
143
144
  "dataSynced$displayName$type": "{type} \"{displayName}\" has been synchronized",
144
145
  "dataCreate$displayName$type": "{type} \"{displayName}\" has been created",
145
146
  "dataUpdate$displayName$type": "{type} \"{displayName}\" has been updated",
@@ -140,6 +140,7 @@
140
140
  "socket_status_0": "Server nie je pripojený",
141
141
  "socket_status_1": "Server je pripojený",
142
142
  "socket_status_2": "Pripájanie k serveru...",
143
+ "app_loading": "Načítava sa obsah aplikácie",
143
144
  "dataSynced$displayName$type": "{type} \"{displayName}\" bol synchronizovaný",
144
145
  "dataCreate$displayName$type": "{type} \"{displayName}\" bol vytvorený",
145
146
  "dataUpdate$displayName$type": "{type} \"{displayName}\" bol aktualizovaný",
@@ -110,6 +110,7 @@
110
110
  "socket_status_0": "Qun Qapbogh",
111
111
  "socket_status_1": "Qun QaptaH",
112
112
  "socket_status_2": "QunDaq QaptaH…",
113
+ "app_loading": "App SeHlaw Loading",
113
114
  "dataSynced$displayName$type": "{type} “{displayName}” QaptaH",
114
115
  "dataCreate$displayName$type": "{type} “{displayName}” botlhutlh",
115
116
  "dataUpdate$displayName$type": "{type} “{displayName}” Qapla’",
@@ -1,41 +1,20 @@
1
1
  import type { App as VueApp } from "vue"
2
2
  import createRacletteApp, { corePluginApi } from "@racletteCore/main"
3
+ import { extractSessionIdFromUrl } from "@racletteCore/lib/workSessionUrl"
3
4
  import { createVueApp, createLoginVueApp } from "./main"
4
5
  import { initApp } from "./setup/application"
5
- import { validate } from "uuid"
6
6
  export * from "./exports"
7
7
 
8
8
  const VUE_MOUNTING_ELEMENT = "#app"
9
9
  let currentApp: VueApp | undefined
10
10
 
11
- /**
12
- * Check if URL matches :workSession/id={uuid} pattern and extract sessionId
13
- * Supports racletteJS route format: /:workSession/id={uuid}
14
- */
15
- const extractSessionIdFromUrl = (): string | null => {
16
- const path = window.location.pathname + (window.location.hash || "")
17
- // Match /:workSession/id={uuid} pattern
18
- // The route parser will handle this, but we need to extract it here for bootstrapping
19
- const workSessionMatch = path.match(/\/:workSession\/id=([^/&#]+)/)
20
-
21
- if (workSessionMatch && workSessionMatch[1]) {
22
- const sessionId = workSessionMatch[1]
23
- // Validate UUID format
24
- if (validate(sessionId)) {
25
- return sessionId
26
- }
27
- }
28
-
29
- return null
30
- }
31
-
32
11
  const startApp = async () => {
33
12
  try {
34
13
  // Check if URL indicates a work session
35
14
  const sessionId = extractSessionIdFromUrl()
36
15
 
37
16
  const res = await createRacletteApp({
38
- requireAuth: !sessionId, // Skip auth requirement if sessionId present
17
+ requireAuth: !sessionId,
39
18
  sessionId: sessionId || undefined,
40
19
  })
41
20
 
@@ -47,7 +26,11 @@ const startApp = async () => {
47
26
  if (!res.authenticated && !sessionId) {
48
27
  const noUsers = res.code === "RCLT_DB_NO_USERS"
49
28
  if (res.login) {
50
- currentApp = await createLoginVueApp(VUE_MOUNTING_ELEMENT, res.login, noUsers)
29
+ currentApp = await createLoginVueApp(
30
+ VUE_MOUNTING_ELEMENT,
31
+ res.login,
32
+ noUsers,
33
+ )
51
34
  }
52
35
  } else {
53
36
  currentApp = await createVueApp(VUE_MOUNTING_ELEMENT)
@@ -0,0 +1,48 @@
1
+ import { isRef, toValue, type MaybeRefOrGetter } from "vue"
2
+ import type {
3
+ CustomStoreActionParams,
4
+ QueryParamsInput,
5
+ } from "@racletteCore/types"
6
+ import { isDevelopmentMode } from "@racletteCore/helpers/devMode"
7
+ import log from "@racletteCore/lib/logger"
8
+ import type { VueQueryParamsInput } from "../types/DataApi"
9
+
10
+ function isPlainParamsObject(
11
+ value: unknown,
12
+ ): value is CustomStoreActionParams {
13
+ return value != null && typeof value === "object" && !Array.isArray(value)
14
+ }
15
+
16
+ function warnInvalidQueryParams(context: string, value: unknown): void {
17
+ if (!isDevelopmentMode()) return
18
+ const kind =
19
+ value == null ? String(value) : Array.isArray(value) ? "array" : typeof value
20
+ log.warning(
21
+ "Invalid query params",
22
+ `${context}: expected a plain object, got ${kind}. Using {}.`,
23
+ )
24
+ }
25
+
26
+ /**
27
+ * Normalize Vue reactive params into core {@link QueryParamsInput}:
28
+ * a plain object snapshot, or a subscriber re-read on each `execute()`.
29
+ */
30
+ export function toQueryParamsSource(
31
+ params?: VueQueryParamsInput,
32
+ ): QueryParamsInput {
33
+ if (params == null) return {}
34
+
35
+ if (typeof params === "function" || isRef(params)) {
36
+ return () => {
37
+ const resolved = toValue(params as MaybeRefOrGetter)
38
+ if (isPlainParamsObject(resolved)) return resolved
39
+ warnInvalidQueryParams("Reactive params subscriber", resolved)
40
+ return {}
41
+ }
42
+ }
43
+
44
+ if (isPlainParamsObject(params)) return params
45
+
46
+ warnInvalidQueryParams("Query params", params)
47
+ return {}
48
+ }
@@ -12,12 +12,12 @@ import log from "@racletteCore/lib/logger"
12
12
  import { initPluginRegistration } from "@racletteCore/main"
13
13
  let loginVueApp: VueApp<Element> | undefined
14
14
 
15
- export const createVueApp = async (el: string): VueApp => {
16
- // unmount login vue app, if exists
15
+ export const createVueApp = async (el: string): Promise<VueApp> => {
17
16
  if (loginVueApp) {
18
17
  loginVueApp.unmount()
19
18
  loginVueApp = undefined
20
19
  }
20
+
21
21
  const vueApp = createApp(App)
22
22
  const { i18nInstance, instanceAdapter, dateLocale } = await initI18n()
23
23
  await initPluginRegistration()
@@ -4,32 +4,13 @@
4
4
  import { createRouter, createWebHistory } from "vue-router"
5
5
  import { afterEach, beforeEach } from "./routerHooks"
6
6
  import routes from "./routes"
7
- import { $eventbus } from "@racletteCore/main"
8
-
9
- // create router instance
10
7
 
11
8
  const router = createRouter({
12
9
  history: createWebHistory(),
13
10
  routes,
14
11
  })
15
12
 
16
- /* Rooter hooks */
17
13
  router.beforeEach(beforeEach)
18
14
  router.afterEach(afterEach)
19
- $eventbus.on("backend/error", (error) => {
20
- if (error.name === "AxiosError") {
21
- const errorResponse = error.response?.data
22
- if (errorResponse?.code === "FST_JWT_AUTHORIZATION_TOKEN_EXPIRED") {
23
- // Capture current route path (including query and hash)
24
- const currentUrl = router.currentRoute.value.fullPath
25
- // Redirect to login with the current URL as a query param
26
- if (window.location.href !== window.location.origin + "/login") {
27
- window.location.href =
28
- window.location.origin + "/login?redirect=" + currentUrl
29
- }
30
15
 
31
- return
32
- }
33
- }
34
- })
35
16
  export default router
@@ -0,0 +1,29 @@
1
+ import type { Router } from "vue-router"
2
+ import type EventEmitter from "@racletteCore/lib/eventEmitter"
3
+ import { applyCompositionSlotsFromRoute } from "./routeStore"
4
+
5
+ /**
6
+ * Wire router reactions to the global event bus.
7
+ * Kept separate from router creation so it can be invoked from initApp (testable).
8
+ */
9
+ export const registerRouterEventHandlers = (
10
+ router: Router,
11
+ eventbus: EventEmitter,
12
+ ) => {
13
+ eventbus.on("ui/syncRouteCompositions", () => {
14
+ applyCompositionSlotsFromRoute(router.currentRoute.value)
15
+ })
16
+
17
+ eventbus.on("backend/error", (error) => {
18
+ if (error.name === "AxiosError") {
19
+ const errorResponse = error.response?.data
20
+ if (errorResponse?.code === "FST_JWT_AUTHORIZATION_TOKEN_EXPIRED") {
21
+ const currentUrl = router.currentRoute.value.fullPath
22
+ if (window.location.href !== window.location.origin + "/login") {
23
+ window.location.href =
24
+ window.location.origin + "/login?redirect=" + currentUrl
25
+ }
26
+ }
27
+ }
28
+ })
29
+ }
@@ -6,6 +6,7 @@ import {
6
6
  type CompositionSlot,
7
7
  } from "@racletteOrchestrator/router/routeParserHelper"
8
8
  import { getCompositionIdByPath } from "@racletteCore/store/reducers/compositions/selectors"
9
+ import logger from "@racletteCore/lib/logger"
9
10
 
10
11
  // TODO: find better filename
11
12
 
@@ -35,6 +36,12 @@ export const dispatchSetCompositionAs = (
35
36
  },
36
37
  })
37
38
  }
39
+ } else {
40
+ logger.warning(
41
+ "compositionNotFound",
42
+ `No composition for path "${compositionPath}" (slot "${slot}"). ` +
43
+ "Route may be ahead of socket bootstrap or the composition was removed.",
44
+ )
38
45
  }
39
46
  } else {
40
47
  $store.dispatch({
@@ -99,36 +106,82 @@ const getCompositionSlotChanges = (
99
106
  return { toRemove, toAdd, toUpdate }
100
107
  }
101
108
 
102
- export const updateActiveCompositions = (
103
- to: RouteLocationNormalized,
104
- from: RouteLocationNormalized,
109
+ /**
110
+ * Same slot name, different composition path — swap in one `setCompositionAs`
111
+ * instead of unset + set (avoids a frame with no active composition).
112
+ */
113
+ const partitionSlotChanges = (
114
+ toRemove: CompositionSlot[],
115
+ toAdd: CompositionSlot[],
105
116
  ) => {
106
- const isInitialRouting = from.name === undefined
107
- const hasToPageIdParameter = Boolean(to.params.pageId?.length)
108
- const hasFromPageIdParameter = Boolean(from.params.pageId?.length)
117
+ const replacements: CompositionSlot[] = []
118
+ const stillToRemove: CompositionSlot[] = []
119
+ const stillToAdd: CompositionSlot[] = []
109
120
 
110
- const compositionSlots = parseRouteToCompositionSlots(
111
- to.fullPath,
112
- undefined,
113
- !hasToPageIdParameter,
121
+ const addBySlotName = new Map(
122
+ toAdd.map((slot) => [slot.slotName, slot]),
114
123
  )
115
124
 
116
- const beforeCompositionSlots = parseRouteToCompositionSlots(
117
- from.fullPath,
125
+ for (const removed of toRemove) {
126
+ const replacement = addBySlotName.get(removed.slotName)
127
+ if (replacement) {
128
+ replacements.push(replacement)
129
+ addBySlotName.delete(removed.slotName)
130
+ } else {
131
+ stillToRemove.push(removed)
132
+ }
133
+ }
134
+
135
+ stillToAdd.push(...addBySlotName.values())
136
+
137
+ return { replacements, stillToRemove, stillToAdd }
138
+ }
139
+
140
+ export const getCompositionSlotsForRoute = (
141
+ route: RouteLocationNormalized,
142
+ options?: { useDefaultSlotFallback?: boolean },
143
+ ) => {
144
+ const hasPageIdParameter = Boolean(route.params.pageId?.length)
145
+ const useDefaultSlotFallback =
146
+ options?.useDefaultSlotFallback ?? !hasPageIdParameter
147
+
148
+ return parseRouteToCompositionSlots(
149
+ route.fullPath,
118
150
  undefined,
119
- !isInitialRouting && !hasFromPageIdParameter,
151
+ useDefaultSlotFallback,
152
+ )
153
+ }
154
+
155
+ export const applyCompositionSlots = (slots: CompositionSlot[]) => {
156
+ slots.forEach((slot) =>
157
+ dispatchSetCompositionAs(slot.slotName, slot.slotValue, slot.slotParams),
120
158
  )
159
+ }
160
+
161
+ export const updateActiveCompositions = (
162
+ to: RouteLocationNormalized,
163
+ from: RouteLocationNormalized,
164
+ ) => {
165
+ const isInitialRouting = from.name === undefined
166
+
167
+ const compositionSlots = getCompositionSlotsForRoute(to)
168
+ const beforeCompositionSlots = getCompositionSlotsForRoute(from, {
169
+ useDefaultSlotFallback:
170
+ !isInitialRouting && !Boolean(from.params.pageId?.length),
171
+ })
121
172
 
122
173
  const { toRemove, toAdd, toUpdate } = getCompositionSlotChanges(
123
174
  beforeCompositionSlots,
124
175
  compositionSlots,
125
176
  )
126
177
 
127
- // Remove slots that are no longer needed
128
- toRemove.forEach((slot) => dispatchSetCompositionAs(slot.slotName))
178
+ const { replacements, stillToRemove, stillToAdd } = partitionSlotChanges(
179
+ toRemove,
180
+ toAdd,
181
+ )
129
182
 
130
- // Add completely new slots
131
- toAdd.forEach((slot) =>
183
+ // Same slot, new composition path — set directly (no unset in between)
184
+ replacements.forEach((slot) =>
132
185
  dispatchSetCompositionAs(slot.slotName, slot.slotValue, slot.slotParams),
133
186
  )
134
187
 
@@ -149,6 +202,14 @@ export const updateActiveCompositions = (
149
202
  })
150
203
  }
151
204
  })
205
+
206
+ // New slots (slot name was not active before)
207
+ stillToAdd.forEach((slot) =>
208
+ dispatchSetCompositionAs(slot.slotName, slot.slotValue, slot.slotParams),
209
+ )
210
+
211
+ // Slots removed without a replacement on the same slot name
212
+ stillToRemove.forEach((slot) => dispatchSetCompositionAs(slot.slotName))
152
213
  }
153
214
 
154
215
  export const handleRouteChange = (
@@ -183,3 +244,17 @@ export const getDefaultSlotValue = (url: string) =>
183
244
  parseRouteToCompositionSlots(url, undefined, true).find(
184
245
  (slot) => slot.slotName === DEFAULT_SLOT_NAME,
185
246
  )?.slotValue
247
+
248
+ /**
249
+ * Re-apply composition slots from the current route without a navigation event.
250
+ * Used after socket project bootstrap clears ui.page while the URL is unchanged.
251
+ */
252
+ export const applyCompositionSlotsFromRoute = (
253
+ route: RouteLocationNormalized,
254
+ ) => {
255
+ if (route.name !== "app.space.page") {
256
+ return
257
+ }
258
+
259
+ applyCompositionSlots(getCompositionSlotsForRoute(route))
260
+ }
@@ -1,6 +1,7 @@
1
1
  import type { RouteRecordRaw } from "vue-router"
2
2
  import ProductOrchestrator from "../ProductOrchestrator.vue"
3
3
  import useAuthentication from "@racletteOrchestrator/composables/useAuthentication"
4
+ import { $store } from "@racletteCore"
4
5
 
5
6
  const routes: Readonly<RouteRecordRaw[]> = [
6
7
  {
@@ -17,6 +18,10 @@ const routes: Readonly<RouteRecordRaw[]> = [
17
18
  const { clearCookies } = useAuthentication()
18
19
 
19
20
  clearCookies()
21
+
22
+ // Clear project context so a later socket re-join cannot light-rejoin on stale state.
23
+ $store.dispatch({ type: "projectInit" })
24
+ $store.dispatch({ type: "project/set", payload: { _id: null } })
20
25
 
21
26
  if (window.location.href !== window.location.origin + "/login") {
22
27
  window.location.href = window.location.origin + "/login"
@@ -1,3 +1,12 @@
1
- export const initApp = async (corePluginApi) => {
2
- console.log("initApp was called")
1
+ import type { CorePluginApi } from "@racletteCore"
2
+ import router from "@racletteOrchestrator/router"
3
+ import { registerRouterEventHandlers } from "@racletteOrchestrator/router/registerEventHandlers"
4
+
5
+ let routerHandlersRegistered = false
6
+
7
+ export const initApp = async (corePluginApi: CorePluginApi) => {
8
+ if (!routerHandlersRegistered) {
9
+ registerRouterEventHandlers(router, corePluginApi.$eventbus)
10
+ routerHandlersRegistered = true
11
+ }
3
12
  }
@@ -0,0 +1,19 @@
1
+ import type { MaybeRefOrGetter } from "vue"
2
+ import type {
3
+ CustomStoreActionParams,
4
+ CustomStoreActionsPayload,
5
+ } from "@racletteCore/types"
6
+
7
+ /** Params accepted by Vue plugin `$data` methods (refs/computed/getters allowed). */
8
+ export type VueQueryParamsInput =
9
+ | MaybeRefOrGetter<CustomStoreActionParams | undefined>
10
+ | CustomStoreActionParams
11
+ | undefined
12
+
13
+ /** Vue orchestrator payload — extends core with reactive `params`. */
14
+ export type VueStoreActionsPayload = Omit<
15
+ CustomStoreActionsPayload,
16
+ "params"
17
+ > & {
18
+ params?: MaybeRefOrGetter<CustomStoreActionParams | undefined>
19
+ }
@@ -1,24 +1,21 @@
1
1
  import type { useI18n } from "vue-i18n"
2
2
  import type { ComputedRef, Ref } from "vue"
3
- import type { CorePluginApi } from "@racletteCore/main"
4
- import type {
5
- CustomStoreActionsPayload,
6
- PluginStoreApi,
7
- } from "@racletteCore/types"
3
+ import type { PluginStoreApi, CorePluginApi } from "@racletteCore/types"
4
+ import type { VueQueryParamsInput, VueStoreActionsPayload } from "./DataApi"
8
5
  import type { Query } from "@racletteCore/store/reducers/queries/reducers"
9
6
  import type { actionId } from "@racletteCore"
10
7
  import type { Socket } from "socket.io-client"
11
8
  import type { User } from "@shared/types"
12
9
  // TODO: finalize types
13
10
  // TODO: generate this type from index.ts in the plugin & extend it globally
14
- export type PluginMethod = (payload?: CustomStoreActionsPayload) => {
11
+ export type PluginMethod = (payload?: VueStoreActionsPayload) => {
15
12
  isLoading: Boolean
16
13
  response: Ref<unknown>
17
14
  error: Ref<unknown>
18
15
  data: ComputedRef<unknown>
19
16
  dataArr: Array<ComputedRef<unknown>>
20
17
  execute: (
21
- params?: Record<string, any>,
18
+ params?: VueQueryParamsInput,
22
19
  ) => Promise<{ response: any; result: any }>
23
20
  // query and uuids only provided from queryObservableHelper
24
21
  query?: ComputedRef<Query>
@@ -54,6 +54,8 @@ export type WidgetDeclaration = Omit<
54
54
  RegisteredWidgetDeclaration,
55
55
  "pluginKey" | "author" | "name" // name will be extracted via filename, cannot be overwritten
56
56
  > & {
57
+ title: string
58
+ description?: string
57
59
  color?: string
58
60
  icon?: string
59
61
  images?: string[]
@@ -1,3 +1,4 @@
1
1
  export type * from "./PluginApi"
2
+ export type * from "./DataApi"
2
3
  export type * from "./Widgets"
3
4
  export type * from "./Composables"
@@ -56,7 +56,7 @@ services:
56
56
  MONGO_HOST=${RACLETTE_MONGODB_HOST:-mongodb://mongodb:${RACLETTE_MONGODB_PORT:-27017}/${RACLETTE_MONGODB_DATABASE:-raclette}}
57
57
  - >-
58
58
  CACHE_URL=${RACLETTE_CACHE_URL:-redis://cache:${RACLETTE_CACHE_PORT:-6379}}
59
- - RACLETTE_DEBUG_MODE=true
59
+ - RACLETTE_DEBUG_MODE=${RACLETTE_DEBUG_MODE:-true}
60
60
  - RACLETTE_APP_PATH=/home/shookie/core-dev/@raclettejs/playground
61
61
  volumes:
62
62
  - yarn-cache:/usr/local/share/.cache/yarn
@@ -111,9 +111,6 @@ services:
111
111
  - RACLETTE_DEBUG_MODE=${RACLETTE_DEBUG_MODE:-true}
112
112
  - >-
113
113
  RACLETTE_SERVER_BASE_URL=${RACLETTE_SERVER_BASE_URL:-http://localhost:${RACLETTE_SERVER_PORT:-8082}}
114
- - >-
115
- RACLETTE_SOCKET_URL=${RACLETTE_SOCKET_URL:-http://localhost:${RACLETTE_SERVER_PORT:-8082}}
116
- - RACLETTE_DEBUG_MODE=true
117
114
  - RACLETTE_APP_PATH=/home/shookie/core-dev/@raclettejs/playground
118
115
  volumes:
119
116
  - yarn-cache:/usr/local/share/.cache/yarn
package/templates/fs.yml CHANGED
@@ -17,7 +17,7 @@ patterns:
17
17
  ext: ["**/*.vue", "**/*.ts", "**/*.js", "**/*.json", "**/*.css", "**/*.scss"]
18
18
  pkg: ["**/package.json", "**/yarn.lock"] # changes here require new install anyway
19
19
  # always added when globbing, config.root is also always added
20
- ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/.prettierrc", "**/eslint.config.js"]
20
+ ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/.prettierrc", "**/eslint.config.js", "**/.yarnrc.yml", "**/.yarn/**"]
21
21
 
22
22
 
23
23
  mappings: