@bhooai/nexus-core 2.0.15 → 2.0.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bhooai/nexus-core",
3
- "version": "2.0.15",
3
+ "version": "2.0.16",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -20,6 +20,7 @@
20
20
  "dependencies": {
21
21
  "@bhooai/nexus-ai-client": "^2.0.1",
22
22
  "@bhooai/nexus-auth": "^2.0.1",
23
+ "@bhooai/nexus-crypto": "^2.0.15",
23
24
  "@bhooai/nexus-data": "^2.0.1",
24
25
  "selfsigned": "^5.5.0",
25
26
  "zod": "^3.23.8"
@@ -40,6 +40,7 @@ import { createLazyDb, registerAdminRoutes, RequestLogBuffer } from './adminModu
40
40
  import { registerAuthRoutes } from './authModule.js';
41
41
  import { issueCsrfToken, getCsrfToken, csrf, authToken, requireRole } from '@bhooai/nexus-auth';
42
42
  import { connect } from '@bhooai/nexus-data';
43
+ import { ensureLicense } from '@bhooai/nexus-crypto';
43
44
  import { createGateway, createFederatedGateway, graphqlHttpHandler, getExplorerHtml, helloSubgraph, SubscriptionServer } from '@bhooai/nexus-graphql';
44
45
  import type { Subgraph, GraphQLContext } from '@bhooai/nexus-graphql';
45
46
 
@@ -103,6 +104,19 @@ export async function createNexusApp(opts: CreateNexusAppOptions = {}): Promise<
103
104
  const projectRoot = resolve(opts.projectRoot ?? dirname(srcRoot));
104
105
  let config = opts.config ?? (await loadConfigAuto({ root: projectRoot }));
105
106
 
107
+ // License check at project start — verify the token (signature via fetched
108
+ // JWKS + online status) with the authority. On failure the app still boots,
109
+ // but every request is answered with 402 LICENSE_REQUIRED (middleware below)
110
+ // instead of crashing — a crashed backend surfaces as an opaque HTTP 500
111
+ // through dev proxies.
112
+ let licenseError: string | null = null;
113
+ try {
114
+ await ensureLicense({ projectDir: projectRoot });
115
+ } catch (err) {
116
+ licenseError = (err as Error).message;
117
+ console.error(`[${name}] license check failed: ${licenseError}`);
118
+ }
119
+
106
120
  // ------------------------------------------------------------------
107
121
  // Per-app ports: nexus.config.ts `apps[]` is authoritative. The app's own
108
122
  // entry (matched by `opts.name`) wins over `server.port`. NEXUS_PORT (set
@@ -516,6 +530,17 @@ export async function createNexusApp(opts: CreateNexusAppOptions = {}): Promise<
516
530
  },
517
531
  });
518
532
 
533
+ // License gate — when the project-start check failed, answer every request
534
+ // (except /health) with 402 so the UI can show the license error rather than
535
+ // an opaque 500 from a crashed backend.
536
+ if (licenseError) {
537
+ const message = licenseError;
538
+ server.use(async (ctx, next) => {
539
+ if (ctx.path === '/health') return next();
540
+ ctx.json({ error: { code: 'LICENSE_REQUIRED', message } }, 402);
541
+ });
542
+ }
543
+
519
544
  // ------------------------------------------------------------------
520
545
  // Realtime WS server — mount discovered ws/*.room.ts handlers (chat lobby)
521
546
  // React stack only — future uses live canvas
package/src/errors.ts CHANGED
@@ -54,9 +54,30 @@ export class PaymentError extends NexusError {
54
54
  }
55
55
  }
56
56
 
57
+ /**
58
+ * A license failure surfaced by `@bhooai/nexus-crypto` (a plain Error carrying
59
+ * `code: 'LICENSE_REQUIRED'`). Kept shape-based so core need not import crypto.
60
+ */
61
+ export class LicenseRequiredError extends NexusError {
62
+ constructor(message: string, details?: unknown) {
63
+ super(message, { code: 'LICENSE_REQUIRED', statusCode: 402, details });
64
+ }
65
+ }
66
+
67
+ function licenseCodeOf(err: Error): string | undefined {
68
+ const code = (err as { code?: unknown }).code;
69
+ return typeof code === 'string' ? code : undefined;
70
+ }
71
+
57
72
  /** Normalize an unknown thrown value into a NexusError. */
58
73
  export function toNexusError(err: unknown): NexusError {
59
74
  if (err instanceof NexusError) return err;
60
- if (err instanceof Error) return new NexusError(err.message, { code: 'NEXUS_ERROR' });
75
+ if (err instanceof Error) {
76
+ if (licenseCodeOf(err) === 'LICENSE_REQUIRED') {
77
+ const hint = (err as { hint?: unknown }).hint;
78
+ return new LicenseRequiredError(err.message, typeof hint === 'string' ? hint : undefined);
79
+ }
80
+ return new NexusError(err.message, { code: 'NEXUS_ERROR' });
81
+ }
61
82
  return new NexusError(String(err));
62
83
  }
package/vitest.config.ts CHANGED
@@ -4,6 +4,7 @@ export default defineProject({
4
4
  test: {
5
5
  environment: 'node',
6
6
  include: ['tests/**/*.test.ts'],
7
+ env: { NEXUS_LICENSE_KEY: 'nxl_test_master_key_001' },
7
8
  globals: false,
8
9
  },
9
10
  });