@gravito/spectrum 3.0.1 → 3.0.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.
package/dist/index.js CHANGED
@@ -1,46 +1,113 @@
1
+ // src/SpectrumOrbit.ts
2
+ import { getCsrfToken } from "@gravito/core";
3
+
1
4
  // src/storage/MemoryStorage.ts
2
5
  var MemoryStorage = class {
3
6
  requests = [];
4
7
  logs = [];
5
8
  queries = [];
6
9
  maxItems = 1e3;
10
+ /**
11
+ * Initializes the memory storage.
12
+ *
13
+ * As memory storage does not require external resources, this is a no-op.
14
+ *
15
+ * @returns Resolves immediately
16
+ */
7
17
  async init() {
8
18
  }
19
+ /**
20
+ * Temporarily stores a captured HTTP request in memory.
21
+ *
22
+ * @param req - The request snapshot to store
23
+ */
9
24
  async storeRequest(req) {
10
25
  this.requests.unshift(req);
11
26
  this.trim(this.requests);
12
27
  }
28
+ /**
29
+ * Temporarily stores a captured log entry in memory.
30
+ *
31
+ * @param log - The log entry to store
32
+ */
13
33
  async storeLog(log) {
14
34
  this.logs.unshift(log);
15
35
  this.trim(this.logs);
16
36
  }
37
+ /**
38
+ * Temporarily stores a captured database query in memory.
39
+ *
40
+ * @param query - The query information to store
41
+ */
17
42
  async storeQuery(query) {
18
43
  this.queries.unshift(query);
19
44
  this.trim(this.queries);
20
45
  }
46
+ /**
47
+ * Retrieves a list of recent HTTP requests from memory.
48
+ *
49
+ * @param limit - Maximum number of requests to return
50
+ * @param offset - Number of requests to skip for pagination
51
+ * @returns A promise resolving to an array of captured requests
52
+ */
21
53
  async getRequests(limit = 100, offset = 0) {
22
54
  return this.requests.slice(offset, offset + limit);
23
55
  }
56
+ /**
57
+ * Finds a specific HTTP request by its unique identifier.
58
+ *
59
+ * @param id - The unique ID of the request to find
60
+ * @returns The found request or null if not present
61
+ */
24
62
  async getRequest(id) {
25
63
  return this.requests.find((r) => r.id === id) || null;
26
64
  }
65
+ /**
66
+ * Retrieves a list of recent application logs from memory.
67
+ *
68
+ * @param limit - Maximum number of logs to return
69
+ * @param offset - Number of logs to skip for pagination
70
+ * @returns A promise resolving to an array of captured logs
71
+ */
27
72
  async getLogs(limit = 100, offset = 0) {
28
73
  return this.logs.slice(offset, offset + limit);
29
74
  }
75
+ /**
76
+ * Retrieves a list of recent database queries from memory.
77
+ *
78
+ * @param limit - Maximum number of queries to return
79
+ * @param offset - Number of queries to skip for pagination
80
+ * @returns A promise resolving to an array of captured queries
81
+ */
30
82
  async getQueries(limit = 100, offset = 0) {
31
83
  return this.queries.slice(offset, offset + limit);
32
84
  }
85
+ /**
86
+ * Wipes all captured telemetry data from memory.
87
+ *
88
+ * @returns Resolves when all collections are emptied
89
+ */
33
90
  async clear() {
34
91
  this.requests = [];
35
92
  this.logs = [];
36
93
  this.queries = [];
37
94
  }
95
+ /**
96
+ * Sets a new capacity limit and prunes existing data to fit.
97
+ *
98
+ * @param maxItems - The new maximum number of items to retain per category
99
+ */
38
100
  async prune(maxItems) {
39
101
  this.maxItems = maxItems;
40
102
  this.trim(this.requests);
41
103
  this.trim(this.logs);
42
104
  this.trim(this.queries);
43
105
  }
106
+ /**
107
+ * Truncates an array to ensure it does not exceed the maximum allowed capacity.
108
+ *
109
+ * @param arr - The target array to truncate
110
+ */
44
111
  trim(arr) {
45
112
  if (arr.length > this.maxItems) {
46
113
  arr.splice(this.maxItems);
@@ -50,13 +117,30 @@ var MemoryStorage = class {
50
117
 
51
118
  // src/SpectrumOrbit.ts
52
119
  var SpectrumOrbit = class _SpectrumOrbit {
120
+ /**
121
+ * Global instance of SpectrumOrbit.
122
+ *
123
+ * Used for internal access and singleton patterns within the framework.
124
+ */
53
125
  static instance;
54
126
  name = "spectrum";
55
127
  config;
56
128
  // Event listeners for SSE
57
129
  listeners = /* @__PURE__ */ new Set();
130
+ currentRequestId = null;
58
131
  warnedSecurity = false;
59
132
  deps;
133
+ /**
134
+ * Initializes a new instance of SpectrumOrbit.
135
+ *
136
+ * @param config - Configuration options for behavior and storage
137
+ * @param deps - Internal dependencies for integration with other orbits
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * const spectrum = new SpectrumOrbit({ path: '/debug' });
142
+ * ```
143
+ */
60
144
  constructor(config = {}, deps = {}) {
61
145
  this.config = {
62
146
  path: config.path || "/gravito/spectrum",
@@ -69,29 +153,60 @@ var SpectrumOrbit = class _SpectrumOrbit {
69
153
  this.deps = deps;
70
154
  _SpectrumOrbit.instance = this;
71
155
  }
156
+ /**
157
+ * Checks if the current operation should be captured based on sample rate.
158
+ *
159
+ * @returns True if capture is allowed
160
+ */
72
161
  shouldCapture() {
73
162
  if (this.config.sampleRate >= 1) {
74
163
  return true;
75
164
  }
76
165
  return Math.random() < this.config.sampleRate;
77
166
  }
167
+ /**
168
+ * Broadcasts telemetry data to all connected SSE clients.
169
+ *
170
+ * @param type - The category of the data
171
+ * @param data - The telemetry payload
172
+ */
78
173
  broadcast(type, data) {
79
174
  const payload = JSON.stringify({ type, data });
80
175
  for (const listener of this.listeners) {
81
176
  listener(payload);
82
177
  }
83
178
  }
179
+ /**
180
+ * Installs the Spectrum orbit into the Gravito core.
181
+ *
182
+ * Sets up collection listeners, initializes storage, and registers API/UI routes.
183
+ *
184
+ * @param core - The planet core instance
185
+ * @returns Resolves when installation is complete
186
+ * @throws {Error} If storage initialization fails
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * await spectrum.install(core);
191
+ * ```
192
+ */
84
193
  async install(core) {
85
194
  if (!this.config.enabled) {
86
195
  return;
87
196
  }
88
197
  await this.config.storage.init();
198
+ await this.config.storage.prune(this.config.maxItems);
89
199
  this.setupHttpCollection(core);
90
200
  this.setupLogCollection(core);
91
201
  this.setupDatabaseCollection(core);
92
202
  this.registerRoutes(core);
93
203
  core.logger.info(`[Spectrum] Debug Dashboard initialized at ${this.config.path}`);
94
204
  }
205
+ /**
206
+ * Configures collection of database queries from Atlas orbit.
207
+ *
208
+ * @param core - The planet core instance
209
+ */
95
210
  setupDatabaseCollection(core) {
96
211
  const attachListener = (atlas) => {
97
212
  if (!atlas?.Connection) {
@@ -103,7 +218,8 @@ var SpectrumOrbit = class _SpectrumOrbit {
103
218
  }
104
219
  const data = {
105
220
  id: crypto.randomUUID(),
106
- ...query
221
+ ...query,
222
+ requestId: this.currentRequestId || void 0
107
223
  };
108
224
  this.config.storage.storeQuery(data);
109
225
  this.broadcast("query", data);
@@ -131,21 +247,29 @@ var SpectrumOrbit = class _SpectrumOrbit {
131
247
  } catch (_e) {
132
248
  }
133
249
  }
250
+ /**
251
+ * Configures interception of HTTP requests and responses.
252
+ *
253
+ * @param core - The planet core instance
254
+ */
134
255
  setupHttpCollection(core) {
135
256
  const middleware = async (c, next) => {
136
257
  if (c.req.path.startsWith(this.config.path)) {
137
258
  return await next();
138
259
  }
260
+ const requestId = crypto.randomUUID();
261
+ this.currentRequestId = requestId;
139
262
  const startTime = performance.now();
140
263
  const startTimestamp = Date.now();
141
264
  const res = await next();
142
265
  const duration = performance.now() - startTime;
266
+ this.currentRequestId = null;
143
267
  if (!this.shouldCapture()) {
144
268
  return res;
145
269
  }
146
270
  const finalRes = res || c.res;
147
271
  const request = {
148
- id: crypto.randomUUID(),
272
+ id: requestId,
149
273
  method: c.req.method,
150
274
  path: c.req.path,
151
275
  url: c.req.url,
@@ -155,7 +279,8 @@ var SpectrumOrbit = class _SpectrumOrbit {
155
279
  ip: c.req.header("x-forwarded-for") || "127.0.0.1",
156
280
  userAgent: c.req.header("user-agent"),
157
281
  requestHeaders: Object.fromEntries(c.req.raw.headers.entries()),
158
- responseHeaders: finalRes ? Object.fromEntries(finalRes.headers.entries()) : {}
282
+ responseHeaders: finalRes ? Object.fromEntries(finalRes.headers.entries()) : {},
283
+ requestId
159
284
  };
160
285
  this.config.storage.storeRequest(request);
161
286
  this.broadcast("request", request);
@@ -163,6 +288,13 @@ var SpectrumOrbit = class _SpectrumOrbit {
163
288
  };
164
289
  core.adapter.use("*", middleware);
165
290
  }
291
+ /**
292
+ * Configures interception of application logs.
293
+ *
294
+ * Wraps the core logger to capture all log calls.
295
+ *
296
+ * @param core - The planet core instance
297
+ */
166
298
  setupLogCollection(core) {
167
299
  const originalLogger = core.logger;
168
300
  const spectrumLogger = {
@@ -185,6 +317,13 @@ var SpectrumOrbit = class _SpectrumOrbit {
185
317
  };
186
318
  core.logger = spectrumLogger;
187
319
  }
320
+ /**
321
+ * Processes and stores a captured log entry.
322
+ *
323
+ * @param level - Log severity level
324
+ * @param message - Primary message string
325
+ * @param args - Additional log arguments
326
+ */
188
327
  captureLog(level, message, args) {
189
328
  if (!this.shouldCapture()) {
190
329
  return;
@@ -194,11 +333,17 @@ var SpectrumOrbit = class _SpectrumOrbit {
194
333
  level,
195
334
  message,
196
335
  args,
197
- timestamp: Date.now()
336
+ timestamp: Date.now(),
337
+ requestId: this.currentRequestId || void 0
198
338
  };
199
339
  this.config.storage.storeLog(log);
200
340
  this.broadcast("log", log);
201
341
  }
342
+ /**
343
+ * Registers all API and UI routes for the Spectrum dashboard.
344
+ *
345
+ * @param core - The planet core instance
346
+ */
202
347
  registerRoutes(core) {
203
348
  const router = core.router;
204
349
  const apiPath = `${this.config.path}/api`;
@@ -242,6 +387,13 @@ var SpectrumOrbit = class _SpectrumOrbit {
242
387
  router.post(
243
388
  `${apiPath}/clear`,
244
389
  wrap(async (c) => {
390
+ if (c.req && typeof c.req.header === "function") {
391
+ const token = c.req.header("x-csrf-token") || (await c.req.parseBody())?._csrf;
392
+ const expectedToken = getCsrfToken(c);
393
+ if (expectedToken && (!token || token !== expectedToken)) {
394
+ return c.json({ error: "Invalid CSRF token" }, 419);
395
+ }
396
+ }
245
397
  await this.config.storage.clear();
246
398
  return c.json({ success: true });
247
399
  })
@@ -252,23 +404,37 @@ var SpectrumOrbit = class _SpectrumOrbit {
252
404
  const { readable, writable } = new TransformStream();
253
405
  const writer = writable.getWriter();
254
406
  const encoder = new TextEncoder();
407
+ let isClosed = false;
408
+ const cleanup = () => {
409
+ if (isClosed) {
410
+ return;
411
+ }
412
+ isClosed = true;
413
+ clearInterval(heartbeat);
414
+ this.listeners.delete(send);
415
+ if (typeof writer.close === "function") {
416
+ writer.close().catch(() => {
417
+ });
418
+ }
419
+ };
255
420
  const send = (payload) => {
256
- try {
257
- writer.write(encoder.encode(`data: ${payload}
258
-
259
- `));
260
- } catch (_e) {
261
- this.listeners.delete(send);
421
+ if (isClosed) {
422
+ return;
262
423
  }
424
+ Promise.resolve().then(() => writer.write(encoder.encode(`data: ${payload}
425
+
426
+ `))).catch(() => {
427
+ cleanup();
428
+ });
263
429
  };
264
430
  this.listeners.add(send);
265
431
  const heartbeat = setInterval(() => {
266
- try {
267
- writer.write(encoder.encode(": heartbeat\n\n"));
268
- } catch (_e) {
269
- clearInterval(heartbeat);
270
- this.listeners.delete(send);
432
+ if (isClosed) {
433
+ return;
271
434
  }
435
+ Promise.resolve().then(() => writer.write(encoder.encode(": heartbeat\n\n"))).catch(() => {
436
+ cleanup();
437
+ });
272
438
  }, 3e4);
273
439
  return new Response(readable, {
274
440
  headers: {
@@ -282,6 +448,13 @@ var SpectrumOrbit = class _SpectrumOrbit {
282
448
  router.post(
283
449
  `${apiPath}/replay/:id`,
284
450
  wrap(async (c) => {
451
+ if (c.req && typeof c.req.header === "function") {
452
+ const token = c.req.header("x-csrf-token") || (await c.req.parseBody())?._csrf;
453
+ const expectedToken = getCsrfToken(c);
454
+ if (expectedToken && (!token || token !== expectedToken)) {
455
+ return c.json({ error: "Invalid CSRF token" }, 419);
456
+ }
457
+ }
285
458
  const id = c.req.param("id");
286
459
  if (!id) {
287
460
  return c.json({ error: "ID required" }, 400);
@@ -294,7 +467,6 @@ var SpectrumOrbit = class _SpectrumOrbit {
294
467
  const replayReq = new Request(req.url, {
295
468
  method: req.method,
296
469
  headers: req.requestHeaders
297
- // Body logic would be needed here (if captured)
298
470
  });
299
471
  const res = await core.adapter.fetch(replayReq);
300
472
  return c.json({
@@ -445,7 +617,8 @@ var SpectrumOrbit = class _SpectrumOrbit {
445
617
  logs: [],
446
618
  queries: [],
447
619
  connected: false,
448
- eventSource: null
620
+ eventSource: null,
621
+ csrfToken: this.getCsrfTokenFromCookie()
449
622
  }
450
623
  },
451
624
  computed: {
@@ -467,6 +640,14 @@ var SpectrumOrbit = class _SpectrumOrbit {
467
640
  this.fetchData();
468
641
  this.initRealtime();
469
642
  },
643
+ unmounted() {
644
+ // Cleanup EventSource to prevent memory leaks
645
+ if (this.eventSource) {
646
+ this.eventSource.close();
647
+ this.eventSource = null;
648
+ this.connected = false;
649
+ }
650
+ },
470
651
  methods: {
471
652
  initRealtime() {
472
653
  this.eventSource = new EventSource('${apiPath}/events');
@@ -505,13 +686,35 @@ var SpectrumOrbit = class _SpectrumOrbit {
505
686
  },
506
687
  async clearData() {
507
688
  if (confirm('Are you sure you want to clear all debug data?')) {
508
- await fetch('${apiPath}/clear', { method: 'POST' });
509
- this.fetchData();
689
+ try {
690
+ const res = await fetch('${apiPath}/clear', {
691
+ method: 'POST',
692
+ headers: {
693
+ 'X-CSRF-Token': this.csrfToken
694
+ }
695
+ });
696
+ if (res.status === 419) {
697
+ alert('CSRF token invalid. Please refresh the page.');
698
+ return;
699
+ }
700
+ this.fetchData();
701
+ } catch (e) {
702
+ alert('Failed to clear data: ' + e.message);
703
+ }
510
704
  }
511
705
  },
512
706
  async replayRequest(id) {
513
707
  try {
514
- const res = await fetch('${apiPath}/replay/' + id, { method: 'POST' });
708
+ const res = await fetch('${apiPath}/replay/' + id, {
709
+ method: 'POST',
710
+ headers: {
711
+ 'X-CSRF-Token': this.csrfToken
712
+ }
713
+ });
714
+ if (res.status === 419) {
715
+ alert('CSRF token invalid. Please refresh the page.');
716
+ return;
717
+ }
515
718
  const data = await res.json();
516
719
  if (data.success) {
517
720
  alert('Replay successful! Status: ' + data.status);
@@ -550,6 +753,10 @@ var SpectrumOrbit = class _SpectrumOrbit {
550
753
  'debug': 'text-slate-500'
551
754
  };
552
755
  return map[l] || 'text-slate-400';
756
+ },
757
+ getCsrfTokenFromCookie() {
758
+ const match = document.cookie.match(/csrf_token=([^;]+)/);
759
+ return match ? match[1] : '';
553
760
  }
554
761
  }
555
762
  }).mount('#app')
@@ -566,6 +773,16 @@ var SpectrumOrbit = class _SpectrumOrbit {
566
773
  import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
567
774
  import { join } from "path";
568
775
  var FileStorage = class {
776
+ /**
777
+ * Initializes a new instance of FileStorage.
778
+ *
779
+ * @param config - Configuration including the target directory for JSONL files
780
+ *
781
+ * @example
782
+ * ```typescript
783
+ * const storage = new FileStorage({ directory: './storage' });
784
+ * ```
785
+ */
569
786
  constructor(config) {
570
787
  this.config = config;
571
788
  this.requestsPath = join(config.directory, "spectrum-requests.jsonl");
@@ -583,6 +800,12 @@ var FileStorage = class {
583
800
  logs: [],
584
801
  queries: []
585
802
  };
803
+ /**
804
+ * Initializes the storage by creating the directory and loading existing files into memory.
805
+ *
806
+ * @returns Resolves when initialization is complete
807
+ * @throws {Error} If directory creation fails
808
+ */
586
809
  async init() {
587
810
  if (!existsSync(this.config.directory)) {
588
811
  mkdirSync(this.config.directory, { recursive: true });
@@ -591,6 +814,12 @@ var FileStorage = class {
591
814
  this.loadCache(this.logsPath, this.cache.logs);
592
815
  this.loadCache(this.queriesPath, this.cache.queries);
593
816
  }
817
+ /**
818
+ * Loads newline-delimited JSON data from a file into a target array.
819
+ *
820
+ * @param path - The absolute path to the JSONL file
821
+ * @param target - The array to populate with parsed objects
822
+ */
594
823
  loadCache(path, target) {
595
824
  if (!existsSync(path)) {
596
825
  return;
@@ -608,6 +837,13 @@ var FileStorage = class {
608
837
  console.error(`[Spectrum] Failed to load cache from ${path}`, e);
609
838
  }
610
839
  }
840
+ /**
841
+ * Internal helper to append data to both the in-memory cache and the JSONL file.
842
+ *
843
+ * @param path - File path to append to
844
+ * @param data - The telemetry object
845
+ * @param list - The cache array
846
+ */
611
847
  async append(path, data, list) {
612
848
  list.unshift(data);
613
849
  try {
@@ -617,27 +853,72 @@ var FileStorage = class {
617
853
  console.error(`[Spectrum] Failed to write to ${path}`, e);
618
854
  }
619
855
  }
856
+ /**
857
+ * Persists a captured HTTP request.
858
+ *
859
+ * @param req - The request snapshot
860
+ */
620
861
  async storeRequest(req) {
621
862
  await this.append(this.requestsPath, req, this.cache.requests);
622
863
  }
864
+ /**
865
+ * Persists a captured log entry.
866
+ *
867
+ * @param log - The log snapshot
868
+ */
623
869
  async storeLog(log) {
624
870
  await this.append(this.logsPath, log, this.cache.logs);
625
871
  }
872
+ /**
873
+ * Persists a captured database query.
874
+ *
875
+ * @param query - The query snapshot
876
+ */
626
877
  async storeQuery(query) {
627
878
  await this.append(this.queriesPath, query, this.cache.queries);
628
879
  }
880
+ /**
881
+ * Retrieves recent HTTP requests from storage.
882
+ *
883
+ * @param limit - Maximum number of items to return
884
+ * @param offset - Pagination offset
885
+ * @returns Array of requests
886
+ */
629
887
  async getRequests(limit = 100, offset = 0) {
630
888
  return this.cache.requests.slice(offset, offset + limit);
631
889
  }
890
+ /**
891
+ * Retrieves a specific HTTP request by its unique ID.
892
+ *
893
+ * @param id - The snapshot ID
894
+ * @returns The request or null if not found
895
+ */
632
896
  async getRequest(id) {
633
897
  return this.cache.requests.find((r) => r.id === id) || null;
634
898
  }
899
+ /**
900
+ * Retrieves recent logs from storage.
901
+ *
902
+ * @param limit - Maximum number of items to return
903
+ * @param offset - Pagination offset
904
+ * @returns Array of logs
905
+ */
635
906
  async getLogs(limit = 100, offset = 0) {
636
907
  return this.cache.logs.slice(offset, offset + limit);
637
908
  }
909
+ /**
910
+ * Retrieves recent database queries from storage.
911
+ *
912
+ * @param limit - Maximum number of items to return
913
+ * @param offset - Pagination offset
914
+ * @returns Array of queries
915
+ */
638
916
  async getQueries(limit = 100, offset = 0) {
639
917
  return this.cache.queries.slice(offset, offset + limit);
640
918
  }
919
+ /**
920
+ * Wipes all data from both cache and files.
921
+ */
641
922
  async clear() {
642
923
  this.cache.requests = [];
643
924
  this.cache.logs = [];
@@ -646,12 +927,31 @@ var FileStorage = class {
646
927
  writeFileSync(this.logsPath, "");
647
928
  writeFileSync(this.queriesPath, "");
648
929
  }
930
+ /**
931
+ * Truncates the storage to stay within the specified limit.
932
+ *
933
+ * @param maxItems - The maximum allowed records per category
934
+ */
649
935
  async prune(maxItems) {
650
936
  if (this.cache.requests.length > maxItems) {
651
937
  this.cache.requests = this.cache.requests.slice(0, maxItems);
652
938
  this.rewrite(this.requestsPath, this.cache.requests);
653
939
  }
940
+ if (this.cache.logs.length > maxItems) {
941
+ this.cache.logs = this.cache.logs.slice(0, maxItems);
942
+ this.rewrite(this.logsPath, this.cache.logs);
943
+ }
944
+ if (this.cache.queries.length > maxItems) {
945
+ this.cache.queries = this.cache.queries.slice(0, maxItems);
946
+ this.rewrite(this.queriesPath, this.cache.queries);
947
+ }
654
948
  }
949
+ /**
950
+ * Overwrites the file content with the current in-memory data.
951
+ *
952
+ * @param path - File path to overwrite
953
+ * @param data - The array of data objects
954
+ */
655
955
  rewrite(path, data) {
656
956
  const content = `${data.slice().reverse().map((d) => JSON.stringify(d)).join("\n")}
657
957
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravito/spectrum",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -28,23 +28,25 @@
28
28
  "scripts": {
29
29
  "build": "bun run build.ts",
30
30
  "typecheck": "bun tsc -p tsconfig.json --noEmit --skipLibCheck",
31
- "test": "bun test",
32
- "test:coverage": "bun test --coverage --coverage-threshold=80",
33
- "test:ci": "bun test --coverage --coverage-threshold=80"
31
+ "test": "bun test --timeout=10000",
32
+ "test:coverage": "bun test --timeout=10000 --coverage --coverage-reporter=lcov --coverage-dir coverage && bun run --bun scripts/check-coverage.ts",
33
+ "test:ci": "bun test --timeout=10000 --coverage --coverage-reporter=lcov --coverage-dir coverage && bun run --bun scripts/check-coverage.ts",
34
+ "test:unit": "bun test tests/ --timeout=10000",
35
+ "test:integration": "test $(find tests -name '*.integration.test.ts' 2>/dev/null | wc -l) -gt 0 && find tests -name '*.integration.test.ts' -print0 | xargs -0 bun test --timeout=10000 || echo 'No integration tests found'"
34
36
  },
35
37
  "peerDependencies": {
36
- "@gravito/core": "workspace:*",
37
- "@gravito/photon": "workspace:*"
38
+ "@gravito/core": "^1.6.1",
39
+ "@gravito/photon": "^1.0.1"
38
40
  },
39
41
  "devDependencies": {
40
- "@types/bun": "latest",
42
+ "bun-types": "latest",
41
43
  "@opentelemetry/api": "^1.9.0",
42
44
  "@opentelemetry/sdk-node": "^0.57.0",
43
45
  "@opentelemetry/exporter-trace-otlp-http": "^0.57.0",
44
46
  "@opentelemetry/resources": "^1.29.0",
45
47
  "@opentelemetry/semantic-conventions": "^1.28.0",
46
- "@gravito/core": "workspace:*",
47
- "tsup": "^8.2.4",
48
+ "@gravito/core": "^1.6.1",
49
+ "tsup": "^8.0.0",
48
50
  "typescript": "^5.9.3"
49
51
  },
50
52
  "optionalDependencies": {
@@ -70,4 +72,4 @@
70
72
  "url": "https://github.com/gravito-framework/gravito.git",
71
73
  "directory": "packages/monitor"
72
74
  }
73
- }
75
+ }