@ipation/specbridge 2.0.0 → 2.1.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
@@ -179,7 +179,7 @@ interface LevelConfig {
179
179
  * Warning during verification (non-blocking)
180
180
  */
181
181
  interface VerificationWarning {
182
- type: 'missing_verifier' | 'invalid_pattern' | 'other';
182
+ type: 'missing_verifier' | 'invalid_pattern' | 'invalid_params' | 'other';
183
183
  message: string;
184
184
  decisionId: string;
185
185
  constraintId: string;
@@ -1540,7 +1540,7 @@ declare class VerificationEngine {
1540
1540
  /**
1541
1541
  * Verify a single file
1542
1542
  */
1543
- verifyFile(filePath: string, decisions: Decision[], severityFilter?: Severity[], cwd?: string, reporter?: ExplainReporter): Promise<{
1543
+ verifyFile(filePath: string, decisions: Decision[], severityFilter?: Severity[], cwd?: string, reporter?: ExplainReporter, signal?: AbortSignal): Promise<{
1544
1544
  violations: Violation[];
1545
1545
  warnings: VerificationWarning[];
1546
1546
  errors: VerificationIssue[];
@@ -1595,6 +1595,8 @@ interface VerificationContext {
1595
1595
  sourceFile: SourceFile;
1596
1596
  constraint: Constraint;
1597
1597
  decisionId: string;
1598
+ /** Optional AbortSignal for cancellation support */
1599
+ signal?: AbortSignal;
1598
1600
  }
1599
1601
  /**
1600
1602
  * Verifier interface - all verifiers must implement this
package/dist/index.js CHANGED
@@ -7654,7 +7654,7 @@ var PluginLoader = class {
7654
7654
  }
7655
7655
  this.loaded = true;
7656
7656
  if (this.plugins.size > 0) {
7657
- console.log(`Loaded ${this.plugins.size} custom verifier(s)`);
7657
+ console.error(`Loaded ${this.plugins.size} custom verifier(s)`);
7658
7658
  }
7659
7659
  if (this.loadErrors.length > 0) {
7660
7660
  console.warn(`Failed to load ${this.loadErrors.length} plugin(s)`);
@@ -7676,7 +7676,7 @@ var PluginLoader = class {
7676
7676
  `Plugin ID "${plugin.metadata.id}" is already registered. Each plugin must have a unique ID.`
7677
7677
  );
7678
7678
  }
7679
- this.plugins.set(plugin.metadata.id, plugin.createVerifier);
7679
+ this.plugins.set(plugin.metadata.id, plugin);
7680
7680
  }
7681
7681
  /**
7682
7682
  * Validate plugin structure and metadata
@@ -7735,8 +7735,55 @@ var PluginLoader = class {
7735
7735
  * @returns Verifier instance or null if not found
7736
7736
  */
7737
7737
  getVerifier(id) {
7738
- const factory = this.plugins.get(id);
7739
- return factory ? factory() : null;
7738
+ const plugin = this.plugins.get(id);
7739
+ return plugin ? plugin.createVerifier() : null;
7740
+ }
7741
+ /**
7742
+ * Get a plugin by ID (includes paramsSchema)
7743
+ *
7744
+ * @param id - Plugin ID
7745
+ * @returns Plugin or null if not found
7746
+ */
7747
+ getPlugin(id) {
7748
+ return this.plugins.get(id) || null;
7749
+ }
7750
+ /**
7751
+ * Validate params against a plugin's paramsSchema
7752
+ *
7753
+ * @param id - Plugin ID
7754
+ * @param params - Parameters to validate
7755
+ * @returns Validation result with success flag and error message if failed
7756
+ */
7757
+ validateParams(id, params) {
7758
+ const plugin = this.plugins.get(id);
7759
+ if (!plugin) {
7760
+ return { success: false, error: `Plugin ${id} not found` };
7761
+ }
7762
+ if (!plugin.paramsSchema) {
7763
+ return { success: true };
7764
+ }
7765
+ if (!params) {
7766
+ return { success: false, error: `Plugin ${id} requires params but none were provided` };
7767
+ }
7768
+ if (typeof plugin.paramsSchema !== "object" || !plugin.paramsSchema || !("parse" in plugin.paramsSchema)) {
7769
+ return { success: false, error: `Plugin ${id} has invalid paramsSchema (must be a Zod schema)` };
7770
+ }
7771
+ const schema = plugin.paramsSchema;
7772
+ if (schema.safeParse) {
7773
+ const result = schema.safeParse(params);
7774
+ if (!result.success) {
7775
+ const errors = result.error?.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ") || "Validation failed";
7776
+ return { success: false, error: `Invalid params for ${id}: ${errors}` };
7777
+ }
7778
+ } else {
7779
+ try {
7780
+ schema.parse(params);
7781
+ } catch (error) {
7782
+ const message = error instanceof Error ? error.message : String(error);
7783
+ return { success: false, error: `Invalid params for ${id}: ${message}` };
7784
+ }
7785
+ }
7786
+ return { success: true };
7740
7787
  }
7741
7788
  /**
7742
7789
  * Get all registered plugin IDs
@@ -8054,9 +8101,13 @@ var VerificationEngine = class {
8054
8101
  let passed = 0;
8055
8102
  let failed = 0;
8056
8103
  const skipped = 0;
8104
+ const abortController = new AbortController();
8057
8105
  let timeoutHandle = null;
8058
8106
  const timeoutPromise = new Promise((resolve2) => {
8059
- timeoutHandle = setTimeout(() => resolve2("timeout"), timeout);
8107
+ timeoutHandle = setTimeout(() => {
8108
+ abortController.abort();
8109
+ resolve2("timeout");
8110
+ }, timeout);
8060
8111
  timeoutHandle.unref();
8061
8112
  });
8062
8113
  const verificationPromise = this.verifyFiles(
@@ -8065,6 +8116,7 @@ var VerificationEngine = class {
8065
8116
  severityFilter,
8066
8117
  cwd,
8067
8118
  options.reporter,
8119
+ abortController.signal,
8068
8120
  (violations, warnings, errors) => {
8069
8121
  allViolations.push(...violations);
8070
8122
  allWarnings.push(...warnings);
@@ -8123,10 +8175,13 @@ var VerificationEngine = class {
8123
8175
  /**
8124
8176
  * Verify a single file
8125
8177
  */
8126
- async verifyFile(filePath, decisions, severityFilter, cwd = process.cwd(), reporter) {
8178
+ async verifyFile(filePath, decisions, severityFilter, cwd = process.cwd(), reporter, signal) {
8127
8179
  const violations = [];
8128
8180
  const warnings = [];
8129
8181
  const errors = [];
8182
+ if (signal?.aborted) {
8183
+ return { violations, warnings, errors };
8184
+ }
8130
8185
  const sourceFile = await this.astCache.get(filePath, this.project);
8131
8186
  if (!sourceFile) return { violations, warnings, errors };
8132
8187
  let fileHash = null;
@@ -8207,11 +8262,35 @@ var VerificationEngine = class {
8207
8262
  continue;
8208
8263
  }
8209
8264
  }
8265
+ if (constraint.check?.verifier && constraint.check?.params) {
8266
+ const pluginLoader2 = getPluginLoader();
8267
+ const validationResult = pluginLoader2.validateParams(constraint.check.verifier, constraint.check.params);
8268
+ if (!validationResult.success) {
8269
+ warnings.push({
8270
+ type: "invalid_params",
8271
+ message: validationResult.error,
8272
+ decisionId: decision.metadata.id,
8273
+ constraintId: constraint.id,
8274
+ file: filePath
8275
+ });
8276
+ if (reporter) {
8277
+ reporter.add({
8278
+ file: filePath,
8279
+ decision,
8280
+ constraint,
8281
+ applied: false,
8282
+ reason: `Params validation failed: ${validationResult.error}`
8283
+ });
8284
+ }
8285
+ continue;
8286
+ }
8287
+ }
8210
8288
  const ctx = {
8211
8289
  filePath,
8212
8290
  sourceFile,
8213
8291
  constraint,
8214
- decisionId: decision.metadata.id
8292
+ decisionId: decision.metadata.id,
8293
+ signal
8215
8294
  };
8216
8295
  const verificationStart = Date.now();
8217
8296
  try {
@@ -8287,12 +8366,15 @@ var VerificationEngine = class {
8287
8366
  /**
8288
8367
  * Verify multiple files
8289
8368
  */
8290
- async verifyFiles(files, decisions, severityFilter, cwd, reporter, onFileVerified) {
8369
+ async verifyFiles(files, decisions, severityFilter, cwd, reporter, signal, onFileVerified) {
8291
8370
  const BATCH_SIZE = 50;
8292
8371
  for (let i = 0; i < files.length; i += BATCH_SIZE) {
8372
+ if (signal.aborted) {
8373
+ break;
8374
+ }
8293
8375
  const batch = files.slice(i, i + BATCH_SIZE);
8294
8376
  const results = await Promise.all(
8295
- batch.map((file) => this.verifyFile(file, decisions, severityFilter, cwd, reporter))
8377
+ batch.map((file) => this.verifyFile(file, decisions, severityFilter, cwd, reporter, signal))
8296
8378
  );
8297
8379
  for (const result of results) {
8298
8380
  onFileVerified(result.violations, result.warnings, result.errors);