@gravito/core 3.0.0 → 3.0.1

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 (59) hide show
  1. package/README.md +64 -25
  2. package/dist/Application.d.ts +2 -9
  3. package/dist/Container.d.ts +18 -1
  4. package/dist/GravitoServer.d.ts +8 -8
  5. package/dist/HookManager.d.ts +36 -34
  6. package/dist/PlanetCore.d.ts +48 -0
  7. package/dist/Route.d.ts +5 -0
  8. package/dist/Router.d.ts +14 -1
  9. package/dist/adapters/bun/BunContext.d.ts +1 -1
  10. package/dist/adapters/bun/BunNativeAdapter.d.ts +11 -1
  11. package/dist/adapters/bun/FastPathRegistry.d.ts +31 -0
  12. package/dist/adapters/bun/RadixNode.d.ts +4 -3
  13. package/dist/adapters/bun/RadixRouter.d.ts +2 -2
  14. package/dist/adapters/bun/types.d.ts +7 -0
  15. package/dist/adapters/types.d.ts +20 -0
  16. package/dist/compat/async-local-storage.d.ts +5 -1
  17. package/dist/compat/async-local-storage.js.map +2 -2
  18. package/dist/compat/crypto.d.ts +6 -1
  19. package/dist/compat/crypto.js.map +2 -2
  20. package/dist/engine/AOTRouter.d.ts +1 -1
  21. package/dist/engine/FastContext.d.ts +4 -4
  22. package/dist/engine/MinimalContext.d.ts +3 -3
  23. package/dist/engine/index.js +29 -8
  24. package/dist/engine/index.js.map +10 -10
  25. package/dist/engine/types.d.ts +5 -5
  26. package/dist/events/MessageQueueBridge.d.ts +2 -1
  27. package/dist/events/observability/EventMetrics.d.ts +1 -2
  28. package/dist/events/observability/ObservableHookManager.d.ts +1 -1
  29. package/dist/exceptions/AuthException.d.ts +11 -2
  30. package/dist/exceptions/AuthenticationException.d.ts +7 -1
  31. package/dist/exceptions/ContainerBindingCollisionException.d.ts +10 -0
  32. package/dist/exceptions/MiddlewareDriftException.d.ts +10 -0
  33. package/dist/exceptions/index.d.ts +3 -1
  34. package/dist/ffi/NativeAccelerator.js.map +3 -3
  35. package/dist/ffi/NativeHasher.d.ts +14 -0
  36. package/dist/ffi/NativeHasher.js +24 -1
  37. package/dist/ffi/NativeHasher.js.map +4 -4
  38. package/dist/ffi/cbor-fallback.js.map +1 -1
  39. package/dist/ffi/hash-fallback.d.ts +15 -0
  40. package/dist/ffi/hash-fallback.js +12 -1
  41. package/dist/ffi/hash-fallback.js.map +3 -3
  42. package/dist/ffi/types.d.ts +13 -0
  43. package/dist/ffi/types.js.map +1 -1
  44. package/dist/hooks/types.d.ts +7 -3
  45. package/dist/http/types.d.ts +2 -2
  46. package/dist/index.browser.d.ts +5 -5
  47. package/dist/index.browser.js +138 -17
  48. package/dist/index.browser.js.map +44 -42
  49. package/dist/index.d.ts +17 -7
  50. package/dist/index.js +588 -295
  51. package/dist/index.js.map +81 -77
  52. package/dist/runtime/NativeOrbitDetector.d.ts +59 -0
  53. package/dist/runtime/index.browser.d.ts +1 -1
  54. package/dist/runtime/index.d.ts +7 -0
  55. package/dist/runtime.d.ts +1 -1
  56. package/dist/testing/HttpTester.d.ts +4 -4
  57. package/dist/testing/TestResponse.d.ts +4 -4
  58. package/dist/types.d.ts +3 -3
  59. package/package.json +4 -3
@@ -1,6 +1,8 @@
1
1
  import type { CircuitBreakerOptions } from '../events/CircuitBreaker';
2
2
  import type { EventBackend } from '../events/EventBackend';
3
+ import type { MessageQueueBridge } from '../events/MessageQueueBridge';
3
4
  import type { EventQueueConfig } from '../events/EventPriorityQueue';
5
+ import type { AggregationConfig } from '../events/aggregation/types';
4
6
  /**
5
7
  * Callback function for filters (transforms values).
6
8
  * @public
@@ -87,8 +89,10 @@ export interface HookManagerConfig {
87
89
  /**
88
90
  * Database connection for persistent DLQ (optional).
89
91
  * If provided, failed events after max retries will be persisted to database.
92
+ * Typed as `unknown` since core does not depend on @gravito/atlas or any specific DB type.
93
+ * The consuming code (DeadLetterQueueManager) accepts and narrows this to its internal type.
90
94
  */
91
- db?: any;
95
+ db?: unknown;
92
96
  /**
93
97
  * Enable persistent DLQ for failed events (requires db).
94
98
  * @default false
@@ -98,10 +102,10 @@ export interface HookManagerConfig {
98
102
  * Message Queue Bridge for distributed event processing via Bull Queue.
99
103
  * When provided, enables dispatchQueued() method for routing events to Redis-backed queue.
100
104
  */
101
- messageQueueBridge?: any;
105
+ messageQueueBridge?: MessageQueueBridge;
102
106
  /**
103
107
  * Event aggregation configuration (FS-102).
104
108
  * Enables deduplication and micro-batching for improved throughput.
105
109
  */
106
- aggregation?: any;
110
+ aggregation?: AggregationConfig;
107
111
  }
@@ -320,7 +320,7 @@ export interface GravitoContext<V extends GravitoVariables = GravitoVariables> {
320
320
  * URL generator helper.
321
321
  * Generates a URL for a named route.
322
322
  */
323
- route: (name: string, params?: Record<string, any>, query?: Record<string, any>) => string;
323
+ route: (name: string, params?: Record<string, string | number>, query?: Record<string, string | number | boolean | null | undefined>) => string;
324
324
  /**
325
325
  * Access the native context object from the underlying HTTP engine.
326
326
  *
@@ -338,7 +338,7 @@ export interface GravitoContext<V extends GravitoVariables = GravitoVariables> {
338
338
  * Access the RequestScopeManager for this request
339
339
  * Services resolved through this manager are scoped to the HTTP request lifetime.
340
340
  */
341
- requestScope(): any;
341
+ requestScope(): unknown;
342
342
  /**
343
343
  * Resolve or create a request-scoped service
344
344
  *
@@ -23,12 +23,12 @@ export { EventManager } from './EventManager';
23
23
  export * from './events';
24
24
  export { HookManager, type HookManagerConfig } from './HookManager';
25
25
  export { HealthProvider } from './health/HealthProvider';
26
- export { Arr, abort, abortIf, abortUnless, app, blank, config, DumpDieError, dd, dump, env, filled, hasApp, logger, router, Str, setApp, tap, throwIf, throwUnless, value, } from './helpers';
27
- export * from './helpers/data';
28
- export * from './helpers/errors';
29
- export * from './helpers/response';
26
+ export { Arr, abort, abortIf, abortUnless, app, blank, config, DumpDieError, dd, dump, env, filled, hasApp, logger, router, Str, tap, throwIf, throwUnless, value, } from './helpers';
27
+ export { type DataPath, dataGet, dataHas, dataSet, type PathSegment } from './helpers/data';
28
+ export { createErrorBag, type ErrorBag, errors, old } from './helpers/errors';
29
+ export { type ApiFailure, type ApiSuccess, fail, jsonFail, jsonSuccess, ok, } from './helpers/response';
30
30
  export { CookieJar, type CookieOptions } from './http/CookieJar';
31
31
  export { deleteCookie, getCookie, setCookie } from './http/cookie';
32
32
  export { ServiceProvider } from './ServiceProvider';
33
33
  export * from './runtime/index.browser';
34
- export declare function defineConfig(config: any): any;
34
+ export declare function defineConfig<T extends Record<string, unknown>>(config: T): T;
@@ -149,7 +149,7 @@ var randomBytes = randomBytesFn;
149
149
  // package.json
150
150
  var package_default = {
151
151
  name: "@gravito/core",
152
- version: "3.0.0",
152
+ version: "3.0.1",
153
153
  description: "",
154
154
  module: "./dist/index.js",
155
155
  main: "./dist/index.js",
@@ -158,9 +158,9 @@ var package_default = {
158
158
  types: "./dist/index.d.ts",
159
159
  exports: {
160
160
  ".": {
161
+ types: "./dist/index.d.ts",
161
162
  browser: "./dist/index.browser.js",
162
163
  bun: "./dist/index.js",
163
- types: "./dist/index.d.ts",
164
164
  default: "./dist/index.js"
165
165
  },
166
166
  "./compat": {
@@ -206,7 +206,8 @@ var package_default = {
206
206
  typecheck: "bun tsc -p tsconfig.json --noEmit --skipLibCheck",
207
207
  prepublishOnly: "bun run typecheck && bun run test && bun run build",
208
208
  "test:unit": "bun test $(find tests -name '*.test.ts' ! -name '*.integration.test.ts' 2>/dev/null | tr '\\n' ' ') --timeout=10000",
209
- "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'"
209
+ "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'",
210
+ publint: "publint"
210
211
  },
211
212
  keywords: [],
212
213
  author: "Carl Lee <carllee0520@gmail.com>",
@@ -253,6 +254,7 @@ class RadixNode {
253
254
  paramChild = null;
254
255
  wildcardChild = null;
255
256
  handlers = new Map;
257
+ options = new Map;
256
258
  paramName = null;
257
259
  regex = null;
258
260
  constructor(segment = "", type = 0 /* STATIC */) {
@@ -266,13 +268,15 @@ class RadixNode {
266
268
  children: Array.from(this.children.entries()).map(([k, v]) => [k, v.toJSON()]),
267
269
  paramChild: this.paramChild?.toJSON() || null,
268
270
  wildcardChild: this.wildcardChild?.toJSON() || null,
271
+ handlers: Array.from(this.handlers.entries()),
272
+ options: Array.from(this.options.entries()),
269
273
  paramName: this.paramName,
270
274
  regex: this.regex ? this.regex.source : null
271
275
  };
272
276
  }
273
277
  static fromJSON(json) {
274
278
  const node = new RadixNode(json.segment, json.type);
275
- node.paramName = json.paramName;
279
+ node.paramName = json.paramName ?? null;
276
280
  if (json.regex) {
277
281
  node.regex = new RegExp(json.regex);
278
282
  }
@@ -287,6 +291,16 @@ class RadixNode {
287
291
  if (json.wildcardChild) {
288
292
  node.wildcardChild = RadixNode.fromJSON(json.wildcardChild);
289
293
  }
294
+ if (json.handlers) {
295
+ for (const [method, handlers] of json.handlers) {
296
+ node.handlers.set(method, handlers);
297
+ }
298
+ }
299
+ if (json.options) {
300
+ for (const [method, options2] of json.options) {
301
+ node.options.set(method, options2);
302
+ }
303
+ }
290
304
  return node;
291
305
  }
292
306
  }
@@ -322,7 +336,7 @@ class RadixRouter {
322
336
  where(param, regex) {
323
337
  this.globalConstraints.set(param, regex);
324
338
  }
325
- add(method, path2, handlers) {
339
+ add(method, path2, handlers, options2) {
326
340
  let node = this.root;
327
341
  const segments = this.splitPath(path2);
328
342
  for (let i = 0;i < segments.length; i++) {
@@ -352,7 +366,11 @@ class RadixRouter {
352
366
  node = node.children.get(segment);
353
367
  }
354
368
  }
355
- node.handlers.set(method.toLowerCase(), handlers);
369
+ const normalizedMethod = method.toLowerCase();
370
+ node.handlers.set(normalizedMethod, handlers);
371
+ if (options2) {
372
+ node.options.set(normalizedMethod, options2);
373
+ }
356
374
  this.routeCache.clear();
357
375
  }
358
376
  match(method, path2) {
@@ -360,7 +378,7 @@ class RadixRouter {
360
378
  if (path2 === "/" || path2 === "") {
361
379
  const handlers = this.root.handlers.get(normalizedMethod);
362
380
  if (handlers) {
363
- return { handlers, params: {} };
381
+ return { handlers, params: {}, options: this.root.options.get(normalizedMethod) };
364
382
  }
365
383
  return null;
366
384
  }
@@ -377,11 +395,13 @@ class RadixRouter {
377
395
  matchRecursive(node, segments, depth, params, method) {
378
396
  if (depth >= segments.length) {
379
397
  let handlers = node.handlers.get(method);
398
+ let options2 = node.options.get(method);
380
399
  if (!handlers) {
381
400
  handlers = node.handlers.get("all");
401
+ options2 = node.options.get("all");
382
402
  }
383
403
  if (handlers) {
384
- return { handlers, params };
404
+ return { handlers, params, options: options2 };
385
405
  }
386
406
  return null;
387
407
  }
@@ -408,11 +428,13 @@ class RadixRouter {
408
428
  }
409
429
  if (node.wildcardChild) {
410
430
  let handlers = node.wildcardChild.handlers.get(method);
431
+ let options2 = node.wildcardChild.options.get(method);
411
432
  if (!handlers) {
412
433
  handlers = node.wildcardChild.handlers.get("all");
434
+ options2 = node.wildcardChild.options.get("all");
413
435
  }
414
436
  if (handlers) {
415
- return { handlers, params };
437
+ return { handlers, params, options: options2 };
416
438
  }
417
439
  }
418
440
  return null;
@@ -894,7 +916,7 @@ class RequestScopeManager {
894
916
  let servicesCleaned = 0;
895
917
  for (const [, instance] of this.scoped) {
896
918
  if (instance && typeof instance === "object" && "cleanup" in instance) {
897
- const fn = instance.cleanup;
919
+ const fn = instance["cleanup"];
898
920
  if (typeof fn === "function") {
899
921
  try {
900
922
  await fn.call(instance);
@@ -2548,7 +2570,8 @@ function createNodeAdapter() {
2548
2570
  },
2549
2571
  resourceUsage: async () => {
2550
2572
  try {
2551
- const usage = child.resourceUsage?.();
2573
+ const childWithUsage = child;
2574
+ const usage = childWithUsage.resourceUsage?.();
2552
2575
  if (!usage) {
2553
2576
  return;
2554
2577
  }
@@ -2757,6 +2780,78 @@ function createUnknownAdapter() {
2757
2780
  }
2758
2781
  };
2759
2782
  }
2783
+ // src/runtime/NativeOrbitDetector.ts
2784
+ function probeCryptoHasher(CryptoHasherCtor, algo) {
2785
+ try {
2786
+ const ctor = CryptoHasherCtor;
2787
+ new ctor(algo).update("").digest("hex");
2788
+ return true;
2789
+ } catch {
2790
+ return false;
2791
+ }
2792
+ }
2793
+
2794
+ class NativeOrbitDetector {
2795
+ static cached = null;
2796
+ static detectBunCapabilities() {
2797
+ if (this.cached !== null) {
2798
+ return this.cached;
2799
+ }
2800
+ const kind2 = getRuntimeKind();
2801
+ const B2 = globalThis.Bun;
2802
+ if (kind2 !== "bun" || !B2) {
2803
+ this.cached = Object.freeze({
2804
+ runtime: kind2,
2805
+ bunVersion: null,
2806
+ password: Object.freeze({ available: false, argon2id: false, bcrypt: false }),
2807
+ cryptoHasher: Object.freeze({
2808
+ available: false,
2809
+ sha256: false,
2810
+ sha512: false,
2811
+ blake2b: false
2812
+ }),
2813
+ glob: false
2814
+ });
2815
+ return this.cached;
2816
+ }
2817
+ const hasPassword = typeof B2.password?.hash === "function" && typeof B2.password?.verify === "function";
2818
+ const passwordFeatures = Object.freeze({
2819
+ available: hasPassword,
2820
+ argon2id: hasPassword,
2821
+ bcrypt: hasPassword
2822
+ });
2823
+ const HasherCtor = B2.CryptoHasher;
2824
+ const hasHasher = typeof HasherCtor === "function";
2825
+ const sha256 = hasHasher ? probeCryptoHasher(HasherCtor, "sha256") : false;
2826
+ const sha512 = hasHasher ? probeCryptoHasher(HasherCtor, "sha512") : false;
2827
+ const blake2b = hasHasher ? probeCryptoHasher(HasherCtor, "blake2b256") : false;
2828
+ const cryptoHasherFeatures = Object.freeze({
2829
+ available: hasHasher && sha256,
2830
+ sha256,
2831
+ sha512,
2832
+ blake2b
2833
+ });
2834
+ const hasGlob = typeof B2.Glob === "function";
2835
+ this.cached = Object.freeze({
2836
+ runtime: kind2,
2837
+ bunVersion: B2.version ?? null,
2838
+ password: passwordFeatures,
2839
+ cryptoHasher: cryptoHasherFeatures,
2840
+ glob: hasGlob
2841
+ });
2842
+ return this.cached;
2843
+ }
2844
+ static reset() {
2845
+ this.cached = null;
2846
+ }
2847
+ }
2848
+ function formatCapabilityReport(f) {
2849
+ const passwordPart = f.password.argon2id ? "Bun.password argon2id \u2713" : "Bun.password argon2id \u2717 (fallback: none)";
2850
+ const hasherPart = f.cryptoHasher.available ? "Bun.CryptoHasher \u2713" : "Bun.CryptoHasher \u2717 (fallback: node:crypto)";
2851
+ const globPart = f.glob ? "Bun.Glob \u2713" : "Bun.Glob \u2717 (fallback: node:fs glob)";
2852
+ return `[gravito] native: ${passwordPart}, ${hasherPart}, ${globPart}`;
2853
+ }
2854
+
2760
2855
  // src/runtime/archive.ts
2761
2856
  function createBunArchiveAdapter() {
2762
2857
  return {
@@ -2896,6 +2991,9 @@ async function archiveFromDirectory(dirPath, archivePath, options = {}) {
2896
2991
  let entries = {};
2897
2992
  if (kind === "bun") {
2898
2993
  const B = globalThis.Bun;
2994
+ if (!B?.Glob || !B.file) {
2995
+ throw new Error("[RuntimeArchiveAdapter] Bun global not available for directory scanning");
2996
+ }
2899
2997
  const glob = new B.Glob(options.glob ?? "**/*");
2900
2998
  for await (const file of glob.scan(dirPath)) {
2901
2999
  const pathMod = await eval('import("node:path")');
@@ -3509,6 +3607,9 @@ function getPasswordAdapter() {
3509
3607
  };
3510
3608
  return passwordAdapter;
3511
3609
  }
3610
+ function resetPasswordAdapter() {
3611
+ passwordAdapter = null;
3612
+ }
3512
3613
  async function createSqliteDatabase(path2) {
3513
3614
  const kind2 = getRuntimeKind();
3514
3615
  const B2 = globalThis.Bun;
@@ -3657,6 +3758,15 @@ class CircularDependencyException extends SystemException {
3657
3758
  }
3658
3759
  }
3659
3760
 
3761
+ // src/exceptions/ContainerBindingCollisionException.ts
3762
+ class ContainerBindingCollisionException extends SystemException {
3763
+ constructor(message, options2 = {}) {
3764
+ super(500, "system.container_binding_collision", { ...options2, message });
3765
+ this.name = "ContainerBindingCollisionException";
3766
+ Object.setPrototypeOf(this, new.target.prototype);
3767
+ }
3768
+ }
3769
+
3660
3770
  // src/Container.ts
3661
3771
  var scopeStorage = new AsyncLocalStorage;
3662
3772
 
@@ -3681,6 +3791,17 @@ class Container {
3681
3791
  scope: "singleton"
3682
3792
  });
3683
3793
  }
3794
+ singletonInline(namespace, key, factory) {
3795
+ const namespacedKey = `inline:${namespace}:${key}`;
3796
+ if (this.has(namespacedKey)) {
3797
+ if (true) {
3798
+ throw new ContainerBindingCollisionException(`Binding '${namespacedKey}' already registered by plugin '${namespace}'`);
3799
+ }
3800
+ console.warn(`[gravito] Binding '${namespacedKey}' collision detected \u2014 skipping duplicate registration.`);
3801
+ return;
3802
+ }
3803
+ this.singleton(namespacedKey, factory);
3804
+ }
3684
3805
  scoped(key, factory) {
3685
3806
  this.bindings.set(key, {
3686
3807
  factory,
@@ -6266,7 +6387,8 @@ class MessageQueueBridge {
6266
6387
  throw new Error(`[MessageQueueBridge] No listeners registered for event: ${eventName}`);
6267
6388
  }
6268
6389
  if (this.config.enableCircuitBreaker) {
6269
- const breaker = this.config.hookManager.getCircuitBreaker?.(eventName);
6390
+ const hm = this.config.hookManager;
6391
+ const breaker = hm.getCircuitBreaker?.(eventName);
6270
6392
  if (breaker?.getState?.() === "OPEN") {
6271
6393
  throw new Error(`[MessageQueueBridge] Circuit breaker is OPEN for event: ${eventName}`);
6272
6394
  }
@@ -6392,9 +6514,9 @@ class RetryScheduler {
6392
6514
  try {
6393
6515
  const queue = this.getOrCreateQueue(eventName);
6394
6516
  const delay = this.calculateDelay(retryCount);
6395
- const addMethod = queue.add;
6396
- if (typeof addMethod === "function") {
6397
- await addMethod.call(queue, "retry", { payload, error: error.message, retryCount }, { delay });
6517
+ const queueObj = queue;
6518
+ if (typeof queueObj.add === "function") {
6519
+ await queueObj.add.call(queue, "retry", { payload, error: error.message, retryCount }, { delay });
6398
6520
  }
6399
6521
  } catch (schedulerError) {
6400
6522
  const err = schedulerError instanceof Error ? schedulerError : new Error(String(schedulerError));
@@ -8975,7 +9097,6 @@ export {
8975
9097
  throwIf,
8976
9098
  tap,
8977
9099
  setCookie,
8978
- setApp,
8979
9100
  router,
8980
9101
  registerQueueCommands,
8981
9102
  old,
@@ -9052,4 +9173,4 @@ export {
9052
9173
  Arr
9053
9174
  };
9054
9175
 
9055
- //# debugId=004674489562A73F64756E2164756E21
9176
+ //# debugId=245BF91366B5C49E64756E2164756E21