@effect/language-service 0.84.2 → 0.85.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/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Pipeable.js
3
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Pipeable.js
4
4
  var pipeArguments = (self, args3) => {
5
5
  switch (args3.length) {
6
6
  case 0:
@@ -44,7 +44,7 @@ var Class = /* @__PURE__ */ (function() {
44
44
  return PipeableBase;
45
45
  })();
46
46
 
47
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Function.js
47
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Function.js
48
48
  var dual = function(arity, body) {
49
49
  if (typeof arity === "function") {
50
50
  return function() {
@@ -92,9 +92,9 @@ function pipe(a, ...args3) {
92
92
  return pipeArguments(a, args3);
93
93
  }
94
94
 
95
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Equivalence.js
95
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Equivalence.js
96
96
  var make = (isEquivalent) => (self, that) => self === that || isEquivalent(self, that);
97
- function Array2(item) {
97
+ function Array_(item) {
98
98
  return make((self, that) => {
99
99
  if (self.length !== that.length) return false;
100
100
  for (let i = 0; i < self.length; i++) {
@@ -104,7 +104,7 @@ function Array2(item) {
104
104
  });
105
105
  }
106
106
 
107
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/equal.js
107
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/equal.js
108
108
  var getAllObjectKeys = (obj) => {
109
109
  const keys2 = new Set(Reflect.ownKeys(obj));
110
110
  if (obj.constructor === Object) return keys2;
@@ -127,7 +127,7 @@ var getAllObjectKeys = (obj) => {
127
127
  };
128
128
  var byReferenceInstances = /* @__PURE__ */ new WeakSet();
129
129
 
130
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Predicate.js
130
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Predicate.js
131
131
  function isString(input) {
132
132
  return typeof input === "string";
133
133
  }
@@ -148,7 +148,7 @@ function isObjectKeyword(input) {
148
148
  }
149
149
  var hasProperty = /* @__PURE__ */ dual(2, (self, property) => isObjectKeyword(self) && property in self);
150
150
 
151
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Hash.js
151
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Hash.js
152
152
  var symbol = "~effect/interfaces/Hash";
153
153
  var hash = (self) => {
154
154
  switch (typeof self) {
@@ -267,7 +267,7 @@ function withVisitedTracking(obj, fn2) {
267
267
  return result;
268
268
  }
269
269
 
270
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Equal.js
270
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Equal.js
271
271
  var symbol2 = "~effect/interfaces/Equal";
272
272
  function equals() {
273
273
  if (arguments.length === 1) {
@@ -429,7 +429,7 @@ var compareSets = /* @__PURE__ */ makeCompareSet(compareBoth);
429
429
  var isEqual = (u) => hasProperty(u, symbol2);
430
430
  var asEquivalence = () => equals;
431
431
 
432
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Redactable.js
432
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Redactable.js
433
433
  var symbolRedactable = /* @__PURE__ */ Symbol.for("~effect/Inspectable/redactable");
434
434
  var isRedactable = (u) => hasProperty(u, symbolRedactable);
435
435
  function redact(u) {
@@ -448,7 +448,7 @@ var emptyServiceMap = {
448
448
  }
449
449
  };
450
450
 
451
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Formatter.js
451
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Formatter.js
452
452
  function format(input, options) {
453
453
  const space = options?.space ?? 0;
454
454
  const seen = /* @__PURE__ */ new WeakSet();
@@ -527,7 +527,7 @@ function safeToString(input) {
527
527
  }
528
528
  }
529
529
 
530
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Inspectable.js
530
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Inspectable.js
531
531
  var NodeInspectSymbol = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
532
532
  var toJson = (input) => {
533
533
  try {
@@ -571,7 +571,7 @@ var Class2 = class {
571
571
  }
572
572
  };
573
573
 
574
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Utils.js
574
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Utils.js
575
575
  var SingleShotGen = class _SingleShotGen {
576
576
  called = false;
577
577
  self;
@@ -614,7 +614,7 @@ var forced = {
614
614
  var isNotOptimizedAway = /* @__PURE__ */ standard[InternalTypeId](() => new Error().stack)?.includes(InternalTypeId) === true;
615
615
  var internalCall = isNotOptimizedAway ? standard[InternalTypeId] : forced[InternalTypeId];
616
616
 
617
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/core.js
617
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/core.js
618
618
  var EffectTypeId = `~effect/Effect`;
619
619
  var ExitTypeId = `~effect/Exit`;
620
620
  var effectVariance = {
@@ -961,7 +961,7 @@ var DoneVoid = {
961
961
  value: void 0
962
962
  };
963
963
 
964
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/option.js
964
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/option.js
965
965
  var TypeId = "~effect/data/Option";
966
966
  var CommonProto = {
967
967
  [TypeId]: {
@@ -1032,7 +1032,7 @@ var some = (value) => {
1032
1032
  return a;
1033
1033
  };
1034
1034
 
1035
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/result.js
1035
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/result.js
1036
1036
  var TypeId2 = "~effect/data/Result";
1037
1037
  var CommonProto2 = {
1038
1038
  [TypeId2]: {
@@ -1103,7 +1103,7 @@ var succeed = (success) => {
1103
1103
  return a;
1104
1104
  };
1105
1105
 
1106
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Result.js
1106
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Result.js
1107
1107
  var succeed2 = succeed;
1108
1108
  var fail2 = fail;
1109
1109
  var isFailure2 = isFailure;
@@ -1111,10 +1111,10 @@ var isSuccess2 = isSuccess;
1111
1111
  var map = /* @__PURE__ */ dual(2, (self, f) => isSuccess2(self) ? succeed2(f(self.success)) : fail2(self.failure));
1112
1112
  var getOrElse = /* @__PURE__ */ dual(2, (self, onFailure) => isFailure2(self) ? onFailure(self.failure) : self.success);
1113
1113
 
1114
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/array.js
1114
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/array.js
1115
1115
  var isArrayNonEmpty = (self) => self.length > 0;
1116
1116
 
1117
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Order.js
1117
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Order.js
1118
1118
  function make2(compare) {
1119
1119
  return (self, that) => self === that ? 0 : compare(self, that);
1120
1120
  }
@@ -1137,7 +1137,7 @@ var combine2 = /* @__PURE__ */ dual(2, (self, that) => make2((a1, a2) => {
1137
1137
  }));
1138
1138
  var mapInput = /* @__PURE__ */ dual(2, (self, f) => make2((b1, b2) => self(f(b1), f(b2))));
1139
1139
 
1140
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Option.js
1140
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Option.js
1141
1141
  var none2 = () => none;
1142
1142
  var some2 = some;
1143
1143
  var isNone2 = isNone;
@@ -1148,7 +1148,7 @@ var fromNullishOr = (a) => a == null ? none2() : some2(a);
1148
1148
  var getOrUndefined = /* @__PURE__ */ getOrElse2(constUndefined);
1149
1149
  var map2 = /* @__PURE__ */ dual(2, (self, f) => isNone2(self) ? none2() : some2(f(self.value)));
1150
1150
 
1151
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Record.js
1151
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Record.js
1152
1152
  var map3 = /* @__PURE__ */ dual(2, (self, f) => {
1153
1153
  const out = {
1154
1154
  ...self
@@ -1160,12 +1160,12 @@ var map3 = /* @__PURE__ */ dual(2, (self, f) => {
1160
1160
  });
1161
1161
  var keys = (self) => Object.keys(self);
1162
1162
 
1163
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Array.js
1164
- var Array3 = globalThis.Array;
1165
- var fromIterable = (collection) => Array3.isArray(collection) ? collection : Array3.from(collection);
1163
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Array.js
1164
+ var Array2 = globalThis.Array;
1165
+ var fromIterable = (collection) => Array2.isArray(collection) ? collection : Array2.from(collection);
1166
1166
  var append = /* @__PURE__ */ dual(2, (self, last) => [...self, last]);
1167
1167
  var appendAll = /* @__PURE__ */ dual(2, (self, that) => fromIterable(self).concat(fromIterable(that)));
1168
- var isArray = Array3.isArray;
1168
+ var isArray = Array2.isArray;
1169
1169
  var isArrayEmpty = (self) => self.length === 0;
1170
1170
  var isReadonlyArrayEmpty = isArrayEmpty;
1171
1171
  var isReadonlyArrayNonEmpty = isArrayNonEmpty;
@@ -1187,7 +1187,7 @@ var head = /* @__PURE__ */ get(0);
1187
1187
  var headNonEmpty = /* @__PURE__ */ getUnsafe(0);
1188
1188
  var tailNonEmpty = (self) => self.slice(1);
1189
1189
  var sort = /* @__PURE__ */ dual(2, (self, O) => {
1190
- const out = Array3.from(self);
1190
+ const out = Array2.from(self);
1191
1191
  out.sort(O);
1192
1192
  return out;
1193
1193
  });
@@ -1251,7 +1251,7 @@ var dedupeWith = /* @__PURE__ */ dual(2, (self, isEquivalent) => {
1251
1251
  var dedupe = (self) => dedupeWith(self, asEquivalence());
1252
1252
  var join = /* @__PURE__ */ dual(2, (self, sep) => fromIterable(self).join(sep));
1253
1253
 
1254
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Data.js
1254
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Data.js
1255
1255
  var Class3 = class extends Class {
1256
1256
  constructor(props) {
1257
1257
  super();
@@ -1262,7 +1262,7 @@ var Class3 = class extends Class {
1262
1262
  };
1263
1263
  var TaggedError2 = TaggedError;
1264
1264
 
1265
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Encoding.js
1265
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Encoding.js
1266
1266
  var EncodingErrorTypeId = "~effect/encoding/EncodingError";
1267
1267
  var EncodingError = class extends (/* @__PURE__ */ TaggedError2("EncodingError")) {
1268
1268
  /**
@@ -1298,7 +1298,7 @@ var base64EncodeUint8Array = (bytes) => {
1298
1298
  var base64abc = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"];
1299
1299
  var base64UrlEncodeUint8Array = (data) => base64EncodeUint8Array(data).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
1300
1300
 
1301
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Graph.js
1301
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Graph.js
1302
1302
  var TypeId3 = "~effect/collections/Graph";
1303
1303
  var Edge = class extends Class3 {
1304
1304
  };
@@ -4130,6 +4130,14 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
4130
4130
  ),
4131
4131
  ([A, E, R]) => ({ A, E, R })
4132
4132
  );
4133
+ const streamVarianceStruct = (type, atLocation) => map5(
4134
+ all(
4135
+ varianceStructCovariantType(type, atLocation, "_A"),
4136
+ varianceStructCovariantType(type, atLocation, "_E"),
4137
+ varianceStructCovariantType(type, atLocation, "_R")
4138
+ ),
4139
+ ([A, E, R]) => ({ A, E, R })
4140
+ );
4133
4141
  const layerVarianceStruct = (type, atLocation) => map5(
4134
4142
  all(
4135
4143
  varianceStructContravariantType(type, atLocation, "_ROut"),
@@ -4176,6 +4184,21 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
4176
4184
  "TypeParser.strictEffectType",
4177
4185
  (type) => type
4178
4186
  );
4187
+ const streamType = cachedBy(
4188
+ fn("TypeParser.streamType")(function* (type, atLocation) {
4189
+ if (supportedEffect() === "v3") {
4190
+ return yield* effectType(type, atLocation);
4191
+ }
4192
+ const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/Stream");
4193
+ if (!typeIdSymbol) {
4194
+ return yield* typeParserIssue("Type is not a stream", type, atLocation);
4195
+ }
4196
+ const typeIdType = typeChecker.getTypeOfSymbolAtLocation(typeIdSymbol, atLocation);
4197
+ return yield* streamVarianceStruct(typeIdType, atLocation);
4198
+ }),
4199
+ "TypeParser.streamType",
4200
+ (type) => type
4201
+ );
4179
4202
  const isEffectTypeSourceFile = cachedBy(
4180
4203
  fn("TypeParser.isEffectTypeSourceFile")(function* (sourceFile) {
4181
4204
  const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
@@ -4747,33 +4770,33 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
4747
4770
  ),
4748
4771
  ([Identifier, Service]) => ({ Identifier, Service })
4749
4772
  );
4750
- const serviceVarianceStruct = (type, atLocation) => map5(
4751
- all(
4752
- varianceStructInvariantType(type, atLocation, "_Identifier"),
4753
- varianceStructInvariantType(type, atLocation, "_Service")
4754
- ),
4755
- ([Identifier, Service]) => ({ Identifier, Service })
4756
- );
4757
4773
  const serviceType = cachedBy(
4758
4774
  fn("TypeParser.serviceType")(function* (type, atLocation) {
4775
+ if (supportedEffect() !== "v4") return yield* typeParserIssue("v4 only");
4759
4776
  yield* pipeableType(type, atLocation);
4760
- const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
4761
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
4762
- );
4763
- if (propertiesSymbols.length === 0) {
4764
- return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
4777
+ const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/ServiceMap/Service");
4778
+ if (!typeIdSymbol) {
4779
+ return yield* typeParserIssue("Type has no service key type id", type, atLocation);
4765
4780
  }
4766
- propertiesSymbols.sort((a, b) => ts.symbolName(b).indexOf("TypeId") - ts.symbolName(a).indexOf("TypeId"));
4767
- return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
4768
- const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
4769
- return serviceVarianceStruct(propertyType, atLocation);
4770
- }));
4781
+ const identifierSymbol = typeChecker.getPropertyOfType(type, "Identifier");
4782
+ if (!identifierSymbol) {
4783
+ return yield* typeParserIssue("Type has no 'Identifier' property", type, atLocation);
4784
+ }
4785
+ const serviceSymbol = typeChecker.getPropertyOfType(type, "Service");
4786
+ if (!serviceSymbol) {
4787
+ return yield* typeParserIssue("Type has no 'Service' property", type, atLocation);
4788
+ }
4789
+ return {
4790
+ Identifier: typeChecker.getTypeOfSymbolAtLocation(identifierSymbol, atLocation),
4791
+ Service: typeChecker.getTypeOfSymbolAtLocation(serviceSymbol, atLocation)
4792
+ };
4771
4793
  }),
4772
4794
  "TypeParser.serviceType",
4773
4795
  (type) => type
4774
4796
  );
4775
4797
  const contextTag = cachedBy(
4776
4798
  fn("TypeParser.contextTag")(function* (type, atLocation) {
4799
+ if (supportedEffect() !== "v3") return yield* typeParserIssue("v3 only");
4777
4800
  yield* pipeableType(type, atLocation);
4778
4801
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
4779
4802
  (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
@@ -4914,6 +4937,19 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
4914
4937
  "TypeParser.promiseLike",
4915
4938
  (type) => type
4916
4939
  );
4940
+ const promiseType = cachedBy(
4941
+ function(type, atLocation) {
4942
+ const promiseSymbol = typeChecker.resolveName("Promise", void 0, ts.SymbolFlags.Type, false);
4943
+ if (!promiseSymbol) return typeParserIssue("global Promise type not found", type, atLocation);
4944
+ const globalPromiseType = typeChecker.getDeclaredTypeOfSymbol(promiseSymbol);
4945
+ if (type === globalPromiseType || "target" in type && type.target === globalPromiseType || typeChecker.isTypeAssignableTo(type, globalPromiseType)) {
4946
+ return succeed3(type);
4947
+ }
4948
+ return typeParserIssue("type is not a Promise", type, atLocation);
4949
+ },
4950
+ "TypeParser.promiseType",
4951
+ (type) => type
4952
+ );
4917
4953
  const extendsSchemaClass = cachedBy(
4918
4954
  fn("TypeParser.extendsSchemaClass")(function* (atLocation) {
4919
4955
  if (!atLocation.name) {
@@ -5911,6 +5947,7 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
5911
5947
  isServiceMapTypeSourceFile,
5912
5948
  isNodeReferenceToServiceMapModuleApi,
5913
5949
  effectType,
5950
+ streamType,
5914
5951
  strictEffectType,
5915
5952
  layerType,
5916
5953
  fiberType,
@@ -5932,6 +5969,7 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
5932
5969
  singleArgCall,
5933
5970
  scopeType,
5934
5971
  promiseLike,
5972
+ promiseType,
5935
5973
  extendsEffectTag,
5936
5974
  extendsEffectService,
5937
5975
  extendsServiceMapService,
@@ -7291,6 +7329,37 @@ var anyUnknownInErrorContext = createDiagnostic({
7291
7329
  })
7292
7330
  });
7293
7331
 
7332
+ // src/diagnostics/asyncFunction.ts
7333
+ var asyncFunction = createDiagnostic({
7334
+ name: "asyncFunction",
7335
+ code: 69,
7336
+ description: "Warns when declaring async functions and suggests using Effect values and Effect.gen for async control flow",
7337
+ group: "effectNative",
7338
+ severity: "off",
7339
+ fixable: false,
7340
+ supportedEffect: ["v3", "v4"],
7341
+ apply: fn("asyncFunction.apply")(function* (sourceFile, report) {
7342
+ const ts = yield* service(TypeScriptApi);
7343
+ const hasAsyncModifier = (node) => ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) === true;
7344
+ const visit = (node) => {
7345
+ if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) {
7346
+ ts.forEachChild(node, visit);
7347
+ return void 0;
7348
+ }
7349
+ if (hasAsyncModifier(node)) {
7350
+ report({
7351
+ location: node,
7352
+ messageText: "This code declares an async function, consider representing this async control flow with Effect values and `Effect.gen`.",
7353
+ fixes: []
7354
+ });
7355
+ }
7356
+ ts.forEachChild(node, visit);
7357
+ return void 0;
7358
+ };
7359
+ ts.forEachChild(sourceFile, visit);
7360
+ })
7361
+ });
7362
+
7294
7363
  // src/diagnostics/catchAllToMapError.ts
7295
7364
  var catchAllToMapError = createDiagnostic({
7296
7365
  name: "catchAllToMapError",
@@ -7369,7 +7438,7 @@ var catchAllToMapError = createDiagnostic({
7369
7438
  const { failArg, failCall } = failCallInfo;
7370
7439
  report({
7371
7440
  location: transformation.callee,
7372
- messageText: `You can use Effect.mapError instead of Effect.${catchAllName} + Effect.fail to transform the error type.`,
7441
+ messageText: `\`Effect.mapError\` expresses the same error-type transformation more directly than \`Effect.${catchAllName}\` followed by \`Effect.fail\`.`,
7373
7442
  fixes: [{
7374
7443
  fixName: "catchAllToMapError_fix",
7375
7444
  description: "Replace with Effect.mapError",
@@ -7433,7 +7502,7 @@ var catchUnfailableEffect = createDiagnostic({
7433
7502
  if (E.flags & ts.TypeFlags.Never) {
7434
7503
  report({
7435
7504
  location: transformation.callee,
7436
- messageText: `Looks like the previous effect never fails, so probably this error handling will never be triggered.`,
7505
+ messageText: "The previous Effect does not fail, so this error-handling branch will never run.",
7437
7506
  fixes: []
7438
7507
  });
7439
7508
  }
@@ -7496,7 +7565,7 @@ var classSelfMismatch = createDiagnostic({
7496
7565
  if (actualName !== expectedName) {
7497
7566
  report({
7498
7567
  location: selfTypeNode,
7499
- messageText: `Self type parameter should be '${expectedName}'`,
7568
+ messageText: `The \`Self\` type parameter for this class should be \`${expectedName}\`.`,
7500
7569
  fixes: [{
7501
7570
  fixName: "classSelfMismatch_fix",
7502
7571
  description: `Replace '${actualName}' with '${expectedName}'`,
@@ -7519,6 +7588,59 @@ var classSelfMismatch = createDiagnostic({
7519
7588
  })
7520
7589
  });
7521
7590
 
7591
+ // src/diagnostics/cryptoRandomUUIDInEffect.ts
7592
+ var makeCryptoRandomUUIDApply = (checkInEffect) => fn(`cryptoRandomUUID${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7593
+ const ts = yield* service(TypeScriptApi);
7594
+ const typeChecker = yield* service(TypeCheckerApi);
7595
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7596
+ const typeParser = yield* service(TypeParser);
7597
+ const cryptoSymbol = typeChecker.resolveName("crypto", void 0, ts.SymbolFlags.Value, false);
7598
+ if (!cryptoSymbol) return;
7599
+ const nodeToVisit = [];
7600
+ const appendNodeToVisit = (node) => {
7601
+ nodeToVisit.push(node);
7602
+ return void 0;
7603
+ };
7604
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7605
+ while (nodeToVisit.length > 0) {
7606
+ const node = nodeToVisit.shift();
7607
+ ts.forEachChild(node, appendNodeToVisit);
7608
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "randomUUID") continue;
7609
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
7610
+ if (!symbol3) continue;
7611
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== cryptoSymbol) continue;
7612
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7613
+ if (inEffect !== checkInEffect) continue;
7614
+ report({
7615
+ location: node,
7616
+ messageText: checkInEffect ? "This Effect code uses `crypto.randomUUID()`, prefer the Effect `Random` module instead because it uses Effect-injected randomness rather than the `crypto` module behind the scenes." : "This code uses `crypto.randomUUID()`, prefer the Effect `Random` module instead because it uses Effect-injected randomness rather than the `crypto` module behind the scenes.",
7617
+ fixes: []
7618
+ });
7619
+ }
7620
+ });
7621
+ var cryptoRandomUUIDInEffect = createDiagnostic({
7622
+ name: "cryptoRandomUUIDInEffect",
7623
+ code: 67,
7624
+ description: "Warns when using crypto.randomUUID() inside Effect generators instead of the Effect Random module, which uses Effect-injected randomness rather than the crypto module behind the scenes",
7625
+ group: "effectNative",
7626
+ severity: "off",
7627
+ fixable: false,
7628
+ supportedEffect: ["v4"],
7629
+ apply: makeCryptoRandomUUIDApply(true)
7630
+ });
7631
+
7632
+ // src/diagnostics/cryptoRandomUUID.ts
7633
+ var cryptoRandomUUID = createDiagnostic({
7634
+ name: "cryptoRandomUUID",
7635
+ code: 66,
7636
+ description: "Warns when using crypto.randomUUID() outside Effect generators instead of the Effect Random module, which uses Effect-injected randomness rather than the crypto module behind the scenes",
7637
+ group: "effectNative",
7638
+ severity: "off",
7639
+ fixable: false,
7640
+ supportedEffect: ["v4"],
7641
+ apply: makeCryptoRandomUUIDApply(false)
7642
+ });
7643
+
7522
7644
  // src/diagnostics/deterministicKeys.ts
7523
7645
  var deterministicKeys = createDiagnostic({
7524
7646
  name: "deterministicKeys",
@@ -7620,7 +7742,7 @@ var deterministicKeys = createDiagnostic({
7620
7742
  if (actualIdentifier !== expectedKey) {
7621
7743
  report({
7622
7744
  location: keyStringLiteral,
7623
- messageText: `Key should be '${expectedKey}'`,
7745
+ messageText: `This key does not match the deterministic key for this declaration. The expected key is \`${expectedKey}\`.`,
7624
7746
  fixes: [{
7625
7747
  fixName: "deterministicKeys_fix",
7626
7748
  description: `Replace '${actualIdentifier}' with '${expectedKey}'`,
@@ -7659,9 +7781,8 @@ var duplicatePackage = createDiagnostic({
7659
7781
  const versions = Object.keys(resolvedPackages[packageName]);
7660
7782
  report({
7661
7783
  location: sourceFile.statements[0],
7662
- messageText: `Package ${packageName} is referenced multiple times with different versions (${versions.join(", ")}) and may cause unexpected type errors.
7663
- Cleanup your dependencies and your package lockfile to avoid multiple instances of this package and reload the project.
7664
- If this is intended set the LSP config "allowedDuplicatedPackages" to ${JSON.stringify(options.allowedDuplicatedPackages.concat([packageName]))}.
7784
+ messageText: `Multiple versions of package \`${packageName}\` were detected: ${versions.join(", ")}. Package duplication can change runtime identity and type equality across Effect modules.
7785
+ If this is intentional, set the LSP config \`allowedDuplicatedPackages\` to ${JSON.stringify(options.allowedDuplicatedPackages.concat([packageName]))}.
7665
7786
 
7666
7787
  ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageName][version]}`).join("\n")}`,
7667
7788
  fixes: []
@@ -7671,6 +7792,40 @@ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageNam
7671
7792
  })
7672
7793
  });
7673
7794
 
7795
+ // src/diagnostics/effectDoNotation.ts
7796
+ var effectDoNotation = createDiagnostic({
7797
+ name: "effectDoNotation",
7798
+ code: 73,
7799
+ description: "Suggests using Effect.gen or Effect.fn instead of the Effect.Do notation helpers",
7800
+ group: "style",
7801
+ severity: "off",
7802
+ fixable: false,
7803
+ supportedEffect: ["v3", "v4"],
7804
+ apply: fn("effectDoNotation.apply")(function* (sourceFile, report) {
7805
+ const ts = yield* service(TypeScriptApi);
7806
+ const typeParser = yield* service(TypeParser);
7807
+ const nodeToVisit = [];
7808
+ const appendNodeToVisit = (node) => {
7809
+ nodeToVisit.push(node);
7810
+ return void 0;
7811
+ };
7812
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7813
+ while (nodeToVisit.length > 0) {
7814
+ const node = nodeToVisit.shift();
7815
+ const isReference = yield* orUndefined(typeParser.isNodeReferenceToEffectModuleApi("Do")(node));
7816
+ if (isReference) {
7817
+ report({
7818
+ location: node,
7819
+ messageText: "This uses the Effect do emulation. `Effect.gen` or `Effect.fn` achieve the same result with native JS scopes.",
7820
+ fixes: []
7821
+ });
7822
+ continue;
7823
+ }
7824
+ ts.forEachChild(node, appendNodeToVisit);
7825
+ }
7826
+ })
7827
+ });
7828
+
7674
7829
  // src/diagnostics/effectFnIife.ts
7675
7830
  var effectFnIife = createDiagnostic({
7676
7831
  name: "effectFnIife",
@@ -7769,7 +7924,7 @@ var effectFnIife = createDiagnostic({
7769
7924
  const traceExpressionText = traceExpression ? sourceFile.text.slice(traceExpression.pos, traceExpression.end) : void 0;
7770
7925
  report({
7771
7926
  location: node,
7772
- messageText: `${effectModuleName}.${kind} returns a reusable function that can take arguments, but here it's called immediately. Use Effect.gen instead${traceExpressionText ? ` with Effect.withSpan(${traceExpressionText}) piped in the end to mantain tracing spans` : ``}.`,
7927
+ messageText: `\`${effectModuleName}.${kind}\` returns a reusable function that can take arguments, but it is invoked immediately here. \`Effect.gen\` represents the immediate-use form for this pattern${traceExpressionText ? ` with \`Effect.withSpan(${traceExpressionText})\` piped at the end to maintain tracing spans` : ``}.`,
7773
7928
  fixes
7774
7929
  });
7775
7930
  }
@@ -7854,7 +8009,7 @@ var effectFnImplicitAny = createDiagnostic({
7854
8009
  const parameterName = getParameterName(ts, parameter.name);
7855
8010
  report({
7856
8011
  location: parameter.name,
7857
- messageText: `Parameter '${parameterName}' implicitly has an 'any' type in Effect.fn/Effect.fnUntraced. Add an explicit type annotation or provide a contextual function type.`,
8012
+ messageText: `Parameter \`${parameterName}\` implicitly has type \`any\` in \`Effect.fn\`, \`Effect.fnUntraced\`, or \`Effect.fnUntracedEager\`. No parameter type is available from an explicit annotation or contextual function type.`,
7858
8013
  fixes: []
7859
8014
  });
7860
8015
  }
@@ -8450,7 +8605,7 @@ var effectFnOpportunity = createDiagnostic({
8450
8605
  const expectedSignature = generateExpectedSignature();
8451
8606
  report({
8452
8607
  location: nameIdentifier ?? targetNode,
8453
- messageText: `Can be rewritten as a reusable function: ${expectedSignature}`,
8608
+ messageText: `This expression can be rewritten in the reusable function form \`${expectedSignature}\`.`,
8454
8609
  fixes
8455
8610
  });
8456
8611
  }
@@ -8617,6 +8772,43 @@ var effectInVoidSuccess = createDiagnostic({
8617
8772
  })
8618
8773
  });
8619
8774
 
8775
+ // src/diagnostics/effectMapFlatten.ts
8776
+ var effectMapFlatten = createDiagnostic({
8777
+ name: "effectMapFlatten",
8778
+ code: 74,
8779
+ description: "Suggests using Effect.flatMap instead of Effect.map followed by Effect.flatten in piping flows",
8780
+ group: "style",
8781
+ severity: "suggestion",
8782
+ fixable: false,
8783
+ supportedEffect: ["v3", "v4"],
8784
+ apply: fn("effectMapFlatten.apply")(function* (sourceFile, report) {
8785
+ const typeParser = yield* service(TypeParser);
8786
+ const flows = yield* typeParser.pipingFlows(false)(sourceFile);
8787
+ for (const flow2 of flows) {
8788
+ for (let index = 0; index < flow2.transformations.length - 1; index++) {
8789
+ const mapTransformation = flow2.transformations[index];
8790
+ const flattenTransformation = flow2.transformations[index + 1];
8791
+ if (!mapTransformation || !flattenTransformation || !mapTransformation?.args || flattenTransformation?.args || mapTransformation.kind !== "pipe" && mapTransformation.kind !== "pipeable" || flattenTransformation.kind !== mapTransformation.kind) {
8792
+ continue;
8793
+ }
8794
+ const isMapCall = yield* orUndefined(
8795
+ typeParser.isNodeReferenceToEffectModuleApi("map")(mapTransformation.callee)
8796
+ );
8797
+ const isFlattenCall = yield* orUndefined(
8798
+ typeParser.isNodeReferenceToEffectModuleApi("flatten")(flattenTransformation.callee)
8799
+ );
8800
+ if (isMapCall && isFlattenCall) {
8801
+ report({
8802
+ location: flattenTransformation.callee,
8803
+ messageText: "`Effect.map` + `Effect.flatten` is the same as `Effect.flatMap` that expresses the same steps more directly.",
8804
+ fixes: []
8805
+ });
8806
+ }
8807
+ }
8808
+ }
8809
+ })
8810
+ });
8811
+
8620
8812
  // src/diagnostics/effectMapVoid.ts
8621
8813
  var effectMapVoid = createDiagnostic({
8622
8814
  name: "effectMapVoid",
@@ -8662,7 +8854,7 @@ var effectMapVoid = createDiagnostic({
8662
8854
  if (isNone2(match2)) continue;
8663
8855
  report({
8664
8856
  location: node.expression,
8665
- messageText: "Effect.asVoid can be used instead to discard the success value",
8857
+ messageText: "This expression discards the success value through mapping. `Effect.asVoid` represents that form directly.",
8666
8858
  fixes: [{
8667
8859
  fixName: "effectMapVoid_fix",
8668
8860
  description: "Replace with Effect.asVoid",
@@ -8717,7 +8909,7 @@ var effectSucceedWithVoid = createDiagnostic({
8717
8909
  if (!tsUtils.isVoidExpression(argument)) continue;
8718
8910
  report({
8719
8911
  location: node,
8720
- messageText: "Effect.void can be used instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
8912
+ messageText: "`Effect.void` represents the same outcome as `Effect.succeed(undefined)` or `Effect.succeed(void 0)`.",
8721
8913
  fixes: [{
8722
8914
  fixName: "effectSucceedWithVoid_fix",
8723
8915
  description: "Replace with Effect.void",
@@ -8779,7 +8971,7 @@ var extendsNativeError = createDiagnostic({
8779
8971
  if (isNativeError) {
8780
8972
  report({
8781
8973
  location: node.name ?? typeExpression,
8782
- messageText: "Avoid extending the native 'Error' class directly. Consider using a tagged error (e.g. Data.TaggedError) to maintain type safety in the Effect failure channel.",
8974
+ messageText: "This class extends the native `Error` type directly. Untagged native errors lose distinction in the Effect failure channel.",
8783
8975
  fixes: []
8784
8976
  });
8785
8977
  }
@@ -8824,7 +9016,12 @@ var floatingEffect = createDiagnostic({
8824
9016
  if (!isFloatingExpression(node)) continue;
8825
9017
  const type = typeCheckerUtils.getTypeAtLocation(node.expression);
8826
9018
  if (!type) continue;
8827
- const effect = yield* option(typeParser.effectType(type, node.expression));
9019
+ const effect = yield* option(
9020
+ pipe(
9021
+ typeParser.effectType(type, node.expression),
9022
+ orElse2(() => typeParser.streamType(type, node.expression))
9023
+ )
9024
+ );
8828
9025
  if (isSome2(effect)) {
8829
9026
  const allowedFloatingEffects = yield* pipe(
8830
9027
  typeParser.fiberType(type, node.expression),
@@ -8833,10 +9030,9 @@ var floatingEffect = createDiagnostic({
8833
9030
  );
8834
9031
  if (isNone2(allowedFloatingEffects)) {
8835
9032
  const isStrictEffect = yield* option(typeParser.strictEffectType(type, node.expression));
8836
- const name = isSome2(isStrictEffect) ? "Effect" : "Effect-able " + typeChecker.typeToString(type);
8837
9033
  report({
8838
9034
  location: node,
8839
- messageText: `${name} must be yielded or assigned to a variable.`,
9035
+ messageText: isSome2(isStrictEffect) ? "This Effect value is neither yielded nor used in an assignment." : `This Effect-able \`${typeChecker.typeToString(type)}\` value is neither yielded nor assigned to a variable.`,
8840
9036
  fixes: []
8841
9037
  });
8842
9038
  }
@@ -8880,6 +9076,7 @@ var genericEffectServices = createDiagnostic({
8880
9076
  for (const [type, reportAt] of typesToCheck) {
8881
9077
  yield* pipe(
8882
9078
  typeParser.contextTag(type, node),
9079
+ orElse2(() => typeParser.serviceType(type, node)),
8883
9080
  map5(() => {
8884
9081
  report({
8885
9082
  location: reportAt,
@@ -8931,7 +9128,7 @@ var makeGlobalConsoleApply = (checkInEffect) => fn(`globalConsole${checkInEffect
8931
9128
  if (inEffect !== checkInEffect) continue;
8932
9129
  report({
8933
9130
  location: node,
8934
- messageText: checkInEffect ? `Prefer using ${alternative} instead of console.${method} inside Effect generators.` : `Prefer using ${alternative} instead of console.${method}.`,
9131
+ messageText: checkInEffect ? `This Effect code uses \`console.${method}\`, logging in Effect code is represented through \`${alternative}\`.` : `This code uses \`console.${method}\`, the corresponding Effect logging API is \`${alternative}\`.`,
8935
9132
  fixes: []
8936
9133
  });
8937
9134
  }
@@ -8980,10 +9177,10 @@ var makeGlobalDateApply = (checkInEffect) => fn(`globalDate${checkInEffect ? "In
8980
9177
  let objectNode;
8981
9178
  if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.idText(node.expression.name) === "now") {
8982
9179
  objectNode = node.expression.expression;
8983
- messageText = checkInEffect ? "Prefer using Clock or DateTime from Effect instead of Date.now() inside Effect generators." : "Prefer using Clock or DateTime from Effect instead of Date.now().";
9180
+ messageText = checkInEffect ? "This Effect code uses `Date.now()`, time access in Effect code is represented through `Clock` from Effect." : "This code uses `Date.now()`, time access is represented through `Clock` from Effect.";
8984
9181
  } else if (ts.isNewExpression(node)) {
8985
9182
  objectNode = node.expression;
8986
- messageText = checkInEffect ? "Prefer using DateTime from Effect instead of new Date() inside Effect generators." : "Prefer using DateTime from Effect instead of new Date().";
9183
+ messageText = checkInEffect ? "This Effect code constructs `new Date()`, date values in Effect code are represented through `DateTime` from Effect." : "This code constructs `new Date()`, date values are represented through `DateTime` from Effect.";
8987
9184
  }
8988
9185
  if (!messageText || !objectNode) continue;
8989
9186
  const symbol3 = typeChecker.getSymbolAtLocation(objectNode);
@@ -9066,7 +9263,7 @@ var globalErrorInEffectCatch = createDiagnostic({
9066
9263
  );
9067
9264
  report({
9068
9265
  location: node.expression,
9069
- messageText: `The 'catch' callback in ${nodeText} returns global 'Error', which loses type safety as untagged errors merge together. Consider using a tagged error and optionally wrapping the original in a 'cause' property.`,
9266
+ messageText: `The \`catch\` callback in \`${nodeText}\` returns the global \`Error\` type. Untagged errors merge together in the Effect error channel and lose type-level distinction; a tagged error preserves that distinction and can wrap the original error in a \`cause\` property.`,
9070
9267
  fixes: []
9071
9268
  });
9072
9269
  }
@@ -9146,7 +9343,7 @@ var makeGlobalFetchApply = (checkInEffect) => fn(`globalFetch${checkInEffect ? "
9146
9343
  if (!fetchSymbol) return;
9147
9344
  const effectVersion = typeParser.supportedEffect();
9148
9345
  const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
9149
- const messageText = checkInEffect ? `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function inside Effect generators.` : `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function.`;
9346
+ const messageText = checkInEffect ? `This Effect code calls the global \`fetch\` function, HTTP requests in Effect code are represented through \`HttpClient\` from \`${packageName}\`.` : `This code uses the global \`fetch\` function, HTTP requests are represented through \`HttpClient\` from \`${packageName}\`.`;
9150
9347
  const nodeToVisit = [];
9151
9348
  const appendNodeToVisit = (node) => {
9152
9349
  nodeToVisit.push(node);
@@ -9219,7 +9416,7 @@ var makeGlobalRandomApply = (checkInEffect) => fn(`globalRandom${checkInEffect ?
9219
9416
  if (inEffect !== checkInEffect) continue;
9220
9417
  report({
9221
9418
  location: node,
9222
- messageText: checkInEffect ? "Prefer using the Random service from Effect instead of Math.random() inside Effect generators." : "Prefer using the Random service from Effect instead of Math.random().",
9419
+ messageText: checkInEffect ? "This Effect code uses `Math.random()`, randomness is represented through the Effect `Random` service." : "This code uses `Math.random()`, randomness is represented through the Effect `Random` service.",
9223
9420
  fixes: []
9224
9421
  });
9225
9422
  }
@@ -9250,12 +9447,12 @@ var globalRandom = createDiagnostic({
9250
9447
  // src/diagnostics/globalTimersInEffect.ts
9251
9448
  var timerAlternatives = {
9252
9449
  "setTimeout": {
9253
- inEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout inside Effect generators.",
9254
- outsideEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout."
9450
+ inEffect: "This Effect code uses `setTimeout`, the corresponding timer API in this context is `Effect.sleep or Schedule` from Effect.",
9451
+ outsideEffect: "This code uses `setTimeout`, the corresponding Effect timer API is `Effect.sleep or Schedule` from Effect."
9255
9452
  },
9256
9453
  "setInterval": {
9257
- inEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval inside Effect generators.",
9258
- outsideEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval."
9454
+ inEffect: "This Effect code uses `setInterval`, the corresponding timer API in this context is `Schedule or Effect.repeat` from Effect.",
9455
+ outsideEffect: "This code uses `setInterval`, the corresponding Effect timer API is `Schedule or Effect.repeat` from Effect."
9259
9456
  }
9260
9457
  };
9261
9458
  var makeGlobalTimersApply = (checkInEffect) => fn(`globalTimers${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
@@ -9497,7 +9694,7 @@ var instanceOfSchema = createDiagnostic({
9497
9694
  if (isSchemaType._tag === "Some") {
9498
9695
  report({
9499
9696
  location: node,
9500
- messageText: "Consider using Schema.is instead of instanceof for Effect Schema types.",
9697
+ messageText: "This code uses `instanceof` with an Effect Schema type. `Schema.is` is the schema-aware runtime check for this case.",
9501
9698
  fixes: [{
9502
9699
  fixName: "instanceOfSchema_fix",
9503
9700
  description: "Replace with Schema.is",
@@ -9643,6 +9840,55 @@ var layerMergeAllWithDependencies = createDiagnostic({
9643
9840
  })
9644
9841
  });
9645
9842
 
9843
+ // src/diagnostics/lazyPromiseInEffectSync.ts
9844
+ var lazyPromiseInEffectSync = createDiagnostic({
9845
+ name: "lazyPromiseInEffectSync",
9846
+ code: 70,
9847
+ description: "Warns when Effect.sync lazily returns a Promise instead of using an async Effect constructor",
9848
+ group: "antipattern",
9849
+ severity: "warning",
9850
+ fixable: false,
9851
+ supportedEffect: ["v3", "v4"],
9852
+ apply: fn("lazyPromiseInEffectSync.apply")(function* (sourceFile, report) {
9853
+ const ts = yield* service(TypeScriptApi);
9854
+ const typeChecker = yield* service(TypeCheckerApi);
9855
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
9856
+ const typeParser = yield* service(TypeParser);
9857
+ const nodeToVisit = [];
9858
+ const appendNodeToVisit = (node) => {
9859
+ nodeToVisit.push(node);
9860
+ return void 0;
9861
+ };
9862
+ ts.forEachChild(sourceFile, appendNodeToVisit);
9863
+ while (nodeToVisit.length > 0) {
9864
+ const node = nodeToVisit.shift();
9865
+ ts.forEachChild(node, appendNodeToVisit);
9866
+ if (!ts.isCallExpression(node)) continue;
9867
+ const isSyncCall = yield* orUndefined(
9868
+ typeParser.isNodeReferenceToEffectModuleApi("sync")(node.expression)
9869
+ );
9870
+ if (!isSyncCall) continue;
9871
+ const lazyArg = node.arguments[0];
9872
+ if (!lazyArg) continue;
9873
+ const lazyArgType = typeCheckerUtils.getTypeAtLocation(lazyArg);
9874
+ if (!lazyArgType) continue;
9875
+ const entries2 = typeCheckerUtils.unrollUnionMembers(lazyArgType).flatMap(
9876
+ (member) => typeChecker.getSignaturesOfType(member, ts.SignatureKind.Call).map(
9877
+ (signature) => typeParser.promiseType(typeChecker.getReturnTypeOfSignature(signature), lazyArg)
9878
+ )
9879
+ );
9880
+ if (entries2.length === 0) continue;
9881
+ const promiseReturn = yield* orUndefined(firstSuccessOf(entries2));
9882
+ if (!promiseReturn) continue;
9883
+ report({
9884
+ location: lazyArg,
9885
+ messageText: "This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work.",
9886
+ fixes: []
9887
+ });
9888
+ }
9889
+ })
9890
+ });
9891
+
9646
9892
  // src/diagnostics/leakingRequirements.ts
9647
9893
  var leakingRequirements = createDiagnostic({
9648
9894
  name: "leakingRequirements",
@@ -9767,7 +10013,7 @@ var leakingRequirements = createDiagnostic({
9767
10013
  location: node,
9768
10014
  messageText: `Methods of this Service require \`${requirementsStr}\` from every caller.
9769
10015
 
9770
- This leaks implementation details into the service's public type \u2014 callers shouldn't need to know *how* the service works internally, only *what* it provides.
10016
+ The requirement becomes part of the public service surface instead of remaining internal to Layer implementation.
9771
10017
 
9772
10018
  Resolve these dependencies at Layer creation and provide them to each method, so the service's type reflects its purpose, not its implementation.
9773
10019
 
@@ -9964,7 +10210,7 @@ var missedPipeableOpportunity = createDiagnostic({
9964
10210
  ).trim() : "";
9965
10211
  report({
9966
10212
  location: flow2.node,
9967
- messageText: `Nested function calls can be converted to pipeable style for better readability; consider using ${subjectText}.pipe(...) instead.`,
10213
+ messageText: `This nested call structure has a pipeable form. \`${subjectText}.pipe(...)\` represents the same call sequence in pipe style and may be easier to read.`,
9968
10214
  fixes: [{
9969
10215
  fixName: "missedPipeableOpportunity_fix",
9970
10216
  description: "Convert to pipe style",
@@ -10045,7 +10291,7 @@ var missingEffectContext = createDiagnostic({
10045
10291
  (missingTypes) => missingTypes.length > 0 ? report(
10046
10292
  {
10047
10293
  location: node,
10048
- messageText: `Missing '${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Effect context.`,
10294
+ messageText: `This Effect requires a service that is missing from the expected Effect context: \`${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}\`.`,
10049
10295
  fixes: []
10050
10296
  }
10051
10297
  ) : void 0
@@ -10396,7 +10642,7 @@ var missingReturnYieldStar = createDiagnostic({
10396
10642
  }];
10397
10643
  report({
10398
10644
  location: unwrapped,
10399
- messageText: `It is recommended to use return yield* for Effects that never succeed to signal a definitive exit point for type narrowing and tooling support.`,
10645
+ messageText: "This Effect never succeeds; using `return yield*` preserves a definitive generator exit point for type narrowing and tooling support.",
10400
10646
  fixes: fix
10401
10647
  });
10402
10648
  }
@@ -10452,7 +10698,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
10452
10698
  brokenGenerators.forEach(
10453
10699
  (pos) => report({
10454
10700
  location: { pos, end: pos + "function".length },
10455
- messageText: `Seems like you used yield instead of yield* inside this Effect.gen.`,
10701
+ messageText: "This uses `yield` for an `Effect` value. `yield*` is the Effect-aware form in this context.",
10456
10702
  fixes: []
10457
10703
  })
10458
10704
  );
@@ -10474,7 +10720,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
10474
10720
  }] : [];
10475
10721
  report({
10476
10722
  location: node,
10477
- messageText: `When yielding Effects inside Effect.gen, you should use yield* instead of yield.`,
10723
+ messageText: "This uses `yield` for an `Effect` value. `yield*` is the Effect-aware form in this context.",
10478
10724
  fixes: fix
10479
10725
  });
10480
10726
  });
@@ -10542,7 +10788,7 @@ var multipleEffectProvide = createDiagnostic({
10542
10788
  if (chunk.length < 2) continue;
10543
10789
  report({
10544
10790
  location: chunk[0].node,
10545
- messageText: "Avoid chaining Effect.provide calls, as this can lead to service lifecycle issues. Instead, merge layers and provide them in a single call.",
10791
+ messageText: "This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers.",
10546
10792
  fixes: [{
10547
10793
  fixName: "multipleEffectProvide_fix",
10548
10794
  description: "Combine into a single provide",
@@ -10576,6 +10822,74 @@ var multipleEffectProvide = createDiagnostic({
10576
10822
  })
10577
10823
  });
10578
10824
 
10825
+ // src/diagnostics/nestedEffectGenYield.ts
10826
+ var nestedEffectGenYield = createDiagnostic({
10827
+ name: "nestedEffectGenYield",
10828
+ code: 71,
10829
+ description: "Warns when yielding a nested bare Effect.gen inside an existing Effect generator context",
10830
+ group: "style",
10831
+ severity: "off",
10832
+ fixable: false,
10833
+ supportedEffect: ["v3", "v4"],
10834
+ apply: fn("nestedEffectGenYield.apply")(function* (sourceFile, report) {
10835
+ const ts = yield* service(TypeScriptApi);
10836
+ const typeParser = yield* service(TypeParser);
10837
+ const nodeToVisit = [];
10838
+ const appendNodeToVisit = (node) => {
10839
+ nodeToVisit.push(node);
10840
+ return void 0;
10841
+ };
10842
+ ts.forEachChild(sourceFile, appendNodeToVisit);
10843
+ while (nodeToVisit.length > 0) {
10844
+ const node = nodeToVisit.shift();
10845
+ ts.forEachChild(node, appendNodeToVisit);
10846
+ if (!ts.isYieldExpression(node) || !node.asteriskToken || !node.expression) continue;
10847
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
10848
+ if (!inEffect) continue;
10849
+ const bareNestedEffectGen = yield* orUndefined(typeParser.effectGen(node.expression));
10850
+ if (!bareNestedEffectGen) continue;
10851
+ report({
10852
+ location: node.expression,
10853
+ messageText: "This `yield*` is applied to a nested `Effect.gen(...)` that can be inlined in the parent Effect generator context.",
10854
+ fixes: []
10855
+ });
10856
+ }
10857
+ })
10858
+ });
10859
+
10860
+ // src/diagnostics/newPromise.ts
10861
+ var newPromise = createDiagnostic({
10862
+ name: "newPromise",
10863
+ code: 68,
10864
+ description: "Warns when constructing promises with new Promise instead of using Effect APIs",
10865
+ group: "effectNative",
10866
+ severity: "off",
10867
+ fixable: false,
10868
+ supportedEffect: ["v3", "v4"],
10869
+ apply: fn("newPromise.apply")(function* (sourceFile, report) {
10870
+ const ts = yield* service(TypeScriptApi);
10871
+ const typeChecker = yield* service(TypeCheckerApi);
10872
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
10873
+ const promiseSymbol = typeChecker.resolveName("Promise", void 0, ts.SymbolFlags.Value, false);
10874
+ if (!promiseSymbol) return;
10875
+ const visit = (node) => {
10876
+ if (ts.isNewExpression(node)) {
10877
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
10878
+ if (symbol3 && typeCheckerUtils.resolveToGlobalSymbol(symbol3) === promiseSymbol) {
10879
+ report({
10880
+ location: node,
10881
+ messageText: "This code constructs `new Promise(...)`, prefer Effect APIs such as `Effect.async`, `Effect.promise`, or `Effect.tryPromise` instead of manual Promise construction.",
10882
+ fixes: []
10883
+ });
10884
+ }
10885
+ }
10886
+ ts.forEachChild(node, visit);
10887
+ return void 0;
10888
+ };
10889
+ ts.forEachChild(sourceFile, visit);
10890
+ })
10891
+ });
10892
+
10579
10893
  // src/diagnostics/nodeBuiltinImport.ts
10580
10894
  var moduleAlternativesV3 = /* @__PURE__ */ new Map([
10581
10895
  ["fs", { alternative: "FileSystem", module: "fs", package: "@effect/platform" }],
@@ -10632,7 +10946,7 @@ var nodeBuiltinImport = createDiagnostic({
10632
10946
  if (match2) {
10633
10947
  report({
10634
10948
  location: statement.moduleSpecifier,
10635
- messageText: `Prefer using ${match2.alternative} from ${match2.package} instead of the Node.js '${match2.module}' module.`,
10949
+ messageText: `This module reference uses the \`${match2.module}\` module, the corresponding Effect API is \`${match2.alternative}\` from \`${match2.package}\`.`,
10636
10950
  fixes: []
10637
10951
  });
10638
10952
  }
@@ -10645,7 +10959,7 @@ var nodeBuiltinImport = createDiagnostic({
10645
10959
  if (match2) {
10646
10960
  report({
10647
10961
  location: arg,
10648
- messageText: `Prefer using ${match2.alternative} from ${match2.package} instead of the Node.js '${match2.module}' module.`,
10962
+ messageText: `This module reference uses the \`${match2.module}\` module, the corresponding Effect API is \`${match2.alternative}\` from \`${match2.package}\`.`,
10649
10963
  fixes: []
10650
10964
  });
10651
10965
  }
@@ -10699,7 +11013,7 @@ var nonObjectEffectServiceType = createDiagnostic({
10699
11013
  const propertyValue = property.initializer;
10700
11014
  const errorToReport = {
10701
11015
  location: property.name,
10702
- messageText: "Effect.Service requires the service type to be an object {} and not a primitive type. \nConsider wrapping the value in an object, or manually using Context.Tag or Effect.Tag if you want to use a primitive instead.",
11016
+ messageText: "`Effect.Service` is declared with a primitive service type. `Effect.Service` models object-shaped services; primitive values use `Context.Tag` or `Effect.Tag` directly.",
10703
11017
  fixes: []
10704
11018
  };
10705
11019
  if (propertyName === "succeed") {
@@ -10919,6 +11233,14 @@ var effectModuleMigrationDb = {
10919
11233
  "yieldNow": asUnchanged,
10920
11234
  "zip": asUnchanged,
10921
11235
  "zipWith": asUnchanged,
11236
+ "annotateLogsScoped": asUnchanged,
11237
+ "awaitAllChildren": asUnchanged,
11238
+ "bind": asUnchanged,
11239
+ "bindTo": asUnchanged,
11240
+ "Do": asUnchanged,
11241
+ "let": asUnchanged,
11242
+ "partition": asUnchanged,
11243
+ "validate": asUnchanged,
10922
11244
  // Renamed APIs (v3 name → v4 name)
10923
11245
  "catchAll": asRenamedSameBehaviour("catch"),
10924
11246
  "catchAllCause": asRenamedSameBehaviour("catchCause"),
@@ -10938,14 +11260,6 @@ var effectModuleMigrationDb = {
10938
11260
  "serviceOptional": asRenamedSameBehaviour("serviceOption"),
10939
11261
  "tapErrorCause": asRenamedSameBehaviour("tapCause"),
10940
11262
  // Removed APIs
10941
- "annotateLogsScoped": asUnchanged,
10942
- "awaitAllChildren": asUnchanged,
10943
- "bind": asUnchanged,
10944
- "bindTo": asUnchanged,
10945
- "Do": asUnchanged,
10946
- "let": asUnchanged,
10947
- "partition": asUnchanged,
10948
- "validate": asUnchanged,
10949
11263
  "catchSomeDefect": asRemoved(
10950
11264
  "Use Effect.catchDefect or Effect.matchCause to handle specific defects."
10951
11265
  ),
@@ -11461,7 +11775,7 @@ var outdatedApi = createDiagnostic({
11461
11775
  hasReported = true;
11462
11776
  report({
11463
11777
  location: propertyAccess.name,
11464
- messageText: `${propertyName} is an Effect v3 API, but the project is using Effect v4.`,
11778
+ messageText: `This project targets Effect v4, but this code uses the Effect v3 API \`${propertyName}\`. The referenced API belongs to the v3 surface rather than the configured v4 surface.`,
11465
11779
  fixes: []
11466
11780
  });
11467
11781
  }
@@ -11504,7 +11818,7 @@ var outdatedApi = createDiagnostic({
11504
11818
  if (hasReported) {
11505
11819
  report({
11506
11820
  location: { pos: 0, end: 0 },
11507
- messageText: "This project targets Effect v4, but is using Effect v3 APIs. To find the correct API to use, clone and consult the github.com/effect-ts/effect-smol repository for the corresponding v4 replacement.",
11821
+ messageText: "This project targets Effect v4, but this code uses Effect v3 APIs. The referenced API belongs to the v3 surface rather than the configured v4 surface.",
11508
11822
  fixes: []
11509
11823
  });
11510
11824
  }
@@ -11680,7 +11994,7 @@ var overriddenSchemaConstructor = createDiagnostic({
11680
11994
  };
11681
11995
  report({
11682
11996
  location: member,
11683
- messageText: "Classes extending Schema must not override the constructor; this is because it silently breaks the schema decoding behaviour. If that's needed, we recommend instead to use a static 'new' method that constructs the instance.",
11997
+ messageText: "This Schema subclass defines its own constructor. For Schema classes, constructor overrides break decoding behavior for the class shape. Custom construction can be expressed through a static `new` method instead.",
11684
11998
  fixes: (member.body ? [fixAsStaticNew] : []).concat([{
11685
11999
  fixName: "overriddenSchemaConstructor_fix",
11686
12000
  description: "Remove the constructor override",
@@ -11802,7 +12116,7 @@ var preferSchemaOverJson = createDiagnostic({
11802
12116
  if (isSome2(match2)) {
11803
12117
  report({
11804
12118
  location: match2.value,
11805
- messageText: "Consider using Effect Schema for JSON operations instead of JSON.parse/JSON.stringify",
12119
+ messageText: "This code uses `JSON.parse` or `JSON.stringify`. Effect Schema provides Effect-aware APIs for JSON parsing and stringifying.",
11806
12120
  fixes: []
11807
12121
  });
11808
12122
  }
@@ -11810,6 +12124,63 @@ var preferSchemaOverJson = createDiagnostic({
11810
12124
  })
11811
12125
  });
11812
12126
 
12127
+ // src/diagnostics/processEnvInEffect.ts
12128
+ var isEnvPropertyAccess = (tsApi, node) => tsApi.isPropertyAccessExpression(node) && tsApi.idText(node.name) === "env";
12129
+ var isProcessEnvMemberAccess = (tsApi, node) => (tsApi.isPropertyAccessExpression(node) || tsApi.isElementAccessExpression(node)) && isEnvPropertyAccess(tsApi, node.expression);
12130
+ var makeProcessEnvApply = (checkInEffect) => fn(`processEnv${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
12131
+ const ts = yield* service(TypeScriptApi);
12132
+ const typeChecker = yield* service(TypeCheckerApi);
12133
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
12134
+ const typeParser = yield* service(TypeParser);
12135
+ const processSymbol = typeChecker.resolveName("process", void 0, ts.SymbolFlags.Value, false);
12136
+ if (!processSymbol) return;
12137
+ const nodeToVisit = [];
12138
+ const appendNodeToVisit = (node) => {
12139
+ nodeToVisit.push(node);
12140
+ return void 0;
12141
+ };
12142
+ ts.forEachChild(sourceFile, appendNodeToVisit);
12143
+ while (nodeToVisit.length > 0) {
12144
+ const node = nodeToVisit.shift();
12145
+ ts.forEachChild(node, appendNodeToVisit);
12146
+ if (!isProcessEnvMemberAccess(ts, node)) continue;
12147
+ const processNode2 = node.expression.expression;
12148
+ if (!ts.isIdentifier(processNode2) || ts.idText(processNode2) !== "process") continue;
12149
+ const symbol3 = typeChecker.getSymbolAtLocation(processNode2);
12150
+ if (!symbol3) continue;
12151
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== processSymbol) continue;
12152
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
12153
+ if (inEffect !== checkInEffect) continue;
12154
+ report({
12155
+ location: node,
12156
+ messageText: checkInEffect ? "This Effect code reads from `process.env`, environment configuration in Effect code is represented through `Config` from Effect." : "This code reads from `process.env`, environment configuration is represented through `Config` from Effect.",
12157
+ fixes: []
12158
+ });
12159
+ }
12160
+ });
12161
+ var processEnvInEffect = createDiagnostic({
12162
+ name: "processEnvInEffect",
12163
+ code: 65,
12164
+ description: "Warns when reading process.env inside Effect generators instead of using Effect Config",
12165
+ group: "effectNative",
12166
+ severity: "off",
12167
+ fixable: false,
12168
+ supportedEffect: ["v3", "v4"],
12169
+ apply: makeProcessEnvApply(true)
12170
+ });
12171
+
12172
+ // src/diagnostics/processEnv.ts
12173
+ var processEnv = createDiagnostic({
12174
+ name: "processEnv",
12175
+ code: 64,
12176
+ description: "Warns when reading process.env outside Effect generators instead of using Effect Config",
12177
+ group: "effectNative",
12178
+ severity: "off",
12179
+ fixable: false,
12180
+ supportedEffect: ["v3", "v4"],
12181
+ apply: makeProcessEnvApply(false)
12182
+ });
12183
+
11813
12184
  // src/diagnostics/redundantSchemaTagIdentifier.ts
11814
12185
  var redundantSchemaTagIdentifier = createDiagnostic({
11815
12186
  name: "redundantSchemaTagIdentifier",
@@ -11920,9 +12291,7 @@ var returnEffectInGen = createDiagnostic({
11920
12291
  }] : [];
11921
12292
  report({
11922
12293
  location: node,
11923
- messageText: `You are returning an Effect-able type inside a generator function, and will result in nested Effect<Effect<...>>.
11924
- Maybe you wanted to return yield* instead?
11925
- Nested Effect-able types may be intended if you plan to later manually flatten or unwrap this Effect, if so you can safely disable this diagnostic for this line through quickfixes.`,
12294
+ messageText: "This generator returns an Effect-able value directly, which produces a nested `Effect<Effect<...>>`. If the intended result is the inner Effect value, `return yield*` represents that form.",
11926
12295
  fixes: fix
11927
12296
  });
11928
12297
  }),
@@ -12077,9 +12446,7 @@ var runEffectInsideEffect = createDiagnostic({
12077
12446
  );
12078
12447
  });
12079
12448
  const v4MethodName = `${isEffectRunCall.value.methodName}With`;
12080
- const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
12081
- Consider extracting the current services by using for example Effect.services and then use Effect.${v4MethodName} with the extracted services instead.` : `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
12082
- Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
12449
+ const messageText = supportedEffect === "v4" ? `\`${nodeText}\` is called inside an Effect with a separate services invocation. In this context, child Effects run with the surrounding services, which can be accessed through \`Effect.services\` and \`Effect.${v4MethodName}\`.` : `\`${nodeText}\` is called inside an Effect with a separate runtime invocation. In this context, run child Effects with the surrounding runtime, which can be accessed through \`Effect.runtime\` and \`Runtime.${isEffectRunCall.value.methodName}\`.`;
12083
12450
  report({
12084
12451
  location: node.expression,
12085
12452
  messageText,
@@ -12092,7 +12459,7 @@ Consider extracting the Runtime by using for example Effect.runtime and then use
12092
12459
  } else {
12093
12460
  report({
12094
12461
  location: node.expression,
12095
- messageText: `Using ${nodeText} inside an Effect is not recommended. Effects inside generators can usually just be yielded.`,
12462
+ messageText: `\`${nodeText}\` is called inside an existing Effect context. Here, the inner Effect can be used directly.`,
12096
12463
  fixes: []
12097
12464
  });
12098
12465
  }
@@ -12147,7 +12514,7 @@ var schemaStructWithTag = createDiagnostic({
12147
12514
  const otherProperties = arg.properties.filter((prop) => prop !== tagProperty);
12148
12515
  report({
12149
12516
  location: node,
12150
- messageText: "Schema.Struct with a _tag field can be simplified to Schema.TaggedStruct to make the tag optional in the constructor.",
12517
+ messageText: "This `Schema.Struct` includes a `_tag` field. `Schema.TaggedStruct` is the tagged-struct form for this pattern and makes the tag optional in the constructor.",
12151
12518
  fixes: [{
12152
12519
  fixName: "schemaStructWithTag_fix",
12153
12520
  description: "Replace with Schema.TaggedStruct",
@@ -12240,7 +12607,7 @@ var schemaSyncInEffect = createDiagnostic({
12240
12607
  const effectMethodName = syncToEffectMethod[isSchemaSyncCall.value.methodName];
12241
12608
  report({
12242
12609
  location: node.expression,
12243
- messageText: `Using ${nodeText} inside an Effect generator is not recommended. Use Schema.${effectMethodName} instead to get properly typed error channel.`,
12610
+ messageText: `\`${nodeText}\` is used inside an Effect generator. \`Schema.${effectMethodName}\` preserves the typed Effect error channel for this operation without throwing.`,
12244
12611
  fixes: []
12245
12612
  });
12246
12613
  }
@@ -12300,7 +12667,7 @@ var schemaUnionOfLiterals = createDiagnostic({
12300
12667
  const schemaLiteralExpression = firstLiteralCall.expression;
12301
12668
  report({
12302
12669
  location: node,
12303
- messageText: "A Schema.Union of multiple Schema.Literal calls can be simplified to a single Schema.Literal call.",
12670
+ messageText: "This `Schema.Union` contains multiple `Schema.Literal` members and can be simplified to a single `Schema.Literal` call.",
12304
12671
  fixes: [{
12305
12672
  fixName: "schemaUnionOfLiterals_fix",
12306
12673
  description: "Replace with a single Schema.Literal call",
@@ -12363,8 +12730,7 @@ var scopeInLayerEffect = createDiagnostic({
12363
12730
  map5(
12364
12731
  () => report({
12365
12732
  location: node,
12366
- messageText: `Seems like you are constructing a layer with a scope in the requirements.
12367
- Consider using "scoped" instead to get rid of the scope in the requirements.`,
12733
+ messageText: "This layer construction leaves `Scope` in the requirement set. The scoped API removes `Scope` from the resulting requirements.",
12368
12734
  fixes: methodIdentifier ? [{
12369
12735
  fixName: "scopeInLayerEffect_scoped",
12370
12736
  description: "Use scoped for Layer creation",
@@ -12464,7 +12830,7 @@ var serviceNotAsClass = createDiagnostic({
12464
12830
  const shapeText = typeArgs.length > 0 ? typeArgs.map((t) => sourceFile.text.substring(ts.getTokenPosOfNode(t, sourceFile), t.end)).join(", ") : "Shape";
12465
12831
  report({
12466
12832
  location: callExpr,
12467
- messageText: `ServiceMap.Service should be used in a class declaration instead of as a variable. Use: class ${variableName} extends ServiceMap.Service<${variableName}, ${shapeText}>()("${argsText.replace(/['"]/g, "")}") {}`,
12833
+ messageText: `\`ServiceMap.Service\` is assigned to a variable here, but this API is intended for a class declaration shape such as \`class ${variableName} extends ServiceMap.Service<${variableName}, ${shapeText}>()("${argsText.replace(/['"]/g, "")}") {}\`.`,
12468
12834
  fixes: [{
12469
12835
  fixName: "serviceNotAsClass",
12470
12836
  description: `Convert to class declaration`,
@@ -12677,7 +13043,7 @@ var tryCatchInEffectGen = createDiagnostic({
12677
13043
  map5(() => {
12678
13044
  report({
12679
13045
  location: node,
12680
- messageText: `Avoid using try/catch inside Effect generators. Use Effect's error handling mechanisms instead (e.g. ${alternatives.join(", ")}).`,
13046
+ messageText: `This Effect generator contains \`try/catch\`; in this context, error handling is expressed with Effect APIs such as ${alternatives.join(", ")}).`,
12681
13047
  fixes: []
12682
13048
  });
12683
13049
  }),
@@ -12738,8 +13104,7 @@ var unknownInEffectCatch = createDiagnostic({
12738
13104
  );
12739
13105
  report({
12740
13106
  location: node.expression,
12741
- messageText: `The 'catch' callback in ${nodeText} returns 'unknown'. The catch callback should be used to provide typed errors.
12742
- Consider wrapping unknown errors into Effect's Data.TaggedError for example, or narrow down the type to the specific error raised.`,
13107
+ messageText: `The \`catch\` callback in \`${nodeText}\` returns \`unknown\`, so the Effect error type stays untyped. A specific typed error preserves error-channel information, for example by narrowing the value or wrapping it in \`Data.TaggedError\`.`,
12743
13108
  fixes: []
12744
13109
  });
12745
13110
  }
@@ -12753,6 +13118,56 @@ Consider wrapping unknown errors into Effect's Data.TaggedError for example, or
12753
13118
  })
12754
13119
  });
12755
13120
 
13121
+ // src/diagnostics/unnecessaryArrowBlock.ts
13122
+ var unnecessaryArrowBlock = createDiagnostic({
13123
+ name: "unnecessaryArrowBlock",
13124
+ code: 72,
13125
+ description: "Suggests using a concise arrow body when the block only returns an expression",
13126
+ group: "style",
13127
+ severity: "off",
13128
+ fixable: true,
13129
+ supportedEffect: ["v3", "v4"],
13130
+ apply: fn("unnecessaryArrowBlock.apply")(function* (sourceFile, report) {
13131
+ const ts = yield* service(TypeScriptApi);
13132
+ const nodeToVisit = [];
13133
+ const appendNodeToVisit = (node) => {
13134
+ nodeToVisit.push(node);
13135
+ return void 0;
13136
+ };
13137
+ ts.forEachChild(sourceFile, appendNodeToVisit);
13138
+ while (nodeToVisit.length > 0) {
13139
+ const node = nodeToVisit.shift();
13140
+ ts.forEachChild(node, appendNodeToVisit);
13141
+ if (!ts.isArrowFunction(node) || !ts.isBlock(node.body)) continue;
13142
+ if (node.body.statements.length !== 1) continue;
13143
+ const [statement] = node.body.statements;
13144
+ if (!ts.isReturnStatement(statement) || !statement.expression) continue;
13145
+ const returnedExpression = statement.expression;
13146
+ report({
13147
+ location: node.body,
13148
+ messageText: "This arrow function block only returns an expression and can use a concise body.",
13149
+ fixes: [{
13150
+ fixName: "unnecessaryArrowBlock_fix",
13151
+ description: "Use a concise arrow body",
13152
+ apply: gen(function* () {
13153
+ const changeTracker = yield* service(ChangeTracker);
13154
+ const replacementNode = ts.factory.updateArrowFunction(
13155
+ node,
13156
+ node.modifiers,
13157
+ node.typeParameters,
13158
+ node.parameters,
13159
+ node.type,
13160
+ node.equalsGreaterThanToken,
13161
+ ts.factory.createParenthesizedExpression(returnedExpression)
13162
+ );
13163
+ changeTracker.replaceNode(sourceFile, node, replacementNode);
13164
+ })
13165
+ }]
13166
+ });
13167
+ }
13168
+ })
13169
+ });
13170
+
12756
13171
  // src/diagnostics/unnecessaryEffectGen.ts
12757
13172
  var unnecessaryEffectGen = createDiagnostic({
12758
13173
  name: "unnecessaryEffectGen",
@@ -12836,7 +13251,7 @@ var unnecessaryFailYieldableError = createDiagnostic({
12836
13251
  map5(
12837
13252
  () => report({
12838
13253
  location: node,
12839
- messageText: `This Effect.fail call uses a yieldable error type as argument. You can yield* the error directly instead.`,
13254
+ messageText: "This `yield* Effect.fail(...)` passes a yieldable error value. `yield*` represents that value directly without wrapping it in `Effect.fail`.",
12840
13255
  fixes: [{
12841
13256
  fixName: "unnecessaryFailYieldableError_fix",
12842
13257
  description: "Replace yield* Effect.fail with yield*",
@@ -12941,7 +13356,7 @@ var unnecessaryPipeChain = createDiagnostic({
12941
13356
  map5(({ innerCall, pipeCall }) => {
12942
13357
  report({
12943
13358
  location: node,
12944
- messageText: `Chained pipe calls can be simplified to a single pipe call`,
13359
+ messageText: "This expression contains chained `pipe` calls that can be simplified to a single `pipe` call.",
12945
13360
  fixes: [{
12946
13361
  fixName: "unnecessaryPipeChain_fix",
12947
13362
  description: "Rewrite as single pipe call",
@@ -13053,12 +13468,17 @@ var unsupportedServiceAccessors = createDiagnostic({
13053
13468
  var diagnostics = [
13054
13469
  outdatedApi,
13055
13470
  anyUnknownInErrorContext,
13471
+ asyncFunction,
13056
13472
  instanceOfSchema,
13057
13473
  catchAllToMapError,
13058
13474
  catchUnfailableEffect,
13059
13475
  classSelfMismatch,
13476
+ cryptoRandomUUID,
13477
+ cryptoRandomUUIDInEffect,
13060
13478
  duplicatePackage,
13479
+ effectDoNotation,
13061
13480
  effectFnImplicitAny,
13481
+ effectMapFlatten,
13062
13482
  effectGenUsesAdapter,
13063
13483
  missingEffectContext,
13064
13484
  missingEffectError,
@@ -13067,6 +13487,8 @@ var diagnostics = [
13067
13487
  floatingEffect,
13068
13488
  effectInFailure,
13069
13489
  missingStarInYieldEffectGen,
13490
+ newPromise,
13491
+ lazyPromiseInEffectSync,
13070
13492
  unnecessaryEffectGen,
13071
13493
  unnecessaryFailYieldableError,
13072
13494
  missingReturnYieldStar,
@@ -13075,6 +13497,8 @@ var diagnostics = [
13075
13497
  genericEffectServices,
13076
13498
  globalFetch,
13077
13499
  globalFetchInEffect,
13500
+ processEnv,
13501
+ processEnvInEffect,
13078
13502
  returnEffectInGen,
13079
13503
  tryCatchInEffectGen,
13080
13504
  importFromBarrel,
@@ -13092,6 +13516,8 @@ var diagnostics = [
13092
13516
  strictEffectProvide,
13093
13517
  unknownInEffectCatch,
13094
13518
  runEffectInsideEffect,
13519
+ nestedEffectGenYield,
13520
+ unnecessaryArrowBlock,
13095
13521
  schemaUnionOfLiterals,
13096
13522
  schemaStructWithTag,
13097
13523
  globalErrorInEffectCatch,
@@ -14409,7 +14835,7 @@ var middlewareGenLike = fn("middlewareGenLike")(function* (sourceFile, _span, pr
14409
14835
  // src/quickinfo/dedupeJsDocs.ts
14410
14836
  var SymbolDisplayPartEq = make((fa, fb) => fa.kind === fb.kind && fa.text === fb.text);
14411
14837
  var JSDocTagInfoEq = make(
14412
- (fa, fb) => fa.name === fb.name && typeof fa.text === typeof fb.text && (typeof fa.text !== "undefined" ? Array2(SymbolDisplayPartEq)(fa.text, fb.text) : true)
14838
+ (fa, fb) => fa.name === fb.name && typeof fa.text === typeof fb.text && (typeof fa.text !== "undefined" ? Array_(SymbolDisplayPartEq)(fa.text, fb.text) : true)
14413
14839
  );
14414
14840
  function dedupeJsDocs(quickInfo2) {
14415
14841
  if (!quickInfo2) return succeed3(quickInfo2);