@buenojs/bueno 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/.env.example +109 -0
  2. package/.github/workflows/ci.yml +31 -0
  3. package/LICENSE +21 -0
  4. package/README.md +892 -0
  5. package/architecture.md +652 -0
  6. package/bun.lock +70 -0
  7. package/dist/cli/index.js +3233 -0
  8. package/dist/index.js +9014 -0
  9. package/package.json +77 -0
  10. package/src/cache/index.ts +795 -0
  11. package/src/cli/ARCHITECTURE.md +837 -0
  12. package/src/cli/bin.ts +10 -0
  13. package/src/cli/commands/build.ts +425 -0
  14. package/src/cli/commands/dev.ts +248 -0
  15. package/src/cli/commands/generate.ts +541 -0
  16. package/src/cli/commands/help.ts +55 -0
  17. package/src/cli/commands/index.ts +112 -0
  18. package/src/cli/commands/migration.ts +355 -0
  19. package/src/cli/commands/new.ts +804 -0
  20. package/src/cli/commands/start.ts +208 -0
  21. package/src/cli/core/args.ts +283 -0
  22. package/src/cli/core/console.ts +349 -0
  23. package/src/cli/core/index.ts +60 -0
  24. package/src/cli/core/prompt.ts +424 -0
  25. package/src/cli/core/spinner.ts +265 -0
  26. package/src/cli/index.ts +135 -0
  27. package/src/cli/templates/deploy.ts +295 -0
  28. package/src/cli/templates/docker.ts +307 -0
  29. package/src/cli/templates/index.ts +24 -0
  30. package/src/cli/utils/fs.ts +428 -0
  31. package/src/cli/utils/index.ts +8 -0
  32. package/src/cli/utils/strings.ts +197 -0
  33. package/src/config/env.ts +408 -0
  34. package/src/config/index.ts +506 -0
  35. package/src/config/loader.ts +329 -0
  36. package/src/config/merge.ts +285 -0
  37. package/src/config/types.ts +320 -0
  38. package/src/config/validation.ts +441 -0
  39. package/src/container/forward-ref.ts +143 -0
  40. package/src/container/index.ts +386 -0
  41. package/src/context/index.ts +360 -0
  42. package/src/database/index.ts +1142 -0
  43. package/src/database/migrations/index.ts +371 -0
  44. package/src/database/schema/index.ts +619 -0
  45. package/src/frontend/api-routes.ts +640 -0
  46. package/src/frontend/bundler.ts +643 -0
  47. package/src/frontend/console-client.ts +419 -0
  48. package/src/frontend/console-stream.ts +587 -0
  49. package/src/frontend/dev-server.ts +846 -0
  50. package/src/frontend/file-router.ts +611 -0
  51. package/src/frontend/frameworks/index.ts +106 -0
  52. package/src/frontend/frameworks/react.ts +85 -0
  53. package/src/frontend/frameworks/solid.ts +104 -0
  54. package/src/frontend/frameworks/svelte.ts +110 -0
  55. package/src/frontend/frameworks/vue.ts +92 -0
  56. package/src/frontend/hmr-client.ts +663 -0
  57. package/src/frontend/hmr.ts +728 -0
  58. package/src/frontend/index.ts +342 -0
  59. package/src/frontend/islands.ts +552 -0
  60. package/src/frontend/isr.ts +555 -0
  61. package/src/frontend/layout.ts +475 -0
  62. package/src/frontend/ssr/react.ts +446 -0
  63. package/src/frontend/ssr/solid.ts +523 -0
  64. package/src/frontend/ssr/svelte.ts +546 -0
  65. package/src/frontend/ssr/vue.ts +504 -0
  66. package/src/frontend/ssr.ts +699 -0
  67. package/src/frontend/types.ts +2274 -0
  68. package/src/health/index.ts +604 -0
  69. package/src/index.ts +410 -0
  70. package/src/lock/index.ts +587 -0
  71. package/src/logger/index.ts +444 -0
  72. package/src/logger/transports/index.ts +969 -0
  73. package/src/metrics/index.ts +494 -0
  74. package/src/middleware/built-in.ts +360 -0
  75. package/src/middleware/index.ts +94 -0
  76. package/src/modules/filters.ts +458 -0
  77. package/src/modules/guards.ts +405 -0
  78. package/src/modules/index.ts +1256 -0
  79. package/src/modules/interceptors.ts +574 -0
  80. package/src/modules/lazy.ts +418 -0
  81. package/src/modules/lifecycle.ts +478 -0
  82. package/src/modules/metadata.ts +90 -0
  83. package/src/modules/pipes.ts +626 -0
  84. package/src/router/index.ts +339 -0
  85. package/src/router/linear.ts +371 -0
  86. package/src/router/regex.ts +292 -0
  87. package/src/router/tree.ts +562 -0
  88. package/src/rpc/index.ts +1263 -0
  89. package/src/security/index.ts +436 -0
  90. package/src/ssg/index.ts +631 -0
  91. package/src/storage/index.ts +456 -0
  92. package/src/telemetry/index.ts +1097 -0
  93. package/src/testing/index.ts +1586 -0
  94. package/src/types/index.ts +236 -0
  95. package/src/types/optional-deps.d.ts +219 -0
  96. package/src/validation/index.ts +276 -0
  97. package/src/websocket/index.ts +1004 -0
  98. package/tests/integration/cli.test.ts +1016 -0
  99. package/tests/integration/fullstack.test.ts +234 -0
  100. package/tests/unit/cache.test.ts +174 -0
  101. package/tests/unit/cli-commands.test.ts +892 -0
  102. package/tests/unit/cli.test.ts +1258 -0
  103. package/tests/unit/container.test.ts +279 -0
  104. package/tests/unit/context.test.ts +221 -0
  105. package/tests/unit/database.test.ts +183 -0
  106. package/tests/unit/linear-router.test.ts +280 -0
  107. package/tests/unit/lock.test.ts +336 -0
  108. package/tests/unit/middleware.test.ts +184 -0
  109. package/tests/unit/modules.test.ts +142 -0
  110. package/tests/unit/pubsub.test.ts +257 -0
  111. package/tests/unit/regex-router.test.ts +265 -0
  112. package/tests/unit/router.test.ts +373 -0
  113. package/tests/unit/rpc.test.ts +1248 -0
  114. package/tests/unit/security.test.ts +174 -0
  115. package/tests/unit/telemetry.test.ts +371 -0
  116. package/tests/unit/test-cache.test.ts +110 -0
  117. package/tests/unit/test-database.test.ts +282 -0
  118. package/tests/unit/tree-router.test.ts +325 -0
  119. package/tests/unit/validation.test.ts +794 -0
  120. package/tsconfig.json +27 -0
@@ -0,0 +1,419 @@
1
+ /**
2
+ * Client-side Console Interception Script
3
+ *
4
+ * This script is injected into the browser to capture console.* calls
5
+ * and send them to the server via WebSocket for terminal output.
6
+ *
7
+ * @module frontend/console-client
8
+ */
9
+
10
+ // This is the client-side script that will be injected into HTML pages
11
+ // It's stored as a string constant for injection
12
+
13
+ export const CONSOLE_CLIENT_SCRIPT = `
14
+ (function() {
15
+ 'use strict';
16
+
17
+ // Configuration
18
+ const WS_PORT = typeof CONSOLE_PORT !== 'undefined' ? CONSOLE_PORT : 3002;
19
+ const WS_URL = 'ws://localhost:' + WS_PORT + '/_console';
20
+ const MAX_ARGS_LENGTH = 10; // Maximum number of args to send
21
+ const MAX_STRING_LENGTH = 10000; // Maximum string length before truncation
22
+ const MAX_OBJECT_DEPTH = 5; // Maximum depth for object serialization
23
+ const RECONNECT_DELAY = 1000; // Delay before reconnecting (ms)
24
+ const MAX_RECONNECT_ATTEMPTS = 10; // Maximum reconnection attempts
25
+
26
+ // State
27
+ let ws = null;
28
+ let clientId = null;
29
+ let reconnectAttempts = 0;
30
+ let isConnected = false;
31
+ let messageQueue = [];
32
+ let originalConsole = {};
33
+
34
+ // Console methods to intercept
35
+ const CONSOLE_METHODS = ['log', 'info', 'warn', 'error', 'debug', 'trace', 'table'];
36
+
37
+ /**
38
+ * Serialize a value for transmission
39
+ * Handles circular references and truncates large values
40
+ */
41
+ function serializeValue(value, depth, seen) {
42
+ depth = depth || 0;
43
+ seen = seen || new WeakSet();
44
+
45
+ // Handle depth limit
46
+ if (depth > MAX_OBJECT_DEPTH) {
47
+ return { __type: 'truncated', reason: 'max-depth' };
48
+ }
49
+
50
+ // Handle null and undefined
51
+ if (value === null) return null;
52
+ if (value === undefined) return { __type: 'undefined' };
53
+
54
+ // Handle primitives
55
+ if (typeof value === 'string') {
56
+ if (value.length > MAX_STRING_LENGTH) {
57
+ return value.substring(0, MAX_STRING_LENGTH) + '... [truncated]';
58
+ }
59
+ return value;
60
+ }
61
+ if (typeof value === 'number') {
62
+ if (Number.isNaN(value)) return { __type: 'NaN' };
63
+ if (!Number.isFinite(value)) return { __type: 'Infinity', negative: value < 0 };
64
+ return value;
65
+ }
66
+ if (typeof value === 'boolean') return value;
67
+ if (typeof value === 'symbol') return { __type: 'symbol', value: value.toString() };
68
+ if (typeof value === 'function') return { __type: 'function', name: value.name || 'anonymous' };
69
+ if (typeof value === 'bigint') return { __type: 'bigint', value: value.toString() };
70
+
71
+ // Handle circular references
72
+ if (typeof value === 'object') {
73
+ if (seen.has(value)) {
74
+ return { __type: 'circular' };
75
+ }
76
+ seen.add(value);
77
+ }
78
+
79
+ // Handle Error objects
80
+ if (value instanceof Error) {
81
+ return {
82
+ __type: 'Error',
83
+ name: value.name,
84
+ message: value.message,
85
+ stack: value.stack
86
+ };
87
+ }
88
+
89
+ // Handle Date objects
90
+ if (value instanceof Date) {
91
+ return { __type: 'Date', value: value.toISOString() };
92
+ }
93
+
94
+ // Handle RegExp
95
+ if (value instanceof RegExp) {
96
+ return { __type: 'RegExp', value: value.toString() };
97
+ }
98
+
99
+ // Handle Map
100
+ if (value instanceof Map) {
101
+ const entries = [];
102
+ let count = 0;
103
+ for (const [k, v] of value) {
104
+ if (count++ >= 20) break; // Limit entries
105
+ entries.push([serializeValue(k, depth + 1, seen), serializeValue(v, depth + 1, seen)]);
106
+ }
107
+ return { __type: 'Map', entries: entries, size: value.size };
108
+ }
109
+
110
+ // Handle Set
111
+ if (value instanceof Set) {
112
+ const values = [];
113
+ let count = 0;
114
+ for (const v of value) {
115
+ if (count++ >= 20) break; // Limit values
116
+ values.push(serializeValue(v, depth + 1, seen));
117
+ }
118
+ return { __type: 'Set', values: values, size: value.size };
119
+ }
120
+
121
+ // Handle Array
122
+ if (Array.isArray(value)) {
123
+ const arr = [];
124
+ const len = Math.min(value.length, 100); // Limit array length
125
+ for (let i = 0; i < len; i++) {
126
+ arr.push(serializeValue(value[i], depth + 1, seen));
127
+ }
128
+ if (value.length > len) {
129
+ arr.push({ __type: 'truncated', count: value.length - len });
130
+ }
131
+ return arr;
132
+ }
133
+
134
+ // Handle ArrayBuffer / TypedArray
135
+ if (value instanceof ArrayBuffer) {
136
+ return { __type: 'ArrayBuffer', byteLength: value.byteLength };
137
+ }
138
+ if (ArrayBuffer.isView(value)) {
139
+ return {
140
+ __type: 'TypedArray',
141
+ constructor: value.constructor.name,
142
+ length: value.length,
143
+ byteLength: value.byteLength
144
+ };
145
+ }
146
+
147
+ // Handle DOM elements (simplified)
148
+ if (typeof Element !== 'undefined' && value instanceof Element) {
149
+ return {
150
+ __type: 'Element',
151
+ tagName: value.tagName,
152
+ id: value.id,
153
+ className: value.className
154
+ };
155
+ }
156
+
157
+ // Handle plain objects
158
+ try {
159
+ const obj = {};
160
+ const keys = Object.keys(value).slice(0, 50); // Limit keys
161
+ for (const key of keys) {
162
+ obj[key] = serializeValue(value[key], depth + 1, seen);
163
+ }
164
+ const symbolKeys = Object.getOwnPropertySymbols(value).slice(0, 10);
165
+ for (const sym of symbolKeys) {
166
+ obj['Symbol(' + sym.toString() + ')'] = serializeValue(value[sym], depth + 1, seen);
167
+ }
168
+ return obj;
169
+ } catch (e) {
170
+ return { __type: 'unserializable', error: String(e) };
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Parse stack trace to extract file, line, column
176
+ */
177
+ function parseStackTrace(stack) {
178
+ if (!stack) return null;
179
+
180
+ // Get the first non-console line from the stack
181
+ const lines = stack.split('\\n');
182
+
183
+ for (const line of lines) {
184
+ // Skip internal console-stream lines
185
+ if (line.includes('/_console') || line.includes('console-client')) continue;
186
+
187
+ // Match various stack trace formats
188
+ // Chrome: " at functionName (file:line:col)"
189
+ // Firefox: "functionName@file:line:col"
190
+ // Safari: "functionName@file:line:col"
191
+
192
+ const chromeMatch = line.match(/at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?/);
193
+ if (chromeMatch) {
194
+ return {
195
+ file: chromeMatch[2],
196
+ line: parseInt(chromeMatch[3], 10),
197
+ column: parseInt(chromeMatch[4], 10)
198
+ };
199
+ }
200
+
201
+ const firefoxMatch = line.match(/(?:([^@]+)@)?(.+?):(\\d+):(\\d+)/);
202
+ if (firefoxMatch) {
203
+ return {
204
+ file: firefoxMatch[2],
205
+ line: parseInt(firefoxMatch[3], 10),
206
+ column: parseInt(firefoxMatch[4], 10)
207
+ };
208
+ }
209
+ }
210
+
211
+ return null;
212
+ }
213
+
214
+ /**
215
+ * Get caller location from stack trace
216
+ */
217
+ function getCallerLocation() {
218
+ try {
219
+ throw new Error();
220
+ } catch (e) {
221
+ return parseStackTrace(e.stack);
222
+ }
223
+ return null;
224
+ }
225
+
226
+ /**
227
+ * Send a console message to the server
228
+ */
229
+ function sendConsoleMessage(type, args) {
230
+ const location = getCallerLocation();
231
+
232
+ const message = {
233
+ type: 'console',
234
+ consoleType: type,
235
+ args: Array.from(args).slice(0, MAX_ARGS_LENGTH).map(function(arg) {
236
+ return serializeValue(arg, 0, new WeakSet());
237
+ }),
238
+ timestamp: Date.now(),
239
+ url: window.location.href
240
+ };
241
+
242
+ // Add location info
243
+ if (location) {
244
+ message.file = location.file;
245
+ message.line = location.line;
246
+ message.column = location.column;
247
+ }
248
+
249
+ // Add stack trace for errors
250
+ if (type === 'error' && args[0] instanceof Error) {
251
+ message.stack = args[0].stack;
252
+ }
253
+ if (type === 'trace') {
254
+ try {
255
+ message.stack = new Error().stack;
256
+ } catch (e) {}
257
+ }
258
+
259
+ // Send or queue the message
260
+ if (isConnected && ws && ws.readyState === WebSocket.OPEN) {
261
+ ws.send(JSON.stringify(message));
262
+ } else {
263
+ messageQueue.push(message);
264
+ // Limit queue size
265
+ if (messageQueue.length > 100) {
266
+ messageQueue.shift();
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Intercept console methods
273
+ */
274
+ function interceptConsole() {
275
+ CONSOLE_METHODS.forEach(function(method) {
276
+ originalConsole[method] = console[method];
277
+
278
+ console[method] = function() {
279
+ // Call original method first
280
+ try {
281
+ originalConsole[method].apply(console, arguments);
282
+ } catch (e) {}
283
+
284
+ // Send to server
285
+ try {
286
+ sendConsoleMessage(method, arguments);
287
+ } catch (e) {}
288
+ };
289
+ });
290
+ }
291
+
292
+ /**
293
+ * Restore original console methods
294
+ */
295
+ function restoreConsole() {
296
+ CONSOLE_METHODS.forEach(function(method) {
297
+ if (originalConsole[method]) {
298
+ console[method] = originalConsole[method];
299
+ }
300
+ });
301
+ }
302
+
303
+ /**
304
+ * Process queued messages
305
+ */
306
+ function processQueue() {
307
+ while (messageQueue.length > 0 && ws && ws.readyState === WebSocket.OPEN) {
308
+ const message = messageQueue.shift();
309
+ ws.send(JSON.stringify(message));
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Connect to WebSocket server
315
+ */
316
+ function connect() {
317
+ if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) {
318
+ return;
319
+ }
320
+
321
+ try {
322
+ ws = new WebSocket(WS_URL);
323
+
324
+ ws.onopen = function() {
325
+ isConnected = true;
326
+ reconnectAttempts = 0;
327
+ originalConsole.log && originalConsole.log('[Console Stream] Connected to server');
328
+ processQueue();
329
+ };
330
+
331
+ ws.onclose = function() {
332
+ isConnected = false;
333
+ ws = null;
334
+
335
+ // Attempt to reconnect
336
+ if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
337
+ reconnectAttempts++;
338
+ setTimeout(connect, RECONNECT_DELAY * reconnectAttempts);
339
+ } else {
340
+ originalConsole.warn && originalConsole.warn('[Console Stream] Max reconnection attempts reached');
341
+ }
342
+ };
343
+
344
+ ws.onerror = function(error) {
345
+ originalConsole.error && originalConsole.error('[Console Stream] WebSocket error', error);
346
+ };
347
+
348
+ ws.onmessage = function(event) {
349
+ try {
350
+ const data = JSON.parse(event.data);
351
+ if (data.type === 'connected') {
352
+ clientId = data.clientId;
353
+ }
354
+ } catch (e) {}
355
+ };
356
+ } catch (e) {
357
+ originalConsole.error && originalConsole.error('[Console Stream] Failed to connect', e);
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Initialize console streaming
363
+ */
364
+ function init() {
365
+ // Don't initialize in iframes unless specifically enabled
366
+ if (window !== window.top && !window.__CONSOLE_STREAM_ENABLED__) {
367
+ return;
368
+ }
369
+
370
+ // Don't initialize in service workers
371
+ if (typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope) {
372
+ return;
373
+ }
374
+
375
+ interceptConsole();
376
+ connect();
377
+
378
+ // Handle page unload
379
+ window.addEventListener('beforeunload', function() {
380
+ if (ws) {
381
+ ws.close();
382
+ }
383
+ });
384
+
385
+ // Handle visibility change for reconnection
386
+ document.addEventListener('visibilitychange', function() {
387
+ if (document.visibilityState === 'visible' && (!ws || ws.readyState !== WebSocket.OPEN)) {
388
+ connect();
389
+ }
390
+ });
391
+ }
392
+
393
+ // Start initialization
394
+ if (document.readyState === 'loading') {
395
+ document.addEventListener('DOMContentLoaded', init);
396
+ } else {
397
+ init();
398
+ }
399
+
400
+ // Expose for debugging
401
+ window.__CONSOLE_STREAM__ = {
402
+ connect: connect,
403
+ disconnect: function() {
404
+ if (ws) ws.close();
405
+ },
406
+ restore: restoreConsole,
407
+ getClientId: function() { return clientId; },
408
+ isConnected: function() { return isConnected; }
409
+ };
410
+ })();
411
+ `;
412
+
413
+ // Export a function to get the client script with custom port
414
+ export function getConsoleClientScript(port: number): string {
415
+ return CONSOLE_CLIENT_SCRIPT.replace(
416
+ /typeof CONSOLE_PORT !== 'undefined' \? CONSOLE_PORT : \d+/,
417
+ `typeof CONSOLE_PORT !== 'undefined' ? CONSOLE_PORT : ${port}`
418
+ );
419
+ }