c0ckp1t 1.0.16 → 1.0.18

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
  // ________________________________________________________________________________
@@ -21,7 +21,7 @@ export function loadAce() {
21
21
  // Reuse in-flight / resolved promise
22
22
  if (_promise) return _promise
23
23
 
24
- const componentPrefix = storeMain.appConfig?.componentPrefix || ""
24
+ const componentPrefix = storeMain.config?.componentPrefix || ""
25
25
  const src = `${componentPrefix}${ACE_SCRIPT_LOCAL}`
26
26
 
27
27
  _promise = new Promise((resolve, reject) => {
@@ -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,363 @@
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
+ navItems: [],
43
+ navHasSearch: false,
44
+ navHasThemeSel: false,
45
+
46
+ // ________________________________________________________________________________
47
+ // Footer Configuration
48
+ // ________________________________________________________________________________
49
+ showFooter: true,
50
+
51
+ // ________________________________________________________________________________
52
+ // Documentation Configuration
53
+ // ________________________________________________________________________________
54
+ showDocNav: true,
55
+ showDocReload: true,
56
+ showDocTrail: false,
57
+ allowDocWrite: true,
58
+ allowDocReload: true,
59
+
60
+ // ________________________________________________________________________________
61
+ // Router Configuration
62
+ // ________________________________________________________________________________
63
+ // Determine if VueRouter is createWebHashHistory or createWebHistory
64
+ vueRouterModeIsHash: true,
65
+ // Used for default routes prefix (used only in config)
66
+ routePrefix: "",
67
+
68
+ // ________________________________________________________________________________
69
+ // Logger Config (see Logging.mjs)
70
+ // ________________________________________________________________________________
71
+ defaultLogLevel: "INFO",
72
+ defaultLoggerLevels: {
73
+ "GlobalStore.mjs": "INFO",
74
+ "VueUtils.mjs": "INFO",
75
+ "Connection.mjs": "INFO",
76
+ "default": "INFO",
77
+ "anonymous": "INFO",
78
+ "demo": "INFO"
79
+ },
80
+
81
+ // ________________________________________________________________________________
82
+ // Theming Configuration
83
+ // ________________________________________________________________________________
84
+ bootswatchURL: "https://cdn.jsdelivr.net/npm/bootswatch@5.3.8/dist",
85
+ };
86
+
87
+ // ________________________________________________________________________________
88
+ // Default Nav Tree
89
+ // ________________________________________________________________________________
90
+ export function buildNavTree(instanceId) {
91
+ return {
92
+ icon: "fa-house",
93
+ depth: 0,
94
+ endpoint: "/",
95
+ isLeaf: false,
96
+ isRoot: true,
97
+ name: "",
98
+ path: [],
99
+ children: [
100
+ {
101
+ depth: 1,
102
+ endpoint: `/${instanceId}/connections`,
103
+ isLeaf: true,
104
+ isRoot: false,
105
+ path: ["connections"],
106
+ name: "Connections",
107
+ children: []
108
+ },
109
+ {
110
+ depth: 1,
111
+ endpoint: `/${instanceId}/cache`,
112
+ isLeaf: true,
113
+ isRoot: false,
114
+ path: ["cache"],
115
+ name: "Cache",
116
+ children: []
117
+ },
118
+ {
119
+ icon: "fa-network-wired",
120
+ depth: 1,
121
+ endpoint: `/${instanceId}/traffic`,
122
+ isLeaf: true,
123
+ isRoot: false,
124
+ path: ["traffic"],
125
+ name: "Traffic",
126
+ children: []
127
+ },
128
+ {
129
+ icon: "fa-bell",
130
+ depth: 1,
131
+ endpoint: `/${instanceId}/notifies`,
132
+ isLeaf: true,
133
+ isRoot: false,
134
+ path: ["notifies"],
135
+ name: "Notifies",
136
+ children: []
137
+ },
138
+ {
139
+ icon: "fa-info",
140
+ depth: 1,
141
+ endpoint: `/${instanceId}/docs`,
142
+ isLeaf: true,
143
+ isRoot: false,
144
+ path: ["docs"],
145
+ name: "Documentation",
146
+ children: []
147
+ },
148
+ {
149
+ icon: "fa-info",
150
+ depth: 1,
151
+ endpoint: `/${instanceId}/components`,
152
+ isLeaf: true,
153
+ isRoot: false,
154
+ path: ["components"],
155
+ name: "Components",
156
+ children: [
157
+ {
158
+ icon: "fa-info",
159
+ depth: 2,
160
+ endpoint: `/${instanceId}/components/bootstrap`,
161
+ isLeaf: true,
162
+ isRoot: false,
163
+ path: ["bootstrap"],
164
+ name: "Bootstrap",
165
+ children: []
166
+ },
167
+ {
168
+ icon: "fa-info",
169
+ depth: 2,
170
+ endpoint: `/${instanceId}/components/basic`,
171
+ isLeaf: true,
172
+ isRoot: false,
173
+ path: ["basic"],
174
+ name: "Basic",
175
+ children: []
176
+ },
177
+ {
178
+ icon: "fa-info",
179
+ depth: 2,
180
+ endpoint: `/${instanceId}/components/advanced`,
181
+ isLeaf: true,
182
+ isRoot: false,
183
+ path: ["advanced"],
184
+ name: "Advanced",
185
+ children: []
186
+ },
187
+ {
188
+ icon: "fa-info",
189
+ depth: 2,
190
+ endpoint: `/${instanceId}/components/theme`,
191
+ isLeaf: true,
192
+ isRoot: false,
193
+ path: ["theme"],
194
+ name: "Theme",
195
+ children: []
196
+ },
197
+ ]
198
+ }
199
+ ]
200
+ };
201
+ }
202
+
203
+ // ________________________________________________________________________________
204
+ // Default Routes
205
+ // ________________________________________________________________________________
206
+ export function buildRoutes(instanceId = "default", prefix = "") {
207
+ return [
208
+ { path: '/', name: 'root', children: [
209
+ {path: '', redirect: `/${instanceId}/docs/Introduction.md`},
210
+ {path: `${instanceId}`, children: [
211
+ {path: 'docs', redirect: `/${instanceId}/docs/Introduction.md`},
212
+ {path: 'docs/:pathMatch(.*)*', location: `${prefix}/core/pages/Documentation.vue`},
213
+ {path: 'connections', location: `${prefix}/core/pages/Connections.vue`},
214
+ {path: 'connections/:id', location: `${prefix}/core/pages/Connection.vue`},
215
+ {path: 'cache', location: `${prefix}/core/pages/Cache.vue`},
216
+ {path: 'traffic', location: `${prefix}/core/pages/Traffic.vue`},
217
+ {path: 'notifies', location: `${prefix}/core/pages/Notifies.vue`},
218
+ {path: 'components', location: `${prefix}/core/pages/frontend/Components.vue`, children: [
219
+ {path: 'basic', location: `${prefix}/core/pages/frontend/ComponentsBasic.vue`},
220
+ {path: 'advanced', location: `${prefix}/core/pages/frontend/ComponentsAdv.vue`},
221
+ {path: 'theme', location: `${prefix}/core/pages/frontend/Theme.vue`},
222
+ {path: 'bootstrap', location: `${prefix}/core/pages/frontend/Bootstrap.vue`},
223
+ ]},
224
+ ]}
225
+ ] },
226
+ { path: '/:pathMatch(.*)*', name: '404', location: `${prefix}/core/Page404.vue` }
227
+ ];
228
+ }
229
+
230
+ // ________________________________________________________________________________
231
+ // Components
232
+ // ________________________________________________________________________________
233
+ /**
234
+ * Return the default Vue components
235
+ * using sha1 hashes
236
+ * @returns {Object}
237
+ */
238
+ export function defaultVueComponents(prefix = "") {
239
+ return {
240
+ ExecButton: { path: `${prefix}/components/ExecButton.vue`, hash: `97e3d2ce89808c5a69f41404e1337f743015f0cc` },
241
+ XInput: { path: `${prefix}/components/xinput.vue`, hash: `cd47d7d038316367df6fd7e265aa3625b72a7777` },
242
+ XInput2: { path: `${prefix}/components/xinput2.vue`, hash: `320e52ac991baebded7c2a9f52a4cfc2cde47b55` },
243
+ XLabel: { path: `${prefix}/components/xlabel.vue`, hash: `90a9837aa8e06f1d3a5b7601337afd78a872d181` },
244
+ XDropdown: { path: `${prefix}/components/xdropdown.vue`, hash: `52b29b72fd6512eefccd9caa76e26dd91d9f9f9e` },
245
+ XDropdown2: { path: `${prefix}/components/xdropdown2.vue`, hash: `e7b163eda42fb7e1a6f07b011c423f8f275eb65d` },
246
+ XSection: { path: `${prefix}/components/xsection.vue`, hash: `27285d606f57d13f80156bb11cd34449f88df950` },
247
+ XTableOpen: { path: `${prefix}/components/xtable-open.vue`, hash: `a1b9a4f670817978022a4c596e2e2f89dd4568c2` },
248
+ XCollapse: { path: `${prefix}/components/xcollapse.vue`, hash: `ba1479bd1080a4fa5abdf6c91c7328ae679f78e6` },
249
+ XToggle: { path: `${prefix}/components/xtoggle.vue`, hash: `2f6871d2d3069ac8f35b6bf76de6c2109f42d9d1` },
250
+ XToggle3: { path: `${prefix}/components/xtoggle3.vue`, hash: `61d0464e6ed9983e817eb0cde928dceb7c0fdc75` },
251
+ XCheck: { path: `${prefix}/components/xcheck.vue`, hash: `ee0d6b30600fb41589123f6d66f2791f1332d4f7` },
252
+ XCheckbox: { path: `${prefix}/components/xcheckbox.vue`, hash: `a290c0cbb7bffce83c235dd5a1c98ed9c441a5b0` },
253
+ XTextarea: { path: `${prefix}/components/xtextarea.vue`, hash: `f8bb08419082aa5443630ab07172674b50c7a248` },
254
+ XHidden: { path: `${prefix}/components/xhidden.vue`, hash: `ecb396e12dd894040e715c0854275e4d5016fcb9` },
255
+ XCode: { path: `${prefix}/components/xcode.vue`, hash: `4d9d9165fea0539c9a983fcdffae8dedcfd537ae` },
256
+ XCodeSlot: { path: `${prefix}/components/xcode-slot.vue`, hash: `` },
257
+ XButton: { path: `${prefix}/components/xbutton.vue`, hash: `2e956caa47e46377ea5a809f7438d0fc38be73b9` },
258
+ XTabs: { path: `${prefix}/components/xtabs.vue`, hash: `83dc219106bdc86ae86dcd16cf95ebd7f11bc952` },
259
+ XKv: { path: `${prefix}/components/xkv.vue`, hash: `8951d3a5e3786cfc9c705b13c1f71e3f90dd2552` },
260
+ XNav: { path: `${prefix}/components/xnav.vue`, hash: `8d51c73e5716deed3577652e362c50526ddbe4e1` },
261
+ XMap: { path: `${prefix}/components/xmap.vue`, hash: `daee357d9e2ef96df0166dd7add0339d46a1cc01` },
262
+ XList: { path: `${prefix}/components/xlist.vue`, hash: `217ced04a333238d169c300a721b75f0ddd5e95b` },
263
+ XJson: { path: `${prefix}/components/xjson.vue`, hash: `0a3ef6265b4070b0f002d659776c02756cc1da5a` },
264
+ XCard: { path: `${prefix}/components/xcard.vue`, hash: `de3fbb23ae7b00d4c90a717dd361cb9315e9ded6` },
265
+ XCardH: { path: `${prefix}/components/xcard-h.vue`, hash: `de4d42f1056c5d2b8431f15e6b1180d9f9898ac2` },
266
+ XColor: { path: `${prefix}/components/xcolor.vue`, hash: `9bf9497ff66e213277f17af290c21c0a35752510` },
267
+ "v-ace-editor": { path: `${prefix}/components/vue3-ace-editor.vue`, hash: `70ce4a39152af5cf0f7cb6b1d4fdafc8b6225edc` },
268
+ XMarkdown: { path: `${prefix}/components/xmarkdown.vue`, hash: `15f835547fab8a8c8aad47d72640c8e918a7b9da` },
269
+ XSound: { path: `${prefix}/components/xsound.vue`, hash: `3e8ad4aa3c767f757dd49b99aa6f961547caf970` },
270
+ XUpload: { path: `${prefix}/components/xupload.vue`, hash: `7a872277e0047fca11e950efe08f2bffa670abdb` },
271
+ XTree: { path: `${prefix}/components/xtree.vue`, hash: `3b6534e86996c48ab05072a9b793ecc78d83a0eb` },
272
+ CodeMirror: { path: `${prefix}/components/code-mirror.vue`, hash: `3ce1028adb75831e01c4264d5764c14f60a1bd00` },
273
+ XTerminal: { path: `${prefix}/components/xterminal.vue`, hash: `1c01f92c0a08bd4937f9768a1d49f12a9a84feea` },
274
+ }
275
+ }
276
+
277
+ // ________________________________________________________________________________
278
+ // UTILITY FUNCTIONS
279
+ // ________________________________________________________________________________
280
+ /**
281
+ * Validate and set defaults for the config object.
282
+ * @param config
283
+ * @returns {*}
284
+ */
285
+ export function validateAppConfig(config) {
286
+ if (!config) {
287
+ throw new Error("config is required")
288
+ }
289
+ if (typeof config !== 'object') {
290
+ throw new Error("Config must be an object must was `" + typeof config + "`")
291
+ }
292
+
293
+ // Apply all DEFAULTS — fill in any missing properties
294
+ for (const key of Object.keys(DEFAULTS)) {
295
+ if (config[key] === undefined || config[key] === null) {
296
+ config[key] = structuredClone(DEFAULTS[key]);
297
+ }
298
+ }
299
+
300
+ // Special validations (type coercion / empty-string guards)
301
+ if (typeof config.instanceId !== 'string' || config.instanceId.trim() === '') {
302
+ config.instanceId = DEFAULTS.instanceId;
303
+ }
304
+ if (typeof config.type !== 'string' || config.type.trim() === '') {
305
+ config.type = DEFAULTS.type;
306
+ }
307
+ if (!Array.isArray(config.navItems)) {
308
+ config.navItems = DEFAULTS.navItems;
309
+ }
310
+
311
+ // Derived defaults that depend on other config values
312
+ if (!Array.isArray(config.routes) || config.routes.length < 1) {
313
+ config.routes = buildRoutes(config.instanceId, config.routePrefix);
314
+ }
315
+ if (typeof config.components !== 'object' || config.components === null) {
316
+ config.components = defaultVueComponents(config.componentPrefix);
317
+ }
318
+ if (!config.root || typeof config.root !== 'object') {
319
+ config.root = buildNavTree(config.instanceId);
320
+ }
321
+
322
+ return config
323
+ }
324
+
325
+ /**
326
+ * Use window to extract the hostname, port, protocol, and whether the connection is secure.
327
+ * @returns {{hostname: string, port: string, protocol: string, isSecure: boolean}}
328
+ */
329
+ export function findHostnamePortProtocol() {
330
+ const hostname = window.location.hostname
331
+ const protocol = window.location.protocol.toLowerCase()
332
+ const isSecure = protocol.toLowerCase() === 'https:'
333
+ const port = window.location.port || (isSecure ? "443" : "80")
334
+ const serverUrl = `${protocol}//${hostname}:${port}`
335
+ return {hostname, port, protocol, isSecure, serverUrl}
336
+ }
337
+
338
+ /**
339
+ * Deep merge source into target. Returns a new object.
340
+ * - Objects are recursively merged (source keys override target keys)
341
+ * - Arrays are replaced entirely (source array wins)
342
+ * - Scalars are replaced (source wins)
343
+ */
344
+ export function deepMerge(target, source) {
345
+ const result = { ...target };
346
+ for (const key of Object.keys(source)) {
347
+ const sourceVal = source[key];
348
+ const targetVal = target[key];
349
+ if (
350
+ sourceVal !== null &&
351
+ typeof sourceVal === 'object' &&
352
+ !Array.isArray(sourceVal) &&
353
+ targetVal !== null &&
354
+ typeof targetVal === 'object' &&
355
+ !Array.isArray(targetVal)
356
+ ) {
357
+ result[key] = deepMerge(targetVal, sourceVal);
358
+ } else {
359
+ result[key] = sourceVal;
360
+ }
361
+ }
362
+ return result;
363
+ }