@sarj/eslint-plugin 19.14.0 → 19.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/catalog.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  publicDocumentation,
3
3
  ruleSpec,
4
4
  src_default
5
- } from "./chunk-YWEV6GQM.js";
5
+ } from "./chunk-SIHWYU24.js";
6
6
 
7
7
  // src/docs/directive-disable-enable-pair.ts
8
8
  var DIRECTIVE_DISABLE_ENABLE_PAIR_DOCUMENTATION = {
@@ -2523,8 +2523,13 @@ var NO_RAW_ENV_DOCUMENTATION = {
2523
2523
  remediation: "Define a Zod settings schema, call SettingsSchema.parse(process.env) once at startup, and export or inject that single parsed settings object. Use Pydantic/Pydantic Settings for the equivalent Python boundary. Required values must fail before remote effects; preserve independent security policy and existing optional defaults.",
2524
2524
  category: "correctness",
2525
2525
  limitations: [
2526
- "Host markers, assignment targets, tests, ordinary scripts and build config retain their policy exemptions. Boundary-named modules, including env.mjs and settings.ts, are checked. A boundary filename or schema declaration alone does not exempt raw reads: each read must flow through a parse/safeParse/createEnv argument, directly or through stable nonexported const aliases whose every use reaches validation. Only unshadowed Number/String/Boolean/BigInt calls are traversed as scalar coercions; arbitrary calls cannot consume raw settings before the parser.",
2527
- "This is bounded same-file syntax analysis (24 flow steps, 128 reads per alias and 512 total expression visits per raw read); flows exceeding these budgets are unreported. It does not prove a parser's implementation, cross-file schemas, schema completeness, safeParse failure handling or a project-wide singleton. Handle safeParse failure before exposing result.data; prefer throwing parse at startup. Nested component settings and separate server/client runtime boundaries remain valid.",
2526
+ "Host markers, assignment targets, tests, ordinary scripts and build config retain their policy exemptions. Boundary-named modules, including env.mjs and settings.ts, are checked. Native named process env imports and simple const { env } = process aliases follow the same read policy; foreign, type-only, shadowed and mutable sources are not inferred.",
2527
+ "Each raw value must reach a parse/safeParse argument or supported environment loader, directly or through stable nonexported const aliases whose every use reaches validation. Directly awaited parseAsync/safeParseAsync calls also count; unawaited promises or later await-through-alias flows do not. Conditional tests and logical-and conditions are control inputs, not the parsed result; value arms and existing nullish/logical-or defaults remain supported.",
2528
+ "Simple nonexported const object patterns may select static keys into plain identifier bindings when every ordinary read of each selected field reaches validation. Type-only references are ignored. Native whole-environment host marker keys retain their exemptions, including through stable aliases; an arbitrary raw field with a marker-shaped property does not. Unused, unparsed, escaping or later-written ordinary fields fail the proof. Opaque computed keys, defaults, rest and nested patterns remain unsupported.",
2529
+ "Renamed value imports of createEnv from @t3-oss/env-core or @t3-oss/env-nextjs retain loader ownership. Existing unresolved project parse/safeParse and legacy createEnv protocols remain accepted; this is not a proof of their implementation. A narrowly proven private identity adapter only forwards its input: that input must still reach an actual parser.",
2530
+ "Identity-adapter detection is limited to an explicit module, no namespace or runtime eval reference, one stable nonexported binding and a synchronous function whose sole plain identifier parameter is returned unchanged. An object may contain only one or two unique own parse/safeParse identity methods, with every ordinary receiver reference a direct method call. Exported/mutable/escaped receivers, spreads, getters, duplicate/opaque keys, extra methods, imported loaders, real Zod wrappers and optional/awaited adapter-result ambiguity retain the existing protocol. The proof stops after 128 binding references, 512 module references or 32 enclosing AST parents; it adds no whole-program library-provenance requirement.",
2531
+ "Only unshadowed Number/String/Boolean/BigInt calls and one-argument native JSON decoding are traversed as preprocessing. JSON.parse alone does not validate settings: its decoded result must itself reach the parser. JSON revivers and arbitrary calls cannot consume raw settings under this preprocessing exemption.",
2532
+ "This is bounded same-file syntax analysis (24 flow steps, 128 selected fields per pattern, 128 reads per binding and 512 total expression/field visits per raw read; whole-environment host-marker provenance follows at most 16 aliases); flows exceeding these budgets are unreported. It does not prove a parser's implementation, cross-file schemas, schema completeness, safeParse failure handling or a project-wide singleton. Handle safeParse failure before exposing result.data; prefer throwing parse at startup. Nested component settings and separate server/client runtime boundaries remain valid.",
2528
2533
  "Assignment-target exemptions also include compound writes that read the previous value. Whole-environment spreads outside boundary modules retain the existing forwarding exemption; a spread inside a boundary must also reach its parser. No automatic rewrite."
2529
2534
  ],
2530
2535
  examples: [
@@ -2559,6 +2564,190 @@ var NO_RAW_ENV_DOCUMENTATION = {
2559
2564
  focusPath: "src/env.ts",
2560
2565
  expectedCount: 1,
2561
2566
  public: true
2567
+ },
2568
+ {
2569
+ id: "native-env-import-read",
2570
+ scenarioId: "native-env-import",
2571
+ title: "Native env import aliases still need validation",
2572
+ outcome: "match",
2573
+ files: [
2574
+ {
2575
+ path: "src/database.ts",
2576
+ source: 'import { env as raw } from "node:process";\nexport const databaseUrl = raw.DATABASE_URL;'
2577
+ }
2578
+ ],
2579
+ focusPath: "src/database.ts",
2580
+ expectedCount: 1,
2581
+ public: true
2582
+ },
2583
+ {
2584
+ id: "native-env-import-validated",
2585
+ scenarioId: "native-env-import",
2586
+ title: "Parse the imported environment once and share settings",
2587
+ outcome: "no-match",
2588
+ files: [
2589
+ {
2590
+ path: "src/database.ts",
2591
+ source: 'import { settings } from "./env.js";\nexport const databaseUrl = settings.DATABASE_URL;'
2592
+ },
2593
+ {
2594
+ path: "src/env.ts",
2595
+ source: 'import { env as raw } from "node:process";\nimport { z } from "zod";\nconst SettingsSchema = z.object({ DATABASE_URL: z.url() });\nexport const settings = SettingsSchema.parse(raw);'
2596
+ }
2597
+ ],
2598
+ focusPath: "src/env.ts",
2599
+ expectedCount: 0,
2600
+ public: true
2601
+ },
2602
+ {
2603
+ id: "json-decoding-without-validation",
2604
+ scenarioId: "json-settings",
2605
+ title: "JSON decoding does not validate the settings contract",
2606
+ outcome: "match",
2607
+ files: [
2608
+ {
2609
+ path: "src/env.ts",
2610
+ source: "export const settings = JSON.parse(process.env.SETTINGS_JSON);"
2611
+ }
2612
+ ],
2613
+ focusPath: "src/env.ts",
2614
+ expectedCount: 1,
2615
+ public: true
2616
+ },
2617
+ {
2618
+ id: "json-decoding-then-validation",
2619
+ scenarioId: "json-settings",
2620
+ title: "Validate decoded environment JSON with the actual schema",
2621
+ outcome: "no-match",
2622
+ files: [
2623
+ {
2624
+ path: "src/env.ts",
2625
+ source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_ORIGIN: z.url() });\nexport const settings = SettingsSchema.parse(JSON.parse(process.env.SETTINGS_JSON));'
2626
+ }
2627
+ ],
2628
+ focusPath: "src/env.ts",
2629
+ expectedCount: 0,
2630
+ public: true
2631
+ },
2632
+ {
2633
+ id: "raw-input-controls-effects",
2634
+ scenarioId: "conditional-settings",
2635
+ title: "A raw conditional input is not the value reaching the parser",
2636
+ outcome: "match",
2637
+ files: [
2638
+ {
2639
+ path: "src/env.ts",
2640
+ source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_ORIGIN: z.url() });\nexport const settings = SettingsSchema.parse(process.env.LOAD_REMOTE ? fetch("https://example.test/config") : {});'
2641
+ }
2642
+ ],
2643
+ focusPath: "src/env.ts",
2644
+ expectedCount: 1,
2645
+ public: true
2646
+ },
2647
+ {
2648
+ id: "validated-input-controls-effects",
2649
+ scenarioId: "conditional-settings",
2650
+ title: "Parse the control setting before remote effects",
2651
+ outcome: "no-match",
2652
+ files: [
2653
+ {
2654
+ path: "src/env.ts",
2655
+ source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ LOAD_REMOTE: z.enum(["0", "1"]).transform(value => value === "1") });\nexport const settings = SettingsSchema.parse(process.env);\nif (settings.LOAD_REMOTE) await fetch("https://example.test/config");'
2656
+ }
2657
+ ],
2658
+ focusPath: "src/env.ts",
2659
+ expectedCount: 0,
2660
+ public: true
2661
+ },
2662
+ {
2663
+ id: "async-settings-promise",
2664
+ scenarioId: "async-settings",
2665
+ title: "An unfinished parser promise is not startup settings",
2666
+ outcome: "match",
2667
+ files: [
2668
+ {
2669
+ path: "src/env.ts",
2670
+ source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1) });\nexport const settings = SettingsSchema.parseAsync(process.env);'
2671
+ }
2672
+ ],
2673
+ focusPath: "src/env.ts",
2674
+ expectedCount: 1,
2675
+ public: true
2676
+ },
2677
+ {
2678
+ id: "async-settings-awaited",
2679
+ scenarioId: "async-settings",
2680
+ title: "Await parsing before exporting the shared settings object",
2681
+ outcome: "no-match",
2682
+ files: [
2683
+ {
2684
+ path: "src/env.ts",
2685
+ source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1) });\nexport const settings = await SettingsSchema.parseAsync(process.env);'
2686
+ }
2687
+ ],
2688
+ focusPath: "src/env.ts",
2689
+ expectedCount: 0,
2690
+ public: true
2691
+ },
2692
+ {
2693
+ id: "selected-fields-raw-escape",
2694
+ scenarioId: "selected-settings-fields",
2695
+ title: "Parsing one selected field does not validate another raw export",
2696
+ outcome: "match",
2697
+ files: [
2698
+ {
2699
+ path: "src/env.ts",
2700
+ source: 'import { env as raw } from "node:process";\nimport { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1) });\nconst { API_KEY, API_ORIGIN } = raw;\nexport const settings = SettingsSchema.parse({ API_KEY });\nexport const unparsedOrigin = API_ORIGIN;'
2701
+ }
2702
+ ],
2703
+ focusPath: "src/env.ts",
2704
+ expectedCount: 1,
2705
+ public: true
2706
+ },
2707
+ {
2708
+ id: "selected-fields-validated",
2709
+ scenarioId: "selected-settings-fields",
2710
+ title: "Validate every selected ordinary field and reuse the parsed result",
2711
+ outcome: "no-match",
2712
+ files: [
2713
+ {
2714
+ path: "src/env.ts",
2715
+ source: 'import { env as raw } from "node:process";\nimport { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1), API_ORIGIN: z.url() });\nconst { API_KEY, API_ORIGIN } = raw;\nexport const settings = SettingsSchema.parse({ API_KEY, API_ORIGIN });'
2716
+ }
2717
+ ],
2718
+ focusPath: "src/env.ts",
2719
+ expectedCount: 0,
2720
+ public: true
2721
+ },
2722
+ {
2723
+ id: "identity-loader-without-parser",
2724
+ scenarioId: "closed-identity-adapter",
2725
+ title: "A local identity adapter does not validate environment input",
2726
+ outcome: "match",
2727
+ files: [
2728
+ {
2729
+ path: "src/env.ts",
2730
+ source: "const createEnv = input => input;\nexport const settings = createEnv(process.env);"
2731
+ }
2732
+ ],
2733
+ focusPath: "src/env.ts",
2734
+ expectedCount: 1,
2735
+ public: true
2736
+ },
2737
+ {
2738
+ id: "identity-loader-before-real-parser",
2739
+ scenarioId: "closed-identity-adapter",
2740
+ title: "An identity adapter can forward into the actual Zod parser",
2741
+ outcome: "no-match",
2742
+ files: [
2743
+ {
2744
+ path: "src/env.ts",
2745
+ source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ API_KEY: z.string().min(1) });\nconst createEnv = input => input;\nexport const settings = SettingsSchema.parse(createEnv(process.env));'
2746
+ }
2747
+ ],
2748
+ focusPath: "src/env.ts",
2749
+ expectedCount: 0,
2750
+ public: true
2562
2751
  }
2563
2752
  ]
2564
2753
  };
@@ -3205,12 +3394,13 @@ var NO_SENTINEL_RETURN_ON_CATCH_DOCUMENTATION = {
3205
3394
 
3206
3395
  // src/docs/no-server-env-in-client-component.ts
3207
3396
  var NO_SERVER_ENV_IN_CLIENT_COMPONENT_DOCUMENTATION = {
3208
- summary: "server-only environment settings imported by a client component",
3209
- rationale: "Next.js client modules run in the browser, where server-only environment values are unavailable; importing a server settings module can produce undefined configuration or bundle a secret-bearing module into the client graph.",
3210
- remediation: "Pass an explicitly public value from a Server Component, or import it from a separately validated client-settings module backed only by NEXT_PUBLIC_* values.",
3397
+ summary: "server-only environment settings imported or re-exported by a client module",
3398
+ rationale: "Next.js client modules run in the browser, where server-only environment values are unavailable; importing or re-exporting a server settings module can produce undefined configuration or bundle a secret-bearing module into the client graph.",
3399
+ remediation: "Pass an explicitly public value from a Server Component, or import/re-export it from a separately validated client-settings module backed only by NEXT_PUBLIC_* values.",
3211
3400
  category: "correctness",
3212
3401
  limitations: [
3213
- "Only static value imports in files with a top-level 'use client' directive and conventionally named server-env/server-settings modules are checked."
3402
+ "Only direct static runtime imports and re-exports in files with a top-level 'use client' directive targeting the existing server-env/server-settings module protocol are checked. Type-only declarations/specifiers, local exports without a source and ordinary server modules are excluded.",
3403
+ "Dynamic imports, require calls and transitive barrel graphs are not followed. Public client-settings modules and unrelated module paths are not classified as server-only."
3214
3404
  ],
3215
3405
  examples: [
3216
3406
  {
@@ -3240,6 +3430,44 @@ var NO_SERVER_ENV_IN_CLIENT_COMPONENT_DOCUMENTATION = {
3240
3430
  focusPath: "status-card.tsx",
3241
3431
  expectedCount: 1,
3242
3432
  public: true
3433
+ },
3434
+ {
3435
+ id: "public-client-settings-re-export",
3436
+ scenarioId: "client-re-export",
3437
+ title: "Forward public settings from the client boundary",
3438
+ outcome: "no-match",
3439
+ files: [
3440
+ {
3441
+ path: "client-barrel.tsx",
3442
+ source: "'use client';\nexport { CLIENT_SETTINGS } from './client-settings.js';\n"
3443
+ },
3444
+ {
3445
+ path: "client-settings.ts",
3446
+ source: "import { z } from 'zod';\nconst SettingsSchema = z.object({ apiOrigin: z.url() });\nexport const CLIENT_SETTINGS = SettingsSchema.parse({ apiOrigin: process.env.NEXT_PUBLIC_API_ORIGIN });\n"
3447
+ }
3448
+ ],
3449
+ focusPath: "client-barrel.tsx",
3450
+ expectedCount: 0,
3451
+ public: true
3452
+ },
3453
+ {
3454
+ id: "server-settings-client-re-export",
3455
+ scenarioId: "client-re-export",
3456
+ title: "A client barrel cannot forward server settings",
3457
+ outcome: "match",
3458
+ files: [
3459
+ {
3460
+ path: "client-barrel.tsx",
3461
+ source: "'use client';\nexport { SERVER_SETTINGS } from './server-settings.js';\n"
3462
+ },
3463
+ {
3464
+ path: "server-settings.ts",
3465
+ source: "import { z } from 'zod';\nconst SettingsSchema = z.object({ apiOrigin: z.url() });\nexport const SERVER_SETTINGS = SettingsSchema.parse({ apiOrigin: process.env.API_ORIGIN });\n"
3466
+ }
3467
+ ],
3468
+ focusPath: "client-barrel.tsx",
3469
+ expectedCount: 1,
3470
+ public: true
3243
3471
  }
3244
3472
  ]
3245
3473
  };
@@ -4545,7 +4773,8 @@ var PREFER_CONFIGURED_CLOUD_SCOPE_DOCUMENTATION = {
4545
4773
  requiresTypeInformation: false,
4546
4774
  limitations: [
4547
4775
  "Initial coverage is canonical HTTPS Cloudflare v4 account-management URLs with a 32-character hexadecimal account segment sent to an unshadowed fetch, stable const alias, or fetch-default parameter. Other providers, SDKs, arbitrary endpoints, Worker names and resource IDs require separate evidence.",
4548
- "Follows bounded same-file const aliases, concatenations and template strings; an unknown account, mutable binding, function return, object property, fallback expression, URL/Request object or cross-file value is not inferred. Direct writes to the ambient fetch or its global receivers veto findings; indirect runtime monkeypatches are not modeled.",
4776
+ "Follows bounded same-file const aliases, concatenations and template strings. One-argument native URL wrappers are supported only for fully known absolute account URLs whose native normalization leaves the text unchanged. Local/imported constructors, dynamic constructor arguments, relative-base URLs, Request objects, unknown accounts, fallback expressions, function returns and cross-file values remain unreported.",
4777
+ "Native URL constructor/prototype mutation or capability capture outside new/typeof vetoes wrapper folding. Escaping/dynamic global receiver access and ambient eval/Function also leave URL semantics unknown. A stored URL must be nonexported, have at most 128 references, and every runtime read must be a direct native fetch argument; mutable instances, aliases, injected transports and other escapes remain unknown. These URL limits do not hide independent direct string fetch findings. Direct writes to ambient fetch or its global receivers retain their existing veto; indirect runtime monkeypatches are not modeled.",
4549
4778
  "Analysis stops at 16 nested expressions or fetch aliases, 256 URL-expression visits per request, 128 string fragments or 8,192 UTF-16 code units of assembled URL text. These limits deliberately leave more complex expressions unreported.",
4550
4779
  "Scripts remain covered. Test/fixture and generated files are excluded. Comparisons, schemas, allowlists and unused URL strings do not produce findings. Each originating account literal is reported at most once per file.",
4551
4780
  "This is a warning without an automatic fix: the deployment configuration boundary and independent authorization policy require review. Application code should import validated configuration as required by no-raw-env; browser-exposed variables must never contain secrets."
@@ -4586,6 +4815,40 @@ var PREFER_CONFIGURED_CLOUD_SCOPE_DOCUMENTATION = {
4586
4815
  focusPath: ".github/scripts/publish.mjs",
4587
4816
  expectedCount: 0,
4588
4817
  public: true
4818
+ },
4819
+ {
4820
+ id: "native-url-literal-account",
4821
+ scenarioId: "native-url-account",
4822
+ title: "A native URL wrapper still fixes the outgoing account in source",
4823
+ outcome: "match",
4824
+ files: [
4825
+ {
4826
+ path: "src/cloud-client.ts",
4827
+ source: 'export function list() { return fetch(new URL("https://api.cloudflare.com/client/v4/accounts/0123456789abcdef0123456789abcdef/workers/scripts")); }'
4828
+ }
4829
+ ],
4830
+ focusPath: "src/cloud-client.ts",
4831
+ expectedCount: 1,
4832
+ public: true
4833
+ },
4834
+ {
4835
+ id: "native-url-configured-account",
4836
+ scenarioId: "native-url-account",
4837
+ title: "Supply the account from shared Zod-validated settings",
4838
+ outcome: "no-match",
4839
+ files: [
4840
+ {
4841
+ path: "src/cloud-client.ts",
4842
+ source: 'import { settings } from "./env.js";\nexport function list() { return fetch(new URL(`https://api.cloudflare.com/client/v4/accounts/${settings.CLOUDFLARE_ACCOUNT_ID}/workers/scripts`)); }'
4843
+ },
4844
+ {
4845
+ path: "src/env.ts",
4846
+ source: 'import { z } from "zod";\nconst SettingsSchema = z.object({ CLOUDFLARE_ACCOUNT_ID: z.string().length(32).regex(/^[a-fA-F0-9]+$/u) });\nexport const settings = SettingsSchema.parse(process.env);'
4847
+ }
4848
+ ],
4849
+ focusPath: "src/cloud-client.ts",
4850
+ expectedCount: 0,
4851
+ public: true
4589
4852
  }
4590
4853
  ]
4591
4854
  };
@@ -15576,9 +15576,9 @@ var PLATFORM_MARKERS = /* @__PURE__ */ new Set([
15576
15576
  "VERCEL_ENV",
15577
15577
  "CI"
15578
15578
  ]);
15579
- function isProcessEnv(node, sourceCode2) {
15580
- const receiver = unwrapExpression(node.object);
15581
- if (receiver.type !== "Identifier" || getPropertyName(node) !== "env") return false;
15579
+ function isNativeProcess(node, sourceCode2) {
15580
+ const receiver = unwrapExpression(node);
15581
+ if (receiver.type !== "Identifier") return false;
15582
15582
  const binding3 = findVariable(sourceCode2.getScope(receiver), receiver.name);
15583
15583
  if (binding3 === null || binding3.defs.length === 0) return receiver.name === "process";
15584
15584
  return binding3.defs.every((definition) => {
@@ -15586,6 +15586,9 @@ function isProcessEnv(node, sourceCode2) {
15586
15586
  return definition.type === "ImportBinding" && parent?.type === "ImportDeclaration" && parent.importKind !== "type" && PROCESS_MODULES.includes(parent.source.value) && PROCESS_IMPORT_SPECIFIERS.includes(definition.node.type);
15587
15587
  });
15588
15588
  }
15589
+ function isProcessEnv(node, sourceCode2) {
15590
+ return getPropertyName(node) === "env" && isNativeProcess(node.object, sourceCode2);
15591
+ }
15589
15592
  function isImportMetaEnv(node) {
15590
15593
  return getPropertyName(node) === "env" && node.object.type === "MetaProperty" && node.object.meta.name === "import" && node.object.property.name === "meta";
15591
15594
  }
@@ -15611,37 +15614,170 @@ function isWholeEnvSpread(node) {
15611
15614
  const parent = parentOf(node);
15612
15615
  return parent?.type === "SpreadElement" && parent.argument === node;
15613
15616
  }
15614
- function reachesValidation(node, sourceCode2, seen = /* @__PURE__ */ new Set(), depth = 0, budget = { remaining: 512 }) {
15615
- if (depth >= 24 || budget.remaining-- <= 0) return true;
15617
+ function createValidationBudget() {
15618
+ let remaining = 512;
15619
+ return () => remaining--;
15620
+ }
15621
+ function reachesValidation(node, sourceCode2, seen = /* @__PURE__ */ new Set(), depth = 0, budget = createValidationBudget()) {
15622
+ if (depth >= 24 || budget() <= 0) return true;
15616
15623
  const parent = parentOf(node);
15617
15624
  if (parent === null) return false;
15618
15625
  switch (parent.type) {
15619
- case "CallExpression":
15620
- return parent.arguments.some((argument) => argument === node) && (isValidationCall(parent) || isScalarCoercion(parent, sourceCode2) && reachesValidation(parent, sourceCode2, seen, depth + 1, budget));
15626
+ case "CallExpression": {
15627
+ const identity2 = isClosedIdentityAdapter(parent, sourceCode2);
15628
+ return parent.arguments.some((argument) => argument === node) && (!identity2 && isValidationCall(parent, sourceCode2) || (identity2 || isScalarCoercion(parent, sourceCode2) || isNativeJsonParse(parent, sourceCode2) && parent.arguments.length === 1) && reachesValidation(parent, sourceCode2, seen, depth + 1, budget));
15629
+ }
15621
15630
  case "VariableDeclarator":
15622
15631
  return parent.init === node && validatedAlias(parent, sourceCode2, seen, depth, budget);
15623
15632
  case "MemberExpression":
15624
15633
  return parent.object === node && reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
15625
15634
  case "Property":
15626
15635
  return parent.value === node && parent.kind === "init" && !parent.method && reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
15636
+ case "ConditionalExpression":
15637
+ return parent.test !== node && reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
15638
+ case "LogicalExpression":
15639
+ return !(parent.operator === "&&" && parent.left === node) && reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
15627
15640
  case "ObjectExpression":
15628
15641
  case "ArrayExpression":
15629
15642
  case "SpreadElement":
15630
- case "LogicalExpression":
15631
15643
  case "BinaryExpression":
15632
- case "ConditionalExpression":
15633
15644
  case "TemplateLiteral":
15634
15645
  case "TSAsExpression":
15635
15646
  case "TSNonNullExpression":
15636
15647
  case "TSSatisfiesExpression":
15648
+ case "TSTypeAssertion":
15637
15649
  return reachesValidation(parent, sourceCode2, seen, depth + 1, budget);
15638
15650
  default:
15639
15651
  return false;
15640
15652
  }
15641
15653
  }
15642
- function isValidationCall(node) {
15654
+ function identityFunction(node) {
15655
+ const value = unwrapExpression(node);
15656
+ if (value.type !== "ArrowFunctionExpression" && value.type !== "FunctionExpression" && value.type !== "FunctionDeclaration")
15657
+ return false;
15658
+ if (value.async || value.type !== "ArrowFunctionExpression" && value.generator || value.params.length !== 1)
15659
+ return false;
15660
+ const [parameter] = value.params;
15661
+ if (parameter?.type !== "Identifier" || value.body === null) return false;
15662
+ let returned2 = value.body;
15663
+ if (returned2.type === "BlockStatement") {
15664
+ const [statement] = returned2.body;
15665
+ if (returned2.body.length !== 1 || statement?.type !== "ReturnStatement" || statement.argument === null)
15666
+ return false;
15667
+ returned2 = statement.argument;
15668
+ }
15669
+ const result = unwrapExpression(returned2);
15670
+ return result.type === "Identifier" && result.name === parameter.name;
15671
+ }
15672
+ function isClosedIdentityAdapter(node, sourceCode2) {
15673
+ const callee = unwrapExpression(node.callee);
15674
+ const member3 = callee.type === "MemberExpression" ? callee : null;
15675
+ const receiver = member3 === null ? callee : unwrapExpression(member3.object);
15676
+ const method = member3 === null ? null : getPropertyName(member3);
15677
+ const output = parentOf(outerExpression(node));
15678
+ if (node.arguments.length !== 1 || node.arguments[0]?.type === "SpreadElement" || node.optional || member3?.optional || output?.type === "AwaitExpression" || output?.type === "ChainExpression")
15679
+ return false;
15680
+ if (receiver.type !== "Identifier" || (member3 === null ? receiver.name !== "createEnv" : method !== "parse" && method !== "safeParse"))
15681
+ return false;
15682
+ const variable = findVariable(sourceCode2.getScope(receiver), receiver.name);
15683
+ if (variable?.defs.length !== 1 || variable.references.length > 128 || !stableValueBinding(variable))
15684
+ return false;
15685
+ const value = privateIdentityValue(variable);
15686
+ if (value === null) return false;
15687
+ const methods = member3 === null ? null : identityMethods(value);
15688
+ if (member3 !== null && (methods === null || !methods.has(method ?? ""))) return false;
15689
+ if (member3 === null && !identityFunction(value)) return false;
15690
+ return identityReferences(variable, methods) && closedIdentityModule(sourceCode2);
15691
+ }
15692
+ function privateIdentityValue(variable) {
15693
+ const definition = variable.defs[0]?.node;
15694
+ if (definition === void 0) return null;
15695
+ let value = definition;
15696
+ let declaration = parentOf(definition);
15697
+ if (definition.type === "VariableDeclarator") {
15698
+ if (definition.id.type !== "Identifier" || definition.init === null || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const")
15699
+ return null;
15700
+ value = unwrapExpression(definition.init);
15701
+ declaration = parentOf(declaration);
15702
+ } else if (definition.type !== "FunctionDeclaration") return null;
15703
+ if (declaration?.type === "ExportNamedDeclaration" || declaration?.type === "ExportDefaultDeclaration")
15704
+ return null;
15705
+ let ancestor = declaration;
15706
+ let ancestors2 = 32;
15707
+ while (ancestor !== null && ancestor.type !== "Program") {
15708
+ if (ancestors2-- <= 0 || ancestor.type === "TSModuleBlock" || ancestor.type === "TSModuleDeclaration")
15709
+ return null;
15710
+ ancestor = parentOf(ancestor);
15711
+ }
15712
+ return value;
15713
+ }
15714
+ function closedIdentityModule(sourceCode2) {
15715
+ if (!sourceCode2.ast.body.some(
15716
+ (statement) => statement.type === "ImportDeclaration" || statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" || statement.type === "ExportAllDeclaration"
15717
+ ))
15718
+ return false;
15719
+ let remaining = 512;
15720
+ for (const scope of sourceCode2.scopeManager.scopes) {
15721
+ for (const reference of scope.references) {
15722
+ if (remaining-- <= 0) return false;
15723
+ if (reference.identifier.name === "eval" && !typeOnlyReference(tsReference(reference)))
15724
+ return false;
15725
+ }
15726
+ }
15727
+ return true;
15728
+ }
15729
+ function identityMethods(value) {
15730
+ if (value.type !== "ObjectExpression" || value.properties.length === 0 || value.properties.length > 2)
15731
+ return null;
15732
+ const methods = /* @__PURE__ */ new Set();
15733
+ for (const property of value.properties) {
15734
+ if (property.type !== "Property" || property.kind !== "init" || !identityFunction(property.value))
15735
+ return null;
15736
+ const name = getPropertyName(property);
15737
+ if (name !== "parse" && name !== "safeParse" || methods.has(name)) return null;
15738
+ methods.add(name);
15739
+ }
15740
+ return methods;
15741
+ }
15742
+ function identityReferences(variable, methods) {
15743
+ return variable.references.every((reference) => {
15744
+ if (reference.init === true || typeOnlyReference(reference)) return true;
15745
+ let target = outerExpression(reference.identifier);
15746
+ if (methods !== null) {
15747
+ const property = parentOf(target);
15748
+ if (property?.type !== "MemberExpression" || property.object !== target || property.optional || !methods.has(getPropertyName(property) ?? ""))
15749
+ return false;
15750
+ target = outerExpression(property);
15751
+ }
15752
+ const use = parentOf(target);
15753
+ return use?.type === "CallExpression" && use.callee === target && !use.optional && parentOf(use)?.type !== "ChainExpression";
15754
+ });
15755
+ }
15756
+ function isValidationCall(node, sourceCode2) {
15757
+ const callee = unwrapExpression(node.callee);
15758
+ if (callee.type === "Identifier") {
15759
+ if (callee.name === "createEnv") return true;
15760
+ const binding3 = findVariable(sourceCode2.getScope(callee), callee.name);
15761
+ const definition = binding3?.defs.length === 1 ? binding3.defs[0] : void 0;
15762
+ const declaration = definition?.parent;
15763
+ const specifier = definition?.node;
15764
+ return definition?.type === "ImportBinding" && declaration?.type === "ImportDeclaration" && declaration.importKind !== "type" && ["@t3-oss/env-core", "@t3-oss/env-nextjs"].includes(declaration.source.value) && specifier?.type === "ImportSpecifier" && specifier.importKind !== "type" && importSpecifierName(specifier) === "createEnv" && binding3 !== null && stableValueBinding(binding3);
15765
+ }
15766
+ if (callee.type !== "MemberExpression" || isNativeJsonParse(node, sourceCode2)) return false;
15767
+ const method = getPropertyName(callee);
15768
+ if (method === "parse" || method === "safeParse") return true;
15769
+ if (method !== "parseAsync" && method !== "safeParseAsync") return false;
15770
+ const value = outerExpression(node);
15771
+ const parent = parentOf(value);
15772
+ return parent?.type === "AwaitExpression" && parent.argument === value;
15773
+ }
15774
+ function isNativeJsonParse(node, sourceCode2) {
15643
15775
  const callee = unwrapExpression(node.callee);
15644
- return callee.type === "Identifier" ? callee.name === "createEnv" : callee.type === "MemberExpression" && ["parse", "safeParse"].includes(getPropertyName(callee) ?? "");
15776
+ if (callee.type !== "MemberExpression" || getPropertyName(callee) !== "parse") return false;
15777
+ const receiver = unwrapExpression(callee.object);
15778
+ if (receiver.type !== "Identifier" || receiver.name !== "JSON") return false;
15779
+ const binding3 = findVariable(sourceCode2.getScope(receiver), receiver.name);
15780
+ return binding3 === null || binding3.defs.length === 0;
15645
15781
  }
15646
15782
  function isScalarCoercion(node, sourceCode2) {
15647
15783
  const callee = unwrapExpression(node.callee);
@@ -15650,19 +15786,73 @@ function isScalarCoercion(node, sourceCode2) {
15650
15786
  const binding3 = findVariable(sourceCode2.getScope(callee), callee.name);
15651
15787
  return binding3 === null || binding3.defs.length === 0;
15652
15788
  }
15789
+ function isWholeEnvironment(node, sourceCode2, seen = /* @__PURE__ */ new Set()) {
15790
+ const value = unwrapExpression(node);
15791
+ if (value.type === "MemberExpression")
15792
+ return isProcessEnv(value, sourceCode2) || isImportMetaEnv(value);
15793
+ if (value.type !== "Identifier" || seen.size >= 16) return false;
15794
+ const variable = stableAliasBinding(value, sourceCode2, seen);
15795
+ const definition = variable?.defs[0];
15796
+ if (variable === null || definition === void 0) return false;
15797
+ const declaration = definition.parent;
15798
+ const binding3 = definition.node;
15799
+ if (definition.type === "ImportBinding") {
15800
+ return declaration?.type === "ImportDeclaration" && declaration.importKind !== "type" && PROCESS_MODULES.includes(declaration.source.value) && binding3.type === "ImportSpecifier" && binding3.importKind !== "type" && importSpecifierName(binding3) === "env";
15801
+ }
15802
+ if (binding3.type !== "VariableDeclarator" || binding3.init === null || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const")
15803
+ return false;
15804
+ if (binding3.id.type === "Identifier")
15805
+ return isWholeEnvironment(binding3.init, sourceCode2, /* @__PURE__ */ new Set([...seen, variable]));
15806
+ return binding3.id.type === "ObjectPattern" && isNativeProcess(binding3.init, sourceCode2) && binding3.id.properties.some(
15807
+ (property) => property.type === "Property" && getPropertyName(property) === "env" && property.value.type === "Identifier" && property.value.name === value.name
15808
+ );
15809
+ }
15810
+ function stableAliasBinding(node, sourceCode2, seen) {
15811
+ const variable = findVariable(sourceCode2.getScope(node), node.name);
15812
+ return variable !== null && variable.defs.length === 1 && !seen.has(variable) && stableValueBinding(variable) ? variable : null;
15813
+ }
15653
15814
  function validatedAlias(node, sourceCode2, seen, depth, budget) {
15654
15815
  const declaration = parentOf(node);
15655
- if (node.id.type !== "Identifier" || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const")
15816
+ if (declaration?.type !== "VariableDeclaration" || declaration.kind !== "const" || parentOf(declaration)?.type === "ExportNamedDeclaration")
15656
15817
  return false;
15657
- if (parentOf(declaration)?.type === "ExportNamedDeclaration") return false;
15658
- const variable = findVariable(sourceCode2.getScope(node.id), node.id.name);
15659
- if (variable === null || seen.has(variable) || variable.references.some((ref) => ref.isWrite() && ref.init !== true))
15818
+ if (node.id.type === "Identifier") {
15819
+ const variable = stableAliasBinding(node.id, sourceCode2, seen);
15820
+ return variable !== null && validatedBinding(variable, sourceCode2, seen, depth, budget);
15821
+ }
15822
+ if (node.id.type !== "ObjectPattern" || node.id.properties.length === 0 || node.init === null)
15660
15823
  return false;
15661
- const reads = variable.references.filter((ref) => ref.isRead());
15824
+ if (node.id.properties.length > 128) return true;
15825
+ return validatedFields(node.id, node.init, sourceCode2, seen, depth, budget);
15826
+ }
15827
+ function validatedFields(pattern, input, sourceCode2, seen, depth, budget) {
15828
+ const wholeEnvironment = isWholeEnvironment(input, sourceCode2);
15829
+ for (const property of pattern.properties) {
15830
+ if (budget() <= 0) return true;
15831
+ if (property.type !== "Property" || property.value.type !== "Identifier") return false;
15832
+ const name = getPropertyName(property);
15833
+ if (name === null) return false;
15834
+ const variable = stableAliasBinding(property.value, sourceCode2, seen);
15835
+ if (variable === null) return false;
15836
+ if (wholeEnvironment && (BUILD_TIME_CONSTANTS.has(name) || PLATFORM_MARKERS.has(name)))
15837
+ continue;
15838
+ if (!validatedBinding(variable, sourceCode2, seen, depth, budget)) return false;
15839
+ }
15840
+ return true;
15841
+ }
15842
+ function validatedBinding(variable, sourceCode2, seen, depth, budget) {
15843
+ const reads = variable.references.filter((ref) => ref.isRead() && !typeOnlyReference(ref));
15662
15844
  const next = /* @__PURE__ */ new Set([...seen, variable]);
15663
15845
  if (reads.length > 128) return true;
15664
15846
  return reads.length > 0 && reads.every((ref) => reachesValidation(ref.identifier, sourceCode2, next, depth + 1, budget));
15665
15847
  }
15848
+ function isPlatformExtraction(node, sourceCode2) {
15849
+ const declaration = parentOf(node);
15850
+ return declaration?.type === "VariableDeclarator" && declaration.init === node && declaration.id.type === "ObjectPattern" && declaration.id.properties.length > 0 && declaration.id.properties.length <= 128 && declaration.id.properties.every((property) => {
15851
+ if (property.type !== "Property" || property.value.type !== "Identifier") return false;
15852
+ const name = getPropertyName(property);
15853
+ return name !== null && (BUILD_TIME_CONSTANTS.has(name) || PLATFORM_MARKERS.has(name));
15854
+ }) && isWholeEnvironment(node, sourceCode2) && validatedAlias(declaration, sourceCode2, /* @__PURE__ */ new Set(), 0, createValidationBudget());
15855
+ }
15666
15856
  var no_raw_env_default = defineRule({
15667
15857
  meta: {
15668
15858
  type: "problem",
@@ -15679,6 +15869,17 @@ var no_raw_env_default = defineRule({
15679
15869
  let sourceCode2;
15680
15870
  let reads = [];
15681
15871
  let boundaryFile = false;
15872
+ function recordRead(node) {
15873
+ if (!isExemptVariableAccess(node) && !isWriteTarget(node) && !isPlatformExtraction(node, sourceCode2) && !(isWholeEnvSpread(node) && !boundaryFile))
15874
+ reads.push(node);
15875
+ }
15876
+ function recordAlias(node) {
15877
+ const variable = findVariable(sourceCode2.getScope(node), node.name);
15878
+ if (variable?.defs.length !== 1 || !stableValueBinding(variable)) return;
15879
+ for (const reference of variable.references) {
15880
+ if (reference.isRead() && !typeOnlyReference(reference)) recordRead(reference.identifier);
15881
+ }
15882
+ }
15682
15883
  return {
15683
15884
  before() {
15684
15885
  const filename = context.filename;
@@ -15691,11 +15892,25 @@ var no_raw_env_default = defineRule({
15691
15892
  reads = [];
15692
15893
  return void 0;
15693
15894
  },
15694
- MemberExpression(node) {
15695
- if ((isProcessEnv(node, sourceCode2) || isImportMetaEnv(node)) && !isExemptVariableAccess(node) && !isWriteTarget(node) && !(isWholeEnvSpread(node) && !boundaryFile)) {
15696
- reads.push(node);
15895
+ ImportDeclaration(node) {
15896
+ if (node.importKind === "type" || !PROCESS_MODULES.includes(node.source.value)) return;
15897
+ for (const specifier of node.specifiers) {
15898
+ if (specifier.type === "ImportSpecifier" && specifier.importKind !== "type" && importSpecifierName(specifier) === "env")
15899
+ recordAlias(specifier.local);
15900
+ }
15901
+ },
15902
+ VariableDeclarator(node) {
15903
+ const declaration = parentOf(node);
15904
+ if (node.id.type !== "ObjectPattern" || node.init === null || declaration?.type !== "VariableDeclaration" || declaration.kind !== "const" || !isNativeProcess(node.init, sourceCode2))
15905
+ return;
15906
+ for (const property of node.id.properties) {
15907
+ if (property.type === "Property" && getPropertyName(property) === "env" && property.value.type === "Identifier")
15908
+ recordAlias(property.value);
15697
15909
  }
15698
15910
  },
15911
+ MemberExpression(node) {
15912
+ if (isProcessEnv(node, sourceCode2) || isImportMetaEnv(node)) recordRead(node);
15913
+ },
15699
15914
  "Program:exit"() {
15700
15915
  for (const node of reads) {
15701
15916
  if (!boundaryFile || !reachesValidation(node, sourceCode2))
@@ -18559,29 +18774,37 @@ function isTypeOnlyImport(node) {
18559
18774
  (specifier) => specifier.type === "ImportSpecifier" && specifier.importKind === "type"
18560
18775
  );
18561
18776
  }
18777
+ function isTypeOnlyExport(node) {
18778
+ return node.exportKind === "type" || node.type === "ExportNamedDeclaration" && node.specifiers.length > 0 && node.specifiers.every((specifier) => specifier.exportKind === "type");
18779
+ }
18562
18780
  var no_server_env_in_client_component_default = defineRule({
18563
18781
  meta: {
18564
18782
  type: "problem",
18565
18783
  docs: {
18566
- description: "server-only environment settings imported by a client component",
18784
+ description: "server-only environment settings imported or re-exported by a client module",
18567
18785
  url: docsUrl("no-server-env-in-client-component")
18568
18786
  },
18569
18787
  schema: [],
18570
18788
  messages: {
18571
- noServerEnvInClientComponent: "A 'use client' module cannot import server-only settings. Pass public data from a Server Component or use a validated client-settings module."
18789
+ noServerEnvInClientComponent: "Do not import or re-export server-only settings from a 'use client' module; use validated client settings."
18572
18790
  }
18573
18791
  },
18574
18792
  createOnce(context) {
18575
18793
  let clientModule = false;
18794
+ function checkDependency(node) {
18795
+ if (!clientModule || node.source === null) return;
18796
+ const typeOnly = node.type === "ImportDeclaration" ? isTypeOnlyImport(node) : isTypeOnlyExport(node);
18797
+ if (!typeOnly && SERVER_ENV_MODULE_RE.test(node.source.value)) {
18798
+ context.report({ node, messageId: "noServerEnvInClientComponent" });
18799
+ }
18800
+ }
18576
18801
  return {
18577
18802
  Program(node) {
18578
18803
  clientModule = isClientModule(node);
18579
18804
  },
18580
- ImportDeclaration(node) {
18581
- if (clientModule && !isTypeOnlyImport(node) && SERVER_ENV_MODULE_RE.test(node.source.value)) {
18582
- context.report({ node, messageId: "noServerEnvInClientComponent" });
18583
- }
18584
- }
18805
+ ImportDeclaration: checkDependency,
18806
+ ExportNamedDeclaration: checkDependency,
18807
+ ExportAllDeclaration: checkDependency
18585
18808
  };
18586
18809
  }
18587
18810
  });
@@ -22094,6 +22317,7 @@ var prefer_coded_error_result_default = defineRule({
22094
22317
  });
22095
22318
 
22096
22319
  // src/rules/prefer-configured-cloud-scope.ts
22320
+ import { URL as NativeURL } from "url";
22097
22321
  var NAME92 = "prefer-configured-cloud-scope";
22098
22322
  var ACCOUNT_PREFIX = "https://api.cloudflare.com/client/v4/accounts/";
22099
22323
  var ACCOUNT_URL = /^https:\/\/api\.cloudflare\.com\/client\/v4\/accounts\/[a-fA-F\d]{32}(?=\/|[?#]|$)/u;
@@ -22129,6 +22353,7 @@ var prefer_configured_cloud_scope_default = defineRule({
22129
22353
  const requests = [];
22130
22354
  let fetchOverridden = false;
22131
22355
  let nodesRemaining = 0;
22356
+ let nativeUrlSafe;
22132
22357
  function bindingOf(node) {
22133
22358
  return findVariable(sourceCode2.getScope(node), node.name);
22134
22359
  }
@@ -22137,7 +22362,8 @@ var prefer_configured_cloud_scope_default = defineRule({
22137
22362
  return binding3 === null || binding3.defs.length === 0;
22138
22363
  }
22139
22364
  function globalReceiver2(node) {
22140
- return node.type === "Identifier" && GLOBAL_RECEIVERS5.has(node.name) && unshadowed(node);
22365
+ const value = unwrapExpression(node);
22366
+ return value.type === "Identifier" && GLOBAL_RECEIVERS5.has(value.name) && unshadowed(value);
22141
22367
  }
22142
22368
  function outgoingFetch(node, seen = /* @__PURE__ */ new Set()) {
22143
22369
  const callee = unwrapExpression(node);
@@ -22160,6 +22386,11 @@ var prefer_configured_cloud_scope_default = defineRule({
22160
22386
  const declarator = definition.node;
22161
22387
  return declarator.id.type === "Identifier" && declarator.init !== null && outgoingFetch(declarator.init, next);
22162
22388
  }
22389
+ function nativeUrlReference(node) {
22390
+ const value = unwrapExpression(node);
22391
+ if (value.type === "Identifier") return value.name === "URL" && unshadowed(value);
22392
+ return value.type === "MemberExpression" && globalReceiver2(value.object) && staticPropertyName(value, sourceCode2) === "URL";
22393
+ }
22163
22394
  function checkWrite(target, depth = 0) {
22164
22395
  if (depth >= 16) {
22165
22396
  fetchOverridden = true;
@@ -22201,17 +22432,7 @@ var prefer_configured_cloud_scope_default = defineRule({
22201
22432
  if (value.type === "Literal" && typeof value.value === "string") {
22202
22433
  return boundedParts([{ text: value.value, source: value }]);
22203
22434
  }
22204
- if (value.type === "Identifier") {
22205
- const binding3 = bindingOf(value);
22206
- if (binding3 === null || seen.has(binding3) || binding3.defs.length !== 1 || binding3.references.some((ref) => ref.isWrite() && ref.init !== true))
22207
- return UNKNOWN;
22208
- const [definition] = binding3.defs;
22209
- if (definition?.type !== "Variable" || definition.parent?.type !== "VariableDeclaration" || definition.parent.kind !== "const")
22210
- return UNKNOWN;
22211
- const declarator = definition.node;
22212
- if (declarator.id.type !== "Identifier" || declarator.init === null) return UNKNOWN;
22213
- return urlParts(declarator.init, /* @__PURE__ */ new Set([...seen, binding3]), depth + 1);
22214
- }
22435
+ if (value.type === "Identifier") return identifierUrlParts(value, seen, depth);
22215
22436
  if (value.type === "BinaryExpression" && value.operator === "+") {
22216
22437
  return boundedParts([
22217
22438
  ...urlParts(value.left, seen, depth + 1),
@@ -22219,8 +22440,78 @@ var prefer_configured_cloud_scope_default = defineRule({
22219
22440
  ]);
22220
22441
  }
22221
22442
  if (value.type === "TemplateLiteral") return templateParts(value, seen, depth);
22443
+ if (value.type === "NewExpression") return nativeUrlParts(value, seen, depth);
22222
22444
  return UNKNOWN;
22223
22445
  }
22446
+ function identifierUrlParts(identifier, seen, depth) {
22447
+ const binding3 = bindingOf(identifier);
22448
+ if (binding3 === null || seen.has(binding3) || binding3.defs.length !== 1 || binding3.references.some((ref) => ref.isWrite() && ref.init !== true))
22449
+ return UNKNOWN;
22450
+ const [definition] = binding3.defs;
22451
+ if (definition?.type !== "Variable" || definition.parent?.type !== "VariableDeclaration" || definition.parent.kind !== "const")
22452
+ return UNKNOWN;
22453
+ const declarator = definition.node;
22454
+ if (declarator.id.type !== "Identifier" || declarator.init === null) return UNKNOWN;
22455
+ const initializer = unwrapExpression(declarator.init);
22456
+ if (initializer.type === "NewExpression" && nativeUrlReference(initializer.callee) && !onlyUrlRequests(binding3, declarator))
22457
+ return UNKNOWN;
22458
+ return urlParts(declarator.init, /* @__PURE__ */ new Set([...seen, binding3]), depth + 1);
22459
+ }
22460
+ function onlyUrlRequests(variable, declarator) {
22461
+ if (variable.references.length > 128) return false;
22462
+ const declaration = parentOf(declarator);
22463
+ if (declaration !== null && parentOf(declaration)?.type === "ExportNamedDeclaration")
22464
+ return false;
22465
+ return variable.references.every((reference) => {
22466
+ if (reference.init === true || typeOnlyReference(reference)) return true;
22467
+ const value = outerExpression(reference.identifier);
22468
+ const use = parentOf(value);
22469
+ if (use?.type !== "CallExpression" || use.arguments[0] !== value) return false;
22470
+ const callee = unwrapExpression(use.callee);
22471
+ return callee.type === "Identifier" && callee.name === "fetch" && unshadowed(callee) || callee.type === "MemberExpression" && globalReceiver2(callee.object) && staticPropertyName(callee, sourceCode2) === "fetch";
22472
+ });
22473
+ }
22474
+ function nativeUrlParts(value, seen, depth) {
22475
+ if (value.arguments.length !== 1 || !nativeUrlReference(value.callee) || !nativeUrlUnchanged())
22476
+ return UNKNOWN;
22477
+ const [argument] = value.arguments;
22478
+ if (argument === void 0 || argument.type === "SpreadElement") return UNKNOWN;
22479
+ const parts = urlParts(argument, seen, depth + 1);
22480
+ if (parts.some((part) => part.source === null)) return UNKNOWN;
22481
+ const text = parts.map((part) => part.text).join("");
22482
+ if (!ACCOUNT_URL.test(text)) return UNKNOWN;
22483
+ try {
22484
+ return new NativeURL(text).href === text ? parts : UNKNOWN;
22485
+ } catch {
22486
+ return UNKNOWN;
22487
+ }
22488
+ }
22489
+ function nativeUrlUnchanged() {
22490
+ if (nativeUrlSafe !== void 0) return nativeUrlSafe;
22491
+ nativeUrlSafe = sourceCode2.scopeManager.scopes.every(
22492
+ (scope) => scope.references.every((reference) => {
22493
+ if (typeOnlyReference(tsReference(reference))) return true;
22494
+ const identifier = reference.identifier;
22495
+ if (["eval", "Function"].includes(identifier.name) && unshadowed(identifier))
22496
+ return false;
22497
+ let value = outerExpression(identifier);
22498
+ if (!nativeUrlReference(value)) {
22499
+ const member3 = parentOf(value);
22500
+ if (globalReceiver2(value)) {
22501
+ if (member3?.type !== "MemberExpression" || member3.object !== value) return false;
22502
+ const key = staticPropertyName(member3, sourceCode2);
22503
+ if (key === null || GLOBAL_RECEIVERS5.has(key)) return false;
22504
+ }
22505
+ if (member3?.type !== "MemberExpression" || member3.object !== value || !nativeUrlReference(member3))
22506
+ return true;
22507
+ value = outerExpression(member3);
22508
+ }
22509
+ const use = parentOf(value);
22510
+ return use?.type === "NewExpression" && use.callee === value || use?.type === "UnaryExpression" && use.operator === "typeof";
22511
+ })
22512
+ );
22513
+ return nativeUrlSafe;
22514
+ }
22224
22515
  function templateParts(value, seen, depth) {
22225
22516
  const parts = [];
22226
22517
  for (let index = 0; index < value.quasis.length; index++) {
@@ -22239,6 +22530,7 @@ var prefer_configured_cloud_scope_default = defineRule({
22239
22530
  reported.clear();
22240
22531
  requests.length = 0;
22241
22532
  fetchOverridden = false;
22533
+ nativeUrlSafe = void 0;
22242
22534
  const file = { filename: context.filename, cwd: context.cwd };
22243
22535
  return !isTestFile(file) && !isGeneratedFile(file, sourceCode2.text);
22244
22536
  },
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  src_default
3
- } from "./chunk-YWEV6GQM.js";
3
+ } from "./chunk-SIHWYU24.js";
4
4
  export {
5
5
  src_default as default
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "19.14.0",
3
+ "version": "19.15.0",
4
4
  "packageManager": "npm@12.2.1",
5
5
  "description": "Custom Oxlint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",