@doccov/sdk 0.19.0 → 0.20.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/dist/index.d.ts CHANGED
@@ -114,21 +114,19 @@ interface DetectedSchemaEntry {
114
114
  schema: Record<string, unknown>;
115
115
  vendor: string;
116
116
  }
117
- /**
118
- * Runtime Schema Detection (Stubbed)
119
- *
120
- * Standard Schema extraction has been removed. This module provides
121
- * empty stubs to maintain API compatibility.
122
- */
123
117
  interface SchemaDetectionContext {
124
118
  baseDir: string;
125
119
  entryFile: string;
126
120
  }
121
+ interface DetectedSchema {
122
+ schema: Record<string, unknown>;
123
+ vendor: string;
124
+ }
127
125
  interface SchemaDetectionResult {
128
- schemas: Map<string, never>;
126
+ schemas: Map<string, DetectedSchema>;
129
127
  errors: string[];
130
128
  }
131
- declare function detectRuntimeSchemas(_context: SchemaDetectionContext): Promise<SchemaDetectionResult>;
129
+ declare function detectRuntimeSchemas(context: SchemaDetectionContext): Promise<SchemaDetectionResult>;
132
130
  declare function clearSchemaCache(): void;
133
131
  import * as TS from "typescript";
134
132
  /**
@@ -2298,8 +2296,9 @@ declare class DocCov {
2298
2296
  */
2299
2297
  private findPackageJson;
2300
2298
  /**
2301
- * Opportunistically detect Standard Schema exports from compiled modules.
2302
- * Returns undefined if detection fails or no schemas found (fallback to AST).
2299
+ * Detect Standard Schema exports from compiled modules.
2300
+ * Only runs when schemaExtraction is 'runtime' or 'hybrid'.
2301
+ * Returns undefined if detection is disabled, fails, or no schemas found.
2303
2302
  */
2304
2303
  private detectSchemas;
2305
2304
  private normalizeDiagnostic;
package/dist/index.js CHANGED
@@ -17,12 +17,180 @@ var __toESM = (mod, isNodeMode, target) => {
17
17
  };
18
18
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
19
 
20
- // src/analysis/schema-detection.ts
21
- async function detectRuntimeSchemas(_context) {
22
- return {
20
+ // src/extract/schema/standard-schema.ts
21
+ import { spawn } from "node:child_process";
22
+ import * as fs from "node:fs";
23
+ import * as path from "node:path";
24
+ function isStandardJSONSchema(obj) {
25
+ if (typeof obj !== "object" || obj === null)
26
+ return false;
27
+ const std = obj["~standard"];
28
+ if (typeof std !== "object" || std === null)
29
+ return false;
30
+ const stdObj = std;
31
+ if (typeof stdObj.version !== "number")
32
+ return false;
33
+ if (typeof stdObj.vendor !== "string")
34
+ return false;
35
+ const jsonSchema = stdObj.jsonSchema;
36
+ if (typeof jsonSchema !== "object" || jsonSchema === null)
37
+ return false;
38
+ const jsObj = jsonSchema;
39
+ return typeof jsObj.output === "function";
40
+ }
41
+ var WORKER_SCRIPT = `
42
+ const path = require('path');
43
+ const { pathToFileURL } = require('url');
44
+
45
+ async function extract() {
46
+ // With node -e, argv is: [node, arg1, arg2, ...]
47
+ // (the -e script is NOT in argv)
48
+ const [modulePath, target] = process.argv.slice(1);
49
+
50
+ try {
51
+ // Import the module using dynamic import (works with ESM and CJS)
52
+ const absPath = path.resolve(modulePath);
53
+ const mod = await import(pathToFileURL(absPath).href);
54
+ const results = [];
55
+
56
+ // Check each export
57
+ for (const [name, value] of Object.entries(mod)) {
58
+ if (name.startsWith('_')) continue;
59
+ if (typeof value !== 'object' || value === null) continue;
60
+
61
+ const std = value['~standard'];
62
+ if (!std || typeof std !== 'object') continue;
63
+ if (typeof std.version !== 'number') continue;
64
+ if (typeof std.vendor !== 'string') continue;
65
+ if (!std.jsonSchema || typeof std.jsonSchema.output !== 'function') continue;
66
+
67
+ try {
68
+ const outputSchema = std.jsonSchema.output(target);
69
+ const inputSchema = std.jsonSchema.input ? std.jsonSchema.input(target) : undefined;
70
+
71
+ results.push({
72
+ exportName: name,
73
+ vendor: std.vendor,
74
+ outputSchema,
75
+ inputSchema
76
+ });
77
+ } catch (e) {
78
+ // Skip schemas that fail to extract
79
+ }
80
+ }
81
+
82
+ console.log(JSON.stringify({ success: true, results }));
83
+ } catch (e) {
84
+ console.log(JSON.stringify({ success: false, error: e.message }));
85
+ }
86
+ }
87
+
88
+ extract();
89
+ `;
90
+ function resolveCompiledPath(tsPath, baseDir) {
91
+ const relativePath = path.relative(baseDir, tsPath);
92
+ const withoutExt = relativePath.replace(/\.tsx?$/, "");
93
+ const candidates = [
94
+ path.join(baseDir, `${withoutExt}.js`),
95
+ path.join(baseDir, "dist", `${withoutExt.replace(/^src\//, "")}.js`),
96
+ path.join(baseDir, "build", `${withoutExt.replace(/^src\//, "")}.js`),
97
+ path.join(baseDir, "lib", `${withoutExt.replace(/^src\//, "")}.js`)
98
+ ];
99
+ for (const candidate of candidates) {
100
+ if (fs.existsSync(candidate)) {
101
+ return candidate;
102
+ }
103
+ }
104
+ return null;
105
+ }
106
+ async function extractStandardSchemas(compiledJsPath, options = {}) {
107
+ const { timeout = 1e4, target = "draft-2020-12" } = options;
108
+ const result = {
23
109
  schemas: new Map,
24
110
  errors: []
25
111
  };
112
+ if (!fs.existsSync(compiledJsPath)) {
113
+ result.errors.push(`Compiled JS not found: ${compiledJsPath}`);
114
+ return result;
115
+ }
116
+ return new Promise((resolve) => {
117
+ const child = spawn("node", ["-e", WORKER_SCRIPT, compiledJsPath, target], {
118
+ timeout,
119
+ stdio: ["ignore", "pipe", "pipe"]
120
+ });
121
+ let stdout = "";
122
+ let stderr = "";
123
+ child.stdout.on("data", (data) => {
124
+ stdout += data.toString();
125
+ });
126
+ child.stderr.on("data", (data) => {
127
+ stderr += data.toString();
128
+ });
129
+ child.on("close", (code) => {
130
+ if (code !== 0) {
131
+ result.errors.push(`Extraction process failed: ${stderr || `exit code ${code}`}`);
132
+ resolve(result);
133
+ return;
134
+ }
135
+ try {
136
+ const parsed = JSON.parse(stdout);
137
+ if (!parsed.success) {
138
+ result.errors.push(`Extraction failed: ${parsed.error}`);
139
+ resolve(result);
140
+ return;
141
+ }
142
+ for (const item of parsed.results) {
143
+ result.schemas.set(item.exportName, {
144
+ exportName: item.exportName,
145
+ vendor: item.vendor,
146
+ outputSchema: item.outputSchema,
147
+ inputSchema: item.inputSchema
148
+ });
149
+ }
150
+ } catch (e) {
151
+ result.errors.push(`Failed to parse extraction output: ${e}`);
152
+ }
153
+ resolve(result);
154
+ });
155
+ child.on("error", (err) => {
156
+ result.errors.push(`Subprocess error: ${err.message}`);
157
+ resolve(result);
158
+ });
159
+ });
160
+ }
161
+ async function extractStandardSchemasFromProject(entryFile, baseDir, options = {}) {
162
+ const compiledPath = resolveCompiledPath(entryFile, baseDir);
163
+ if (!compiledPath) {
164
+ return {
165
+ schemas: new Map,
166
+ errors: [`Could not find compiled JS for ${entryFile}. Build the project first.`]
167
+ };
168
+ }
169
+ return extractStandardSchemas(compiledPath, options);
170
+ }
171
+
172
+ // src/analysis/schema-detection.ts
173
+ async function detectRuntimeSchemas(context) {
174
+ const { baseDir, entryFile } = context;
175
+ const compiledPath = resolveCompiledPath(entryFile, baseDir);
176
+ if (!compiledPath) {
177
+ return {
178
+ schemas: new Map,
179
+ errors: []
180
+ };
181
+ }
182
+ const extraction = await extractStandardSchemasFromProject(entryFile, baseDir);
183
+ const schemas = new Map;
184
+ for (const [name, result] of extraction.schemas) {
185
+ schemas.set(name, {
186
+ schema: result.outputSchema,
187
+ vendor: result.vendor
188
+ });
189
+ }
190
+ return {
191
+ schemas,
192
+ errors: extraction.errors
193
+ };
26
194
  }
27
195
  function clearSchemaCache() {}
28
196
  // src/extract/schema/types.ts
@@ -209,155 +377,6 @@ function getRegisteredAdapters() {
209
377
  function getSupportedLibraries() {
210
378
  return adapters.flatMap((a) => a.packages);
211
379
  }
212
- // src/extract/schema/standard-schema.ts
213
- import { spawn } from "node:child_process";
214
- import * as fs from "node:fs";
215
- import * as path from "node:path";
216
- function isStandardJSONSchema(obj) {
217
- if (typeof obj !== "object" || obj === null)
218
- return false;
219
- const std = obj["~standard"];
220
- if (typeof std !== "object" || std === null)
221
- return false;
222
- const stdObj = std;
223
- if (typeof stdObj.version !== "number")
224
- return false;
225
- if (typeof stdObj.vendor !== "string")
226
- return false;
227
- const jsonSchema = stdObj.jsonSchema;
228
- if (typeof jsonSchema !== "object" || jsonSchema === null)
229
- return false;
230
- const jsObj = jsonSchema;
231
- return typeof jsObj.output === "function";
232
- }
233
- var WORKER_SCRIPT = `
234
- const path = require('path');
235
-
236
- async function extract() {
237
- // With node -e, argv is: [node, arg1, arg2, ...]
238
- // (the -e script is NOT in argv)
239
- const [modulePath, target] = process.argv.slice(1);
240
-
241
- try {
242
- // Import the module
243
- const mod = require(path.resolve(modulePath));
244
- const results = [];
245
-
246
- // Check each export
247
- for (const [name, value] of Object.entries(mod)) {
248
- if (name.startsWith('_')) continue;
249
- if (typeof value !== 'object' || value === null) continue;
250
-
251
- const std = value['~standard'];
252
- if (!std || typeof std !== 'object') continue;
253
- if (typeof std.version !== 'number') continue;
254
- if (typeof std.vendor !== 'string') continue;
255
- if (!std.jsonSchema || typeof std.jsonSchema.output !== 'function') continue;
256
-
257
- try {
258
- const outputSchema = std.jsonSchema.output(target);
259
- const inputSchema = std.jsonSchema.input ? std.jsonSchema.input(target) : undefined;
260
-
261
- results.push({
262
- exportName: name,
263
- vendor: std.vendor,
264
- outputSchema,
265
- inputSchema
266
- });
267
- } catch (e) {
268
- // Skip schemas that fail to extract
269
- }
270
- }
271
-
272
- console.log(JSON.stringify({ success: true, results }));
273
- } catch (e) {
274
- console.log(JSON.stringify({ success: false, error: e.message }));
275
- }
276
- }
277
-
278
- extract();
279
- `;
280
- function resolveCompiledPath(tsPath, baseDir) {
281
- const relativePath = path.relative(baseDir, tsPath);
282
- const withoutExt = relativePath.replace(/\.tsx?$/, "");
283
- const candidates = [
284
- path.join(baseDir, `${withoutExt}.js`),
285
- path.join(baseDir, "dist", `${withoutExt.replace(/^src\//, "")}.js`),
286
- path.join(baseDir, "build", `${withoutExt.replace(/^src\//, "")}.js`),
287
- path.join(baseDir, "lib", `${withoutExt.replace(/^src\//, "")}.js`)
288
- ];
289
- for (const candidate of candidates) {
290
- if (fs.existsSync(candidate)) {
291
- return candidate;
292
- }
293
- }
294
- return null;
295
- }
296
- async function extractStandardSchemas(compiledJsPath, options = {}) {
297
- const { timeout = 1e4, target = "draft-2020-12" } = options;
298
- const result = {
299
- schemas: new Map,
300
- errors: []
301
- };
302
- if (!fs.existsSync(compiledJsPath)) {
303
- result.errors.push(`Compiled JS not found: ${compiledJsPath}`);
304
- return result;
305
- }
306
- return new Promise((resolve) => {
307
- const child = spawn("node", ["-e", WORKER_SCRIPT, compiledJsPath, target], {
308
- timeout,
309
- stdio: ["ignore", "pipe", "pipe"]
310
- });
311
- let stdout = "";
312
- let stderr = "";
313
- child.stdout.on("data", (data) => {
314
- stdout += data.toString();
315
- });
316
- child.stderr.on("data", (data) => {
317
- stderr += data.toString();
318
- });
319
- child.on("close", (code) => {
320
- if (code !== 0) {
321
- result.errors.push(`Extraction process failed: ${stderr || `exit code ${code}`}`);
322
- resolve(result);
323
- return;
324
- }
325
- try {
326
- const parsed = JSON.parse(stdout);
327
- if (!parsed.success) {
328
- result.errors.push(`Extraction failed: ${parsed.error}`);
329
- resolve(result);
330
- return;
331
- }
332
- for (const item of parsed.results) {
333
- result.schemas.set(item.exportName, {
334
- exportName: item.exportName,
335
- vendor: item.vendor,
336
- outputSchema: item.outputSchema,
337
- inputSchema: item.inputSchema
338
- });
339
- }
340
- } catch (e) {
341
- result.errors.push(`Failed to parse extraction output: ${e}`);
342
- }
343
- resolve(result);
344
- });
345
- child.on("error", (err) => {
346
- result.errors.push(`Subprocess error: ${err.message}`);
347
- resolve(result);
348
- });
349
- });
350
- }
351
- async function extractStandardSchemasFromProject(entryFile, baseDir, options = {}) {
352
- const compiledPath = resolveCompiledPath(entryFile, baseDir);
353
- if (!compiledPath) {
354
- return {
355
- schemas: new Map,
356
- errors: [`Could not find compiled JS for ${entryFile}. Build the project first.`]
357
- };
358
- }
359
- return extractStandardSchemas(compiledPath, options);
360
- }
361
380
  // src/analysis/docs-coverage.ts
362
381
  import {
363
382
  DRIFT_CATEGORIES
@@ -8978,6 +8997,10 @@ class DocCov {
8978
8997
  }
8979
8998
  }
8980
8999
  async detectSchemas(entryFile, packageDir) {
9000
+ const mode = this.options.schemaExtraction ?? "static";
9001
+ if (mode === "static") {
9002
+ return;
9003
+ }
8981
9004
  try {
8982
9005
  const result = await detectRuntimeSchemas({
8983
9006
  baseDir: packageDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doccov/sdk",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "DocCov SDK - Documentation coverage and drift detection for TypeScript",
5
5
  "keywords": [
6
6
  "typescript",