@cofferdam/check-sdk 0.2.2

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 (46) hide show
  1. package/README.md +120 -0
  2. package/dist/ast.d.ts +120 -0
  3. package/dist/ast.d.ts.map +1 -0
  4. package/dist/ast.js +21 -0
  5. package/dist/ast.js.map +1 -0
  6. package/dist/category.d.ts +9 -0
  7. package/dist/category.d.ts.map +1 -0
  8. package/dist/category.js +15 -0
  9. package/dist/category.js.map +1 -0
  10. package/dist/check-context.d.ts +73 -0
  11. package/dist/check-context.d.ts.map +1 -0
  12. package/dist/check-context.js +7 -0
  13. package/dist/check-context.js.map +1 -0
  14. package/dist/define-check.d.ts +105 -0
  15. package/dist/define-check.d.ts.map +1 -0
  16. package/dist/define-check.js +57 -0
  17. package/dist/define-check.js.map +1 -0
  18. package/dist/index.d.ts +15 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +30 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/line-view.d.ts +25 -0
  23. package/dist/line-view.d.ts.map +1 -0
  24. package/dist/line-view.js +21 -0
  25. package/dist/line-view.js.map +1 -0
  26. package/dist/loader.d.ts +32 -0
  27. package/dist/loader.d.ts.map +1 -0
  28. package/dist/loader.js +74 -0
  29. package/dist/loader.js.map +1 -0
  30. package/dist/options.d.ts +37 -0
  31. package/dist/options.d.ts.map +1 -0
  32. package/dist/options.js +16 -0
  33. package/dist/options.js.map +1 -0
  34. package/dist/plugin-host.d.ts +64 -0
  35. package/dist/plugin-host.d.ts.map +1 -0
  36. package/dist/plugin-host.js +129 -0
  37. package/dist/plugin-host.js.map +1 -0
  38. package/dist/severity.d.ts +9 -0
  39. package/dist/severity.d.ts.map +1 -0
  40. package/dist/severity.js +14 -0
  41. package/dist/severity.js.map +1 -0
  42. package/dist/span.d.ts +15 -0
  43. package/dist/span.d.ts.map +1 -0
  44. package/dist/span.js +9 -0
  45. package/dist/span.js.map +1 -0
  46. package/package.json +45 -0
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @cofferdam/check-sdk
2
+
3
+ Author [cofferdam](https://github.com/TAJD/cofferdam) plugin checks in TypeScript.
4
+
5
+ `cofferdam` is a code-quality analyzer for TypeScript with a Rust core. The built-in checks cover the common ground (complexity, dead exports, triple-equals, etc); this SDK lets you express **project-specific architectural rules** as checks the same engine runs alongside the built-ins.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add -D @cofferdam/check-sdk # pnpm
11
+ npm install -D @cofferdam/check-sdk # npm
12
+ yarn add -D @cofferdam/check-sdk # yarn
13
+ ```
14
+
15
+ The SDK ships compiled JS + types; no runtime dependencies. Node 16+.
16
+
17
+ ## Hello check
18
+
19
+ ```ts
20
+ // my-checks/no-rovikore.ts
21
+ import { Category, defineCheck, Severity } from "@cofferdam/check-sdk";
22
+
23
+ export default defineCheck({
24
+ id: "BrandCasing",
25
+ category: Category.Warning,
26
+ basePriority: 15,
27
+ defaultSeverity: Severity.High,
28
+ explanation: "Brand must be spelled ROVIKORE.",
29
+ files: { extensions: ["ts", "tsx"] },
30
+ options: {
31
+ brand: { default: "ROVIKORE", type: "string" },
32
+ },
33
+ run(file, ctx, opts) {
34
+ for (const line of file.lines()) {
35
+ // Pattern A — line walk. Skip comments and skip non-string content
36
+ // so we don't false-positive on identifiers / JSX text.
37
+ if (line.isComment || line.isJsxText) continue;
38
+ if (!line.isStringLiteral) continue;
39
+ const m = /\bRovikore\b/.exec(line.text);
40
+ if (!m) continue;
41
+ ctx.report({
42
+ message: `Brand must be ${opts.brand}, not ${m[0]}.`,
43
+ span: line.spanFor(m.index, m.index + m[0].length),
44
+ });
45
+ }
46
+ },
47
+ });
48
+ ```
49
+
50
+ Wire it into `cofferdam.toml`:
51
+
52
+ ```toml
53
+ plugins = ["./my-checks/no-rovikore.ts"]
54
+
55
+ [checks."BrandCasing"]
56
+ brand = "ROVIKORE"
57
+ ```
58
+
59
+ Then `cofferdam check src/` runs your plugin alongside every built-in check. Findings flow through the same priority computation, suppression directives, baseline diffing, and output formats.
60
+
61
+ ## Three patterns
62
+
63
+ The SDK supports three check shapes, each suited to a different rule class:
64
+
65
+ - **Pattern A — line walk.** `for (const line of file.lines()) { … }`. Best for content checks against string literals or comments (brand spelling, banned words, license headers). The fastest path; no AST parse needed.
66
+ - **Pattern B — AST findAll.** `file.ast.findAll("CallExpression")`, `findAll("ImportDeclaration")`. Best when the rule is a tag-and-match against specific node kinds (banned imports, forbidden API calls).
67
+ - **Pattern C — stateful walk.** `file.ast.walk(visitor)` with accumulator state, decided per-file. Best when the rule depends on inter-statement context (tenant scoping across queries, accumulator-vs-mutator analysis).
68
+
69
+ `AstView` exposes 9 node kinds today: `Program`, `CallExpression`, `ImportDeclaration`, `Function`, `ArrowFunctionExpression`, `Class`, `ObjectExpression`, `MemberExpression`, `IdentifierReference`. The set is additive — new kinds can land in minor releases without breaking existing plugins.
70
+
71
+ ## API surface
72
+
73
+ ```ts
74
+ import {
75
+ defineCheck, // factory — returns a Check
76
+ Category, // Consistency | Design | Readability | Refactor | Warning
77
+ Severity, // Info | Low | Medium | High | Critical
78
+ Walk, // visitor return: Continue | Skip
79
+ } from "@cofferdam/check-sdk";
80
+
81
+ import type {
82
+ Check, CheckContext, ReportArgs, Fix,
83
+ SourceFile, LineView, Span, RelatedSpan,
84
+ AstView, AstVisitor, AstNode, NodeKind,
85
+ // Per-kind typed nodes:
86
+ ProgramNode, CallExpressionNode, ImportDeclarationNode,
87
+ FunctionNode, ArrowFunctionExpressionNode, ClassNode,
88
+ ObjectExpressionNode, MemberExpressionNode, IdentifierReferenceNode,
89
+ // Options schema helpers:
90
+ OptionKind, OptionSpec, OptionsSchema, ResolvedOptions,
91
+ } from "@cofferdam/check-sdk";
92
+ ```
93
+
94
+ Everything in this list is part of the stability contract; new exports are additive. Removing or renaming an export is a breaking change.
95
+
96
+ ## Versioning
97
+
98
+ The SDK ships in lockstep with the cofferdam binary. `@cofferdam/check-sdk@X.Y.Z` is built and tested against `cofferdam@X.Y.Z`; the cofferdam plugin host enforces a major-version compatibility check at load time and refuses to run plugins whose vendored SDK is from a different major.
99
+
100
+ For 0.x: minor bumps may be breaking. Pin both packages to the same exact version until 1.0:
101
+
102
+ ```jsonc
103
+ {
104
+ "devDependencies": {
105
+ "cofferdam": "0.2.2",
106
+ "@cofferdam/check-sdk": "0.2.2"
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Documentation
112
+
113
+ - Full plugin authoring guide (with worked examples for all three patterns): <https://tajd.github.io/cofferdam>
114
+ - AST surface reference: [`design/sdk-ast-surface.md`](https://github.com/TAJD/cofferdam/blob/main/design/sdk-ast-surface.md)
115
+ - AST wire format (Rust ↔ Node host): [`design/sdk-ast-wire.md`](https://github.com/TAJD/cofferdam/blob/main/design/sdk-ast-wire.md)
116
+ - Three end-to-end fixtures (working plugins): [`examples-plugins/`](https://github.com/TAJD/cofferdam/tree/main/examples-plugins)
117
+
118
+ ## License
119
+
120
+ MIT.
package/dist/ast.d.ts ADDED
@@ -0,0 +1,120 @@
1
+ import type { Span } from "./span.js";
2
+ /** Outcome of an AstVisitor visit. Controls per-node descent. */
3
+ export declare const Walk: {
4
+ readonly Continue: "continue";
5
+ readonly Skip: "skip";
6
+ };
7
+ export type Walk = (typeof Walk)[keyof typeof Walk];
8
+ /**
9
+ * Kinds exposed via {@link AstView.findAll} and the {@link AstNode}
10
+ * union. Deliberately a small set covering the rovikore-host plugin
11
+ * patterns; grows as plugin needs surface.
12
+ *
13
+ * v0 freeze: cd-717 (`design/sdk-ast-surface.md`). 5 strict +
14
+ * 4 borderline kinds. `NewExpression`, `BinaryExpression`,
15
+ * `StringLiteral`, `NumericLiteral` are deferred until a fixture
16
+ * justifies them — adding kinds is non-breaking, removing isn't.
17
+ */
18
+ export type NodeKind = "Program" | "CallExpression" | "ImportDeclaration" | "Function" | "ArrowFunctionExpression" | "Class" | "ObjectExpression" | "MemberExpression" | "IdentifierReference";
19
+ /**
20
+ * Common base for every AST node exposed across the FFI. Concrete
21
+ * kinds extend this with their own typed properties.
22
+ */
23
+ export interface AstNodeBase<K extends NodeKind> {
24
+ readonly kind: K;
25
+ readonly span: Span;
26
+ }
27
+ /**
28
+ * The Program root — entry point for `view.root`. Body holds top-level
29
+ * statements; iterate it for declarations / imports / exports without
30
+ * walking the rest of the tree.
31
+ *
32
+ * Added in cd-svf to make `view.root: AstNode` typeable. Without
33
+ * Program in the union, root narrows to `never` on any TS switch.
34
+ */
35
+ export interface ProgramNode extends AstNodeBase<"Program"> {
36
+ readonly body: readonly AstNode[];
37
+ }
38
+ export interface CallExpressionNode extends AstNodeBase<"CallExpression"> {
39
+ /** The callee expression — typically an `IdentifierReference` or `MemberExpression`. */
40
+ readonly callee: AstNode;
41
+ readonly arguments: readonly AstNode[];
42
+ }
43
+ export interface ImportDeclarationNode extends AstNodeBase<"ImportDeclaration"> {
44
+ /** The module specifier, e.g. `"axios"`. */
45
+ readonly source: string;
46
+ /** Each specifier's local name. */
47
+ readonly specifiers: readonly {
48
+ readonly localName: string;
49
+ readonly imported?: string;
50
+ }[];
51
+ }
52
+ export interface FunctionNode extends AstNodeBase<"Function"> {
53
+ readonly name: string | undefined;
54
+ readonly params: readonly AstNode[];
55
+ readonly async: boolean;
56
+ readonly generator: boolean;
57
+ }
58
+ export interface ArrowFunctionExpressionNode extends AstNodeBase<"ArrowFunctionExpression"> {
59
+ readonly params: readonly AstNode[];
60
+ readonly async: boolean;
61
+ readonly expression: boolean;
62
+ }
63
+ export interface ClassNode extends AstNodeBase<"Class"> {
64
+ readonly name: string | undefined;
65
+ }
66
+ export interface ObjectExpressionNode extends AstNodeBase<"ObjectExpression"> {
67
+ readonly properties: readonly AstNode[];
68
+ }
69
+ export interface MemberExpressionNode extends AstNodeBase<"MemberExpression"> {
70
+ readonly object: AstNode;
71
+ /** For `.foo` / `["foo"]`, the resolved property name when statically determinable. */
72
+ readonly property: string | undefined;
73
+ readonly computed: boolean;
74
+ }
75
+ export interface IdentifierReferenceNode extends AstNodeBase<"IdentifierReference"> {
76
+ readonly name: string;
77
+ }
78
+ /** Discriminated union of every node kind {@link AstView} surfaces. */
79
+ export type AstNode = ProgramNode | CallExpressionNode | ImportDeclarationNode | FunctionNode | ArrowFunctionExpressionNode | ClassNode | ObjectExpressionNode | MemberExpressionNode | IdentifierReferenceNode;
80
+ /** Map from kind string to typed node — drives `findAll`'s return type. */
81
+ export type NodeOfKind<K extends NodeKind> = Extract<AstNode, {
82
+ kind: K;
83
+ }>;
84
+ /**
85
+ * Visitor passed to {@link AstView.walk}. Override only the kinds you
86
+ * care about; defaults are `Walk.Continue` no-ops. Returning
87
+ * `Walk.Skip` stops descent into that node only — sibling nodes still
88
+ * visit.
89
+ */
90
+ export interface AstVisitor {
91
+ visitProgram?(node: ProgramNode): Walk;
92
+ visitCallExpression?(node: CallExpressionNode): Walk;
93
+ visitImportDeclaration?(node: ImportDeclarationNode): Walk;
94
+ visitFunction?(node: FunctionNode): Walk;
95
+ visitArrowFunctionExpression?(node: ArrowFunctionExpressionNode): Walk;
96
+ visitClass?(node: ClassNode): Walk;
97
+ visitObjectExpression?(node: ObjectExpressionNode): Walk;
98
+ visitMemberExpression?(node: MemberExpressionNode): Walk;
99
+ visitIdentifierReference?(node: IdentifierReferenceNode): Walk;
100
+ }
101
+ /**
102
+ * Borrowed view of a parsed file's AST. Cheap to construct; cofferdam's
103
+ * napi loader builds one per file per check invocation.
104
+ */
105
+ export interface AstView {
106
+ /** Direct access to the program root for surgical inspection. */
107
+ readonly root: AstNode;
108
+ /**
109
+ * Eagerly collect every node of `kind` in document order. O(N) over
110
+ * the AST. Allocates an array. For multi-kind queries prefer a single
111
+ * {@link walk} pass.
112
+ */
113
+ findAll<K extends NodeKind>(kind: K): readonly NodeOfKind<K>[];
114
+ /**
115
+ * Run a stateful visitor over the tree. See {@link AstVisitor} for
116
+ * the per-kind callbacks.
117
+ */
118
+ walk(visitor: AstVisitor): void;
119
+ }
120
+ //# sourceMappingURL=ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
package/dist/ast.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // AST surface for plugins (cd-81a.2). Mirrors cofferdam_core::ast — a
3
+ // thin TS proxy over oxc's parsed tree. The napi loader serializes the
4
+ // shape across the FFI boundary so plugins program against this.
5
+ //
6
+ // Three access patterns:
7
+ //
8
+ // 1. Eager filter — `view.findAll("CallExpression")` returns every node
9
+ // of the given kind in document order.
10
+ // 2. Stateful walk — `view.walk(visitor)`. Each visit method returns
11
+ // `Walk.Continue` or `Walk.Skip` to control descent.
12
+ // 3. Direct rooted access — `view.root` for surgical inspection. Escape
13
+ // hatch when the kinds we expose don't cover the case yet.
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.Walk = void 0;
16
+ /** Outcome of an AstVisitor visit. Controls per-node descent. */
17
+ exports.Walk = {
18
+ Continue: "continue",
19
+ Skip: "skip",
20
+ };
21
+ //# sourceMappingURL=ast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.js","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":";AAAA,sEAAsE;AACtE,uEAAuE;AACvE,iEAAiE;AACjE,EAAE;AACF,yBAAyB;AACzB,EAAE;AACF,wEAAwE;AACxE,0CAA0C;AAC1C,qEAAqE;AACrE,wDAAwD;AACxD,wEAAwE;AACxE,8DAA8D;;;AAI9D,iEAAiE;AACpD,QAAA,IAAI,GAAG;IAClB,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,MAAM;CACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare const Category: {
2
+ readonly Consistency: "consistency";
3
+ readonly Design: "design";
4
+ readonly Readability: "readability";
5
+ readonly Refactor: "refactor";
6
+ readonly Warning: "warning";
7
+ };
8
+ export type Category = (typeof Category)[keyof typeof Category];
9
+ //# sourceMappingURL=category.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"category.d.ts","sourceRoot":"","sources":["../src/category.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,QAAQ;;;;;;CAMX,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ // Mirror of cofferdam_core::Category. The Rust enum serializes as the
3
+ // lowercase variant ("warning", "design", ...); we hold both shapes so
4
+ // authors can write `Category.Warning` for ergonomics and the engine
5
+ // always reads the wire form.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.Category = void 0;
8
+ exports.Category = {
9
+ Consistency: "consistency",
10
+ Design: "design",
11
+ Readability: "readability",
12
+ Refactor: "refactor",
13
+ Warning: "warning",
14
+ };
15
+ //# sourceMappingURL=category.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"category.js","sourceRoot":"","sources":["../src/category.ts"],"names":[],"mappings":";AAAA,sEAAsE;AACtE,uEAAuE;AACvE,qEAAqE;AACrE,8BAA8B;;;AAEjB,QAAA,QAAQ,GAAG;IACtB,WAAW,EAAE,aAAa;IAC1B,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,aAAa;IAC1B,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;CACV,CAAC"}
@@ -0,0 +1,73 @@
1
+ import type { AstView } from "./ast.js";
2
+ import type { Severity } from "./severity.js";
3
+ import type { LineView } from "./line-view.js";
4
+ import type { Span } from "./span.js";
5
+ /** A source file passed to a check's `run` callback. */
6
+ export interface SourceFile {
7
+ /** Absolute path as cofferdam discovered it. Forward-slashed on every host. */
8
+ readonly path: string;
9
+ /** Full text of the file, UTF-8. Byte offsets in spans index into this. */
10
+ readonly text: string;
11
+ /**
12
+ * Iterate every line in the file with classification flags drawn
13
+ * from the comment table + an AST walk over string/template/JSX.
14
+ * See {@link LineView} for flag semantics.
15
+ */
16
+ lines(): IterableIterator<LineView>;
17
+ /**
18
+ * Plugin-facing AST view. `null` only when parsing produced no
19
+ * usable program (the engine emitted `Warning.ParseError` for those
20
+ * files — your check is not invoked again on the failed file).
21
+ */
22
+ readonly ast: AstView | null;
23
+ }
24
+ /**
25
+ * A single mechanical fix: replace the bytes in `span` with `replacement`.
26
+ * Mirrors `cofferdam_core::TextEdit`. Plugin authors attach this at
27
+ * report time; the cofferdam fix engine prefers it over the built-in
28
+ * `Check::autofix` trait method (cd-81a.6).
29
+ *
30
+ * Edits must be non-overlapping; the fix engine applies them in reverse
31
+ * byte-offset order so earlier replacements don't invalidate later spans.
32
+ */
33
+ export interface Fix {
34
+ readonly span: Span;
35
+ readonly replacement: string;
36
+ }
37
+ /** Per-issue payload passed to {@link CheckContext.report}. */
38
+ export interface ReportArgs {
39
+ /** Required. Human-readable problem description. */
40
+ readonly message: string;
41
+ /** Required. Where the issue lives. Build via `LineView.spanFor` or
42
+ * `SourceFile.ast` node spans. */
43
+ readonly span: Span;
44
+ /**
45
+ * Optional severity override for this specific finding. Defaults to
46
+ * the check's `defaultSeverity` declared on `defineCheck`.
47
+ */
48
+ readonly severity?: Severity;
49
+ /**
50
+ * Optional secondary locations participating in the same finding —
51
+ * e.g. the duplicate of a duplicate-block, the other declarer of a
52
+ * duplicate-export. Omitted from JSON when empty.
53
+ */
54
+ readonly related?: readonly {
55
+ readonly file: string;
56
+ readonly span: Span;
57
+ }[];
58
+ /**
59
+ * Optional autofix payload. When present, `cofferdam fix` applies it
60
+ * in place of any built-in autofix logic for this check.
61
+ */
62
+ readonly fix?: Fix;
63
+ }
64
+ /**
65
+ * Mutable per-file scratch passed to `Check.run`. The check emits
66
+ * findings via `ctx.report(...)`; the engine collects, suppresses,
67
+ * baselines, and renders.
68
+ */
69
+ export interface CheckContext {
70
+ /** Emit a finding. Calls accumulate; order is preserved. */
71
+ report(args: ReportArgs): void;
72
+ }
73
+ //# sourceMappingURL=check-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-context.d.ts","sourceRoot":"","sources":["../src/check-context.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,wDAAwD;AACxD,MAAM,WAAW,UAAU;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,KAAK,IAAI,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;uCACmC;IACnC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;KAAE,EAAE,CAAC;IAC7E;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CAChC"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ // SourceFile + CheckContext — the per-file scratch passed to a check's
3
+ // `run(file, ctx, opts)` callback. Mirrors cofferdam_core::CheckContext
4
+ // minus the bits that are only meaningful in the Rust pipeline (corpus,
5
+ // FinalizeContext — those land in their own beads).
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=check-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-context.js","sourceRoot":"","sources":["../src/check-context.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,wEAAwE;AACxE,wEAAwE;AACxE,oDAAoD"}
@@ -0,0 +1,105 @@
1
+ import type { Category } from "./category.js";
2
+ import type { CheckContext, SourceFile } from "./check-context.js";
3
+ import type { OptionsSchema, ResolvedOptions } from "./options.js";
4
+ import type { Severity } from "./severity.js";
5
+ /**
6
+ * Declarative file-scope filter (cd-81a.5). When set, the engine
7
+ * pre-filters discovered files against this scope before invoking
8
+ * `run()`. Replaces the `scannable?` boilerplate that rovikore-host
9
+ * checks ship today.
10
+ */
11
+ export interface FileScope {
12
+ /** File extensions (without leading dot). Empty/omitted = any. */
13
+ readonly extensions?: readonly string[];
14
+ /**
15
+ * Optional include glob (gitignore syntax via globset). When set,
16
+ * the file path must match.
17
+ */
18
+ readonly pathPattern?: string;
19
+ /**
20
+ * Exclude globs. Paths matching any of these are skipped, even if
21
+ * `pathPattern` matched.
22
+ */
23
+ readonly excludePatterns?: readonly string[];
24
+ }
25
+ /**
26
+ * The shape `defineCheck` returns — what the napi loader picks up
27
+ * from the plugin module's default export. Holds the static metadata
28
+ * + the `run` callback.
29
+ *
30
+ * The `_optionsKeyType` field is a phantom (never set at runtime); it
31
+ * pins the schema shape into the type so the loader can pass typed
32
+ * `opts` without TS losing inference.
33
+ */
34
+ export interface Check<S extends OptionsSchema = OptionsSchema> {
35
+ readonly id: string;
36
+ readonly category: Category;
37
+ readonly basePriority: number;
38
+ readonly defaultSeverity: Severity;
39
+ readonly explanation: string;
40
+ /** Optional long-form catalog body used by `cofferdam explain --full`. */
41
+ readonly body: string | undefined;
42
+ /** Whether the check needs full type-aware analysis (routes through ts-morph). */
43
+ readonly requiresTypes: boolean;
44
+ /** Two-pass consistency mode. */
45
+ readonly consistency: boolean;
46
+ readonly options: S;
47
+ /** File-scope filter. `undefined` = applies to every file. */
48
+ readonly files: FileScope | undefined;
49
+ run(file: SourceFile, ctx: CheckContext, opts: ResolvedOptions<S>): void;
50
+ }
51
+ /**
52
+ * Input to `defineCheck`. Same shape as {@link Check} but the schema
53
+ * generic is inferred at the call site. Optional fields may be omitted
54
+ * (TS picks the conservative default).
55
+ */
56
+ export interface DefineCheckInput<S extends OptionsSchema> {
57
+ readonly id: string;
58
+ readonly category: Category;
59
+ readonly basePriority: number;
60
+ /** Defaults to `Severity.Medium` if omitted. */
61
+ readonly defaultSeverity?: Severity;
62
+ readonly explanation: string;
63
+ readonly body?: string;
64
+ readonly requiresTypes?: boolean;
65
+ readonly consistency?: boolean;
66
+ /** Schema; defaults to `{}`. Inferred at the call site. */
67
+ readonly options?: S;
68
+ /** File-scope filter; defaults to "every file". */
69
+ readonly files?: FileScope;
70
+ run(file: SourceFile, ctx: CheckContext, opts: ResolvedOptions<S>): void;
71
+ }
72
+ /**
73
+ * Author a cofferdam plugin check. The call site is the contract: the
74
+ * value passed in is the value the napi loader receives, with TS
75
+ * inference flowing from `options` into `run`'s third argument.
76
+ *
77
+ * @example
78
+ * import { defineCheck, Category, Severity } from "@cofferdam/check-sdk";
79
+ *
80
+ * export default defineCheck({
81
+ * id: "BrandCasing",
82
+ * category: Category.Warning,
83
+ * basePriority: 15,
84
+ * explanation: "Brand name must be all-caps in user-facing copy.",
85
+ * options: {
86
+ * brand: { default: "ROVIKORE", type: "string" },
87
+ * allowedAliases: { default: [] as string[], type: "string[]" },
88
+ * },
89
+ * run(file, ctx, opts) {
90
+ * for (const ln of file.lines()) {
91
+ * if (ln.isComment) continue;
92
+ * if (!ln.isStringLiteral) continue;
93
+ * const m = /\bRovikore\b/.exec(ln.text);
94
+ * if (!m) continue;
95
+ * if (opts.allowedAliases.some((a) => ln.text.includes(a))) continue;
96
+ * ctx.report({
97
+ * message: `Brand name must be "${opts.brand}", not "${m[0]}".`,
98
+ * span: ln.spanFor(m.index, m.index + m[0].length),
99
+ * });
100
+ * }
101
+ * },
102
+ * });
103
+ */
104
+ export declare function defineCheck<const S extends OptionsSchema = {}>(input: DefineCheckInput<S>): Check<S>;
105
+ //# sourceMappingURL=define-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-check.d.ts","sourceRoot":"","sources":["../src/define-check.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,KAAK,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa;IAC5D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,kFAAkF;IAClF,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,iCAAiC;IACjC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IACtC,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAC1E;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,aAAa;IACvD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,gDAAgD;IAChD,QAAQ,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACrB,mDAAmD;IACnD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAC1E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,CAAC,SAAS,aAAa,GAAG,EAAE,EAC5D,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GACzB,KAAK,CAAC,CAAC,CAAC,CAcV"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ // defineCheck — the factory plugin authors call to declare a check.
3
+ //
4
+ // Type inference flows from the `options` schema declared on the input
5
+ // to the `opts` argument of `run`. This is the surface that earns the
6
+ // SDK its right to exist — without it, plugin authors hand-write
7
+ // matching types twice.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.defineCheck = defineCheck;
10
+ /**
11
+ * Author a cofferdam plugin check. The call site is the contract: the
12
+ * value passed in is the value the napi loader receives, with TS
13
+ * inference flowing from `options` into `run`'s third argument.
14
+ *
15
+ * @example
16
+ * import { defineCheck, Category, Severity } from "@cofferdam/check-sdk";
17
+ *
18
+ * export default defineCheck({
19
+ * id: "BrandCasing",
20
+ * category: Category.Warning,
21
+ * basePriority: 15,
22
+ * explanation: "Brand name must be all-caps in user-facing copy.",
23
+ * options: {
24
+ * brand: { default: "ROVIKORE", type: "string" },
25
+ * allowedAliases: { default: [] as string[], type: "string[]" },
26
+ * },
27
+ * run(file, ctx, opts) {
28
+ * for (const ln of file.lines()) {
29
+ * if (ln.isComment) continue;
30
+ * if (!ln.isStringLiteral) continue;
31
+ * const m = /\bRovikore\b/.exec(ln.text);
32
+ * if (!m) continue;
33
+ * if (opts.allowedAliases.some((a) => ln.text.includes(a))) continue;
34
+ * ctx.report({
35
+ * message: `Brand name must be "${opts.brand}", not "${m[0]}".`,
36
+ * span: ln.spanFor(m.index, m.index + m[0].length),
37
+ * });
38
+ * }
39
+ * },
40
+ * });
41
+ */
42
+ function defineCheck(input) {
43
+ return {
44
+ id: input.id,
45
+ category: input.category,
46
+ basePriority: input.basePriority,
47
+ defaultSeverity: input.defaultSeverity ?? "medium",
48
+ explanation: input.explanation,
49
+ body: input.body,
50
+ requiresTypes: input.requiresTypes ?? false,
51
+ consistency: input.consistency ?? false,
52
+ options: input.options ?? {},
53
+ files: input.files,
54
+ run: input.run,
55
+ };
56
+ }
57
+ //# sourceMappingURL=define-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-check.js","sourceRoot":"","sources":["../src/define-check.ts"],"names":[],"mappings":";AAAA,oEAAoE;AACpE,EAAE;AACF,uEAAuE;AACvE,sEAAsE;AACtE,iEAAiE;AACjE,wBAAwB;;AA6GxB,kCAgBC;AAhDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,SAAgB,WAAW,CACzB,KAA0B;IAE1B,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,QAAQ;QAClD,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,KAAK;QAC3C,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,KAAK;QACvC,OAAO,EAAE,KAAK,CAAC,OAAO,IAAK,EAAQ;QACnC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,EAAE,KAAK,CAAC,GAAG;KACf,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ export { Category } from "./category.js";
2
+ export { Severity } from "./severity.js";
3
+ export { Walk } from "./ast.js";
4
+ export { defineCheck } from "./define-check.js";
5
+ export { runPlugin, buildSourceFile } from "./plugin-host.js";
6
+ export { loadPlugins, runPlugins } from "./loader.js";
7
+ export type { PluginReport, PluginRunInput, NativeLineView, } from "./plugin-host.js";
8
+ export type { LoadedPlugin, RunPluginsOptions } from "./loader.js";
9
+ export type { Check, DefineCheckInput, FileScope } from "./define-check.js";
10
+ export type { CheckContext, Fix, ReportArgs, SourceFile } from "./check-context.js";
11
+ export type { LineView } from "./line-view.js";
12
+ export type { Span, RelatedSpan } from "./span.js";
13
+ export type { AstView, AstVisitor, AstNode, AstNodeBase, ProgramNode, CallExpressionNode, ImportDeclarationNode, FunctionNode, ArrowFunctionExpressionNode, ClassNode, ObjectExpressionNode, MemberExpressionNode, IdentifierReferenceNode, NodeKind, NodeOfKind, } from "./ast.js";
14
+ export type { OptionKind, OptionSpec, OptionsSchema, ResolvedOptions, NoOptions, } from "./options.js";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIhD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,YAAY,EACV,YAAY,EACZ,cAAc,EACd,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnE,YAAY,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,YAAY,EACV,OAAO,EACP,UAAU,EACV,OAAO,EACP,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,2BAA2B,EAC3B,SAAS,EACT,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACR,UAAU,GACX,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,SAAS,GACV,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ // @cofferdam/check-sdk — author cofferdam plugin checks in TypeScript.
3
+ //
4
+ // Plugin authors install this package, write a check with `defineCheck`,
5
+ // and `default export` it. The cofferdam napi loader (cd-81a.7) picks
6
+ // the module up via `cofferdam.toml`'s `plugins = [...]` array and runs
7
+ // it in a worker_thread per source file.
8
+ //
9
+ // This is the public surface. Everything exported here is part of the
10
+ // stability contract; everything kept internal is not. New exports are
11
+ // additive — removing or renaming an export is a breaking change.
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.runPlugins = exports.loadPlugins = exports.buildSourceFile = exports.runPlugin = exports.defineCheck = exports.Walk = exports.Severity = exports.Category = void 0;
14
+ var category_js_1 = require("./category.js");
15
+ Object.defineProperty(exports, "Category", { enumerable: true, get: function () { return category_js_1.Category; } });
16
+ var severity_js_1 = require("./severity.js");
17
+ Object.defineProperty(exports, "Severity", { enumerable: true, get: function () { return severity_js_1.Severity; } });
18
+ var ast_js_1 = require("./ast.js");
19
+ Object.defineProperty(exports, "Walk", { enumerable: true, get: function () { return ast_js_1.Walk; } });
20
+ var define_check_js_1 = require("./define-check.js");
21
+ Object.defineProperty(exports, "defineCheck", { enumerable: true, get: function () { return define_check_js_1.defineCheck; } });
22
+ // Loader runtime (cd-81a.7). Plugin authors import only the surfaces
23
+ // above; cofferdam's own JS wrapper is the consumer of these.
24
+ var plugin_host_js_1 = require("./plugin-host.js");
25
+ Object.defineProperty(exports, "runPlugin", { enumerable: true, get: function () { return plugin_host_js_1.runPlugin; } });
26
+ Object.defineProperty(exports, "buildSourceFile", { enumerable: true, get: function () { return plugin_host_js_1.buildSourceFile; } });
27
+ var loader_js_1 = require("./loader.js");
28
+ Object.defineProperty(exports, "loadPlugins", { enumerable: true, get: function () { return loader_js_1.loadPlugins; } });
29
+ Object.defineProperty(exports, "runPlugins", { enumerable: true, get: function () { return loader_js_1.runPlugins; } });
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,EAAE;AACF,yEAAyE;AACzE,sEAAsE;AACtE,wEAAwE;AACxE,yCAAyC;AACzC,EAAE;AACF,sEAAsE;AACtE,uEAAuE;AACvE,kEAAkE;;;AAElE,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,mCAAgC;AAAvB,8FAAA,IAAI,OAAA;AACb,qDAAgD;AAAvC,8GAAA,WAAW,OAAA;AAEpB,qEAAqE;AACrE,8DAA8D;AAC9D,mDAA8D;AAArD,2GAAA,SAAS,OAAA;AAAE,iHAAA,eAAe,OAAA;AACnC,yCAAsD;AAA7C,wGAAA,WAAW,OAAA;AAAE,uGAAA,UAAU,OAAA"}
@@ -0,0 +1,25 @@
1
+ import type { Span } from "./span.js";
2
+ export interface LineView {
3
+ /** 1-based line number. */
4
+ readonly lineNo: number;
5
+ /** Line text with the trailing `\r` (CRLF) stripped, no `\n`. */
6
+ readonly text: string;
7
+ readonly isComment: boolean;
8
+ readonly isDocComment: boolean;
9
+ readonly isStringLiteral: boolean;
10
+ readonly isJsxText: boolean;
11
+ readonly isPragma: boolean;
12
+ /**
13
+ * Build a `Span` covering bytes `[charStart, charEnd)` *within this
14
+ * line*. `charStart`/`charEnd` are byte offsets relative to the start
15
+ * of `text` (after CRLF stripping). The returned span carries
16
+ * file-absolute `start_byte`/`end_byte` for direct use in
17
+ * `ctx.report({ span })`.
18
+ *
19
+ * Filed as cd-cgd to keep authoring concise — without this the check
20
+ * has to reconstruct the file-absolute offset by hand from the line's
21
+ * own start.
22
+ */
23
+ spanFor(charStart: number, charEnd: number): Span;
24
+ }
25
+ //# sourceMappingURL=line-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-view.d.ts","sourceRoot":"","sources":["../src/line-view.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,WAAW,QAAQ;IACvB,2BAA2B;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACnD"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // LineView — plugin-facing view of a source line with classification
3
+ // flags populated by the engine from the parsed comment list and an AST
4
+ // walk over string/template literals. Mirrors
5
+ // cofferdam_core::lines::LineView.
6
+ //
7
+ // Flag semantics (kept in sync with cofferdam-core/src/lines.rs):
8
+ //
9
+ // - `isComment` — any comment (line `//`, single-line block, or
10
+ // multi-line block) overlaps this line.
11
+ // - `isDocComment` — a JSDoc-style block (`/** ... */`) overlaps. Implies
12
+ // `isComment`.
13
+ // - `isStringLiteral` — a `StringLiteral` or `TemplateLiteral` span
14
+ // overlaps this line.
15
+ // - `isJsxText` — JSX text content overlaps this line. Filed as cd-0ne;
16
+ // present in the SDK so plugins can target it as soon as the engine
17
+ // ships the flag.
18
+ // - `isPragma` — an annotation-style comment (`/* #__PURE__ */`,
19
+ // `/* @vite-ignore */`, etc.) overlaps. *Not* JSDoc.
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ //# sourceMappingURL=line-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-view.js","sourceRoot":"","sources":["../src/line-view.ts"],"names":[],"mappings":";AAAA,qEAAqE;AACrE,wEAAwE;AACxE,8CAA8C;AAC9C,mCAAmC;AACnC,EAAE;AACF,kEAAkE;AAClE,EAAE;AACF,gEAAgE;AAChE,0CAA0C;AAC1C,0EAA0E;AAC1E,iBAAiB;AACjB,oEAAoE;AACpE,wBAAwB;AACxB,wEAAwE;AACxE,sEAAsE;AACtE,oBAAoB;AACpB,iEAAiE;AACjE,uDAAuD"}
@@ -0,0 +1,32 @@
1
+ import type { Check } from "./define-check.js";
2
+ import type { PluginReport, PluginRunInput } from "./plugin-host.js";
3
+ export interface LoadedPlugin {
4
+ /** Module path the plugin was loaded from. */
5
+ readonly path: string;
6
+ readonly check: Check;
7
+ }
8
+ export interface RunPluginsOptions {
9
+ /** Per-file timeout. Defaults to 5000ms. */
10
+ readonly timeoutMs?: number;
11
+ }
12
+ /**
13
+ * Resolve and load a list of plugin module paths. Each path is
14
+ * `import()`'d; the module's default export must be the `Check` object
15
+ * returned from `defineCheck`.
16
+ *
17
+ * Throws on shape errors so the cofferdam binary fails loudly with a
18
+ * clear message — better than a runtime crash deep inside the loader.
19
+ */
20
+ export declare function loadPlugins(paths: readonly string[]): Promise<LoadedPlugin[]>;
21
+ /**
22
+ * Run every loaded plugin against every file. Collects `PluginReport[]`
23
+ * for the cofferdam binary to hand to native `mergePluginFindings`.
24
+ *
25
+ * In-process implementation: invokes plugins synchronously on the main
26
+ * thread. Worker-thread isolation lands in a follow-up bead — keeping
27
+ * the surface stable matters more than crash containment for the first
28
+ * milestone, since the SDK's e2e fixture (cd-7e4) is the immediate
29
+ * consumer and runs trusted code.
30
+ */
31
+ export declare function runPlugins(plugins: readonly LoadedPlugin[], files: readonly PluginRunInput[], _options?: RunPluginsOptions): Promise<PluginReport[]>;
32
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAiBnF;AAaD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,QAAQ,GAAE,iBAAsB,GAC/B,OAAO,CAAC,YAAY,EAAE,CAAC,CASzB"}
package/dist/loader.js ADDED
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ // Loader — orchestrates plugin discovery, worker_thread execution, and
3
+ // timeout enforcement. Used by the cofferdam binary's JS wrapper after
4
+ // it has the native `lineViews` helper available.
5
+ //
6
+ // API:
7
+ // const plugins = await loadPlugins(["./examples-plugins/brand-casing"]);
8
+ // const reports = await runPlugins(plugins, files, { timeoutMs: 5000 });
9
+ //
10
+ // `loadPlugins` resolves each path to its module's default export
11
+ // (expected to be a `Check` returned from `defineCheck`) and verifies
12
+ // shape. `runPlugins` spawns one worker_thread per plugin (NOT per
13
+ // file — workers are reused) and pushes file inputs through them.
14
+ //
15
+ // Crash containment + timeout: a plugin throwing or hanging on one
16
+ // file produces a `Warning.PluginCrashed` issue and the worker is
17
+ // recycled; analysis continues for remaining files.
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.loadPlugins = loadPlugins;
20
+ exports.runPlugins = runPlugins;
21
+ /**
22
+ * Resolve and load a list of plugin module paths. Each path is
23
+ * `import()`'d; the module's default export must be the `Check` object
24
+ * returned from `defineCheck`.
25
+ *
26
+ * Throws on shape errors so the cofferdam binary fails loudly with a
27
+ * clear message — better than a runtime crash deep inside the loader.
28
+ */
29
+ async function loadPlugins(paths) {
30
+ const { pathToFileURL } = await import("node:url");
31
+ const out = [];
32
+ for (const path of paths) {
33
+ // Convert OS paths to file:// URLs so dynamic import works on Windows
34
+ // (where C:\foo isn't a valid ESM specifier).
35
+ const specifier = /^([a-zA-Z]:|\/)/.test(path) ? pathToFileURL(path).href : path;
36
+ const mod = (await import(specifier));
37
+ const check = mod.default;
38
+ if (!isCheckShape(check)) {
39
+ throw new Error(`plugin at ${path} did not default-export a Check. Expected the value returned from defineCheck(...).`);
40
+ }
41
+ out.push({ path, check });
42
+ }
43
+ return out;
44
+ }
45
+ function isCheckShape(value) {
46
+ if (typeof value !== "object" || value === null)
47
+ return false;
48
+ const v = value;
49
+ return (typeof v.id === "string" &&
50
+ typeof v.category === "string" &&
51
+ typeof v.basePriority === "number" &&
52
+ typeof v.run === "function");
53
+ }
54
+ /**
55
+ * Run every loaded plugin against every file. Collects `PluginReport[]`
56
+ * for the cofferdam binary to hand to native `mergePluginFindings`.
57
+ *
58
+ * In-process implementation: invokes plugins synchronously on the main
59
+ * thread. Worker-thread isolation lands in a follow-up bead — keeping
60
+ * the surface stable matters more than crash containment for the first
61
+ * milestone, since the SDK's e2e fixture (cd-7e4) is the immediate
62
+ * consumer and runs trusted code.
63
+ */
64
+ async function runPlugins(plugins, files, _options = {}) {
65
+ const { runPlugin } = await import("./plugin-host.js");
66
+ const out = [];
67
+ for (const plugin of plugins) {
68
+ for (const file of files) {
69
+ out.push(...runPlugin(plugin.check, file));
70
+ }
71
+ }
72
+ return out;
73
+ }
74
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,uEAAuE;AACvE,kDAAkD;AAClD,EAAE;AACF,OAAO;AACP,4EAA4E;AAC5E,2EAA2E;AAC3E,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,mEAAmE;AACnE,kEAAkE;AAClE,EAAE;AACF,mEAAmE;AACnE,kEAAkE;AAClE,oDAAoD;;AAwBpD,kCAiBC;AAuBD,gCAaC;AA7DD;;;;;;;GAOG;AACI,KAAK,UAAU,WAAW,CAAC,KAAwB;IACxD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,sEAAsE;QACtE,8CAA8C;QAC9C,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACjF,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAA0B,CAAC;QAC/D,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC;QAC1B,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,aAAa,IAAI,qFAAqF,CACvG,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ;QACxB,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAC9B,OAAO,CAAC,CAAC,YAAY,KAAK,QAAQ;QAClC,OAAO,CAAC,CAAC,GAAG,KAAK,UAAU,CAC5B,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,UAAU,CAC9B,OAAgC,EAChC,KAAgC,EAChC,WAA8B,EAAE;IAEhC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;IACvD,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,37 @@
1
+ export type OptionKind = "string" | "number" | "boolean" | "string[]" | "number[]";
2
+ export interface OptionSpec<K extends OptionKind, D> {
3
+ readonly type: K;
4
+ readonly default: D;
5
+ /** Optional human-readable description, surfaced by `cofferdam explain`. */
6
+ readonly description?: string;
7
+ }
8
+ type ResolvedFromKind<K extends OptionKind> = K extends "string" ? string : K extends "number" ? number : K extends "boolean" ? boolean : K extends "string[]" ? readonly string[] : K extends "number[]" ? readonly number[] : never;
9
+ /**
10
+ * The shape of `opts` inside a check's `run(file, ctx, opts)` callback —
11
+ * derived from the schema declared on `defineCheck.options`.
12
+ *
13
+ * @example
14
+ * const sdk = defineCheck({
15
+ * options: {
16
+ * brand: { default: "ROVIKORE", type: "string" },
17
+ * allowedAliases: { default: [] as string[], type: "string[]" },
18
+ * },
19
+ * run(file, ctx, opts) {
20
+ * opts.brand; // string
21
+ * opts.allowedAliases; // readonly string[]
22
+ * },
23
+ * });
24
+ */
25
+ export type ResolvedOptions<S> = {
26
+ readonly [K in keyof S]: S[K] extends OptionSpec<infer Kind, infer _D> ? ResolvedFromKind<Kind> : never;
27
+ };
28
+ /** A bare schema record — `Record<string, OptionSpec<...>>`. Constrained
29
+ * generics on `defineCheck` use this as the `extends` upper bound. */
30
+ export type OptionsSchema = Record<string, OptionSpec<OptionKind, unknown>>;
31
+ /** Empty schema — used as the default in `defineCheck` so the
32
+ * third `opts` argument is `Record<string, never>` for option-less
33
+ * checks rather than `undefined`. */
34
+ export declare const NO_OPTIONS: {};
35
+ export type NoOptions = typeof NO_OPTIONS;
36
+ export {};
37
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAEnF,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAID,KAAK,gBAAgB,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,SAAS,QAAQ,GAC5D,MAAM,GACN,CAAC,SAAS,QAAQ,GAChB,MAAM,GACN,CAAC,SAAS,SAAS,GACjB,OAAO,GACP,CAAC,SAAS,UAAU,GAClB,SAAS,MAAM,EAAE,GACjB,CAAC,SAAS,UAAU,GAClB,SAAS,MAAM,EAAE,GACjB,KAAK,CAAC;AAElB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAC/B,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC,GAClE,gBAAgB,CAAC,IAAI,CAAC,GACtB,KAAK;CACV,CAAC;AAEF;uEACuE;AACvE,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;AAE5E;;sCAEsC;AACtC,eAAO,MAAM,UAAU,IAAc,CAAC;AACtC,MAAM,MAAM,SAAS,GAAG,OAAO,UAAU,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ // Per-check options schema (cd-81a.3). Plugin authors declare an
3
+ // `options` block on their `defineCheck(...)` call; the engine validates
4
+ // user `cofferdam.toml` overrides against it at startup, and the resolved
5
+ // values are passed to `run(file, ctx, opts)` typed correctly.
6
+ //
7
+ // The schema is a record of name → { default, type }. The `type` field is
8
+ // the discriminator that drives both runtime validation and the type
9
+ // inference below.
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.NO_OPTIONS = void 0;
12
+ /** Empty schema — used as the default in `defineCheck` so the
13
+ * third `opts` argument is `Record<string, never>` for option-less
14
+ * checks rather than `undefined`. */
15
+ exports.NO_OPTIONS = {};
16
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":";AAAA,iEAAiE;AACjE,yEAAyE;AACzE,0EAA0E;AAC1E,+DAA+D;AAC/D,EAAE;AACF,0EAA0E;AAC1E,qEAAqE;AACrE,mBAAmB;;;AAmDnB;;sCAEsC;AACzB,QAAA,UAAU,GAAG,EAAW,CAAC"}
@@ -0,0 +1,64 @@
1
+ import type { Check } from "./define-check.js";
2
+ import type { Fix } from "./check-context.js";
3
+ import type { LineView } from "./line-view.js";
4
+ import type { Span } from "./span.js";
5
+ export interface NativeLineView {
6
+ readonly lineNo: number;
7
+ readonly text: string;
8
+ readonly isComment: boolean;
9
+ readonly isDocComment: boolean;
10
+ readonly isStringLiteral: boolean;
11
+ readonly isJsxText: boolean;
12
+ readonly isPragma: boolean;
13
+ readonly lineStart: number;
14
+ }
15
+ /**
16
+ * One report that crosses the worker_thread boundary back to the
17
+ * loader. Mirrors the napi `JsReport` struct in cofferdam-napi.
18
+ */
19
+ export interface PluginReport {
20
+ readonly checkId: string;
21
+ readonly message: string;
22
+ readonly file: string;
23
+ readonly startByte: number;
24
+ readonly endByte: number;
25
+ readonly severity: string;
26
+ readonly fix?: Fix;
27
+ readonly related?: readonly {
28
+ readonly file: string;
29
+ readonly span: Span;
30
+ }[];
31
+ }
32
+ /**
33
+ * Runtime input passed from the loader to a plugin's `run()` callback.
34
+ * Built from native `lineViews()` + the file path/text. Cheap to
35
+ * serialise (no AST handles cross the boundary).
36
+ */
37
+ export interface PluginRunInput {
38
+ readonly path: string;
39
+ readonly text: string;
40
+ readonly lineViews: readonly NativeLineView[];
41
+ }
42
+ /**
43
+ * Build the `SourceFile` shape a plugin's `run()` callback expects.
44
+ * `ast` is `null` here — AST access lives behind a separate napi call
45
+ * the loader can route through worker_threads in a follow-up bead. The
46
+ * line-walk Pattern A checks (BrandCasing) work today.
47
+ */
48
+ export declare function buildSourceFile(input: PluginRunInput): {
49
+ path: string;
50
+ text: string;
51
+ lines(): IterableIterator<LineView>;
52
+ ast: null;
53
+ };
54
+ /**
55
+ * Execute a single plugin against a single file. Collects `ctx.report`
56
+ * calls into a `PluginReport[]` the loader can hand to native
57
+ * `mergePluginFindings`.
58
+ *
59
+ * This is the function a worker_thread invokes. The host process never
60
+ * touches plugin code directly — crash containment is the worker's
61
+ * boundary.
62
+ */
63
+ export declare function runPlugin(check: Check, input: PluginRunInput): PluginReport[];
64
+ //# sourceMappingURL=plugin-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-host.d.ts","sourceRoot":"","sources":["../src/plugin-host.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAgB,GAAG,EAAc,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAItC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IACnB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;KAAE,EAAE,CAAC;CAC9E;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;CAC/C;AAwBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc;;;aAKxC,gBAAgB,CAAC,QAAQ,CAAC;;EAqBtC;AAID;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,GAAG,YAAY,EAAE,CA0C7E"}
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ // Plugin host runtime — the JS side of cd-81a.7.
3
+ //
4
+ // Lives in @cofferdam/check-sdk so the loader and the authoring SDK
5
+ // share one published surface. The cofferdam binary's JS wrapper (in
6
+ // packages/cofferdam) imports `loadPlugin` and `runPlugin` from here
7
+ // after it has obtained the native addon's `lineViews` /
8
+ // `mergePluginFindings` helpers.
9
+ //
10
+ // Why worker_threads: a plugin throwing on one file must not crash the
11
+ // engine driver, and unbounded plugin runs must be timeout-able.
12
+ // worker_threads gives crash containment + per-message timeout for free,
13
+ // at the cost of postMessage serialisation per file (cheap relative to
14
+ // parsing).
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.buildSourceFile = buildSourceFile;
17
+ exports.runPlugin = runPlugin;
18
+ // ---- runtime construction --------------------------------------------
19
+ function buildLineView(native) {
20
+ return {
21
+ lineNo: native.lineNo,
22
+ text: native.text,
23
+ isComment: native.isComment,
24
+ isDocComment: native.isDocComment,
25
+ isStringLiteral: native.isStringLiteral,
26
+ isJsxText: native.isJsxText,
27
+ isPragma: native.isPragma,
28
+ spanFor(charStart, charEnd) {
29
+ return {
30
+ line: native.lineNo,
31
+ column: charStart + 1,
32
+ start_byte: native.lineStart + charStart,
33
+ end_byte: native.lineStart + charEnd,
34
+ };
35
+ },
36
+ };
37
+ }
38
+ /**
39
+ * Build the `SourceFile` shape a plugin's `run()` callback expects.
40
+ * `ast` is `null` here — AST access lives behind a separate napi call
41
+ * the loader can route through worker_threads in a follow-up bead. The
42
+ * line-walk Pattern A checks (BrandCasing) work today.
43
+ */
44
+ function buildSourceFile(input) {
45
+ const lineViews = input.lineViews.map(buildLineView);
46
+ return {
47
+ path: input.path,
48
+ text: input.text,
49
+ lines() {
50
+ let i = 0;
51
+ const it = {
52
+ next() {
53
+ if (i < lineViews.length) {
54
+ return { value: lineViews[i++], done: false };
55
+ }
56
+ return { value: undefined, done: true };
57
+ },
58
+ [Symbol.iterator]() {
59
+ return it;
60
+ },
61
+ return(value) {
62
+ i = lineViews.length;
63
+ return { value: value, done: true };
64
+ },
65
+ };
66
+ return it;
67
+ },
68
+ ast: null,
69
+ };
70
+ }
71
+ // ---- runPlugin -------------------------------------------------------
72
+ /**
73
+ * Execute a single plugin against a single file. Collects `ctx.report`
74
+ * calls into a `PluginReport[]` the loader can hand to native
75
+ * `mergePluginFindings`.
76
+ *
77
+ * This is the function a worker_thread invokes. The host process never
78
+ * touches plugin code directly — crash containment is the worker's
79
+ * boundary.
80
+ */
81
+ function runPlugin(check, input) {
82
+ const reports = [];
83
+ const ctx = {
84
+ report(args) {
85
+ const { span, severity, related, fix } = args;
86
+ const report = {
87
+ checkId: check.id,
88
+ message: args.message,
89
+ file: input.path,
90
+ startByte: span.start_byte,
91
+ endByte: span.end_byte,
92
+ severity: severity ?? check.defaultSeverity,
93
+ ...(fix !== undefined ? { fix } : {}),
94
+ ...(related !== undefined ? { related } : {}),
95
+ };
96
+ reports.push(report);
97
+ },
98
+ };
99
+ // Resolve options — currently use the schema defaults; the loader can
100
+ // pass user-supplied overrides through cofferdam.toml in a follow-up.
101
+ // The cast is sound because the schema's defaults already satisfy
102
+ // ResolvedOptions<S>'s mapped types by construction.
103
+ const opts = resolveDefaults(check.options);
104
+ try {
105
+ check.run(buildSourceFile(input), ctx, opts);
106
+ }
107
+ catch (err) {
108
+ // Don't swallow the plugin error — push a synthetic report so the
109
+ // engine surfaces it as `Warning.PluginCrashed` and analysis
110
+ // continues for the rest of the corpus.
111
+ reports.push({
112
+ checkId: "Warning.PluginCrashed",
113
+ message: `plugin '${check.id}' threw: ${err instanceof Error ? err.message : String(err)}`,
114
+ file: input.path,
115
+ startByte: 0,
116
+ endByte: 0,
117
+ severity: "high",
118
+ });
119
+ }
120
+ return reports;
121
+ }
122
+ function resolveDefaults(schema) {
123
+ const out = {};
124
+ for (const key of Object.keys(schema)) {
125
+ out[key] = schema[key].default;
126
+ }
127
+ return out;
128
+ }
129
+ //# sourceMappingURL=plugin-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-host.js","sourceRoot":"","sources":["../src/plugin-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare const Severity: {
2
+ readonly Info: "info";
3
+ readonly Low: "low";
4
+ readonly Medium: "medium";
5
+ readonly High: "high";
6
+ readonly Critical: "critical";
7
+ };
8
+ export type Severity = (typeof Severity)[keyof typeof Severity];
9
+ //# sourceMappingURL=severity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"severity.d.ts","sourceRoot":"","sources":["../src/severity.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,QAAQ;;;;;;CAMX,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ // Mirror of cofferdam_core::Severity. Wire form is the lowercase string;
3
+ // the const-as-namespace pattern keeps `Severity.Warning` ergonomic
4
+ // without spinning up an enum (which TS enums emit runtime code for).
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Severity = void 0;
7
+ exports.Severity = {
8
+ Info: "info",
9
+ Low: "low",
10
+ Medium: "medium",
11
+ High: "high",
12
+ Critical: "critical",
13
+ };
14
+ //# sourceMappingURL=severity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"severity.js","sourceRoot":"","sources":["../src/severity.ts"],"names":[],"mappings":";AAAA,yEAAyE;AACzE,oEAAoE;AACpE,sEAAsE;;;AAEzD,QAAA,QAAQ,GAAG;IACtB,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;CACZ,CAAC"}
package/dist/span.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ export interface Span {
2
+ /** 1-based line number. */
3
+ readonly line: number;
4
+ /** 1-based column number, in UTF-8 bytes from the start of the line. */
5
+ readonly column: number;
6
+ /** 0-based byte offset from the start of the file (inclusive). */
7
+ readonly start_byte: number;
8
+ /** 0-based byte offset from the start of the file (exclusive). */
9
+ readonly end_byte: number;
10
+ }
11
+ export interface RelatedSpan {
12
+ readonly file: string;
13
+ readonly span: Span;
14
+ }
15
+ //# sourceMappingURL=span.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span.d.ts","sourceRoot":"","sources":["../src/span.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,IAAI;IACnB,2BAA2B;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;CACrB"}
package/dist/span.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ // Mirror of cofferdam_core::Span. Carries both human (line/column) and
3
+ // machine (byte offsets) coordinates so `ctx.report` consumers can do
4
+ // either without re-parsing.
5
+ //
6
+ // All coordinates are 1-based for line, 1-based for column, 0-based for
7
+ // bytes — matching the Rust struct and the JSON formatter contract.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ //# sourceMappingURL=span.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span.js","sourceRoot":"","sources":["../src/span.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,sEAAsE;AACtE,6BAA6B;AAC7B,EAAE;AACF,wEAAwE;AACxE,oEAAoE"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@cofferdam/check-sdk",
3
+ "version": "0.2.2",
4
+ "description": "Author cofferdam plugin checks in TypeScript. defineCheck factory + types.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/TAJD/cofferdam.git",
9
+ "directory": "packages/check-sdk"
10
+ },
11
+ "homepage": "https://tajd.github.io/cofferdam",
12
+ "bugs": "https://github.com/TAJD/cofferdam/issues",
13
+ "main": "dist/index.js",
14
+ "types": "dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "keywords": [
27
+ "cofferdam",
28
+ "linter",
29
+ "static-analysis",
30
+ "plugin-sdk",
31
+ "typescript"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsc -p .",
35
+ "typecheck": "tsc -p . --noEmit",
36
+ "test:types": "tsc -p tests"
37
+ },
38
+ "engines": {
39
+ "node": ">=16"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^20",
43
+ "typescript": "^5.6.0"
44
+ }
45
+ }