brainzlab 0.1.1 → 0.1.2

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/lib/brainzlab/beacon/client.rb +209 -0
  4. data/lib/brainzlab/beacon/provisioner.rb +44 -0
  5. data/lib/brainzlab/beacon.rb +215 -0
  6. data/lib/brainzlab/configuration.rb +341 -3
  7. data/lib/brainzlab/cortex/cache.rb +59 -0
  8. data/lib/brainzlab/cortex/client.rb +141 -0
  9. data/lib/brainzlab/cortex/provisioner.rb +49 -0
  10. data/lib/brainzlab/cortex.rb +227 -0
  11. data/lib/brainzlab/dendrite/client.rb +232 -0
  12. data/lib/brainzlab/dendrite/provisioner.rb +44 -0
  13. data/lib/brainzlab/dendrite.rb +195 -0
  14. data/lib/brainzlab/devtools/assets/devtools.css +1106 -0
  15. data/lib/brainzlab/devtools/assets/devtools.js +322 -0
  16. data/lib/brainzlab/devtools/assets/logo.svg +6 -0
  17. data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +500 -0
  18. data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
  19. data/lib/brainzlab/devtools/data/collector.rb +248 -0
  20. data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
  21. data/lib/brainzlab/devtools/middleware/database_handler.rb +180 -0
  22. data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
  23. data/lib/brainzlab/devtools/middleware/error_page.rb +376 -0
  24. data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +155 -0
  25. data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +94 -0
  26. data/lib/brainzlab/devtools.rb +75 -0
  27. data/lib/brainzlab/flux/buffer.rb +96 -0
  28. data/lib/brainzlab/flux/client.rb +70 -0
  29. data/lib/brainzlab/flux/provisioner.rb +57 -0
  30. data/lib/brainzlab/flux.rb +174 -0
  31. data/lib/brainzlab/instrumentation/active_record.rb +18 -1
  32. data/lib/brainzlab/instrumentation/aws.rb +179 -0
  33. data/lib/brainzlab/instrumentation/dalli.rb +108 -0
  34. data/lib/brainzlab/instrumentation/excon.rb +152 -0
  35. data/lib/brainzlab/instrumentation/good_job.rb +102 -0
  36. data/lib/brainzlab/instrumentation/resque.rb +115 -0
  37. data/lib/brainzlab/instrumentation/solid_queue.rb +198 -0
  38. data/lib/brainzlab/instrumentation/stripe.rb +164 -0
  39. data/lib/brainzlab/instrumentation/typhoeus.rb +104 -0
  40. data/lib/brainzlab/instrumentation.rb +72 -0
  41. data/lib/brainzlab/nerve/client.rb +217 -0
  42. data/lib/brainzlab/nerve/provisioner.rb +44 -0
  43. data/lib/brainzlab/nerve.rb +219 -0
  44. data/lib/brainzlab/pulse/instrumentation.rb +35 -2
  45. data/lib/brainzlab/pulse/propagation.rb +1 -1
  46. data/lib/brainzlab/pulse/tracer.rb +1 -1
  47. data/lib/brainzlab/pulse.rb +1 -1
  48. data/lib/brainzlab/rails/log_subscriber.rb +1 -2
  49. data/lib/brainzlab/rails/railtie.rb +36 -3
  50. data/lib/brainzlab/recall/provisioner.rb +17 -0
  51. data/lib/brainzlab/recall.rb +6 -1
  52. data/lib/brainzlab/reflex.rb +2 -2
  53. data/lib/brainzlab/sentinel/client.rb +218 -0
  54. data/lib/brainzlab/sentinel/provisioner.rb +44 -0
  55. data/lib/brainzlab/sentinel.rb +165 -0
  56. data/lib/brainzlab/signal/client.rb +62 -0
  57. data/lib/brainzlab/signal/provisioner.rb +55 -0
  58. data/lib/brainzlab/signal.rb +136 -0
  59. data/lib/brainzlab/synapse/client.rb +290 -0
  60. data/lib/brainzlab/synapse/provisioner.rb +44 -0
  61. data/lib/brainzlab/synapse.rb +270 -0
  62. data/lib/brainzlab/utilities/circuit_breaker.rb +265 -0
  63. data/lib/brainzlab/utilities/health_check.rb +296 -0
  64. data/lib/brainzlab/utilities/log_formatter.rb +256 -0
  65. data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
  66. data/lib/brainzlab/utilities.rb +17 -0
  67. data/lib/brainzlab/vault/cache.rb +80 -0
  68. data/lib/brainzlab/vault/client.rb +198 -0
  69. data/lib/brainzlab/vault/provisioner.rb +49 -0
  70. data/lib/brainzlab/vault.rb +268 -0
  71. data/lib/brainzlab/version.rb +1 -1
  72. data/lib/brainzlab/vision/client.rb +128 -0
  73. data/lib/brainzlab/vision/provisioner.rb +136 -0
  74. data/lib/brainzlab/vision.rb +157 -0
  75. data/lib/brainzlab.rb +101 -0
  76. metadata +60 -1
@@ -0,0 +1,322 @@
1
+ /**
2
+ * BrainzLab DevTools - Stimulus Controller
3
+ */
4
+ (function() {
5
+ 'use strict';
6
+
7
+ // Load Stimulus if not available
8
+ let stimulusApp = null;
9
+ let StimulusController = null;
10
+
11
+ async function loadStimulus() {
12
+ if (stimulusApp) return { app: stimulusApp, Controller: StimulusController };
13
+
14
+ // Check if already loading
15
+ if (window._stimulusLoading) return window._stimulusLoading;
16
+
17
+ window._stimulusLoading = new Promise((resolve) => {
18
+ const script = document.createElement('script');
19
+ script.src = 'https://unpkg.com/@hotwired/stimulus@3.2.2/dist/stimulus.umd.js';
20
+ script.onload = () => {
21
+ StimulusController = window.Stimulus.Controller;
22
+ stimulusApp = window.Stimulus.Application.start();
23
+ resolve({ app: stimulusApp, Controller: StimulusController });
24
+ };
25
+ document.head.appendChild(script);
26
+ });
27
+
28
+ return window._stimulusLoading;
29
+ }
30
+
31
+ // Stimulus Controller Definition
32
+ const DevtoolsController = {
33
+ static: {
34
+ targets: ['panel', 'tab', 'pane', 'toast']
35
+ },
36
+
37
+ // Lifecycle
38
+ connect() {
39
+ this.loadState();
40
+ this.bindKeyboardShortcuts();
41
+ },
42
+
43
+ disconnect() {
44
+ this.unbindKeyboardShortcuts();
45
+ },
46
+
47
+ // Actions
48
+ togglePanel() {
49
+ this.element.classList.toggle('collapsed');
50
+ this.saveState();
51
+ },
52
+
53
+ switchTab(event) {
54
+ event.stopPropagation();
55
+ const tabName = event.currentTarget.dataset.tab;
56
+
57
+ // Update tab buttons
58
+ this.element.querySelectorAll('[data-devtools-target="tab"]').forEach(t => {
59
+ t.classList.toggle('active', t.dataset.tab === tabName);
60
+ });
61
+
62
+ // Update content panes
63
+ this.element.querySelectorAll('[data-devtools-target="pane"]').forEach(p => {
64
+ p.classList.toggle('active', p.dataset.pane === tabName);
65
+ });
66
+
67
+ this.saveState();
68
+ },
69
+
70
+ copyToAi(event) {
71
+ event.stopPropagation();
72
+ event.preventDefault();
73
+
74
+ const button = event.currentTarget;
75
+ const issueType = button.dataset.issueType;
76
+ const prompt = this.buildPrompt(issueType, button);
77
+
78
+ this.copyToClipboard(prompt);
79
+ this.showToast('Copied to clipboard');
80
+
81
+ // Visual feedback
82
+ button.classList.add('copied');
83
+ setTimeout(() => button.classList.remove('copied'), 1500);
84
+ },
85
+
86
+ copySql(event) {
87
+ const cell = event.currentTarget;
88
+ const sql = cell.getAttribute('title') || cell.textContent;
89
+ this.copyToClipboard(sql);
90
+ this.showToast('SQL copied');
91
+ },
92
+
93
+ // Prompt Builders
94
+ buildPrompt(issueType, button) {
95
+ const builders = {
96
+ n_plus_one: () => this.buildN1Prompt(button),
97
+ slow_query: () => this.buildSlowQueryPrompt(button),
98
+ too_many_queries: () => this.buildTooManyQueriesPrompt(button),
99
+ slow_view: () => this.buildSlowViewPrompt(button),
100
+ high_memory: () => this.buildHighMemoryPrompt(button)
101
+ };
102
+
103
+ return builders[issueType]?.() || 'Unknown issue type';
104
+ },
105
+
106
+ buildN1Prompt(btn) {
107
+ const { n1Count, n1Duration, n1Source, n1Query, n1Pattern } = btn.dataset;
108
+ return `Fix this N+1 query issue in my Rails application:
109
+
110
+ ## Problem
111
+ Detected ${n1Count || 'unknown'}x similar queries (${n1Duration || 'unknown'}ms total) from:
112
+ \`${n1Source || 'unknown location'}\`
113
+
114
+ ## Sample Query
115
+ \`\`\`sql
116
+ ${n1Query || ''}
117
+ \`\`\`
118
+
119
+ ## Pattern
120
+ \`\`\`
121
+ ${n1Pattern || ''}
122
+ \`\`\`
123
+
124
+ ## Instructions
125
+ 1. Find the source code at the location mentioned above
126
+ 2. Identify why this query is being executed multiple times
127
+ 3. Suggest a fix using eager loading (includes/preload/eager_load) or other optimization
128
+ 4. Show me the before and after code`;
129
+ },
130
+
131
+ buildSlowQueryPrompt(btn) {
132
+ const { queryDuration, querySql, querySource, queryName } = btn.dataset;
133
+ return `Optimize this slow database query in my Rails application:
134
+
135
+ ## Problem
136
+ Query taking ${queryDuration || 'unknown'}ms (threshold: 100ms)
137
+ Source: \`${querySource || 'unknown location'}\`
138
+ Name: ${queryName || 'SQL'}
139
+
140
+ ## Query
141
+ \`\`\`sql
142
+ ${querySql || ''}
143
+ \`\`\`
144
+
145
+ ## Instructions
146
+ 1. Analyze why this query might be slow
147
+ 2. Check if there are missing database indexes
148
+ 3. Suggest query optimizations or restructuring
149
+ 4. If applicable, suggest caching strategies
150
+ 5. Show me the recommended changes`;
151
+ },
152
+
153
+ buildTooManyQueriesPrompt(btn) {
154
+ const { queryCount, controllerName, actionName } = btn.dataset;
155
+ return `Reduce the number of database queries in my Rails application:
156
+
157
+ ## Problem
158
+ ${queryCount || 'unknown'} queries executed in a single request (threshold: 20)
159
+ Controller: ${controllerName || 'unknown'}#${actionName || 'unknown'}
160
+
161
+ ## Instructions
162
+ 1. Look at the controller action and identify what data is being loaded
163
+ 2. Find opportunities to use eager loading (includes/preload/eager_load)
164
+ 3. Identify if any queries can be combined or eliminated
165
+ 4. Check for queries inside loops (potential N+1)
166
+ 5. Suggest caching for frequently accessed data
167
+ 6. Show me the before and after code`;
168
+ },
169
+
170
+ buildSlowViewPrompt(btn) {
171
+ const { viewDuration, viewTemplate, viewType } = btn.dataset;
172
+ return `Optimize this slow view render in my Rails application:
173
+
174
+ ## Problem
175
+ ${viewType || 'template'} taking ${viewDuration || 'unknown'}ms to render (threshold: 50ms)
176
+ Template: ${viewTemplate || 'unknown'}
177
+
178
+ ## Instructions
179
+ 1. Look at the template and identify expensive operations
180
+ 2. Check for complex logic that should be moved to helpers or presenters
181
+ 3. Look for database queries being made in the view
182
+ 4. Identify if fragment caching could help
183
+ 5. Check for unnecessary partial renders or loops
184
+ 6. Suggest optimizations and show me the recommended changes`;
185
+ },
186
+
187
+ buildHighMemoryPrompt(btn) {
188
+ const { memoryDelta, memoryBefore, memoryAfter, controllerName, actionName } = btn.dataset;
189
+ return `Investigate high memory usage in my Rails application:
190
+
191
+ ## Problem
192
+ Request allocated +${memoryDelta || 'unknown'}MB of memory (threshold: 50MB)
193
+ Memory before: ${memoryBefore || 'unknown'}MB
194
+ Memory after: ${memoryAfter || 'unknown'}MB
195
+ Controller: ${controllerName || 'unknown'}#${actionName || 'unknown'}
196
+
197
+ ## Instructions
198
+ 1. Look at the controller action and identify what data is being loaded
199
+ 2. Check for loading large datasets into memory (use find_each/find_in_batches)
200
+ 3. Look for creating many objects in loops
201
+ 4. Check if large files or blobs are being processed
202
+ 5. Identify if streaming responses could help
203
+ 6. Suggest memory optimizations and show me the recommended changes`;
204
+ },
205
+
206
+ // Utilities
207
+ copyToClipboard(text) {
208
+ if (navigator.clipboard?.writeText) {
209
+ navigator.clipboard.writeText(text);
210
+ } else {
211
+ const textarea = document.createElement('textarea');
212
+ textarea.value = text;
213
+ textarea.style.cssText = 'position:fixed;opacity:0';
214
+ document.body.appendChild(textarea);
215
+ textarea.select();
216
+ document.execCommand('copy');
217
+ document.body.removeChild(textarea);
218
+ }
219
+ },
220
+
221
+ showToast(message) {
222
+ // Remove existing toast
223
+ const existing = document.querySelector('.brainz-toast');
224
+ if (existing) existing.remove();
225
+
226
+ const toast = document.createElement('div');
227
+ toast.className = 'brainz-toast';
228
+ toast.textContent = message;
229
+ document.body.appendChild(toast);
230
+
231
+ requestAnimationFrame(() => toast.classList.add('visible'));
232
+
233
+ setTimeout(() => {
234
+ toast.classList.remove('visible');
235
+ setTimeout(() => toast.remove(), 200);
236
+ }, 2000);
237
+ },
238
+
239
+ // State Management
240
+ saveState() {
241
+ try {
242
+ const activeTab = this.element.querySelector('[data-devtools-target="tab"].active');
243
+ sessionStorage.setItem('brainz-devtools-state', JSON.stringify({
244
+ collapsed: this.element.classList.contains('collapsed'),
245
+ activeTab: activeTab?.dataset.tab || 'request'
246
+ }));
247
+ } catch (e) { /* ignore */ }
248
+ },
249
+
250
+ loadState() {
251
+ try {
252
+ const state = JSON.parse(sessionStorage.getItem('brainz-devtools-state'));
253
+ if (state?.collapsed) this.element.classList.add('collapsed');
254
+ if (state?.activeTab) {
255
+ const tab = this.element.querySelector(`[data-tab="${state.activeTab}"]`);
256
+ if (tab) tab.click();
257
+ }
258
+ } catch (e) { /* ignore */ }
259
+ },
260
+
261
+ // Keyboard Shortcuts
262
+ bindKeyboardShortcuts() {
263
+ this._keyHandler = (e) => {
264
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'B') {
265
+ e.preventDefault();
266
+ this.togglePanel();
267
+ }
268
+ if (e.key === 'Escape' && !this.element.classList.contains('collapsed')) {
269
+ this.togglePanel();
270
+ }
271
+ };
272
+ document.addEventListener('keydown', this._keyHandler);
273
+ },
274
+
275
+ unbindKeyboardShortcuts() {
276
+ if (this._keyHandler) {
277
+ document.removeEventListener('keydown', this._keyHandler);
278
+ }
279
+ }
280
+ };
281
+
282
+ // Register Stimulus controller
283
+ async function initialize() {
284
+ const panel = document.querySelector('.brainz-debug-panel');
285
+ if (!panel) return;
286
+
287
+ // Load Stimulus and register controller
288
+ const { app, Controller } = await loadStimulus();
289
+
290
+ // Register the devtools controller
291
+ app.register('devtools', class extends Controller {
292
+ static targets = ['tab', 'pane'];
293
+
294
+ connect() { DevtoolsController.connect.call(this); }
295
+ disconnect() { DevtoolsController.disconnect.call(this); }
296
+ togglePanel() { DevtoolsController.togglePanel.call(this); }
297
+ switchTab(e) { DevtoolsController.switchTab.call(this, e); }
298
+ copyToAi(e) { DevtoolsController.copyToAi.call(this, e); }
299
+ copySql(e) { DevtoolsController.copySql.call(this, e); }
300
+
301
+ buildPrompt(...args) { return DevtoolsController.buildPrompt.call(this, ...args); }
302
+ buildN1Prompt(...args) { return DevtoolsController.buildN1Prompt.call(this, ...args); }
303
+ buildSlowQueryPrompt(...args) { return DevtoolsController.buildSlowQueryPrompt.call(this, ...args); }
304
+ buildTooManyQueriesPrompt(...args) { return DevtoolsController.buildTooManyQueriesPrompt.call(this, ...args); }
305
+ buildSlowViewPrompt(...args) { return DevtoolsController.buildSlowViewPrompt.call(this, ...args); }
306
+ buildHighMemoryPrompt(...args) { return DevtoolsController.buildHighMemoryPrompt.call(this, ...args); }
307
+ copyToClipboard(...args) { return DevtoolsController.copyToClipboard.call(this, ...args); }
308
+ showToast(...args) { return DevtoolsController.showToast.call(this, ...args); }
309
+ saveState() { DevtoolsController.saveState.call(this); }
310
+ loadState() { DevtoolsController.loadState.call(this); }
311
+ bindKeyboardShortcuts() { DevtoolsController.bindKeyboardShortcuts.call(this); }
312
+ unbindKeyboardShortcuts() { DevtoolsController.unbindKeyboardShortcuts.call(this); }
313
+ });
314
+ }
315
+
316
+ // Initialize on DOM ready
317
+ if (document.readyState === 'loading') {
318
+ document.addEventListener('DOMContentLoaded', initialize);
319
+ } else {
320
+ initialize();
321
+ }
322
+ })();
@@ -0,0 +1,6 @@
1
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="32" height="32" rx="8" fill="#FF6B35"/>
3
+ <path d="M8 12C8 10.8954 8.89543 10 10 10H22C23.1046 10 24 10.8954 24 12V13C24 13.5523 23.5523 14 23 14H9C8.44772 14 8 13.5523 8 13V12Z" fill="white"/>
4
+ <path d="M8 17C8 16.4477 8.44772 16 9 16H23C23.5523 16 24 16.4477 24 17V18C24 18.5523 23.5523 19 23 19H9C8.44772 19 8 18.5523 8 18V17Z" fill="white" fill-opacity="0.8"/>
5
+ <path d="M8 22C8 21.4477 8.44772 21 9 21H18C18.5523 21 19 21.4477 19 22C19 22.5523 18.5523 23 18 23H9C8.44772 23 8 22.5523 8 22Z" fill="white" fill-opacity="0.6"/>
6
+ </svg>