@gxp-dev/tools 2.0.6 → 2.0.7

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 (99) hide show
  1. package/browser-extensions/README.md +1 -0
  2. package/browser-extensions/chrome/background.js +857 -0
  3. package/browser-extensions/chrome/content.js +51 -0
  4. package/browser-extensions/chrome/devtools.html +9 -0
  5. package/browser-extensions/chrome/devtools.js +23 -0
  6. package/browser-extensions/chrome/icons/gx_off_128.png +0 -0
  7. package/browser-extensions/chrome/icons/gx_off_16.png +0 -0
  8. package/browser-extensions/chrome/icons/gx_off_32.png +0 -0
  9. package/browser-extensions/chrome/icons/gx_off_64.png +0 -0
  10. package/browser-extensions/chrome/icons/gx_on_128.png +0 -0
  11. package/browser-extensions/chrome/icons/gx_on_16.png +0 -0
  12. package/browser-extensions/chrome/icons/gx_on_32.png +0 -0
  13. package/browser-extensions/chrome/icons/gx_on_64.png +0 -0
  14. package/browser-extensions/chrome/inspector.js +1087 -0
  15. package/browser-extensions/chrome/manifest.json +70 -0
  16. package/browser-extensions/chrome/panel.html +638 -0
  17. package/browser-extensions/chrome/panel.js +862 -0
  18. package/browser-extensions/chrome/popup.html +399 -0
  19. package/browser-extensions/chrome/popup.js +515 -0
  20. package/browser-extensions/chrome/rules.json +1 -0
  21. package/browser-extensions/chrome/test-chrome.html +145 -0
  22. package/browser-extensions/chrome/test-mixed-content.html +190 -0
  23. package/browser-extensions/chrome/test-uri-pattern.html +199 -0
  24. package/browser-extensions/firefox/README.md +134 -0
  25. package/browser-extensions/firefox/background.js +804 -0
  26. package/browser-extensions/firefox/content.js +120 -0
  27. package/browser-extensions/firefox/debug-errors.html +229 -0
  28. package/browser-extensions/firefox/debug-https.html +113 -0
  29. package/browser-extensions/firefox/devtools.html +9 -0
  30. package/browser-extensions/firefox/devtools.js +24 -0
  31. package/browser-extensions/firefox/icons/gx_off_128.png +0 -0
  32. package/browser-extensions/firefox/icons/gx_off_16.png +0 -0
  33. package/browser-extensions/firefox/icons/gx_off_32.png +0 -0
  34. package/browser-extensions/firefox/icons/gx_off_64.png +0 -0
  35. package/browser-extensions/firefox/icons/gx_on_128.png +0 -0
  36. package/browser-extensions/firefox/icons/gx_on_16.png +0 -0
  37. package/browser-extensions/firefox/icons/gx_on_32.png +0 -0
  38. package/browser-extensions/firefox/icons/gx_on_64.png +0 -0
  39. package/browser-extensions/firefox/inspector.js +1087 -0
  40. package/browser-extensions/firefox/manifest.json +67 -0
  41. package/browser-extensions/firefox/panel.html +638 -0
  42. package/browser-extensions/firefox/panel.js +862 -0
  43. package/browser-extensions/firefox/popup.html +525 -0
  44. package/browser-extensions/firefox/popup.js +536 -0
  45. package/browser-extensions/firefox/test-gramercy.html +126 -0
  46. package/browser-extensions/firefox/test-imports.html +58 -0
  47. package/browser-extensions/firefox/test-masking.html +147 -0
  48. package/browser-extensions/firefox/test-uri-pattern.html +199 -0
  49. package/package.json +7 -2
  50. package/runtime/PortalContainer.vue +326 -0
  51. package/runtime/dev-tools/DevToolsModal.vue +217 -0
  52. package/runtime/dev-tools/LayoutSwitcher.vue +221 -0
  53. package/runtime/dev-tools/MockDataEditor.vue +621 -0
  54. package/runtime/dev-tools/SocketSimulator.vue +562 -0
  55. package/runtime/dev-tools/StoreInspector.vue +644 -0
  56. package/runtime/dev-tools/index.js +6 -0
  57. package/runtime/gxpStringsPlugin.js +428 -0
  58. package/runtime/index.html +22 -0
  59. package/runtime/main.js +32 -0
  60. package/runtime/mock-api/auth-middleware.js +97 -0
  61. package/runtime/mock-api/image-generator.js +221 -0
  62. package/runtime/mock-api/index.js +197 -0
  63. package/runtime/mock-api/response-generator.js +394 -0
  64. package/runtime/mock-api/route-generator.js +323 -0
  65. package/runtime/mock-api/socket-triggers.js +371 -0
  66. package/runtime/mock-api/spec-loader.js +300 -0
  67. package/runtime/server.js +180 -0
  68. package/runtime/stores/gxpPortalConfigStore.js +554 -0
  69. package/runtime/stores/index.js +6 -0
  70. package/runtime/vite-inspector-plugin.js +749 -0
  71. package/runtime/vite-source-tracker-plugin.js +232 -0
  72. package/runtime/vite.config.js +402 -0
  73. package/scripts/launch-chrome.js +90 -0
  74. package/scripts/pack-chrome.js +91 -0
  75. package/socket-events/AiSessionMessageCreated.json +18 -0
  76. package/socket-events/SocialStreamPostCreated.json +24 -0
  77. package/socket-events/SocialStreamPostVariantCompleted.json +23 -0
  78. package/template/README.md +332 -0
  79. package/template/app-manifest.json +32 -0
  80. package/template/dev-assets/images/avatar-placeholder.png +0 -0
  81. package/template/dev-assets/images/background-placeholder.jpg +0 -0
  82. package/template/dev-assets/images/banner-placeholder.jpg +0 -0
  83. package/template/dev-assets/images/icon-placeholder.png +0 -0
  84. package/template/dev-assets/images/logo-placeholder.png +0 -0
  85. package/template/dev-assets/images/product-placeholder.jpg +0 -0
  86. package/template/dev-assets/images/thumbnail-placeholder.jpg +0 -0
  87. package/template/env.example +51 -0
  88. package/template/gitignore +53 -0
  89. package/template/index.html +22 -0
  90. package/template/main.js +28 -0
  91. package/template/src/DemoPage.vue +459 -0
  92. package/template/src/Plugin.vue +38 -0
  93. package/template/src/stores/index.js +9 -0
  94. package/template/src/stores/test-data.json +173 -0
  95. package/template/theme-layouts/AdditionalStyling.css +0 -0
  96. package/template/theme-layouts/PrivateLayout.vue +39 -0
  97. package/template/theme-layouts/PublicLayout.vue +39 -0
  98. package/template/theme-layouts/SystemLayout.vue +39 -0
  99. package/template/vite.config.js +333 -0
@@ -0,0 +1,428 @@
1
+ /**
2
+ * GxP Strings Plugin for Vue 3
3
+ *
4
+ * Provides directives for auto-replacement of content from the GxP store:
5
+ *
6
+ * gxp-string - Replace text content with value from stringsList (default)
7
+ * <h1 gxp-string="welcome_title">Welcome</h1>
8
+ *
9
+ * gxp-string + gxp-settings - Replace text content with value from pluginVars
10
+ * <span gxp-string="company_name" gxp-settings>Default Company</span>
11
+ *
12
+ * gxp-string + gxp-assets - Replace text content with value from assetList
13
+ * <span gxp-string="logo_url" gxp-assets>/default/logo.png</span>
14
+ *
15
+ * gxp-string + gxp-state - Replace text content with value from triggerState
16
+ * <span gxp-string="current_status" gxp-state>idle</span>
17
+ *
18
+ * gxp-src - Replace src attribute with value from assetList (default)
19
+ * <img gxp-src="hero_image" src="/dev-assets/placeholder.jpg" />
20
+ *
21
+ * gxp-src + gxp-state - Replace src attribute with value from triggerState
22
+ * <img gxp-src="dynamic_image" gxp-state src="/dev-assets/placeholder.jpg" />
23
+ *
24
+ * The directives will:
25
+ * 1. Store the original value as default
26
+ * 2. Look up the key in the appropriate store
27
+ * 3. Replace content/attribute if a value exists
28
+ * 4. Fall back to the original if no value found
29
+ */
30
+
31
+ import { watch } from "vue";
32
+
33
+ /**
34
+ * Create the GxP Strings Plugin
35
+ * @param {Object} store - The GxP Pinia store instance
36
+ * @returns {Object} Vue plugin
37
+ */
38
+ export function createGxpStringsPlugin(store) {
39
+ return {
40
+ install(app) {
41
+ // Register the v-gxp-string directive
42
+ app.directive("gxp-string", {
43
+ // Called when the element is mounted
44
+ mounted(el, binding) {
45
+ const key = binding.value || el.getAttribute("gxp-string");
46
+ if (!key) return;
47
+
48
+ // Store the original text content as default
49
+ const defaultValue = el.textContent || "";
50
+ el._gxpStringDefault = defaultValue;
51
+ el._gxpStringKey = key;
52
+
53
+ // Initial update
54
+ updateElementText(el, key, defaultValue, store);
55
+
56
+ // Watch for changes in the appropriate store based on attributes
57
+ // Using deep: true to catch when entire object is replaced (async manifest load)
58
+ if (store) {
59
+ const watchSource = getWatchSource(el, key, store);
60
+ if (watchSource) {
61
+ el._gxpUnwatch = watch(watchSource, () => {
62
+ updateElementText(el, key, defaultValue, store);
63
+ }, { deep: true });
64
+ }
65
+ }
66
+ },
67
+
68
+ // Called when binding value changes
69
+ updated(el, binding) {
70
+ const key = binding.value || el.getAttribute("gxp-string");
71
+ if (!key) return;
72
+
73
+ const defaultValue = el._gxpStringDefault || el.textContent || "";
74
+ updateElementText(el, key, defaultValue, store);
75
+ },
76
+
77
+ // Cleanup when element is unmounted
78
+ unmounted(el) {
79
+ if (el._gxpUnwatch) {
80
+ el._gxpUnwatch();
81
+ }
82
+ delete el._gxpStringDefault;
83
+ delete el._gxpStringKey;
84
+ delete el._gxpUnwatch;
85
+ },
86
+ });
87
+
88
+ // Register the v-gxp-src directive for replacing src attributes
89
+ app.directive("gxp-src", {
90
+ // Called when the element is mounted
91
+ mounted(el, binding) {
92
+ const key = binding.value || el.getAttribute("gxp-src");
93
+ if (!key) return;
94
+
95
+ // Store the original src as default
96
+ const defaultSrc = el.getAttribute("src") || "";
97
+ el._gxpSrcDefault = defaultSrc;
98
+ el._gxpSrcKey = key;
99
+
100
+ // Initial update
101
+ updateElementSrc(el, key, defaultSrc, store);
102
+
103
+ // Watch for changes in the appropriate store based on attributes
104
+ // Using deep: true to catch when entire object is replaced (async manifest load)
105
+ if (store) {
106
+ const watchSource = getSrcWatchSource(el, key, store);
107
+ if (watchSource) {
108
+ el._gxpSrcUnwatch = watch(watchSource, () => {
109
+ updateElementSrc(el, key, defaultSrc, store);
110
+ }, { deep: true });
111
+ }
112
+ }
113
+ },
114
+
115
+ // Called when binding value changes
116
+ updated(el, binding) {
117
+ const key = binding.value || el.getAttribute("gxp-src");
118
+ if (!key) return;
119
+
120
+ const defaultSrc = el._gxpSrcDefault || el.getAttribute("src") || "";
121
+ updateElementSrc(el, key, defaultSrc, store);
122
+ },
123
+
124
+ // Cleanup when element is unmounted
125
+ unmounted(el) {
126
+ if (el._gxpSrcUnwatch) {
127
+ el._gxpSrcUnwatch();
128
+ }
129
+ delete el._gxpSrcDefault;
130
+ delete el._gxpSrcKey;
131
+ delete el._gxpSrcUnwatch;
132
+ },
133
+ });
134
+
135
+ // Also handle raw gxp-string and gxp-src attributes (without v- prefix)
136
+ // This runs after mount to catch all elements with the attribute
137
+ // We also set up a watcher for manifestLoaded to re-process when manifest loads
138
+ app.mixin({
139
+ mounted() {
140
+ this.$nextTick(() => {
141
+ processGxpStringAttributes(this.$el, store);
142
+ processGxpSrcAttributes(this.$el, store);
143
+ });
144
+
145
+ // Watch for manifest loading to re-process attributes
146
+ if (store && store.manifestLoaded !== undefined) {
147
+ const rootEl = this.$el;
148
+ this._gxpManifestUnwatch = watch(
149
+ () => store.manifestLoaded,
150
+ (loaded) => {
151
+ if (loaded) {
152
+ this.$nextTick(() => {
153
+ reprocessGxpStringAttributes(rootEl, store);
154
+ reprocessGxpSrcAttributes(rootEl, store);
155
+ });
156
+ }
157
+ }
158
+ );
159
+ }
160
+ },
161
+ updated() {
162
+ this.$nextTick(() => {
163
+ processGxpStringAttributes(this.$el, store);
164
+ processGxpSrcAttributes(this.$el, store);
165
+ });
166
+ },
167
+ unmounted() {
168
+ if (this._gxpManifestUnwatch) {
169
+ this._gxpManifestUnwatch();
170
+ }
171
+ },
172
+ });
173
+ },
174
+ };
175
+ }
176
+
177
+ /**
178
+ * Get the appropriate watch source for gxp-string based on element attributes
179
+ * We watch for both:
180
+ * 1. The specific key value changing (for DevTools updates)
181
+ * 2. The entire object changing (for manifest loading)
182
+ *
183
+ * By returning the specific value along with the object reference,
184
+ * Vue's watch will trigger when either changes.
185
+ */
186
+ function getWatchSource(el, key, store) {
187
+ if (el.hasAttribute("gxp-state") && store.triggerState !== undefined) {
188
+ // Watch specific key AND entire object to catch both types of updates
189
+ return () => ({
190
+ value: store.triggerState?.[key],
191
+ obj: store.triggerState,
192
+ loaded: store.manifestLoaded
193
+ });
194
+ } else if (el.hasAttribute("gxp-settings") && store.pluginVars !== undefined) {
195
+ return () => ({
196
+ value: store.pluginVars?.[key],
197
+ obj: store.pluginVars,
198
+ loaded: store.manifestLoaded
199
+ });
200
+ } else if (el.hasAttribute("gxp-assets") && store.assetList !== undefined) {
201
+ return () => ({
202
+ value: store.assetList?.[key],
203
+ obj: store.assetList,
204
+ loaded: store.manifestLoaded
205
+ });
206
+ } else if (store.stringsList !== undefined) {
207
+ return () => ({
208
+ value: store.stringsList?.[key],
209
+ obj: store.stringsList,
210
+ loaded: store.manifestLoaded
211
+ });
212
+ }
213
+ return null;
214
+ }
215
+
216
+ /**
217
+ * Get the appropriate watch source for gxp-src based on element attributes
218
+ * We watch for both the specific key and the entire object.
219
+ */
220
+ function getSrcWatchSource(el, key, store) {
221
+ if (el.hasAttribute("gxp-state") && store.triggerState !== undefined) {
222
+ return () => ({
223
+ value: store.triggerState?.[key],
224
+ obj: store.triggerState,
225
+ loaded: store.manifestLoaded
226
+ });
227
+ } else if (store.assetList !== undefined) {
228
+ return () => ({
229
+ value: store.assetList?.[key],
230
+ obj: store.assetList,
231
+ loaded: store.manifestLoaded
232
+ });
233
+ }
234
+ return null;
235
+ }
236
+
237
+ /**
238
+ * Update element text content based on store value
239
+ */
240
+ function updateElementText(el, key, defaultValue, store) {
241
+ if (!store) {
242
+ return;
243
+ }
244
+
245
+ let translatedValue;
246
+ if (el.hasAttribute("gxp-state")) {
247
+ translatedValue = store.getState?.(key);
248
+ } else if (el.hasAttribute("gxp-settings")) {
249
+ translatedValue = store.getSetting?.(key);
250
+ } else if (el.hasAttribute("gxp-assets")) {
251
+ translatedValue = store.getAsset?.(key);
252
+ } else {
253
+ translatedValue = store.getString?.(key);
254
+ }
255
+
256
+ if (translatedValue && translatedValue !== defaultValue) {
257
+ el.textContent = translatedValue;
258
+ } else {
259
+ el.textContent = defaultValue;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Update element src attribute based on store value
265
+ * Uses triggerState if gxp-state attribute is present, otherwise assetList
266
+ */
267
+ function updateElementSrc(el, key, defaultSrc, store) {
268
+ if (!store) {
269
+ return;
270
+ }
271
+
272
+ let srcUrl;
273
+ if (el.hasAttribute("gxp-state")) {
274
+ srcUrl = store.getState?.(key);
275
+ } else {
276
+ srcUrl = store.getAsset?.(key);
277
+ }
278
+
279
+ if (srcUrl && srcUrl !== defaultSrc) {
280
+ el.setAttribute("src", srcUrl);
281
+ } else if (defaultSrc) {
282
+ el.setAttribute("src", defaultSrc);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Process all elements with gxp-string attribute in a subtree
288
+ * This handles elements that use the raw attribute without the v- directive
289
+ */
290
+ function processGxpStringAttributes(rootEl, store) {
291
+ if (!rootEl || !store) return;
292
+
293
+ // Handle case where rootEl is a text node or comment
294
+ if (!rootEl.querySelectorAll) return;
295
+
296
+ const elements = rootEl.querySelectorAll("[gxp-string]");
297
+
298
+ elements.forEach((el) => {
299
+ const key = el.getAttribute("gxp-string");
300
+ if (!key) return;
301
+
302
+ // Store default value only on first processing
303
+ if (!el._gxpStringDefault) {
304
+ el._gxpStringDefault = el.textContent || "";
305
+ }
306
+ el._gxpStringKey = key;
307
+
308
+ // Update text
309
+ updateElementText(el, key, el._gxpStringDefault, store);
310
+
311
+ // Set up watcher for this element (if not already watching)
312
+ if (!el._gxpUnwatch) {
313
+ const watchSource = getWatchSource(el, key, store);
314
+ if (watchSource) {
315
+ el._gxpUnwatch = watch(watchSource, () => {
316
+ updateElementText(el, key, el._gxpStringDefault, store);
317
+ }, { deep: true });
318
+ }
319
+ }
320
+ });
321
+ }
322
+
323
+ /**
324
+ * Re-process all elements with gxp-string attribute in a subtree
325
+ * This is called when manifest loads to update elements that were already processed
326
+ */
327
+ function reprocessGxpStringAttributes(rootEl, store) {
328
+ if (!rootEl || !store) return;
329
+ if (!rootEl.querySelectorAll) return;
330
+
331
+ const elements = rootEl.querySelectorAll("[gxp-string]");
332
+
333
+ elements.forEach((el) => {
334
+ const key = el.getAttribute("gxp-string");
335
+ if (!key) return;
336
+
337
+ const defaultValue = el._gxpStringDefault || el.textContent || "";
338
+ updateElementText(el, key, defaultValue, store);
339
+ });
340
+ }
341
+
342
+ /**
343
+ * Process all elements with gxp-src attribute in a subtree
344
+ * This handles elements that use the raw attribute without the v- directive
345
+ */
346
+ function processGxpSrcAttributes(rootEl, store) {
347
+ if (!rootEl || !store) return;
348
+
349
+ // Handle case where rootEl is a text node or comment
350
+ if (!rootEl.querySelectorAll) return;
351
+
352
+ const elements = rootEl.querySelectorAll("[gxp-src]");
353
+
354
+ elements.forEach((el) => {
355
+ const key = el.getAttribute("gxp-src");
356
+ if (!key) return;
357
+
358
+ // Store default value only on first processing
359
+ if (!el._gxpSrcDefault) {
360
+ el._gxpSrcDefault = el.getAttribute("src") || "";
361
+ }
362
+ el._gxpSrcKey = key;
363
+
364
+ // Update src
365
+ updateElementSrc(el, key, el._gxpSrcDefault, store);
366
+
367
+ // Set up watcher for this element (if not already watching)
368
+ if (!el._gxpSrcUnwatch) {
369
+ const watchSource = getSrcWatchSource(el, key, store);
370
+ if (watchSource) {
371
+ el._gxpSrcUnwatch = watch(watchSource, () => {
372
+ updateElementSrc(el, key, el._gxpSrcDefault, store);
373
+ }, { deep: true });
374
+ }
375
+ }
376
+ });
377
+ }
378
+
379
+ /**
380
+ * Re-process all elements with gxp-src attribute in a subtree
381
+ * This is called when manifest loads to update elements that were already processed
382
+ */
383
+ function reprocessGxpSrcAttributes(rootEl, store) {
384
+ if (!rootEl || !store) return;
385
+ if (!rootEl.querySelectorAll) return;
386
+
387
+ const elements = rootEl.querySelectorAll("[gxp-src]");
388
+
389
+ elements.forEach((el) => {
390
+ const key = el.getAttribute("gxp-src");
391
+ if (!key) return;
392
+
393
+ const defaultSrc = el._gxpSrcDefault || el.getAttribute("src") || "";
394
+ updateElementSrc(el, key, defaultSrc, store);
395
+ });
396
+ }
397
+
398
+ /**
399
+ * Standalone function to process gxp-string attributes
400
+ * Can be called manually if needed
401
+ */
402
+ export function processGxpStrings(rootElement, store) {
403
+ processGxpStringAttributes(rootElement, store);
404
+ }
405
+
406
+ /**
407
+ * Standalone function to process gxp-src attributes
408
+ * Can be called manually if needed
409
+ */
410
+ export function processGxpSrcs(rootElement, store) {
411
+ processGxpSrcAttributes(rootElement, store);
412
+ }
413
+
414
+ /**
415
+ * Get the string key from an element if it has gxp-string attribute
416
+ */
417
+ export function getGxpStringKey(element) {
418
+ return element?.getAttribute?.("gxp-string") || null;
419
+ }
420
+
421
+ /**
422
+ * Check if an element has a gxp-string attribute
423
+ */
424
+ export function hasGxpString(element) {
425
+ return element?.hasAttribute?.("gxp-string") || false;
426
+ }
427
+
428
+ export default createGxpStringsPlugin;
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <title>GxP Plugin Builder</title>
9
+ <script type="module">
10
+ import * as Vue from '/node_modules/vue/dist/vue.esm-bundler.js';
11
+ window.Vue = Vue;
12
+ import * as Pinia from '/node_modules/pinia/dist/pinia.esm-browser.js';
13
+ window.Pinia = Pinia;
14
+ </script>
15
+ </head>
16
+
17
+ <body>
18
+ <div id="app"></div>
19
+ <script type="module" src="/@gx-runtime/main.js"></script>
20
+ </body>
21
+
22
+ </html>
@@ -0,0 +1,32 @@
1
+ import { createApp } from "vue";
2
+ import * as Vue from "vue";
3
+ import * as Pinia from "pinia";
4
+ import { createPinia, setActivePinia } from "pinia";
5
+
6
+ // Create and configure Pinia before any store imports
7
+ const pinia = createPinia();
8
+ setActivePinia(pinia);
9
+
10
+ // Expose Vue and Pinia to window for dynamically loaded plugins
11
+ window.Vue = Vue;
12
+ window.Pinia = Pinia;
13
+ window.pinia = pinia;
14
+
15
+ // Dynamic imports ensure pinia is set up before stores load
16
+ async function init() {
17
+ const { default: App } = await import("@gx-runtime/PortalContainer.vue");
18
+ const { useGxpStore } = await import("@/stores/index.js");
19
+ const { createGxpStringsPlugin } = await import("@gx-runtime/gxpStringsPlugin.js");
20
+
21
+ window.useGxpStore = useGxpStore;
22
+
23
+ const app = createApp(App);
24
+ app.use(pinia);
25
+
26
+ const gxpStore = useGxpStore();
27
+ app.use(createGxpStringsPlugin(gxpStore));
28
+
29
+ app.mount("#app");
30
+ }
31
+
32
+ init();
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Auth Middleware
3
+ *
4
+ * Validates Bearer token format (not signature) for mock API requests.
5
+ * This simulates authentication without actually verifying tokens.
6
+ */
7
+
8
+ /**
9
+ * Validate Bearer token format
10
+ * @param {object} req - Express request
11
+ * @param {object} res - Express response
12
+ * @param {function} next - Next middleware
13
+ */
14
+ function authMiddleware(req, res, next) {
15
+ const auth = req.headers.authorization;
16
+
17
+ // Check if Authorization header exists
18
+ if (!auth) {
19
+ return res.status(401).json({
20
+ error: "Unauthorized",
21
+ message: "Missing Authorization header",
22
+ hint: "Include 'Authorization: Bearer <token>' header",
23
+ });
24
+ }
25
+
26
+ // Check Bearer token format
27
+ if (!auth.startsWith("Bearer ")) {
28
+ return res.status(401).json({
29
+ error: "Unauthorized",
30
+ message: "Invalid Authorization format",
31
+ hint: "Use 'Bearer <token>' format",
32
+ });
33
+ }
34
+
35
+ // Extract token
36
+ const token = auth.slice(7).trim();
37
+
38
+ // Check token is not empty
39
+ if (!token) {
40
+ return res.status(401).json({
41
+ error: "Unauthorized",
42
+ message: "Empty token",
43
+ hint: "Provide a non-empty token after 'Bearer '",
44
+ });
45
+ }
46
+
47
+ // Token format is valid, attach to request for potential use
48
+ req.mockToken = token;
49
+
50
+ // Continue to next middleware/handler
51
+ next();
52
+ }
53
+
54
+ /**
55
+ * Optional auth middleware - allows requests without auth but attaches token if present
56
+ * @param {object} req - Express request
57
+ * @param {object} res - Express response
58
+ * @param {function} next - Next middleware
59
+ */
60
+ function optionalAuthMiddleware(req, res, next) {
61
+ const auth = req.headers.authorization;
62
+
63
+ if (auth && auth.startsWith("Bearer ")) {
64
+ req.mockToken = auth.slice(7).trim();
65
+ }
66
+
67
+ next();
68
+ }
69
+
70
+ /**
71
+ * Create auth middleware based on OpenAPI security requirements
72
+ * @param {object} operation - OpenAPI operation object
73
+ * @returns {function} Middleware function
74
+ */
75
+ function createAuthMiddleware(operation) {
76
+ // Check if operation has security requirements
77
+ const security = operation.security;
78
+
79
+ // If security is explicitly empty array, no auth required
80
+ if (Array.isArray(security) && security.length === 0) {
81
+ return optionalAuthMiddleware;
82
+ }
83
+
84
+ // If security is defined, require auth
85
+ if (security && security.length > 0) {
86
+ return authMiddleware;
87
+ }
88
+
89
+ // Default: require auth (safer default for mock API)
90
+ return authMiddleware;
91
+ }
92
+
93
+ module.exports = {
94
+ authMiddleware,
95
+ optionalAuthMiddleware,
96
+ createAuthMiddleware,
97
+ };