@gravito/zenith 1.0.0-beta.1 → 1.0.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 (35) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/bin.js +436 -43
  3. package/dist/client/assets/index-C332gZ-J.css +1 -0
  4. package/dist/client/assets/{index-oXEse8ih.js → index-D4HibwTK.js} +88 -88
  5. package/dist/client/index.html +2 -2
  6. package/dist/server/index.js +436 -43
  7. package/docs/LARAVEL_ZENITH_ROADMAP.md +109 -0
  8. package/{QUASAR_MASTER_PLAN.md → docs/QUASAR_MASTER_PLAN.md} +6 -3
  9. package/package.json +1 -1
  10. package/scripts/debug_redis_keys.ts +24 -0
  11. package/src/client/App.tsx +1 -1
  12. package/src/client/Layout.tsx +11 -12
  13. package/src/client/WorkerStatus.tsx +97 -56
  14. package/src/client/components/BrandIcons.tsx +119 -44
  15. package/src/client/components/ConfirmDialog.tsx +0 -1
  16. package/src/client/components/JobInspector.tsx +18 -6
  17. package/src/client/components/PageHeader.tsx +32 -28
  18. package/src/client/pages/OverviewPage.tsx +0 -1
  19. package/src/client/pages/PulsePage.tsx +422 -340
  20. package/src/client/pages/SettingsPage.tsx +69 -15
  21. package/src/client/pages/WorkersPage.tsx +70 -2
  22. package/src/server/index.ts +171 -11
  23. package/src/server/services/QueueService.ts +6 -3
  24. package/src/shared/types.ts +2 -0
  25. package/ARCHITECTURE.md +0 -88
  26. package/BATCH_OPERATIONS_IMPLEMENTATION.md +0 -159
  27. package/EVOLUTION_BLUEPRINT.md +0 -112
  28. package/JOBINSPECTOR_SCROLL_FIX.md +0 -152
  29. package/TESTING_BATCH_OPERATIONS.md +0 -252
  30. package/dist/client/assets/index-BSTyMCFd.css +0 -1
  31. /package/{ALERTING_GUIDE.md → docs/ALERTING_GUIDE.md} +0 -0
  32. /package/{DEPLOYMENT.md → docs/DEPLOYMENT.md} +0 -0
  33. /package/{DOCS_INTERNAL.md → docs/DOCS_INTERNAL.md} +0 -0
  34. /package/{QUICK_TEST_GUIDE.md → docs/QUICK_TEST_GUIDE.md} +0 -0
  35. /package/{ROADMAP.md → docs/ROADMAP.md} +0 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @gravito/zenith
2
+
3
+ ## 1.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @gravito/atlas@1.0.1
9
+ - @gravito/stream@1.0.0
package/dist/bin.js CHANGED
@@ -68749,19 +68749,19 @@ class Grammar {
68749
68749
  return `INSERT INTO ${this.wrapTable(query.table)} (${columnList}) VALUES ${valuesList}`;
68750
68750
  }
68751
68751
  compileUpdate(query, values) {
68752
- const columns = Object.keys(values);
68753
68752
  let bindingIndex = 0;
68754
- const setClause = columns.map((col) => {
68753
+ const setClause = Object.entries(values).map(([col, value]) => {
68754
+ if (value instanceof Expression) {
68755
+ return `${this.wrapColumn(col)} = ${value.getValue()}`;
68756
+ }
68755
68757
  const placeholder = this.getPlaceholder(bindingIndex);
68756
68758
  bindingIndex++;
68757
68759
  return `${this.wrapColumn(col)} = ${placeholder}`;
68758
68760
  }).join(", ");
68759
68761
  let sql = `UPDATE ${this.wrapTable(query.table)} SET ${setClause}`;
68760
68762
  if (query.wheres.length > 0) {
68761
- const whereBindingsOffset = columns.length;
68762
- const offsetQuery = { ...query, bindings: query.bindings.slice(whereBindingsOffset) };
68763
- const wheres = this.compileWheres(offsetQuery);
68764
- sql += ` ${this.offsetPlaceholders(wheres, whereBindingsOffset)}`;
68763
+ const wheres = this.compileWheres(query, bindingIndex);
68764
+ sql += ` ${wheres}`;
68765
68765
  }
68766
68766
  return sql;
68767
68767
  }
@@ -70367,7 +70367,14 @@ class QueryBuilder {
70367
70367
  return id;
70368
70368
  }
70369
70369
  async update(data) {
70370
- const values = Object.values(data);
70370
+ const values = [];
70371
+ for (const value of Object.values(data)) {
70372
+ if (value instanceof Expression) {
70373
+ values.push(...value.getBindings());
70374
+ } else {
70375
+ values.push(value);
70376
+ }
70377
+ }
70371
70378
  const allBindings = [...values, ...this.bindingsList];
70372
70379
  const compiled = this.getCompiledQuery();
70373
70380
  compiled.bindings = allBindings;
@@ -71473,7 +71480,7 @@ var require_dist_cjs = __commonJS((exports) => {
71473
71480
 
71474
71481
  // ../../node_modules/.bun/@smithy+protocol-http@5.3.7/node_modules/@smithy/protocol-http/dist-cjs/index.js
71475
71482
  var require_dist_cjs2 = __commonJS((exports) => {
71476
- var types3 = require_dist_cjs();
71483
+ var types4 = require_dist_cjs();
71477
71484
  var getHttpHandlerExtensionConfiguration = (runtimeConfig) => {
71478
71485
  return {
71479
71486
  setHttpHandler(handler) {
@@ -71500,7 +71507,7 @@ var require_dist_cjs2 = __commonJS((exports) => {
71500
71507
  name;
71501
71508
  kind;
71502
71509
  values;
71503
- constructor({ name, kind = types3.FieldPosition.HEADER, values = [] }) {
71510
+ constructor({ name, kind = types4.FieldPosition.HEADER, values = [] }) {
71504
71511
  this.name = name;
71505
71512
  this.kind = kind;
71506
71513
  this.values = values;
@@ -73987,8 +73994,8 @@ var require_dist_cjs16 = __commonJS((exports) => {
73987
73994
 
73988
73995
  // ../../node_modules/.bun/@smithy+util-middleware@4.2.7/node_modules/@smithy/util-middleware/dist-cjs/index.js
73989
73996
  var require_dist_cjs17 = __commonJS((exports) => {
73990
- var types3 = require_dist_cjs();
73991
- var getSmithyContext = (context) => context[types3.SMITHY_CONTEXT_KEY] || (context[types3.SMITHY_CONTEXT_KEY] = {});
73997
+ var types4 = require_dist_cjs();
73998
+ var getSmithyContext = (context) => context[types4.SMITHY_CONTEXT_KEY] || (context[types4.SMITHY_CONTEXT_KEY] = {});
73992
73999
  var normalizeProvider = (input) => {
73993
74000
  if (typeof input === "function")
73994
74001
  return input;
@@ -76933,7 +76940,7 @@ var require_protocols = __commonJS((exports) => {
76933
76940
  var require_dist_cjs19 = __commonJS((exports) => {
76934
76941
  var middlewareStack = require_dist_cjs6();
76935
76942
  var protocols = require_protocols();
76936
- var types3 = require_dist_cjs();
76943
+ var types4 = require_dist_cjs();
76937
76944
  var schema2 = require_schema();
76938
76945
  var serde = require_serde();
76939
76946
 
@@ -77032,7 +77039,7 @@ var require_dist_cjs19 = __commonJS((exports) => {
77032
77039
  commandName,
77033
77040
  inputFilterSensitiveLog,
77034
77041
  outputFilterSensitiveLog,
77035
- [types3.SMITHY_CONTEXT_KEY]: {
77042
+ [types4.SMITHY_CONTEXT_KEY]: {
77036
77043
  commandInstance: this,
77037
77044
  ...smithyContext
77038
77045
  },
@@ -77257,8 +77264,8 @@ var require_dist_cjs19 = __commonJS((exports) => {
77257
77264
  };
77258
77265
  var getChecksumConfiguration = (runtimeConfig) => {
77259
77266
  const checksumAlgorithms = [];
77260
- for (const id in types3.AlgorithmId) {
77261
- const algorithmId = types3.AlgorithmId[id];
77267
+ for (const id in types4.AlgorithmId) {
77268
+ const algorithmId = types4.AlgorithmId[id];
77262
77269
  if (runtimeConfig[algorithmId] === undefined) {
77263
77270
  continue;
77264
77271
  }
@@ -77766,12 +77773,12 @@ var require_dist_cjs20 = __commonJS((exports) => {
77766
77773
 
77767
77774
  // ../../node_modules/.bun/@smithy+core@3.20.0/node_modules/@smithy/core/dist-cjs/index.js
77768
77775
  var require_dist_cjs21 = __commonJS((exports) => {
77769
- var types3 = require_dist_cjs();
77776
+ var types4 = require_dist_cjs();
77770
77777
  var utilMiddleware = require_dist_cjs17();
77771
77778
  var middlewareSerde = require_dist_cjs20();
77772
77779
  var protocolHttp = require_dist_cjs2();
77773
77780
  var protocols = require_protocols();
77774
- var getSmithyContext = (context) => context[types3.SMITHY_CONTEXT_KEY] || (context[types3.SMITHY_CONTEXT_KEY] = {});
77781
+ var getSmithyContext = (context) => context[types4.SMITHY_CONTEXT_KEY] || (context[types4.SMITHY_CONTEXT_KEY] = {});
77775
77782
  var resolveAuthOptions = (candidateAuthOptions, authSchemePreference) => {
77776
77783
  if (!authSchemePreference || authSchemePreference.length === 0) {
77777
77784
  return candidateAuthOptions;
@@ -77986,9 +77993,9 @@ var require_dist_cjs21 = __commonJS((exports) => {
77986
77993
  throw new Error("request could not be signed with `apiKey` since the `apiKey` is not defined");
77987
77994
  }
77988
77995
  const clonedRequest = protocolHttp.HttpRequest.clone(httpRequest);
77989
- if (signingProperties.in === types3.HttpApiKeyAuthLocation.QUERY) {
77996
+ if (signingProperties.in === types4.HttpApiKeyAuthLocation.QUERY) {
77990
77997
  clonedRequest.query[signingProperties.name] = identity.apiKey;
77991
- } else if (signingProperties.in === types3.HttpApiKeyAuthLocation.HEADER) {
77998
+ } else if (signingProperties.in === types4.HttpApiKeyAuthLocation.HEADER) {
77992
77999
  clonedRequest.headers[signingProperties.name] = signingProperties.scheme ? `${signingProperties.scheme} ${identity.apiKey}` : identity.apiKey;
77993
78000
  } else {
77994
78001
  throw new Error("request can only be signed with `apiKey` locations `query` or `header`, " + "but found: `" + signingProperties.in + "`");
@@ -78098,7 +78105,7 @@ var require_dist_cjs21 = __commonJS((exports) => {
78098
78105
 
78099
78106
  // ../../node_modules/.bun/@smithy+util-endpoints@3.2.7/node_modules/@smithy/util-endpoints/dist-cjs/index.js
78100
78107
  var require_dist_cjs22 = __commonJS((exports) => {
78101
- var types3 = require_dist_cjs();
78108
+ var types4 = require_dist_cjs();
78102
78109
 
78103
78110
  class EndpointCache {
78104
78111
  capacity;
@@ -78221,8 +78228,8 @@ var require_dist_cjs22 = __commonJS((exports) => {
78221
78228
  var isSet = (value) => value != null;
78222
78229
  var not = (value) => !value;
78223
78230
  var DEFAULT_PORTS = {
78224
- [types3.EndpointURLScheme.HTTP]: 80,
78225
- [types3.EndpointURLScheme.HTTPS]: 443
78231
+ [types4.EndpointURLScheme.HTTP]: 80,
78232
+ [types4.EndpointURLScheme.HTTPS]: 443
78226
78233
  };
78227
78234
  var parseURL = (value) => {
78228
78235
  const whatwgURL = (() => {
@@ -78251,7 +78258,7 @@ var require_dist_cjs22 = __commonJS((exports) => {
78251
78258
  return null;
78252
78259
  }
78253
78260
  const scheme = protocol.slice(0, -1);
78254
- if (!Object.values(types3.EndpointURLScheme).includes(scheme)) {
78261
+ if (!Object.values(types4.EndpointURLScheme).includes(scheme)) {
78255
78262
  return null;
78256
78263
  }
78257
78264
  const isIp = isIpAddress(hostname);
@@ -84381,7 +84388,7 @@ var require_dist_cjs35 = __commonJS((exports) => {
84381
84388
  var getSSOTokenFilepath = require_getSSOTokenFilepath();
84382
84389
  var getSSOTokenFromFile = require_getSSOTokenFromFile();
84383
84390
  var path = __require("path");
84384
- var types3 = require_dist_cjs();
84391
+ var types4 = require_dist_cjs();
84385
84392
  var readFile = require_readFile();
84386
84393
  var ENV_PROFILE = "AWS_PROFILE";
84387
84394
  var DEFAULT_PROFILE = "default";
@@ -84392,10 +84399,10 @@ var require_dist_cjs35 = __commonJS((exports) => {
84392
84399
  if (indexOfSeparator === -1) {
84393
84400
  return false;
84394
84401
  }
84395
- return Object.values(types3.IniSectionType).includes(key.substring(0, indexOfSeparator));
84402
+ return Object.values(types4.IniSectionType).includes(key.substring(0, indexOfSeparator));
84396
84403
  }).reduce((acc, [key, value]) => {
84397
84404
  const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
84398
- const updatedKey = key.substring(0, indexOfSeparator) === types3.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
84405
+ const updatedKey = key.substring(0, indexOfSeparator) === types4.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
84399
84406
  acc[updatedKey] = value;
84400
84407
  return acc;
84401
84408
  }, {
@@ -84421,7 +84428,7 @@ var require_dist_cjs35 = __commonJS((exports) => {
84421
84428
  const matches = prefixKeyRegex.exec(sectionName);
84422
84429
  if (matches) {
84423
84430
  const [, prefix, , name] = matches;
84424
- if (Object.values(types3.IniSectionType).includes(prefix)) {
84431
+ if (Object.values(types4.IniSectionType).includes(prefix)) {
84425
84432
  currentSection = [prefix, name].join(CONFIG_PREFIX_SEPARATOR);
84426
84433
  }
84427
84434
  } else {
@@ -84478,7 +84485,7 @@ var require_dist_cjs35 = __commonJS((exports) => {
84478
84485
  credentialsFile: parsedFiles[1]
84479
84486
  };
84480
84487
  };
84481
- var getSsoSessionData = (data) => Object.entries(data).filter(([key]) => key.startsWith(types3.IniSectionType.SSO_SESSION + CONFIG_PREFIX_SEPARATOR)).reduce((acc, [key, value]) => ({ ...acc, [key.substring(key.indexOf(CONFIG_PREFIX_SEPARATOR) + 1)]: value }), {});
84488
+ var getSsoSessionData = (data) => Object.entries(data).filter(([key]) => key.startsWith(types4.IniSectionType.SSO_SESSION + CONFIG_PREFIX_SEPARATOR)).reduce((acc, [key, value]) => ({ ...acc, [key.substring(key.indexOf(CONFIG_PREFIX_SEPARATOR) + 1)]: value }), {});
84482
84489
  var swallowError = () => ({});
84483
84490
  var loadSsoSessionData = async (init = {}) => readFile.readFile(init.configFilepath ?? getConfigFilepath()).then(parseIni).then(getSsoSessionData).catch(swallowError);
84484
84491
  var mergeConfigFiles = (...files) => {
@@ -113211,6 +113218,8 @@ class Migrator {
113211
113218
  return MigrationClass;
113212
113219
  }
113213
113220
  }
113221
+ // ../atlas/src/OrbitAtlas.ts
113222
+ init_DB();
113214
113223
  // ../atlas/src/orm/model/DirtyTracker.ts
113215
113224
  class DirtyTracker {
113216
113225
  original = new Map;
@@ -115485,6 +115494,155 @@ var Hono2 = class extends Hono {
115485
115494
  }
115486
115495
  };
115487
115496
 
115497
+ // ../quasar/src/bridges/BaseZenithBridge.ts
115498
+ class BaseZenithBridge {
115499
+ redis;
115500
+ prefix;
115501
+ workerId;
115502
+ listeners = [];
115503
+ constructor(redis, prefix = "flux_console:", workerId) {
115504
+ this.redis = redis;
115505
+ this.prefix = prefix;
115506
+ this.workerId = workerId;
115507
+ }
115508
+ async publishLog(payload) {
115509
+ try {
115510
+ const fullPayload = {
115511
+ ...payload,
115512
+ workerId: payload.workerId || this.workerId,
115513
+ timestamp: payload.timestamp || new Date().toISOString()
115514
+ };
115515
+ await this.redis.publish(`${this.prefix}logs`, JSON.stringify(fullPayload));
115516
+ const historyKey = `${this.prefix}logs:history`;
115517
+ if (typeof this.redis.pipeline === "function") {
115518
+ const pipe = this.redis.pipeline();
115519
+ pipe.lpush(historyKey, JSON.stringify(fullPayload));
115520
+ pipe.ltrim(historyKey, 0, 99);
115521
+ await pipe.exec();
115522
+ } else {
115523
+ await this.redis.lpush(historyKey, JSON.stringify(fullPayload));
115524
+ }
115525
+ } catch (err) {
115526
+ console.error("[BaseZenithBridge] Failed to publish log:", err);
115527
+ }
115528
+ }
115529
+ registerListener(target, event, handler) {
115530
+ this.listeners.push({ target, event, handler });
115531
+ }
115532
+ detach() {
115533
+ for (const { target, event, handler } of this.listeners) {
115534
+ if (typeof target.off === "function") {
115535
+ target.off(event, handler);
115536
+ } else if (typeof target.removeListener === "function") {
115537
+ target.removeListener(event, handler);
115538
+ }
115539
+ }
115540
+ this.listeners = [];
115541
+ }
115542
+ }
115543
+ // ../quasar/src/bridges/BeeQueueBridge.ts
115544
+ class BeeQueueBridge extends BaseZenithBridge {
115545
+ attach(queue) {
115546
+ const onSucceeded = async (job, result) => {
115547
+ await this.publishLog({
115548
+ level: "success",
115549
+ message: `Completed job: ${job.id}`,
115550
+ jobId: job.id,
115551
+ context: {
115552
+ data: job.data,
115553
+ result: typeof result === "object" ? JSON.stringify(result) : result
115554
+ }
115555
+ });
115556
+ };
115557
+ const onFailed = async (job, error) => {
115558
+ await this.publishLog({
115559
+ level: "error",
115560
+ message: `Job failed: ${job.id} - ${error.message}`,
115561
+ jobId: job.id,
115562
+ context: {
115563
+ data: job.data,
115564
+ error: error.message,
115565
+ stack: error.stack
115566
+ }
115567
+ });
115568
+ };
115569
+ const onProgress = async (job, progress) => {
115570
+ await this.publishLog({
115571
+ level: "info",
115572
+ message: `Job progress: ${job.id}`,
115573
+ jobId: job.id,
115574
+ context: {
115575
+ data: job.data,
115576
+ progress
115577
+ }
115578
+ });
115579
+ };
115580
+ queue.on("job succeeded", onSucceeded);
115581
+ queue.on("job failed", onFailed);
115582
+ queue.on("job progress", onProgress);
115583
+ this.registerListener(queue, "job succeeded", onSucceeded);
115584
+ this.registerListener(queue, "job failed", onFailed);
115585
+ this.registerListener(queue, "job progress", onProgress);
115586
+ }
115587
+ }
115588
+ // ../quasar/src/bridges/BullMQBridge.ts
115589
+ class BullMQBridge extends BaseZenithBridge {
115590
+ attach(worker) {
115591
+ const onActive = async (job) => {
115592
+ await this.publishLog({
115593
+ level: "info",
115594
+ message: `Processing job: ${job.name || job.id}`,
115595
+ jobId: job.id,
115596
+ context: {
115597
+ name: job.name,
115598
+ data: job.data
115599
+ }
115600
+ });
115601
+ };
115602
+ const onCompleted = async (job, result) => {
115603
+ await this.publishLog({
115604
+ level: "success",
115605
+ message: `Completed job: ${job.name || job.id}`,
115606
+ jobId: job.id,
115607
+ context: {
115608
+ name: job.name,
115609
+ result: typeof result === "object" ? JSON.stringify(result) : result
115610
+ }
115611
+ });
115612
+ };
115613
+ const onFailed = async (job, error) => {
115614
+ await this.publishLog({
115615
+ level: "error",
115616
+ message: `Job failed: ${job?.name || job?.id} - ${error.message}`,
115617
+ jobId: job?.id,
115618
+ context: {
115619
+ name: job?.name,
115620
+ error: error.message,
115621
+ stack: error.stack
115622
+ }
115623
+ });
115624
+ };
115625
+ const onProgress = async (job, progress) => {
115626
+ await this.publishLog({
115627
+ level: "info",
115628
+ message: `Job progress: ${job.name || job.id}`,
115629
+ jobId: job.id,
115630
+ context: {
115631
+ name: job.name,
115632
+ progress
115633
+ }
115634
+ });
115635
+ };
115636
+ worker.on("active", onActive);
115637
+ worker.on("completed", onCompleted);
115638
+ worker.on("failed", onFailed);
115639
+ worker.on("progress", onProgress);
115640
+ this.registerListener(worker, "active", onActive);
115641
+ this.registerListener(worker, "completed", onCompleted);
115642
+ this.registerListener(worker, "failed", onFailed);
115643
+ this.registerListener(worker, "progress", onProgress);
115644
+ }
115645
+ }
115488
115646
  // ../quasar/src/executors/BaseExecutor.ts
115489
115647
  class BaseExecutor {
115490
115648
  success(commandId, message) {
@@ -115724,6 +115882,76 @@ class CommandListener {
115724
115882
  }
115725
115883
  }
115726
115884
  }
115885
+ // ../quasar/src/probes/BeeQueueProbe.ts
115886
+ class BeeQueueProbe {
115887
+ redis;
115888
+ queueName;
115889
+ prefix;
115890
+ constructor(redis, queueName, prefix = "bq") {
115891
+ this.redis = redis;
115892
+ this.queueName = queueName;
115893
+ this.prefix = prefix;
115894
+ }
115895
+ async getSnapshot() {
115896
+ const key = (suffix) => `${this.prefix}:${this.queueName}:${suffix}`;
115897
+ const pipeline = this.redis.pipeline();
115898
+ pipeline.llen(key("waiting"));
115899
+ pipeline.llen(key("active"));
115900
+ pipeline.llen(key("failed"));
115901
+ const results = await pipeline.exec();
115902
+ if (!results)
115903
+ throw new Error("Redis pipeline failed");
115904
+ const [_waitingErr, waiting] = results[0];
115905
+ const [_activeErr, active] = results[1];
115906
+ const [_failedErr, failed] = results[2];
115907
+ return {
115908
+ name: this.queueName,
115909
+ driver: "redis",
115910
+ size: {
115911
+ waiting: Number(waiting || 0),
115912
+ active: Number(active || 0),
115913
+ failed: Number(failed || 0),
115914
+ delayed: 0
115915
+ }
115916
+ };
115917
+ }
115918
+ }
115919
+ // ../quasar/src/probes/BullMQProbe.ts
115920
+ class BullMQProbe {
115921
+ redis;
115922
+ queueName;
115923
+ prefix;
115924
+ constructor(redis, queueName, prefix = "bull") {
115925
+ this.redis = redis;
115926
+ this.queueName = queueName;
115927
+ this.prefix = prefix;
115928
+ }
115929
+ async getSnapshot() {
115930
+ const key = (suffix) => `${this.prefix}:${this.queueName}:${suffix}`;
115931
+ const pipeline = this.redis.pipeline();
115932
+ pipeline.llen(key("wait"));
115933
+ pipeline.llen(key("active"));
115934
+ pipeline.zcard(key("delayed"));
115935
+ pipeline.scard(key("failed"));
115936
+ const results = await pipeline.exec();
115937
+ if (!results)
115938
+ throw new Error("Redis pipeline failed");
115939
+ const [_waitErr, waiting] = results[0];
115940
+ const [_activeErr, active] = results[1];
115941
+ const [_delayedErr, delayed] = results[2];
115942
+ const [_failedErr, failed] = results[3];
115943
+ return {
115944
+ name: this.queueName,
115945
+ driver: "redis",
115946
+ size: {
115947
+ waiting: Number(waiting || 0),
115948
+ active: Number(active || 0),
115949
+ failed: Number(failed || 0),
115950
+ delayed: Number(delayed || 0)
115951
+ }
115952
+ };
115953
+ }
115954
+ }
115727
115955
  // ../quasar/src/probes/NodeProbe.ts
115728
115956
  import os from "os";
115729
115957
  import process2 from "process";
@@ -115822,10 +116050,10 @@ class BullProbe {
115822
116050
  const results = await pipeline.exec();
115823
116051
  if (!results)
115824
116052
  throw new Error("Redis pipeline failed");
115825
- const [waitingErr, waiting] = results[0];
115826
- const [activeErr, active] = results[1];
115827
- const [delayedErr, delayed] = results[2];
115828
- const [failedErr, failed] = results[3];
116053
+ const [_waitingErr, waiting] = results[0];
116054
+ const [_activeErr, active] = results[1];
116055
+ const [_delayedErr, delayed] = results[2];
116056
+ const [_failedErr, failed] = results[3];
115829
116057
  return {
115830
116058
  name: this.queueName,
115831
116059
  driver: "redis",
@@ -115908,6 +116136,7 @@ class QuasarAgent {
115908
116136
  interval;
115909
116137
  probe;
115910
116138
  queueProbes = [];
116139
+ bridges = [];
115911
116140
  timer = null;
115912
116141
  prefix = "gravito:quasar:node:";
115913
116142
  nodeId;
@@ -115984,7 +116213,30 @@ class QuasarAgent {
115984
116213
  this.queueProbes.push(new LaravelProbe(this.monitorRedis, name));
115985
116214
  } else if (type === "bull") {
115986
116215
  this.queueProbes.push(new BullProbe(this.monitorRedis, name));
116216
+ } else if (type === "bullmq") {
116217
+ this.queueProbes.push(new BullMQProbe(this.monitorRedis, name));
116218
+ } else if (type === "bee-queue") {
116219
+ this.queueProbes.push(new BeeQueueProbe(this.monitorRedis, name));
116220
+ }
116221
+ }
116222
+ attachBridge(worker, type) {
116223
+ if (!this.transportRedis) {
116224
+ console.warn("[Quasar] Cannot attach bridge: transport connection required");
116225
+ return;
115987
116226
  }
116227
+ const workerId = this.nodeId || `${this.service}-${process.pid}`;
116228
+ let bridge;
116229
+ if (type === "bullmq") {
116230
+ bridge = new BullMQBridge(this.transportRedis, "flux_console:", workerId);
116231
+ } else if (type === "bee-queue") {
116232
+ bridge = new BeeQueueBridge(this.transportRedis, "flux_console:", workerId);
116233
+ } else {
116234
+ console.warn(`[Quasar] Unknown bridge type: ${type}`);
116235
+ return;
116236
+ }
116237
+ bridge.attach(worker);
116238
+ this.bridges.push(bridge);
116239
+ console.log(`[Quasar] \uD83D\uDD17 Attached ${type} bridge to worker`);
115988
116240
  }
115989
116241
  async enableRemoteControl() {
115990
116242
  if (!this.monitorRedis) {
@@ -118486,7 +118738,7 @@ class QueueService {
118486
118738
  return jobs;
118487
118739
  }
118488
118740
  }
118489
- async recordStatusMetrics(nodes = {}) {
118741
+ async recordStatusMetrics(nodes = {}, injectedWorkers) {
118490
118742
  const stats = await this.listQueues();
118491
118743
  const totals = stats.reduce((acc, q3) => {
118492
118744
  acc.waiting += q3.waiting;
@@ -118499,7 +118751,7 @@ class QueueService {
118499
118751
  pipe.set(`flux_console:metrics:waiting:${now}`, totals.waiting, "EX", 3600);
118500
118752
  pipe.set(`flux_console:metrics:delayed:${now}`, totals.delayed, "EX", 3600);
118501
118753
  pipe.set(`flux_console:metrics:failed:${now}`, totals.failed, "EX", 3600);
118502
- const workers = await this.listWorkers();
118754
+ const workers = injectedWorkers || await this.listWorkers();
118503
118755
  pipe.set(`flux_console:metrics:workers:${now}`, workers.length, "EX", 3600);
118504
118756
  await pipe.exec();
118505
118757
  this.logEmitter.emit("stats", {
@@ -118688,8 +118940,8 @@ class QueueService {
118688
118940
  if (results.length >= limit) {
118689
118941
  break;
118690
118942
  }
118691
- const types6 = type === "all" ? ["waiting", "delayed", "failed"] : [type];
118692
- for (const jobType of types6) {
118943
+ const types7 = type === "all" ? ["waiting", "delayed", "failed"] : [type];
118944
+ for (const jobType of types7) {
118693
118945
  if (results.length >= limit) {
118694
118946
  break;
118695
118947
  }
@@ -118831,14 +119083,85 @@ queueService.connect().then(() => pulseService.connect()).then(() => commandServ
118831
119083
  });
118832
119084
  agent.start().catch((err) => console.error("[FluxConsole] Quasar Agent Error:", err));
118833
119085
  console.log(`[FluxConsole] Connected to Redis at ${REDIS_URL}`);
118834
- setInterval(async () => {
118835
- const nodes = await pulseService.getNodes();
118836
- queueService.recordStatusMetrics(nodes).catch(console.error);
118837
- }, 2000);
119086
+ const updateMetrics = async () => {
119087
+ try {
119088
+ const [pulseNodes, legacyWorkers] = await Promise.all([
119089
+ pulseService.getNodes(),
119090
+ queueService.listWorkers()
119091
+ ]);
119092
+ const pulseWorkers = Object.values(pulseNodes).flat().flatMap((node) => {
119093
+ const mainNode = {
119094
+ id: node.id,
119095
+ service: node.service,
119096
+ status: node.runtime.status || "online",
119097
+ pid: node.pid,
119098
+ uptime: node.runtime.uptime,
119099
+ metrics: {
119100
+ cpu: node.cpu.process,
119101
+ cores: node.cpu.cores,
119102
+ ram: {
119103
+ rss: node.memory.process.rss,
119104
+ heapUsed: node.memory.process.heapUsed,
119105
+ total: node.memory.system.total
119106
+ }
119107
+ },
119108
+ queues: node.queues,
119109
+ meta: node.meta
119110
+ };
119111
+ const subWorkers = [];
119112
+ if (node.meta?.laravel?.workers && Array.isArray(node.meta.laravel.workers)) {
119113
+ node.meta.laravel.workers.forEach((w3) => {
119114
+ subWorkers.push({
119115
+ id: `${node.id}-php-${w3.pid}`,
119116
+ service: `${node.service} / LARAVEL`,
119117
+ status: w3.status === "running" || w3.status === "sleep" ? "online" : "idle",
119118
+ pid: w3.pid,
119119
+ uptime: node.runtime.uptime,
119120
+ metrics: {
119121
+ cpu: w3.cpu,
119122
+ cores: 1,
119123
+ ram: {
119124
+ rss: w3.memory,
119125
+ heapUsed: w3.memory,
119126
+ total: node.memory.system.total
119127
+ }
119128
+ },
119129
+ meta: { isVirtual: true, cmdline: w3.cmdline }
119130
+ });
119131
+ });
119132
+ }
119133
+ return [mainNode, ...subWorkers];
119134
+ });
119135
+ const formattedLegacy = legacyWorkers.map((w3) => ({
119136
+ id: w3.id,
119137
+ status: "online",
119138
+ pid: w3.pid,
119139
+ uptime: w3.uptime,
119140
+ metrics: {
119141
+ cpu: (w3.loadAvg[0] || 0) * 100,
119142
+ cores: 0,
119143
+ ram: {
119144
+ rss: parseInt(w3.memory.rss || "0", 10),
119145
+ heapUsed: parseInt(w3.memory.heapUsed || "0", 10),
119146
+ total: 0
119147
+ }
119148
+ },
119149
+ queues: w3.queues.map((q3) => ({
119150
+ name: q3,
119151
+ size: { waiting: 0, active: 0, failed: 0, delayed: 0 }
119152
+ })),
119153
+ meta: {}
119154
+ }));
119155
+ await queueService.recordStatusMetrics(pulseNodes, [...pulseWorkers, ...formattedLegacy]);
119156
+ } catch (err) {
119157
+ console.error("[FluxConsole] Metrics Update Error:", err);
119158
+ }
119159
+ };
119160
+ setInterval(updateMetrics, 2000);
118838
119161
  setInterval(() => {
118839
119162
  queueService.tickScheduler().catch(console.error);
118840
119163
  }, 5000);
118841
- pulseService.getNodes().then((nodes) => queueService.recordStatusMetrics(nodes)).catch(console.error);
119164
+ updateMetrics();
118842
119165
  }).catch((err) => {
118843
119166
  console.error("[FluxConsole] Failed to connect to Redis", err);
118844
119167
  });
@@ -119032,9 +119355,79 @@ api.get("/throughput", async (c3) => {
119032
119355
  });
119033
119356
  api.get("/workers", async (c3) => {
119034
119357
  try {
119035
- const workers = await queueService.listWorkers();
119036
- return c3.json({ workers });
119358
+ const [legacyWorkers, pulseNodes] = await Promise.all([
119359
+ queueService.listWorkers(),
119360
+ pulseService.getNodes()
119361
+ ]);
119362
+ const pulseWorkers = Object.values(pulseNodes).flat().flatMap((node) => {
119363
+ const mainNode = {
119364
+ id: node.id,
119365
+ service: node.service,
119366
+ status: node.runtime.status || "online",
119367
+ pid: node.pid,
119368
+ uptime: node.runtime.uptime,
119369
+ metrics: {
119370
+ cpu: node.cpu.process,
119371
+ cores: node.cpu.cores,
119372
+ ram: {
119373
+ rss: node.memory.process.rss,
119374
+ heapUsed: node.memory.process.heapUsed,
119375
+ total: node.memory.system.total
119376
+ }
119377
+ },
119378
+ queues: node.queues,
119379
+ meta: node.meta
119380
+ };
119381
+ const subWorkers = [];
119382
+ if (node.meta?.laravel?.workers && Array.isArray(node.meta.laravel.workers)) {
119383
+ node.meta.laravel.workers.forEach((w3) => {
119384
+ subWorkers.push({
119385
+ id: `${node.id}-php-${w3.pid}`,
119386
+ service: `${node.service} / LARAVEL`,
119387
+ status: w3.status === "running" || w3.status === "sleep" ? "online" : "idle",
119388
+ pid: w3.pid,
119389
+ uptime: node.runtime.uptime,
119390
+ metrics: {
119391
+ cpu: w3.cpu,
119392
+ cores: 1,
119393
+ ram: {
119394
+ rss: w3.memory,
119395
+ heapUsed: w3.memory,
119396
+ total: node.memory.system.total
119397
+ }
119398
+ },
119399
+ meta: {
119400
+ isVirtual: true,
119401
+ cmdline: w3.cmdline
119402
+ }
119403
+ });
119404
+ });
119405
+ }
119406
+ return [mainNode, ...subWorkers];
119407
+ });
119408
+ const formattedLegacy = legacyWorkers.map((w3) => ({
119409
+ id: w3.id,
119410
+ status: "online",
119411
+ pid: w3.pid,
119412
+ uptime: w3.uptime,
119413
+ metrics: {
119414
+ cpu: (w3.loadAvg[0] || 0) * 100,
119415
+ cores: 0,
119416
+ ram: {
119417
+ rss: parseInt(w3.memory.rss || "0", 10),
119418
+ heapUsed: parseInt(w3.memory.heapUsed || "0", 10),
119419
+ total: 0
119420
+ }
119421
+ },
119422
+ queues: w3.queues.map((q3) => ({
119423
+ name: q3,
119424
+ size: { waiting: 0, active: 0, failed: 0, delayed: 0 }
119425
+ })),
119426
+ meta: {}
119427
+ }));
119428
+ return c3.json({ workers: [...pulseWorkers, ...formattedLegacy] });
119037
119429
  } catch (_err) {
119430
+ console.error(_err);
119038
119431
  return c3.json({ error: "Failed to fetch workers" }, 500);
119039
119432
  }
119040
119433
  });