cwe-api-client 0.0.1 → 1.0.0

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/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # cwe-api-client
2
+
3
+ [![CI](https://github.com/ElJijuna/cwe-api-client/actions/workflows/ci.yml/badge.svg)](https://github.com/ElJijuna/cwe-api-client/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/cwe-api-client)](https://www.npmjs.com/package/cwe-api-client)
5
+ [![Bundle size](https://img.shields.io/bundlephobia/minzip/cwe-api-client)](https://bundlephobia.com/package/cwe-api-client)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
8
+ [![Node.js](https://img.shields.io/node/v/cwe-api-client)](https://nodejs.org/)
9
+
10
+ TypeScript client for the [MITRE CWE REST API](https://cwe-api.mitre.org/api/v1).
11
+ Works in **Node.js** and the **browser** (isomorphic). Fully typed, zero runtime dependencies.
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install cwe-api-client
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Quick start
24
+
25
+ ```typescript
26
+ import { CweClient } from 'cwe-api-client';
27
+
28
+ const cwe = new CweClient();
29
+
30
+ // Custom API base URL (e.g. a mirror)
31
+ const cwe = new CweClient({ baseUrl: 'https://my-mirror.example.com/api/v1' });
32
+ ```
33
+
34
+ ---
35
+
36
+ ## API reference
37
+
38
+ ### Version metadata
39
+
40
+ ```typescript
41
+ const version = await cwe.version();
42
+
43
+ console.log(version.ContentVersion); // '4.19.1'
44
+ console.log(version.ContentDate); // '2026-01-21'
45
+ console.log(version.TotalWeaknesses); // 969
46
+ console.log(version.TotalCategories); // 420
47
+ console.log(version.TotalViews); // 58
48
+ ```
49
+
50
+ ### Multi-ID lookup
51
+
52
+ Look up multiple CWE entries by their numeric IDs in a single request:
53
+
54
+ ```typescript
55
+ const entries = await cwe.lookup([74, 79]);
56
+
57
+ entries.forEach(e => console.log(`CWE-${e.ID} (${e.Type})`));
58
+ // CWE-74 (class_weakness)
59
+ // CWE-79 (base_weakness)
60
+ ```
61
+
62
+ ### Weakness details
63
+
64
+ ```typescript
65
+ // Await directly
66
+ const weakness = await cwe.weakness(79);
67
+
68
+ // Or call .get() explicitly
69
+ const weakness = await cwe.weakness(79).get();
70
+
71
+ console.log(weakness.Name); // 'Improper Neutralization of Input...'
72
+ console.log(weakness.Abstraction); // 'Base'
73
+ console.log(weakness.Status); // 'Stable'
74
+ console.log(weakness.LikelihoodOfExploit); // 'High'
75
+ console.log(weakness.Description);
76
+ ```
77
+
78
+ ### Category details
79
+
80
+ ```typescript
81
+ const category = await cwe.category(189);
82
+
83
+ console.log(category.Name); // 'Numeric Errors'
84
+ console.log(category.Status); // 'Draft'
85
+ console.log(category.Summary);
86
+ console.log(category.Relationships?.length); // number of member weaknesses
87
+ ```
88
+
89
+ ### View details
90
+
91
+ ```typescript
92
+ const view = await cwe.view(1425);
93
+
94
+ console.log(view.Name); // 'Weaknesses in the 2023 CWE Top 25...'
95
+ console.log(view.Type); // 'Graph'
96
+ console.log(view.Status); // 'Draft'
97
+ console.log(view.Members?.length); // 25
98
+ ```
99
+
100
+ ### Hierarchy navigation
101
+
102
+ All hierarchy methods accept an optional `viewId` to scope the results to a specific CWE view (e.g. `1000` for Research Concepts):
103
+
104
+ ```typescript
105
+ // Direct parents
106
+ const parents = await cwe.weakness(74).parents(1000);
107
+ parents.forEach(p => console.log(`CWE-${p.ID}`, p.Primary_Parent ? '[primary]' : ''));
108
+
109
+ // Direct children
110
+ const children = await cwe.weakness(74).children(1000);
111
+ children.forEach(c => console.log(`CWE-${c.ID} (${c.Type})`));
112
+
113
+ // Full ancestor tree (recursive, up to the view root)
114
+ const ancestors = await cwe.weakness(74).ancestors(1000);
115
+ // ancestors[0].Data → this node
116
+ // ancestors[0].Parents → parent nodes (recursive)
117
+
118
+ // Full descendant tree (recursive, down to leaf entries)
119
+ const descendants = await cwe.weakness(74).descendants(1000);
120
+ // descendants[0].Data → this node
121
+ // descendants[0].Children → child nodes (recursive)
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Chainable resource pattern
127
+
128
+ Every resource implements `PromiseLike`, so it can be **awaited directly** or **chained** to sub-methods:
129
+
130
+ ```typescript
131
+ // Await directly → fetches the full weakness
132
+ const weakness = await cwe.weakness(79);
133
+
134
+ // Chain → fetches hierarchy
135
+ const parents = await cwe.weakness(74).parents(1000);
136
+ const descendants = await cwe.weakness(74).descendants(1000);
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Request events
142
+
143
+ Subscribe to every HTTP request for logging, monitoring, or debugging:
144
+
145
+ ```typescript
146
+ cwe.on('request', (event) => {
147
+ console.log(`[${event.method}] ${event.url} → ${event.statusCode} (${event.durationMs}ms)`);
148
+ if (event.error) {
149
+ console.error('Request failed:', event.error.message);
150
+ }
151
+ });
152
+ ```
153
+
154
+ The `event` object contains:
155
+
156
+ | Field | Type | Description |
157
+ |---|---|---|
158
+ | `url` | `string` | Full URL that was requested |
159
+ | `method` | `'GET'` | HTTP method used |
160
+ | `startedAt` | `Date` | When the request started |
161
+ | `finishedAt` | `Date` | When the request finished |
162
+ | `durationMs` | `number` | Duration in milliseconds |
163
+ | `statusCode` | `number \| undefined` | HTTP status code, if a response was received |
164
+ | `error` | `Error \| undefined` | Present only if the request failed |
165
+
166
+ Multiple listeners can be registered. The event is always emitted after the request completes, whether it succeeded or failed.
167
+
168
+ ---
169
+
170
+ ## Error handling
171
+
172
+ Non-2xx responses throw a `CweApiError` with the HTTP status code and status text:
173
+
174
+ ```typescript
175
+ import { CweApiError } from 'cwe-api-client';
176
+
177
+ try {
178
+ await cwe.weakness(99999).get();
179
+ } catch (err) {
180
+ if (err instanceof CweApiError) {
181
+ console.log(err.status); // 404
182
+ console.log(err.statusText); // 'Not Found'
183
+ console.log(err.message); // 'CWE API error: 404 Not Found'
184
+ console.log(err.stack); // full stack trace
185
+ }
186
+ }
187
+ ```
188
+
189
+ ---
190
+
191
+ ## TypeScript types
192
+
193
+ All domain types are exported:
194
+
195
+ ```typescript
196
+ import type {
197
+ // Client
198
+ CweClientOptions, RequestEvent, CweClientEvents,
199
+ // Version
200
+ CweVersion,
201
+ // Multi-ID lookup
202
+ CweEntry, CweEntryType,
203
+ // Weakness
204
+ CweWeakness, CweRelatedWeakness, CweWeaknessOrdinality,
205
+ CweApplicablePlatform, CweAlternateTerm,
206
+ // Category
207
+ CweCategory, CweTaxonomyMapping, CweCategoryRelationship, CweCategoryReference,
208
+ // View
209
+ CweView, CweViewAudience, CweViewMember,
210
+ // Relations / hierarchy
211
+ CweRelationEntry, CweAncestorNode, CweDescendantNode,
212
+ } from 'cwe-api-client';
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Contributing
218
+
219
+ See [CONTRIBUTING.md](.github/CONTRIBUTING.md).
220
+
221
+ ---
222
+
223
+ ## License
224
+
225
+ [MIT](LICENSE)
package/dist/index.cjs ADDED
@@ -0,0 +1,382 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CategoryResource: () => CategoryResource,
24
+ CweApiError: () => CweApiError,
25
+ CweClient: () => CweClient,
26
+ ViewResource: () => ViewResource,
27
+ WeaknessResource: () => WeaknessResource
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/errors/CweApiError.ts
32
+ var CweApiError = class extends Error {
33
+ constructor(status, statusText) {
34
+ super(`CWE API error: ${status} ${statusText}`);
35
+ this.name = "CweApiError";
36
+ this.status = status;
37
+ this.statusText = statusText;
38
+ }
39
+ };
40
+
41
+ // src/resources/WeaknessResource.ts
42
+ var WeaknessResource = class {
43
+ /** @internal */
44
+ constructor(request, id) {
45
+ this.request = request;
46
+ this.id = id;
47
+ }
48
+ /**
49
+ * Allows the resource to be awaited directly, resolving with the full weakness.
50
+ * Delegates to {@link WeaknessResource.get}.
51
+ */
52
+ then(onfulfilled, onrejected) {
53
+ return this.get().then(onfulfilled, onrejected);
54
+ }
55
+ /**
56
+ * Fetches the full weakness entry.
57
+ *
58
+ * `GET /cwe/weakness/{id}`
59
+ *
60
+ * @returns The weakness object
61
+ */
62
+ async get() {
63
+ const data = await this.request(`/cwe/weakness/${this.id}`);
64
+ return data.Weaknesses[0];
65
+ }
66
+ /**
67
+ * Fetches the direct parents of this weakness in a given view.
68
+ *
69
+ * `GET /cwe/{id}/parents?view={viewId}`
70
+ *
71
+ * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)
72
+ * @returns Array of direct parent entries
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const parents = await cwe.weakness(74).parents(1000);
77
+ * ```
78
+ */
79
+ async parents(viewId) {
80
+ return this.request(
81
+ `/cwe/${this.id}/parents`,
82
+ viewId !== void 0 ? { view: viewId } : void 0
83
+ );
84
+ }
85
+ /**
86
+ * Fetches the direct children of this weakness in a given view.
87
+ *
88
+ * `GET /cwe/{id}/children?view={viewId}`
89
+ *
90
+ * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)
91
+ * @returns Array of direct child entries
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * const children = await cwe.weakness(74).children(1000);
96
+ * ```
97
+ */
98
+ async children(viewId) {
99
+ return this.request(
100
+ `/cwe/${this.id}/children`,
101
+ viewId !== void 0 ? { view: viewId } : void 0
102
+ );
103
+ }
104
+ /**
105
+ * Fetches the full ancestor tree of this weakness in a given view.
106
+ *
107
+ * `GET /cwe/{id}/ancestors?view={viewId}`
108
+ *
109
+ * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)
110
+ * @returns Recursive ancestor tree from this node up to the view root
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * const tree = await cwe.weakness(74).ancestors(1000);
115
+ * ```
116
+ */
117
+ async ancestors(viewId) {
118
+ return this.request(
119
+ `/cwe/${this.id}/ancestors`,
120
+ viewId !== void 0 ? { view: viewId } : void 0
121
+ );
122
+ }
123
+ /**
124
+ * Fetches the full descendant tree of this weakness in a given view.
125
+ *
126
+ * `GET /cwe/{id}/descendants?view={viewId}`
127
+ *
128
+ * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)
129
+ * @returns Recursive descendant tree from this node down to leaf entries
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const tree = await cwe.weakness(74).descendants(1000);
134
+ * ```
135
+ */
136
+ async descendants(viewId) {
137
+ return this.request(
138
+ `/cwe/${this.id}/descendants`,
139
+ viewId !== void 0 ? { view: viewId } : void 0
140
+ );
141
+ }
142
+ };
143
+
144
+ // src/resources/CategoryResource.ts
145
+ var CategoryResource = class {
146
+ /** @internal */
147
+ constructor(request, id) {
148
+ this.request = request;
149
+ this.id = id;
150
+ }
151
+ /**
152
+ * Allows the resource to be awaited directly, resolving with the full category.
153
+ * Delegates to {@link CategoryResource.get}.
154
+ */
155
+ then(onfulfilled, onrejected) {
156
+ return this.get().then(onfulfilled, onrejected);
157
+ }
158
+ /**
159
+ * Fetches the full category entry.
160
+ *
161
+ * `GET /cwe/category/{id}`
162
+ *
163
+ * @returns The category object
164
+ */
165
+ async get() {
166
+ const data = await this.request(`/cwe/category/${this.id}`);
167
+ return data.Categories[0];
168
+ }
169
+ };
170
+
171
+ // src/resources/ViewResource.ts
172
+ var ViewResource = class {
173
+ /** @internal */
174
+ constructor(request, id) {
175
+ this.request = request;
176
+ this.id = id;
177
+ }
178
+ /**
179
+ * Allows the resource to be awaited directly, resolving with the full view.
180
+ * Delegates to {@link ViewResource.get}.
181
+ */
182
+ then(onfulfilled, onrejected) {
183
+ return this.get().then(onfulfilled, onrejected);
184
+ }
185
+ /**
186
+ * Fetches the full view entry.
187
+ *
188
+ * `GET /cwe/view/{id}`
189
+ *
190
+ * @returns The view object
191
+ */
192
+ async get() {
193
+ const data = await this.request(`/cwe/view/${this.id}`);
194
+ return data.Views[0];
195
+ }
196
+ };
197
+
198
+ // src/CweClient.ts
199
+ var DEFAULT_BASE_URL = "https://cwe-api.mitre.org/api/v1";
200
+ var CweClient = class {
201
+ /**
202
+ * @param options - Optional configuration for the API base URL
203
+ */
204
+ constructor(options = {}) {
205
+ this.listeners = /* @__PURE__ */ new Map();
206
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
207
+ }
208
+ /**
209
+ * Subscribes to a client event.
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * cwe.on('request', (event) => {
214
+ * console.log(`${event.method} ${event.url} — ${event.durationMs}ms`);
215
+ * if (event.error) console.error('Request failed:', event.error);
216
+ * });
217
+ * ```
218
+ */
219
+ on(event, callback) {
220
+ const callbacks = this.listeners.get(event) ?? [];
221
+ callbacks.push(callback);
222
+ this.listeners.set(event, callbacks);
223
+ return this;
224
+ }
225
+ emit(event, payload) {
226
+ const callbacks = this.listeners.get(event) ?? [];
227
+ for (const cb of callbacks) {
228
+ cb(payload);
229
+ }
230
+ }
231
+ /**
232
+ * Performs a GET request to the CWE API.
233
+ *
234
+ * @param path - Path to append to the base URL (e.g. `/cwe/version`)
235
+ * @param params - Optional query parameters
236
+ * @internal
237
+ */
238
+ async request(path, params) {
239
+ const url = buildUrl(`${this.baseUrl}${path}`, params);
240
+ const startedAt = /* @__PURE__ */ new Date();
241
+ let statusCode;
242
+ try {
243
+ const response = await fetch(url, {
244
+ headers: { Accept: "application/json" }
245
+ });
246
+ statusCode = response.status;
247
+ if (!response.ok) {
248
+ throw new CweApiError(response.status, response.statusText);
249
+ }
250
+ const data = await response.json();
251
+ this.emit("request", {
252
+ url,
253
+ method: "GET",
254
+ startedAt,
255
+ finishedAt: /* @__PURE__ */ new Date(),
256
+ durationMs: Date.now() - startedAt.getTime(),
257
+ statusCode
258
+ });
259
+ return data;
260
+ } catch (err) {
261
+ const finishedAt = /* @__PURE__ */ new Date();
262
+ this.emit("request", {
263
+ url,
264
+ method: "GET",
265
+ startedAt,
266
+ finishedAt,
267
+ durationMs: finishedAt.getTime() - startedAt.getTime(),
268
+ statusCode,
269
+ error: err instanceof Error ? err : new Error(String(err))
270
+ });
271
+ throw err;
272
+ }
273
+ }
274
+ /**
275
+ * Fetches CWE content version metadata.
276
+ *
277
+ * `GET /cwe/version`
278
+ *
279
+ * @returns Version metadata including content version, date, and counts
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * const v = await cwe.version();
284
+ * console.log(v.ContentVersion); // '4.19.1'
285
+ * console.log(v.TotalWeaknesses); // 969
286
+ * ```
287
+ */
288
+ async version() {
289
+ return this.request("/cwe/version");
290
+ }
291
+ /**
292
+ * Looks up multiple CWE entries by their numeric IDs in a single request.
293
+ *
294
+ * `GET /cwe/{ids}` (comma-separated)
295
+ *
296
+ * @param ids - Array of CWE numeric IDs (e.g. `[74, 79]`)
297
+ * @returns Array of lightweight CWE entries with type and ID
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * const entries = await cwe.lookup([74, 79]);
302
+ * entries.forEach(e => console.log(e.ID, e.Type));
303
+ * ```
304
+ */
305
+ async lookup(ids) {
306
+ return this.request(`/cwe/${ids.join(",")}`);
307
+ }
308
+ /**
309
+ * Returns a {@link WeaknessResource} for a given CWE weakness ID, providing
310
+ * access to weakness details and its hierarchy (parents, children, ancestors,
311
+ * descendants).
312
+ *
313
+ * The returned resource can be awaited directly to fetch the full weakness,
314
+ * or chained to access hierarchy methods.
315
+ *
316
+ * @param id - The CWE weakness numeric ID (e.g. `79`)
317
+ * @returns A chainable weakness resource
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * const weakness = await cwe.weakness(79);
322
+ * const parents = await cwe.weakness(74).parents(1000);
323
+ * const tree = await cwe.weakness(74).descendants(1000);
324
+ * ```
325
+ */
326
+ weakness(id) {
327
+ return new WeaknessResource(
328
+ (path, params) => this.request(path, params),
329
+ id
330
+ );
331
+ }
332
+ /**
333
+ * Returns a {@link CategoryResource} for a given CWE category ID, providing
334
+ * access to category details.
335
+ *
336
+ * The returned resource can be awaited directly to fetch the full category.
337
+ *
338
+ * @param id - The CWE category numeric ID (e.g. `189`)
339
+ * @returns A chainable category resource
340
+ *
341
+ * @example
342
+ * ```typescript
343
+ * const category = await cwe.category(189);
344
+ * console.log(category.Name); // 'Numeric Errors'
345
+ * ```
346
+ */
347
+ category(id) {
348
+ return new CategoryResource(
349
+ (path, params) => this.request(path, params),
350
+ id
351
+ );
352
+ }
353
+ /**
354
+ * Returns a {@link ViewResource} for a given CWE view ID, providing access
355
+ * to view details.
356
+ *
357
+ * The returned resource can be awaited directly to fetch the full view.
358
+ *
359
+ * @param id - The CWE view numeric ID (e.g. `1425`)
360
+ * @returns A chainable view resource
361
+ *
362
+ * @example
363
+ * ```typescript
364
+ * const view = await cwe.view(1425);
365
+ * console.log(view.Name); // 'Weaknesses in the 2023 CWE Top 25...'
366
+ * ```
367
+ */
368
+ view(id) {
369
+ return new ViewResource(
370
+ (path, params) => this.request(path, params),
371
+ id
372
+ );
373
+ }
374
+ };
375
+ function buildUrl(base, params) {
376
+ if (!params) return base;
377
+ const entries = Object.entries(params).filter(([, v]) => v !== void 0);
378
+ if (entries.length === 0) return base;
379
+ const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));
380
+ return `${base}?${search.toString()}`;
381
+ }
382
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors/CweApiError.ts","../src/resources/WeaknessResource.ts","../src/resources/CategoryResource.ts","../src/resources/ViewResource.ts","../src/CweClient.ts"],"sourcesContent":["export { CweClient } from './CweClient';\nexport { CweApiError } from './errors/CweApiError';\nexport type { CweClientOptions, RequestEvent, CweClientEvents } from './CweClient';\nexport { WeaknessResource } from './resources/WeaknessResource';\nexport { CategoryResource } from './resources/CategoryResource';\nexport { ViewResource } from './resources/ViewResource';\nexport type { CweVersion } from './domain/Version';\nexport type { CweEntry, CweEntryType } from './domain/CweEntry';\nexport type {\n CweWeakness,\n CweRelatedWeakness,\n CweWeaknessOrdinality,\n CweApplicablePlatform,\n CweAlternateTerm,\n} from './domain/Weakness';\nexport type {\n CweCategory,\n CweTaxonomyMapping,\n CweCategoryRelationship,\n CweCategoryReference,\n} from './domain/Category';\nexport type {\n CweView,\n CweViewAudience,\n CweViewMember,\n} from './domain/View';\nexport type {\n CweRelationEntry,\n CweAncestorNode,\n CweDescendantNode,\n} from './domain/Relations';\n","/**\n * Thrown when the CWE API returns a non-2xx response.\n *\n * @example\n * ```typescript\n * import { CweApiError } from 'cwe-api-client';\n *\n * try {\n * await cwe.weakness(99999).get();\n * } catch (err) {\n * if (err instanceof CweApiError) {\n * console.log(err.status); // 404\n * console.log(err.statusText); // 'Not Found'\n * console.log(err.message); // 'CWE API error: 404 Not Found'\n * }\n * }\n * ```\n */\nexport class CweApiError extends Error {\n /** HTTP status code (e.g. `404`, `400`, `500`) */\n readonly status: number;\n /** HTTP status text (e.g. `'Not Found'`, `'Bad Request'`) */\n readonly statusText: string;\n\n constructor(status: number, statusText: string) {\n super(`CWE API error: ${status} ${statusText}`);\n this.name = 'CweApiError';\n this.status = status;\n this.statusText = statusText;\n }\n}\n","import type { CweWeakness } from '../domain/Weakness';\nimport type { CweRelationEntry, CweAncestorNode, CweDescendantNode } from '../domain/Relations';\n\n/** @internal */\nexport type RequestFn = <T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n) => Promise<T>;\n\n/**\n * Represents a CWE weakness resource, providing access to weakness details\n * and its position in the CWE hierarchy.\n *\n * Implements `PromiseLike<CweWeakness>` so it can be awaited directly to\n * fetch the full weakness, while also exposing hierarchy methods.\n *\n * @example\n * ```typescript\n * // Await directly to get full weakness data\n * const weakness = await cwe.weakness(79);\n *\n * // Or call .get() explicitly\n * const weakness = await cwe.weakness(79).get();\n *\n * // Navigate the hierarchy\n * const parents = await cwe.weakness(74).parents(1000);\n * const children = await cwe.weakness(74).children(1000);\n * const ancestors = await cwe.weakness(74).ancestors(1000);\n * const descendants = await cwe.weakness(74).descendants(1000);\n * ```\n */\nexport class WeaknessResource implements PromiseLike<CweWeakness> {\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n private readonly id: number,\n ) {}\n\n /**\n * Allows the resource to be awaited directly, resolving with the full weakness.\n * Delegates to {@link WeaknessResource.get}.\n */\n then<TResult1 = CweWeakness, TResult2 = never>(\n onfulfilled?: ((value: CweWeakness) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the full weakness entry.\n *\n * `GET /cwe/weakness/{id}`\n *\n * @returns The weakness object\n */\n async get(): Promise<CweWeakness> {\n const data = await this.request<{ Weaknesses: CweWeakness[] }>(`/cwe/weakness/${this.id}`);\n return data.Weaknesses[0];\n }\n\n /**\n * Fetches the direct parents of this weakness in a given view.\n *\n * `GET /cwe/{id}/parents?view={viewId}`\n *\n * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)\n * @returns Array of direct parent entries\n *\n * @example\n * ```typescript\n * const parents = await cwe.weakness(74).parents(1000);\n * ```\n */\n async parents(viewId?: number): Promise<CweRelationEntry[]> {\n return this.request<CweRelationEntry[]>(\n `/cwe/${this.id}/parents`,\n viewId !== undefined ? { view: viewId } : undefined,\n );\n }\n\n /**\n * Fetches the direct children of this weakness in a given view.\n *\n * `GET /cwe/{id}/children?view={viewId}`\n *\n * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)\n * @returns Array of direct child entries\n *\n * @example\n * ```typescript\n * const children = await cwe.weakness(74).children(1000);\n * ```\n */\n async children(viewId?: number): Promise<CweRelationEntry[]> {\n return this.request<CweRelationEntry[]>(\n `/cwe/${this.id}/children`,\n viewId !== undefined ? { view: viewId } : undefined,\n );\n }\n\n /**\n * Fetches the full ancestor tree of this weakness in a given view.\n *\n * `GET /cwe/{id}/ancestors?view={viewId}`\n *\n * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)\n * @returns Recursive ancestor tree from this node up to the view root\n *\n * @example\n * ```typescript\n * const tree = await cwe.weakness(74).ancestors(1000);\n * ```\n */\n async ancestors(viewId?: number): Promise<CweAncestorNode[]> {\n return this.request<CweAncestorNode[]>(\n `/cwe/${this.id}/ancestors`,\n viewId !== undefined ? { view: viewId } : undefined,\n );\n }\n\n /**\n * Fetches the full descendant tree of this weakness in a given view.\n *\n * `GET /cwe/{id}/descendants?view={viewId}`\n *\n * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)\n * @returns Recursive descendant tree from this node down to leaf entries\n *\n * @example\n * ```typescript\n * const tree = await cwe.weakness(74).descendants(1000);\n * ```\n */\n async descendants(viewId?: number): Promise<CweDescendantNode[]> {\n return this.request<CweDescendantNode[]>(\n `/cwe/${this.id}/descendants`,\n viewId !== undefined ? { view: viewId } : undefined,\n );\n }\n}\n","import type { CweCategory } from '../domain/Category';\nimport type { RequestFn } from './WeaknessResource';\n\n/**\n * Represents a CWE category resource, providing access to category details.\n *\n * Implements `PromiseLike<CweCategory>` so it can be awaited directly.\n *\n * @example\n * ```typescript\n * // Await directly to get full category data\n * const category = await cwe.category(189);\n *\n * // Or call .get() explicitly\n * const category = await cwe.category(189).get();\n * ```\n */\nexport class CategoryResource implements PromiseLike<CweCategory> {\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n private readonly id: number,\n ) {}\n\n /**\n * Allows the resource to be awaited directly, resolving with the full category.\n * Delegates to {@link CategoryResource.get}.\n */\n then<TResult1 = CweCategory, TResult2 = never>(\n onfulfilled?: ((value: CweCategory) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the full category entry.\n *\n * `GET /cwe/category/{id}`\n *\n * @returns The category object\n */\n async get(): Promise<CweCategory> {\n const data = await this.request<{ Categories: CweCategory[] }>(`/cwe/category/${this.id}`);\n return data.Categories[0];\n }\n}\n","import type { CweView } from '../domain/View';\nimport type { RequestFn } from './WeaknessResource';\n\n/**\n * Represents a CWE view resource, providing access to view details.\n *\n * Implements `PromiseLike<CweView>` so it can be awaited directly.\n *\n * @example\n * ```typescript\n * // Await directly to get full view data\n * const view = await cwe.view(1425);\n *\n * // Or call .get() explicitly\n * const view = await cwe.view(1425).get();\n * ```\n */\nexport class ViewResource implements PromiseLike<CweView> {\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n private readonly id: number,\n ) {}\n\n /**\n * Allows the resource to be awaited directly, resolving with the full view.\n * Delegates to {@link ViewResource.get}.\n */\n then<TResult1 = CweView, TResult2 = never>(\n onfulfilled?: ((value: CweView) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the full view entry.\n *\n * `GET /cwe/view/{id}`\n *\n * @returns The view object\n */\n async get(): Promise<CweView> {\n const data = await this.request<{ Views: CweView[] }>(`/cwe/view/${this.id}`);\n return data.Views[0];\n }\n}\n","import { CweApiError } from './errors/CweApiError';\nimport { WeaknessResource } from './resources/WeaknessResource';\nimport { CategoryResource } from './resources/CategoryResource';\nimport { ViewResource } from './resources/ViewResource';\nimport type { CweVersion } from './domain/Version';\nimport type { CweEntry } from './domain/CweEntry';\n\nconst DEFAULT_BASE_URL = 'https://cwe-api.mitre.org/api/v1';\n\n/**\n * Payload emitted on every HTTP request made by {@link CweClient}.\n */\nexport interface RequestEvent {\n /** Full URL that was requested */\n url: string;\n /** HTTP method used */\n method: 'GET';\n /** Timestamp when the request started */\n startedAt: Date;\n /** Timestamp when the request finished (success or error) */\n finishedAt: Date;\n /** Total duration in milliseconds */\n durationMs: number;\n /** HTTP status code returned by the server, if a response was received */\n statusCode?: number;\n /** Error thrown, if the request failed */\n error?: Error;\n}\n\n/** Map of supported client events to their callback signatures */\nexport interface CweClientEvents {\n request: (event: RequestEvent) => void;\n}\n\n/**\n * Constructor options for {@link CweClient}.\n */\nexport interface CweClientOptions {\n /**\n * Base URL for the CWE API (default: `'https://cwe-api.mitre.org/api/v1'`).\n * Override for mirrors or local instances.\n */\n baseUrl?: string;\n}\n\n/**\n * Main entry point for the MITRE CWE API client.\n *\n * @example\n * ```typescript\n * import { CweClient } from 'cwe-api-client';\n *\n * const cwe = new CweClient();\n *\n * // Get content version metadata\n * const version = await cwe.version();\n *\n * // Look up multiple CWEs at once\n * const entries = await cwe.lookup([74, 79]);\n *\n * // Get full weakness details\n * const weakness = await cwe.weakness(79);\n *\n * // Navigate the hierarchy\n * const parents = await cwe.weakness(74).parents(1000);\n * const descendants = await cwe.weakness(74).descendants(1000);\n *\n * // Get category and view details\n * const category = await cwe.category(189);\n * const view = await cwe.view(1425);\n * ```\n */\nexport class CweClient {\n private readonly baseUrl: string;\n private readonly listeners: Map<\n keyof CweClientEvents,\n CweClientEvents[keyof CweClientEvents][]\n > = new Map();\n\n /**\n * @param options - Optional configuration for the API base URL\n */\n constructor(options: CweClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n }\n\n /**\n * Subscribes to a client event.\n *\n * @example\n * ```typescript\n * cwe.on('request', (event) => {\n * console.log(`${event.method} ${event.url} — ${event.durationMs}ms`);\n * if (event.error) console.error('Request failed:', event.error);\n * });\n * ```\n */\n on<K extends keyof CweClientEvents>(event: K, callback: CweClientEvents[K]): this {\n const callbacks = this.listeners.get(event) ?? [];\n callbacks.push(callback);\n this.listeners.set(event, callbacks);\n return this;\n }\n\n private emit<K extends keyof CweClientEvents>(\n event: K,\n payload: Parameters<CweClientEvents[K]>[0],\n ): void {\n const callbacks = this.listeners.get(event) ?? [];\n for (const cb of callbacks) {\n (cb as (p: typeof payload) => void)(payload);\n }\n }\n\n /**\n * Performs a GET request to the CWE API.\n *\n * @param path - Path to append to the base URL (e.g. `/cwe/version`)\n * @param params - Optional query parameters\n * @internal\n */\n async request<T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n ): Promise<T> {\n const url = buildUrl(`${this.baseUrl}${path}`, params);\n const startedAt = new Date();\n let statusCode: number | undefined;\n try {\n const response = await fetch(url, {\n headers: { Accept: 'application/json' },\n });\n statusCode = response.status;\n if (!response.ok) {\n throw new CweApiError(response.status, response.statusText);\n }\n const data = await response.json() as T;\n this.emit('request', {\n url,\n method: 'GET',\n startedAt,\n finishedAt: new Date(),\n durationMs: Date.now() - startedAt.getTime(),\n statusCode,\n });\n return data;\n } catch (err) {\n const finishedAt = new Date();\n this.emit('request', {\n url,\n method: 'GET',\n startedAt,\n finishedAt,\n durationMs: finishedAt.getTime() - startedAt.getTime(),\n statusCode,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n throw err;\n }\n }\n\n /**\n * Fetches CWE content version metadata.\n *\n * `GET /cwe/version`\n *\n * @returns Version metadata including content version, date, and counts\n *\n * @example\n * ```typescript\n * const v = await cwe.version();\n * console.log(v.ContentVersion); // '4.19.1'\n * console.log(v.TotalWeaknesses); // 969\n * ```\n */\n async version(): Promise<CweVersion> {\n return this.request<CweVersion>('/cwe/version');\n }\n\n /**\n * Looks up multiple CWE entries by their numeric IDs in a single request.\n *\n * `GET /cwe/{ids}` (comma-separated)\n *\n * @param ids - Array of CWE numeric IDs (e.g. `[74, 79]`)\n * @returns Array of lightweight CWE entries with type and ID\n *\n * @example\n * ```typescript\n * const entries = await cwe.lookup([74, 79]);\n * entries.forEach(e => console.log(e.ID, e.Type));\n * ```\n */\n async lookup(ids: number[]): Promise<CweEntry[]> {\n return this.request<CweEntry[]>(`/cwe/${ids.join(',')}`);\n }\n\n /**\n * Returns a {@link WeaknessResource} for a given CWE weakness ID, providing\n * access to weakness details and its hierarchy (parents, children, ancestors,\n * descendants).\n *\n * The returned resource can be awaited directly to fetch the full weakness,\n * or chained to access hierarchy methods.\n *\n * @param id - The CWE weakness numeric ID (e.g. `79`)\n * @returns A chainable weakness resource\n *\n * @example\n * ```typescript\n * const weakness = await cwe.weakness(79);\n * const parents = await cwe.weakness(74).parents(1000);\n * const tree = await cwe.weakness(74).descendants(1000);\n * ```\n */\n weakness(id: number): WeaknessResource {\n return new WeaknessResource(\n <T>(path: string, params?: Record<string, string | number | boolean>) =>\n this.request<T>(path, params),\n id,\n );\n }\n\n /**\n * Returns a {@link CategoryResource} for a given CWE category ID, providing\n * access to category details.\n *\n * The returned resource can be awaited directly to fetch the full category.\n *\n * @param id - The CWE category numeric ID (e.g. `189`)\n * @returns A chainable category resource\n *\n * @example\n * ```typescript\n * const category = await cwe.category(189);\n * console.log(category.Name); // 'Numeric Errors'\n * ```\n */\n category(id: number): CategoryResource {\n return new CategoryResource(\n <T>(path: string, params?: Record<string, string | number | boolean>) =>\n this.request<T>(path, params),\n id,\n );\n }\n\n /**\n * Returns a {@link ViewResource} for a given CWE view ID, providing access\n * to view details.\n *\n * The returned resource can be awaited directly to fetch the full view.\n *\n * @param id - The CWE view numeric ID (e.g. `1425`)\n * @returns A chainable view resource\n *\n * @example\n * ```typescript\n * const view = await cwe.view(1425);\n * console.log(view.Name); // 'Weaknesses in the 2023 CWE Top 25...'\n * ```\n */\n view(id: number): ViewResource {\n return new ViewResource(\n <T>(path: string, params?: Record<string, string | number | boolean>) =>\n this.request<T>(path, params),\n id,\n );\n }\n}\n\n/**\n * Appends query parameters to a URL string, skipping `undefined` values.\n * @internal\n */\nfunction buildUrl(base: string, params?: Record<string, string | number | boolean>): string {\n if (!params) return base;\n const entries = Object.entries(params).filter(([, v]) => v !== undefined);\n if (entries.length === 0) return base;\n const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));\n return `${base}?${search.toString()}`;\n}\n"],"mappings":"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","names":[]}