@effect/language-service 0.84.3 → 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/transform.js CHANGED
@@ -24,7 +24,7 @@ __export(transform_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(transform_exports);
26
26
 
27
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Pipeable.js
27
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Pipeable.js
28
28
  var pipeArguments = (self, args3) => {
29
29
  switch (args3.length) {
30
30
  case 0:
@@ -57,7 +57,7 @@ var pipeArguments = (self, args3) => {
57
57
  }
58
58
  };
59
59
 
60
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Function.js
60
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Function.js
61
61
  var dual = function(arity, body) {
62
62
  if (typeof arity === "function") {
63
63
  return function() {
@@ -105,7 +105,7 @@ function pipe(a, ...args3) {
105
105
  return pipeArguments(a, args3);
106
106
  }
107
107
 
108
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/equal.js
108
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/equal.js
109
109
  var getAllObjectKeys = (obj) => {
110
110
  const keys2 = new Set(Reflect.ownKeys(obj));
111
111
  if (obj.constructor === Object) return keys2;
@@ -128,7 +128,7 @@ var getAllObjectKeys = (obj) => {
128
128
  };
129
129
  var byReferenceInstances = /* @__PURE__ */ new WeakSet();
130
130
 
131
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Predicate.js
131
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Predicate.js
132
132
  function isString(input) {
133
133
  return typeof input === "string";
134
134
  }
@@ -149,7 +149,7 @@ function isObjectKeyword(input) {
149
149
  }
150
150
  var hasProperty = /* @__PURE__ */ dual(2, (self, property) => isObjectKeyword(self) && property in self);
151
151
 
152
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Hash.js
152
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Hash.js
153
153
  var symbol = "~effect/interfaces/Hash";
154
154
  var hash = (self) => {
155
155
  switch (typeof self) {
@@ -268,7 +268,7 @@ function withVisitedTracking(obj, fn2) {
268
268
  return result;
269
269
  }
270
270
 
271
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Equal.js
271
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Equal.js
272
272
  var symbol2 = "~effect/interfaces/Equal";
273
273
  function equals() {
274
274
  if (arguments.length === 1) {
@@ -430,10 +430,10 @@ var compareSets = /* @__PURE__ */ makeCompareSet(compareBoth);
430
430
  var isEqual = (u) => hasProperty(u, symbol2);
431
431
  var asEquivalence = () => equals;
432
432
 
433
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/array.js
433
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/array.js
434
434
  var isArrayNonEmpty = (self) => self.length > 0;
435
435
 
436
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Redactable.js
436
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Redactable.js
437
437
  var symbolRedactable = /* @__PURE__ */ Symbol.for("~effect/Inspectable/redactable");
438
438
  var isRedactable = (u) => hasProperty(u, symbolRedactable);
439
439
  function redact(u) {
@@ -452,7 +452,7 @@ var emptyServiceMap = {
452
452
  }
453
453
  };
454
454
 
455
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Formatter.js
455
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Formatter.js
456
456
  function format(input, options) {
457
457
  const space = options?.space ?? 0;
458
458
  const seen = /* @__PURE__ */ new WeakSet();
@@ -531,7 +531,7 @@ function safeToString(input) {
531
531
  }
532
532
  }
533
533
 
534
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Inspectable.js
534
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Inspectable.js
535
535
  var NodeInspectSymbol = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
536
536
  var toJson = (input) => {
537
537
  try {
@@ -575,7 +575,7 @@ var Class = class {
575
575
  }
576
576
  };
577
577
 
578
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Utils.js
578
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Utils.js
579
579
  var SingleShotGen = class _SingleShotGen {
580
580
  called = false;
581
581
  self;
@@ -618,7 +618,7 @@ var forced = {
618
618
  var isNotOptimizedAway = /* @__PURE__ */ standard[InternalTypeId](() => new Error().stack)?.includes(InternalTypeId) === true;
619
619
  var internalCall = isNotOptimizedAway ? standard[InternalTypeId] : forced[InternalTypeId];
620
620
 
621
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/core.js
621
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/core.js
622
622
  var EffectTypeId = `~effect/Effect`;
623
623
  var ExitTypeId = `~effect/Exit`;
624
624
  var effectVariance = {
@@ -965,7 +965,7 @@ var DoneVoid = {
965
965
  value: void 0
966
966
  };
967
967
 
968
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/option.js
968
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/option.js
969
969
  var TypeId = "~effect/data/Option";
970
970
  var CommonProto = {
971
971
  [TypeId]: {
@@ -1036,7 +1036,7 @@ var some = (value) => {
1036
1036
  return a;
1037
1037
  };
1038
1038
 
1039
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/internal/result.js
1039
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/result.js
1040
1040
  var TypeId2 = "~effect/data/Result";
1041
1041
  var CommonProto2 = {
1042
1042
  [TypeId2]: {
@@ -1107,13 +1107,13 @@ var succeed = (success) => {
1107
1107
  return a;
1108
1108
  };
1109
1109
 
1110
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Order.js
1110
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Order.js
1111
1111
  function make(compare) {
1112
1112
  return (self, that) => self === that ? 0 : compare(self, that);
1113
1113
  }
1114
1114
  var String2 = /* @__PURE__ */ make((self, that) => self < that ? -1 : 1);
1115
1115
 
1116
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Option.js
1116
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Option.js
1117
1117
  var none2 = () => none;
1118
1118
  var some2 = some;
1119
1119
  var isNone2 = isNone;
@@ -1123,7 +1123,7 @@ var orElse = /* @__PURE__ */ dual(2, (self, that) => isNone2(self) ? that() : se
1123
1123
  var fromNullishOr = (a) => a == null ? none2() : some2(a);
1124
1124
  var getOrUndefined = /* @__PURE__ */ getOrElse(constUndefined);
1125
1125
 
1126
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Result.js
1126
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Result.js
1127
1127
  var succeed2 = succeed;
1128
1128
  var fail2 = fail;
1129
1129
  var isFailure2 = isFailure;
@@ -1131,7 +1131,7 @@ var isSuccess2 = isSuccess;
1131
1131
  var map = /* @__PURE__ */ dual(2, (self, f) => isSuccess2(self) ? succeed2(f(self.success)) : fail2(self.failure));
1132
1132
  var getOrElse2 = /* @__PURE__ */ dual(2, (self, onFailure) => isFailure2(self) ? onFailure(self.failure) : self.success);
1133
1133
 
1134
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Record.js
1134
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Record.js
1135
1135
  var map2 = /* @__PURE__ */ dual(2, (self, f) => {
1136
1136
  const out = {
1137
1137
  ...self
@@ -1143,7 +1143,7 @@ var map2 = /* @__PURE__ */ dual(2, (self, f) => {
1143
1143
  });
1144
1144
  var keys = (self) => Object.keys(self);
1145
1145
 
1146
- // ../../node_modules/.pnpm/effect@4.0.0-beta.38/node_modules/effect/dist/Array.js
1146
+ // ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Array.js
1147
1147
  var Array2 = globalThis.Array;
1148
1148
  var fromIterable = (collection) => Array2.isArray(collection) ? collection : Array2.from(collection);
1149
1149
  var append = /* @__PURE__ */ dual(2, (self, last) => [...self, last]);
@@ -4117,33 +4117,33 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
4117
4117
  ),
4118
4118
  ([Identifier, Service]) => ({ Identifier, Service })
4119
4119
  );
4120
- const serviceVarianceStruct = (type, atLocation) => map4(
4121
- all(
4122
- varianceStructInvariantType(type, atLocation, "_Identifier"),
4123
- varianceStructInvariantType(type, atLocation, "_Service")
4124
- ),
4125
- ([Identifier, Service]) => ({ Identifier, Service })
4126
- );
4127
4120
  const serviceType = cachedBy(
4128
4121
  fn("TypeParser.serviceType")(function* (type, atLocation) {
4122
+ if (supportedEffect() !== "v4") return yield* typeParserIssue("v4 only");
4129
4123
  yield* pipeableType(type, atLocation);
4130
- const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
4131
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
4132
- );
4133
- if (propertiesSymbols.length === 0) {
4134
- return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
4124
+ const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/ServiceMap/Service");
4125
+ if (!typeIdSymbol) {
4126
+ return yield* typeParserIssue("Type has no service key type id", type, atLocation);
4135
4127
  }
4136
- propertiesSymbols.sort((a, b) => ts.symbolName(b).indexOf("TypeId") - ts.symbolName(a).indexOf("TypeId"));
4137
- return yield* firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
4138
- const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
4139
- return serviceVarianceStruct(propertyType, atLocation);
4140
- }));
4128
+ const identifierSymbol = typeChecker.getPropertyOfType(type, "Identifier");
4129
+ if (!identifierSymbol) {
4130
+ return yield* typeParserIssue("Type has no 'Identifier' property", type, atLocation);
4131
+ }
4132
+ const serviceSymbol = typeChecker.getPropertyOfType(type, "Service");
4133
+ if (!serviceSymbol) {
4134
+ return yield* typeParserIssue("Type has no 'Service' property", type, atLocation);
4135
+ }
4136
+ return {
4137
+ Identifier: typeChecker.getTypeOfSymbolAtLocation(identifierSymbol, atLocation),
4138
+ Service: typeChecker.getTypeOfSymbolAtLocation(serviceSymbol, atLocation)
4139
+ };
4141
4140
  }),
4142
4141
  "TypeParser.serviceType",
4143
4142
  (type) => type
4144
4143
  );
4145
4144
  const contextTag = cachedBy(
4146
4145
  fn("TypeParser.contextTag")(function* (type, atLocation) {
4146
+ if (supportedEffect() !== "v3") return yield* typeParserIssue("v3 only");
4147
4147
  yield* pipeableType(type, atLocation);
4148
4148
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
4149
4149
  (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
@@ -4284,6 +4284,19 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
4284
4284
  "TypeParser.promiseLike",
4285
4285
  (type) => type
4286
4286
  );
4287
+ const promiseType = cachedBy(
4288
+ function(type, atLocation) {
4289
+ const promiseSymbol = typeChecker.resolveName("Promise", void 0, ts.SymbolFlags.Type, false);
4290
+ if (!promiseSymbol) return typeParserIssue("global Promise type not found", type, atLocation);
4291
+ const globalPromiseType = typeChecker.getDeclaredTypeOfSymbol(promiseSymbol);
4292
+ if (type === globalPromiseType || "target" in type && type.target === globalPromiseType || typeChecker.isTypeAssignableTo(type, globalPromiseType)) {
4293
+ return succeed3(type);
4294
+ }
4295
+ return typeParserIssue("type is not a Promise", type, atLocation);
4296
+ },
4297
+ "TypeParser.promiseType",
4298
+ (type) => type
4299
+ );
4287
4300
  const extendsSchemaClass = cachedBy(
4288
4301
  fn("TypeParser.extendsSchemaClass")(function* (atLocation) {
4289
4302
  if (!atLocation.name) {
@@ -5303,6 +5316,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
5303
5316
  singleArgCall,
5304
5317
  scopeType,
5305
5318
  promiseLike,
5319
+ promiseType,
5306
5320
  extendsEffectTag,
5307
5321
  extendsEffectService,
5308
5322
  extendsServiceMapService,
@@ -5431,6 +5445,37 @@ var anyUnknownInErrorContext = createDiagnostic({
5431
5445
  })
5432
5446
  });
5433
5447
 
5448
+ // src/diagnostics/asyncFunction.ts
5449
+ var asyncFunction = createDiagnostic({
5450
+ name: "asyncFunction",
5451
+ code: 69,
5452
+ description: "Warns when declaring async functions and suggests using Effect values and Effect.gen for async control flow",
5453
+ group: "effectNative",
5454
+ severity: "off",
5455
+ fixable: false,
5456
+ supportedEffect: ["v3", "v4"],
5457
+ apply: fn("asyncFunction.apply")(function* (sourceFile, report) {
5458
+ const ts = yield* service(TypeScriptApi);
5459
+ const hasAsyncModifier = (node) => ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) === true;
5460
+ const visit = (node) => {
5461
+ if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) {
5462
+ ts.forEachChild(node, visit);
5463
+ return void 0;
5464
+ }
5465
+ if (hasAsyncModifier(node)) {
5466
+ report({
5467
+ location: node,
5468
+ messageText: "This code declares an async function, consider representing this async control flow with Effect values and `Effect.gen`.",
5469
+ fixes: []
5470
+ });
5471
+ }
5472
+ ts.forEachChild(node, visit);
5473
+ return void 0;
5474
+ };
5475
+ ts.forEachChild(sourceFile, visit);
5476
+ })
5477
+ });
5478
+
5434
5479
  // src/diagnostics/catchAllToMapError.ts
5435
5480
  var catchAllToMapError = createDiagnostic({
5436
5481
  name: "catchAllToMapError",
@@ -5659,6 +5704,59 @@ var classSelfMismatch = createDiagnostic({
5659
5704
  })
5660
5705
  });
5661
5706
 
5707
+ // src/diagnostics/cryptoRandomUUIDInEffect.ts
5708
+ var makeCryptoRandomUUIDApply = (checkInEffect) => fn(`cryptoRandomUUID${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
5709
+ const ts = yield* service(TypeScriptApi);
5710
+ const typeChecker = yield* service(TypeCheckerApi);
5711
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
5712
+ const typeParser = yield* service(TypeParser);
5713
+ const cryptoSymbol = typeChecker.resolveName("crypto", void 0, ts.SymbolFlags.Value, false);
5714
+ if (!cryptoSymbol) return;
5715
+ const nodeToVisit = [];
5716
+ const appendNodeToVisit = (node) => {
5717
+ nodeToVisit.push(node);
5718
+ return void 0;
5719
+ };
5720
+ ts.forEachChild(sourceFile, appendNodeToVisit);
5721
+ while (nodeToVisit.length > 0) {
5722
+ const node = nodeToVisit.shift();
5723
+ ts.forEachChild(node, appendNodeToVisit);
5724
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "randomUUID") continue;
5725
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
5726
+ if (!symbol3) continue;
5727
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== cryptoSymbol) continue;
5728
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
5729
+ if (inEffect !== checkInEffect) continue;
5730
+ report({
5731
+ location: node,
5732
+ 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.",
5733
+ fixes: []
5734
+ });
5735
+ }
5736
+ });
5737
+ var cryptoRandomUUIDInEffect = createDiagnostic({
5738
+ name: "cryptoRandomUUIDInEffect",
5739
+ code: 67,
5740
+ 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",
5741
+ group: "effectNative",
5742
+ severity: "off",
5743
+ fixable: false,
5744
+ supportedEffect: ["v4"],
5745
+ apply: makeCryptoRandomUUIDApply(true)
5746
+ });
5747
+
5748
+ // src/diagnostics/cryptoRandomUUID.ts
5749
+ var cryptoRandomUUID = createDiagnostic({
5750
+ name: "cryptoRandomUUID",
5751
+ code: 66,
5752
+ 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",
5753
+ group: "effectNative",
5754
+ severity: "off",
5755
+ fixable: false,
5756
+ supportedEffect: ["v4"],
5757
+ apply: makeCryptoRandomUUIDApply(false)
5758
+ });
5759
+
5662
5760
  // src/core/KeyBuilder.ts
5663
5761
  var makeKeyBuilder = fn("KeyBuilder")(
5664
5762
  function* (sourceFile) {
@@ -5876,6 +5974,40 @@ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageNam
5876
5974
  })
5877
5975
  });
5878
5976
 
5977
+ // src/diagnostics/effectDoNotation.ts
5978
+ var effectDoNotation = createDiagnostic({
5979
+ name: "effectDoNotation",
5980
+ code: 73,
5981
+ description: "Suggests using Effect.gen or Effect.fn instead of the Effect.Do notation helpers",
5982
+ group: "style",
5983
+ severity: "off",
5984
+ fixable: false,
5985
+ supportedEffect: ["v3", "v4"],
5986
+ apply: fn("effectDoNotation.apply")(function* (sourceFile, report) {
5987
+ const ts = yield* service(TypeScriptApi);
5988
+ const typeParser = yield* service(TypeParser);
5989
+ const nodeToVisit = [];
5990
+ const appendNodeToVisit = (node) => {
5991
+ nodeToVisit.push(node);
5992
+ return void 0;
5993
+ };
5994
+ ts.forEachChild(sourceFile, appendNodeToVisit);
5995
+ while (nodeToVisit.length > 0) {
5996
+ const node = nodeToVisit.shift();
5997
+ const isReference = yield* orUndefined(typeParser.isNodeReferenceToEffectModuleApi("Do")(node));
5998
+ if (isReference) {
5999
+ report({
6000
+ location: node,
6001
+ messageText: "This uses the Effect do emulation. `Effect.gen` or `Effect.fn` achieve the same result with native JS scopes.",
6002
+ fixes: []
6003
+ });
6004
+ continue;
6005
+ }
6006
+ ts.forEachChild(node, appendNodeToVisit);
6007
+ }
6008
+ })
6009
+ });
6010
+
5879
6011
  // src/diagnostics/effectFnIife.ts
5880
6012
  var effectFnIife = createDiagnostic({
5881
6013
  name: "effectFnIife",
@@ -6822,6 +6954,43 @@ var effectInVoidSuccess = createDiagnostic({
6822
6954
  })
6823
6955
  });
6824
6956
 
6957
+ // src/diagnostics/effectMapFlatten.ts
6958
+ var effectMapFlatten = createDiagnostic({
6959
+ name: "effectMapFlatten",
6960
+ code: 74,
6961
+ description: "Suggests using Effect.flatMap instead of Effect.map followed by Effect.flatten in piping flows",
6962
+ group: "style",
6963
+ severity: "suggestion",
6964
+ fixable: false,
6965
+ supportedEffect: ["v3", "v4"],
6966
+ apply: fn("effectMapFlatten.apply")(function* (sourceFile, report) {
6967
+ const typeParser = yield* service(TypeParser);
6968
+ const flows = yield* typeParser.pipingFlows(false)(sourceFile);
6969
+ for (const flow2 of flows) {
6970
+ for (let index = 0; index < flow2.transformations.length - 1; index++) {
6971
+ const mapTransformation = flow2.transformations[index];
6972
+ const flattenTransformation = flow2.transformations[index + 1];
6973
+ if (!mapTransformation || !flattenTransformation || !mapTransformation?.args || flattenTransformation?.args || mapTransformation.kind !== "pipe" && mapTransformation.kind !== "pipeable" || flattenTransformation.kind !== mapTransformation.kind) {
6974
+ continue;
6975
+ }
6976
+ const isMapCall = yield* orUndefined(
6977
+ typeParser.isNodeReferenceToEffectModuleApi("map")(mapTransformation.callee)
6978
+ );
6979
+ const isFlattenCall = yield* orUndefined(
6980
+ typeParser.isNodeReferenceToEffectModuleApi("flatten")(flattenTransformation.callee)
6981
+ );
6982
+ if (isMapCall && isFlattenCall) {
6983
+ report({
6984
+ location: flattenTransformation.callee,
6985
+ messageText: "`Effect.map` + `Effect.flatten` is the same as `Effect.flatMap` that expresses the same steps more directly.",
6986
+ fixes: []
6987
+ });
6988
+ }
6989
+ }
6990
+ }
6991
+ })
6992
+ });
6993
+
6825
6994
  // src/diagnostics/effectMapVoid.ts
6826
6995
  var effectMapVoid = createDiagnostic({
6827
6996
  name: "effectMapVoid",
@@ -7089,6 +7258,7 @@ var genericEffectServices = createDiagnostic({
7089
7258
  for (const [type, reportAt] of typesToCheck) {
7090
7259
  yield* pipe(
7091
7260
  typeParser.contextTag(type, node),
7261
+ orElse2(() => typeParser.serviceType(type, node)),
7092
7262
  map4(() => {
7093
7263
  report({
7094
7264
  location: reportAt,
@@ -7852,6 +8022,55 @@ var layerMergeAllWithDependencies = createDiagnostic({
7852
8022
  })
7853
8023
  });
7854
8024
 
8025
+ // src/diagnostics/lazyPromiseInEffectSync.ts
8026
+ var lazyPromiseInEffectSync = createDiagnostic({
8027
+ name: "lazyPromiseInEffectSync",
8028
+ code: 70,
8029
+ description: "Warns when Effect.sync lazily returns a Promise instead of using an async Effect constructor",
8030
+ group: "antipattern",
8031
+ severity: "warning",
8032
+ fixable: false,
8033
+ supportedEffect: ["v3", "v4"],
8034
+ apply: fn("lazyPromiseInEffectSync.apply")(function* (sourceFile, report) {
8035
+ const ts = yield* service(TypeScriptApi);
8036
+ const typeChecker = yield* service(TypeCheckerApi);
8037
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
8038
+ const typeParser = yield* service(TypeParser);
8039
+ const nodeToVisit = [];
8040
+ const appendNodeToVisit = (node) => {
8041
+ nodeToVisit.push(node);
8042
+ return void 0;
8043
+ };
8044
+ ts.forEachChild(sourceFile, appendNodeToVisit);
8045
+ while (nodeToVisit.length > 0) {
8046
+ const node = nodeToVisit.shift();
8047
+ ts.forEachChild(node, appendNodeToVisit);
8048
+ if (!ts.isCallExpression(node)) continue;
8049
+ const isSyncCall = yield* orUndefined(
8050
+ typeParser.isNodeReferenceToEffectModuleApi("sync")(node.expression)
8051
+ );
8052
+ if (!isSyncCall) continue;
8053
+ const lazyArg = node.arguments[0];
8054
+ if (!lazyArg) continue;
8055
+ const lazyArgType = typeCheckerUtils.getTypeAtLocation(lazyArg);
8056
+ if (!lazyArgType) continue;
8057
+ const entries = typeCheckerUtils.unrollUnionMembers(lazyArgType).flatMap(
8058
+ (member) => typeChecker.getSignaturesOfType(member, ts.SignatureKind.Call).map(
8059
+ (signature) => typeParser.promiseType(typeChecker.getReturnTypeOfSignature(signature), lazyArg)
8060
+ )
8061
+ );
8062
+ if (entries.length === 0) continue;
8063
+ const promiseReturn = yield* orUndefined(firstSuccessOf(entries));
8064
+ if (!promiseReturn) continue;
8065
+ report({
8066
+ location: lazyArg,
8067
+ messageText: "This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work.",
8068
+ fixes: []
8069
+ });
8070
+ }
8071
+ })
8072
+ });
8073
+
7855
8074
  // src/diagnostics/leakingRequirements.ts
7856
8075
  var leakingRequirements = createDiagnostic({
7857
8076
  name: "leakingRequirements",
@@ -8785,6 +9004,74 @@ var multipleEffectProvide = createDiagnostic({
8785
9004
  })
8786
9005
  });
8787
9006
 
9007
+ // src/diagnostics/nestedEffectGenYield.ts
9008
+ var nestedEffectGenYield = createDiagnostic({
9009
+ name: "nestedEffectGenYield",
9010
+ code: 71,
9011
+ description: "Warns when yielding a nested bare Effect.gen inside an existing Effect generator context",
9012
+ group: "style",
9013
+ severity: "off",
9014
+ fixable: false,
9015
+ supportedEffect: ["v3", "v4"],
9016
+ apply: fn("nestedEffectGenYield.apply")(function* (sourceFile, report) {
9017
+ const ts = yield* service(TypeScriptApi);
9018
+ const typeParser = yield* service(TypeParser);
9019
+ const nodeToVisit = [];
9020
+ const appendNodeToVisit = (node) => {
9021
+ nodeToVisit.push(node);
9022
+ return void 0;
9023
+ };
9024
+ ts.forEachChild(sourceFile, appendNodeToVisit);
9025
+ while (nodeToVisit.length > 0) {
9026
+ const node = nodeToVisit.shift();
9027
+ ts.forEachChild(node, appendNodeToVisit);
9028
+ if (!ts.isYieldExpression(node) || !node.asteriskToken || !node.expression) continue;
9029
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
9030
+ if (!inEffect) continue;
9031
+ const bareNestedEffectGen = yield* orUndefined(typeParser.effectGen(node.expression));
9032
+ if (!bareNestedEffectGen) continue;
9033
+ report({
9034
+ location: node.expression,
9035
+ messageText: "This `yield*` is applied to a nested `Effect.gen(...)` that can be inlined in the parent Effect generator context.",
9036
+ fixes: []
9037
+ });
9038
+ }
9039
+ })
9040
+ });
9041
+
9042
+ // src/diagnostics/newPromise.ts
9043
+ var newPromise = createDiagnostic({
9044
+ name: "newPromise",
9045
+ code: 68,
9046
+ description: "Warns when constructing promises with new Promise instead of using Effect APIs",
9047
+ group: "effectNative",
9048
+ severity: "off",
9049
+ fixable: false,
9050
+ supportedEffect: ["v3", "v4"],
9051
+ apply: fn("newPromise.apply")(function* (sourceFile, report) {
9052
+ const ts = yield* service(TypeScriptApi);
9053
+ const typeChecker = yield* service(TypeCheckerApi);
9054
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
9055
+ const promiseSymbol = typeChecker.resolveName("Promise", void 0, ts.SymbolFlags.Value, false);
9056
+ if (!promiseSymbol) return;
9057
+ const visit = (node) => {
9058
+ if (ts.isNewExpression(node)) {
9059
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
9060
+ if (symbol3 && typeCheckerUtils.resolveToGlobalSymbol(symbol3) === promiseSymbol) {
9061
+ report({
9062
+ location: node,
9063
+ messageText: "This code constructs `new Promise(...)`, prefer Effect APIs such as `Effect.async`, `Effect.promise`, or `Effect.tryPromise` instead of manual Promise construction.",
9064
+ fixes: []
9065
+ });
9066
+ }
9067
+ }
9068
+ ts.forEachChild(node, visit);
9069
+ return void 0;
9070
+ };
9071
+ ts.forEachChild(sourceFile, visit);
9072
+ })
9073
+ });
9074
+
8788
9075
  // src/diagnostics/nodeBuiltinImport.ts
8789
9076
  var moduleAlternativesV3 = /* @__PURE__ */ new Map([
8790
9077
  ["fs", { alternative: "FileSystem", module: "fs", package: "@effect/platform" }],
@@ -9128,6 +9415,14 @@ var effectModuleMigrationDb = {
9128
9415
  "yieldNow": asUnchanged,
9129
9416
  "zip": asUnchanged,
9130
9417
  "zipWith": asUnchanged,
9418
+ "annotateLogsScoped": asUnchanged,
9419
+ "awaitAllChildren": asUnchanged,
9420
+ "bind": asUnchanged,
9421
+ "bindTo": asUnchanged,
9422
+ "Do": asUnchanged,
9423
+ "let": asUnchanged,
9424
+ "partition": asUnchanged,
9425
+ "validate": asUnchanged,
9131
9426
  // Renamed APIs (v3 name → v4 name)
9132
9427
  "catchAll": asRenamedSameBehaviour("catch"),
9133
9428
  "catchAllCause": asRenamedSameBehaviour("catchCause"),
@@ -9147,14 +9442,6 @@ var effectModuleMigrationDb = {
9147
9442
  "serviceOptional": asRenamedSameBehaviour("serviceOption"),
9148
9443
  "tapErrorCause": asRenamedSameBehaviour("tapCause"),
9149
9444
  // Removed APIs
9150
- "annotateLogsScoped": asUnchanged,
9151
- "awaitAllChildren": asUnchanged,
9152
- "bind": asUnchanged,
9153
- "bindTo": asUnchanged,
9154
- "Do": asUnchanged,
9155
- "let": asUnchanged,
9156
- "partition": asUnchanged,
9157
- "validate": asUnchanged,
9158
9445
  "catchSomeDefect": asRemoved(
9159
9446
  "Use Effect.catchDefect or Effect.matchCause to handle specific defects."
9160
9447
  ),
@@ -11088,6 +11375,63 @@ var preferSchemaOverJson = createDiagnostic({
11088
11375
  })
11089
11376
  });
11090
11377
 
11378
+ // src/diagnostics/processEnvInEffect.ts
11379
+ var isEnvPropertyAccess = (tsApi, node) => tsApi.isPropertyAccessExpression(node) && tsApi.idText(node.name) === "env";
11380
+ var isProcessEnvMemberAccess = (tsApi, node) => (tsApi.isPropertyAccessExpression(node) || tsApi.isElementAccessExpression(node)) && isEnvPropertyAccess(tsApi, node.expression);
11381
+ var makeProcessEnvApply = (checkInEffect) => fn(`processEnv${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
11382
+ const ts = yield* service(TypeScriptApi);
11383
+ const typeChecker = yield* service(TypeCheckerApi);
11384
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
11385
+ const typeParser = yield* service(TypeParser);
11386
+ const processSymbol = typeChecker.resolveName("process", void 0, ts.SymbolFlags.Value, false);
11387
+ if (!processSymbol) return;
11388
+ const nodeToVisit = [];
11389
+ const appendNodeToVisit = (node) => {
11390
+ nodeToVisit.push(node);
11391
+ return void 0;
11392
+ };
11393
+ ts.forEachChild(sourceFile, appendNodeToVisit);
11394
+ while (nodeToVisit.length > 0) {
11395
+ const node = nodeToVisit.shift();
11396
+ ts.forEachChild(node, appendNodeToVisit);
11397
+ if (!isProcessEnvMemberAccess(ts, node)) continue;
11398
+ const processNode = node.expression.expression;
11399
+ if (!ts.isIdentifier(processNode) || ts.idText(processNode) !== "process") continue;
11400
+ const symbol3 = typeChecker.getSymbolAtLocation(processNode);
11401
+ if (!symbol3) continue;
11402
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== processSymbol) continue;
11403
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
11404
+ if (inEffect !== checkInEffect) continue;
11405
+ report({
11406
+ location: node,
11407
+ 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.",
11408
+ fixes: []
11409
+ });
11410
+ }
11411
+ });
11412
+ var processEnvInEffect = createDiagnostic({
11413
+ name: "processEnvInEffect",
11414
+ code: 65,
11415
+ description: "Warns when reading process.env inside Effect generators instead of using Effect Config",
11416
+ group: "effectNative",
11417
+ severity: "off",
11418
+ fixable: false,
11419
+ supportedEffect: ["v3", "v4"],
11420
+ apply: makeProcessEnvApply(true)
11421
+ });
11422
+
11423
+ // src/diagnostics/processEnv.ts
11424
+ var processEnv = createDiagnostic({
11425
+ name: "processEnv",
11426
+ code: 64,
11427
+ description: "Warns when reading process.env outside Effect generators instead of using Effect Config",
11428
+ group: "effectNative",
11429
+ severity: "off",
11430
+ fixable: false,
11431
+ supportedEffect: ["v3", "v4"],
11432
+ apply: makeProcessEnvApply(false)
11433
+ });
11434
+
11091
11435
  // src/diagnostics/redundantSchemaTagIdentifier.ts
11092
11436
  var redundantSchemaTagIdentifier = createDiagnostic({
11093
11437
  name: "redundantSchemaTagIdentifier",
@@ -12025,6 +12369,56 @@ var unknownInEffectCatch = createDiagnostic({
12025
12369
  })
12026
12370
  });
12027
12371
 
12372
+ // src/diagnostics/unnecessaryArrowBlock.ts
12373
+ var unnecessaryArrowBlock = createDiagnostic({
12374
+ name: "unnecessaryArrowBlock",
12375
+ code: 72,
12376
+ description: "Suggests using a concise arrow body when the block only returns an expression",
12377
+ group: "style",
12378
+ severity: "off",
12379
+ fixable: true,
12380
+ supportedEffect: ["v3", "v4"],
12381
+ apply: fn("unnecessaryArrowBlock.apply")(function* (sourceFile, report) {
12382
+ const ts = yield* service(TypeScriptApi);
12383
+ const nodeToVisit = [];
12384
+ const appendNodeToVisit = (node) => {
12385
+ nodeToVisit.push(node);
12386
+ return void 0;
12387
+ };
12388
+ ts.forEachChild(sourceFile, appendNodeToVisit);
12389
+ while (nodeToVisit.length > 0) {
12390
+ const node = nodeToVisit.shift();
12391
+ ts.forEachChild(node, appendNodeToVisit);
12392
+ if (!ts.isArrowFunction(node) || !ts.isBlock(node.body)) continue;
12393
+ if (node.body.statements.length !== 1) continue;
12394
+ const [statement] = node.body.statements;
12395
+ if (!ts.isReturnStatement(statement) || !statement.expression) continue;
12396
+ const returnedExpression = statement.expression;
12397
+ report({
12398
+ location: node.body,
12399
+ messageText: "This arrow function block only returns an expression and can use a concise body.",
12400
+ fixes: [{
12401
+ fixName: "unnecessaryArrowBlock_fix",
12402
+ description: "Use a concise arrow body",
12403
+ apply: gen(function* () {
12404
+ const changeTracker = yield* service(ChangeTracker);
12405
+ const replacementNode = ts.factory.updateArrowFunction(
12406
+ node,
12407
+ node.modifiers,
12408
+ node.typeParameters,
12409
+ node.parameters,
12410
+ node.type,
12411
+ node.equalsGreaterThanToken,
12412
+ ts.factory.createParenthesizedExpression(returnedExpression)
12413
+ );
12414
+ changeTracker.replaceNode(sourceFile, node, replacementNode);
12415
+ })
12416
+ }]
12417
+ });
12418
+ }
12419
+ })
12420
+ });
12421
+
12028
12422
  // src/diagnostics/unnecessaryEffectGen.ts
12029
12423
  var unnecessaryEffectGen = createDiagnostic({
12030
12424
  name: "unnecessaryEffectGen",
@@ -12325,12 +12719,17 @@ var unsupportedServiceAccessors = createDiagnostic({
12325
12719
  var diagnostics = [
12326
12720
  outdatedApi,
12327
12721
  anyUnknownInErrorContext,
12722
+ asyncFunction,
12328
12723
  instanceOfSchema,
12329
12724
  catchAllToMapError,
12330
12725
  catchUnfailableEffect,
12331
12726
  classSelfMismatch,
12727
+ cryptoRandomUUID,
12728
+ cryptoRandomUUIDInEffect,
12332
12729
  duplicatePackage,
12730
+ effectDoNotation,
12333
12731
  effectFnImplicitAny,
12732
+ effectMapFlatten,
12334
12733
  effectGenUsesAdapter,
12335
12734
  missingEffectContext,
12336
12735
  missingEffectError,
@@ -12339,6 +12738,8 @@ var diagnostics = [
12339
12738
  floatingEffect,
12340
12739
  effectInFailure,
12341
12740
  missingStarInYieldEffectGen,
12741
+ newPromise,
12742
+ lazyPromiseInEffectSync,
12342
12743
  unnecessaryEffectGen,
12343
12744
  unnecessaryFailYieldableError,
12344
12745
  missingReturnYieldStar,
@@ -12347,6 +12748,8 @@ var diagnostics = [
12347
12748
  genericEffectServices,
12348
12749
  globalFetch,
12349
12750
  globalFetchInEffect,
12751
+ processEnv,
12752
+ processEnvInEffect,
12350
12753
  returnEffectInGen,
12351
12754
  tryCatchInEffectGen,
12352
12755
  importFromBarrel,
@@ -12364,6 +12767,8 @@ var diagnostics = [
12364
12767
  strictEffectProvide,
12365
12768
  unknownInEffectCatch,
12366
12769
  runEffectInsideEffect,
12770
+ nestedEffectGenYield,
12771
+ unnecessaryArrowBlock,
12367
12772
  schemaUnionOfLiterals,
12368
12773
  schemaStructWithTag,
12369
12774
  globalErrorInEffectCatch,