@sarj/eslint-plugin 2.12.0 → 2.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -70,6 +70,10 @@ interface RuleOptions$2 {
70
70
  * `api.ts` / `*-api.ts` module — plus test files and codemod fixtures.
71
71
  * - A method named `fetch` on some other receiver (`cache.fetch(k)`,
72
72
  * `queryClient.fetch()`): only the global is HTTP.
73
+ * - A pre-signed upload/download URL transfer (`fetch(uploadUrl, ...)`,
74
+ * `fetch(file.downloadUrl)`). Those URLs are one-off storage handoffs,
75
+ * not calls to a first-party service API that belongs behind a client
76
+ * wrapper.
73
77
  * - `new Request(...)` / `axios(...)` and friends. This rule is about the
74
78
  * global `fetch`, not about every HTTP library.
75
79
  *
@@ -237,7 +241,9 @@ declare const rules: {
237
241
  "prefer-schema-for-api-payload": _typescript_eslint_utils_ts_eslint.RuleModule<"unparsedJsonAccess", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
238
242
  name: string;
239
243
  };
240
- "prefer-semantic-colors": _typescript_eslint_utils_ts_eslint.RuleModule<"rawPalette" | "arbitraryColor" | "inlineColor", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
244
+ "prefer-semantic-colors": _typescript_eslint_utils_ts_eslint.RuleModule<"rawPalette" | "arbitraryColor" | "inlineColor", readonly [{
245
+ requireSemanticTokens?: boolean;
246
+ }?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
241
247
  name: string;
242
248
  };
243
249
  "prefer-server-actions": _typescript_eslint_utils_ts_eslint.RuleModule<"preferServerAction", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
@@ -379,7 +385,9 @@ declare const plugin: {
379
385
  "prefer-schema-for-api-payload": _typescript_eslint_utils_ts_eslint.RuleModule<"unparsedJsonAccess", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
380
386
  name: string;
381
387
  };
382
- "prefer-semantic-colors": _typescript_eslint_utils_ts_eslint.RuleModule<"rawPalette" | "arbitraryColor" | "inlineColor", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
388
+ "prefer-semantic-colors": _typescript_eslint_utils_ts_eslint.RuleModule<"rawPalette" | "arbitraryColor" | "inlineColor", readonly [{
389
+ requireSemanticTokens?: boolean;
390
+ }?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
383
391
  name: string;
384
392
  };
385
393
  "prefer-server-actions": _typescript_eslint_utils_ts_eslint.RuleModule<"preferServerAction", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
@@ -491,7 +499,9 @@ declare const plugin: {
491
499
  "@sarj/no-string-concat-in-loop": string;
492
500
  "@sarj/prefer-discriminated-union": string;
493
501
  "@sarj/no-comment-cruft": string;
494
- "@sarj/prefer-semantic-colors": string;
502
+ "@sarj/prefer-semantic-colors": (string | {
503
+ requireSemanticTokens: boolean;
504
+ })[];
495
505
  "@sarj/no-fat-try-blocks": string;
496
506
  "@sarj/no-cors-wildcard-with-credentials": string;
497
507
  "@sarj/no-secret-in-log": string;
@@ -535,7 +545,9 @@ declare const plugin: {
535
545
  "@sarj/no-string-concat-in-loop": string;
536
546
  "@sarj/prefer-discriminated-union": string;
537
547
  "@sarj/no-comment-cruft": string;
538
- "@sarj/prefer-semantic-colors": string;
548
+ "@sarj/prefer-semantic-colors": (string | {
549
+ requireSemanticTokens: boolean;
550
+ })[];
539
551
  "@sarj/no-fat-try-blocks": string;
540
552
  "@sarj/no-cors-wildcard-with-credentials": string;
541
553
  "@sarj/no-secret-in-log": string;
package/dist/index.d.ts CHANGED
@@ -70,6 +70,10 @@ interface RuleOptions$2 {
70
70
  * `api.ts` / `*-api.ts` module — plus test files and codemod fixtures.
71
71
  * - A method named `fetch` on some other receiver (`cache.fetch(k)`,
72
72
  * `queryClient.fetch()`): only the global is HTTP.
73
+ * - A pre-signed upload/download URL transfer (`fetch(uploadUrl, ...)`,
74
+ * `fetch(file.downloadUrl)`). Those URLs are one-off storage handoffs,
75
+ * not calls to a first-party service API that belongs behind a client
76
+ * wrapper.
73
77
  * - `new Request(...)` / `axios(...)` and friends. This rule is about the
74
78
  * global `fetch`, not about every HTTP library.
75
79
  *
@@ -237,7 +241,9 @@ declare const rules: {
237
241
  "prefer-schema-for-api-payload": _typescript_eslint_utils_ts_eslint.RuleModule<"unparsedJsonAccess", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
238
242
  name: string;
239
243
  };
240
- "prefer-semantic-colors": _typescript_eslint_utils_ts_eslint.RuleModule<"rawPalette" | "arbitraryColor" | "inlineColor", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
244
+ "prefer-semantic-colors": _typescript_eslint_utils_ts_eslint.RuleModule<"rawPalette" | "arbitraryColor" | "inlineColor", readonly [{
245
+ requireSemanticTokens?: boolean;
246
+ }?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
241
247
  name: string;
242
248
  };
243
249
  "prefer-server-actions": _typescript_eslint_utils_ts_eslint.RuleModule<"preferServerAction", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
@@ -379,7 +385,9 @@ declare const plugin: {
379
385
  "prefer-schema-for-api-payload": _typescript_eslint_utils_ts_eslint.RuleModule<"unparsedJsonAccess", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
380
386
  name: string;
381
387
  };
382
- "prefer-semantic-colors": _typescript_eslint_utils_ts_eslint.RuleModule<"rawPalette" | "arbitraryColor" | "inlineColor", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
388
+ "prefer-semantic-colors": _typescript_eslint_utils_ts_eslint.RuleModule<"rawPalette" | "arbitraryColor" | "inlineColor", readonly [{
389
+ requireSemanticTokens?: boolean;
390
+ }?], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
383
391
  name: string;
384
392
  };
385
393
  "prefer-server-actions": _typescript_eslint_utils_ts_eslint.RuleModule<"preferServerAction", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
@@ -491,7 +499,9 @@ declare const plugin: {
491
499
  "@sarj/no-string-concat-in-loop": string;
492
500
  "@sarj/prefer-discriminated-union": string;
493
501
  "@sarj/no-comment-cruft": string;
494
- "@sarj/prefer-semantic-colors": string;
502
+ "@sarj/prefer-semantic-colors": (string | {
503
+ requireSemanticTokens: boolean;
504
+ })[];
495
505
  "@sarj/no-fat-try-blocks": string;
496
506
  "@sarj/no-cors-wildcard-with-credentials": string;
497
507
  "@sarj/no-secret-in-log": string;
@@ -535,7 +545,9 @@ declare const plugin: {
535
545
  "@sarj/no-string-concat-in-loop": string;
536
546
  "@sarj/prefer-discriminated-union": string;
537
547
  "@sarj/no-comment-cruft": string;
538
- "@sarj/prefer-semantic-colors": string;
548
+ "@sarj/prefer-semantic-colors": (string | {
549
+ requireSemanticTokens: boolean;
550
+ })[];
539
551
  "@sarj/no-fat-try-blocks": string;
540
552
  "@sarj/no-cors-wildcard-with-credentials": string;
541
553
  "@sarj/no-secret-in-log": string;
package/dist/index.js CHANGED
@@ -1,5 +1,29 @@
1
1
  // src/rules/enforce-file-structure.ts
2
2
  import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
3
+
4
+ // src/rules/_paths.ts
5
+ var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
6
+ var STORY_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
7
+ var GENERATED_FILE_RE = /([\\/]generated[\\/])|(\.gen\.[cm]?[jt]sx?$)|(\.generated\.[cm]?[jt]sx?$)|(\.d\.[cm]?ts$)/;
8
+ function isTestFile(filename) {
9
+ const normalized = filename.replaceAll("\\", "/");
10
+ const base = normalized.slice(normalized.lastIndexOf("/") + 1);
11
+ if (/\.(test|spec|e2e|integration)\.[cm]?[jt]sx?$/.test(base)) {
12
+ return true;
13
+ }
14
+ return /(^|\/)(tests?|__tests__|__mocks__|fixtures|e2e|integration)\//.test(normalized);
15
+ }
16
+ function isStoryFile(filename) {
17
+ return STORY_FILE_RE.test(filename);
18
+ }
19
+ function isGeneratedFile(filename, sourceText = "") {
20
+ return GENERATED_FILE_RE.test(filename.replaceAll("\\", "/")) || /@generated\b/.test(sourceText.slice(0, 1024));
21
+ }
22
+ function isScriptFile(filename) {
23
+ return SCRIPT_FILE_RE.test(filename);
24
+ }
25
+
26
+ // src/rules/enforce-file-structure.ts
3
27
  var classifyStatement = (statement) => {
4
28
  switch (statement.type) {
5
29
  case AST_NODE_TYPES.ImportDeclaration:
@@ -36,6 +60,9 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
36
60
  },
37
61
  defaultOptions: [],
38
62
  create(context) {
63
+ if (isTestFile(context.filename)) {
64
+ return {};
65
+ }
39
66
  return {
40
67
  Program(node) {
41
68
  const body = node.body;
@@ -565,22 +592,6 @@ var no_enum_default = ESLintUtils4.RuleCreator(
565
592
 
566
593
  // src/rules/no-insecure-random-id.ts
567
594
  import { ESLintUtils as ESLintUtils5 } from "@typescript-eslint/utils";
568
-
569
- // src/rules/_paths.ts
570
- var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
571
- function isTestFile(filename) {
572
- const normalized = filename.replaceAll("\\", "/");
573
- const base = normalized.slice(normalized.lastIndexOf("/") + 1);
574
- if (/\.(test|spec)\.[cm]?[jt]sx?$/.test(base)) {
575
- return true;
576
- }
577
- return /(^|\/)(tests?|__tests__|__mocks__|fixtures)\//.test(normalized);
578
- }
579
- function isScriptFile(filename) {
580
- return SCRIPT_FILE_RE.test(filename);
581
- }
582
-
583
- // src/rules/no-insecure-random-id.ts
584
595
  var STRONG_SECURITY_PATTERN = /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;
585
596
  var NON_SECURITY_ID_PATTERN = /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;
586
597
  var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
@@ -1124,6 +1135,10 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
1124
1135
  // src/rules/no-raw-env.ts
1125
1136
  import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
1126
1137
  var CONFIG_FILE_RE = /(^|[\\/])[\w.-]+\.config\.[cm]?[jt]sx?$/;
1138
+ var ENV_BOUNDARY_FILE_RE = /(^|[\\/])(?:env|client-env|server-env|client-settings|server-settings)\.[cm]?[jt]sx?$/;
1139
+ function isValidatedEnvBoundary(filename, sourceText) {
1140
+ return ENV_BOUNDARY_FILE_RE.test(filename.replaceAll("\\", "/")) && /\bz\.object\s*\(/.test(sourceText) && /\.parse\s*\(/.test(sourceText);
1141
+ }
1127
1142
  function isProcessEnv(node) {
1128
1143
  return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
1129
1144
  }
@@ -1173,7 +1188,7 @@ var no_raw_env_default = ESLintUtils8.RuleCreator(
1173
1188
  defaultOptions: [],
1174
1189
  create(context) {
1175
1190
  const filename = context.filename;
1176
- if (isTestFile(filename) || isScriptFile(filename) || CONFIG_FILE_RE.test(filename.replaceAll("\\", "/"))) {
1191
+ if (isTestFile(filename) || isScriptFile(filename) || CONFIG_FILE_RE.test(filename.replaceAll("\\", "/")) || isValidatedEnvBoundary(filename, context.sourceCode.text)) {
1177
1192
  return {};
1178
1193
  }
1179
1194
  return {
@@ -2493,6 +2508,8 @@ var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
2493
2508
 
2494
2509
  // src/rules/prefer-semantic-colors.ts
2495
2510
  import { AST_NODE_TYPES as AST_NODE_TYPES8, ESLintUtils as ESLintUtils15 } from "@typescript-eslint/utils";
2511
+ import { existsSync, readFileSync } from "fs";
2512
+ import { dirname, join, parse } from "path";
2496
2513
 
2497
2514
  // src/rules/_tailwind.ts
2498
2515
  var tailwindBase = (token) => token.replace(/^(?:[a-z0-9-]+:)+/i, "").replace(/^!/, "");
@@ -2530,6 +2547,20 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
2530
2547
  ]);
2531
2548
  var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
2532
2549
  var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
2550
+ var SEMANTIC_TOKEN_RE = /--(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\b|(?:bg|text|border)-(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\b/;
2551
+ var DETECTION_FILES = [
2552
+ "components.json",
2553
+ "tailwind.config.js",
2554
+ "tailwind.config.cjs",
2555
+ "tailwind.config.mjs",
2556
+ "tailwind.config.ts",
2557
+ "app/globals.css",
2558
+ "src/app/globals.css",
2559
+ "src/index.css",
2560
+ "src/styles/globals.css",
2561
+ "styles/globals.css"
2562
+ ];
2563
+ var semanticTokenCache = /* @__PURE__ */ new Map();
2533
2564
  var SVG_DEFS_CONTAINERS = /* @__PURE__ */ new Set([
2534
2565
  "mask",
2535
2566
  "clipPath",
@@ -2558,6 +2589,34 @@ var isInsideSvg = (node) => {
2558
2589
  }
2559
2590
  return false;
2560
2591
  };
2592
+ var hasSemanticTokenSystem = (filename) => {
2593
+ let dir = dirname(filename);
2594
+ const root = parse(dir).root;
2595
+ for (let depth = 0; depth < 8; depth += 1) {
2596
+ const cached = semanticTokenCache.get(dir);
2597
+ if (cached !== void 0) return cached;
2598
+ let found = false;
2599
+ for (const rel of DETECTION_FILES) {
2600
+ const candidate = join(dir, rel);
2601
+ if (!existsSync(candidate)) continue;
2602
+ if (rel === "components.json") {
2603
+ found = true;
2604
+ break;
2605
+ }
2606
+ try {
2607
+ if (SEMANTIC_TOKEN_RE.test(readFileSync(candidate, "utf8"))) {
2608
+ found = true;
2609
+ break;
2610
+ }
2611
+ } catch {
2612
+ }
2613
+ }
2614
+ semanticTokenCache.set(dir, found);
2615
+ if (found || dir === root) return found;
2616
+ dir = dirname(dir);
2617
+ }
2618
+ return false;
2619
+ };
2561
2620
  var propName = (key) => {
2562
2621
  if (key.type === AST_NODE_TYPES8.Identifier) return key.name;
2563
2622
  if (key.type === AST_NODE_TYPES8.Literal && typeof key.value === "string") return key.value;
@@ -2572,16 +2631,27 @@ var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
2572
2631
  docs: {
2573
2632
  description: "Enforce design-system semantic color tokens (bg-primary, text-destructive, \u2026) over raw Tailwind palette classes (text-red-500), arbitrary color values (bg-[#fff]), and inline color literals."
2574
2633
  },
2575
- schema: [],
2634
+ schema: [
2635
+ {
2636
+ type: "object",
2637
+ additionalProperties: false,
2638
+ properties: {
2639
+ requireSemanticTokens: { type: "boolean" }
2640
+ }
2641
+ }
2642
+ ],
2576
2643
  messages: {
2577
2644
  rawPalette: "Raw palette class '{{class}}' \u2014 use a semantic token (e.g. text-foreground, bg-primary, text-destructive, bg-muted).",
2578
2645
  arbitraryColor: "Hardcoded color '{{class}}' \u2014 use a semantic token, or var(--\u2026). For charts/brand add an eslint-disable with a reason.",
2579
2646
  inlineColor: "Hardcoded color '{{value}}' \u2014 use a semantic token / CSS variable. For charts/standalone pages add an eslint-disable with a reason."
2580
2647
  }
2581
2648
  },
2582
- defaultOptions: [],
2583
- create(context) {
2649
+ defaultOptions: [{}],
2650
+ create(context, [options]) {
2584
2651
  if (STORIES_FILE_RE.test(context.filename)) return {};
2652
+ if (options?.requireSemanticTokens === true && !hasSemanticTokenSystem(context.filename)) {
2653
+ return {};
2654
+ }
2585
2655
  const reportClasses = (value, node) => {
2586
2656
  for (const token of classTokens(value)) {
2587
2657
  const base = tailwindBase(token);
@@ -2843,7 +2913,7 @@ var INPUT_TYPE_REPLACEMENTS = {
2843
2913
  radio: "RadioGroup",
2844
2914
  range: "Slider"
2845
2915
  };
2846
- var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["hidden"]);
2916
+ var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["file", "hidden"]);
2847
2917
  var kebabCase = (component) => component.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
2848
2918
  var literalTypeAttr = (node) => {
2849
2919
  for (const attribute of node.attributes) {
@@ -2857,8 +2927,12 @@ var literalTypeAttr = (node) => {
2857
2927
  }
2858
2928
  return null;
2859
2929
  };
2930
+ var hasFileAcceptAttr = (node) => node.attributes.some(
2931
+ (attribute) => attribute.type === AST_NODE_TYPES9.JSXAttribute && attribute.name.type === AST_NODE_TYPES9.JSXIdentifier && attribute.name.name === "accept"
2932
+ );
2860
2933
  var resolveInputReplacement = (node) => {
2861
2934
  const typeAttr = literalTypeAttr(node);
2935
+ if (hasFileAcceptAttr(node)) return null;
2862
2936
  if (typeAttr === null || typeAttr.kind === "dynamic") return "Input";
2863
2937
  if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;
2864
2938
  return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? "Input";
@@ -2879,7 +2953,7 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
2879
2953
  },
2880
2954
  defaultOptions: [],
2881
2955
  create(context) {
2882
- if (isTestFile(context.filename)) {
2956
+ if (isTestFile(context.filename) || isStoryFile(context.filename)) {
2883
2957
  return {};
2884
2958
  }
2885
2959
  return {
@@ -3200,7 +3274,7 @@ var zod_naming_convention_default = ESLintUtils20.RuleCreator(
3200
3274
  const convention = optionsArg?.convention ?? "either";
3201
3275
  const { test, messageId } = CONVENTIONS[convention];
3202
3276
  const acceptsSchemaWord = convention !== "prefix";
3203
- if (isTestFile(context.filename)) {
3277
+ if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
3204
3278
  return {};
3205
3279
  }
3206
3280
  return {
@@ -5509,7 +5583,9 @@ var EQUALITY_OPERATORS = /* @__PURE__ */ new Set(["===", "!==", "==", "!="]);
5509
5583
  var SENTINEL_IDENTIFIERS = /* @__PURE__ */ new Set(["undefined", "NaN"]);
5510
5584
  var SENTINEL_WORDS = /(^|_)(SENTINEL|EMPTY|NONE|NULL|UNSET|MISSING|PLACEHOLDER|DUMMY|FAKE|EXAMPLE)(_|$)/;
5511
5585
  var SENTINEL_PREFIX_RE = /^(skip|sentinel|empty|none|missing|unset|placeholder|dummy|fake|example|noop)[A-Z]/;
5586
+ var AST_NODE_TYPE_RE = /^(?:TS|JSX)?[A-Z][A-Za-z]*(?:Signature|Keyword|Expression|Declaration|Element|Literal|Identifier)$/;
5512
5587
  function isConstantReference(identifier) {
5588
+ if (AST_NODE_TYPE_RE.test(identifier)) return true;
5513
5589
  if (isAuthSecretName(identifier) && !SENTINEL_WORDS.test(identifier)) return false;
5514
5590
  return identifier === identifier.toUpperCase() && /[A-Za-z]/.test(identifier);
5515
5591
  }
@@ -5776,6 +5852,7 @@ var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set([
5776
5852
  "window",
5777
5853
  "self"
5778
5854
  ]);
5855
+ var PRESIGNED_URL_NAME_RE = /(?:pre-?signed|signed|upload|download)Url$/i;
5779
5856
  function isGlobalFetchCall(node) {
5780
5857
  const callee = node.callee;
5781
5858
  if (callee.type === "Identifier") {
@@ -5786,6 +5863,23 @@ function isGlobalFetchCall(node) {
5786
5863
  }
5787
5864
  return false;
5788
5865
  }
5866
+ function identifierLikeName(node) {
5867
+ if (node.type === "Identifier") {
5868
+ return node.name;
5869
+ }
5870
+ if (node.type === "MemberExpression" && !node.computed && node.property.type === "Identifier") {
5871
+ return node.property.name;
5872
+ }
5873
+ return null;
5874
+ }
5875
+ function isPresignedUrlTransfer(node) {
5876
+ const first = node.arguments[0];
5877
+ if (first === void 0) {
5878
+ return false;
5879
+ }
5880
+ const name = identifierLikeName(first);
5881
+ return name !== null && PRESIGNED_URL_NAME_RE.test(name);
5882
+ }
5789
5883
  function compile(patterns) {
5790
5884
  const compiled = [];
5791
5885
  for (const pattern of patterns) {
@@ -5832,6 +5926,9 @@ var no_raw_fetch_outside_clients_default = ESLintUtils38.RuleCreator(
5832
5926
  }
5833
5927
  return {
5834
5928
  CallExpression(node) {
5929
+ if (isPresignedUrlTransfer(node)) {
5930
+ return;
5931
+ }
5835
5932
  if (isGlobalFetchCall(node)) {
5836
5933
  context.report({ node, messageId: "rawFetch" });
5837
5934
  }
@@ -6463,7 +6560,7 @@ var rules = {
6463
6560
  var plugin = {
6464
6561
  meta: {
6465
6562
  name: "@sarj/eslint-plugin",
6466
- version: "2.12.0"
6563
+ version: "2.12.1"
6467
6564
  },
6468
6565
  rules,
6469
6566
  configs: {
@@ -6488,7 +6585,7 @@ var plugin = {
6488
6585
  "@sarj/prefer-discriminated-union": "warn",
6489
6586
  "@sarj/no-comment-cruft": "warn",
6490
6587
  // Frontend / styling — distilled from frontend PR-review mining.
6491
- "@sarj/prefer-semantic-colors": "warn",
6588
+ "@sarj/prefer-semantic-colors": ["warn", { requireSemanticTokens: true }],
6492
6589
  // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
6493
6590
  "@sarj/no-fat-try-blocks": "warn",
6494
6591
  "@sarj/no-cors-wildcard-with-credentials": "warn",
@@ -6547,7 +6644,7 @@ var plugin = {
6547
6644
  "@sarj/no-comment-cruft": "error",
6548
6645
  // Frontend / styling — distilled from frontend PR-review mining. Stylistic,
6549
6646
  // no autofix → warn (rollout should prove the FP rate before raising it).
6550
- "@sarj/prefer-semantic-colors": "error",
6647
+ "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
6551
6648
  // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
6552
6649
  "@sarj/no-fat-try-blocks": "error",
6553
6650
  "@sarj/no-cors-wildcard-with-credentials": "error",