@sarj/eslint-plugin 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -6
- package/dist/index.cjs +193 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +193 -6
- package/dist/index.js.map +1 -1
- package/package.json +14 -3
package/dist/index.d.cts
CHANGED
|
@@ -303,6 +303,12 @@ declare const rules: {
|
|
|
303
303
|
"no-sleep-in-test-body": _typescript_eslint_utils_ts_eslint.RuleModule<"noSleepInTestBody", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
304
304
|
name: string;
|
|
305
305
|
};
|
|
306
|
+
"no-hand-rolled-sleep": _typescript_eslint_utils_ts_eslint.RuleModule<"handRolledSleep" | "handRolledTimeoutRace", readonly [{
|
|
307
|
+
allowIn?: readonly string[];
|
|
308
|
+
checkClientModules?: boolean;
|
|
309
|
+
}?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
310
|
+
name: string;
|
|
311
|
+
};
|
|
306
312
|
"no-conditional-in-test": _typescript_eslint_utils_ts_eslint.RuleModule<"noConditionalInTest", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
307
313
|
name: string;
|
|
308
314
|
};
|
|
@@ -472,6 +478,12 @@ declare const plugin: {
|
|
|
472
478
|
"no-sleep-in-test-body": _typescript_eslint_utils_ts_eslint.RuleModule<"noSleepInTestBody", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
473
479
|
name: string;
|
|
474
480
|
};
|
|
481
|
+
"no-hand-rolled-sleep": _typescript_eslint_utils_ts_eslint.RuleModule<"handRolledSleep" | "handRolledTimeoutRace", readonly [{
|
|
482
|
+
allowIn?: readonly string[];
|
|
483
|
+
checkClientModules?: boolean;
|
|
484
|
+
}?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
485
|
+
name: string;
|
|
486
|
+
};
|
|
475
487
|
"no-conditional-in-test": _typescript_eslint_utils_ts_eslint.RuleModule<"noConditionalInTest", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
476
488
|
name: string;
|
|
477
489
|
};
|
|
@@ -564,6 +576,7 @@ declare const plugin: {
|
|
|
564
576
|
"@sarj/store-insert-requires-on-conflict": string;
|
|
565
577
|
"@sarj/no-offset-pagination": string;
|
|
566
578
|
"@sarj/no-select-star": string;
|
|
579
|
+
"@sarj/no-hand-rolled-sleep": string;
|
|
567
580
|
"@sarj/no-sleep-in-test-body": string;
|
|
568
581
|
"@sarj/no-conditional-in-test": string;
|
|
569
582
|
"@sarj/no-repeated-string-literal": string;
|
|
@@ -618,6 +631,7 @@ declare const plugin: {
|
|
|
618
631
|
"@sarj/store-insert-requires-on-conflict": string;
|
|
619
632
|
"@sarj/no-offset-pagination": string;
|
|
620
633
|
"@sarj/no-select-star": string;
|
|
634
|
+
"@sarj/no-hand-rolled-sleep": string;
|
|
621
635
|
"@sarj/no-sleep-in-test-body": string;
|
|
622
636
|
"@sarj/no-conditional-in-test": string;
|
|
623
637
|
"@sarj/no-repeated-string-literal": string;
|
package/dist/index.d.ts
CHANGED
|
@@ -303,6 +303,12 @@ declare const rules: {
|
|
|
303
303
|
"no-sleep-in-test-body": _typescript_eslint_utils_ts_eslint.RuleModule<"noSleepInTestBody", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
304
304
|
name: string;
|
|
305
305
|
};
|
|
306
|
+
"no-hand-rolled-sleep": _typescript_eslint_utils_ts_eslint.RuleModule<"handRolledSleep" | "handRolledTimeoutRace", readonly [{
|
|
307
|
+
allowIn?: readonly string[];
|
|
308
|
+
checkClientModules?: boolean;
|
|
309
|
+
}?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
310
|
+
name: string;
|
|
311
|
+
};
|
|
306
312
|
"no-conditional-in-test": _typescript_eslint_utils_ts_eslint.RuleModule<"noConditionalInTest", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
307
313
|
name: string;
|
|
308
314
|
};
|
|
@@ -472,6 +478,12 @@ declare const plugin: {
|
|
|
472
478
|
"no-sleep-in-test-body": _typescript_eslint_utils_ts_eslint.RuleModule<"noSleepInTestBody", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
473
479
|
name: string;
|
|
474
480
|
};
|
|
481
|
+
"no-hand-rolled-sleep": _typescript_eslint_utils_ts_eslint.RuleModule<"handRolledSleep" | "handRolledTimeoutRace", readonly [{
|
|
482
|
+
allowIn?: readonly string[];
|
|
483
|
+
checkClientModules?: boolean;
|
|
484
|
+
}?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
485
|
+
name: string;
|
|
486
|
+
};
|
|
475
487
|
"no-conditional-in-test": _typescript_eslint_utils_ts_eslint.RuleModule<"noConditionalInTest", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
476
488
|
name: string;
|
|
477
489
|
};
|
|
@@ -564,6 +576,7 @@ declare const plugin: {
|
|
|
564
576
|
"@sarj/store-insert-requires-on-conflict": string;
|
|
565
577
|
"@sarj/no-offset-pagination": string;
|
|
566
578
|
"@sarj/no-select-star": string;
|
|
579
|
+
"@sarj/no-hand-rolled-sleep": string;
|
|
567
580
|
"@sarj/no-sleep-in-test-body": string;
|
|
568
581
|
"@sarj/no-conditional-in-test": string;
|
|
569
582
|
"@sarj/no-repeated-string-literal": string;
|
|
@@ -618,6 +631,7 @@ declare const plugin: {
|
|
|
618
631
|
"@sarj/store-insert-requires-on-conflict": string;
|
|
619
632
|
"@sarj/no-offset-pagination": string;
|
|
620
633
|
"@sarj/no-select-star": string;
|
|
634
|
+
"@sarj/no-hand-rolled-sleep": string;
|
|
621
635
|
"@sarj/no-sleep-in-test-body": string;
|
|
622
636
|
"@sarj/no-conditional-in-test": string;
|
|
623
637
|
"@sarj/no-repeated-string-literal": string;
|
package/dist/index.js
CHANGED
|
@@ -7385,9 +7385,186 @@ var no_async_callback_in_waitfor_default = ESLintUtils49.RuleCreator(
|
|
|
7385
7385
|
}
|
|
7386
7386
|
});
|
|
7387
7387
|
|
|
7388
|
-
// src/rules/
|
|
7388
|
+
// src/rules/no-hand-rolled-sleep.ts
|
|
7389
7389
|
import { AST_NODE_TYPES as AST_NODE_TYPES37, ESLintUtils as ESLintUtils50 } from "@typescript-eslint/utils";
|
|
7390
|
-
var
|
|
7390
|
+
var GLOBAL_OBJECTS2 = /* @__PURE__ */ new Set([
|
|
7391
|
+
"globalThis",
|
|
7392
|
+
"window",
|
|
7393
|
+
"self",
|
|
7394
|
+
"global"
|
|
7395
|
+
]);
|
|
7396
|
+
var CLIENT_ONLY_MODULES = /^(react|react-dom|react-native|svelte|vue|preact|solid-js)(\/|$)|^next\/(navigation|router|link|image)$/;
|
|
7397
|
+
var RACE_METHODS = /* @__PURE__ */ new Set(["race", "any"]);
|
|
7398
|
+
function matchesAnyPattern3(filename, patterns) {
|
|
7399
|
+
for (const pattern of patterns) {
|
|
7400
|
+
const regexSource = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "::DOUBLESTAR::").replace(/\*/g, "[^/\\\\]*").replace(/::DOUBLESTAR::/g, ".*");
|
|
7401
|
+
if (new RegExp(`^${regexSource}$`).test(filename)) {
|
|
7402
|
+
return true;
|
|
7403
|
+
}
|
|
7404
|
+
}
|
|
7405
|
+
return false;
|
|
7406
|
+
}
|
|
7407
|
+
function isSetTimeoutCallee(callee) {
|
|
7408
|
+
if (callee.type === AST_NODE_TYPES37.Identifier) {
|
|
7409
|
+
return callee.name === "setTimeout";
|
|
7410
|
+
}
|
|
7411
|
+
return callee.type === AST_NODE_TYPES37.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES37.Identifier && callee.property.name === "setTimeout" && callee.object.type === AST_NODE_TYPES37.Identifier && GLOBAL_OBJECTS2.has(callee.object.name);
|
|
7412
|
+
}
|
|
7413
|
+
function soleCall(fn) {
|
|
7414
|
+
if (fn.body.type !== AST_NODE_TYPES37.BlockStatement) {
|
|
7415
|
+
return fn.body.type === AST_NODE_TYPES37.CallExpression ? fn.body : null;
|
|
7416
|
+
}
|
|
7417
|
+
if (fn.body.body.length !== 1) {
|
|
7418
|
+
return null;
|
|
7419
|
+
}
|
|
7420
|
+
const [only] = fn.body.body;
|
|
7421
|
+
if (only?.type !== AST_NODE_TYPES37.ExpressionStatement) {
|
|
7422
|
+
return null;
|
|
7423
|
+
}
|
|
7424
|
+
return only.expression.type === AST_NODE_TYPES37.CallExpression ? only.expression : null;
|
|
7425
|
+
}
|
|
7426
|
+
function isTimedDelay(delay) {
|
|
7427
|
+
if (delay === void 0) {
|
|
7428
|
+
return false;
|
|
7429
|
+
}
|
|
7430
|
+
if (delay.type === AST_NODE_TYPES37.Literal && typeof delay.value === "number") {
|
|
7431
|
+
return delay.value !== 0;
|
|
7432
|
+
}
|
|
7433
|
+
return true;
|
|
7434
|
+
}
|
|
7435
|
+
function settlesWithoutValue(callback, name) {
|
|
7436
|
+
if (callback.type === AST_NODE_TYPES37.Identifier) {
|
|
7437
|
+
return callback.name === name;
|
|
7438
|
+
}
|
|
7439
|
+
if (callback.type !== AST_NODE_TYPES37.ArrowFunctionExpression && callback.type !== AST_NODE_TYPES37.FunctionExpression) {
|
|
7440
|
+
return false;
|
|
7441
|
+
}
|
|
7442
|
+
const call = soleCall(callback);
|
|
7443
|
+
return call !== null && call.arguments.length === 0 && call.callee.type === AST_NODE_TYPES37.Identifier && call.callee.name === name;
|
|
7444
|
+
}
|
|
7445
|
+
function rejectsInCallback(callback, name) {
|
|
7446
|
+
if (callback.type === AST_NODE_TYPES37.Identifier) {
|
|
7447
|
+
return callback.name === name;
|
|
7448
|
+
}
|
|
7449
|
+
if (callback.type !== AST_NODE_TYPES37.ArrowFunctionExpression && callback.type !== AST_NODE_TYPES37.FunctionExpression) {
|
|
7450
|
+
return false;
|
|
7451
|
+
}
|
|
7452
|
+
const call = soleCall(callback);
|
|
7453
|
+
return call !== null && call.callee.type === AST_NODE_TYPES37.Identifier && call.callee.name === name;
|
|
7454
|
+
}
|
|
7455
|
+
function parameterName(fn, index) {
|
|
7456
|
+
const parameter = fn.params[index];
|
|
7457
|
+
return parameter?.type === AST_NODE_TYPES37.Identifier ? parameter.name : null;
|
|
7458
|
+
}
|
|
7459
|
+
function isRaceArm(node) {
|
|
7460
|
+
const array = node.parent;
|
|
7461
|
+
if (array?.type !== AST_NODE_TYPES37.ArrayExpression) {
|
|
7462
|
+
return false;
|
|
7463
|
+
}
|
|
7464
|
+
const call = array.parent;
|
|
7465
|
+
return call?.type === AST_NODE_TYPES37.CallExpression && call.arguments[0] === array && call.callee.type === AST_NODE_TYPES37.MemberExpression && !call.callee.computed && call.callee.object.type === AST_NODE_TYPES37.Identifier && call.callee.object.name === "Promise" && call.callee.property.type === AST_NODE_TYPES37.Identifier && RACE_METHODS.has(call.callee.property.name);
|
|
7466
|
+
}
|
|
7467
|
+
var no_hand_rolled_sleep_default = ESLintUtils50.RuleCreator(
|
|
7468
|
+
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
7469
|
+
)({
|
|
7470
|
+
name: "no-hand-rolled-sleep",
|
|
7471
|
+
meta: {
|
|
7472
|
+
type: "problem",
|
|
7473
|
+
docs: {
|
|
7474
|
+
description: "Disallow hand-rolled promisified timers (`new Promise((r) => setTimeout(r, ms))`) and hand-rolled `Promise.race` timeout arms; the stdlib forms are cancellable, these are not."
|
|
7475
|
+
},
|
|
7476
|
+
schema: [
|
|
7477
|
+
{
|
|
7478
|
+
type: "object",
|
|
7479
|
+
additionalProperties: false,
|
|
7480
|
+
properties: {
|
|
7481
|
+
allowIn: {
|
|
7482
|
+
description: "Glob patterns for modules exempt from the rule (e.g. a single sanctioned `sleep` utility). Matched against the ABSOLUTE file path, so anchor with a `**/` prefix (e.g. `**/lib/sleep.ts`).",
|
|
7483
|
+
type: "array",
|
|
7484
|
+
items: { type: "string" }
|
|
7485
|
+
},
|
|
7486
|
+
checkClientModules: {
|
|
7487
|
+
description: "Also report the sleep form in browser/React Native modules. Off by default: those bundles cannot import `node:timers/promises` and the web platform has no equivalent, so the fix is impossible to follow. Turn on only where every file can resolve `node:` builtins.",
|
|
7488
|
+
type: "boolean"
|
|
7489
|
+
}
|
|
7490
|
+
}
|
|
7491
|
+
}
|
|
7492
|
+
],
|
|
7493
|
+
messages: {
|
|
7494
|
+
handRolledSleep: 'Hand-rolled sleep: `new Promise((resolve) => setTimeout(resolve, ms))` cannot be cancelled, so an aborted request or a lost race still waits out the full delay. Use `import { setTimeout as sleep } from "node:timers/promises"` and pass `{ signal }`.',
|
|
7495
|
+
handRolledTimeoutRace: "Hand-rolled timeout arm: when the other promise wins, this timer is never cleared and keeps the event loop alive until it fires. Use `AbortSignal.timeout(ms)` and pass the signal to the operation."
|
|
7496
|
+
}
|
|
7497
|
+
},
|
|
7498
|
+
defaultOptions: [{}],
|
|
7499
|
+
create(context, [optionsArg]) {
|
|
7500
|
+
const { filename, sourceCode } = context;
|
|
7501
|
+
if (isTestFile(filename) || isScriptFile(filename) || isGeneratedFile(filename, sourceCode.getText())) {
|
|
7502
|
+
return {};
|
|
7503
|
+
}
|
|
7504
|
+
const allowIn = optionsArg?.allowIn ?? [];
|
|
7505
|
+
if (allowIn.length > 0 && matchesAnyPattern3(filename, allowIn)) {
|
|
7506
|
+
return {};
|
|
7507
|
+
}
|
|
7508
|
+
const checkClientModules = optionsArg?.checkClientModules ?? false;
|
|
7509
|
+
function isClientModule() {
|
|
7510
|
+
if (/\.[cm]?[jt]sx$/.test(filename)) {
|
|
7511
|
+
return true;
|
|
7512
|
+
}
|
|
7513
|
+
const program = sourceCode.ast;
|
|
7514
|
+
for (const statement of program.body) {
|
|
7515
|
+
if (statement.type === AST_NODE_TYPES37.ExpressionStatement && statement.expression.type === AST_NODE_TYPES37.Literal && statement.expression.value === "use client") {
|
|
7516
|
+
return true;
|
|
7517
|
+
}
|
|
7518
|
+
if (statement.type === AST_NODE_TYPES37.ImportDeclaration && typeof statement.source.value === "string" && CLIENT_ONLY_MODULES.test(statement.source.value)) {
|
|
7519
|
+
return true;
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
7522
|
+
return false;
|
|
7523
|
+
}
|
|
7524
|
+
let clientModule = null;
|
|
7525
|
+
const reportsSleepHere = () => {
|
|
7526
|
+
if (checkClientModules) {
|
|
7527
|
+
return true;
|
|
7528
|
+
}
|
|
7529
|
+
clientModule ??= isClientModule();
|
|
7530
|
+
return !clientModule;
|
|
7531
|
+
};
|
|
7532
|
+
return {
|
|
7533
|
+
NewExpression(node) {
|
|
7534
|
+
if (node.callee.type !== AST_NODE_TYPES37.Identifier || node.callee.name !== "Promise") {
|
|
7535
|
+
return;
|
|
7536
|
+
}
|
|
7537
|
+
const executor = node.arguments[0];
|
|
7538
|
+
if (executor?.type !== AST_NODE_TYPES37.ArrowFunctionExpression && executor?.type !== AST_NODE_TYPES37.FunctionExpression) {
|
|
7539
|
+
return;
|
|
7540
|
+
}
|
|
7541
|
+
const call = soleCall(executor);
|
|
7542
|
+
if (call === null || !isSetTimeoutCallee(call.callee)) {
|
|
7543
|
+
return;
|
|
7544
|
+
}
|
|
7545
|
+
const [callback, delay] = call.arguments;
|
|
7546
|
+
if (callback === void 0 || !isTimedDelay(delay)) {
|
|
7547
|
+
return;
|
|
7548
|
+
}
|
|
7549
|
+
const resolveName = parameterName(executor, 0);
|
|
7550
|
+
if (resolveName !== null && settlesWithoutValue(callback, resolveName)) {
|
|
7551
|
+
if (reportsSleepHere()) {
|
|
7552
|
+
context.report({ node, messageId: "handRolledSleep" });
|
|
7553
|
+
}
|
|
7554
|
+
return;
|
|
7555
|
+
}
|
|
7556
|
+
const rejectName = parameterName(executor, 1);
|
|
7557
|
+
if (rejectName !== null && isRaceArm(node) && rejectsInCallback(callback, rejectName)) {
|
|
7558
|
+
context.report({ node, messageId: "handRolledTimeoutRace" });
|
|
7559
|
+
}
|
|
7560
|
+
}
|
|
7561
|
+
};
|
|
7562
|
+
}
|
|
7563
|
+
});
|
|
7564
|
+
|
|
7565
|
+
// src/rules/prefer-setup-file-mocks.ts
|
|
7566
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES38, ESLintUtils as ESLintUtils51 } from "@typescript-eslint/utils";
|
|
7567
|
+
var prefer_setup_file_mocks_default = ESLintUtils51.RuleCreator(
|
|
7391
7568
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
7392
7569
|
)({
|
|
7393
7570
|
name: "prefer-setup-file-mocks",
|
|
@@ -7408,7 +7585,7 @@ var prefer_setup_file_mocks_default = ESLintUtils50.RuleCreator(
|
|
|
7408
7585
|
}
|
|
7409
7586
|
return {
|
|
7410
7587
|
CallExpression(node) {
|
|
7411
|
-
if (node.callee.type ===
|
|
7588
|
+
if (node.callee.type === AST_NODE_TYPES38.MemberExpression && node.callee.object.type === AST_NODE_TYPES38.Identifier && (node.callee.object.name === "vi" || node.callee.object.name === "jest") && node.callee.property.type === AST_NODE_TYPES38.Identifier && node.callee.property.name === "mock") {
|
|
7412
7589
|
context.report({
|
|
7413
7590
|
node,
|
|
7414
7591
|
messageId: "preferSetupFileMocks"
|
|
@@ -7453,6 +7630,7 @@ var rules = {
|
|
|
7453
7630
|
"no-repeated-string-literal": no_repeated_string_literal_default,
|
|
7454
7631
|
"no-select-star": no_select_star_default,
|
|
7455
7632
|
"no-sleep-in-test-body": no_sleep_in_test_body_default,
|
|
7633
|
+
"no-hand-rolled-sleep": no_hand_rolled_sleep_default,
|
|
7456
7634
|
"no-conditional-in-test": no_conditional_in_test_default,
|
|
7457
7635
|
"prefer-constant-time-secret-compare": prefer_constant_time_secret_compare_default,
|
|
7458
7636
|
"store-insert-requires-on-conflict": store_insert_requires_on_conflict_default,
|
|
@@ -7475,7 +7653,7 @@ var rules = {
|
|
|
7475
7653
|
var plugin = {
|
|
7476
7654
|
meta: {
|
|
7477
7655
|
name: "@sarj/eslint-plugin",
|
|
7478
|
-
version: "4.
|
|
7656
|
+
version: "4.1.0"
|
|
7479
7657
|
},
|
|
7480
7658
|
rules,
|
|
7481
7659
|
configs: {
|
|
@@ -7522,6 +7700,10 @@ var plugin = {
|
|
|
7522
7700
|
"@sarj/store-insert-requires-on-conflict": "warn",
|
|
7523
7701
|
"@sarj/no-offset-pagination": "warn",
|
|
7524
7702
|
"@sarj/no-select-star": "warn",
|
|
7703
|
+
// Uncancellable hand-rolled timers. Verified against the shipped
|
|
7704
|
+
// strict config (205 enabled rules) that nothing already reports this
|
|
7705
|
+
// position; `unicorn` 72 has no promisified-timer rule at all.
|
|
7706
|
+
"@sarj/no-hand-rolled-sleep": "warn",
|
|
7525
7707
|
"@sarj/no-sleep-in-test-body": "warn",
|
|
7526
7708
|
"@sarj/no-conditional-in-test": "warn",
|
|
7527
7709
|
"@sarj/no-repeated-string-literal": "warn",
|
|
@@ -7538,7 +7720,8 @@ var plugin = {
|
|
|
7538
7720
|
// Anti-comment-verbosity family (2026-07), from a 37,918-comment,
|
|
7539
7721
|
// nine-repo measurement study. Each is a deletion-class finding, so each
|
|
7540
7722
|
// was validated against pydantic / trio / attrs as well as the maintained
|
|
7541
|
-
// repos: `no-restated-comment` 0 hits in
|
|
7723
|
+
// repos: `no-restated-comment` 0 hits in the flagship first-party
|
|
7724
|
+
// repo and 4 in the three famous
|
|
7542
7725
|
// corpora combined; `trailing-value-narration` 18 hits, 18 true
|
|
7543
7726
|
// positives; `jsdoc-restates-signature` 36 hits, 0 measured false
|
|
7544
7727
|
// positives, and it offers a suggestion rather than a `--fix` because a
|
|
@@ -7549,7 +7732,7 @@ var plugin = {
|
|
|
7549
7732
|
// The TS half of SARJ057 (2026-07). Python has caught the
|
|
7550
7733
|
// assertion-FREE test since 0.15.0 (SARJ043) and had no TS
|
|
7551
7734
|
// counterpart, which is how `expect(true).toBe(true); // placeholder`
|
|
7552
|
-
// survived in
|
|
7735
|
+
// survived in a first-party repo: the file HAS an assertion.
|
|
7553
7736
|
// Measured across 5,819 .ts/.tsx files (1,003 of them test files) in
|
|
7554
7737
|
// six internal repos plus got / hono / swr / trpc: 3 hits, 3 true
|
|
7555
7738
|
// positives, 0 false positives.
|
|
@@ -7609,6 +7792,10 @@ var plugin = {
|
|
|
7609
7792
|
"@sarj/store-insert-requires-on-conflict": "error",
|
|
7610
7793
|
"@sarj/no-offset-pagination": "error",
|
|
7611
7794
|
"@sarj/no-select-star": "error",
|
|
7795
|
+
// Uncancellable hand-rolled timers. Verified against the shipped
|
|
7796
|
+
// strict config (205 enabled rules) that nothing already reports this
|
|
7797
|
+
// position; `unicorn` 72 has no promisified-timer rule at all.
|
|
7798
|
+
"@sarj/no-hand-rolled-sleep": "error",
|
|
7612
7799
|
"@sarj/no-sleep-in-test-body": "error",
|
|
7613
7800
|
"@sarj/no-conditional-in-test": "error",
|
|
7614
7801
|
"@sarj/no-repeated-string-literal": "error",
|