c0ckp1t 1.0.17 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Config.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import {deepMerge, DEFAULTS} from "CoreUtils";
1
+ import {deepMerge, DEFAULTS, buildNavTree, buildRoutes, defaultVueComponents} from "ConfigUtils";
2
2
  /**
3
3
  * Configuration Factory for C0ckp1t Applications
4
4
  *
@@ -20,197 +20,6 @@ import {deepMerge, DEFAULTS} from "CoreUtils";
20
20
  * isDev: false,
21
21
  * });
22
22
  */
23
- // ________________________________________________________________________________
24
- // Default Nav Tree Builder
25
- // ________________________________________________________________________________
26
- export function buildNavTree(instanceId) {
27
- return {
28
- icon: "fa-house",
29
- depth: 0,
30
- endpoint: "/",
31
- isLeaf: false,
32
- isRoot: true,
33
- name: "",
34
- path: [],
35
- children: [
36
- {
37
- depth: 1,
38
- endpoint: `/${instanceId}/connections`,
39
- isLeaf: true,
40
- isRoot: false,
41
- path: ["connections"],
42
- name: "Connections",
43
- children: []
44
- },
45
- {
46
- depth: 1,
47
- endpoint: `/${instanceId}/cache`,
48
- isLeaf: true,
49
- isRoot: false,
50
- path: ["cache"],
51
- name: "Cache",
52
- children: []
53
- },
54
- {
55
- icon: "fa-network-wired",
56
- depth: 1,
57
- endpoint: `/${instanceId}/traffic`,
58
- isLeaf: true,
59
- isRoot: false,
60
- path: ["traffic"],
61
- name: "Traffic",
62
- children: []
63
- },
64
- {
65
- icon: "fa-bell",
66
- depth: 1,
67
- endpoint: `/${instanceId}/notifies`,
68
- isLeaf: true,
69
- isRoot: false,
70
- path: ["notifies"],
71
- name: "Notifies",
72
- children: []
73
- },
74
- {
75
- icon: "fa-info",
76
- depth: 1,
77
- endpoint: `/${instanceId}/docs`,
78
- isLeaf: true,
79
- isRoot: false,
80
- path: ["docs"],
81
- name: "Documentation",
82
- children: []
83
- },
84
- {
85
- icon: "fa-info",
86
- depth: 1,
87
- endpoint: `/${instanceId}/components`,
88
- isLeaf: true,
89
- isRoot: false,
90
- path: ["components"],
91
- name: "Components",
92
- children: [
93
- {
94
- icon: "fa-info",
95
- depth: 2,
96
- endpoint: `/${instanceId}/components/bootstrap`,
97
- isLeaf: true,
98
- isRoot: false,
99
- path: ["bootstrap"],
100
- name: "Bootstrap",
101
- children: []
102
- },
103
- {
104
- icon: "fa-info",
105
- depth: 2,
106
- endpoint: `/${instanceId}/components/basic`,
107
- isLeaf: true,
108
- isRoot: false,
109
- path: ["basic"],
110
- name: "Basic",
111
- children: []
112
- },
113
- {
114
- icon: "fa-info",
115
- depth: 2,
116
- endpoint: `/${instanceId}/components/advanced`,
117
- isLeaf: true,
118
- isRoot: false,
119
- path: ["advanced"],
120
- name: "Advanced",
121
- children: []
122
- },
123
- {
124
- icon: "fa-info",
125
- depth: 2,
126
- endpoint: `/${instanceId}/components/theme`,
127
- isLeaf: true,
128
- isRoot: false,
129
- path: ["theme"],
130
- name: "Theme",
131
- children: []
132
- },
133
- ]
134
- }
135
- ]
136
- };
137
- }
138
-
139
- // ________________________________________________________________________________
140
- // Routes Builder
141
- // ________________________________________________________________________________
142
- export function buildRoutes(instanceId = "default", prefix = "") {
143
- return [
144
- { path: '/', name: 'root', children: [
145
- {path: '', redirect: `/${instanceId}/docs/Introduction.md`},
146
- {path: `${instanceId}`, children: [
147
- {path: 'docs', redirect: `/${instanceId}/docs/Introduction.md`},
148
- {path: 'docs/:pathMatch(.*)*', location: `${prefix}/core/pages/Documentation.vue`},
149
- {path: 'connections', location: `${prefix}/core/pages/Connections.vue`},
150
- {path: 'connections/:id', location: `${prefix}/core/pages/Connection.vue`},
151
- {path: 'cache', location: `${prefix}/core/pages/Cache.vue`},
152
- {path: 'traffic', location: `${prefix}/core/pages/Traffic.vue`},
153
- {path: 'notifies', location: `${prefix}/core/pages/Notifies.vue`},
154
- {path: 'components', location: `${prefix}/core/pages/frontend/Components.vue`, children: [
155
- {path: 'basic', location: `${prefix}/core/pages/frontend/ComponentsBasic.vue`},
156
- {path: 'advanced', location: `${prefix}/core/pages/frontend/ComponentsAdv.vue`},
157
- {path: 'theme', location: `${prefix}/core/pages/frontend/Theme.vue`},
158
- {path: 'bootstrap', location: `${prefix}/core/pages/frontend/Bootstrap.vue`},
159
- ]},
160
- ]}
161
- ] },
162
- { path: '/:pathMatch(.*)*', name: '404', location: `${prefix}/core/Page404.vue` }
163
- ];
164
- }
165
-
166
- // ________________________________________________________________________________
167
- // Components
168
- // ________________________________________________________________________________
169
- /**
170
- * Return the default Vue components
171
- * using sha1 hashes
172
- * @returns {Object}
173
- */
174
- export function defaultVueComponents(prefix = "") {
175
- return {
176
- ExecButton: { path: `${prefix}/components/ExecButton.vue`, hash: `97e3d2ce89808c5a69f41404e1337f743015f0cc` },
177
- XInput: { path: `${prefix}/components/xinput.vue`, hash: `cd47d7d038316367df6fd7e265aa3625b72a7777` },
178
- XInput2: { path: `${prefix}/components/xinput2.vue`, hash: `320e52ac991baebded7c2a9f52a4cfc2cde47b55` },
179
- XLabel: { path: `${prefix}/components/xlabel.vue`, hash: `90a9837aa8e06f1d3a5b7601337afd78a872d181` },
180
- XDropdown: { path: `${prefix}/components/xdropdown.vue`, hash: `52b29b72fd6512eefccd9caa76e26dd91d9f9f9e` },
181
- XDropdown2: { path: `${prefix}/components/xdropdown2.vue`, hash: `e7b163eda42fb7e1a6f07b011c423f8f275eb65d` },
182
- XSection: { path: `${prefix}/components/xsection.vue`, hash: `27285d606f57d13f80156bb11cd34449f88df950` },
183
- XTableOpen: { path: `${prefix}/components/xtable-open.vue`, hash: `a1b9a4f670817978022a4c596e2e2f89dd4568c2` },
184
- XCollapse: { path: `${prefix}/components/xcollapse.vue`, hash: `ba1479bd1080a4fa5abdf6c91c7328ae679f78e6` },
185
- XToggle: { path: `${prefix}/components/xtoggle.vue`, hash: `2f6871d2d3069ac8f35b6bf76de6c2109f42d9d1` },
186
- XToggle3: { path: `${prefix}/components/xtoggle3.vue`, hash: `61d0464e6ed9983e817eb0cde928dceb7c0fdc75` },
187
- XCheck: { path: `${prefix}/components/xcheck.vue`, hash: `ee0d6b30600fb41589123f6d66f2791f1332d4f7` },
188
- XCheckbox: { path: `${prefix}/components/xcheckbox.vue`, hash: `a290c0cbb7bffce83c235dd5a1c98ed9c441a5b0` },
189
- XTextarea: { path: `${prefix}/components/xtextarea.vue`, hash: `f8bb08419082aa5443630ab07172674b50c7a248` },
190
- XHidden: { path: `${prefix}/components/xhidden.vue`, hash: `ecb396e12dd894040e715c0854275e4d5016fcb9` },
191
- XCode: { path: `${prefix}/components/xcode.vue`, hash: `4d9d9165fea0539c9a983fcdffae8dedcfd537ae` },
192
- XCodeSlot: { path: `${prefix}/components/xcode-slot.vue`, hash: `` },
193
- XButton: { path: `${prefix}/components/xbutton.vue`, hash: `2e956caa47e46377ea5a809f7438d0fc38be73b9` },
194
- XTabs: { path: `${prefix}/components/xtabs.vue`, hash: `83dc219106bdc86ae86dcd16cf95ebd7f11bc952` },
195
- XKv: { path: `${prefix}/components/xkv.vue`, hash: `8951d3a5e3786cfc9c705b13c1f71e3f90dd2552` },
196
- XNav: { path: `${prefix}/components/xnav.vue`, hash: `8d51c73e5716deed3577652e362c50526ddbe4e1` },
197
- XMap: { path: `${prefix}/components/xmap.vue`, hash: `daee357d9e2ef96df0166dd7add0339d46a1cc01` },
198
- XList: { path: `${prefix}/components/xlist.vue`, hash: `217ced04a333238d169c300a721b75f0ddd5e95b` },
199
- XJson: { path: `${prefix}/components/xjson.vue`, hash: `0a3ef6265b4070b0f002d659776c02756cc1da5a` },
200
- XCard: { path: `${prefix}/components/xcard.vue`, hash: `de3fbb23ae7b00d4c90a717dd361cb9315e9ded6` },
201
- XCardH: { path: `${prefix}/components/xcard-h.vue`, hash: `de4d42f1056c5d2b8431f15e6b1180d9f9898ac2` },
202
- XColor: { path: `${prefix}/components/xcolor.vue`, hash: `9bf9497ff66e213277f17af290c21c0a35752510` },
203
- "v-ace-editor": { path: `${prefix}/components/vue3-ace-editor.vue`, hash: `70ce4a39152af5cf0f7cb6b1d4fdafc8b6225edc` },
204
- XMarkdown: { path: `${prefix}/components/xmarkdown.vue`, hash: `15f835547fab8a8c8aad47d72640c8e918a7b9da` },
205
- XSound: { path: `${prefix}/components/xsound.vue`, hash: `3e8ad4aa3c767f757dd49b99aa6f961547caf970` },
206
- XUpload: { path: `${prefix}/components/xupload.vue`, hash: `7a872277e0047fca11e950efe08f2bffa670abdb` },
207
- XTree: { path: `${prefix}/components/xtree.vue`, hash: `3b6534e86996c48ab05072a9b793ecc78d83a0eb` },
208
- CodeMirror: { path: `${prefix}/components/code-mirror.vue`, hash: `3ce1028adb75831e01c4264d5764c14f60a1bd00` },
209
- XTerminal: { path: `${prefix}/components/xterminal.vue`, hash: `1c01f92c0a08bd4937f9768a1d49f12a9a84feea` },
210
- }
211
- }
212
-
213
-
214
23
  // ________________________________________________________________________________
215
24
  // Factory
216
25
  // ________________________________________________________________________________
@@ -20,6 +20,32 @@ const emit = defineEmits(['select'])
20
20
 
21
21
  const id = `json${Math.floor(Math.random() * 100000000)}`
22
22
 
23
+ // ________________________________________________________________________________
24
+ // SANITIZE - deep-clean objects for json-viewer (handles undefined, functions,
25
+ // circular refs, symbols, and Vue reactive proxies)
26
+ // ________________________________________________________________________________
27
+ function sanitize(obj, seen = new WeakSet()) {
28
+ if (obj === undefined) return null
29
+ if (obj === null) return null
30
+ if (typeof obj === 'function') return `[Function: ${obj.name || 'anonymous'}]`
31
+ if (typeof obj === 'symbol') return obj.toString()
32
+ if (typeof obj !== 'object') return obj
33
+
34
+ // Handle circular references
35
+ if (seen.has(obj)) return '[Circular]'
36
+ seen.add(obj)
37
+
38
+ if (Array.isArray(obj)) {
39
+ return obj.map(item => sanitize(item, seen))
40
+ }
41
+
42
+ const result = {}
43
+ for (const [key, value] of Object.entries(obj)) {
44
+ result[key] = sanitize(value, seen)
45
+ }
46
+ return result
47
+ }
48
+
23
49
  // ________________________________________________________________________________
24
50
  // LOCAL STATE
25
51
  // ________________________________________________________________________________
@@ -33,7 +59,7 @@ watch(
33
59
  () => props.obj,
34
60
  (curr, prev) => {
35
61
  // console.log(`json watch triggered`)
36
- local.el.data = {... curr }
62
+ local.el.data = sanitize(curr)
37
63
  },
38
64
  { deep: true }
39
65
  )
@@ -66,7 +92,7 @@ function toggleExpand() {
66
92
  async function init() {
67
93
  // document.querySelector('#json').data = { prop1: true, prop2: 'test' };
68
94
  local.el = markRaw(document.querySelector(`#${id}`))
69
- local.el.data = props.obj
95
+ local.el.data = sanitize(props.obj)
70
96
  if (local.isExpanded) {
71
97
  setTimeout(() => {
72
98
  expandAll()
@@ -0,0 +1,364 @@
1
+ // ________________________________________________________________________________
2
+ // Application Configuration
3
+ // ________________________________________________________________________________
4
+ export const DEFAULTS = {
5
+ isDev: true,
6
+ /**
7
+ * XMLHttpRequest from a different domain cannot set cookie values for their own
8
+ * domain unless withCredentials is set to true before making the request.
9
+ * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredential/
10
+ *
11
+ * `true` tells the browser “this cross-origin request should include credentials”,
12
+ * meaning: - send cookies (and HTTP auth) *with* the request, and
13
+ * allow the browser to store cookies from the response, *if* the server also opts in.
14
+ */
15
+ WITH_CREDENTIALS: false,
16
+
17
+ // ________________________________________________________________________________
18
+ // Island Configuration
19
+ // ________________________________________________________________________________
20
+ defaultInstanceId: "default",
21
+ instanceId: "default",
22
+ type: "IslandDefault",
23
+
24
+ // Main entry point
25
+ appMainComponent: "/core/PageMain.vue",
26
+ // Used for requestion app components and files (GlobalStore.appEndpoint)
27
+ appEndpoint: "",
28
+
29
+ showSidebar: false,
30
+ // ________________________________________________________________________________
31
+ // Components Configuration
32
+ // ________________________________________________________________________________
33
+ componentsDefaultExpand: true,
34
+ // Used for default components prefix (used only in config)
35
+ componentPrefix: "",
36
+
37
+ // ________________________________________________________________________________
38
+ // Nav Configuration
39
+ // ________________________________________________________________________________
40
+ appName: "C0ckp1t Application",
41
+ showTopNavBar: true,
42
+ navAutoCollapse: false,
43
+ navItems: [],
44
+ navHasSearch: false,
45
+ navHasThemeSel: false,
46
+
47
+ // ________________________________________________________________________________
48
+ // Footer Configuration
49
+ // ________________________________________________________________________________
50
+ showFooter: true,
51
+
52
+ // ________________________________________________________________________________
53
+ // Documentation Configuration
54
+ // ________________________________________________________________________________
55
+ showDocNav: true,
56
+ showDocReload: true,
57
+ showDocTrail: false,
58
+ allowDocWrite: true,
59
+ allowDocReload: true,
60
+
61
+ // ________________________________________________________________________________
62
+ // Router Configuration
63
+ // ________________________________________________________________________________
64
+ // Determine if VueRouter is createWebHashHistory or createWebHistory
65
+ vueRouterModeIsHash: true,
66
+ // Used for default routes prefix (used only in config)
67
+ routePrefix: "",
68
+
69
+ // ________________________________________________________________________________
70
+ // Logger Config (see Logging.mjs)
71
+ // ________________________________________________________________________________
72
+ defaultLogLevel: "INFO",
73
+ defaultLoggerLevels: {
74
+ "GlobalStore.mjs": "INFO",
75
+ "VueUtils.mjs": "INFO",
76
+ "Connection.mjs": "INFO",
77
+ "default": "INFO",
78
+ "anonymous": "INFO",
79
+ "demo": "INFO"
80
+ },
81
+
82
+ // ________________________________________________________________________________
83
+ // Theming Configuration
84
+ // ________________________________________________________________________________
85
+ bootswatchURL: "https://cdn.jsdelivr.net/npm/bootswatch@5.3.8/dist",
86
+ };
87
+
88
+ // ________________________________________________________________________________
89
+ // Default Nav Tree
90
+ // ________________________________________________________________________________
91
+ export function buildNavTree(instanceId) {
92
+ return {
93
+ icon: "fa-house",
94
+ depth: 0,
95
+ endpoint: "/",
96
+ isLeaf: false,
97
+ isRoot: true,
98
+ name: "",
99
+ path: [],
100
+ children: [
101
+ {
102
+ depth: 1,
103
+ endpoint: `/${instanceId}/connections`,
104
+ isLeaf: true,
105
+ isRoot: false,
106
+ path: ["connections"],
107
+ name: "Connections",
108
+ children: []
109
+ },
110
+ {
111
+ depth: 1,
112
+ endpoint: `/${instanceId}/cache`,
113
+ isLeaf: true,
114
+ isRoot: false,
115
+ path: ["cache"],
116
+ name: "Cache",
117
+ children: []
118
+ },
119
+ {
120
+ icon: "fa-network-wired",
121
+ depth: 1,
122
+ endpoint: `/${instanceId}/traffic`,
123
+ isLeaf: true,
124
+ isRoot: false,
125
+ path: ["traffic"],
126
+ name: "Traffic",
127
+ children: []
128
+ },
129
+ {
130
+ icon: "fa-bell",
131
+ depth: 1,
132
+ endpoint: `/${instanceId}/notifies`,
133
+ isLeaf: true,
134
+ isRoot: false,
135
+ path: ["notifies"],
136
+ name: "Notifies",
137
+ children: []
138
+ },
139
+ {
140
+ icon: "fa-info",
141
+ depth: 1,
142
+ endpoint: `/${instanceId}/docs`,
143
+ isLeaf: true,
144
+ isRoot: false,
145
+ path: ["docs"],
146
+ name: "Documentation",
147
+ children: []
148
+ },
149
+ {
150
+ icon: "fa-info",
151
+ depth: 1,
152
+ endpoint: `/${instanceId}/components`,
153
+ isLeaf: true,
154
+ isRoot: false,
155
+ path: ["components"],
156
+ name: "Components",
157
+ children: [
158
+ {
159
+ icon: "fa-info",
160
+ depth: 2,
161
+ endpoint: `/${instanceId}/components/bootstrap`,
162
+ isLeaf: true,
163
+ isRoot: false,
164
+ path: ["bootstrap"],
165
+ name: "Bootstrap",
166
+ children: []
167
+ },
168
+ {
169
+ icon: "fa-info",
170
+ depth: 2,
171
+ endpoint: `/${instanceId}/components/basic`,
172
+ isLeaf: true,
173
+ isRoot: false,
174
+ path: ["basic"],
175
+ name: "Basic",
176
+ children: []
177
+ },
178
+ {
179
+ icon: "fa-info",
180
+ depth: 2,
181
+ endpoint: `/${instanceId}/components/advanced`,
182
+ isLeaf: true,
183
+ isRoot: false,
184
+ path: ["advanced"],
185
+ name: "Advanced",
186
+ children: []
187
+ },
188
+ {
189
+ icon: "fa-info",
190
+ depth: 2,
191
+ endpoint: `/${instanceId}/components/theme`,
192
+ isLeaf: true,
193
+ isRoot: false,
194
+ path: ["theme"],
195
+ name: "Theme",
196
+ children: []
197
+ },
198
+ ]
199
+ }
200
+ ]
201
+ };
202
+ }
203
+
204
+ // ________________________________________________________________________________
205
+ // Default Routes
206
+ // ________________________________________________________________________________
207
+ export function buildRoutes(instanceId = "default", prefix = "") {
208
+ return [
209
+ { path: '/', name: 'root', children: [
210
+ {path: '', redirect: `/${instanceId}/docs/Introduction.md`},
211
+ {path: `${instanceId}`, children: [
212
+ {path: 'docs', redirect: `/${instanceId}/docs/Introduction.md`},
213
+ {path: 'docs/:pathMatch(.*)*', location: `${prefix}/core/pages/Documentation.vue`},
214
+ {path: 'connections', location: `${prefix}/core/pages/Connections.vue`},
215
+ {path: 'connections/:id', location: `${prefix}/core/pages/Connection.vue`},
216
+ {path: 'cache', location: `${prefix}/core/pages/Cache.vue`},
217
+ {path: 'traffic', location: `${prefix}/core/pages/Traffic.vue`},
218
+ {path: 'notifies', location: `${prefix}/core/pages/Notifies.vue`},
219
+ {path: 'components', location: `${prefix}/core/pages/frontend/Components.vue`, children: [
220
+ {path: 'basic', location: `${prefix}/core/pages/frontend/ComponentsBasic.vue`},
221
+ {path: 'advanced', location: `${prefix}/core/pages/frontend/ComponentsAdv.vue`},
222
+ {path: 'theme', location: `${prefix}/core/pages/frontend/Theme.vue`},
223
+ {path: 'bootstrap', location: `${prefix}/core/pages/frontend/Bootstrap.vue`},
224
+ ]},
225
+ ]}
226
+ ] },
227
+ { path: '/:pathMatch(.*)*', name: '404', location: `${prefix}/core/Page404.vue` }
228
+ ];
229
+ }
230
+
231
+ // ________________________________________________________________________________
232
+ // Components
233
+ // ________________________________________________________________________________
234
+ /**
235
+ * Return the default Vue components
236
+ * using sha1 hashes
237
+ * @returns {Object}
238
+ */
239
+ export function defaultVueComponents(prefix = "") {
240
+ return {
241
+ ExecButton: { path: `${prefix}/components/ExecButton.vue`, hash: `97e3d2ce89808c5a69f41404e1337f743015f0cc` },
242
+ XInput: { path: `${prefix}/components/xinput.vue`, hash: `cd47d7d038316367df6fd7e265aa3625b72a7777` },
243
+ XInput2: { path: `${prefix}/components/xinput2.vue`, hash: `320e52ac991baebded7c2a9f52a4cfc2cde47b55` },
244
+ XLabel: { path: `${prefix}/components/xlabel.vue`, hash: `90a9837aa8e06f1d3a5b7601337afd78a872d181` },
245
+ XDropdown: { path: `${prefix}/components/xdropdown.vue`, hash: `52b29b72fd6512eefccd9caa76e26dd91d9f9f9e` },
246
+ XDropdown2: { path: `${prefix}/components/xdropdown2.vue`, hash: `e7b163eda42fb7e1a6f07b011c423f8f275eb65d` },
247
+ XSection: { path: `${prefix}/components/xsection.vue`, hash: `27285d606f57d13f80156bb11cd34449f88df950` },
248
+ XTableOpen: { path: `${prefix}/components/xtable-open.vue`, hash: `a1b9a4f670817978022a4c596e2e2f89dd4568c2` },
249
+ XCollapse: { path: `${prefix}/components/xcollapse.vue`, hash: `ba1479bd1080a4fa5abdf6c91c7328ae679f78e6` },
250
+ XToggle: { path: `${prefix}/components/xtoggle.vue`, hash: `2f6871d2d3069ac8f35b6bf76de6c2109f42d9d1` },
251
+ XToggle3: { path: `${prefix}/components/xtoggle3.vue`, hash: `61d0464e6ed9983e817eb0cde928dceb7c0fdc75` },
252
+ XCheck: { path: `${prefix}/components/xcheck.vue`, hash: `ee0d6b30600fb41589123f6d66f2791f1332d4f7` },
253
+ XCheckbox: { path: `${prefix}/components/xcheckbox.vue`, hash: `a290c0cbb7bffce83c235dd5a1c98ed9c441a5b0` },
254
+ XTextarea: { path: `${prefix}/components/xtextarea.vue`, hash: `f8bb08419082aa5443630ab07172674b50c7a248` },
255
+ XHidden: { path: `${prefix}/components/xhidden.vue`, hash: `ecb396e12dd894040e715c0854275e4d5016fcb9` },
256
+ XCode: { path: `${prefix}/components/xcode.vue`, hash: `4d9d9165fea0539c9a983fcdffae8dedcfd537ae` },
257
+ XCodeSlot: { path: `${prefix}/components/xcode-slot.vue`, hash: `` },
258
+ XButton: { path: `${prefix}/components/xbutton.vue`, hash: `2e956caa47e46377ea5a809f7438d0fc38be73b9` },
259
+ XTabs: { path: `${prefix}/components/xtabs.vue`, hash: `83dc219106bdc86ae86dcd16cf95ebd7f11bc952` },
260
+ XKv: { path: `${prefix}/components/xkv.vue`, hash: `8951d3a5e3786cfc9c705b13c1f71e3f90dd2552` },
261
+ XNav: { path: `${prefix}/components/xnav.vue`, hash: `8d51c73e5716deed3577652e362c50526ddbe4e1` },
262
+ XMap: { path: `${prefix}/components/xmap.vue`, hash: `daee357d9e2ef96df0166dd7add0339d46a1cc01` },
263
+ XList: { path: `${prefix}/components/xlist.vue`, hash: `217ced04a333238d169c300a721b75f0ddd5e95b` },
264
+ XJson: { path: `${prefix}/components/xjson.vue`, hash: `0a3ef6265b4070b0f002d659776c02756cc1da5a` },
265
+ XCard: { path: `${prefix}/components/xcard.vue`, hash: `de3fbb23ae7b00d4c90a717dd361cb9315e9ded6` },
266
+ XCardH: { path: `${prefix}/components/xcard-h.vue`, hash: `de4d42f1056c5d2b8431f15e6b1180d9f9898ac2` },
267
+ XColor: { path: `${prefix}/components/xcolor.vue`, hash: `9bf9497ff66e213277f17af290c21c0a35752510` },
268
+ "v-ace-editor": { path: `${prefix}/components/vue3-ace-editor.vue`, hash: `70ce4a39152af5cf0f7cb6b1d4fdafc8b6225edc` },
269
+ XMarkdown: { path: `${prefix}/components/xmarkdown.vue`, hash: `15f835547fab8a8c8aad47d72640c8e918a7b9da` },
270
+ XSound: { path: `${prefix}/components/xsound.vue`, hash: `3e8ad4aa3c767f757dd49b99aa6f961547caf970` },
271
+ XUpload: { path: `${prefix}/components/xupload.vue`, hash: `7a872277e0047fca11e950efe08f2bffa670abdb` },
272
+ XTree: { path: `${prefix}/components/xtree.vue`, hash: `3b6534e86996c48ab05072a9b793ecc78d83a0eb` },
273
+ CodeMirror: { path: `${prefix}/components/code-mirror.vue`, hash: `3ce1028adb75831e01c4264d5764c14f60a1bd00` },
274
+ XTerminal: { path: `${prefix}/components/xterminal.vue`, hash: `1c01f92c0a08bd4937f9768a1d49f12a9a84feea` },
275
+ }
276
+ }
277
+
278
+ // ________________________________________________________________________________
279
+ // UTILITY FUNCTIONS
280
+ // ________________________________________________________________________________
281
+ /**
282
+ * Validate and set defaults for the config object.
283
+ * @param config
284
+ * @returns {*}
285
+ */
286
+ export function validateAppConfig(config) {
287
+ if (!config) {
288
+ throw new Error("config is required")
289
+ }
290
+ if (typeof config !== 'object') {
291
+ throw new Error("Config must be an object must was `" + typeof config + "`")
292
+ }
293
+
294
+ // Apply all DEFAULTS — fill in any missing properties
295
+ for (const key of Object.keys(DEFAULTS)) {
296
+ if (config[key] === undefined || config[key] === null) {
297
+ config[key] = structuredClone(DEFAULTS[key]);
298
+ }
299
+ }
300
+
301
+ // Special validations (type coercion / empty-string guards)
302
+ if (typeof config.instanceId !== 'string' || config.instanceId.trim() === '') {
303
+ config.instanceId = DEFAULTS.instanceId;
304
+ }
305
+ if (typeof config.type !== 'string' || config.type.trim() === '') {
306
+ config.type = DEFAULTS.type;
307
+ }
308
+ if (!Array.isArray(config.navItems)) {
309
+ config.navItems = DEFAULTS.navItems;
310
+ }
311
+
312
+ // Derived defaults that depend on other config values
313
+ if (!Array.isArray(config.routes) || config.routes.length < 1) {
314
+ config.routes = buildRoutes(config.instanceId, config.routePrefix);
315
+ }
316
+ if (typeof config.components !== 'object' || config.components === null) {
317
+ config.components = defaultVueComponents(config.componentPrefix);
318
+ }
319
+ if (!config.root || typeof config.root !== 'object') {
320
+ config.root = buildNavTree(config.instanceId);
321
+ }
322
+
323
+ return config
324
+ }
325
+
326
+ /**
327
+ * Use window to extract the hostname, port, protocol, and whether the connection is secure.
328
+ * @returns {{hostname: string, port: string, protocol: string, isSecure: boolean}}
329
+ */
330
+ export function findHostnamePortProtocol() {
331
+ const hostname = window.location.hostname
332
+ const protocol = window.location.protocol.toLowerCase()
333
+ const isSecure = protocol.toLowerCase() === 'https:'
334
+ const port = window.location.port || (isSecure ? "443" : "80")
335
+ const serverUrl = `${protocol}//${hostname}:${port}`
336
+ return {hostname, port, protocol, isSecure, serverUrl}
337
+ }
338
+
339
+ /**
340
+ * Deep merge source into target. Returns a new object.
341
+ * - Objects are recursively merged (source keys override target keys)
342
+ * - Arrays are replaced entirely (source array wins)
343
+ * - Scalars are replaced (source wins)
344
+ */
345
+ export function deepMerge(target, source) {
346
+ const result = { ...target };
347
+ for (const key of Object.keys(source)) {
348
+ const sourceVal = source[key];
349
+ const targetVal = target[key];
350
+ if (
351
+ sourceVal !== null &&
352
+ typeof sourceVal === 'object' &&
353
+ !Array.isArray(sourceVal) &&
354
+ targetVal !== null &&
355
+ typeof targetVal === 'object' &&
356
+ !Array.isArray(targetVal)
357
+ ) {
358
+ result[key] = deepMerge(targetVal, sourceVal);
359
+ } else {
360
+ result[key] = sourceVal;
361
+ }
362
+ }
363
+ return result;
364
+ }