oxlint-plugin-effect 0.0.0 → 0.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/package.json +6 -7
  3. package/src/presets/core.ts +23 -12
  4. package/src/presets/effect-native.ts +18 -25
  5. package/src/presets/full.ts +32 -46
  6. package/src/presets/functional.ts +18 -0
  7. package/src/presets/index.ts +2 -0
  8. package/src/presets/strict.ts +77 -0
  9. package/src/rules/index.ts +17 -28
  10. package/src/rules/no-arrow-ladder.ts +1 -2
  11. package/src/rules/no-async-function.ts +1 -2
  12. package/src/rules/no-catch-all-to-map-error.ts +89 -0
  13. package/src/rules/no-effect-fn-generator.ts +1 -2
  14. package/src/rules/no-effect-gen-adapter.ts +1 -2
  15. package/src/rules/no-effect-map-flatten.ts +82 -0
  16. package/src/rules/no-effect-map-void.ts +1 -2
  17. package/src/rules/no-effect-orElse-ladder.ts +1 -2
  18. package/src/rules/no-effect-succeed-string.ts +1 -2
  19. package/src/rules/no-effect-succeed-variable.ts +1 -2
  20. package/src/rules/no-effect-succeed-void.ts +1 -2
  21. package/src/rules/no-effect-sync-console.ts +1 -2
  22. package/src/rules/no-effect-sync-wrapper.ts +1 -2
  23. package/src/rules/no-effect-type-alias.ts +1 -2
  24. package/src/rules/no-effect-wrapper-alias.ts +1 -4
  25. package/src/rules/no-extends-native-error.ts +1 -2
  26. package/src/rules/no-flatmap-ladder.ts +1 -3
  27. package/src/rules/no-fromnullable-coalesce.ts +1 -2
  28. package/src/rules/no-global-error-in-catch.ts +104 -0
  29. package/src/rules/no-global-error-in-failure.ts +83 -0
  30. package/src/rules/no-globals.ts +178 -0
  31. package/src/rules/no-iife-wrapper.ts +1 -2
  32. package/src/rules/{no-inline-runtime-provide.ts → no-inline-provide.ts} +3 -4
  33. package/src/rules/no-instanceof-schema.ts +2 -3
  34. package/src/rules/no-manual-effect-channels.ts +1 -2
  35. package/src/rules/no-match-effect-branch.ts +1 -2
  36. package/src/rules/no-match-void-branch.ts +1 -2
  37. package/src/rules/no-multiple-effect-provide.ts +75 -0
  38. package/src/rules/no-nested-effect-call.ts +1 -3
  39. package/src/rules/no-nested-effect-gen.ts +1 -2
  40. package/src/rules/no-nested-pipe.ts +1 -4
  41. package/src/rules/no-new-error.ts +46 -10
  42. package/src/rules/no-new-promise.ts +43 -7
  43. package/src/rules/no-node-builtin-import.ts +1 -3
  44. package/src/rules/no-option-boolean-normalization.ts +1 -2
  45. package/src/rules/no-positional-log-error.ts +76 -0
  46. package/src/rules/no-redundant-schema-tag-identifier.ts +62 -0
  47. package/src/rules/no-return-in-arrow.ts +1 -2
  48. package/src/rules/no-return-nullish.ts +80 -0
  49. package/src/rules/no-schema-struct-with-tag.ts +64 -0
  50. package/src/rules/no-schema-union-of-literals.ts +48 -0
  51. package/src/rules/no-string-sentinel-const.ts +1 -2
  52. package/src/rules/no-ternary.ts +1 -3
  53. package/src/rules/no-throw-statement.ts +32 -6
  54. package/src/rules/no-try-catch.ts +32 -7
  55. package/src/rules/no-unnecessary-arrow-block.ts +1 -2
  56. package/src/rules/no-unnecessary-effect-gen.ts +1 -3
  57. package/src/rules/no-unnecessary-pipe-chain.ts +47 -0
  58. package/src/rules/no-unnecessary-pipe.ts +1 -2
  59. package/src/vendor/effect-oxlint/Rule.ts +111 -173
  60. package/tests/ban-rules.test.ts +0 -167
  61. package/tests/context-rules.test.ts +75 -158
  62. package/tests/pattern-rules.test.ts +39 -50
  63. package/src/rules/no-console-in-effect.ts +0 -46
  64. package/src/rules/no-date-in-effect.ts +0 -61
  65. package/src/rules/no-dynamic-import.ts +0 -13
  66. package/src/rules/no-fetch-in-effect.ts +0 -47
  67. package/src/rules/no-global-console.ts +0 -17
  68. package/src/rules/no-global-date.ts +0 -20
  69. package/src/rules/no-global-fetch.ts +0 -14
  70. package/src/rules/no-global-random.ts +0 -13
  71. package/src/rules/no-global-timers.ts +0 -17
  72. package/src/rules/no-json-in-effect.ts +0 -45
  73. package/src/rules/no-json-parse.ts +0 -18
  74. package/src/rules/no-platform-globals.ts +0 -117
  75. package/src/rules/no-process-env-in-effect.ts +0 -39
  76. package/src/rules/no-process-env.ts +0 -13
  77. package/src/rules/no-random-in-effect.ts +0 -39
  78. package/src/rules/no-return-null.ts +0 -47
  79. package/src/rules/no-throw-in-effect-gen.ts +0 -42
  80. package/src/rules/no-timers-in-effect.ts +0 -47
  81. package/src/rules/no-try-catch-in-effect-gen.ts +0 -42
@@ -1,14 +0,0 @@
1
- /**
2
- * Ban bare `fetch()` calls.
3
- *
4
- * Use Effect HttpClient service instead.
5
- *
6
- * Source: language-service/globalFetch
7
- */
8
- import { Rule } from "../vendor/effect-oxlint/index.js"
9
-
10
- export const noGlobalFetch = Rule.banCallOf("fetch", {
11
- message:
12
- "Avoid global fetch. Use HttpClient from 'effect/unstable/http'.",
13
- meta: { type: "suggestion" },
14
- })
@@ -1,13 +0,0 @@
1
- /**
2
- * Ban `Math.random()`.
3
- *
4
- * Use Effect Random service instead.
5
- *
6
- * Source: language-service/globalRandom
7
- */
8
- import { Rule } from "../vendor/effect-oxlint/index.js"
9
-
10
- export const noGlobalRandom = Rule.banMember("Math", "random", {
11
- message: "Avoid Math.random(). Use Random service.",
12
- meta: { type: "suggestion" },
13
- })
@@ -1,17 +0,0 @@
1
- /**
2
- * Ban `setTimeout` and `setInterval`.
3
- *
4
- * Use Effect Schedule or Effect.sleep instead.
5
- *
6
- * Source: language-service/globalTimers
7
- */
8
- import { Rule } from "../vendor/effect-oxlint/index.js"
9
-
10
- export const noGlobalTimers = Rule.banCallOf(
11
- ["setTimeout", "setInterval"],
12
- {
13
- message:
14
- "Avoid setTimeout/setInterval. Use Effect.sleep or Schedule.",
15
- meta: { type: "suggestion" },
16
- },
17
- )
@@ -1,45 +0,0 @@
1
- /**
2
- * Ban `JSON.parse`/`JSON.stringify` inside Effect.gen/fn context.
3
- *
4
- * Use Schema.fromJsonString for type-safe JSON.
5
- *
6
- * Source: language-service/preferSchemaOverJson (inside Effect context)
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
10
- import { RuleContext } from "../vendor/effect-oxlint/index.js"
11
- import * as Effect from "effect/Effect"
12
- import * as Option from "effect/Option"
13
- import * as Ref from "effect/Ref"
14
-
15
- import { makeEffectContextTracker } from "./_effect-context.js"
16
-
17
- export const noJsonInEffect = Rule.define({
18
- name: "no-json-in-effect",
19
- meta: Rule.meta({
20
- type: "suggestion",
21
- description: "Avoid JSON.parse/stringify inside Effect.gen/fn. Use Schema.fromJsonString.",
22
- }),
23
- create: function* () {
24
- const ctx = yield* RuleContext
25
- const [depth, tracker] = yield* makeEffectContextTracker
26
-
27
- return Visitor.merge(tracker, {
28
- MemberExpression: (node) =>
29
- Effect.flatMap(Ref.get(depth), (d) => {
30
- if (
31
- d > 0 &&
32
- Option.isSome(AST.matchMember(node as ESTree.MemberExpression, "JSON", ["parse", "stringify"]))
33
- ) {
34
- return ctx.report(
35
- Diagnostic.make({
36
- node,
37
- message: "Avoid JSON.parse/stringify inside Effect context. Use Schema.fromJsonString.",
38
- }),
39
- )
40
- }
41
- return Effect.void
42
- }),
43
- })
44
- },
45
- })
@@ -1,18 +0,0 @@
1
- /**
2
- * Ban `JSON.parse` and `JSON.stringify`.
3
- *
4
- * Use Schema.fromJsonString for type-safe JSON handling.
5
- *
6
- * Source: language-service/preferSchemaOverJson
7
- */
8
- import { Rule } from "../vendor/effect-oxlint/index.js"
9
-
10
- export const noJsonParse = Rule.banMember(
11
- "JSON",
12
- ["parse", "stringify"],
13
- {
14
- message:
15
- "Avoid JSON.parse/stringify. Use Schema.fromJsonString for type-safe JSON.",
16
- meta: { type: "suggestion" },
17
- },
18
- )
@@ -1,117 +0,0 @@
1
- /**
2
- * Ban platform-specific global access inside Effect.gen/fn context.
3
- *
4
- * Catches `Bun.*`, `process.*`, `Deno.*` — any runtime-specific API
5
- * that should be wrapped in an Effect service for testability and portability.
6
- *
7
- * Provides Effect alternative hints where known.
8
- *
9
- * Sources: language-service/processEnv, processEnvInEffect + generalized
10
- */
11
- import type { ESTree } from "@oxlint/plugins"
12
- import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
13
- import { RuleContext } from "../vendor/effect-oxlint/index.js"
14
- import * as Effect from "effect/Effect"
15
- import * as Option from "effect/Option"
16
- import * as Ref from "effect/Ref"
17
-
18
- import { makeEffectContextTracker } from "./_effect-context.js"
19
-
20
- const platformObjects = new Set(["Bun", "process", "Deno"])
21
-
22
- /**
23
- * Map of `Object.property` → Effect alternative hint.
24
- * Falls back to a generic message for unknown properties.
25
- */
26
- const alternatives: Record<string, Record<string, string>> = {
27
- Bun: {
28
- file: "Use FileSystem from 'effect'",
29
- write: "Use FileSystem from 'effect'",
30
- read: "Use FileSystem from 'effect'",
31
- serve: "Use HttpServer from 'effect/unstable/http'",
32
- env: "Use Config from 'effect'",
33
- spawn: "Use Command from 'effect'",
34
- sleep: "Use Effect.sleep",
35
- stdin: "Use Terminal from 'effect'",
36
- stdout: "Use Terminal from 'effect'",
37
- stderr: "Use Terminal from 'effect'",
38
- cwd: "Use Path from 'effect'",
39
- which: "Use Command from 'effect'",
40
- resolveSync: "Use Path from 'effect'",
41
- pathToFileURL: "Use Path from 'effect'",
42
- },
43
- process: {
44
- env: "Use Config from 'effect'",
45
- exit: "Use Effect.fail or Effect.die",
46
- cwd: "Use Path from 'effect'",
47
- argv: "Use Args/Command from 'effect/unstable/cli'",
48
- stdin: "Use Terminal from 'effect'",
49
- stdout: "Use Terminal from 'effect'",
50
- stderr: "Use Terminal from 'effect'",
51
- chdir: "Use Path from 'effect'",
52
- kill: "Use Command from 'effect'",
53
- on: "Use Effect.async or Stream.async",
54
- platform: "Use PlatformInfo from 'effect'",
55
- arch: "Use PlatformInfo from 'effect'",
56
- execPath: "Use Command from 'effect'",
57
- execArgv: "Use Args/Command from 'effect/unstable/cli'",
58
- },
59
- Deno: {
60
- readFile: "Use FileSystem from 'effect'",
61
- writeFile: "Use FileSystem from 'effect'",
62
- readTextFile: "Use FileSystem from 'effect'",
63
- writeTextFile: "Use FileSystem from 'effect'",
64
- open: "Use FileSystem from 'effect'",
65
- remove: "Use FileSystem from 'effect'",
66
- mkdir: "Use FileSystem from 'effect'",
67
- readDir: "Use FileSystem from 'effect'",
68
- stat: "Use FileSystem from 'effect'",
69
- cwd: "Use Path from 'effect'",
70
- env: "Use Config from 'effect'",
71
- exit: "Use Effect.fail or Effect.die",
72
- serve: "Use HttpServer from 'effect/unstable/http'",
73
- run: "Use Command from 'effect'",
74
- args: "Use Args/Command from 'effect/unstable/cli'",
75
- },
76
- }
77
-
78
- function getHint(obj: string, prop: string): string {
79
- const hint = alternatives[obj]?.[prop]
80
- if (hint) return `. ${hint}`
81
- return ". Wrap in a service for testability"
82
- }
83
-
84
- export const noPlatformGlobals = Rule.define({
85
- name: "no-platform-globals",
86
- meta: Rule.meta({
87
- type: "suggestion",
88
- description:
89
- "Avoid platform globals (Bun.*, process.*, Deno.*) inside Effect context. Use Effect platform services.",
90
- }),
91
- create: function* () {
92
- const ctx = yield* RuleContext
93
- const [depth, tracker] = yield* makeEffectContextTracker
94
-
95
- return Visitor.merge(tracker, {
96
- MemberExpression: (node) =>
97
- Effect.flatMap(Ref.get(depth), (d) => {
98
- if (d <= 0) return Effect.void
99
-
100
- const names = Option.getOrUndefined(
101
- AST.memberNames(node as ESTree.MemberExpression),
102
- )
103
- if (names === undefined) return Effect.void
104
-
105
- const [obj, prop] = names
106
- if (!platformObjects.has(obj)) return Effect.void
107
-
108
- return ctx.report(
109
- Diagnostic.make({
110
- node,
111
- message: `Avoid ${obj}.${prop} inside Effect context${getHint(obj, prop)}.`,
112
- }),
113
- )
114
- }),
115
- })
116
- },
117
- })
@@ -1,39 +0,0 @@
1
- /**
2
- * Ban `process.env` inside Effect.gen/fn context.
3
- *
4
- * Use Effect Config service instead.
5
- *
6
- * Source: language-service/processEnvInEffect
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
10
- import { RuleContext } from "../vendor/effect-oxlint/index.js"
11
- import * as Effect from "effect/Effect"
12
- import * as Option from "effect/Option"
13
- import * as Ref from "effect/Ref"
14
-
15
- import { makeEffectContextTracker } from "./_effect-context.js"
16
-
17
- export const noProcessEnvInEffect = Rule.define({
18
- name: "no-process-env-in-effect",
19
- meta: Rule.meta({
20
- type: "suggestion",
21
- description: "Avoid process.env inside Effect.gen/fn. Use Config service.",
22
- }),
23
- create: function* () {
24
- const ctx = yield* RuleContext
25
- const [depth, tracker] = yield* makeEffectContextTracker
26
-
27
- return Visitor.merge(tracker, {
28
- MemberExpression: (node) =>
29
- Effect.flatMap(Ref.get(depth), (d) => {
30
- if (d > 0 && Option.isSome(AST.matchMember(node as ESTree.MemberExpression, "process", "env"))) {
31
- return ctx.report(
32
- Diagnostic.make({ node, message: "Avoid process.env inside Effect context. Use Config service." }),
33
- )
34
- }
35
- return Effect.void
36
- }),
37
- })
38
- },
39
- })
@@ -1,13 +0,0 @@
1
- /**
2
- * Ban `process.env` access.
3
- *
4
- * Use Effect Config service instead.
5
- *
6
- * Source: language-service/processEnv
7
- */
8
- import { Rule } from "../vendor/effect-oxlint/index.js"
9
-
10
- export const noProcessEnv = Rule.banMember("process", "env", {
11
- message: "Avoid process.env. Use Effect Config service.",
12
- meta: { type: "suggestion" },
13
- })
@@ -1,39 +0,0 @@
1
- /**
2
- * Ban `Math.random()` inside Effect.gen/fn context.
3
- *
4
- * Use Random service instead.
5
- *
6
- * Source: language-service/globalRandomInEffect
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
10
- import { RuleContext } from "../vendor/effect-oxlint/index.js"
11
- import * as Effect from "effect/Effect"
12
- import * as Option from "effect/Option"
13
- import * as Ref from "effect/Ref"
14
-
15
- import { makeEffectContextTracker } from "./_effect-context.js"
16
-
17
- export const noRandomInEffect = Rule.define({
18
- name: "no-random-in-effect",
19
- meta: Rule.meta({
20
- type: "suggestion",
21
- description: "Avoid Math.random() inside Effect.gen/fn. Use Random service.",
22
- }),
23
- create: function* () {
24
- const ctx = yield* RuleContext
25
- const [depth, tracker] = yield* makeEffectContextTracker
26
-
27
- return Visitor.merge(tracker, {
28
- MemberExpression: (node) =>
29
- Effect.flatMap(Ref.get(depth), (d) => {
30
- if (d > 0 && Option.isSome(AST.matchMember(node as ESTree.MemberExpression, "Math", "random"))) {
31
- return ctx.report(
32
- Diagnostic.make({ node, message: "Avoid Math.random() inside Effect context. Use Random service." }),
33
- )
34
- }
35
- return Effect.void
36
- }),
37
- })
38
- },
39
- })
@@ -1,47 +0,0 @@
1
- /**
2
- * Ban `return null` statements.
3
- *
4
- * Use Option.none for absence or Effect.fail for errors.
5
- *
6
- * Source: biome-effect-linting-rules/no-return-null
7
- */
8
- import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
9
- import { RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
- import * as Option from "effect/Option"
12
- import { pipe } from "effect/Function"
13
-
14
- export const noReturnNull = Rule.define({
15
- name: "no-return-null",
16
- meta: Rule.meta({
17
- type: "suggestion",
18
- description:
19
- "Avoid returning null. Use Option.none for absence or Effect.fail for errors.",
20
- }),
21
- create: function* () {
22
- const ctx = yield* RuleContext
23
- return {
24
- ReturnStatement: (node) => {
25
- if (
26
- node.type === "ReturnStatement" &&
27
- "argument" in node &&
28
- node.argument !== null &&
29
- node.argument !== undefined &&
30
- "type" in node.argument &&
31
- node.argument.type === "Literal" &&
32
- "value" in node.argument &&
33
- node.argument.value === null
34
- ) {
35
- return ctx.report(
36
- Diagnostic.make({
37
- node,
38
- message:
39
- "Avoid returning null. Use Option.none for absence.",
40
- }),
41
- )
42
- }
43
- return Effect.void
44
- },
45
- }
46
- },
47
- })
@@ -1,42 +0,0 @@
1
- /**
2
- * Ban `throw` statements inside Effect.gen/fn generators.
3
- *
4
- * Use Effect.fail with tagged errors instead.
5
- *
6
- * Source: effect convention — errors should be in the type system
7
- */
8
- import { Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
9
- import { RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
- import * as Ref from "effect/Ref"
12
-
13
- import { makeEffectContextTracker } from "./_effect-context.js"
14
-
15
- export const noThrowInEffectGen = Rule.define({
16
- name: "no-throw-in-effect-gen",
17
- meta: Rule.meta({
18
- type: "problem",
19
- description:
20
- "Avoid throw inside Effect.gen/fn. Use Effect.fail with tagged errors.",
21
- }),
22
- create: function* () {
23
- const ctx = yield* RuleContext
24
- const [depth, tracker] = yield* makeEffectContextTracker
25
-
26
- return Visitor.merge(tracker, {
27
- ThrowStatement: (node) =>
28
- Effect.flatMap(Ref.get(depth), (d) => {
29
- if (d > 0) {
30
- return ctx.report(
31
- Diagnostic.make({
32
- node,
33
- message:
34
- "Avoid throw inside Effect.gen/fn. Use Effect.fail with tagged errors.",
35
- }),
36
- )
37
- }
38
- return Effect.void
39
- }),
40
- })
41
- },
42
- })
@@ -1,47 +0,0 @@
1
- /**
2
- * Ban `setTimeout`/`setInterval` inside Effect.gen/fn context.
3
- *
4
- * Use Effect.sleep or Schedule instead.
5
- *
6
- * Source: language-service/globalTimersInEffect
7
- */
8
- import type { ESTree } from "@oxlint/plugins"
9
- import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
10
- import { RuleContext } from "../vendor/effect-oxlint/index.js"
11
- import * as Effect from "effect/Effect"
12
- import * as Option from "effect/Option"
13
- import * as Ref from "effect/Ref"
14
-
15
- import { makeEffectContextTracker } from "./_effect-context.js"
16
-
17
- const timerNames = ["setTimeout", "setInterval"]
18
-
19
- export const noTimersInEffect = Rule.define({
20
- name: "no-timers-in-effect",
21
- meta: Rule.meta({
22
- type: "suggestion",
23
- description: "Avoid setTimeout/setInterval inside Effect.gen/fn. Use Effect.sleep or Schedule.",
24
- }),
25
- create: function* () {
26
- const ctx = yield* RuleContext
27
- const [depth, tracker] = yield* makeEffectContextTracker
28
-
29
- return Visitor.merge(tracker, {
30
- CallExpression: (node) =>
31
- Effect.flatMap(Ref.get(depth), (d) => {
32
- if (d > 0) {
33
- const name = Option.getOrUndefined(AST.calleeName(node as ESTree.CallExpression))
34
- if (name !== undefined && timerNames.includes(name)) {
35
- return ctx.report(
36
- Diagnostic.make({
37
- node,
38
- message: "Avoid setTimeout/setInterval inside Effect context. Use Effect.sleep or Schedule.",
39
- }),
40
- )
41
- }
42
- }
43
- return Effect.void
44
- }),
45
- })
46
- },
47
- })
@@ -1,42 +0,0 @@
1
- /**
2
- * Ban `try/catch` inside Effect.gen/fn generators.
3
- *
4
- * Use Effect.try, Effect.tryPromise, or Effect.catch* instead.
5
- *
6
- * Source: language-service/tryCatchInEffectGen
7
- */
8
- import { Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
9
- import { RuleContext } from "../vendor/effect-oxlint/index.js"
10
- import * as Effect from "effect/Effect"
11
- import * as Ref from "effect/Ref"
12
-
13
- import { makeEffectContextTracker } from "./_effect-context.js"
14
-
15
- export const noTryCatchInEffectGen = Rule.define({
16
- name: "no-try-catch-in-effect-gen",
17
- meta: Rule.meta({
18
- type: "suggestion",
19
- description:
20
- "Avoid try/catch inside Effect.gen/fn. Use Effect.try or Effect.tryPromise.",
21
- }),
22
- create: function* () {
23
- const ctx = yield* RuleContext
24
- const [depth, tracker] = yield* makeEffectContextTracker
25
-
26
- return Visitor.merge(tracker, {
27
- TryStatement: (node) =>
28
- Effect.flatMap(Ref.get(depth), (d) => {
29
- if (d > 0) {
30
- return ctx.report(
31
- Diagnostic.make({
32
- node,
33
- message:
34
- "Avoid try/catch inside Effect.gen/fn. Use Effect.try, Effect.tryPromise, or Effect.catch*.",
35
- }),
36
- )
37
- }
38
- return Effect.void
39
- }),
40
- })
41
- },
42
- })