@sarj/eslint-plugin 15.26.0 → 15.26.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.
- package/dist/index.cjs +69 -16
- package/dist/index.d.cts +6 -8
- package/dist/index.d.ts +6 -8
- package/dist/index.js +69 -16
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -96,6 +96,7 @@ ${missing.join("\n")}`);
|
|
|
96
96
|
rationale: spec.rationale,
|
|
97
97
|
remediation: spec.remediation,
|
|
98
98
|
category: spec.category,
|
|
99
|
+
defaultLevel: spec.defaultLevel,
|
|
99
100
|
languages: spec.languages,
|
|
100
101
|
autofix: spec.autofix,
|
|
101
102
|
aliases: spec.aliases,
|
|
@@ -177,6 +178,7 @@ function nativeSpec(config, documentation) {
|
|
|
177
178
|
rationale: documentation.rationale,
|
|
178
179
|
remediation: documentation.remediation,
|
|
179
180
|
category: documentation.category,
|
|
181
|
+
defaultLevel: documentation.defaultLevel ?? "error",
|
|
180
182
|
languages: [...documentation.languages ?? ["typescript"]],
|
|
181
183
|
autofix: documentation.autofix ?? "none",
|
|
182
184
|
aliases,
|
|
@@ -1020,6 +1022,7 @@ var require_button_accessible_name_default = createRule({
|
|
|
1020
1022
|
// src/rules/require-camelcase-properties.ts
|
|
1021
1023
|
var import_utils9 = require("@typescript-eslint/utils");
|
|
1022
1024
|
var REQUIRE_CAMELCASE_PROPERTIES_DOCUMENTATION = {
|
|
1025
|
+
defaultLevel: "warning",
|
|
1023
1026
|
summary: "Require unquoted lower snake_case TypeScript properties and dot access to use camelCase.",
|
|
1024
1027
|
rationale: "Unquoted snake_case property syntax makes external wire naming indistinguishable from application-domain naming and lets inconsistent contracts spread through typed code.",
|
|
1025
1028
|
remediation: "Rename application properties to camelCase. At an external wire boundary, make the exception explicit with a quoted key and bracket access.",
|
|
@@ -2348,6 +2351,7 @@ var FILE_SELF_REFERENCE_RE = /\bthis (?:file|module)\b/iu;
|
|
|
2348
2351
|
var CHANGE_HISTORY_RE = /\b(?:used to|previously|histor(?:y|ical)|keep moving|models? (?:move|moved|change|changed)|still under review)\b/iu;
|
|
2349
2352
|
var IMPLEMENTATION_MAP_RE = /\b(?:everything (?:above|below)|nothing else|only thing|write half|maps? between|seam between)\b/iu;
|
|
2350
2353
|
var EXCESSIVE_COMMENTARY_DOCUMENTATION = {
|
|
2354
|
+
defaultLevel: "warning",
|
|
2351
2355
|
summary: "Flag long standalone implementation commentary that should be expressed by code.",
|
|
2352
2356
|
rationale: "Narrative implementation paragraphs compete with the code and can drift independently from behavior.",
|
|
2353
2357
|
remediation: "Delete narration and clarify names, types, or structure; retain only durable constraints and external contracts.",
|
|
@@ -4145,12 +4149,12 @@ function createSqlListener(handler) {
|
|
|
4145
4149
|
|
|
4146
4150
|
// src/rules/no-dynamic-sql.ts
|
|
4147
4151
|
var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
4148
|
-
summary: "Disallow runtime values
|
|
4149
|
-
rationale: "Embedding runtime values in SQL bypasses driver parameterization and can introduce injection defects or unstable query plans.",
|
|
4150
|
-
remediation: "
|
|
4152
|
+
summary: "Disallow runtime values and fragments interpolated into SQL passed to statement-execution methods.",
|
|
4153
|
+
rationale: "Embedding runtime values or fragments in SQL bypasses driver parameterization or an explicit allowlist and can introduce injection defects or unstable query plans.",
|
|
4154
|
+
remediation: "Bind data values through SQL placeholders. Select non-bindable identifiers or clauses from fixed, reviewed fragments instead of interpolating runtime text.",
|
|
4151
4155
|
category: "security",
|
|
4152
4156
|
limitations: [
|
|
4153
|
-
"
|
|
4157
|
+
"Template and concatenation fragments are inspected at recognized execution calls. Double-quoted identifiers, comments, and dollar strings are excluded; this is not a general SQL injection detector.",
|
|
4154
4158
|
"Literal fragments, legacy uppercase fragment names, and parameterizing tagged templates are exempt; uppercase spelling does not prove a value is static.",
|
|
4155
4159
|
"The bounded lexer recognizes doubled quotes, comments, and PostgreSQL dollar strings; dialect-specific escape modes and SQL generated through other APIs require separate security review."
|
|
4156
4160
|
],
|
|
@@ -4172,6 +4176,26 @@ var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
|
4172
4176
|
focusPath: "src/users.ts",
|
|
4173
4177
|
expectedCount: 1,
|
|
4174
4178
|
public: true
|
|
4179
|
+
},
|
|
4180
|
+
{
|
|
4181
|
+
id: "bound-unquoted-value",
|
|
4182
|
+
scenarioId: "unquoted",
|
|
4183
|
+
title: "An unquoted runtime value is bound separately",
|
|
4184
|
+
outcome: "no-match",
|
|
4185
|
+
files: [{ path: "src/users.ts", source: "db.prepare('select id from users where id = ?').bind(userId);" }],
|
|
4186
|
+
focusPath: "src/users.ts",
|
|
4187
|
+
expectedCount: 0,
|
|
4188
|
+
public: true
|
|
4189
|
+
},
|
|
4190
|
+
{
|
|
4191
|
+
id: "unquoted-runtime-fragment",
|
|
4192
|
+
scenarioId: "unquoted",
|
|
4193
|
+
title: "A runtime fragment is interpolated into SQL",
|
|
4194
|
+
outcome: "match",
|
|
4195
|
+
files: [{ path: "src/users.ts", source: "db.prepare(`select id from users where id = ${userId}`);" }],
|
|
4196
|
+
focusPath: "src/users.ts",
|
|
4197
|
+
expectedCount: 1,
|
|
4198
|
+
public: true
|
|
4175
4199
|
}
|
|
4176
4200
|
]
|
|
4177
4201
|
};
|
|
@@ -4194,14 +4218,21 @@ function isStaticFragment(expression) {
|
|
|
4194
4218
|
}
|
|
4195
4219
|
function runtimeInterpolations(template) {
|
|
4196
4220
|
const parts = template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw);
|
|
4197
|
-
const
|
|
4221
|
+
const statement = parts.join(RUNTIME_MARKER);
|
|
4222
|
+
const ranges = sqlSingleQuotedRanges(statement);
|
|
4223
|
+
const visible = stripSqlNoise(statement);
|
|
4198
4224
|
let offset = 0;
|
|
4199
|
-
return template.expressions.
|
|
4225
|
+
return template.expressions.flatMap(
|
|
4200
4226
|
(expression, index) => {
|
|
4201
4227
|
offset += parts[index]?.length ?? 0;
|
|
4202
4228
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
4229
|
+
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
4203
4230
|
offset += RUNTIME_MARKER.length;
|
|
4204
|
-
|
|
4231
|
+
if (isStaticFragment(expression)) return [];
|
|
4232
|
+
if (inValue && endsWithSqlQuote(parts[index] ?? "") && startsWithSqlQuote(parts[index + 1] ?? "")) {
|
|
4233
|
+
return [{ expression, messageId: "dynamicSql" }];
|
|
4234
|
+
}
|
|
4235
|
+
return unquoted ? [{ expression, messageId: "dynamicFragment" }] : [];
|
|
4205
4236
|
}
|
|
4206
4237
|
);
|
|
4207
4238
|
}
|
|
@@ -4232,16 +4263,21 @@ function runtimeConcatOperands(node) {
|
|
|
4232
4263
|
return [];
|
|
4233
4264
|
}
|
|
4234
4265
|
const parts = operands.map((operand) => staticLiteralText(operand) ?? RUNTIME_MARKER);
|
|
4235
|
-
const
|
|
4266
|
+
const statement = parts.join("");
|
|
4267
|
+
const ranges = sqlSingleQuotedRanges(statement);
|
|
4268
|
+
const visible = stripSqlNoise(statement);
|
|
4236
4269
|
let offset = 0;
|
|
4237
|
-
return operands.
|
|
4270
|
+
return operands.flatMap((operand, index) => {
|
|
4238
4271
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
4272
|
+
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
4239
4273
|
offset += parts[index]?.length ?? 0;
|
|
4240
|
-
if (
|
|
4241
|
-
if (isStaticFragment(operand)) return false;
|
|
4274
|
+
if (isStaticFragment(operand)) return [];
|
|
4242
4275
|
const before = operands[index - 1];
|
|
4243
4276
|
const after = operands[index + 1];
|
|
4244
|
-
|
|
4277
|
+
if (inValue && before !== void 0 && after !== void 0 && endsWithSqlQuote(staticLiteralText(before) ?? "") && startsWithSqlQuote(staticLiteralText(after) ?? "")) {
|
|
4278
|
+
return [{ expression: operand, messageId: "dynamicSql" }];
|
|
4279
|
+
}
|
|
4280
|
+
return unquoted ? [{ expression: operand, messageId: "dynamicFragment" }] : [];
|
|
4245
4281
|
});
|
|
4246
4282
|
}
|
|
4247
4283
|
function concatOperands(node) {
|
|
@@ -4297,7 +4333,8 @@ var no_dynamic_sql_default = createRule({
|
|
|
4297
4333
|
}
|
|
4298
4334
|
],
|
|
4299
4335
|
messages: {
|
|
4300
|
-
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately."
|
|
4336
|
+
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately.",
|
|
4337
|
+
dynamicFragment: "Runtime fragment interpolated into SQL passed to `{{method}}()`. Bind data values; select non-bindable SQL fragments from fixed, reviewed alternatives."
|
|
4301
4338
|
}
|
|
4302
4339
|
},
|
|
4303
4340
|
defaultOptions: [{}],
|
|
@@ -4316,8 +4353,8 @@ var no_dynamic_sql_default = createRule({
|
|
|
4316
4353
|
const offenders = statement.type === import_utils27.AST_NODE_TYPES.TemplateLiteral ? runtimeInterpolations(statement) : runtimeConcatOperands(statement);
|
|
4317
4354
|
for (const offender of offenders) {
|
|
4318
4355
|
context.report({
|
|
4319
|
-
node: offender,
|
|
4320
|
-
messageId:
|
|
4356
|
+
node: offender.expression,
|
|
4357
|
+
messageId: offender.messageId,
|
|
4321
4358
|
data: { method }
|
|
4322
4359
|
});
|
|
4323
4360
|
}
|
|
@@ -5950,6 +5987,7 @@ function branchTerminates(branch) {
|
|
|
5950
5987
|
var import_utils36 = require("@typescript-eslint/utils");
|
|
5951
5988
|
var import_typescript5 = __toESM(require("typescript"), 1);
|
|
5952
5989
|
var NO_JSON_STRINGIFY_OBJECT_EQUALITY_DOCUMENTATION = {
|
|
5990
|
+
defaultLevel: "warning",
|
|
5953
5991
|
summary: "Do not use JSON serialization as structural object equality.",
|
|
5954
5992
|
rationale: "JSON text equality depends on property insertion order and serialization behavior, so semantically equal objects can compare unequal and distinct values can collapse together.",
|
|
5955
5993
|
remediation: "Compare an explicit domain projection structurally, or use a reviewed canonical serializer when JSON semantics are required.",
|
|
@@ -7209,6 +7247,7 @@ var no_bare_return_from_test_catch_default = createRule({
|
|
|
7209
7247
|
// src/rules/no-bespoke-api-case-conversion.ts
|
|
7210
7248
|
var import_utils45 = require("@typescript-eslint/utils");
|
|
7211
7249
|
var NO_BESPOKE_API_CASE_CONVERSION_DOCUMENTATION = {
|
|
7250
|
+
defaultLevel: "warning",
|
|
7212
7251
|
summary: "Review direct snake_case/camelCase mirror mappings on explicitly API-typed adapter values.",
|
|
7213
7252
|
rationale: "Duplicating wire-name translation can drift from an API client contract. When the SDK owns application-facing names, centralizing conversion avoids maintaining another mirror by hand.",
|
|
7214
7253
|
remediation: "Move wire-name ownership and case conversion into the generated SDK/model layer; keep application adapters on the generated typed surface.",
|
|
@@ -8955,6 +8994,7 @@ var WALL_NARRATION_RE2 = /^(?:(?:\d+[.)]|(?:phase|step)\s+\d+\s*:?)\s*)?(?:add|b
|
|
|
8955
8994
|
var WALL_CLUSTER_MAX_LINE_GAP = 8;
|
|
8956
8995
|
var WALL_CLUSTER_MIN_COMMENTS = 3;
|
|
8957
8996
|
var NO_RESTATED_COMMENT_DOCUMENTATION = {
|
|
8997
|
+
defaultLevel: "warning",
|
|
8958
8998
|
summary: "Flag short standalone comments that repeat the adjacent statement's identifiers.",
|
|
8959
8999
|
rationale: "A comment that only repeats code adds no context and can become stale independently.",
|
|
8960
9000
|
remediation: "Remove a genuine restatement; retain conditions, constraints, rationale, and information absent from the statement.",
|
|
@@ -9104,6 +9144,7 @@ function isRestatableLabel(body2) {
|
|
|
9104
9144
|
// src/rules/no-restated-jsdoc.ts
|
|
9105
9145
|
var import_utils57 = require("@typescript-eslint/utils");
|
|
9106
9146
|
var NO_RESTATED_JSDOC_DOCUMENTATION = {
|
|
9147
|
+
defaultLevel: "warning",
|
|
9107
9148
|
summary: "Flag JSDoc prose that appears to repeat declaration names without adding behavioral information.",
|
|
9108
9149
|
rationale: "Signature-only JSDoc duplicates type information and drifts without helping callers.",
|
|
9109
9150
|
remediation: "Delete the block or document behavior, constraints, failures, or context the signature cannot express.",
|
|
@@ -13663,6 +13704,7 @@ var prefer_input_group_search_default = createRule({
|
|
|
13663
13704
|
// src/rules/prefer-millisecond-control-duration-schema.ts
|
|
13664
13705
|
var import_utils84 = require("@typescript-eslint/utils");
|
|
13665
13706
|
var PREFER_MILLISECOND_CONTROL_DURATION_SCHEMA_DOCUMENTATION = {
|
|
13707
|
+
defaultLevel: "warning",
|
|
13666
13708
|
summary: "Require application-owned Zod control-duration fields to use millisecond granularity.",
|
|
13667
13709
|
rationale: "Second-granularity timeout and scheduling controls lose precision and invite implicit unit conversion at API boundaries. Encoding milliseconds in the schema keeps the unit explicit and composes with platform timing APIs.",
|
|
13668
13710
|
remediation: "Rename the field with an `Ms`/`_ms` suffix and express its bounds and default in milliseconds; update the owning API contract rather than converting in application code.",
|
|
@@ -15282,6 +15324,7 @@ var FUNCTION_TYPES9 = /* @__PURE__ */ new Set([
|
|
|
15282
15324
|
import_utils89.AST_NODE_TYPES.FunctionExpression
|
|
15283
15325
|
]);
|
|
15284
15326
|
var PREFER_MODULE_LEVEL_REFINED_SCHEMA_DOCUMENTATION = {
|
|
15327
|
+
defaultLevel: "warning",
|
|
15285
15328
|
summary: "Declare closed Zod scalar, format, and wrapper schemas at module scope.",
|
|
15286
15329
|
rationale: "A closed validation pipeline created inside a function is rebuilt on every invocation and obscures a reusable constraint.",
|
|
15287
15330
|
remediation: "Move the validation schema to module scope, name it with a PascalCase Schema suffix, and call parse on the shared schema.",
|
|
@@ -15555,6 +15598,7 @@ var prefer_module_level_refined_schema_default = createRule({
|
|
|
15555
15598
|
// src/rules/prefer-multi-value-zod-literal.ts
|
|
15556
15599
|
var import_utils90 = require("@typescript-eslint/utils");
|
|
15557
15600
|
var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
|
|
15601
|
+
defaultLevel: "warning",
|
|
15558
15602
|
summary: "Use the Zod 4 multi-value literal API instead of a union of literal schemas.",
|
|
15559
15603
|
rationale: "One multi-value literal expresses the same closed value domain without repeated schema wrappers.",
|
|
15560
15604
|
remediation: "Replace the union with z.literal([value1, value2, ...]).",
|
|
@@ -15741,6 +15785,7 @@ var prefer_named_callback_domain_default = createRule({
|
|
|
15741
15785
|
// src/rules/prefer-named-complex-return-type.ts
|
|
15742
15786
|
var import_utils92 = require("@typescript-eslint/utils");
|
|
15743
15787
|
var PREFER_NAMED_COMPLEX_RETURN_TYPE_DOCUMENTATION = {
|
|
15788
|
+
defaultLevel: "warning",
|
|
15744
15789
|
summary: "Prefer a named contract for structurally complex function return types.",
|
|
15745
15790
|
rationale: "A large inline return annotation hides a reusable domain concept and makes signatures difficult to scan.",
|
|
15746
15791
|
remediation: "Name the complex nested shape while preserving its generic wrappers and type parameters; reference the named contract from the return annotation.",
|
|
@@ -15901,6 +15946,7 @@ var prefer_native_random_uuid_default = createRule({
|
|
|
15901
15946
|
// src/rules/prefer-node-crypto-hash.ts
|
|
15902
15947
|
var import_utils94 = require("@typescript-eslint/utils");
|
|
15903
15948
|
var PREFER_NODE_CRYPTO_HASH_DOCUMENTATION = {
|
|
15949
|
+
defaultLevel: "warning",
|
|
15904
15950
|
summary: "Prefer the modern one-shot node:crypto hash API when streaming state is unnecessary.",
|
|
15905
15951
|
rationale: "A createHash-update-digest chain allocates mutable streaming state for a single in-memory value; Node's built-in hash function expresses the one-shot operation directly and can use its optimized fast path.",
|
|
15906
15952
|
remediation: "On a supported Node runtime, consider hash(algorithm, value, encoding). Preserve the output encoding explicitly: digest() returns a Buffer, while hash defaults to hex. Keep createHash for streams or multiple updates.",
|
|
@@ -16033,6 +16079,7 @@ function isCreateHashCall(node, directBindings, namespaceBindings, resolve2) {
|
|
|
16033
16079
|
// src/rules/prefer-node-fs-promises.ts
|
|
16034
16080
|
var import_utils95 = require("@typescript-eslint/utils");
|
|
16035
16081
|
var PREFER_NODE_FS_PROMISES_DOCUMENTATION = {
|
|
16082
|
+
defaultLevel: "warning",
|
|
16036
16083
|
summary: "Prefer promise-based Node.js filesystem APIs over synchronous calls in production modules.",
|
|
16037
16084
|
rationale: "Synchronous filesystem work blocks the event loop and can stall unrelated daemon, server, and worker tasks.",
|
|
16038
16085
|
remediation: "Import the promise API from node:fs/promises and await it; use FileHandle.sync only where a documented durability boundary requires it.",
|
|
@@ -17299,6 +17346,7 @@ function ifGuardDominates(use, branch, positive) {
|
|
|
17299
17346
|
// src/rules/prefer-shared-zod-enum.ts
|
|
17300
17347
|
var import_utils100 = require("@typescript-eslint/utils");
|
|
17301
17348
|
var PREFER_SHARED_ZOD_ENUM_DOCUMENTATION = {
|
|
17349
|
+
defaultLevel: "warning",
|
|
17302
17350
|
summary: "Give repeated literal Zod enum domains one reusable module-level schema.",
|
|
17303
17351
|
rationale: "Repeated literal domains hide a shared contract and allow equivalent fields to drift independently.",
|
|
17304
17352
|
remediation: "Declare a module-level named Zod enum schema and reuse it at each field or contract site.",
|
|
@@ -17386,6 +17434,7 @@ var prefer_shared_zod_enum_default = createRule({
|
|
|
17386
17434
|
// src/rules/prefer-switch-for-repeated-equality.ts
|
|
17387
17435
|
var import_utils101 = require("@typescript-eslint/utils");
|
|
17388
17436
|
var PREFER_SWITCH_FOR_REPEATED_EQUALITY_DOCUMENTATION = {
|
|
17437
|
+
defaultLevel: "warning",
|
|
17389
17438
|
summary: "Prefer switch over long if/else-if chains that compare one value for strict equality.",
|
|
17390
17439
|
rationale: "A switch makes finite dispatch cases visually uniform and easier to extend without duplicating the discriminant.",
|
|
17391
17440
|
remediation: "Replace three or more strict-equality branches over the same discriminant with a switch; keep if statements for ranges, guards, and heterogeneous predicates.",
|
|
@@ -18244,6 +18293,7 @@ var LITERAL_KEY_HAZARDS = /* @__PURE__ */ new Set(["__proto__"]);
|
|
|
18244
18293
|
var NUMERIC_SIGNS2 = /* @__PURE__ */ new Set(["-", "+"]);
|
|
18245
18294
|
var MIN_RUN_LENGTH = 2;
|
|
18246
18295
|
var PREFER_WHOLE_OBJECT_ASSERTION_DOCUMENTATION = {
|
|
18296
|
+
defaultLevel: "warning",
|
|
18247
18297
|
summary: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together.",
|
|
18248
18298
|
rationale: "One whole-object assertion presents related expectations together and produces a complete structural diff.",
|
|
18249
18299
|
remediation: "Consider one `toMatchObject` assertion for ordinary data objects. Preserve missing-property checks, identity, and getter or proxy behavior when deciding whether to combine assertions.",
|
|
@@ -20071,6 +20121,7 @@ var require_fetch_timeout_default = createRule({
|
|
|
20071
20121
|
// src/rules/require-interface-for-exported-class.ts
|
|
20072
20122
|
var import_utils110 = require("@typescript-eslint/utils");
|
|
20073
20123
|
var REQUIRE_INTERFACE_FOR_EXPORTED_CLASS_DOCUMENTATION = {
|
|
20124
|
+
defaultLevel: "warning",
|
|
20074
20125
|
summary: "Require exported concrete classes with public behavior to declare a contract.",
|
|
20075
20126
|
rationale: "An explicit contract names the intended public capability separately from implementation details. TypeScript already supports structural compatibility; this is an architecture policy, not a prerequisite for substitution.",
|
|
20076
20127
|
remediation: "Declare a focused interface and add an implements clause, or inherit from an intentional base contract.",
|
|
@@ -20850,6 +20901,7 @@ var MODEL_EXECUTION_METHODS = /* @__PURE__ */ new Set([
|
|
|
20850
20901
|
]);
|
|
20851
20902
|
var DATABASE_NAMES = /^(?:db|database|connection|pool|prisma|query|transaction|tx)$/iu;
|
|
20852
20903
|
var REQUIRE_SQL_ACCESS_CLASS_DOCUMENTATION = {
|
|
20904
|
+
defaultLevel: "warning",
|
|
20853
20905
|
summary: "Keep SQL reads and writes inside a class that receives its database dependency.",
|
|
20854
20906
|
rationale: "An injected repository class is the preferred ownership boundary for database access under this architectural policy; free functions can also express explicit dependencies.",
|
|
20855
20907
|
remediation: "Move the query into a repository or store class and inject the pool, connection, transaction, or typed database binding through its constructor.",
|
|
@@ -22565,6 +22617,7 @@ function importedAssertionKind(specifier) {
|
|
|
22565
22617
|
// src/rules/sole-export-matches-filename.ts
|
|
22566
22618
|
var import_utils120 = require("@typescript-eslint/utils");
|
|
22567
22619
|
var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
|
|
22620
|
+
defaultLevel: "warning",
|
|
22568
22621
|
summary: "Make a module filename reflect its sole named public runtime export.",
|
|
22569
22622
|
rationale: "When a module owns one runtime responsibility, matching names make that responsibility directly discoverable.",
|
|
22570
22623
|
remediation: "Name the module for the exported responsibility, using either the full export name or a clear leading or trailing domain phrase; otherwise colocate genuinely related exports.",
|
|
@@ -23232,7 +23285,7 @@ var RULES = {
|
|
|
23232
23285
|
};
|
|
23233
23286
|
var meta = {
|
|
23234
23287
|
name: "@sarj/eslint-plugin",
|
|
23235
|
-
version: "15.26.
|
|
23288
|
+
version: "15.26.2"
|
|
23236
23289
|
};
|
|
23237
23290
|
var APPLICATION_ONLY_RULES = [];
|
|
23238
23291
|
var LIBRARY_IMPORT_POLICY = ["error", {
|
package/dist/index.d.cts
CHANGED
|
@@ -17,6 +17,7 @@ declare const RETIRED_RULES: Readonly<Record<string, RetiredRule>>;
|
|
|
17
17
|
|
|
18
18
|
type RuleCategory = "architecture" | "correctness" | "maintainability" | "performance" | "security" | "style" | "testing";
|
|
19
19
|
type AutofixPolicy = "none" | "safe" | "suggestion";
|
|
20
|
+
type DefaultLevel = "error" | "warning";
|
|
20
21
|
type ExampleOutcome = "match" | "no-match";
|
|
21
22
|
interface ExampleFile {
|
|
22
23
|
readonly path: string;
|
|
@@ -40,6 +41,7 @@ interface RuleDocumentation {
|
|
|
40
41
|
readonly rationale: string;
|
|
41
42
|
readonly remediation: string;
|
|
42
43
|
readonly category: RuleCategory;
|
|
44
|
+
readonly defaultLevel?: DefaultLevel;
|
|
43
45
|
readonly languages?: readonly "typescript"[];
|
|
44
46
|
readonly autofix?: AutofixPolicy;
|
|
45
47
|
readonly aliases?: readonly string[];
|
|
@@ -69,6 +71,7 @@ interface PublicRuleSpec {
|
|
|
69
71
|
readonly rationale: string;
|
|
70
72
|
readonly remediation: string;
|
|
71
73
|
readonly category: RuleCategory;
|
|
74
|
+
readonly defaultLevel: DefaultLevel;
|
|
72
75
|
readonly languages: readonly "typescript"[];
|
|
73
76
|
readonly autofix: AutofixPolicy;
|
|
74
77
|
readonly aliases: readonly string[];
|
|
@@ -172,11 +175,6 @@ interface RuleOptions$1 {
|
|
|
172
175
|
readonly max?: number;
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
/**
|
|
176
|
-
* @fileoverview no-dynamic-sql — bind runtime values separately from SQL string literals.
|
|
177
|
-
*
|
|
178
|
-
* Examples: https://github.com/sarj-ai/code-standards/blob/main/packages/typescript/tests/rules/no-dynamic-sql.test.ts
|
|
179
|
-
*/
|
|
180
178
|
interface RuleOptions {
|
|
181
179
|
readonly methods?: readonly string[];
|
|
182
180
|
}
|
|
@@ -244,7 +242,7 @@ declare const RULES: {
|
|
|
244
242
|
readonly "no-cors-wildcard-with-credentials": DocumentedRule<readonly [], "corsWildcardWithCredentials">;
|
|
245
243
|
readonly "no-duplicate-lifecycle-refresh-listeners": DocumentedRule<readonly [], "duplicateLifecycleRefresh">;
|
|
246
244
|
readonly "no-dangerously-allow-svg": DocumentedRule<readonly [], "noDangerouslyAllowSvg">;
|
|
247
|
-
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql">;
|
|
245
|
+
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql" | "dynamicFragment">;
|
|
248
246
|
readonly "no-enum": DocumentedRule<readonly [{
|
|
249
247
|
ignoreFiles?: readonly string[];
|
|
250
248
|
}?], "noEnum">;
|
|
@@ -998,7 +996,7 @@ type FlatPreset = {
|
|
|
998
996
|
declare const PLUGIN: {
|
|
999
997
|
readonly meta: {
|
|
1000
998
|
readonly name: "@sarj/eslint-plugin";
|
|
1001
|
-
readonly version: "15.26.
|
|
999
|
+
readonly version: "15.26.2";
|
|
1002
1000
|
};
|
|
1003
1001
|
readonly rules: {
|
|
1004
1002
|
readonly "no-conditional-empty-object-spread": DocumentedRule<[], "avoid">;
|
|
@@ -1034,7 +1032,7 @@ declare const PLUGIN: {
|
|
|
1034
1032
|
readonly "no-cors-wildcard-with-credentials": DocumentedRule<readonly [], "corsWildcardWithCredentials">;
|
|
1035
1033
|
readonly "no-duplicate-lifecycle-refresh-listeners": DocumentedRule<readonly [], "duplicateLifecycleRefresh">;
|
|
1036
1034
|
readonly "no-dangerously-allow-svg": DocumentedRule<readonly [], "noDangerouslyAllowSvg">;
|
|
1037
|
-
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql">;
|
|
1035
|
+
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql" | "dynamicFragment">;
|
|
1038
1036
|
readonly "no-enum": DocumentedRule<readonly [{
|
|
1039
1037
|
ignoreFiles?: readonly string[];
|
|
1040
1038
|
}?], "noEnum">;
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ declare const RETIRED_RULES: Readonly<Record<string, RetiredRule>>;
|
|
|
17
17
|
|
|
18
18
|
type RuleCategory = "architecture" | "correctness" | "maintainability" | "performance" | "security" | "style" | "testing";
|
|
19
19
|
type AutofixPolicy = "none" | "safe" | "suggestion";
|
|
20
|
+
type DefaultLevel = "error" | "warning";
|
|
20
21
|
type ExampleOutcome = "match" | "no-match";
|
|
21
22
|
interface ExampleFile {
|
|
22
23
|
readonly path: string;
|
|
@@ -40,6 +41,7 @@ interface RuleDocumentation {
|
|
|
40
41
|
readonly rationale: string;
|
|
41
42
|
readonly remediation: string;
|
|
42
43
|
readonly category: RuleCategory;
|
|
44
|
+
readonly defaultLevel?: DefaultLevel;
|
|
43
45
|
readonly languages?: readonly "typescript"[];
|
|
44
46
|
readonly autofix?: AutofixPolicy;
|
|
45
47
|
readonly aliases?: readonly string[];
|
|
@@ -69,6 +71,7 @@ interface PublicRuleSpec {
|
|
|
69
71
|
readonly rationale: string;
|
|
70
72
|
readonly remediation: string;
|
|
71
73
|
readonly category: RuleCategory;
|
|
74
|
+
readonly defaultLevel: DefaultLevel;
|
|
72
75
|
readonly languages: readonly "typescript"[];
|
|
73
76
|
readonly autofix: AutofixPolicy;
|
|
74
77
|
readonly aliases: readonly string[];
|
|
@@ -172,11 +175,6 @@ interface RuleOptions$1 {
|
|
|
172
175
|
readonly max?: number;
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
/**
|
|
176
|
-
* @fileoverview no-dynamic-sql — bind runtime values separately from SQL string literals.
|
|
177
|
-
*
|
|
178
|
-
* Examples: https://github.com/sarj-ai/code-standards/blob/main/packages/typescript/tests/rules/no-dynamic-sql.test.ts
|
|
179
|
-
*/
|
|
180
178
|
interface RuleOptions {
|
|
181
179
|
readonly methods?: readonly string[];
|
|
182
180
|
}
|
|
@@ -244,7 +242,7 @@ declare const RULES: {
|
|
|
244
242
|
readonly "no-cors-wildcard-with-credentials": DocumentedRule<readonly [], "corsWildcardWithCredentials">;
|
|
245
243
|
readonly "no-duplicate-lifecycle-refresh-listeners": DocumentedRule<readonly [], "duplicateLifecycleRefresh">;
|
|
246
244
|
readonly "no-dangerously-allow-svg": DocumentedRule<readonly [], "noDangerouslyAllowSvg">;
|
|
247
|
-
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql">;
|
|
245
|
+
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql" | "dynamicFragment">;
|
|
248
246
|
readonly "no-enum": DocumentedRule<readonly [{
|
|
249
247
|
ignoreFiles?: readonly string[];
|
|
250
248
|
}?], "noEnum">;
|
|
@@ -998,7 +996,7 @@ type FlatPreset = {
|
|
|
998
996
|
declare const PLUGIN: {
|
|
999
997
|
readonly meta: {
|
|
1000
998
|
readonly name: "@sarj/eslint-plugin";
|
|
1001
|
-
readonly version: "15.26.
|
|
999
|
+
readonly version: "15.26.2";
|
|
1002
1000
|
};
|
|
1003
1001
|
readonly rules: {
|
|
1004
1002
|
readonly "no-conditional-empty-object-spread": DocumentedRule<[], "avoid">;
|
|
@@ -1034,7 +1032,7 @@ declare const PLUGIN: {
|
|
|
1034
1032
|
readonly "no-cors-wildcard-with-credentials": DocumentedRule<readonly [], "corsWildcardWithCredentials">;
|
|
1035
1033
|
readonly "no-duplicate-lifecycle-refresh-listeners": DocumentedRule<readonly [], "duplicateLifecycleRefresh">;
|
|
1036
1034
|
readonly "no-dangerously-allow-svg": DocumentedRule<readonly [], "noDangerouslyAllowSvg">;
|
|
1037
|
-
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql">;
|
|
1035
|
+
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql" | "dynamicFragment">;
|
|
1038
1036
|
readonly "no-enum": DocumentedRule<readonly [{
|
|
1039
1037
|
ignoreFiles?: readonly string[];
|
|
1040
1038
|
}?], "noEnum">;
|
package/dist/index.js
CHANGED
|
@@ -45,6 +45,7 @@ ${missing.join("\n")}`);
|
|
|
45
45
|
rationale: spec.rationale,
|
|
46
46
|
remediation: spec.remediation,
|
|
47
47
|
category: spec.category,
|
|
48
|
+
defaultLevel: spec.defaultLevel,
|
|
48
49
|
languages: spec.languages,
|
|
49
50
|
autofix: spec.autofix,
|
|
50
51
|
aliases: spec.aliases,
|
|
@@ -126,6 +127,7 @@ function nativeSpec(config, documentation) {
|
|
|
126
127
|
rationale: documentation.rationale,
|
|
127
128
|
remediation: documentation.remediation,
|
|
128
129
|
category: documentation.category,
|
|
130
|
+
defaultLevel: documentation.defaultLevel ?? "error",
|
|
129
131
|
languages: [...documentation.languages ?? ["typescript"]],
|
|
130
132
|
autofix: documentation.autofix ?? "none",
|
|
131
133
|
aliases,
|
|
@@ -979,6 +981,7 @@ var require_button_accessible_name_default = createRule({
|
|
|
979
981
|
// src/rules/require-camelcase-properties.ts
|
|
980
982
|
import { AST_NODE_TYPES as AST_NODE_TYPES8 } from "@typescript-eslint/utils";
|
|
981
983
|
var REQUIRE_CAMELCASE_PROPERTIES_DOCUMENTATION = {
|
|
984
|
+
defaultLevel: "warning",
|
|
982
985
|
summary: "Require unquoted lower snake_case TypeScript properties and dot access to use camelCase.",
|
|
983
986
|
rationale: "Unquoted snake_case property syntax makes external wire naming indistinguishable from application-domain naming and lets inconsistent contracts spread through typed code.",
|
|
984
987
|
remediation: "Rename application properties to camelCase. At an external wire boundary, make the exception explicit with a quoted key and bracket access.",
|
|
@@ -2309,6 +2312,7 @@ var FILE_SELF_REFERENCE_RE = /\bthis (?:file|module)\b/iu;
|
|
|
2309
2312
|
var CHANGE_HISTORY_RE = /\b(?:used to|previously|histor(?:y|ical)|keep moving|models? (?:move|moved|change|changed)|still under review)\b/iu;
|
|
2310
2313
|
var IMPLEMENTATION_MAP_RE = /\b(?:everything (?:above|below)|nothing else|only thing|write half|maps? between|seam between)\b/iu;
|
|
2311
2314
|
var EXCESSIVE_COMMENTARY_DOCUMENTATION = {
|
|
2315
|
+
defaultLevel: "warning",
|
|
2312
2316
|
summary: "Flag long standalone implementation commentary that should be expressed by code.",
|
|
2313
2317
|
rationale: "Narrative implementation paragraphs compete with the code and can drift independently from behavior.",
|
|
2314
2318
|
remediation: "Delete narration and clarify names, types, or structure; retain only durable constraints and external contracts.",
|
|
@@ -4109,12 +4113,12 @@ function createSqlListener(handler) {
|
|
|
4109
4113
|
|
|
4110
4114
|
// src/rules/no-dynamic-sql.ts
|
|
4111
4115
|
var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
4112
|
-
summary: "Disallow runtime values
|
|
4113
|
-
rationale: "Embedding runtime values in SQL bypasses driver parameterization and can introduce injection defects or unstable query plans.",
|
|
4114
|
-
remediation: "
|
|
4116
|
+
summary: "Disallow runtime values and fragments interpolated into SQL passed to statement-execution methods.",
|
|
4117
|
+
rationale: "Embedding runtime values or fragments in SQL bypasses driver parameterization or an explicit allowlist and can introduce injection defects or unstable query plans.",
|
|
4118
|
+
remediation: "Bind data values through SQL placeholders. Select non-bindable identifiers or clauses from fixed, reviewed fragments instead of interpolating runtime text.",
|
|
4115
4119
|
category: "security",
|
|
4116
4120
|
limitations: [
|
|
4117
|
-
"
|
|
4121
|
+
"Template and concatenation fragments are inspected at recognized execution calls. Double-quoted identifiers, comments, and dollar strings are excluded; this is not a general SQL injection detector.",
|
|
4118
4122
|
"Literal fragments, legacy uppercase fragment names, and parameterizing tagged templates are exempt; uppercase spelling does not prove a value is static.",
|
|
4119
4123
|
"The bounded lexer recognizes doubled quotes, comments, and PostgreSQL dollar strings; dialect-specific escape modes and SQL generated through other APIs require separate security review."
|
|
4120
4124
|
],
|
|
@@ -4136,6 +4140,26 @@ var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
|
4136
4140
|
focusPath: "src/users.ts",
|
|
4137
4141
|
expectedCount: 1,
|
|
4138
4142
|
public: true
|
|
4143
|
+
},
|
|
4144
|
+
{
|
|
4145
|
+
id: "bound-unquoted-value",
|
|
4146
|
+
scenarioId: "unquoted",
|
|
4147
|
+
title: "An unquoted runtime value is bound separately",
|
|
4148
|
+
outcome: "no-match",
|
|
4149
|
+
files: [{ path: "src/users.ts", source: "db.prepare('select id from users where id = ?').bind(userId);" }],
|
|
4150
|
+
focusPath: "src/users.ts",
|
|
4151
|
+
expectedCount: 0,
|
|
4152
|
+
public: true
|
|
4153
|
+
},
|
|
4154
|
+
{
|
|
4155
|
+
id: "unquoted-runtime-fragment",
|
|
4156
|
+
scenarioId: "unquoted",
|
|
4157
|
+
title: "A runtime fragment is interpolated into SQL",
|
|
4158
|
+
outcome: "match",
|
|
4159
|
+
files: [{ path: "src/users.ts", source: "db.prepare(`select id from users where id = ${userId}`);" }],
|
|
4160
|
+
focusPath: "src/users.ts",
|
|
4161
|
+
expectedCount: 1,
|
|
4162
|
+
public: true
|
|
4139
4163
|
}
|
|
4140
4164
|
]
|
|
4141
4165
|
};
|
|
@@ -4158,14 +4182,21 @@ function isStaticFragment(expression) {
|
|
|
4158
4182
|
}
|
|
4159
4183
|
function runtimeInterpolations(template) {
|
|
4160
4184
|
const parts = template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw);
|
|
4161
|
-
const
|
|
4185
|
+
const statement = parts.join(RUNTIME_MARKER);
|
|
4186
|
+
const ranges = sqlSingleQuotedRanges(statement);
|
|
4187
|
+
const visible = stripSqlNoise(statement);
|
|
4162
4188
|
let offset = 0;
|
|
4163
|
-
return template.expressions.
|
|
4189
|
+
return template.expressions.flatMap(
|
|
4164
4190
|
(expression, index) => {
|
|
4165
4191
|
offset += parts[index]?.length ?? 0;
|
|
4166
4192
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
4193
|
+
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
4167
4194
|
offset += RUNTIME_MARKER.length;
|
|
4168
|
-
|
|
4195
|
+
if (isStaticFragment(expression)) return [];
|
|
4196
|
+
if (inValue && endsWithSqlQuote(parts[index] ?? "") && startsWithSqlQuote(parts[index + 1] ?? "")) {
|
|
4197
|
+
return [{ expression, messageId: "dynamicSql" }];
|
|
4198
|
+
}
|
|
4199
|
+
return unquoted ? [{ expression, messageId: "dynamicFragment" }] : [];
|
|
4169
4200
|
}
|
|
4170
4201
|
);
|
|
4171
4202
|
}
|
|
@@ -4196,16 +4227,21 @@ function runtimeConcatOperands(node) {
|
|
|
4196
4227
|
return [];
|
|
4197
4228
|
}
|
|
4198
4229
|
const parts = operands.map((operand) => staticLiteralText(operand) ?? RUNTIME_MARKER);
|
|
4199
|
-
const
|
|
4230
|
+
const statement = parts.join("");
|
|
4231
|
+
const ranges = sqlSingleQuotedRanges(statement);
|
|
4232
|
+
const visible = stripSqlNoise(statement);
|
|
4200
4233
|
let offset = 0;
|
|
4201
|
-
return operands.
|
|
4234
|
+
return operands.flatMap((operand, index) => {
|
|
4202
4235
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
4236
|
+
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
4203
4237
|
offset += parts[index]?.length ?? 0;
|
|
4204
|
-
if (
|
|
4205
|
-
if (isStaticFragment(operand)) return false;
|
|
4238
|
+
if (isStaticFragment(operand)) return [];
|
|
4206
4239
|
const before = operands[index - 1];
|
|
4207
4240
|
const after = operands[index + 1];
|
|
4208
|
-
|
|
4241
|
+
if (inValue && before !== void 0 && after !== void 0 && endsWithSqlQuote(staticLiteralText(before) ?? "") && startsWithSqlQuote(staticLiteralText(after) ?? "")) {
|
|
4242
|
+
return [{ expression: operand, messageId: "dynamicSql" }];
|
|
4243
|
+
}
|
|
4244
|
+
return unquoted ? [{ expression: operand, messageId: "dynamicFragment" }] : [];
|
|
4209
4245
|
});
|
|
4210
4246
|
}
|
|
4211
4247
|
function concatOperands(node) {
|
|
@@ -4261,7 +4297,8 @@ var no_dynamic_sql_default = createRule({
|
|
|
4261
4297
|
}
|
|
4262
4298
|
],
|
|
4263
4299
|
messages: {
|
|
4264
|
-
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately."
|
|
4300
|
+
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately.",
|
|
4301
|
+
dynamicFragment: "Runtime fragment interpolated into SQL passed to `{{method}}()`. Bind data values; select non-bindable SQL fragments from fixed, reviewed alternatives."
|
|
4265
4302
|
}
|
|
4266
4303
|
},
|
|
4267
4304
|
defaultOptions: [{}],
|
|
@@ -4280,8 +4317,8 @@ var no_dynamic_sql_default = createRule({
|
|
|
4280
4317
|
const offenders = statement.type === AST_NODE_TYPES25.TemplateLiteral ? runtimeInterpolations(statement) : runtimeConcatOperands(statement);
|
|
4281
4318
|
for (const offender of offenders) {
|
|
4282
4319
|
context.report({
|
|
4283
|
-
node: offender,
|
|
4284
|
-
messageId:
|
|
4320
|
+
node: offender.expression,
|
|
4321
|
+
messageId: offender.messageId,
|
|
4285
4322
|
data: { method }
|
|
4286
4323
|
});
|
|
4287
4324
|
}
|
|
@@ -5921,6 +5958,7 @@ import {
|
|
|
5921
5958
|
} from "@typescript-eslint/utils";
|
|
5922
5959
|
import ts5 from "typescript";
|
|
5923
5960
|
var NO_JSON_STRINGIFY_OBJECT_EQUALITY_DOCUMENTATION = {
|
|
5961
|
+
defaultLevel: "warning",
|
|
5924
5962
|
summary: "Do not use JSON serialization as structural object equality.",
|
|
5925
5963
|
rationale: "JSON text equality depends on property insertion order and serialization behavior, so semantically equal objects can compare unequal and distinct values can collapse together.",
|
|
5926
5964
|
remediation: "Compare an explicit domain projection structurally, or use a reviewed canonical serializer when JSON semantics are required.",
|
|
@@ -7191,6 +7229,7 @@ var no_bare_return_from_test_catch_default = createRule({
|
|
|
7191
7229
|
// src/rules/no-bespoke-api-case-conversion.ts
|
|
7192
7230
|
import { AST_NODE_TYPES as AST_NODE_TYPES38, ASTUtils as ASTUtils15 } from "@typescript-eslint/utils";
|
|
7193
7231
|
var NO_BESPOKE_API_CASE_CONVERSION_DOCUMENTATION = {
|
|
7232
|
+
defaultLevel: "warning",
|
|
7194
7233
|
summary: "Review direct snake_case/camelCase mirror mappings on explicitly API-typed adapter values.",
|
|
7195
7234
|
rationale: "Duplicating wire-name translation can drift from an API client contract. When the SDK owns application-facing names, centralizing conversion avoids maintaining another mirror by hand.",
|
|
7196
7235
|
remediation: "Move wire-name ownership and case conversion into the generated SDK/model layer; keep application adapters on the generated typed surface.",
|
|
@@ -8937,6 +8976,7 @@ var WALL_NARRATION_RE2 = /^(?:(?:\d+[.)]|(?:phase|step)\s+\d+\s*:?)\s*)?(?:add|b
|
|
|
8937
8976
|
var WALL_CLUSTER_MAX_LINE_GAP = 8;
|
|
8938
8977
|
var WALL_CLUSTER_MIN_COMMENTS = 3;
|
|
8939
8978
|
var NO_RESTATED_COMMENT_DOCUMENTATION = {
|
|
8979
|
+
defaultLevel: "warning",
|
|
8940
8980
|
summary: "Flag short standalone comments that repeat the adjacent statement's identifiers.",
|
|
8941
8981
|
rationale: "A comment that only repeats code adds no context and can become stale independently.",
|
|
8942
8982
|
remediation: "Remove a genuine restatement; retain conditions, constraints, rationale, and information absent from the statement.",
|
|
@@ -9086,6 +9126,7 @@ function isRestatableLabel(body2) {
|
|
|
9086
9126
|
// src/rules/no-restated-jsdoc.ts
|
|
9087
9127
|
import { AST_NODE_TYPES as AST_NODE_TYPES47 } from "@typescript-eslint/utils";
|
|
9088
9128
|
var NO_RESTATED_JSDOC_DOCUMENTATION = {
|
|
9129
|
+
defaultLevel: "warning",
|
|
9089
9130
|
summary: "Flag JSDoc prose that appears to repeat declaration names without adding behavioral information.",
|
|
9090
9131
|
rationale: "Signature-only JSDoc duplicates type information and drifts without helping callers.",
|
|
9091
9132
|
remediation: "Delete the block or document behavior, constraints, failures, or context the signature cannot express.",
|
|
@@ -13658,6 +13699,7 @@ import {
|
|
|
13658
13699
|
AST_NODE_TYPES as AST_NODE_TYPES68
|
|
13659
13700
|
} from "@typescript-eslint/utils";
|
|
13660
13701
|
var PREFER_MILLISECOND_CONTROL_DURATION_SCHEMA_DOCUMENTATION = {
|
|
13702
|
+
defaultLevel: "warning",
|
|
13661
13703
|
summary: "Require application-owned Zod control-duration fields to use millisecond granularity.",
|
|
13662
13704
|
rationale: "Second-granularity timeout and scheduling controls lose precision and invite implicit unit conversion at API boundaries. Encoding milliseconds in the schema keeps the unit explicit and composes with platform timing APIs.",
|
|
13663
13705
|
remediation: "Rename the field with an `Ms`/`_ms` suffix and express its bounds and default in milliseconds; update the owning API contract rather than converting in application code.",
|
|
@@ -15280,6 +15322,7 @@ var FUNCTION_TYPES9 = /* @__PURE__ */ new Set([
|
|
|
15280
15322
|
AST_NODE_TYPES73.FunctionExpression
|
|
15281
15323
|
]);
|
|
15282
15324
|
var PREFER_MODULE_LEVEL_REFINED_SCHEMA_DOCUMENTATION = {
|
|
15325
|
+
defaultLevel: "warning",
|
|
15283
15326
|
summary: "Declare closed Zod scalar, format, and wrapper schemas at module scope.",
|
|
15284
15327
|
rationale: "A closed validation pipeline created inside a function is rebuilt on every invocation and obscures a reusable constraint.",
|
|
15285
15328
|
remediation: "Move the validation schema to module scope, name it with a PascalCase Schema suffix, and call parse on the shared schema.",
|
|
@@ -15556,6 +15599,7 @@ import {
|
|
|
15556
15599
|
ASTUtils as ASTUtils34
|
|
15557
15600
|
} from "@typescript-eslint/utils";
|
|
15558
15601
|
var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
|
|
15602
|
+
defaultLevel: "warning",
|
|
15559
15603
|
summary: "Use the Zod 4 multi-value literal API instead of a union of literal schemas.",
|
|
15560
15604
|
rationale: "One multi-value literal expresses the same closed value domain without repeated schema wrappers.",
|
|
15561
15605
|
remediation: "Replace the union with z.literal([value1, value2, ...]).",
|
|
@@ -15742,6 +15786,7 @@ var prefer_named_callback_domain_default = createRule({
|
|
|
15742
15786
|
// src/rules/prefer-named-complex-return-type.ts
|
|
15743
15787
|
import { AST_NODE_TYPES as AST_NODE_TYPES76 } from "@typescript-eslint/utils";
|
|
15744
15788
|
var PREFER_NAMED_COMPLEX_RETURN_TYPE_DOCUMENTATION = {
|
|
15789
|
+
defaultLevel: "warning",
|
|
15745
15790
|
summary: "Prefer a named contract for structurally complex function return types.",
|
|
15746
15791
|
rationale: "A large inline return annotation hides a reusable domain concept and makes signatures difficult to scan.",
|
|
15747
15792
|
remediation: "Name the complex nested shape while preserving its generic wrappers and type parameters; reference the named contract from the return annotation.",
|
|
@@ -15902,6 +15947,7 @@ var prefer_native_random_uuid_default = createRule({
|
|
|
15902
15947
|
// src/rules/prefer-node-crypto-hash.ts
|
|
15903
15948
|
import { AST_NODE_TYPES as AST_NODE_TYPES78, ASTUtils as ASTUtils36 } from "@typescript-eslint/utils";
|
|
15904
15949
|
var PREFER_NODE_CRYPTO_HASH_DOCUMENTATION = {
|
|
15950
|
+
defaultLevel: "warning",
|
|
15905
15951
|
summary: "Prefer the modern one-shot node:crypto hash API when streaming state is unnecessary.",
|
|
15906
15952
|
rationale: "A createHash-update-digest chain allocates mutable streaming state for a single in-memory value; Node's built-in hash function expresses the one-shot operation directly and can use its optimized fast path.",
|
|
15907
15953
|
remediation: "On a supported Node runtime, consider hash(algorithm, value, encoding). Preserve the output encoding explicitly: digest() returns a Buffer, while hash defaults to hex. Keep createHash for streams or multiple updates.",
|
|
@@ -16034,6 +16080,7 @@ function isCreateHashCall(node, directBindings, namespaceBindings, resolve2) {
|
|
|
16034
16080
|
// src/rules/prefer-node-fs-promises.ts
|
|
16035
16081
|
import { AST_NODE_TYPES as AST_NODE_TYPES79, ASTUtils as ASTUtils37 } from "@typescript-eslint/utils";
|
|
16036
16082
|
var PREFER_NODE_FS_PROMISES_DOCUMENTATION = {
|
|
16083
|
+
defaultLevel: "warning",
|
|
16037
16084
|
summary: "Prefer promise-based Node.js filesystem APIs over synchronous calls in production modules.",
|
|
16038
16085
|
rationale: "Synchronous filesystem work blocks the event loop and can stall unrelated daemon, server, and worker tasks.",
|
|
16039
16086
|
remediation: "Import the promise API from node:fs/promises and await it; use FileHandle.sync only where a documented durability boundary requires it.",
|
|
@@ -17308,6 +17355,7 @@ function ifGuardDominates(use, branch, positive) {
|
|
|
17308
17355
|
// src/rules/prefer-shared-zod-enum.ts
|
|
17309
17356
|
import { AST_NODE_TYPES as AST_NODE_TYPES84, ASTUtils as ASTUtils42 } from "@typescript-eslint/utils";
|
|
17310
17357
|
var PREFER_SHARED_ZOD_ENUM_DOCUMENTATION = {
|
|
17358
|
+
defaultLevel: "warning",
|
|
17311
17359
|
summary: "Give repeated literal Zod enum domains one reusable module-level schema.",
|
|
17312
17360
|
rationale: "Repeated literal domains hide a shared contract and allow equivalent fields to drift independently.",
|
|
17313
17361
|
remediation: "Declare a module-level named Zod enum schema and reuse it at each field or contract site.",
|
|
@@ -17395,6 +17443,7 @@ var prefer_shared_zod_enum_default = createRule({
|
|
|
17395
17443
|
// src/rules/prefer-switch-for-repeated-equality.ts
|
|
17396
17444
|
import { AST_NODE_TYPES as AST_NODE_TYPES85 } from "@typescript-eslint/utils";
|
|
17397
17445
|
var PREFER_SWITCH_FOR_REPEATED_EQUALITY_DOCUMENTATION = {
|
|
17446
|
+
defaultLevel: "warning",
|
|
17398
17447
|
summary: "Prefer switch over long if/else-if chains that compare one value for strict equality.",
|
|
17399
17448
|
rationale: "A switch makes finite dispatch cases visually uniform and easier to extend without duplicating the discriminant.",
|
|
17400
17449
|
remediation: "Replace three or more strict-equality branches over the same discriminant with a switch; keep if statements for ranges, guards, and heterogeneous predicates.",
|
|
@@ -18253,6 +18302,7 @@ var LITERAL_KEY_HAZARDS = /* @__PURE__ */ new Set(["__proto__"]);
|
|
|
18253
18302
|
var NUMERIC_SIGNS2 = /* @__PURE__ */ new Set(["-", "+"]);
|
|
18254
18303
|
var MIN_RUN_LENGTH = 2;
|
|
18255
18304
|
var PREFER_WHOLE_OBJECT_ASSERTION_DOCUMENTATION = {
|
|
18305
|
+
defaultLevel: "warning",
|
|
18256
18306
|
summary: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together.",
|
|
18257
18307
|
rationale: "One whole-object assertion presents related expectations together and produces a complete structural diff.",
|
|
18258
18308
|
remediation: "Consider one `toMatchObject` assertion for ordinary data objects. Preserve missing-property checks, identity, and getter or proxy behavior when deciding whether to combine assertions.",
|
|
@@ -20086,6 +20136,7 @@ var require_fetch_timeout_default = createRule({
|
|
|
20086
20136
|
// src/rules/require-interface-for-exported-class.ts
|
|
20087
20137
|
import { AST_NODE_TYPES as AST_NODE_TYPES93 } from "@typescript-eslint/utils";
|
|
20088
20138
|
var REQUIRE_INTERFACE_FOR_EXPORTED_CLASS_DOCUMENTATION = {
|
|
20139
|
+
defaultLevel: "warning",
|
|
20089
20140
|
summary: "Require exported concrete classes with public behavior to declare a contract.",
|
|
20090
20141
|
rationale: "An explicit contract names the intended public capability separately from implementation details. TypeScript already supports structural compatibility; this is an architecture policy, not a prerequisite for substitution.",
|
|
20091
20142
|
remediation: "Declare a focused interface and add an implements clause, or inherit from an intentional base contract.",
|
|
@@ -20865,6 +20916,7 @@ var MODEL_EXECUTION_METHODS = /* @__PURE__ */ new Set([
|
|
|
20865
20916
|
]);
|
|
20866
20917
|
var DATABASE_NAMES = /^(?:db|database|connection|pool|prisma|query|transaction|tx)$/iu;
|
|
20867
20918
|
var REQUIRE_SQL_ACCESS_CLASS_DOCUMENTATION = {
|
|
20919
|
+
defaultLevel: "warning",
|
|
20868
20920
|
summary: "Keep SQL reads and writes inside a class that receives its database dependency.",
|
|
20869
20921
|
rationale: "An injected repository class is the preferred ownership boundary for database access under this architectural policy; free functions can also express explicit dependencies.",
|
|
20870
20922
|
remediation: "Move the query into a repository or store class and inject the pool, connection, transaction, or typed database binding through its constructor.",
|
|
@@ -22583,6 +22635,7 @@ function importedAssertionKind(specifier) {
|
|
|
22583
22635
|
// src/rules/sole-export-matches-filename.ts
|
|
22584
22636
|
import { AST_NODE_TYPES as AST_NODE_TYPES101 } from "@typescript-eslint/utils";
|
|
22585
22637
|
var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
|
|
22638
|
+
defaultLevel: "warning",
|
|
22586
22639
|
summary: "Make a module filename reflect its sole named public runtime export.",
|
|
22587
22640
|
rationale: "When a module owns one runtime responsibility, matching names make that responsibility directly discoverable.",
|
|
22588
22641
|
remediation: "Name the module for the exported responsibility, using either the full export name or a clear leading or trailing domain phrase; otherwise colocate genuinely related exports.",
|
|
@@ -23253,7 +23306,7 @@ var RULES = {
|
|
|
23253
23306
|
};
|
|
23254
23307
|
var meta = {
|
|
23255
23308
|
name: "@sarj/eslint-plugin",
|
|
23256
|
-
version: "15.26.
|
|
23309
|
+
version: "15.26.2"
|
|
23257
23310
|
};
|
|
23258
23311
|
var APPLICATION_ONLY_RULES = [];
|
|
23259
23312
|
var LIBRARY_IMPORT_POLICY = ["error", {
|