create-weave-backend-app 2.6.0 → 2.7.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 (65) hide show
  1. package/dist/{create-app-viPT34gS.js → create-app-d0GGTvpm.js} +49 -23
  2. package/dist/create-app-d0GGTvpm.js.map +1 -0
  3. package/dist/create-app.js +1 -1
  4. package/dist/index.js +87 -2
  5. package/dist/index.js.map +1 -1
  6. package/package.json +4 -1
  7. package/template/+express+azure-web-pubsub/fonts/Impact.ttf +0 -0
  8. package/template/+express+azure-web-pubsub/fonts/Verdana-Bold.ttf +0 -0
  9. package/template/+express+azure-web-pubsub/fonts/Verdana-BoldItalic.ttf +0 -0
  10. package/template/+express+azure-web-pubsub/fonts/Verdana-Italic.ttf +0 -0
  11. package/template/+express+azure-web-pubsub/fonts/Verdana.ttf +0 -0
  12. package/template/+express+azure-web-pubsub/fonts/inter-bold.ttf +0 -0
  13. package/template/+express+azure-web-pubsub/fonts/inter-italic-bold.ttf +0 -0
  14. package/template/+express+azure-web-pubsub/fonts/inter-italic.ttf +0 -0
  15. package/template/+express+azure-web-pubsub/fonts/inter-regular.ttf +0 -0
  16. package/template/+express+azure-web-pubsub/fonts/sansita-bold.ttf +0 -0
  17. package/template/+express+azure-web-pubsub/fonts/sansita-regular.ttf +0 -0
  18. package/template/+express+azure-web-pubsub/nodemon.json +6 -0
  19. package/template/+express+azure-web-pubsub/src/api/v1/controllers/getImage.ts +2 -2
  20. package/template/+express+azure-web-pubsub/src/api/v1/controllers/getRoom.ts +33 -0
  21. package/template/+express+azure-web-pubsub/src/api/v1/controllers/getRoomConnect.ts +1 -1
  22. package/template/+express+azure-web-pubsub/src/api/v1/controllers/postExportToImage.ts +98 -0
  23. package/template/+express+azure-web-pubsub/src/api/v1/controllers/postRemoveBackground.ts +1 -2
  24. package/template/+express+azure-web-pubsub/src/api/v1/controllers/workers/exportToImage.ts +52 -0
  25. package/template/+express+azure-web-pubsub/src/api/v1/controllers/workers/types.ts +5 -0
  26. package/template/+express+azure-web-pubsub/src/api/v1/router.ts +6 -2
  27. package/template/+express+azure-web-pubsub/src/canvas/fonts.ts +167 -0
  28. package/template/+express+azure-web-pubsub/src/canvas/nodes/color-token/color-token.ts +155 -0
  29. package/template/+express+azure-web-pubsub/src/canvas/types.ts +13 -0
  30. package/template/+express+azure-web-pubsub/src/canvas/weave.ts +207 -0
  31. package/template/+express+azure-web-pubsub/src/store.ts +9 -3
  32. package/template/+express+azure-web-pubsub/src/utils.ts +23 -0
  33. package/template/+express+azure-web-pubsub/src/workers/workers.ts +40 -0
  34. package/template/+express+azure-web-pubsub/tsconfig.json +1 -1
  35. package/template/+express+websockets/example.gitignore +5 -5
  36. package/template/+express+websockets/fonts/Impact.ttf +0 -0
  37. package/template/+express+websockets/fonts/Verdana-Bold.ttf +0 -0
  38. package/template/+express+websockets/fonts/Verdana-BoldItalic.ttf +0 -0
  39. package/template/+express+websockets/fonts/Verdana-Italic.ttf +0 -0
  40. package/template/+express+websockets/fonts/Verdana.ttf +0 -0
  41. package/template/+express+websockets/fonts/inter-bold.ttf +0 -0
  42. package/template/+express+websockets/fonts/inter-italic-bold.ttf +0 -0
  43. package/template/+express+websockets/fonts/inter-italic.ttf +0 -0
  44. package/template/+express+websockets/fonts/inter-regular.ttf +0 -0
  45. package/template/+express+websockets/fonts/sansita-bold.ttf +0 -0
  46. package/template/+express+websockets/fonts/sansita-regular.ttf +0 -0
  47. package/template/+express+websockets/nodemon.json +6 -0
  48. package/template/+express+websockets/src/api/v1/controllers/getImage.ts +2 -2
  49. package/template/+express+websockets/src/api/v1/controllers/getRoom.ts +33 -0
  50. package/template/+express+websockets/src/api/v1/controllers/postExportToImage.ts +96 -0
  51. package/template/+express+websockets/src/api/v1/controllers/postRemoveBackground.ts +1 -2
  52. package/template/+express+websockets/src/api/v1/controllers/workers/exportToImage.ts +48 -0
  53. package/template/+express+websockets/src/api/v1/controllers/workers/types.ts +1 -0
  54. package/template/+express+websockets/src/api/v1/router.ts +7 -1
  55. package/template/+express+websockets/src/canvas/fonts.ts +163 -0
  56. package/template/+express+websockets/src/canvas/nodes/color-token/color-token.ts +151 -0
  57. package/template/+express+websockets/src/canvas/types.ts +13 -0
  58. package/template/+express+websockets/src/canvas/weave.ts +203 -0
  59. package/template/+express+websockets/src/server.ts +4 -0
  60. package/template/+express+websockets/src/store.ts +2 -2
  61. package/template/+express+websockets/src/utils.ts +23 -0
  62. package/template/+express+websockets/src/workers/workers.ts +36 -0
  63. package/template/+express+websockets/tsconfig.json +1 -1
  64. package/template/package.json +7 -1
  65. package/dist/create-app-viPT34gS.js.map +0 -1
@@ -0,0 +1,52 @@
1
+ // SPDX-FileCopyrightText: 2025 2025 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.)
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ import { parentPort } from 'worker_threads'
6
+ import sharp from 'sharp'
7
+ import { WEAVE_EXPORT_FORMATS } from '@inditextech/weave-types'
8
+ import { renderWeaveRoom } from '../../../../canvas/weave.js'
9
+
10
+ parentPort?.on('message', async ({ config, roomData, nodes, options }) => {
11
+ const { instance, destroy } = await renderWeaveRoom(config, roomData)
12
+
13
+ const { composites, width, height } = await instance.exportNodesServerSide(
14
+ nodes,
15
+ (nodes) => nodes,
16
+ {
17
+ format: options.format,
18
+ padding: options.padding,
19
+ pixelRatio: options.pixelRatio,
20
+ backgroundColor: options.backgroundColor,
21
+ quality: options.quality
22
+ }
23
+ )
24
+
25
+ destroy()
26
+
27
+ try {
28
+ const composedImage = sharp({
29
+ create: {
30
+ width,
31
+ height,
32
+ channels: 4,
33
+ background: { r: 0, g: 0, b: 0, alpha: 0 }
34
+ }
35
+ }).composite(composites)
36
+
37
+ let imagePipeline = composedImage
38
+ if (options.format === WEAVE_EXPORT_FORMATS.JPEG) {
39
+ imagePipeline = composedImage.jpeg({
40
+ quality: (options.quality ?? 0.8) * 100
41
+ })
42
+ }
43
+ if (options.format === WEAVE_EXPORT_FORMATS.PNG) {
44
+ imagePipeline = composedImage.png()
45
+ }
46
+
47
+ const imageBuffer = await imagePipeline.toBuffer()
48
+ parentPort?.postMessage(imageBuffer, [imageBuffer.buffer as ArrayBuffer])
49
+ } catch (error) {
50
+ parentPort?.postMessage((error as Error).message)
51
+ }
52
+ })
@@ -0,0 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2025 2025 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.)
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ export type ExportToImageWorkerResult = Buffer
@@ -1,6 +1,5 @@
1
1
  import { Express, Router } from 'express'
2
2
  import multer from 'multer'
3
- import { getAzureWebPubsubServer } from '@/store'
4
3
  import { getHealthController } from './controllers/getHealth.js'
5
4
  import { getRoomConnectController } from './controllers/getRoomConnect.js'
6
5
  import { getImageController } from './controllers/getImage.js'
@@ -8,6 +7,8 @@ import { postUploadImageController } from './controllers/postUploadImage.js'
8
7
  import { delImageController } from './controllers/delImage.js'
9
8
  import { getImagesController } from './controllers/getImages.js'
10
9
  import { postRemoveBackgroundController } from './controllers/postRemoveBackground.js'
10
+ import { postExportToImageController } from './controllers/postExportToImage.js'
11
+ import { getRoomController } from './controllers/getRoom.js'
11
12
 
12
13
  const router: Router = Router()
13
14
 
@@ -25,7 +26,7 @@ export function setupApiV1Router(app: Express) {
25
26
  router.get(`/health`, getHealthController())
26
27
 
27
28
  // Room handling API
28
- router.use(getAzureWebPubsubServer().getMiddleware())
29
+ router.get(`/rooms/:roomId`, getRoomController())
29
30
  router.get(`/rooms/:roomId/connect`, getRoomConnectController())
30
31
 
31
32
  // Images handling API
@@ -42,5 +43,8 @@ export function setupApiV1Router(app: Express) {
42
43
  )
43
44
  router.delete(`/rooms/:roomId/images/:imageId`, delImageController())
44
45
 
46
+ // Render Canvas API
47
+ router.post(`/export`, postExportToImageController())
48
+
45
49
  app.use('/api/v1', router)
46
50
  }
@@ -0,0 +1,167 @@
1
+ // SPDX-FileCopyrightText: 2025 2025 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.)
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ import path from 'node:path'
6
+ import { registerFont } from 'canvas'
7
+ import { FontLibrary } from 'skia-canvas'
8
+ import { CanvasFont, SkiaFont } from './types.js'
9
+
10
+ let registered = false
11
+
12
+ export const registerSkiaFonts = () => {
13
+ if (registered) {
14
+ return
15
+ }
16
+
17
+ FontLibrary.reset()
18
+
19
+ const fonts: SkiaFont[] = [
20
+ // Arial font family
21
+ {
22
+ family: 'Arial',
23
+ paths: [
24
+ path.resolve(process.cwd(), 'fonts/ARIALI.ttf'),
25
+ path.resolve(process.cwd(), 'fonts/ARIAL.ttf')
26
+ ]
27
+ },
28
+ // NotoSansMono font family
29
+ {
30
+ family: 'NotoSansMono',
31
+ // paths: [path.resolve(process.cwd(), "fonts/NotoSansMono-Italic.ttf")],
32
+ paths: [path.resolve(process.cwd(), 'fonts/NotoSansMono-Regular.ttf')]
33
+ },
34
+ // Impact font family
35
+ {
36
+ family: 'Impact',
37
+ paths: [path.resolve(process.cwd(), 'fonts/Impact.ttf')]
38
+ },
39
+ // Verdana font family
40
+ {
41
+ family: 'Verdana',
42
+ paths: [
43
+ path.resolve(process.cwd(), 'fonts/Verdana-Italic.ttf'),
44
+ path.resolve(process.cwd(), 'fonts/Verdana-Bold.ttf'),
45
+ path.resolve(process.cwd(), 'fonts/Verdana-BoldItalic.ttf'),
46
+ path.resolve(process.cwd(), 'fonts/Verdana.ttf')
47
+ ]
48
+ },
49
+ // Inter font family
50
+ {
51
+ family: 'Inter',
52
+ paths: [
53
+ path.resolve(process.cwd(), 'fonts/inter-bold.ttf'),
54
+ path.resolve(process.cwd(), 'fonts/inter-italic.ttf'),
55
+ path.resolve(process.cwd(), 'fonts/inter-italic-bold.ttf'),
56
+ path.resolve(process.cwd(), 'fonts/inter-regular.ttf')
57
+ ]
58
+ },
59
+ // Sansita font family
60
+ {
61
+ family: 'Sansita',
62
+ paths: [
63
+ path.resolve(process.cwd(), 'fonts/sansita-bold.ttf'),
64
+ path.resolve(process.cwd(), 'fonts/sansita-regular.ttf')
65
+ ]
66
+ }
67
+ ]
68
+
69
+ for (const font of fonts) {
70
+ FontLibrary.use(font.family, font.paths)
71
+ }
72
+
73
+ registered = true
74
+ }
75
+
76
+ export const registerCanvasFonts = () => {
77
+ const fonts: CanvasFont[] = [
78
+ // Impact font family
79
+ {
80
+ path: path.resolve(process.cwd(), 'fonts/Impact.ttf'),
81
+ fontFace: {
82
+ family: 'Impact',
83
+ weight: '400',
84
+ style: 'normal'
85
+ }
86
+ },
87
+ // Verdana font family
88
+ {
89
+ path: path.resolve(process.cwd(), 'fonts/Verdana.ttf'),
90
+ fontFace: {
91
+ family: 'Verdana',
92
+ weight: '400',
93
+ style: 'normal'
94
+ }
95
+ },
96
+ {
97
+ path: path.resolve(process.cwd(), 'fonts/Verdana-Bold.ttf'),
98
+ fontFace: {
99
+ family: 'Verdana',
100
+ weight: '700',
101
+ style: 'normal'
102
+ }
103
+ },
104
+ {
105
+ path: path.resolve(process.cwd(), 'fonts/Verdana-Italic.ttf'),
106
+ fontFace: {
107
+ family: 'Verdana',
108
+ weight: '400',
109
+ style: 'italic'
110
+ }
111
+ },
112
+ {
113
+ path: path.resolve(process.cwd(), 'fonts/Verdana-BoldItalic.ttf'),
114
+ fontFace: {
115
+ family: 'Verdana',
116
+ weight: '700',
117
+ style: 'italic'
118
+ }
119
+ },
120
+ // Inter font family
121
+ {
122
+ path: path.resolve(process.cwd(), 'fonts/inter-regular.ttf'),
123
+ fontFace: {
124
+ family: 'Inter',
125
+ weight: '400',
126
+ style: 'normal'
127
+ }
128
+ },
129
+ {
130
+ path: path.resolve(process.cwd(), 'fonts/inter-bold.ttf'),
131
+ fontFace: {
132
+ family: 'Inter',
133
+ weight: '700',
134
+ style: 'normal'
135
+ }
136
+ },
137
+ {
138
+ path: path.resolve(process.cwd(), 'fonts/inter-italic.ttf'),
139
+ fontFace: {
140
+ family: 'Inter',
141
+ weight: '400',
142
+ style: 'italic'
143
+ }
144
+ },
145
+ {
146
+ path: path.resolve(process.cwd(), 'fonts/inter-italic-bold.ttf'),
147
+ fontFace: {
148
+ family: 'Inter',
149
+ weight: '700',
150
+ style: 'italic'
151
+ }
152
+ },
153
+ // Sansita font family
154
+ {
155
+ path: path.resolve(process.cwd(), 'fonts/sansita-regular.ttf'),
156
+ fontFace: {
157
+ family: 'Sansita',
158
+ weight: '400',
159
+ style: 'normal'
160
+ }
161
+ }
162
+ ]
163
+
164
+ for (const font of fonts) {
165
+ registerFont(font.path, font.fontFace)
166
+ }
167
+ }
@@ -0,0 +1,155 @@
1
+ // SPDX-FileCopyrightText: 2025 2025 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.)
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ import Konva from 'konva'
6
+ import { WeaveNode } from '@inditextech/weave-sdk/server'
7
+ import {
8
+ WeaveElementAttributes,
9
+ WeaveElementInstance
10
+ } from '@inditextech/weave-types'
11
+
12
+ export const COLOR_TOKEN_NODE_TYPE = 'color-token'
13
+
14
+ export class ColorTokenNode extends WeaveNode {
15
+ protected nodeType = COLOR_TOKEN_NODE_TYPE
16
+
17
+ onRender(props: WeaveElementAttributes) {
18
+ const { id } = props
19
+
20
+ const colorTokenColor = props.colorToken ?? '#DEFFA0'
21
+
22
+ const colorTokenParams = {
23
+ ...props
24
+ }
25
+ delete colorTokenParams.zIndex
26
+
27
+ const colorTokenNode = new Konva.Group({
28
+ ...colorTokenParams,
29
+ width: colorTokenParams.width,
30
+ height: colorTokenParams.height,
31
+ name: 'node'
32
+ })
33
+
34
+ this.setupDefaultNodeAugmentation(colorTokenNode)
35
+
36
+ const internalRect = new Konva.Rect({
37
+ groupId: id,
38
+ nodeId: id,
39
+ id: `${id}-colorToken`,
40
+ x: 0,
41
+ y: 0,
42
+ fill: '#FFFFFFFF',
43
+ width: colorTokenParams.width,
44
+ height: colorTokenParams.height,
45
+ strokeEnabled: false
46
+ })
47
+
48
+ colorTokenNode.add(internalRect)
49
+
50
+ const internalRect2 = new Konva.Rect({
51
+ id: `${id}-colorToken-1`,
52
+ groupId: id,
53
+ nodeId: id,
54
+ x: 0,
55
+ y: 0,
56
+ fill: colorTokenColor,
57
+ strokeWidth: 0,
58
+ strokeEnabled: false,
59
+ width: colorTokenParams.width,
60
+ height: (colorTokenParams.height ?? 0) - 60,
61
+ listening: false,
62
+ draggable: false
63
+ })
64
+
65
+ colorTokenNode.add(internalRect2)
66
+
67
+ const internalText = new Konva.Text({
68
+ id: `${id}-colorToken-code`,
69
+ groupId: id,
70
+ nodeId: id,
71
+ x: 20,
72
+ y: 260,
73
+ fontSize: 20,
74
+ fontFamily: 'Inter, sans-serif',
75
+ fill: '#CCCCCCFF',
76
+ strokeEnabled: false,
77
+ stroke: '#000000FF',
78
+ strokeWidth: 1,
79
+ text: `${colorTokenColor}`,
80
+ width: (colorTokenParams.width ?? 0) - 40,
81
+ height: 20,
82
+ align: 'left',
83
+ listening: false,
84
+ draggable: false
85
+ })
86
+
87
+ colorTokenNode.add(internalText)
88
+
89
+ const border = new Konva.Rect({
90
+ groupId: id,
91
+ nodeId: id,
92
+ id: `${id}-colorToken-border`,
93
+ x: 0,
94
+ y: 0,
95
+ fill: 'transparent',
96
+ width: colorTokenParams.width,
97
+ height: colorTokenParams.height,
98
+ strokeScaleEnabled: true,
99
+ stroke: 'black',
100
+ strokeWidth: 1
101
+ })
102
+
103
+ colorTokenNode.add(border)
104
+ border.moveToTop()
105
+
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ ;(colorTokenNode as any).getTransformerProperties = () => {
108
+ const baseConfig = this.defaultGetTransformerProperties({})
109
+ return {
110
+ ...baseConfig,
111
+ resizeEnabled: false,
112
+ enabledAnchors: [] as string[],
113
+ borderStrokeWidth: 2,
114
+ padding: 0
115
+ }
116
+ }
117
+
118
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
+ ;(colorTokenNode as any).allowedAnchors = () => {
120
+ return []
121
+ }
122
+
123
+ this.setupDefaultNodeEvents(colorTokenNode)
124
+
125
+ return colorTokenNode
126
+ }
127
+
128
+ onUpdate(
129
+ nodeInstance: WeaveElementInstance,
130
+ nextProps: WeaveElementAttributes
131
+ ) {
132
+ const { id, colorToken } = nextProps
133
+
134
+ const colorTokenNode = nodeInstance as Konva.Group
135
+
136
+ const nodeInstanceZIndex = nodeInstance.zIndex()
137
+ nodeInstance.setAttrs({
138
+ ...nextProps,
139
+ zIndex: nodeInstanceZIndex
140
+ })
141
+
142
+ const colorTokenColor = colorToken ?? '#DEFFA0'
143
+
144
+ const colorTokenNode1 = colorTokenNode.findOne(`#${id}-colorToken-1`)
145
+ if (colorTokenNode1) {
146
+ colorTokenNode1.setAttrs({
147
+ fill: colorTokenColor
148
+ })
149
+ }
150
+ const colorTokenCode = colorTokenNode.findOne(`#${id}-colorToken-code`)
151
+ if (colorTokenCode) {
152
+ colorTokenCode.setAttr('text', `${colorTokenColor}`)
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,13 @@
1
+ export type SkiaFont = {
2
+ family: string
3
+ paths: string[]
4
+ }
5
+
6
+ export type CanvasFont = {
7
+ path: string
8
+ fontFace: {
9
+ family: string
10
+ weight?: string | undefined
11
+ style?: string | undefined
12
+ }
13
+ }
@@ -0,0 +1,207 @@
1
+ // SPDX-FileCopyrightText: 2025 2025 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.)
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ import { WeaveStoreStandalone } from '@inditextech/weave-store-standalone/server'
6
+ import {
7
+ Weave,
8
+ WeaveStageNode,
9
+ WeaveLayerNode,
10
+ WeaveGroupNode,
11
+ WeaveRectangleNode,
12
+ WeaveEllipseNode,
13
+ WeaveLineNode,
14
+ WeaveTextNode,
15
+ WeaveImageNode,
16
+ WeaveVideoNode,
17
+ WeaveStarNode,
18
+ WeaveArrowNode,
19
+ WeaveRegularPolygonNode,
20
+ WeaveFrameNode,
21
+ WeaveStrokeNode,
22
+ // setupSkiaBackend,
23
+ setupCanvasBackend
24
+ } from '@inditextech/weave-sdk/server'
25
+ import { ColorTokenNode } from './nodes/color-token/color-token.js'
26
+ import { isAbsoluteUrl, stripOrigin } from '../utils.js'
27
+ import { ServiceConfig } from '../types.js'
28
+ import {
29
+ // registerSkiaFonts,
30
+ registerCanvasFonts
31
+ } from './fonts.js'
32
+
33
+ export type RenderWeaveRoom = {
34
+ instance: Weave
35
+ destroy: () => void
36
+ }
37
+
38
+ export const renderWeaveRoom = (
39
+ config: ServiceConfig,
40
+ roomData: string
41
+ ): Promise<RenderWeaveRoom> => {
42
+ let weave: Weave | undefined = undefined
43
+
44
+ // eslint-disable-next-line no-async-promise-executor
45
+ return new Promise(async (resolve) => {
46
+ const destroyWeaveRoom = () => {
47
+ if (weave) {
48
+ weave.destroy()
49
+ }
50
+ }
51
+
52
+ // Setup Skia backend
53
+ // registerSkiaFonts();
54
+ // await setupSkiaBackend();
55
+
56
+ // Setup Canvas backend
57
+ registerCanvasFonts()
58
+ await setupCanvasBackend()
59
+
60
+ const store = new WeaveStoreStandalone(
61
+ {
62
+ roomData
63
+ },
64
+ {
65
+ getUser: () => {
66
+ return {
67
+ id: 'user-dummy',
68
+ name: 'User Dummy',
69
+ email: 'user@mail.com'
70
+ }
71
+ }
72
+ }
73
+ )
74
+
75
+ weave = new Weave(
76
+ {
77
+ store,
78
+ nodes: getNodes(config),
79
+ actions: [],
80
+ plugins: [],
81
+ fonts: [],
82
+ logger: {
83
+ level: 'info'
84
+ }
85
+ },
86
+ {
87
+ container: undefined,
88
+ width: 800,
89
+ height: 600
90
+ }
91
+ )
92
+
93
+ let roomLoaded = false
94
+
95
+ const checkIfRoomLoaded = () => {
96
+ if (!weave) {
97
+ return false
98
+ }
99
+
100
+ if (!weave.getStage()) {
101
+ return false
102
+ }
103
+
104
+ if (roomLoaded && weave.asyncElementsLoaded()) {
105
+ return true
106
+ }
107
+
108
+ return false
109
+ }
110
+
111
+ weave.addEventListener('onRoomLoaded', async (status: boolean) => {
112
+ if (!weave) {
113
+ return
114
+ }
115
+
116
+ if (!weave.getStage()) {
117
+ return false
118
+ }
119
+
120
+ if (status) {
121
+ roomLoaded = true
122
+ }
123
+
124
+ if (checkIfRoomLoaded()) {
125
+ resolve({ instance: weave, destroy: destroyWeaveRoom })
126
+ }
127
+ })
128
+
129
+ weave.addEventListener('onAsyncElementChange', () => {
130
+ if (!weave) {
131
+ return
132
+ }
133
+
134
+ if (!weave.getStage()) {
135
+ return false
136
+ }
137
+
138
+ if (checkIfRoomLoaded()) {
139
+ resolve({ instance: weave, destroy: destroyWeaveRoom })
140
+ }
141
+ })
142
+
143
+ weave.start()
144
+ })
145
+ }
146
+
147
+ const getNodes = (config: ServiceConfig) => {
148
+ return [
149
+ new WeaveStageNode(),
150
+ new WeaveLayerNode(),
151
+ new WeaveGroupNode(),
152
+ new WeaveRectangleNode(),
153
+ new WeaveEllipseNode(),
154
+ new WeaveLineNode(),
155
+ new WeaveStrokeNode(),
156
+ new WeaveTextNode(),
157
+ new WeaveImageNode({
158
+ config: {
159
+ urlTransformer: (url: string) => {
160
+ const isAbsolute = isAbsoluteUrl(url)
161
+
162
+ let relativeUrl = url
163
+ if (isAbsolute) {
164
+ relativeUrl = stripOrigin(url)
165
+ }
166
+
167
+ const transformedUrl = relativeUrl.replace('/weavebff', '')
168
+ return `http://localhost:${config.service.port}${transformedUrl}`
169
+ }
170
+ }
171
+ }),
172
+ new WeaveVideoNode({
173
+ config: {
174
+ urlTransformer: (url: string) => {
175
+ const isAbsolute = isAbsoluteUrl(url)
176
+
177
+ let relativeUrl = url
178
+ if (isAbsolute) {
179
+ relativeUrl = stripOrigin(url)
180
+ }
181
+
182
+ const transformedUrl = relativeUrl.replace('/weavebff', '')
183
+ return `http://localhost:${config.service.port}${transformedUrl}`
184
+ }
185
+ }
186
+ }),
187
+ new WeaveStarNode(),
188
+ new WeaveArrowNode(),
189
+ new WeaveRegularPolygonNode(),
190
+ new WeaveFrameNode({
191
+ config: {
192
+ fontFamily: "'Inter', sans-serif",
193
+ fontStyle: 'normal',
194
+ fontSize: 14,
195
+ borderColor: '#9E9994',
196
+ fontColor: '#757575',
197
+ titleMargin: 5,
198
+ transform: {
199
+ rotateEnabled: false,
200
+ resizeEnabled: false,
201
+ enabledAnchors: [] as string[]
202
+ }
203
+ }
204
+ }),
205
+ new ColorTokenNode()
206
+ ]
207
+ }
@@ -1,7 +1,11 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs/promises'
3
+ import { fileURLToPath } from 'node:url'
3
4
  import { WeaveAzureWebPubsubServer } from '@inditextech/weave-store-azure-web-pubsub/server'
4
- import { createFolder, existsFolder } from '@/utils'
5
+ import { createFolder, existsFolder } from '@/utils.js'
6
+
7
+ const __filename = fileURLToPath(import.meta.url)
8
+ const __dirname = path.dirname(__filename)
5
9
 
6
10
  const endpoint = process.env.WEAVE_AZURE_WEB_PUBSUB_ENDPOINT
7
11
  const key = process.env.WEAVE_AZURE_WEB_PUBSUB_KEY
@@ -23,9 +27,8 @@ export const getAzureWebPubsubServer = () => {
23
27
 
24
28
  export const setupStore = () => {
25
29
  azureWebPubsubServer = new WeaveAzureWebPubsubServer({
26
- pubsubConfig: {
30
+ pubSubConfig: {
27
31
  endpoint,
28
- key,
29
32
  hubName
30
33
  },
31
34
  fetchRoom: async (docName: string) => {
@@ -48,6 +51,8 @@ export const setupStore = () => {
48
51
  actualState: Uint8Array<ArrayBufferLike>
49
52
  ) => {
50
53
  try {
54
+ console.log(`Persisting room ${docName}...`)
55
+
51
56
  const roomsFolder = path.join(__dirname, 'rooms')
52
57
 
53
58
  if (!(await existsFolder(roomsFolder))) {
@@ -68,6 +73,7 @@ export const setupStore = () => {
68
73
  }
69
74
 
70
75
  const roomsFile = path.join(roomsFolder, docName)
76
+ console.log(`Persisting room ${roomsFile}...`)
71
77
  await fs.writeFile(roomsFile, actualState)
72
78
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
73
79
  } catch (_) {
@@ -17,6 +17,20 @@ export const getFileContents = async (
17
17
  }
18
18
  }
19
19
 
20
+ export const existFile = async (filePath: string) => {
21
+ try {
22
+ const stats = await fs.stat(filePath)
23
+ return stats.isFile()
24
+ } catch (err) {
25
+ console.error(
26
+ `Error reading file ${filePath}: ${
27
+ err instanceof Error ? err.message : err
28
+ }`
29
+ )
30
+ return false
31
+ }
32
+ }
33
+
20
34
  export const existsFolder = async (folderPath: string) => {
21
35
  try {
22
36
  const stats = await fs.stat(folderPath)
@@ -30,3 +44,12 @@ export const existsFolder = async (folderPath: string) => {
30
44
  export const createFolder = async (folderPath: string): Promise<void> => {
31
45
  await fs.mkdir(folderPath, { recursive: true })
32
46
  }
47
+
48
+ export function isAbsoluteUrl(url: string): boolean {
49
+ return /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(url)
50
+ }
51
+
52
+ export function stripOrigin(url: string): string {
53
+ const parsedUrl = new URL(url)
54
+ return parsedUrl.pathname + parsedUrl.search + parsedUrl.hash
55
+ }