@corelicense/node 0.5.0 → 0.5.5

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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -162
  3. package/dist/client/http-error-utils.d.ts +10 -0
  4. package/dist/client/http-error-utils.d.ts.map +1 -0
  5. package/dist/client/http-error-utils.js +38 -0
  6. package/dist/client/http-error-utils.js.map +1 -0
  7. package/dist/client/license-api-client.d.ts.map +1 -1
  8. package/dist/client/license-api-client.js +26 -6
  9. package/dist/client/license-api-client.js.map +1 -1
  10. package/dist/client/map-client-error.d.ts.map +1 -1
  11. package/dist/client/map-client-error.js +38 -12
  12. package/dist/client/map-client-error.js.map +1 -1
  13. package/dist/client/public-keys-client.d.ts.map +1 -1
  14. package/dist/client/public-keys-client.js +48 -4
  15. package/dist/client/public-keys-client.js.map +1 -1
  16. package/dist/core-license.js +1 -1
  17. package/dist/crypto/key-loader.d.ts +1 -1
  18. package/dist/crypto/key-loader.d.ts.map +1 -1
  19. package/dist/crypto/key-loader.js +34 -9
  20. package/dist/crypto/key-loader.js.map +1 -1
  21. package/dist/errors/api-error.d.ts +1 -0
  22. package/dist/errors/api-error.d.ts.map +1 -1
  23. package/dist/errors/api-error.js +2 -2
  24. package/dist/errors/api-error.js.map +1 -1
  25. package/dist/errors/codes.d.ts +5 -0
  26. package/dist/errors/codes.d.ts.map +1 -1
  27. package/dist/errors/codes.js +5 -0
  28. package/dist/errors/codes.js.map +1 -1
  29. package/dist/errors/license-error.d.ts +2 -0
  30. package/dist/errors/license-error.d.ts.map +1 -1
  31. package/dist/errors/license-error.js +13 -2
  32. package/dist/errors/license-error.js.map +1 -1
  33. package/dist/errors/throw-for-status.d.ts.map +1 -1
  34. package/dist/errors/throw-for-status.js +3 -1
  35. package/dist/errors/throw-for-status.js.map +1 -1
  36. package/dist/index.d.ts +0 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +0 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/types/status.d.ts +1 -1
  41. package/dist/types/status.d.ts.map +1 -1
  42. package/docs/api-reference.md +261 -0
  43. package/docs/installation.md +188 -0
  44. package/docs/integration.md +789 -0
  45. package/package.json +6 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CoreLicense
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,193 +1,83 @@
1
1
  # @corelicense/node
2
2
 
3
- CoreLicense Node.js SDK — runtime license và policy layer cho ứng dụng Node.js.
3
+ Runtime license và policy layer cho ứng dụng Node.js — không chỉ middleware HTTP, mà là lớp kiểm soát **bootstrap**, **module registry**, **DI**, **worker/CLI**, **quota** và **feature gate** theo policy đã ký.
4
4
 
5
- > **Trạng thái:** v0.4 — tích hợp sâu (module registry & DI), production default URL, revoke handling.
5
+ **Phiên bản:** `0.5.1`
6
+ **License:** [MIT](./LICENSE)
7
+ **Repository:** [github.com/trailanglc/corelicense](https://github.com/trailanglc/corelicense)
8
+ **Docs trực tuyến:** [corelicense.net/docs](https://corelicense.net/docs/nodejs-sdk/quick-start)
6
9
 
7
- ## Triết lý
10
+ ---
8
11
 
9
- CoreLicense không chỉ middleware. SDK được thiết kế như một **Runtime Layer** nằm sâu trong bootstrap, runtime config, module registry, service guard, worker/CLI guard và quota enforcement.
12
+ ## CoreLicense là gì?
10
13
 
11
- ## Cài đặt
14
+ CoreLicense gồm **License Server** (API Go) và **Node.js SDK** (package này). Admin cấp license key + policy (features, limits, quotas). SDK:
12
15
 
13
- ```bash
14
- npm install @corelicense/node
15
- ```
16
-
17
- Peer dependencies tùy chọn (chỉ cài khi dùng integration tương ứng):
18
-
19
- ```bash
20
- npm install express # Express middleware
21
- npm install fastify # Fastify plugin
22
- npm install @nestjs/common # NestJS module/guard
23
- ```
24
-
25
- ## Quick start
26
-
27
- Chỉ cần **2 biến môi trường**. SDK mặc định kết nối `https://api.corelicense.net` — không cần cấu hình thêm cho production.
28
-
29
- License key mới (CL1.*) vẫn ưu tiên URL nhúng trong key nếu server generate với `SDK_PUBLIC_URL` khác.
30
-
31
- ```env
32
- CORELICENSE_KEY=CL1.xxxxx.CL-AAAAA-BBBBB-...
33
- CORELICENSE_PRODUCT_ID=demo
34
- ```
35
-
36
- `CORELICENSE_PUBLIC_KEY` **optional** — pin public key (enterprise / air-gapped).
37
- `CORELICENSE_API_URL` **optional** — override thủ công (dev local hoặc legacy key).
38
-
39
- Dev local với server Go chạy localhost:
40
-
41
- ```env
42
- CORELICENSE_API_URL=http://localhost:8080
43
- ```
44
-
45
- ```ts
46
- import express from 'express';
47
- import { CoreLicense, loadOptionsFromEnv } from '@corelicense/node';
16
+ 1. Kích hoạt / kiểm tra license với server
17
+ 2. Verify **policy token** bằng public key (EdDSA)
18
+ 3. Cache policy local, refresh định kỳ
19
+ 4. Chặn runtime khi revoke / suspend / expired
20
+ 5. (Tùy chọn) Chỉ bind module & service được cấp trong policy
48
21
 
49
- const app = express();
22
+ ---
50
23
 
51
- const license = new CoreLicense(loadOptionsFromEnv({
52
- cachePath: './storage/corelicense',
53
- instance: {
54
- strategy: 'install-id-domain-hash',
55
- domain: process.env.APP_DOMAIN,
56
- },
57
- }));
24
+ ## Tài liệu
58
25
 
59
- async function main() {
60
- await license.boot();
26
+ | Tài liệu | Nội dung |
27
+ |----------|----------|
28
+ | [Cài đặt](./docs/installation.md) | `npm install`, biến môi trường, boot lần đầu, dev/prod |
29
+ | [Tích hợp](./docs/integration.md) | **Hướng dẫn chi tiết** — Express, Fastify, NestJS, registry/DI, worker, quota, production |
30
+ | [API Reference](./docs/api-reference.md) | Public API, options, error codes |
31
+ | [LICENSE](./LICENSE) | MIT |
61
32
 
62
- app.use(license.express({
63
- allowPaths: ['/health', '/license/status'],
64
- }));
33
+ > Dành cho agent / CI: đọc [docs/integration.md](./docs/integration.md) trước khi generate code tích hợp.
65
34
 
66
- app.get('/license/status', async (_req, res) => {
67
- res.json(await license.getStatus());
68
- });
35
+ ---
69
36
 
70
- app.listen(3000);
71
- }
37
+ ## Quick links
72
38
 
73
- main().catch(console.error);
39
+ ```bash
40
+ npm install @corelicense/node
74
41
  ```
75
42
 
76
- ### Pin public key (optional)
43
+ Production mặc định: `https://api.corelicense.net` — chỉ cần `CORELICENSE_KEY` + `CORELICENSE_PRODUCT_ID`.
77
44
 
78
45
  ```ts
79
- const license = new CoreLicense(loadOptionsFromEnv({
80
- publicKey: process.env.CORELICENSE_PUBLIC_KEY, // pinned mode
81
- }));
82
- ```
83
-
84
- Khi pin, SDK không fetch keys từ server. Nếu verify fail sau key rotation, cập nhật `CORELICENSE_PUBLIC_KEY` thủ công.
85
-
86
- ## Public API
87
-
88
- | Method | Mô tả |
89
- |--------|-------|
90
- | `boot()` | Khởi tạo SDK, fetch/cache public keys, load cache, verify token |
91
- | `refresh()` | Refresh policy token từ server |
92
- | `requireActive()` | Yêu cầu license active |
93
- | `requireFeature(name)` | Kiểm tra feature flag |
94
- | `checkLimit(name, value)` | Kiểm tra limit |
95
- | `consumeQuota(name, amount?)` | Tiêu thụ quota local |
96
- | `getStatus()` | Trạng thái license hiện tại |
97
- | `getPolicy()` | Policy đang active |
98
- | `getRuntimeConfig()` | Runtime config cho app bootstrap |
99
- | `express(options?)` | Express middleware |
100
- | `fastifyPlugin(options?)` | Fastify plugin |
101
- | `guardWorker(name)` | Guard worker |
102
- | `guardCommand(name)` | Guard CLI/cron command |
103
- | `createLicensedRegistry()` | Module registry gắn license |
104
- | `createLicensedContainer()` | DI container gắn license |
105
- | `bootstrap(options?)` | Bootstrap modules + services theo policy |
106
-
107
- ## Tích hợp sâu (v0.4)
46
+ import { CoreLicense, loadOptionsFromEnv } from '@corelicense/node';
108
47
 
109
- ```ts
48
+ const license = new CoreLicense(loadOptionsFromEnv({ cachePath: './storage/corelicense' }));
110
49
  await license.boot();
111
-
112
- const { modules, services, skipped } = await license.bootstrap({
113
- modules: [
114
- { name: 'automation', feature: 'automation', factory: ({ license }) => new AutomationModule(license) },
115
- ],
116
- services: [
117
- { name: 'ExportService', feature: 'export', factory: ({ license }) => new ExportService(license) },
118
- ],
119
- });
120
-
121
- const automation = await modules.get<AutomationModule>('automation');
122
- const exportService = await services.resolve<ExportService>('ExportService');
123
-
124
- import { registerLicensedRoutes } from '@corelicense/node';
125
- registerLicensedRoutes(app, modules, [
126
- { path: '/automation', module: 'automation', router: automationRouter },
127
- ]);
128
- ```
129
-
130
- `get()` và `resolve()` gọi `requireFeature` mỗi lần truy cập — revoke/suspend chặn ngay. Đổi feature trong policy vẫn cần **restart** để bind/unbind module mới.
131
-
132
- Xem [Tích hợp sâu](https://corelicense.net/docs/nodejs-sdk/deep-integration) trên docs site.
133
-
134
- ## Xử lý lỗi
135
-
136
- Mọi lỗi SDK đều có `code` (mã định vị) và `source` (module SDK phát sinh):
137
-
138
- ```ts
139
- import { isLicenseError, formatLicenseError, LicenseErrorCode } from '@corelicense/node';
140
-
141
- try {
142
- await license.boot();
143
- } catch (error) {
144
- if (isLicenseError(error)) {
145
- console.error(error.code, error.source); // e.g. CONFIG_MISSING_ENV, load-options-from-env
146
- }
147
- }
148
50
  ```
149
51
 
150
- Dùng `error.toJSON()` hoặc `formatLicenseError(error)` khi trả lỗi qua HTTP API.
151
-
152
- ## Production deploy
52
+ Chi tiết: [docs/installation.md](./docs/installation.md) [docs/integration.md](./docs/integration.md).
153
53
 
154
- **SDK:** `cd SDK/corelicense-node && npm test && npm run build && npm publish --access public`
54
+ ---
155
55
 
156
- **API (Go):** deploy backend với `SDK_PUBLIC_URL=https://api.corelicense.net`, policy signer configured, và `APP_ENV=production`. Production validate yêu cầu HTTPS URL (không localhost) và secrets không dùng `change-me`.
56
+ ## Subpath exports
157
57
 
158
- ## Cấu trúc module
159
-
160
- ```txt
161
- src/
162
- core-license.ts # Class chính
163
- types/ # TypeScript interfaces
164
- errors/ # Error hierarchy
165
- crypto/ # Policy token verification + public key resolver
166
- client/ # License API + public keys client
167
- cache/ # File/memory cache
168
- instance/ # Instance ID resolution
169
- guards/ # Express, Fastify, NestJS, worker, CLI
170
- runtime/ # Runtime config, module registry, DI container
171
- audit/ # Local audit log
172
- events/ # Event bus
173
- ```
58
+ | Import | Mục đích |
59
+ |--------|----------|
60
+ | `@corelicense/node` | Core API |
61
+ | `@corelicense/node/express` | Express middleware |
62
+ | `@corelicense/node/fastify` | Fastify plugin |
63
+ | `@corelicense/node/nestjs` | NestJS module, guards, filter |
174
64
 
175
- ## Tài liệu thiết kế
65
+ ---
176
66
 
177
- Xem [CoreLicense Node.js SDK — Tài liệu thiết kế & tích hợp sâu](../../docs/CoreLicense_NodeJS_SDK_Deep_Integration.md).
67
+ ## Yêu cầu
178
68
 
179
- ## Roadmap
69
+ - Node.js **≥ 18**
70
+ - License Server reachable (hoặc cache + offline grace hợp lệ)
180
71
 
181
- - **v0.1** — Types, errors, boot, express middleware, file cache, local audit
182
- - **v0.2** — Auto-fetch public keys, key rotation retry, worker/CLI guard, offline grace
183
- - **v0.3** — Production default URL, activate flow, revoke handling, server contract sync
184
- - **v0.4** — Licensed module registry, DI container, Express routes, NestJS `@RequireFeature`
185
- - **v0.4.1** — Runtime re-check license trên `get()` / `resolve()`, fix `ModuleNotLicensedError` status
186
- - **v0.4.2** — Mã lỗi (`code`) + `source` trên mọi `LicenseError`, `LicenseErrorCode`, `formatLicenseError()`
187
- - **v0.4.3** — Re-check registry/DI không ghi audit; middleware 403 trả `source` qua `toClientPayload()`
188
- - **v0.5.0** — Fail-closed offline boot, quota/limit gates, apiUrl pin, Nest 403 filter, sync mutex
189
- - **v1.0** — Stable API, production-ready, security review
72
+ ---
190
73
 
191
- ## License
74
+ ## Changelog (tóm tắt)
192
75
 
193
- MIT
76
+ | Version | Highlights |
77
+ |---------|------------|
78
+ | **0.5.1** | Tách tài liệu (installation, integration, api-reference), sửa repository URL |
79
+ | **0.5.0** | Fail-closed offline boot, quota/limit gates, apiUrl pin, sync mutex, Nest 403 filter |
80
+ | **0.4.x** | Deep integration (registry/DI), error `code` + `source`, runtime re-check |
81
+ | **0.3.x** | Production default URL, activate/revoke sync |
82
+ | **0.2.x** | Auto-fetch public keys, worker/CLI guard |
83
+ | **0.1.x** | Boot, express middleware, file cache |
@@ -0,0 +1,10 @@
1
+ export interface ApiErrorBody {
2
+ code?: string;
3
+ message?: string;
4
+ }
5
+ export declare function parseApiErrorBody(response: Response): Promise<ApiErrorBody | null>;
6
+ export declare function isNetworkUnreachable(error: unknown): boolean;
7
+ /** Retry only when a repeat request may succeed (transient server/network errors). */
8
+ export declare function isRetryableHttpStatus(status: number): boolean;
9
+ export declare function serverMessage(body: ApiErrorBody | null, fallback: string): string;
10
+ //# sourceMappingURL=http-error-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-error-utils.d.ts","sourceRoot":"","sources":["../../src/client/http-error-utils.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAUxF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAsB5D;AAED,sFAAsF;AACtF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjF"}
@@ -0,0 +1,38 @@
1
+ export async function parseApiErrorBody(response) {
2
+ try {
3
+ const data = (await response.json());
4
+ if (data && (typeof data.code === 'string' || typeof data.message === 'string')) {
5
+ return data;
6
+ }
7
+ return null;
8
+ }
9
+ catch {
10
+ return null;
11
+ }
12
+ }
13
+ export function isNetworkUnreachable(error) {
14
+ if (!(error instanceof Error)) {
15
+ return false;
16
+ }
17
+ const code = error.code;
18
+ if (code === 'ECONNREFUSED' || code === 'ENOTFOUND' || code === 'EHOSTUNREACH' || code === 'ENETUNREACH') {
19
+ return true;
20
+ }
21
+ const cause = error.cause;
22
+ if (cause instanceof Error) {
23
+ const causeCode = cause.code;
24
+ return (causeCode === 'ECONNREFUSED' ||
25
+ causeCode === 'ENOTFOUND' ||
26
+ causeCode === 'EHOSTUNREACH' ||
27
+ causeCode === 'ENETUNREACH');
28
+ }
29
+ return false;
30
+ }
31
+ /** Retry only when a repeat request may succeed (transient server/network errors). */
32
+ export function isRetryableHttpStatus(status) {
33
+ return status === 408 || status === 429 || status >= 500;
34
+ }
35
+ export function serverMessage(body, fallback) {
36
+ return body?.message?.trim() || fallback;
37
+ }
38
+ //# sourceMappingURL=http-error-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-error-utils.js","sourceRoot":"","sources":["../../src/client/http-error-utils.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAkB;IACxD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAiB,CAAC;QACrD,IAAI,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,EAAE,CAAC;YAChF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAC;IACnD,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;QACzG,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC1B,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAI,KAA+B,CAAC,IAAI,CAAC;QACxD,OAAO,CACL,SAAS,KAAK,cAAc;YAC5B,SAAS,KAAK,WAAW;YACzB,SAAS,KAAK,cAAc;YAC5B,SAAS,KAAK,aAAa,CAC5B,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAyB,EAAE,QAAgB;IACvE,OAAO,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,QAAQ,CAAC;AAC3C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"license-api-client.d.ts","sourceRoot":"","sources":["../../src/client/license-api-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAK7B,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAIxE,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAIlE,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAI5D,aAAa;YAuBb,IAAI;CA+CnB"}
1
+ {"version":3,"file":"license-api-client.d.ts","sourceRoot":"","sources":["../../src/client/license-api-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAY7B,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAcD,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAIxE,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAIlE,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAI5D,aAAa;YAuBb,IAAI;CA6DnB"}
@@ -1,7 +1,16 @@
1
1
  import { LicenseApiError } from '../errors/api-error.js';
2
2
  import { LicenseErrorCode } from '../errors/codes.js';
3
3
  import { LicenseRateLimitError } from '../errors/license-error.js';
4
+ import { isNetworkUnreachable, isRetryableHttpStatus, parseApiErrorBody, serverMessage, } from './http-error-utils.js';
4
5
  const RETRY_DELAYS_MS = [1000, 3000];
6
+ function mapHttpError(status, statusText, body) {
7
+ const message = serverMessage(body, `License API error: ${status} ${statusText}`);
8
+ return new LicenseApiError(message, {
9
+ code: LicenseErrorCode.LICENSE_API_ERROR,
10
+ httpStatus: status,
11
+ retryable: isRetryableHttpStatus(status),
12
+ });
13
+ }
5
14
  export class LicenseApiClient {
6
15
  options;
7
16
  constructor(options) {
@@ -39,17 +48,17 @@ export class LicenseApiClient {
39
48
  try {
40
49
  const response = await fetch(url, {
41
50
  method: 'POST',
42
- headers: { 'Content-Type': 'application/json' },
51
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
43
52
  body: JSON.stringify(body),
44
53
  signal: controller.signal,
45
54
  });
46
55
  if (response.status === 429) {
47
- throw new LicenseRateLimitError();
56
+ const body = await parseApiErrorBody(response);
57
+ throw new LicenseRateLimitError(serverMessage(body, 'License API rate limit exceeded'));
48
58
  }
49
59
  if (!response.ok) {
50
- throw new LicenseApiError(`License API error: ${response.status} ${response.statusText}`, {
51
- httpStatus: response.status,
52
- });
60
+ const errorBody = await parseApiErrorBody(response);
61
+ throw mapHttpError(response.status, response.statusText, errorBody);
53
62
  }
54
63
  return (await response.json());
55
64
  }
@@ -61,9 +70,20 @@ export class LicenseApiClient {
61
70
  throw new LicenseApiError('License API request timed out', {
62
71
  code: LicenseErrorCode.LICENSE_API_TIMEOUT,
63
72
  cause: error,
73
+ retryable: true,
74
+ });
75
+ }
76
+ if (isNetworkUnreachable(error)) {
77
+ throw new LicenseApiError('License API is unreachable', {
78
+ code: LicenseErrorCode.LICENSE_API_UNREACHABLE,
79
+ cause: error,
80
+ retryable: true,
64
81
  });
65
82
  }
66
- throw new LicenseApiError('License API request failed', { cause: error });
83
+ throw new LicenseApiError('License API request failed', {
84
+ cause: error,
85
+ retryable: true,
86
+ });
67
87
  }
68
88
  finally {
69
89
  clearTimeout(timeout);
@@ -1 +1 @@
1
- {"version":3,"file":"license-api-client.js","sourceRoot":"","sources":["../../src/client/license-api-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"license-api-client.js","sourceRoot":"","sources":["../../src/client/license-api-client.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"map-client-error.d.ts","sourceRoot":"","sources":["../../src/client/map-client-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAEL,YAAY,EAMb,MAAM,4BAA4B,CAAC;AAyBpC,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAUhF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,YAAY,CAgC3E"}
1
+ {"version":3,"file":"map-client-error.d.ts","sourceRoot":"","sources":["../../src/client/map-client-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhE,OAAO,EAEL,YAAY,EAMb,MAAM,4BAA4B,CAAC;AA2BpC,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAUhF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,YAAY,CAuD3E"}
@@ -1,3 +1,4 @@
1
+ import { LicenseErrorCode } from '../errors/codes.js';
1
2
  import { InstanceBlockedError, LicenseExpiredError, LicenseInvalidError, LicenseRevokedError, LicenseSuspendedError, MaxInstancesError, } from '../errors/license-error.js';
2
3
  import { PolicySignatureError } from '../errors/policy-error.js';
3
4
  const TERMINAL_STATUSES = new Set([
@@ -13,6 +14,8 @@ const TERMINAL_CODES = new Set([
13
14
  'LICENSE_SUSPENDED',
14
15
  'LICENSE_EXPIRED',
15
16
  'LICENSE_INVALID',
17
+ 'LICENSE_NOT_FOUND',
18
+ 'PRODUCT_NOT_FOUND',
16
19
  'INVALID_LICENSE',
17
20
  'INVALID_PRODUCT',
18
21
  'INSTANCE_BLOCKED',
@@ -31,33 +34,56 @@ export function isTerminalClientResponse(response) {
31
34
  }
32
35
  export function mapClientError(response) {
33
36
  const message = response.message ?? 'License check failed';
37
+ const source = 'map-client-error';
34
38
  switch (response.code) {
35
39
  case 'LICENSE_REVOKED':
36
- return new LicenseRevokedError(message);
40
+ return new LicenseRevokedError(message, source);
37
41
  case 'LICENSE_SUSPENDED':
38
- return new LicenseSuspendedError(message);
42
+ return new LicenseSuspendedError(message, source);
39
43
  case 'LICENSE_EXPIRED':
40
- return new LicenseExpiredError(message);
44
+ return new LicenseExpiredError(message, source);
45
+ case 'LICENSE_NOT_STARTED':
46
+ return new LicenseInvalidError(message, {
47
+ code: LicenseErrorCode.LICENSE_NOT_STARTED,
48
+ source,
49
+ });
50
+ case 'PRODUCT_NOT_FOUND':
51
+ case 'INVALID_PRODUCT':
52
+ return new LicenseInvalidError(message, {
53
+ code: LicenseErrorCode.PRODUCT_NOT_FOUND,
54
+ source,
55
+ });
56
+ case 'LICENSE_NOT_FOUND':
57
+ return new LicenseInvalidError(message, {
58
+ code: LicenseErrorCode.LICENSE_NOT_FOUND,
59
+ source,
60
+ });
61
+ case 'LICENSE_INVALID':
62
+ case 'INVALID_LICENSE':
63
+ return new LicenseInvalidError(message, {
64
+ code: LicenseErrorCode.LICENSE_INVALID,
65
+ source,
66
+ });
41
67
  case 'INSTANCE_BLOCKED':
42
- return new InstanceBlockedError(message);
68
+ return new InstanceBlockedError(message, source);
43
69
  case 'MAX_INSTANCES':
44
- return new MaxInstancesError(message);
70
+ return new MaxInstancesError(message, source);
45
71
  case 'POLICY_SIGN_FAILED':
46
- return new PolicySignatureError(message, {
47
- source: 'map-client-error',
48
- });
72
+ return new PolicySignatureError(message, { source });
49
73
  default:
50
74
  break;
51
75
  }
52
76
  switch (response.status) {
53
77
  case 'revoked':
54
- return new LicenseRevokedError(message);
78
+ return new LicenseRevokedError(message, source);
55
79
  case 'suspended':
56
- return new LicenseSuspendedError(message);
80
+ return new LicenseSuspendedError(message, source);
57
81
  case 'expired':
58
- return new LicenseExpiredError(message);
82
+ return new LicenseExpiredError(message, source);
83
+ case 'blocked':
84
+ return new InstanceBlockedError(message, source);
59
85
  default:
60
- return new LicenseInvalidError(message);
86
+ return new LicenseInvalidError(message, { source });
61
87
  }
62
88
  }
63
89
  //# sourceMappingURL=map-client-error.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"map-client-error.js","sourceRoot":"","sources":["../../src/client/map-client-error.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EAEpB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,SAAS;IACT,WAAW;IACX,SAAS;IACT,SAAS;IACT,SAAS;IACT,eAAe;CAChB,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,iBAAiB;IACjB,mBAAmB;IACnB,iBAAiB;IACjB,iBAAiB;IACjB,iBAAiB;IACjB,iBAAiB;IACjB,kBAAkB;IAClB,eAAe;IACf,oBAAoB;IACpB,qBAAqB;CACtB,CAAC,CAAC;AAEH,MAAM,UAAU,wBAAwB,CAAC,QAA8B;IACrE,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAA8B;IAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,IAAI,sBAAsB,CAAC;IAE3D,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;QACtB,KAAK,iBAAiB;YACpB,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1C,KAAK,mBAAmB;YACtB,OAAO,IAAI,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAC5C,KAAK,iBAAiB;YACpB,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1C,KAAK,kBAAkB;YACrB,OAAO,IAAI,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAC3C,KAAK,eAAe;YAClB,OAAO,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACxC,KAAK,oBAAoB;YACvB,OAAO,IAAI,oBAAoB,CAAC,OAAO,EAAE;gBACvC,MAAM,EAAE,kBAAkB;aAC3B,CAAC,CAAC;QACL;YACE,MAAM;IACV,CAAC;IAED,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxB,KAAK,SAAS;YACZ,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1C,KAAK,WAAW;YACd,OAAO,IAAI,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAC5C,KAAK,SAAS;YACZ,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1C;YACE,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"map-client-error.js","sourceRoot":"","sources":["../../src/client/map-client-error.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"public-keys-client.d.ts","sourceRoot":"","sources":["../../src/client/public-keys-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAInE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;CA+CxE"}
1
+ {"version":3,"file":"public-keys-client.d.ts","sourceRoot":"","sources":["../../src/client/public-keys-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAWnE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AA2CD,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,uBAAuB;IAEvD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;CA2DxE"}
@@ -1,5 +1,38 @@
1
1
  import { PublicKeysError } from '../errors/api-error.js';
2
2
  import { LicenseErrorCode } from '../errors/codes.js';
3
+ import { isNetworkUnreachable, isRetryableHttpStatus, parseApiErrorBody, serverMessage, } from './http-error-utils.js';
4
+ function mapHttpError(status, statusText, body) {
5
+ const message = serverMessage(body, `Public keys API error: ${status} ${statusText}`);
6
+ const retryable = isRetryableHttpStatus(status);
7
+ if (status === 429 || body?.code === 'RATE_LIMITED') {
8
+ return new PublicKeysError(message, {
9
+ code: LicenseErrorCode.RATE_LIMITED,
10
+ httpStatus: status,
11
+ retryable: true,
12
+ });
13
+ }
14
+ if (status === 404) {
15
+ if (body?.code === 'PRODUCT_NOT_FOUND') {
16
+ return new PublicKeysError(message, {
17
+ code: LicenseErrorCode.PRODUCT_NOT_FOUND,
18
+ httpStatus: status,
19
+ retryable: false,
20
+ });
21
+ }
22
+ if (body?.code === 'PUBLIC_KEYS_NOT_PUBLISHED' || body?.code === 'NOT_FOUND') {
23
+ return new PublicKeysError(message, {
24
+ code: LicenseErrorCode.PUBLIC_KEYS_NOT_PUBLISHED,
25
+ httpStatus: status,
26
+ retryable: false,
27
+ });
28
+ }
29
+ }
30
+ return new PublicKeysError(message, {
31
+ code: LicenseErrorCode.PUBLIC_KEYS_FETCH_FAILED,
32
+ httpStatus: status,
33
+ retryable,
34
+ });
35
+ }
3
36
  export class PublicKeysClient {
4
37
  options;
5
38
  constructor(options) {
@@ -17,14 +50,14 @@ export class PublicKeysClient {
17
50
  signal: controller.signal,
18
51
  });
19
52
  if (!response.ok) {
20
- throw new PublicKeysError(`Public keys API error: ${response.status} ${response.statusText}`, {
21
- httpStatus: response.status,
22
- });
53
+ const body = await parseApiErrorBody(response);
54
+ throw mapHttpError(response.status, response.statusText, body);
23
55
  }
24
56
  const data = (await response.json());
25
57
  if (!data.keys?.length) {
26
58
  throw new PublicKeysError('Public keys API returned no keys', {
27
59
  code: LicenseErrorCode.PUBLIC_KEYS_EMPTY,
60
+ retryable: false,
28
61
  });
29
62
  }
30
63
  return data;
@@ -37,9 +70,20 @@ export class PublicKeysClient {
37
70
  throw new PublicKeysError('Public keys API request timed out', {
38
71
  code: LicenseErrorCode.LICENSE_API_TIMEOUT,
39
72
  cause: error,
73
+ retryable: true,
74
+ });
75
+ }
76
+ if (isNetworkUnreachable(error)) {
77
+ throw new PublicKeysError('Public keys API is unreachable', {
78
+ code: LicenseErrorCode.LICENSE_API_UNREACHABLE,
79
+ cause: error,
80
+ retryable: true,
40
81
  });
41
82
  }
42
- throw new PublicKeysError('Public keys API request failed', { cause: error });
83
+ throw new PublicKeysError('Public keys API request failed', {
84
+ cause: error,
85
+ retryable: true,
86
+ });
43
87
  }
44
88
  finally {
45
89
  clearTimeout(timeout);
@@ -1 +1 @@
1
- {"version":3,"file":"public-keys-client.js","sourceRoot":"","sources":["../../src/client/public-keys-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAOtD,MAAM,OAAO,gBAAgB;IACE;IAA7B,YAA6B,OAAgC;QAAhC,YAAO,GAAP,OAAO,CAAyB;IAAG,CAAC;IAEjE,KAAK,CAAC,gBAAgB,CAAC,SAAiB;QACtC,MAAM,IAAI,GAAG,uBAAuB,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC;QACzE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,CACxB,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EACxB,IAAI,CAAC,OAAO,CAAC,gBAAgB,IAAI,IAAI,CACtC,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;gBACvC,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,eAAe,CAAC,0BAA0B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,EAAE;oBAC5F,UAAU,EAAE,QAAQ,CAAC,MAAM;iBAC5B,CAAC,CAAC;YACL,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;YAC5D,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;gBACvB,MAAM,IAAI,eAAe,CAAC,kCAAkC,EAAE;oBAC5D,IAAI,EAAE,gBAAgB,CAAC,iBAAiB;iBACzC,CAAC,CAAC;YACL,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;gBACrC,MAAM,KAAK,CAAC;YACd,CAAC;YAED,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC1D,MAAM,IAAI,eAAe,CAAC,mCAAmC,EAAE;oBAC7D,IAAI,EAAE,gBAAgB,CAAC,mBAAmB;oBAC1C,KAAK,EAAE,KAAK;iBACb,CAAC,CAAC;YACL,CAAC;YAED,MAAM,IAAI,eAAe,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAChF,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"public-keys-client.js","sourceRoot":"","sources":["../../src/client/public-keys-client.ts"],"names":[],"mappings":"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"}
@@ -25,7 +25,7 @@ import { PolicySignatureError } from './errors/policy-error.js';
25
25
  import { throwForTerminalStatus } from './errors/throw-for-status.js';
26
26
  import { resolveApiUrl, parseEmbeddedApiUrl } from './env/resolve-api-url.js';
27
27
  const SDK_NAME = '@corelicense/node';
28
- const SDK_VERSION = '0.5.0';
28
+ const SDK_VERSION = '0.5.1';
29
29
  const DEFAULT_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
30
30
  function hashPolicyToken(token) {
31
31
  return createHash('sha256').update(token).digest('hex');
@@ -1,4 +1,4 @@
1
- export type KeyFormat = 'spki' | 'pkcs8';
1
+ export type KeyFormat = 'spki' | 'pkcs8' | 'raw';
2
2
  export declare function loadPublicKey(keyMaterial: string, algorithm?: 'Ed25519' | 'RS256' | 'ES256'): Promise<CryptoKey>;
3
3
  export declare function loadPrivateKey(keyMaterial: string, algorithm?: 'Ed25519' | 'RS256' | 'ES256'): Promise<CryptoKey>;
4
4
  //# sourceMappingURL=key-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"key-loader.d.ts","sourceRoot":"","sources":["../../src/crypto/key-loader.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,SAAS,GAAG,OAAO,GAAG,OAAmB,GACnD,OAAO,CAAC,SAAS,CAAC,CAGpB;AAED,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,SAAS,GAAG,OAAO,GAAG,OAAmB,GACnD,OAAO,CAAC,SAAS,CAAC,CAGpB"}
1
+ {"version":3,"file":"key-loader.d.ts","sourceRoot":"","sources":["../../src/crypto/key-loader.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAIjD,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,SAAS,GAAG,OAAO,GAAG,OAAmB,GACnD,OAAO,CAAC,SAAS,CAAC,CAcpB;AAED,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,SAAS,GAAG,OAAO,GAAG,OAAmB,GACnD,OAAO,CAAC,SAAS,CAAC,CAQpB"}