@bhooai/nexus-crypto 2.0.16 → 2.0.18

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/license.ts +46 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bhooai/nexus-crypto",
3
- "version": "2.0.16",
3
+ "version": "2.0.18",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/license.ts CHANGED
@@ -10,23 +10,24 @@
10
10
  *
11
11
  * Flow:
12
12
  * - `nexus license add` (browser) stores the token at
13
- * `<projectRoot>/license/license.jwt`.
13
+ * `<projectRoot>/nexus/licence/licence.jwt`.
14
14
  * - Each run calls `ensureLicense()`, which verifies the token (signature via
15
15
  * fetched JWKS + online status) and caches the result in memory for the run.
16
16
  * - Gated features call `requireLicense()` (sync, reads the in-memory result).
17
17
  *
18
18
  * Lookup order: `NEXUS_LICENSE_KEY` env (CI/containers) →
19
- * `<projectRoot>/license/license.jwt`. The token is a bearer credential, so the
20
- * `license/` folder should be gitignored.
19
+ * `<projectRoot>/nexus/licence/licence.jwt`. The token is a bearer credential, so the
20
+ * `nexus/licence/` folder should be gitignored.
21
21
  */
22
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
23
- import { join } from 'node:path';
22
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
23
+ import { join, dirname, resolve } from 'node:path';
24
24
  import { createLocalJWKSet, jwtVerify, type JSONWebKeySet } from 'jose';
25
25
 
26
26
  export const LICENSE_ENV_VAR = 'NEXUS_LICENSE_KEY';
27
27
  export const LICENSE_SERVER_ENV_VAR = 'NEXUS_LICENSE_SERVER';
28
- export const LICENSE_FILE_NAME = 'license.jwt';
29
- export const LICENSE_DIR_NAME = 'license';
28
+ export const LICENSE_FILE_NAME = 'licence.jwt';
29
+ /** Per-project license folder: `<projectRoot>/nexus/licence/`. */
30
+ export const LICENSE_DIR_NAME = 'nexus/licence';
30
31
  export const LICENSE_ISSUER = 'nexus-bhooai-com';
31
32
 
32
33
  /** Default authority. Override per-project/CI via `NEXUS_LICENSE_SERVER`. */
@@ -85,15 +86,30 @@ export function resolveLicenseToken(opts: ResolveOptions = {}): ResolvedLicense
85
86
  const fromEnv = (env[LICENSE_ENV_VAR] ?? '').trim();
86
87
  if (fromEnv) return { token: fromEnv, source: 'env' };
87
88
 
89
+ // Candidate roots: an explicit NEXUS_PROJECT_ROOT (set by `nexus dev`, so app
90
+ // processes whose own projectRoot is a subfolder like `apps/backend` still
91
+ // find the project's `license/`), then the given projectDir and its parents.
92
+ const roots: string[] = [];
93
+ const explicit = (env.NEXUS_PROJECT_ROOT ?? '').trim();
94
+ if (explicit) roots.push(resolve(explicit));
88
95
  if (opts.projectDir) {
89
- const file = licenseFilePath(opts.projectDir);
90
- if (existsSync(file)) {
91
- try {
92
- const token = readFileSync(file, 'utf8').trim();
93
- if (token) return { token, source: 'file', file };
94
- } catch {
95
- // unreadable → treated as no license
96
- }
96
+ let dir = resolve(opts.projectDir);
97
+ for (let i = 0; i < 4; i++) {
98
+ if (!roots.includes(dir)) roots.push(dir);
99
+ const parent = dirname(dir);
100
+ if (parent === dir) break;
101
+ dir = parent;
102
+ }
103
+ }
104
+
105
+ for (const root of roots) {
106
+ const file = licenseFilePath(root);
107
+ if (!existsSync(file)) continue;
108
+ try {
109
+ const token = readFileSync(file, 'utf8').trim();
110
+ if (token) return { token, source: 'file', file };
111
+ } catch {
112
+ // unreadable → try the next root
97
113
  }
98
114
  }
99
115
  return null;
@@ -112,13 +128,27 @@ export function storeLicenseToken(projectDir: string, token: string): string {
112
128
  export function removeLicense(projectDir: string): boolean {
113
129
  const dir = join(projectDir, LICENSE_DIR_NAME);
114
130
  let removed = false;
115
- for (const name of [LICENSE_FILE_NAME, 'license.json', 'license.lic']) {
131
+ for (const name of [LICENSE_FILE_NAME, 'licence.json', 'licence.lic']) {
116
132
  const p = join(dir, name);
117
133
  if (existsSync(p)) {
118
134
  rmSync(p);
119
135
  removed = true;
120
136
  }
121
137
  }
138
+ // Legacy plain-JSON license folder (pre-`nexus/licence`).
139
+ const legacyDir = join(projectDir, 'license');
140
+ for (const name of [LICENSE_FILE_NAME, 'license.jwt', 'license.json', 'license.lic']) {
141
+ const p = join(legacyDir, name);
142
+ if (existsSync(p)) {
143
+ rmSync(p);
144
+ removed = true;
145
+ }
146
+ }
147
+ try {
148
+ if (existsSync(legacyDir) && readdirSync(legacyDir).length === 0) rmSync(legacyDir);
149
+ } catch {
150
+ // ignore
151
+ }
122
152
  const legacy = join(projectDir, '.nexus-license.json');
123
153
  if (existsSync(legacy)) {
124
154
  rmSync(legacy);