@raclettejs/core 0.1.22 → 0.1.24

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 (36) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/bin/cli.js +2 -2
  3. package/dist/cli.js +136 -155
  4. package/dist/cli.js.map +4 -4
  5. package/dist/index.js +15 -16
  6. package/dist/index.js.map +4 -4
  7. package/package.json +24 -22
  8. package/services/backend/.yarn/install-state.gz +0 -0
  9. package/services/backend/package.json +21 -19
  10. package/services/backend/src/core/contracting/contractRegistrar.ts +1 -1
  11. package/services/backend/src/core/pluginSystem/configGenerator/generatorTypes.ts +1 -0
  12. package/services/backend/src/core/pluginSystem/configGenerator/index.ts +2 -2
  13. package/services/backend/src/core/pluginSystem/pluginLoader.ts +99 -44
  14. package/services/backend/src/core/pluginSystem/pluginTypes.ts +1 -0
  15. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/index.ts +2 -0
  16. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.patchOwn.ts +68 -0
  17. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.model.ts +1 -1
  18. package/services/backend/tsconfig.json +1 -0
  19. package/services/backend/yarn.lock +698 -1061
  20. package/services/frontend/.yarn/install-state.gz +0 -0
  21. package/services/frontend/eslint.config.js +9 -0
  22. package/services/frontend/index.html +3 -3
  23. package/services/frontend/package.json +30 -28
  24. package/services/frontend/src/core/setup/plugin-system/discovery/app-plugins.ts +43 -4
  25. package/services/frontend/src/orchestrator/ProductOrchestrator.vue +10 -11
  26. package/services/frontend/src/orchestrator/assets/styles/layers.css +11 -0
  27. package/services/frontend/src/orchestrator/assets/styles/tailwindStyles.css +5 -11
  28. package/services/frontend/src/orchestrator/assets/styles/vuetifyStyles.scss +0 -4
  29. package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +5 -18
  30. package/services/frontend/src/orchestrator/composables/useWidgets/helperFunctions.ts +54 -5
  31. package/services/frontend/src/orchestrator/helpers/index.ts +1 -0
  32. package/services/frontend/src/orchestrator/helpers/widgetVisuals.ts +71 -0
  33. package/services/frontend/src/orchestrator/setup/vuetify.ts +2 -0
  34. package/services/frontend/src/orchestrator/types/Widgets.ts +5 -1
  35. package/services/frontend/vite.config.ts +22 -9
  36. package/services/frontend/yarn.lock +0 -5224
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raclettejs/core",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "racletteJS core package",
5
5
  "repository": "https://gitlab.com/raclettejs/core",
6
6
  "author": "Pacifico Digital Explorations GmbH <info@raclettejs.com>",
@@ -59,40 +59,42 @@
59
59
  },
60
60
  "config": {},
61
61
  "dependencies": {
62
- "@inquirer/prompts": "8.2.0",
62
+ "@inquirer/prompts": "8.3.2",
63
63
  "@types/mustache": "4.2.6",
64
64
  "chalk": "5.6.2",
65
- "chokidar": "3.6.0",
65
+ "chokidar": "5.0.0",
66
66
  "cli-highlight": "2.1.11",
67
- "commander": "14.0.1",
68
- "dotenv": "17.2.2",
69
- "esbuild": "0.25.9",
70
- "fs-extra": "11.3.2",
71
- "globby": "14.1.0",
72
- "js-yaml": "4.1.0",
67
+ "commander": "14.0.3",
68
+ "dotenv": "17.3.1",
69
+ "esbuild": "0.27.4",
70
+ "fs-extra": "11.3.4",
71
+ "globby": "16.1.1",
72
+ "js-yaml": "4.1.1",
73
73
  "mustache": "4.2.0",
74
- "ramda": "0.31.3",
74
+ "ramda": "0.32.0",
75
75
  "tsc-alias": "1.8.16"
76
76
  },
77
77
  "devDependencies": {
78
+ "@emnapi/core": "1.9.2",
79
+ "@emnapi/runtime": "1.9.2",
78
80
  "@eslint/js": "9.35.0",
79
- "@raclettejs/types": "^0.1.22",
81
+ "@raclettejs/types": "^0.1.24",
80
82
  "@types/fs-extra": "11.0.4",
81
83
  "@types/js-yaml": "4.0.9",
82
- "@types/node": "24.5.1",
84
+ "@types/node": "25.5.0",
83
85
  "@types/ramda": "0.31.1",
84
86
  "eslint": "9.35.0",
85
- "eslint-config-prettier": "10.1.8",
86
- "eslint-flat-config-utils": "2.1.1",
87
+ "eslint-config-prettier": "9.1.2",
88
+ "eslint-flat-config-utils": "3.0.2",
87
89
  "eslint-plugin-import": "2.32.0",
88
- "eslint-plugin-prefer-arrow-functions": "3.8.1",
89
- "eslint-plugin-prettier": "5.5.4",
90
- "eslint-plugin-vue": "10.4.0",
91
- "globals": "16.4.0",
90
+ "eslint-plugin-prefer-arrow-functions": "3.9.1",
91
+ "eslint-plugin-prettier": "5.5.5",
92
+ "eslint-plugin-vue": "9.33.0",
93
+ "globals": "17.4.0",
92
94
  "husky": "9.1.7",
93
- "prettier": "3.6.2",
94
- "typescript": "5.9.2",
95
- "typescript-eslint": "8.44.0",
96
- "vue-eslint-parser": "10.2.0"
95
+ "prettier": "3.8.1",
96
+ "typescript": "5.9.3",
97
+ "typescript-eslint": "8.57.1",
98
+ "vue-eslint-parser": "9.4.3"
97
99
  }
98
100
  }
@@ -16,6 +16,7 @@
16
16
  "build:assets:src": "copyfiles -u 1 'src/**/*.{json,js,yml,yaml,env*}' dist",
17
17
  "build:assets:root": "copyfiles -u 0 'raclette.config.js' dist",
18
18
  "build:assets": "npm run build:assets:src && npm run build:assets:root",
19
+ "lint": "eslint .",
19
20
  "migrate:create": "npx migrate-mongo create -f './mongo-migrations/migrate-mongo-config.js'",
20
21
  "migrate:up": "migrate-mongo up -f './mongo-migrations/migrate-mongo-config.js'",
21
22
  "migrate:down": "migrate-mongo down -f './mongo-migrations/migrate-mongo-config.js'",
@@ -24,28 +25,29 @@
24
25
  "typecheck:watch": "tsc --noEmit --watch"
25
26
  },
26
27
  "dependencies": {
27
- "@fastify/cors": "11.1.0",
28
+ "@fastify/cors": "11.2.0",
28
29
  "@fastify/jwt": "10.0.0",
29
30
  "@fastify/rate-limit": "10.3.0",
30
31
  "@fastify/schedule": "6.0.0",
31
- "@fastify/sensible": "6.0.3",
32
- "@sinclair/typebox": "0.34.41",
32
+ "@fastify/sensible": "6.0.4",
33
+ "@sinclair/typebox": "0.34.48",
33
34
  "@types/vary": "1.1.3",
34
- "axios": "1.13.2",
35
+ "axios": "1.13.6",
35
36
  "bcrypt": "6.0.0",
36
37
  "copyfiles": "2.4.1",
37
- "fastify": "5.6.0",
38
+ "fastify": "5.8.2",
38
39
  "fastify-custom-healthcheck": "4.0.0",
39
- "glob": "11.0.3",
40
+ "fs-extra": "11.3.4",
41
+ "glob": "13.0.6",
40
42
  "iovalkey": "0.3.3",
41
- "migrate-mongo": "12.1.3",
42
- "mongodb": "^6.20.0",
43
- "mongoose": "8.18.1",
44
- "nanoid": "5.1.5",
45
- "pino": "9.9.5",
46
- "pino-pretty": "13.1.1",
47
- "ramda": "0.31.3",
48
- "socket.io": "4.8.1",
43
+ "migrate-mongo": "14.0.7",
44
+ "mongodb": "6.21.0",
45
+ "mongoose": "8.23.0",
46
+ "nanoid": "5.1.7",
47
+ "pino": "10.3.1",
48
+ "pino-pretty": "13.1.3",
49
+ "ramda": "0.32.0",
50
+ "socket.io": "4.8.3",
49
51
  "toad-scheduler": "3.1.0",
50
52
  "tsc-alias": "1.8.16",
51
53
  "uuid": "13.0.0"
@@ -58,9 +60,9 @@
58
60
  "@types/uuid": "11.0.0",
59
61
  "eslint": "9.35.0",
60
62
  "eslint-plugin-import": "2.32.0",
61
- "eslint-plugin-prefer-arrow-functions": "3.8.1",
62
- "tsx": "4.20.5",
63
- "typescript": "5.9.2",
64
- "typescript-eslint": "8.44.0"
63
+ "eslint-plugin-prefer-arrow-functions": "3.9.1",
64
+ "tsx": "4.21.0",
65
+ "typescript": "5.9.3",
66
+ "typescript-eslint": "8.57.1"
65
67
  }
66
- }
68
+ }
@@ -12,7 +12,7 @@ export const registerPluginContract = (
12
12
  contract: PluginContract,
13
13
  ): void => {
14
14
  if (contracts[pluginKey]) {
15
- throw new Error(`Plugin contract for "${pluginKey}" is already registered`)
15
+ throw new Error(`Plugin contract for "${pluginKey}" is already registered. Make sure that each plugin is calling fastify.generatePluginContract only once! Multiple calls are not allowed.`)
16
16
  }
17
17
 
18
18
  contracts[pluginKey] = contract
@@ -13,6 +13,7 @@ export type PluginRegistration = {
13
13
  author: string
14
14
  pluginKey: string
15
15
  pluginPath: string
16
+ frontendPath: string
16
17
  routePrefix: string
17
18
  entityMapping?: Record<string, string>
18
19
  customConfig?: {
@@ -42,6 +42,7 @@ class FrontendConfigRegistry {
42
42
  author: pluginData.author,
43
43
  pluginKey: pluginData.key,
44
44
  pluginPath: pluginData.absolutePath,
45
+ frontendPath: pluginData.frontendAbsolutePath,
45
46
  routePrefix: pluginData.routePrefix,
46
47
  entityMapping: options?.entityMapping || {},
47
48
  customConfig: options?.customConfig,
@@ -451,8 +452,7 @@ const writePluginConfigFile = async (
451
452
 
452
453
  const configContent = generatePluginConfigFile(dataTypes, registration)
453
454
  const outputPath = path.join(
454
- registration.pluginPath,
455
- "frontend",
455
+ registration.frontendPath,
456
456
  "generated-config.ts",
457
457
  )
458
458
 
@@ -30,6 +30,7 @@ interface PluginInfo {
30
30
  pluginName: string
31
31
  modulePath: string
32
32
  pluginPath: string
33
+ frontendPath: string
33
34
  type: PluginType
34
35
  metadata: PluginMetadata
35
36
  }
@@ -75,6 +76,74 @@ const logPluginError = (
75
76
  fastify.log.debug(`${createLogPrefix(type)} ${identifier}: ERROR - ${error}`)
76
77
  }
77
78
 
79
+ const normalizeRelativeDir = (dir?: string): string | undefined =>
80
+ dir?.replace(/^\.\//, "")
81
+
82
+ const resolveExistingServicePath = async (
83
+ pluginPath: string,
84
+ candidates: string[],
85
+ ): Promise<{ relativePath: string; absolutePath: string } | undefined> => {
86
+ for (const candidatePath of candidates) {
87
+ const normalizedCandidatePath = normalizeRelativeDir(candidatePath)
88
+ if (!normalizedCandidatePath) continue
89
+
90
+ const candidateAbsolutePath = path.join(pluginPath, normalizedCandidatePath)
91
+ try {
92
+ const stat = await fs.promises.stat(candidateAbsolutePath)
93
+ if (stat.isDirectory()) {
94
+ return {
95
+ relativePath: normalizedCandidatePath,
96
+ absolutePath: candidateAbsolutePath,
97
+ }
98
+ }
99
+ } catch {
100
+ // path missing or not accessible
101
+ }
102
+ }
103
+ }
104
+
105
+ const resolveBackendPath = async (
106
+ pluginPath: string,
107
+ metadata: PluginMetadata,
108
+ ): Promise<{ relativePath: string; absolutePath: string } | undefined> => {
109
+ const backendPathCandidates = getBackendPathCandidates(metadata)
110
+
111
+ return resolveExistingServicePath(pluginPath, backendPathCandidates)
112
+ }
113
+
114
+ const getBackendPathCandidates = (metadata: PluginMetadata): string[] => {
115
+ const explicitBackendDir = normalizeRelativeDir(metadata.backendDir)
116
+ if (explicitBackendDir) {
117
+ return [explicitBackendDir]
118
+ }
119
+
120
+ return ["backend", "src/backend"]
121
+ }
122
+
123
+ const resolveFrontendPath = async (
124
+ pluginPath: string,
125
+ metadata: PluginMetadata,
126
+ backendRelativePath?: string,
127
+ ): Promise<string> => {
128
+ const explicitFrontendDir = normalizeRelativeDir(metadata.frontendDir)
129
+ if (explicitFrontendDir) {
130
+ return path.join(pluginPath, explicitFrontendDir)
131
+ }
132
+
133
+ const resolvedFrontendPath = await resolveExistingServicePath(pluginPath, [
134
+ "frontend",
135
+ "src/frontend",
136
+ ])
137
+ if (resolvedFrontendPath) {
138
+ return resolvedFrontendPath.absolutePath
139
+ }
140
+
141
+ // Fall back to src/frontend when backend is under src, otherwise frontend.
142
+ const fallbackRelativePath =
143
+ backendRelativePath?.startsWith("src/") ? "src/frontend" : "frontend"
144
+ return path.join(pluginPath, fallbackRelativePath)
145
+ }
146
+
78
147
  /**
79
148
  * Get app plugin paths for current environment
80
149
  */
@@ -254,16 +323,18 @@ const collectLocalPluginsFromPath = async (
254
323
  continue
255
324
  }
256
325
 
257
- // Get backend path from metadata or use default
258
- const backendRelativePath = metadata.backendDir || "backend"
259
- const backendPath = path.join(pluginPath, backendRelativePath)
326
+ // Get backend path from metadata or try supported defaults.
327
+ const resolvedBackendPath = await resolveBackendPath(pluginPath, metadata)
328
+ const backendRelativePath = resolvedBackendPath?.relativePath
329
+ const backendPath = resolvedBackendPath?.absolutePath
260
330
 
261
- if (!fs.existsSync(backendPath)) {
331
+ if (!backendRelativePath || !backendPath) {
332
+ const backendPathCandidates = getBackendPathCandidates(metadata)
262
333
  logPluginSkipped(
263
334
  fastify,
264
335
  pluginType,
265
336
  pluginIdentifier,
266
- `backend directory not found: ${backendRelativePath}`,
337
+ `backend directory not found: ${backendPathCandidates.join(" or ")}`,
267
338
  )
268
339
  if (metadata.backendDir) {
269
340
  // we only cancel when the dir was explicitly set
@@ -277,24 +348,6 @@ const collectLocalPluginsFromPath = async (
277
348
  continue
278
349
  }
279
350
 
280
- if (!fs.statSync(backendPath).isDirectory()) {
281
- logPluginSkipped(
282
- fastify,
283
- pluginType,
284
- pluginIdentifier,
285
- `backend path is not a directory: ${backendRelativePath}`,
286
- )
287
- if (metadata.backendDir) {
288
- handlePluginError(
289
- fastify,
290
- `Server directory not found for plugin: ${configPath}`,
291
- pluginType,
292
- errorFlags,
293
- )
294
- }
295
- continue
296
- }
297
-
298
351
  // Find the main module file
299
352
  const modulePath = getFilePathWithExtension(backendPath, "index")
300
353
  if (!modulePath) {
@@ -315,12 +368,18 @@ const collectLocalPluginsFromPath = async (
315
368
 
316
369
  const author = slugify(metadata.author)
317
370
  const pluginName = slugify(metadata.name)
371
+ const frontendPath = await resolveFrontendPath(
372
+ pluginPath,
373
+ metadata,
374
+ backendRelativePath,
375
+ )
318
376
 
319
377
  plugins.push({
320
378
  author,
321
379
  pluginName,
322
380
  modulePath,
323
381
  pluginPath,
382
+ frontendPath,
324
383
  type: pluginType,
325
384
  metadata,
326
385
  })
@@ -462,32 +521,21 @@ const collectNpmPlugins = async (
462
521
  continue
463
522
  }
464
523
 
465
- // Get backend path from metadata or use default
466
- const backendRelativePath = pluginMetadata.backendDir || "src/backend"
467
- const backendPath = path.join(pluginPath, backendRelativePath)
468
-
469
- if (!fs.existsSync(backendPath)) {
470
- logPluginSkipped(
471
- fastify,
472
- PluginType.NPM,
473
- pluginPackage,
474
- `backend directory not found: ${backendRelativePath}`,
475
- )
476
- handlePluginError(
477
- fastify,
478
- `Server directory not found for npm plugin: ${pluginPackage}`,
479
- PluginType.NPM,
480
- errorFlags,
481
- )
482
- continue
483
- }
524
+ // Get backend path from metadata or try supported defaults.
525
+ const resolvedBackendPath = await resolveBackendPath(
526
+ pluginPath,
527
+ pluginMetadata,
528
+ )
529
+ const backendRelativePath = resolvedBackendPath?.relativePath
530
+ const backendPath = resolvedBackendPath?.absolutePath
484
531
 
485
- if (!fs.statSync(backendPath).isDirectory()) {
532
+ if (!backendRelativePath || !backendPath) {
533
+ const backendPathCandidates = getBackendPathCandidates(pluginMetadata)
486
534
  logPluginSkipped(
487
535
  fastify,
488
536
  PluginType.NPM,
489
537
  pluginPackage,
490
- `backend path is not a directory: ${backendRelativePath}`,
538
+ `backend directory not found: ${backendPathCandidates.join(" or ")}`,
491
539
  )
492
540
  handlePluginError(
493
541
  fastify,
@@ -518,12 +566,18 @@ const collectNpmPlugins = async (
518
566
 
519
567
  const author = slugify(pluginMetadata.author)
520
568
  const pluginName = slugify(pluginMetadata.name)
569
+ const frontendPath = await resolveFrontendPath(
570
+ pluginPath,
571
+ pluginMetadata,
572
+ backendRelativePath,
573
+ )
521
574
 
522
575
  plugins.push({
523
576
  author,
524
577
  pluginName,
525
578
  modulePath,
526
579
  pluginPath,
580
+ frontendPath,
527
581
  type: PluginType.NPM,
528
582
  metadata: pluginMetadata,
529
583
  })
@@ -704,6 +758,7 @@ const registerCollectedPlugins = async (
704
758
  collectionPrefix,
705
759
  routePrefix,
706
760
  absolutePath: plugin.pluginPath,
761
+ frontendAbsolutePath: plugin.frontendPath,
707
762
  }
708
763
 
709
764
  // Dynamically import the plugin module
@@ -25,6 +25,7 @@ export type PluginOptions = {
25
25
  pluginName: string
26
26
  author: string
27
27
  absolutePath: string
28
+ frontendAbsolutePath: string
28
29
  type: PluginType
29
30
  collectionPrefix: string
30
31
  routePrefix: string
@@ -2,6 +2,7 @@ import { PluginFastifyInstance } from "types"
2
2
  import get from "./route.user.get"
3
3
  import getAll from "./route.user.getAll"
4
4
  import patch from "./route.user.patch"
5
+ import patchOwn from "./route.user.patchOwn"
5
6
  import patchLastProject from "./route.user.patchLastProject"
6
7
  import post from "./route.user.post"
7
8
  import remove from "./route.user.remove"
@@ -12,6 +13,7 @@ const registerRoutes = async (fastify: PluginFastifyInstance) => {
12
13
  await fastify.get("/user/all", getAll(fastify))
13
14
  await fastify.get("/user/:_id", get(fastify))
14
15
  await fastify.patch("/user/:_id", patch(fastify))
16
+ await fastify.patch("/user/self", patchOwn(fastify))
15
17
  await fastify.patch("/user/:_id/restore", restore(fastify))
16
18
  await fastify.post("/user", post(fastify))
17
19
  await fastify.delete("/user/:_id", remove(fastify))
@@ -0,0 +1,68 @@
1
+ import type { Static } from "@sinclair/typebox"
2
+ import type { FastifyReply, FastifyRequest } from "fastify"
3
+ import type { PluginFastifyInstance } from "types"
4
+ import { Type } from "@sinclair/typebox"
5
+ import { UnauthorizedError } from "@/utils/errors"
6
+ import { userUpdateSchema } from "../user.schema"
7
+
8
+ type UserBody = Static<typeof userUpdateSchema>
9
+
10
+ export default (fastify: PluginFastifyInstance) => {
11
+ const userService = fastify.custom.userService
12
+
13
+ const handler = async (
14
+ req: FastifyRequest<{
15
+ Params: {}
16
+ Body: UserBody
17
+ }>,
18
+ reply: FastifyReply,
19
+ ) => {
20
+ try {
21
+ if (req.user._id !== req.body._id) {
22
+ throw new UnauthorizedError("not your user")
23
+ }
24
+ // only let the user update non vital fields
25
+ const updateObject = {}
26
+ if (req.body?.email?.length) {
27
+ updateObject.email = req.body.email
28
+ // TODO Creation of the field should be happening in the user.model pre save hook
29
+ updateObject.email_verified = false
30
+ }
31
+ if (req.body?.firstname?.length) {
32
+ updateObject.firstname = req.body.firstname
33
+ }
34
+ if (req.body?.lastname?.length) {
35
+ updateObject.lastname = req.body.lastname
36
+ }
37
+ if (req.body?.password?.length) {
38
+ updateObject.password = req.body.password
39
+ }
40
+ const payloadWithUser = await userService.updateUser(
41
+ fastify,
42
+ req.requestParams,
43
+ req.user._id,
44
+ updateObject,
45
+ )
46
+
47
+ return payloadWithUser
48
+ } catch (error: any) {
49
+ fastify.log.error(error.message)
50
+ return reply.internalServerError(error.message)
51
+ }
52
+ }
53
+
54
+ return {
55
+ handler,
56
+ onRequest: [fastify.authenticate],
57
+ config: {
58
+ type: "dataUpdate",
59
+ broadcastChannels: ["userUpdated"],
60
+ },
61
+ schema: {
62
+ summary: "Update a user",
63
+ description: "Update specific fields of a user profile",
64
+ tags: ["core/user"],
65
+ body: userUpdateSchema,
66
+ },
67
+ }
68
+ }
@@ -2,7 +2,7 @@ import type { IUserMethods, UserDoc } from "./user.schema"
2
2
  import bcrypt from "bcrypt"
3
3
  import { v4 as uuidv4 } from "uuid"
4
4
  import { randomIdGenerator } from "@/helpers"
5
- import { PluginFastifyInstance } from "types"
5
+ import type { PluginFastifyInstance } from "types"
6
6
  import mongoose from "mongoose"
7
7
 
8
8
  export const MODEL_BASENAME = "user"
@@ -17,6 +17,7 @@
17
17
  "@m/*": ["./src/modules/*"],
18
18
  "@shared/*": ["./src/shared/*"],
19
19
  "@/*": ["./src/*"],
20
+ "@app/*": ["./app/*"],
20
21
  "@raclettejs/core/backend": ["./src/index.ts", "./src"],
21
22
  "@raclettejs/core/backend/*": ["./src/*"],
22
23
  "@raclettejs/core": ["../..", "/app/types"]