react-doctor 0.9.9 → 0.9.10-dev.27a39de

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/{build-section-divider-rq_KQONI.js → build-section-divider-CNiJjeDe.js} +22 -103
  2. package/dist/{ci-BwCh-hUD.js → ci-CuWsakTj.js} +11 -11
  3. package/dist/cli.js +33 -30
  4. package/dist/{design-CTL1QpZB.js → design-BSKWQXBy.js} +4 -4
  5. package/dist/{exit-gracefully-C7wgCb-s.js → exit-gracefully-BclyGg_q.js} +7 -6
  6. package/dist/index.d.ts +9 -1
  7. package/dist/index.js +159 -159
  8. package/dist/{inspect-CGCfCxoM.js → inspect-BZx09uPZ.js} +16 -16
  9. package/dist/{install-CdlRGSxJ.js → install-ZWuhAWQE.js} +8 -8
  10. package/dist/{install-doctor-script-BEIl1lby.js → install-doctor-script-DmWC51TH.js} +4 -4
  11. package/dist/{install-github-workflow-Cun64Fxh.js → install-github-workflow-B_3-SNxE.js} +6 -6
  12. package/dist/{install-react-doctor-Bn-dP3rT.js → install-react-doctor-DNoeqkzq.js} +8 -8
  13. package/dist/lsp.js +2122 -327
  14. package/dist/{print-branded-header-C9de_0PE.js → print-branded-header-1rKTZeCu.js} +4 -4
  15. package/dist/{read-object-file-ClOsQUSD.js → read-object-file-qJl0_zQn.js} +4 -4
  16. package/dist/{record-metric-b5KUd2XK.js → record-metric-Cdqt5Ap3.js} +1654 -295
  17. package/dist/{report-error-DU6Bxy9R.js → report-error-CWdzTO39.js} +18 -12
  18. package/dist/{report-workflow-result-CjrL22OD.js → report-workflow-result-CPu88rum.js} +5 -5
  19. package/dist/{resolve-scope-pjSIB8ZY.js → resolve-scope-BsFLek-x.js} +6 -6
  20. package/dist/{rules-Ds2rHd-a.js → rules-Bo3Q3wQp.js} +6 -6
  21. package/dist/{run-scan-app-D8wBLuwl.js → run-scan-app-DJrqd1mR.js} +10 -10
  22. package/dist/{scan-CHh6DGmw.js → scan-Bg0iJFfk.js} +8 -8
  23. package/dist/{should-fail-scan-gate-Bif6gnI9.js → should-fail-scan-gate-BB_spOAA.js} +114 -192
  24. package/dist/{should-skip-prompts-Q3cS356Z.js → should-skip-prompts-CnukxPv0.js} +4 -4
  25. package/dist/{unref-stdin-DTLx8HEf.js → unref-stdin-Bsw57mR2.js} +112 -5
  26. package/dist/{version-BrAILQSv.js → version-CFaOM-r8.js} +4 -4
  27. package/dist/{why-DB_XpRpg.js → why-YX7k5are.js} +4 -4
  28. package/package.json +6 -6
package/dist/lsp.js CHANGED
@@ -1199,7 +1199,7 @@ const random = (self) => {
1199
1199
  * @category hashing
1200
1200
  * @since 2.0.0
1201
1201
  */
1202
- const combine = /* @__PURE__ */ dual(2, (self, b) => self * 53 ^ b);
1202
+ const combine$1 = /* @__PURE__ */ dual(2, (self, b) => self * 53 ^ b);
1203
1203
  /**
1204
1204
  * Applies bit manipulation techniques to optimize a hash value.
1205
1205
  *
@@ -1374,7 +1374,7 @@ const string$2 = (str) => {
1374
1374
  */
1375
1375
  const structureKeys = (o, keys) => {
1376
1376
  let h = 12289;
1377
- for (const key of keys) h ^= combine(hash(key), hash(o[key]));
1377
+ for (const key of keys) h ^= combine$1(hash(key), hash(o[key]));
1378
1378
  return optimize(h);
1379
1379
  };
1380
1380
  /**
@@ -1455,7 +1455,7 @@ const iterableWith = (seed, f) => (iter) => {
1455
1455
  * @since 2.0.0
1456
1456
  */
1457
1457
  const array = /* @__PURE__ */ iterableWith(6151, hash);
1458
- const hashMap = /* @__PURE__ */ iterableWith(/* @__PURE__ */ string$2("Map"), ([k, v]) => combine(hash(k), hash(v)));
1458
+ const hashMap = /* @__PURE__ */ iterableWith(/* @__PURE__ */ string$2("Map"), ([k, v]) => combine$1(hash(k), hash(v)));
1459
1459
  const hashSet = /* @__PURE__ */ iterableWith(/* @__PURE__ */ string$2("Set"), hash);
1460
1460
  const randomHashCache = /* @__PURE__ */ new WeakMap();
1461
1461
  const hashCache = /* @__PURE__ */ new WeakMap();
@@ -2681,7 +2681,7 @@ var Fail = class extends ReasonBase {
2681
2681
  return isFailReason$1(that) && equals$2(this.error, that.error) && equals$2(this.annotations, that.annotations);
2682
2682
  }
2683
2683
  [symbol$1]() {
2684
- return combine(string$2(this._tag))(combine(hash(this.error))(hash(this.annotations)));
2684
+ return combine$1(string$2(this._tag))(combine$1(hash(this.error))(hash(this.annotations)));
2685
2685
  }
2686
2686
  };
2687
2687
  /** @internal */
@@ -2710,7 +2710,7 @@ var Die = class extends ReasonBase {
2710
2710
  return isDieReason(that) && equals$2(this.defect, that.defect) && equals$2(this.annotations, that.annotations);
2711
2711
  }
2712
2712
  [symbol$1]() {
2713
- return combine(string$2(this._tag))(combine(hash(this.defect))(hash(this.annotations)));
2713
+ return combine$1(string$2(this._tag))(combine$1(hash(this.defect))(hash(this.annotations)));
2714
2714
  }
2715
2715
  };
2716
2716
  /** @internal */
@@ -2770,7 +2770,7 @@ const makeExit = (options) => {
2770
2770
  return isExit(that) && that._tag === this._tag && equals$2(this[args], that[args]);
2771
2771
  },
2772
2772
  [symbol$1]() {
2773
- return combine(string$2(options.op), hash(this[args]));
2773
+ return combine$1(string$2(options.op), hash(this[args]));
2774
2774
  }
2775
2775
  };
2776
2776
  return function(value) {
@@ -2892,9 +2892,9 @@ const done$2 = (value) => {
2892
2892
  /**
2893
2893
  * @since 2.0.0
2894
2894
  */
2895
- const TypeId$38 = "~effect/data/Option";
2895
+ const TypeId$39 = "~effect/data/Option";
2896
2896
  const CommonProto$1 = {
2897
- [TypeId$38]: { _A: (_) => _ },
2897
+ [TypeId$39]: { _A: (_) => _ },
2898
2898
  ...PipeInspectableProto,
2899
2899
  [Symbol.iterator]() {
2900
2900
  return new SingleShotGen(this);
@@ -2907,7 +2907,7 @@ const SomeProto = /* @__PURE__ */ Object.defineProperty(/* @__PURE__ */ Object.a
2907
2907
  return isOption(that) && isSome$1(that) && equals$2(this.value, that.value);
2908
2908
  },
2909
2909
  [symbol$1]() {
2910
- return combine(hash(this._tag))(hash(this.value));
2910
+ return combine$1(hash(this._tag))(hash(this.value));
2911
2911
  },
2912
2912
  toString() {
2913
2913
  return `some(${format$1(this.value)})`;
@@ -2944,7 +2944,7 @@ const NoneProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(Co
2944
2944
  }
2945
2945
  });
2946
2946
  /** @internal */
2947
- const isOption = (input) => hasProperty(input, TypeId$38);
2947
+ const isOption = (input) => hasProperty(input, TypeId$39);
2948
2948
  /** @internal */
2949
2949
  const isNone$1 = (fa) => fa._tag === "None";
2950
2950
  /** @internal */
@@ -2959,9 +2959,9 @@ const some$1 = (value) => {
2959
2959
  };
2960
2960
  //#endregion
2961
2961
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/internal/result.js
2962
- const TypeId$37 = "~effect/data/Result";
2962
+ const TypeId$38 = "~effect/data/Result";
2963
2963
  const CommonProto = {
2964
- [TypeId$37]: {
2964
+ [TypeId$38]: {
2965
2965
  /* v8 ignore next 2 */
2966
2966
  _A: (_) => _,
2967
2967
  _E: (_) => _
@@ -2978,7 +2978,7 @@ const SuccessProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create
2978
2978
  return isResult(that) && isSuccess$2(that) && equals$2(this.success, that.success);
2979
2979
  },
2980
2980
  [symbol$1]() {
2981
- return combine(hash(this._tag))(hash(this.success));
2981
+ return combine$1(hash(this._tag))(hash(this.success));
2982
2982
  },
2983
2983
  toString() {
2984
2984
  return `success(${format$1(this.success)})`;
@@ -2998,7 +2998,7 @@ const FailureProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create
2998
2998
  return isResult(that) && isFailure$2(that) && equals$2(this.failure, that.failure);
2999
2999
  },
3000
3000
  [symbol$1]() {
3001
- return combine(hash(this._tag))(hash(this.failure));
3001
+ return combine$1(hash(this._tag))(hash(this.failure));
3002
3002
  },
3003
3003
  toString() {
3004
3004
  return `failure(${format$1(this.failure)})`;
@@ -3012,7 +3012,7 @@ const FailureProto = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create
3012
3012
  }
3013
3013
  });
3014
3014
  /** @internal */
3015
- const isResult = (input) => hasProperty(input, TypeId$37);
3015
+ const isResult = (input) => hasProperty(input, TypeId$38);
3016
3016
  /** @internal */
3017
3017
  const isFailure$2 = (result) => result._tag === "Failure";
3018
3018
  /** @internal */
@@ -4247,6 +4247,37 @@ const isArrayNonEmpty = isArrayNonEmpty$1;
4247
4247
  * @since 4.0.0
4248
4248
  */
4249
4249
  const isReadonlyArrayNonEmpty = isArrayNonEmpty$1;
4250
+ /**
4251
+ * Sorts an array by the given `Order`, returning a new array.
4252
+ *
4253
+ * **When to use**
4254
+ *
4255
+ * Use to sort an array using a single `Order` comparator.
4256
+ *
4257
+ * **Details**
4258
+ *
4259
+ * Preserves `NonEmptyArray` in the return type. Use `sortWith` to sort by a
4260
+ * derived key, or `sortBy` for multi-key sorting.
4261
+ *
4262
+ * **Example** (Sorting numbers)
4263
+ *
4264
+ * ```ts
4265
+ * import { Array, Order } from "effect"
4266
+ *
4267
+ * console.log(Array.sort([3, 1, 4, 1, 5], Order.Number)) // [1, 1, 3, 4, 5]
4268
+ * ```
4269
+ *
4270
+ * @see {@link sortWith} — sort by a mapping function
4271
+ * @see {@link sortBy} — sort by multiple orders
4272
+ *
4273
+ * @category sorting
4274
+ * @since 2.0.0
4275
+ */
4276
+ const sort$1 = /* @__PURE__ */ dual(2, (self, O) => {
4277
+ const out = Array$1.from(self);
4278
+ out.sort(O);
4279
+ return out;
4280
+ });
4250
4281
  const hashBucketsAdd = (buckets, value) => {
4251
4282
  const hash$5 = hash(value);
4252
4283
  const bucket = buckets.get(hash$5);
@@ -4412,12 +4443,12 @@ const dedupe = (self) => {
4412
4443
  *
4413
4444
  * @since 2.0.0
4414
4445
  */
4415
- const TypeId$36 = "~effect/BigDecimal";
4446
+ const TypeId$37 = "~effect/BigDecimal";
4416
4447
  const BigDecimalProto = {
4417
- [TypeId$36]: TypeId$36,
4448
+ [TypeId$37]: TypeId$37,
4418
4449
  [symbol$1]() {
4419
4450
  const normalized = normalize(this);
4420
- return combine(hash(normalized.value), number$1(normalized.scale));
4451
+ return combine$1(hash(normalized.value), number$1(normalized.scale));
4421
4452
  },
4422
4453
  [symbol](that) {
4423
4454
  return isBigDecimal(that) && equals$1(this, that);
@@ -4460,7 +4491,7 @@ const BigDecimalProto = {
4460
4491
  * @category guards
4461
4492
  * @since 2.0.0
4462
4493
  */
4463
- const isBigDecimal = (u) => hasProperty(u, TypeId$36);
4494
+ const isBigDecimal = (u) => hasProperty(u, TypeId$37);
4464
4495
  /**
4465
4496
  * Creates a `BigDecimal` from a `bigint` value and a scale.
4466
4497
  *
@@ -4500,14 +4531,14 @@ const make$36 = (value, scale) => {
4500
4531
  * @internal
4501
4532
  */
4502
4533
  const makeNormalizedUnsafe = (value, scale) => {
4503
- if (value !== bigint0$2 && value % bigint10 === bigint0$2) throw new RangeError("Value must be normalized");
4534
+ if (value !== bigint0$3 && value % bigint10 === bigint0$3) throw new RangeError("Value must be normalized");
4504
4535
  const o = make$36(value, scale);
4505
4536
  o.normalized = o;
4506
4537
  return o;
4507
4538
  };
4508
- const bigint0$2 = /* @__PURE__ */ BigInt(0);
4539
+ const bigint0$3 = /* @__PURE__ */ BigInt(0);
4509
4540
  const bigint10 = /* @__PURE__ */ BigInt(10);
4510
- const zero$1 = /* @__PURE__ */ makeNormalizedUnsafe(bigint0$2, 0);
4541
+ const zero$1 = /* @__PURE__ */ makeNormalizedUnsafe(bigint0$3, 0);
4511
4542
  /**
4512
4543
  * Normalizes a given `BigDecimal` by removing trailing zeros.
4513
4544
  *
@@ -4538,7 +4569,7 @@ const zero$1 = /* @__PURE__ */ makeNormalizedUnsafe(bigint0$2, 0);
4538
4569
  * @since 2.0.0
4539
4570
  */
4540
4571
  const normalize = (self) => {
4541
- if (self.normalized === void 0) if (self.value === bigint0$2) self.normalized = zero$1;
4572
+ if (self.normalized === void 0) if (self.value === bigint0$3) self.normalized = zero$1;
4542
4573
  else {
4543
4574
  const digits = `${self.value}`;
4544
4575
  let trail = 0;
@@ -4609,7 +4640,7 @@ const scale = /* @__PURE__ */ dual(2, (self, scale) => {
4609
4640
  * @category math
4610
4641
  * @since 2.0.0
4611
4642
  */
4612
- const abs = (n) => n.value < bigint0$2 ? make$36(-n.value, n.scale) : n;
4643
+ const abs = (n) => n.value < bigint0$3 ? make$36(-n.value, n.scale) : n;
4613
4644
  /**
4614
4645
  * Provides an `Equivalence` instance for `BigDecimal` that determines equality between BigDecimal values.
4615
4646
  *
@@ -4697,7 +4728,7 @@ const equals$1 = /* @__PURE__ */ dual(2, (self, that) => Equivalence$5(self, tha
4697
4728
  const format = (n) => {
4698
4729
  const normalized = normalize(n);
4699
4730
  if (Math.abs(normalized.scale) >= 16) return toExponential(normalized);
4700
- const negative = normalized.value < bigint0$2;
4731
+ const negative = normalized.value < bigint0$3;
4701
4732
  const absolute = negative ? `${normalized.value}`.substring(1) : `${normalized.value}`;
4702
4733
  let before;
4703
4734
  let after;
@@ -4770,7 +4801,7 @@ const toExponential = (n) => {
4770
4801
  * @category predicates
4771
4802
  * @since 2.0.0
4772
4803
  */
4773
- const isZero$1 = (n) => n.value === bigint0$2;
4804
+ const isZero$1 = (n) => n.value === bigint0$3;
4774
4805
  /**
4775
4806
  * Checks whether a given `BigDecimal` is negative.
4776
4807
  *
@@ -4792,7 +4823,7 @@ const isZero$1 = (n) => n.value === bigint0$2;
4792
4823
  * @category predicates
4793
4824
  * @since 2.0.0
4794
4825
  */
4795
- const isNegative = (n) => n.value < bigint0$2;
4826
+ const isNegative = (n) => n.value < bigint0$3;
4796
4827
  //#endregion
4797
4828
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/Effectable.js
4798
4829
  /**
@@ -4961,7 +4992,7 @@ const ServiceProto = {
4961
4992
  }
4962
4993
  };
4963
4994
  const ReferenceTypeId = "~effect/Context/Reference";
4964
- const TypeId$35 = "~effect/Context";
4995
+ const TypeId$36 = "~effect/Context";
4965
4996
  /**
4966
4997
  * Creates a `Context` from an existing service map.
4967
4998
  *
@@ -5000,7 +5031,7 @@ const makeUnsafe$7 = (mapUnsafe) => {
5000
5031
  };
5001
5032
  const Proto$13 = {
5002
5033
  ...PipeInspectableProto,
5003
- [TypeId$35]: { _Services: (_) => _ },
5034
+ [TypeId$36]: { _Services: (_) => _ },
5004
5035
  toJSON() {
5005
5036
  return {
5006
5037
  _id: "Context",
@@ -5052,7 +5083,7 @@ const Proto$13 = {
5052
5083
  * @category guards
5053
5084
  * @since 2.0.0
5054
5085
  */
5055
- const isContext = (u) => hasProperty(u, TypeId$35);
5086
+ const isContext = (u) => hasProperty(u, TypeId$36);
5056
5087
  /**
5057
5088
  * Checks whether the provided argument is a `Reference`.
5058
5089
  *
@@ -5388,7 +5419,7 @@ const getOption = /* @__PURE__ */ dual(2, (self, service) => {
5388
5419
  * @category combining
5389
5420
  * @since 2.0.0
5390
5421
  */
5391
- const merge$3 = /* @__PURE__ */ dual(2, (self, that) => {
5422
+ const merge$4 = /* @__PURE__ */ dual(2, (self, that) => {
5392
5423
  if (self.mapUnsafe.size === 0) return that;
5393
5424
  if (that.mapUnsafe.size === 0) return self;
5394
5425
  return withMapUnsafe(self, (map) => {
@@ -5498,8 +5529,8 @@ const withMapUnsafe = (self, f) => {
5498
5529
  const Reference = Service;
5499
5530
  //#endregion
5500
5531
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/Duration.js
5501
- const TypeId$34 = "~effect/time/Duration";
5502
- const bigint0$1 = /* @__PURE__ */ BigInt(0);
5532
+ const TypeId$35 = "~effect/time/Duration";
5533
+ const bigint0$2 = /* @__PURE__ */ BigInt(0);
5503
5534
  const bigint1 = /* @__PURE__ */ BigInt(1);
5504
5535
  const bigint1e3 = /* @__PURE__ */ BigInt(1e3);
5505
5536
  const roundTiesAwayFromZero = (input) => BigInt(input < 0 ? Math.ceil(input - .5) : Math.floor(input + .5));
@@ -5563,7 +5594,7 @@ const fromInputUnsafe = (input) => {
5563
5594
  }
5564
5595
  case "object": {
5565
5596
  if (input === null) break;
5566
- if (TypeId$34 in input) return input;
5597
+ if (TypeId$35 in input) return input;
5567
5598
  if (Array.isArray(input)) {
5568
5599
  if (input.length !== 2 || !input.every(isNumber)) return invalid(input);
5569
5600
  if (Number.isNaN(input[0]) || Number.isNaN(input[1])) return zero;
@@ -5595,7 +5626,7 @@ const zeroDurationValue = {
5595
5626
  const infinityDurationValue = { _tag: "Infinity" };
5596
5627
  const negativeInfinityDurationValue = { _tag: "NegativeInfinity" };
5597
5628
  const DurationProto = {
5598
- [TypeId$34]: TypeId$34,
5629
+ [TypeId$35]: TypeId$35,
5599
5630
  [symbol$1]() {
5600
5631
  return structure(this.value);
5601
5632
  },
@@ -5651,7 +5682,7 @@ const make$34 = (input) => {
5651
5682
  _tag: "Millis",
5652
5683
  millis: input
5653
5684
  };
5654
- else if (input === bigint0$1) duration.value = zeroDurationValue;
5685
+ else if (input === bigint0$2) duration.value = zeroDurationValue;
5655
5686
  else duration.value = {
5656
5687
  _tag: "Nanos",
5657
5688
  nanos: input
@@ -5673,7 +5704,7 @@ const make$34 = (input) => {
5673
5704
  * @category guards
5674
5705
  * @since 2.0.0
5675
5706
  */
5676
- const isDuration = (u) => hasProperty(u, TypeId$34);
5707
+ const isDuration = (u) => hasProperty(u, TypeId$35);
5677
5708
  /**
5678
5709
  * Checks whether a Duration is finite (not infinite).
5679
5710
  *
@@ -5708,7 +5739,7 @@ const isFinite$1 = (self) => self.value._tag !== "Infinity" && self.value._tag !
5708
5739
  const isZero = (self) => {
5709
5740
  switch (self.value._tag) {
5710
5741
  case "Millis": return self.value.millis === 0;
5711
- case "Nanos": return self.value.nanos === bigint0$1;
5742
+ case "Nanos": return self.value.nanos === bigint0$2;
5712
5743
  case "Infinity":
5713
5744
  case "NegativeInfinity": return false;
5714
5745
  }
@@ -6741,7 +6772,7 @@ var Interrupt = class extends ReasonBase {
6741
6772
  return isInterruptReason(that) && this.fiberId === that.fiberId && this.annotations === that.annotations;
6742
6773
  }
6743
6774
  [symbol$1]() {
6744
- return combine(string$2(`${this._tag}:${this.fiberId}`))(random(this.annotations));
6775
+ return combine$1(string$2(`${this._tag}:${this.fiberId}`))(random(this.annotations));
6745
6776
  }
6746
6777
  };
6747
6778
  /** @internal */
@@ -7171,7 +7202,7 @@ const fiberInterrupt = (self) => withFiber$1((fiber) => fiberInterruptAs(self, f
7171
7202
  /** @internal */
7172
7203
  const fiberInterruptAs = /* @__PURE__ */ dual((args) => hasProperty(args[0], FiberTypeId), (self, fiberId, annotations) => withFiber$1((parent) => {
7173
7204
  let ann = fiberStackAnnotations(parent);
7174
- ann = ann && annotations ? merge$3(ann, annotations) : ann ?? annotations;
7205
+ ann = ann && annotations ? merge$4(ann, annotations) : ann ?? annotations;
7175
7206
  self.interruptUnsafe(fiberId, ann);
7176
7207
  return asVoid$1(fiberAwait(self));
7177
7208
  }));
@@ -7531,6 +7562,8 @@ const exitAsVoidAll = (exits) => {
7531
7562
  return failures.length === 0 ? exitVoid : exitFailCause(causeFromReasons(failures));
7532
7563
  };
7533
7564
  /** @internal */
7565
+ const service$1 = (service) => service;
7566
+ /** @internal */
7534
7567
  const serviceOption$1 = (service) => withFiber$1((fiber) => succeed$5(getOption(fiber.context, service)));
7535
7568
  /** @internal */
7536
7569
  const updateContext$1 = /* @__PURE__ */ dual(2, (self, f) => withFiber$1((fiber) => {
@@ -7550,14 +7583,14 @@ const updateService = /* @__PURE__ */ dual(3, (self, service, f) => updateContex
7550
7583
  return add(s, service, next);
7551
7584
  }));
7552
7585
  /** @internal */
7553
- const context$1 = () => getContext;
7586
+ const context$2 = () => getContext;
7554
7587
  const getContext = /* @__PURE__ */ withFiber$1((fiber) => succeed$5(fiber.context));
7555
7588
  /** @internal */
7556
7589
  const contextWith$1 = (f) => withFiber$1((fiber) => f(fiber.context));
7557
7590
  /** @internal */
7558
7591
  const provideContext$1 = /* @__PURE__ */ dual(2, (self, context) => {
7559
7592
  if (effectIsExit(self)) return self;
7560
- return updateContext$1(self, merge$3(context));
7593
+ return updateContext$1(self, merge$4(context));
7561
7594
  });
7562
7595
  /** @internal */
7563
7596
  const provideService$3 = function() {
@@ -8285,7 +8318,7 @@ const makeLatchUnsafe = (open) => new Latch(open ?? false);
8285
8318
  const makeLatch = (open) => sync$2(() => makeLatchUnsafe(open));
8286
8319
  /** @internal */
8287
8320
  const withTracerEnabled$1 = /* @__PURE__ */ provideService$3(TracerEnabled);
8288
- const bigint0 = /* @__PURE__ */ BigInt(0);
8321
+ const bigint0$1 = /* @__PURE__ */ BigInt(0);
8289
8322
  const NoopSpanProto = {
8290
8323
  _tag: "Span",
8291
8324
  spanId: "noop",
@@ -8293,8 +8326,8 @@ const NoopSpanProto = {
8293
8326
  sampled: false,
8294
8327
  status: {
8295
8328
  _tag: "Ended",
8296
- startTime: bigint0,
8297
- endTime: bigint0,
8329
+ startTime: bigint0$1,
8330
+ endTime: bigint0$1,
8298
8331
  exit: exitVoid
8299
8332
  },
8300
8333
  attributes: /* @__PURE__ */ new Map(),
@@ -8727,6 +8760,38 @@ const hasInterruptsOnly = hasInterruptsOnly$1;
8727
8760
  */
8728
8761
  const map$4 = causeMap;
8729
8762
  /**
8763
+ * Merges two causes into a single cause whose `reasons` array is the union
8764
+ * of both inputs (de-duplicated by value equality).
8765
+ *
8766
+ * **When to use**
8767
+ *
8768
+ * Use to merge independent causes into one structured failure value.
8769
+ *
8770
+ * **Details**
8771
+ *
8772
+ * - Combining with `empty` returns the other cause unchanged.
8773
+ * - If the result is structurally equal to `self`, `self` is returned
8774
+ * (referential shortcut).
8775
+ *
8776
+ * **Example** (Combining two causes)
8777
+ *
8778
+ * ```ts
8779
+ * import { Cause } from "effect"
8780
+ *
8781
+ * const cause1 = Cause.fail("error1")
8782
+ * const cause2 = Cause.fail("error2")
8783
+ * const combined = Cause.combine(cause1, cause2)
8784
+ * console.log(combined.reasons.length) // 2
8785
+ * ```
8786
+ *
8787
+ * @see {@link fromReasons} — build a cause from an array of reasons
8788
+ * @see {@link empty} for the identity cause used when combining
8789
+ *
8790
+ * @category combining
8791
+ * @since 4.0.0
8792
+ */
8793
+ const combine = causeCombine;
8794
+ /**
8730
8795
  * Collapses a `Cause` into a single `unknown` value, picking the "most
8731
8796
  * important" failure in this order:
8732
8797
  *
@@ -9898,14 +9963,14 @@ const forkUnsafe = scopeForkUnsafe;
9898
9963
  const close = scopeClose;
9899
9964
  //#endregion
9900
9965
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/Layer.js
9901
- const TypeId$32 = "~effect/Layer";
9966
+ const TypeId$33 = "~effect/Layer";
9902
9967
  const MemoMapTypeId = "~effect/Layer/MemoMap";
9903
9968
  const memoMapReuse = (entry, scope) => {
9904
9969
  entry.observers++;
9905
9970
  return andThen$1(scopeAddFinalizerExit(scope, (exit) => entry.finalizer(exit)), entry.effect);
9906
9971
  };
9907
9972
  const LayerProto = {
9908
- [TypeId$32]: {
9973
+ [TypeId$33]: {
9909
9974
  _ROut: identity,
9910
9975
  _E: identity,
9911
9976
  _RIn: identity
@@ -10561,6 +10626,50 @@ const mergeAllEffect = (layers, memoMap, scope) => {
10561
10626
  * @since 2.0.0
10562
10627
  */
10563
10628
  const mergeAll$1 = (...layers) => fromBuild((memoMap, scope) => mergeAllEffect(layers, memoMap, scope));
10629
+ /**
10630
+ * Merges this layer with another layer concurrently, producing a new layer with
10631
+ * combined input, error, and output types.
10632
+ *
10633
+ * **When to use**
10634
+ *
10635
+ * Use to combine an existing `Layer` with another `Layer` or an array of
10636
+ * layers while preserving pipeline style.
10637
+ *
10638
+ * **Details**
10639
+ *
10640
+ * This is a binary version of `mergeAll` that merges exactly two layers or one
10641
+ * layer with an array of layers. The layers are built concurrently and their
10642
+ * outputs are combined.
10643
+ *
10644
+ * **Example** (Merging two layers)
10645
+ *
10646
+ * ```ts
10647
+ * import { Context, Effect, Layer } from "effect"
10648
+ *
10649
+ * class Database extends Context.Service<Database, {
10650
+ * readonly query: (sql: string) => Effect.Effect<string>
10651
+ * }>()("Database") {}
10652
+ *
10653
+ * class Logger extends Context.Service<Logger, {
10654
+ * readonly log: (msg: string) => Effect.Effect<void>
10655
+ * }>()("Logger") {}
10656
+ *
10657
+ * const dbLayer = Layer.succeed(Database, {
10658
+ * query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
10659
+ * })
10660
+ * const loggerLayer = Layer.succeed(Logger, {
10661
+ * log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
10662
+ * })
10663
+ *
10664
+ * const mergedLayer = Layer.merge(dbLayer, loggerLayer)
10665
+ * ```
10666
+ *
10667
+ * @see {@link mergeAll} for merging several layers at once
10668
+ *
10669
+ * @category zipping
10670
+ * @since 2.0.0
10671
+ */
10672
+ const merge$3 = /* @__PURE__ */ dual(2, (self, that) => mergeAll$1(self, ...Array.isArray(that) ? that : [that]));
10564
10673
  const provideWith = (self, that, f) => fromBuild((memoMap, scope) => flatMap$4(Array.isArray(that) ? mergeAllEffect(that, memoMap, scope) : that.build(memoMap, scope), (context) => self.build(memoMap, scope).pipe(provideContext$1(context), map$5((merged) => f(merged, context)))));
10565
10674
  /**
10566
10675
  * Feeds the output services of the dependency layer into the requirements of
@@ -10723,7 +10832,7 @@ const provide$2 = /* @__PURE__ */ dual(2, (self, that) => provideWith(self, that
10723
10832
  * @category providing services
10724
10833
  * @since 2.0.0
10725
10834
  */
10726
- const provideMerge = /* @__PURE__ */ dual(2, (self, that) => provideWith(self, that, (self, that) => merge$3(that, self)));
10835
+ const provideMerge = /* @__PURE__ */ dual(2, (self, that) => provideWith(self, that, (self, that) => merge$4(that, self)));
10727
10836
  /**
10728
10837
  * Constructs a layer dynamically based on the output of this layer.
10729
10838
  *
@@ -10828,11 +10937,11 @@ const Clock = ClockRef;
10828
10937
  //#endregion
10829
10938
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/internal/dateTime.js
10830
10939
  /** @internal */
10831
- const TypeId$31 = "~effect/time/DateTime";
10940
+ const TypeId$32 = "~effect/time/DateTime";
10832
10941
  /** @internal */
10833
10942
  const TimeZoneTypeId = "~effect/time/DateTime/TimeZone";
10834
10943
  const Proto$12 = {
10835
- [TypeId$31]: TypeId$31,
10944
+ [TypeId$32]: TypeId$32,
10836
10945
  pipe() {
10837
10946
  return pipeArguments(this, arguments);
10838
10947
  },
@@ -11077,7 +11186,7 @@ const matchEffect$1 = /* @__PURE__ */ dual(2, (self, options) => matchCauseEffec
11077
11186
  }));
11078
11187
  //#endregion
11079
11188
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/Schedule.js
11080
- const TypeId$30 = "~effect/Schedule";
11189
+ const TypeId$31 = "~effect/Schedule";
11081
11190
  /**
11082
11191
  * Context reference containing metadata for the currently running schedule step.
11083
11192
  *
@@ -11102,7 +11211,7 @@ const CurrentMetadata = /* @__PURE__ */ Reference("effect/Schedule/CurrentMetada
11102
11211
  elapsedSincePrevious: 0
11103
11212
  }) });
11104
11213
  const ScheduleProto = {
11105
- [TypeId$30]: {
11214
+ [TypeId$31]: {
11106
11215
  _Out: identity,
11107
11216
  _In: identity,
11108
11217
  _Env: identity
@@ -11131,7 +11240,7 @@ const ScheduleProto = {
11131
11240
  * @category guards
11132
11241
  * @since 2.0.0
11133
11242
  */
11134
- const isSchedule = (u) => hasProperty(u, TypeId$30);
11243
+ const isSchedule = (u) => hasProperty(u, TypeId$31);
11135
11244
  /**
11136
11245
  * Creates a Schedule from a step function that returns a Pull.
11137
11246
  *
@@ -11596,6 +11705,54 @@ const buildFromOptions = (options) => {
11596
11705
  return schedule;
11597
11706
  };
11598
11707
  /**
11708
+ * Context reference for metric attributes applied from the current Effect
11709
+ * context.
11710
+ *
11711
+ * **When to use**
11712
+ *
11713
+ * Use to provide default attributes that should be merged into metric updates
11714
+ * and reads in a scoped part of a program.
11715
+ *
11716
+ * **Details**
11717
+ *
11718
+ * The default value is an empty attribute set. Metric reads and updates merge
11719
+ * these contextual attributes with the metric's own attributes to select the
11720
+ * metric series being accessed.
11721
+ *
11722
+ * **Example** (Providing current metric attributes)
11723
+ *
11724
+ * ```ts
11725
+ * import { Data, Effect, Metric } from "effect"
11726
+ *
11727
+ * class AttributesError extends Data.TaggedError("AttributesError")<{
11728
+ * readonly operation: string
11729
+ * }> {}
11730
+ *
11731
+ * const program = Effect.gen(function*() {
11732
+ * // Access current metric attributes
11733
+ * const attributes = yield* Metric.CurrentMetricAttributes
11734
+ * console.log("Current attributes:", attributes)
11735
+ *
11736
+ * // Set new attributes context
11737
+ * const newAttributes = { service: "api", version: "1.0" }
11738
+ * const result = yield* Effect.provideService(
11739
+ * Effect.gen(function*() {
11740
+ * const updatedAttributes = yield* Metric.CurrentMetricAttributes
11741
+ * return updatedAttributes
11742
+ * }),
11743
+ * Metric.CurrentMetricAttributes,
11744
+ * newAttributes
11745
+ * )
11746
+ *
11747
+ * return result
11748
+ * })
11749
+ * ```
11750
+ *
11751
+ * @category references
11752
+ * @since 4.0.0
11753
+ */
11754
+ const CurrentMetricAttributes = /* @__PURE__ */ Reference("effect/Metric/CurrentMetricAttributes", { defaultValue: () => ({}) });
11755
+ /**
11599
11756
  * Context reference for the metric registry in the current context.
11600
11757
  *
11601
11758
  * **When to use**
@@ -11622,6 +11779,348 @@ const buildFromOptions = (options) => {
11622
11779
  * @since 4.0.0
11623
11780
  */
11624
11781
  const MetricRegistry = /* @__PURE__ */ Reference("~effect/observability/Metric/MetricRegistryKey", { defaultValue: () => /* @__PURE__ */ new Map() });
11782
+ const TypeId$30 = "~effect/observability/Metric";
11783
+ var Metric$ = class {
11784
+ [TypeId$30] = TypeId$30;
11785
+ #metadataCache = /* @__PURE__ */ new WeakMap();
11786
+ #metadata;
11787
+ id;
11788
+ description;
11789
+ attributes;
11790
+ constructor(id, description, attributes) {
11791
+ this.id = id;
11792
+ this.description = description;
11793
+ this.attributes = attributes;
11794
+ }
11795
+ valueUnsafe(context) {
11796
+ return this.hook(context).get(context);
11797
+ }
11798
+ modifyUnsafe(input, context) {
11799
+ return this.hook(context).modify(input, context);
11800
+ }
11801
+ updateUnsafe(input, context) {
11802
+ return this.hook(context).update(input, context);
11803
+ }
11804
+ hook(context) {
11805
+ const extraAttributes = get$4(context, CurrentMetricAttributes);
11806
+ if (Object.keys(extraAttributes).length === 0) {
11807
+ if (isNotUndefined(this.#metadata)) return this.#metadata.hooks;
11808
+ this.#metadata = this.getOrCreate(context, this.attributes);
11809
+ return this.#metadata.hooks;
11810
+ }
11811
+ const mergedAttributes = mergeAttributes(this.attributes, extraAttributes);
11812
+ let metadata = this.#metadataCache.get(mergedAttributes);
11813
+ if (isNotUndefined(metadata)) return metadata.hooks;
11814
+ metadata = this.getOrCreate(context, mergedAttributes);
11815
+ this.#metadataCache.set(mergedAttributes, metadata);
11816
+ return metadata.hooks;
11817
+ }
11818
+ getOrCreate(context, attributes) {
11819
+ const key = makeKey(this, attributes);
11820
+ const registry = get$4(context, MetricRegistry);
11821
+ if (registry.has(key)) return registry.get(key);
11822
+ const hooks = this.createHooks();
11823
+ const meta = {
11824
+ id: this.id,
11825
+ type: this.type,
11826
+ description: this.description,
11827
+ attributes: attributesToRecord(attributes),
11828
+ hooks
11829
+ };
11830
+ registry.set(key, meta);
11831
+ return meta;
11832
+ }
11833
+ pipe() {
11834
+ return pipeArguments(this, arguments);
11835
+ }
11836
+ };
11837
+ const bigint0 = /* @__PURE__ */ BigInt(0);
11838
+ var CounterMetric = class extends Metric$ {
11839
+ type = "Counter";
11840
+ #bigint;
11841
+ #incremental;
11842
+ constructor(id, options) {
11843
+ super(id, options?.description, attributesToRecord(options?.attributes));
11844
+ this.#bigint = options?.bigint ?? false;
11845
+ this.#incremental = options?.incremental ?? false;
11846
+ }
11847
+ createHooks() {
11848
+ let count = this.#bigint ? bigint0 : 0;
11849
+ const canUpdate = this.#incremental ? this.#bigint ? (value) => value >= bigint0 : (value) => value >= 0 : (_value) => true;
11850
+ const update = (value) => {
11851
+ if (canUpdate(value)) count = count + value;
11852
+ };
11853
+ return makeHooks(() => ({
11854
+ count,
11855
+ incremental: this.#incremental
11856
+ }), update);
11857
+ }
11858
+ };
11859
+ var HistogramMetric = class extends Metric$ {
11860
+ type = "Histogram";
11861
+ #boundaries;
11862
+ constructor(id, options) {
11863
+ super(id, options?.description, attributesToRecord(options?.attributes));
11864
+ this.#boundaries = options.boundaries;
11865
+ }
11866
+ createHooks() {
11867
+ const bounds = this.#boundaries;
11868
+ const size = bounds.length;
11869
+ const values = new Uint32Array(size + 1);
11870
+ const boundaries = new Float64Array(size);
11871
+ let count = 0;
11872
+ let sum = 0;
11873
+ let min = Number.MAX_VALUE;
11874
+ let max = -Number.MAX_VALUE;
11875
+ map$6(sort$1(bounds, Number$5), (n, i) => {
11876
+ boundaries[i] = n;
11877
+ });
11878
+ const update = (value) => {
11879
+ let from = 0;
11880
+ let to = size;
11881
+ while (from !== to) {
11882
+ const mid = Math.floor(from + (to - from) / 2);
11883
+ if (value <= boundaries[mid]) to = mid;
11884
+ else from = mid;
11885
+ if (to === from + 1) if (value <= boundaries[from]) to = from;
11886
+ else from = to;
11887
+ }
11888
+ values[from] = values[from] + 1;
11889
+ count = count + 1;
11890
+ sum = sum + value;
11891
+ if (value < min) min = value;
11892
+ if (value > max) max = value;
11893
+ };
11894
+ const getBuckets = () => {
11895
+ const builder = allocate(size);
11896
+ let cumulated = 0;
11897
+ for (let i = 0; i < size; i++) {
11898
+ const boundary = boundaries[i];
11899
+ const value = values[i];
11900
+ cumulated = cumulated + value;
11901
+ builder[i] = [boundary, cumulated];
11902
+ }
11903
+ return builder;
11904
+ };
11905
+ return makeHooks(() => ({
11906
+ buckets: getBuckets(),
11907
+ count,
11908
+ min,
11909
+ max,
11910
+ sum
11911
+ }), update);
11912
+ }
11913
+ };
11914
+ var MetricTransform = class extends Metric$ {
11915
+ type;
11916
+ metric;
11917
+ valueUnsafe;
11918
+ updateUnsafe;
11919
+ modifyUnsafe;
11920
+ constructor(metric, valueUnsafe, updateUnsafe, modifyUnsafe) {
11921
+ super(metric.id, metric.description, metric.attributes);
11922
+ this.metric = metric;
11923
+ this.valueUnsafe = valueUnsafe;
11924
+ this.updateUnsafe = updateUnsafe;
11925
+ this.modifyUnsafe = modifyUnsafe;
11926
+ this.type = metric.type;
11927
+ }
11928
+ createHooks() {
11929
+ return this.metric.createHooks();
11930
+ }
11931
+ };
11932
+ /**
11933
+ * Represents a Counter metric that tracks cumulative numerical values over
11934
+ * time. Counters can be incremented and decremented and provide a running total
11935
+ * of changes.
11936
+ *
11937
+ * **Details**
11938
+ *
11939
+ * The optional `description` describes the counter, and `attributes` attach
11940
+ * dimensions to it. Set `bigint` to create a counter that accepts `bigint`
11941
+ * inputs. Set `incremental` to `true` to create a counter that can only ever be
11942
+ * incremented.
11943
+ *
11944
+ * **Example** (Creating counter metrics)
11945
+ *
11946
+ * ```ts
11947
+ * import { Data, Effect, Metric } from "effect"
11948
+ *
11949
+ * class CounterError extends Data.TaggedError("CounterError")<{
11950
+ * readonly operation: string
11951
+ * }> {}
11952
+ *
11953
+ * const program = Effect.gen(function*() {
11954
+ * // Create a basic counter for tracking requests
11955
+ * const requestCounter = Metric.counter("http_requests_total", {
11956
+ * description: "Total number of HTTP requests processed"
11957
+ * })
11958
+ *
11959
+ * // Create an incremental-only counter for events
11960
+ * const eventCounter = Metric.counter("events_processed", {
11961
+ * description: "Events processed (increment only)",
11962
+ * incremental: true
11963
+ * })
11964
+ *
11965
+ * // Create a bigint counter for large values
11966
+ * const bytesCounter = Metric.counter("bytes_transferred", {
11967
+ * description: "Total bytes transferred",
11968
+ * bigint: true,
11969
+ * attributes: { service: "file-transfer" }
11970
+ * })
11971
+ *
11972
+ * // Update counters with values
11973
+ * yield* Metric.update(requestCounter, 1) // Increment by 1
11974
+ * yield* Metric.update(requestCounter, 5) // Increment by 5 (total: 6)
11975
+ * yield* Metric.update(eventCounter, 1) // Increment by 1
11976
+ * yield* Metric.update(bytesCounter, 1024n) // Add 1024 bytes
11977
+ *
11978
+ * // Get current counter values
11979
+ * const requestValue = yield* Metric.value(requestCounter)
11980
+ * const eventValue = yield* Metric.value(eventCounter)
11981
+ * const bytesValue = yield* Metric.value(bytesCounter)
11982
+ *
11983
+ * return { requestValue, eventValue, bytesValue }
11984
+ * })
11985
+ * ```
11986
+ *
11987
+ * @category constructors
11988
+ * @since 2.0.0
11989
+ */
11990
+ const counter = (name, options) => new CounterMetric(name, options);
11991
+ /**
11992
+ * Represents a `Histogram` metric that records observations into buckets.
11993
+ *
11994
+ * **When to use**
11995
+ *
11996
+ * Use when you need a metric for measuring the distribution of values within a
11997
+ * range.
11998
+ *
11999
+ * **Details**
12000
+ *
12001
+ * The optional `description` describes the histogram, and `attributes` attach
12002
+ * dimensions to it. The required `boundaries` option defines the histogram
12003
+ * bucket boundaries.
12004
+ *
12005
+ * **Example** (Creating histogram metrics)
12006
+ *
12007
+ * ```ts
12008
+ * import { Data, Effect, Metric } from "effect"
12009
+ *
12010
+ * class HistogramError extends Data.TaggedError("HistogramError")<{
12011
+ * readonly operation: string
12012
+ * }> {}
12013
+ *
12014
+ * const program = Effect.gen(function*() {
12015
+ * // Create a histogram for API response times
12016
+ * const responseTimeHistogram = Metric.histogram("api_response_time", {
12017
+ * description: "Distribution of API response times in milliseconds",
12018
+ * boundaries: Metric.linearBoundaries({ start: 0, width: 50, count: 10 })
12019
+ * // Creates buckets: 0-50ms, 50-100ms, 100-150ms, ..., 400-450ms, 450ms+
12020
+ * })
12021
+ *
12022
+ * // Create a histogram for request payload sizes
12023
+ * const payloadSizeHistogram = Metric.histogram("payload_size", {
12024
+ * description: "Distribution of request payload sizes in KB",
12025
+ * boundaries: Metric.exponentialBoundaries({ start: 1, factor: 2, count: 8 }),
12026
+ * // Creates exponential buckets: 1KB, 2KB, 4KB, 8KB, 16KB, 32KB, 64KB, 128KB+
12027
+ * attributes: { service: "api-gateway" }
12028
+ * })
12029
+ *
12030
+ * // Create a histogram with custom boundaries
12031
+ * const customHistogram = Metric.histogram("custom_metric", {
12032
+ * description: "Custom distribution metric",
12033
+ * boundaries: [0.1, 0.5, 1, 2.5, 5, 10, 25, 50, 100]
12034
+ * })
12035
+ *
12036
+ * // Record various response times
12037
+ * yield* Metric.update(responseTimeHistogram, 25) // Goes in 0-50ms bucket
12038
+ * yield* Metric.update(responseTimeHistogram, 75) // Goes in 50-100ms bucket
12039
+ * yield* Metric.update(responseTimeHistogram, 125) // Goes in 100-150ms bucket
12040
+ * yield* Metric.update(responseTimeHistogram, 200) // Goes in 150-200ms bucket
12041
+ * yield* Metric.update(responseTimeHistogram, 75) // Another 50-100ms
12042
+ *
12043
+ * // Record payload sizes
12044
+ * yield* Metric.update(payloadSizeHistogram, 3) // Goes in 2-4KB bucket
12045
+ * yield* Metric.update(payloadSizeHistogram, 15) // Goes in 8-16KB bucket
12046
+ * yield* Metric.update(payloadSizeHistogram, 0.5) // Goes in 0-1KB bucket
12047
+ *
12048
+ * // Get histogram state with distribution data
12049
+ * const responseTimeState = yield* Metric.value(responseTimeHistogram)
12050
+ * const payloadSizeState = yield* Metric.value(payloadSizeHistogram)
12051
+ *
12052
+ * // responseTimeState will contain:
12053
+ * // - buckets: [[50, 1], [100, 3], [150, 4], [200, 5], ...]
12054
+ * // - count: 5, min: 25, max: 200, sum: 500
12055
+ * // - Useful for calculating percentiles, averages, etc.
12056
+ *
12057
+ * return { responseTimeState, payloadSizeState }
12058
+ * })
12059
+ * ```
12060
+ *
12061
+ * @category constructors
12062
+ * @since 2.0.0
12063
+ */
12064
+ const histogram = (name, options) => new HistogramMetric(name, options);
12065
+ /**
12066
+ * Returns a new metric that applies the specified attributes to all operations.
12067
+ *
12068
+ * **Details**
12069
+ *
12070
+ * Attributes are key-value pairs that provide additional context for metrics,
12071
+ * enabling filtering, grouping, and more detailed analysis. Each combination
12072
+ * of attribute values creates a separate metric series.
12073
+ *
12074
+ * **Example** (Applying metric attributes)
12075
+ *
12076
+ * ```ts
12077
+ * import { Effect, Metric } from "effect"
12078
+ *
12079
+ * const requestCounter = Metric.counter("http_requests_total", {
12080
+ * description: "Total HTTP requests"
12081
+ * })
12082
+ *
12083
+ * // Create tagged versions of the metric
12084
+ * const getRequests = Metric.withAttributes(requestCounter, {
12085
+ * method: "GET",
12086
+ * endpoint: "/api/users"
12087
+ * })
12088
+ *
12089
+ * const postRequests = Metric.withAttributes(requestCounter, {
12090
+ * method: "POST",
12091
+ * endpoint: "/api/users"
12092
+ * })
12093
+ *
12094
+ * const program = Effect.gen(function*() {
12095
+ * // These will be tracked as separate metric series
12096
+ * yield* Metric.update(getRequests, 1) // http_requests_total{method="GET", endpoint="/api/users"}
12097
+ * yield* Metric.update(postRequests, 1) // http_requests_total{method="POST", endpoint="/api/users"}
12098
+ * yield* Metric.update(getRequests, 1) // Increments the GET counter
12099
+ *
12100
+ * // You can also chain attributes
12101
+ * const taggedMetric = requestCounter.pipe(
12102
+ * Metric.withAttributes({ service: "user-api" }),
12103
+ * Metric.withAttributes({ version: "v1" })
12104
+ * )
12105
+ *
12106
+ * yield* Metric.update(taggedMetric, 1) // http_requests_total{service="user-api", version="v1"}
12107
+ * })
12108
+ *
12109
+ * // When taking snapshots, each attribute combination appears as a separate metric
12110
+ * const viewMetrics = Effect.gen(function*() {
12111
+ * const snapshots = yield* Metric.snapshot
12112
+ * for (const metric of snapshots) {
12113
+ * if (metric.id === "http_requests_total") {
12114
+ * console.log(`${metric.id}`, metric.attributes, metric.state)
12115
+ * }
12116
+ * }
12117
+ * })
12118
+ * ```
12119
+ *
12120
+ * @category Attributes
12121
+ * @since 4.0.0
12122
+ */
12123
+ const withAttributes = /* @__PURE__ */ dual(2, (self, attributes) => new MetricTransform(self, (context) => self.valueUnsafe(addAttributesToContext(context, attributes)), (input, context) => self.updateUnsafe(input, addAttributesToContext(context, attributes)), (input, context) => self.modifyUnsafe(input, addAttributesToContext(context, attributes))));
11625
12124
  /**
11626
12125
  * Captures a snapshot of all registered metrics synchronously using the provided
11627
12126
  * service context.
@@ -11698,6 +12197,41 @@ const snapshotUnsafe = (context) => {
11698
12197
  state: hooks.get(context)
11699
12198
  }));
11700
12199
  };
12200
+ function makeKey(metric, attributes) {
12201
+ let key = `${metric.type}:${metric.id}`;
12202
+ if (isNotUndefined(metric.description)) key += `:${metric.description}`;
12203
+ if (isNotUndefined(attributes)) key += `:${serializeAttributes(attributes)}`;
12204
+ return key;
12205
+ }
12206
+ function makeHooks(get, update, modify) {
12207
+ return {
12208
+ get,
12209
+ update,
12210
+ modify: modify ?? update
12211
+ };
12212
+ }
12213
+ function serializeAttributes(attributes) {
12214
+ return serializeEntries(Array.isArray(attributes) ? attributes : Object.entries(attributes));
12215
+ }
12216
+ function serializeEntries(entries) {
12217
+ return entries.map(([key, value]) => `${key}=${value}`).join(",");
12218
+ }
12219
+ function mergeAttributes(self, other) {
12220
+ return {
12221
+ ...attributesToRecord(self),
12222
+ ...attributesToRecord(other)
12223
+ };
12224
+ }
12225
+ function attributesToRecord(attributes) {
12226
+ if (isNotUndefined(attributes) && Array.isArray(attributes)) return attributes.reduce((acc, [key, value]) => {
12227
+ assignProperty(acc, key, value);
12228
+ return acc;
12229
+ }, {});
12230
+ return attributes;
12231
+ }
12232
+ function addAttributesToContext(context, attributes) {
12233
+ return add(context, CurrentMetricAttributes, mergeAttributes(get$4(context, CurrentMetricAttributes), attributes));
12234
+ }
11701
12235
  //#endregion
11702
12236
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/Effect.js
11703
12237
  /**
@@ -13589,7 +14123,7 @@ const matchEffect = matchEffect$2;
13589
14123
  * @category environment
13590
14124
  * @since 2.0.0
13591
14125
  */
13592
- const context = context$1;
14126
+ const context$1 = context$2;
13593
14127
  /**
13594
14128
  * Transforms the current context using the provided function.
13595
14129
  *
@@ -13722,6 +14256,30 @@ const provide = provide$1;
13722
14256
  */
13723
14257
  const provideContext = provideContext$1;
13724
14258
  /**
14259
+ * Accesses a service from the context.
14260
+ *
14261
+ * **Example** (Accessing a required service)
14262
+ *
14263
+ * ```ts
14264
+ * import { Context, Effect } from "effect"
14265
+ *
14266
+ * interface Database {
14267
+ * readonly query: (sql: string) => Effect.Effect<string>
14268
+ * }
14269
+ *
14270
+ * const Database = Context.Service<Database>("Database")
14271
+ *
14272
+ * const program = Effect.gen(function*() {
14273
+ * const db = yield* Effect.service(Database)
14274
+ * return yield* db.query("SELECT * FROM users")
14275
+ * })
14276
+ * ```
14277
+ *
14278
+ * @category context
14279
+ * @since 4.0.0
14280
+ */
14281
+ const service = service$1;
14282
+ /**
13725
14283
  * Optionally accesses a service from the environment.
13726
14284
  *
13727
14285
  * **When to use**
@@ -54752,7 +55310,7 @@ const mapEffectConcurrent = (self, f, options) => fromTransformBracket(fnUntrace
54752
55310
  const concurrencyN = options.concurrency === "unbounded" ? Number.MAX_SAFE_INTEGER : options.concurrency;
54753
55311
  const queue = yield* bounded(0);
54754
55312
  yield* addFinalizer(forkedScope, shutdown(queue));
54755
- const runFork = runForkWith(yield* context());
55313
+ const runFork = runForkWith(yield* context$1());
54756
55314
  const trackFiber = runIn(forkedScope);
54757
55315
  if (options.unordered) {
54758
55316
  const semaphore = makeUnsafe$2(concurrencyN);
@@ -56623,7 +57181,7 @@ const flatten = /* @__PURE__ */ dual((args) => isStream(args[0]), (self, options
56623
57181
  * @category sequencing
56624
57182
  * @since 2.0.0
56625
57183
  */
56626
- const concat = /* @__PURE__ */ dual(2, (self, that) => flatten(fromArray([self, that])));
57184
+ const concat$1 = /* @__PURE__ */ dual(2, (self, that) => flatten(fromArray([self, that])));
56627
57185
  /**
56628
57186
  * Merges two streams, emitting elements from both as they arrive.
56629
57187
  *
@@ -57102,7 +57660,7 @@ const toReadableStreamWith = /* @__PURE__ */ dual((args) => isStream(args[0]), (
57102
57660
  * @category destructors
57103
57661
  * @since 2.0.0
57104
57662
  */
57105
- const toReadableStreamEffect = /* @__PURE__ */ dual((args) => isStream(args[0]), (self, options) => map$3(context(), (context) => toReadableStreamWith(self, context, options)));
57663
+ const toReadableStreamEffect = /* @__PURE__ */ dual((args) => isStream(args[0]), (self, options) => map$3(context$1(), (context) => toReadableStreamWith(self, context, options)));
57106
57664
  //#endregion
57107
57665
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/FileSystem.js
57108
57666
  /**
@@ -59911,7 +60469,7 @@ const SpanNameGenerator = /* @__PURE__ */ Reference("effect/http/HttpClient/Span
59911
60469
  * @category layers
59912
60470
  * @since 4.0.0
59913
60471
  */
59914
- const layerMergedContext = (effect$2) => effect(HttpClient)(contextWith((context) => map$3(effect$2, (client) => transformResponse(client, updateContext((input) => merge$3(context, input))))));
60472
+ const layerMergedContext = (effect$2) => effect(HttpClient)(contextWith((context) => map$3(effect$2, (client) => transformResponse(client, updateContext((input) => merge$4(context, input))))));
59915
60473
  const responseRegistry = /* @__PURE__ */ (() => {
59916
60474
  if ("FinalizationRegistry" in globalThis && globalThis.FinalizationRegistry) {
59917
60475
  const registry = /* @__PURE__ */ new FinalizationRegistry((controller) => {
@@ -60452,7 +61010,7 @@ const runImpl = (self, effect, options) => withFiber((parent) => {
60452
61010
  * @category combinators
60453
61011
  * @since 2.0.0
60454
61012
  */
60455
- const runtime = (self) => () => map$3(context(), (services) => {
61013
+ const runtime = (self) => () => map$3(context$1(), (services) => {
60456
61014
  const runFork = runForkWith(services);
60457
61015
  return (effect, options) => {
60458
61016
  if (self.state._tag === "Closed") return constInterruptedFiber();
@@ -60574,7 +61132,7 @@ const layerFlusher = /* @__PURE__ */ sync$1(Flusher, () => {
60574
61132
  * @since 4.0.0
60575
61133
  */
60576
61134
  const make$8 = /* @__PURE__ */ fnUntraced(function* (options) {
60577
- const services = yield* context();
61135
+ const services = yield* context$1();
60578
61136
  const clock = get$4(services, Clock);
60579
61137
  const scope = get$4(services, Scope);
60580
61138
  const exportInterval = max(fromInputUnsafe(options.exportInterval), zero);
@@ -60743,6 +61301,537 @@ const unknownToAttributeValue = (value) => {
60743
61301
  }
60744
61302
  };
60745
61303
  //#endregion
61304
+ //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/unstable/observability/internal/protobuf.js
61305
+ /**
61306
+ * Low-level protobuf wire format encoding utilities.
61307
+ *
61308
+ * Protobuf wire types:
61309
+ * - 0: Varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum)
61310
+ * - 1: 64-bit (fixed64, sfixed64, double)
61311
+ * - 2: Length-delimited (string, bytes, embedded messages, packed repeated fields)
61312
+ * - 5: 32-bit (fixed32, sfixed32, float)
61313
+ */
61314
+ const WireType = {
61315
+ Varint: 0,
61316
+ Fixed64: 1,
61317
+ LengthDelimited: 2,
61318
+ Fixed32: 5
61319
+ };
61320
+ /**
61321
+ * Encodes a field tag (field number + wire type)
61322
+ */
61323
+ const encodeTag = (fieldNumber, wireType) => fieldNumber << 3 | wireType;
61324
+ /**
61325
+ * Encodes a varint (variable-length integer)
61326
+ *
61327
+ * @internal
61328
+ */
61329
+ const encodeVarint = (value) => {
61330
+ const bytes = [];
61331
+ let n = typeof value === "bigint" ? value : BigInt(value);
61332
+ while (n > BigInt(127)) {
61333
+ bytes.push(Number(n & BigInt(127)) | 128);
61334
+ n >>= BigInt(7);
61335
+ }
61336
+ bytes.push(Number(n));
61337
+ return new Uint8Array(bytes);
61338
+ };
61339
+ /**
61340
+ * Encodes a 64-bit fixed value (little-endian)
61341
+ *
61342
+ * @internal
61343
+ */
61344
+ const encodeFixed64 = (value) => {
61345
+ const bytes = new Uint8Array(8);
61346
+ new DataView(bytes.buffer).setBigUint64(0, value, true);
61347
+ return bytes;
61348
+ };
61349
+ /**
61350
+ * Encodes a 32-bit fixed value (little-endian)
61351
+ *
61352
+ * @internal
61353
+ */
61354
+ const encodeFixed32 = (value) => {
61355
+ const bytes = new Uint8Array(4);
61356
+ new DataView(bytes.buffer).setUint32(0, value, true);
61357
+ return bytes;
61358
+ };
61359
+ /**
61360
+ * Encodes a double (64-bit float, little-endian)
61361
+ *
61362
+ * @internal
61363
+ */
61364
+ const encodeDouble = (value) => {
61365
+ const bytes = new Uint8Array(8);
61366
+ new DataView(bytes.buffer).setFloat64(0, value, true);
61367
+ return bytes;
61368
+ };
61369
+ /**
61370
+ * Encodes a string to UTF-8 bytes
61371
+ *
61372
+ * @internal
61373
+ */
61374
+ const encodeString = (value) => new TextEncoder().encode(value);
61375
+ /**
61376
+ * Encodes bytes as a hex string to Uint8Array
61377
+ *
61378
+ * @internal
61379
+ */
61380
+ const encodeHexBytes = (hex) => {
61381
+ const bytes = new Uint8Array(hex.length / 2);
61382
+ for (let i = 0; i < hex.length; i += 2) bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
61383
+ return bytes;
61384
+ };
61385
+ /**
61386
+ * Concatenates multiple Uint8Arrays
61387
+ *
61388
+ * @internal
61389
+ */
61390
+ const concat = (...arrays) => {
61391
+ const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
61392
+ const result = new Uint8Array(totalLength);
61393
+ let offset = 0;
61394
+ for (const arr of arrays) {
61395
+ result.set(arr, offset);
61396
+ offset += arr.length;
61397
+ }
61398
+ return result;
61399
+ };
61400
+ /**
61401
+ * Encodes a varint field
61402
+ *
61403
+ * @internal
61404
+ */
61405
+ const varintField = (fieldNumber, value) => concat(encodeVarint(encodeTag(fieldNumber, WireType.Varint)), encodeVarint(value));
61406
+ /**
61407
+ * Encodes a bool field
61408
+ *
61409
+ * @internal
61410
+ */
61411
+ const boolField = (fieldNumber, value) => varintField(fieldNumber, value ? 1 : 0);
61412
+ /**
61413
+ * Encodes a fixed64 field
61414
+ *
61415
+ * @internal
61416
+ */
61417
+ const fixed64Field = (fieldNumber, value) => concat(encodeVarint(encodeTag(fieldNumber, WireType.Fixed64)), encodeFixed64(value));
61418
+ /**
61419
+ * Encodes a fixed32 field
61420
+ *
61421
+ * @internal
61422
+ */
61423
+ const fixed32Field = (fieldNumber, value) => concat(encodeVarint(encodeTag(fieldNumber, WireType.Fixed32)), encodeFixed32(value));
61424
+ /**
61425
+ * Encodes a double field
61426
+ *
61427
+ * @internal
61428
+ */
61429
+ const doubleField = (fieldNumber, value) => concat(encodeVarint(encodeTag(fieldNumber, WireType.Fixed64)), encodeDouble(value));
61430
+ /**
61431
+ * Encodes a length-delimited field (bytes, string, embedded message)
61432
+ *
61433
+ * @internal
61434
+ */
61435
+ const lengthDelimitedField = (fieldNumber, value) => concat(encodeVarint(encodeTag(fieldNumber, WireType.LengthDelimited)), encodeVarint(value.length), value);
61436
+ /**
61437
+ * Encodes a string field
61438
+ *
61439
+ * @internal
61440
+ */
61441
+ const stringField = (fieldNumber, value) => lengthDelimitedField(fieldNumber, encodeString(value));
61442
+ /**
61443
+ * Encodes a bytes field from hex string
61444
+ *
61445
+ * @internal
61446
+ */
61447
+ const bytesFieldFromHex = (fieldNumber, hex) => lengthDelimitedField(fieldNumber, encodeHexBytes(hex));
61448
+ /**
61449
+ * Encodes an embedded message field
61450
+ *
61451
+ * @internal
61452
+ */
61453
+ const messageField = (fieldNumber, message) => lengthDelimitedField(fieldNumber, message);
61454
+ /**
61455
+ * Encodes repeated fields
61456
+ *
61457
+ * @internal
61458
+ */
61459
+ const repeatedField = (fieldNumber, values, encode) => concat(...values.map((v) => messageField(fieldNumber, encode(v))));
61460
+ /**
61461
+ * Helper to conditionally encode a string field if non-empty
61462
+ *
61463
+ * @internal
61464
+ */
61465
+ const optionalStringField = (fieldNumber, value) => value !== void 0 && value !== "" ? stringField(fieldNumber, value) : new Uint8Array(0);
61466
+ //#endregion
61467
+ //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/unstable/observability/internal/otlpProtobuf.js
61468
+ /**
61469
+ * OTLP Protobuf encoding for traces, metrics, and logs.
61470
+ *
61471
+ * Implements the protobuf wire format according to:
61472
+ * https://github.com/open-telemetry/opentelemetry-proto
61473
+ */
61474
+ /**
61475
+ * Encodes an AnyValue message.
61476
+ *
61477
+ * message AnyValue {
61478
+ * oneof value {
61479
+ * string string_value = 1;
61480
+ * bool bool_value = 2;
61481
+ * int64 int_value = 3;
61482
+ * double double_value = 4;
61483
+ * ArrayValue array_value = 5;
61484
+ * KeyValueList kvlist_value = 6;
61485
+ * bytes bytes_value = 7;
61486
+ * }
61487
+ * }
61488
+ *
61489
+ * @internal
61490
+ */
61491
+ const encodeAnyValue = (value) => {
61492
+ if (value.stringValue !== void 0 && value.stringValue !== null) return stringField(1, value.stringValue);
61493
+ if (value.boolValue !== void 0 && value.boolValue !== null) return boolField(2, value.boolValue);
61494
+ if (value.intValue !== void 0 && value.intValue !== null) return varintField(3, BigInt(value.intValue));
61495
+ if (value.doubleValue !== void 0 && value.doubleValue !== null) return doubleField(4, value.doubleValue);
61496
+ if (value.arrayValue !== void 0) return messageField(5, encodeArrayValue(value.arrayValue));
61497
+ if (value.kvlistValue !== void 0) return messageField(6, encodeKeyValueList(value.kvlistValue));
61498
+ if (value.bytesValue !== void 0) return lengthDelimitedField(7, value.bytesValue);
61499
+ return new Uint8Array(0);
61500
+ };
61501
+ /**
61502
+ * Encodes an ArrayValue message.
61503
+ *
61504
+ * message ArrayValue {
61505
+ * repeated AnyValue values = 1;
61506
+ * }
61507
+ *
61508
+ * @internal
61509
+ */
61510
+ const encodeArrayValue = (value) => repeatedField(1, value.values, encodeAnyValue);
61511
+ /**
61512
+ * Encodes a KeyValueList message.
61513
+ *
61514
+ * message KeyValueList {
61515
+ * repeated KeyValue values = 1;
61516
+ * }
61517
+ *
61518
+ * @internal
61519
+ */
61520
+ const encodeKeyValueList = (value) => repeatedField(1, value.values, encodeKeyValue);
61521
+ /**
61522
+ * Encodes a KeyValue message.
61523
+ *
61524
+ * message KeyValue {
61525
+ * string key = 1;
61526
+ * AnyValue value = 2;
61527
+ * }
61528
+ *
61529
+ * @internal
61530
+ */
61531
+ const encodeKeyValue = (kv) => concat(stringField(1, kv.key), messageField(2, encodeAnyValue(kv.value)));
61532
+ /**
61533
+ * Encodes an InstrumentationScope message.
61534
+ *
61535
+ * message InstrumentationScope {
61536
+ * string name = 1;
61537
+ * string version = 2;
61538
+ * repeated KeyValue attributes = 3;
61539
+ * uint32 dropped_attributes_count = 4;
61540
+ * }
61541
+ *
61542
+ * @internal
61543
+ */
61544
+ const encodeInstrumentationScope = (scope) => concat(stringField(1, scope.name), optionalStringField(2, scope.version), scope.attributes ? repeatedField(3, scope.attributes, encodeKeyValue) : new Uint8Array(0), scope.droppedAttributesCount ? varintField(4, scope.droppedAttributesCount) : new Uint8Array(0));
61545
+ /**
61546
+ * Encodes a Resource message.
61547
+ *
61548
+ * message Resource {
61549
+ * repeated KeyValue attributes = 1;
61550
+ * uint32 dropped_attributes_count = 2;
61551
+ * }
61552
+ *
61553
+ * @internal
61554
+ */
61555
+ const encodeResource = (resource) => concat(repeatedField(1, resource.attributes, encodeKeyValue), resource.droppedAttributesCount > 0 ? varintField(2, resource.droppedAttributesCount) : new Uint8Array(0));
61556
+ /**
61557
+ * Encodes a Status message.
61558
+ *
61559
+ * message Status {
61560
+ * string message = 2;
61561
+ * StatusCode code = 3;
61562
+ * }
61563
+ *
61564
+ * @internal
61565
+ */
61566
+ const encodeStatus = (status) => concat(optionalStringField(2, status.message), varintField(3, status.code));
61567
+ /**
61568
+ * Encodes an Event message.
61569
+ *
61570
+ * message Event {
61571
+ * fixed64 time_unix_nano = 1;
61572
+ * string name = 2;
61573
+ * repeated KeyValue attributes = 3;
61574
+ * uint32 dropped_attributes_count = 4;
61575
+ * }
61576
+ *
61577
+ * @internal
61578
+ */
61579
+ const encodeEvent = (event) => concat(fixed64Field(1, BigInt(event.timeUnixNano)), stringField(2, event.name), repeatedField(3, event.attributes, encodeKeyValue), event.droppedAttributesCount > 0 ? varintField(4, event.droppedAttributesCount) : new Uint8Array(0));
61580
+ /**
61581
+ * Encodes a Link message.
61582
+ *
61583
+ * message Link {
61584
+ * bytes trace_id = 1;
61585
+ * bytes span_id = 2;
61586
+ * string trace_state = 3;
61587
+ * repeated KeyValue attributes = 4;
61588
+ * uint32 dropped_attributes_count = 5;
61589
+ * fixed32 flags = 6;
61590
+ * }
61591
+ *
61592
+ * @internal
61593
+ */
61594
+ const encodeLink = (link) => concat(bytesFieldFromHex(1, link.traceId), bytesFieldFromHex(2, link.spanId), optionalStringField(3, link.traceState), repeatedField(4, link.attributes, encodeKeyValue), link.droppedAttributesCount > 0 ? varintField(5, link.droppedAttributesCount) : new Uint8Array(0), link.flags !== void 0 ? fixed32Field(6, link.flags) : new Uint8Array(0));
61595
+ /**
61596
+ * Encodes a Span message.
61597
+ *
61598
+ * message Span {
61599
+ * bytes trace_id = 1;
61600
+ * bytes span_id = 2;
61601
+ * string trace_state = 3;
61602
+ * bytes parent_span_id = 4;
61603
+ * string name = 5;
61604
+ * SpanKind kind = 6;
61605
+ * fixed64 start_time_unix_nano = 7;
61606
+ * fixed64 end_time_unix_nano = 8;
61607
+ * repeated KeyValue attributes = 9;
61608
+ * uint32 dropped_attributes_count = 10;
61609
+ * repeated Event events = 11;
61610
+ * uint32 dropped_events_count = 12;
61611
+ * repeated Link links = 13;
61612
+ * uint32 dropped_links_count = 14;
61613
+ * Status status = 15;
61614
+ * fixed32 flags = 16;
61615
+ * }
61616
+ *
61617
+ * @internal
61618
+ */
61619
+ const encodeSpan = (span) => concat(bytesFieldFromHex(1, span.traceId), bytesFieldFromHex(2, span.spanId), optionalStringField(3, span.traceState), span.parentSpanId !== void 0 ? bytesFieldFromHex(4, span.parentSpanId) : new Uint8Array(0), stringField(5, span.name), varintField(6, span.kind), fixed64Field(7, BigInt(span.startTimeUnixNano)), fixed64Field(8, BigInt(span.endTimeUnixNano)), repeatedField(9, span.attributes, encodeKeyValue), span.droppedAttributesCount > 0 ? varintField(10, span.droppedAttributesCount) : new Uint8Array(0), repeatedField(11, span.events, encodeEvent), span.droppedEventsCount > 0 ? varintField(12, span.droppedEventsCount) : new Uint8Array(0), repeatedField(13, span.links, encodeLink), span.droppedLinksCount > 0 ? varintField(14, span.droppedLinksCount) : new Uint8Array(0), messageField(15, encodeStatus(span.status)), span.flags !== void 0 ? fixed32Field(16, span.flags) : new Uint8Array(0));
61620
+ /**
61621
+ * Encodes a ScopeSpans message.
61622
+ *
61623
+ * message ScopeSpans {
61624
+ * InstrumentationScope scope = 1;
61625
+ * repeated Span spans = 2;
61626
+ * string schema_url = 3;
61627
+ * }
61628
+ *
61629
+ * @internal
61630
+ */
61631
+ const encodeScopeSpans = (scopeSpans) => concat(messageField(1, encodeInstrumentationScope(scopeSpans.scope)), repeatedField(2, scopeSpans.spans, encodeSpan), optionalStringField(3, scopeSpans.schemaUrl));
61632
+ /**
61633
+ * Encodes a ResourceSpans message.
61634
+ *
61635
+ * message ResourceSpans {
61636
+ * Resource resource = 1;
61637
+ * repeated ScopeSpans scope_spans = 2;
61638
+ * string schema_url = 3;
61639
+ * }
61640
+ *
61641
+ * @internal
61642
+ */
61643
+ const encodeResourceSpans = (resourceSpans) => concat(messageField(1, encodeResource(resourceSpans.resource)), repeatedField(2, resourceSpans.scopeSpans, encodeScopeSpans), optionalStringField(3, resourceSpans.schemaUrl));
61644
+ /**
61645
+ * Encodes a TracesData message (top-level export request).
61646
+ *
61647
+ * message TracesData {
61648
+ * repeated ResourceSpans resource_spans = 1;
61649
+ * }
61650
+ *
61651
+ * @internal
61652
+ */
61653
+ const encodeTracesData = (tracesData) => repeatedField(1, tracesData.resourceSpans, encodeResourceSpans);
61654
+ /**
61655
+ * Encodes a NumberDataPoint message.
61656
+ *
61657
+ * message NumberDataPoint {
61658
+ * repeated KeyValue attributes = 7;
61659
+ * fixed64 start_time_unix_nano = 2;
61660
+ * fixed64 time_unix_nano = 3;
61661
+ * oneof value {
61662
+ * double as_double = 4;
61663
+ * sfixed64 as_int = 6;
61664
+ * }
61665
+ * repeated Exemplar exemplars = 5;
61666
+ * uint32 flags = 8;
61667
+ * }
61668
+ *
61669
+ * @internal
61670
+ */
61671
+ const encodeNumberDataPoint = (point) => concat(fixed64Field(2, BigInt(point.startTimeUnixNano)), fixed64Field(3, BigInt(point.timeUnixNano)), point.asDouble !== void 0 ? doubleField(4, point.asDouble) : new Uint8Array(0), point.asInt !== void 0 ? fixed64Field(6, BigInt(point.asInt)) : new Uint8Array(0), repeatedField(7, point.attributes, encodeKeyValue), point.flags !== void 0 ? varintField(8, point.flags) : new Uint8Array(0));
61672
+ /**
61673
+ * Encodes a HistogramDataPoint message.
61674
+ *
61675
+ * message HistogramDataPoint {
61676
+ * repeated KeyValue attributes = 9;
61677
+ * fixed64 start_time_unix_nano = 2;
61678
+ * fixed64 time_unix_nano = 3;
61679
+ * fixed64 count = 4;
61680
+ * optional double sum = 5;
61681
+ * repeated fixed64 bucket_counts = 6;
61682
+ * repeated double explicit_bounds = 7;
61683
+ * optional double min = 11;
61684
+ * optional double max = 12;
61685
+ * uint32 flags = 10;
61686
+ * }
61687
+ *
61688
+ * @internal
61689
+ */
61690
+ const encodeHistogramDataPoint = (point) => {
61691
+ const bucketCountsEncoded = concat(...point.bucketCounts.map((count) => fixed64Field(6, BigInt(count))));
61692
+ const explicitBoundsEncoded = concat(...point.explicitBounds.map((bound) => doubleField(7, bound)));
61693
+ return concat(fixed64Field(2, BigInt(point.startTimeUnixNano)), fixed64Field(3, BigInt(point.timeUnixNano)), fixed64Field(4, BigInt(point.count)), point.sum !== void 0 ? doubleField(5, point.sum) : new Uint8Array(0), bucketCountsEncoded, explicitBoundsEncoded, repeatedField(9, point.attributes, encodeKeyValue), point.flags !== void 0 ? varintField(10, point.flags) : new Uint8Array(0), point.min !== void 0 ? doubleField(11, point.min) : new Uint8Array(0), point.max !== void 0 ? doubleField(12, point.max) : new Uint8Array(0));
61694
+ };
61695
+ /**
61696
+ * Encodes a Gauge message.
61697
+ *
61698
+ * message Gauge {
61699
+ * repeated NumberDataPoint data_points = 1;
61700
+ * }
61701
+ *
61702
+ * @internal
61703
+ */
61704
+ const encodeGauge = (gauge) => repeatedField(1, gauge.dataPoints, encodeNumberDataPoint);
61705
+ /**
61706
+ * Encodes a Sum message.
61707
+ *
61708
+ * message Sum {
61709
+ * repeated NumberDataPoint data_points = 1;
61710
+ * AggregationTemporality aggregation_temporality = 2;
61711
+ * bool is_monotonic = 3;
61712
+ * }
61713
+ *
61714
+ * @internal
61715
+ */
61716
+ const encodeSum = (sum) => concat(repeatedField(1, sum.dataPoints, encodeNumberDataPoint), varintField(2, sum.aggregationTemporality), boolField(3, sum.isMonotonic));
61717
+ /**
61718
+ * Encodes a Histogram message.
61719
+ *
61720
+ * message Histogram {
61721
+ * repeated HistogramDataPoint data_points = 1;
61722
+ * AggregationTemporality aggregation_temporality = 2;
61723
+ * }
61724
+ *
61725
+ * @internal
61726
+ */
61727
+ const encodeHistogram = (histogram) => concat(repeatedField(1, histogram.dataPoints, encodeHistogramDataPoint), varintField(2, histogram.aggregationTemporality));
61728
+ /**
61729
+ * Encodes a Metric message.
61730
+ *
61731
+ * message Metric {
61732
+ * string name = 1;
61733
+ * string description = 2;
61734
+ * string unit = 3;
61735
+ * oneof data {
61736
+ * Gauge gauge = 5;
61737
+ * Sum sum = 7;
61738
+ * Histogram histogram = 9;
61739
+ * ExponentialHistogram exponential_histogram = 10;
61740
+ * Summary summary = 11;
61741
+ * }
61742
+ * }
61743
+ *
61744
+ * @internal
61745
+ */
61746
+ const encodeMetric = (metric) => concat(stringField(1, metric.name), optionalStringField(2, metric.description), optionalStringField(3, metric.unit), metric.gauge !== void 0 ? messageField(5, encodeGauge(metric.gauge)) : new Uint8Array(0), metric.sum !== void 0 ? messageField(7, encodeSum(metric.sum)) : new Uint8Array(0), metric.histogram !== void 0 ? messageField(9, encodeHistogram(metric.histogram)) : new Uint8Array(0));
61747
+ /**
61748
+ * Encodes a ScopeMetrics message.
61749
+ *
61750
+ * message ScopeMetrics {
61751
+ * InstrumentationScope scope = 1;
61752
+ * repeated Metric metrics = 2;
61753
+ * string schema_url = 3;
61754
+ * }
61755
+ *
61756
+ * @internal
61757
+ */
61758
+ const encodeScopeMetrics = (scopeMetrics) => concat(messageField(1, encodeInstrumentationScope(scopeMetrics.scope)), repeatedField(2, scopeMetrics.metrics, encodeMetric), optionalStringField(3, scopeMetrics.schemaUrl));
61759
+ /**
61760
+ * Encodes a ResourceMetrics message.
61761
+ *
61762
+ * message ResourceMetrics {
61763
+ * Resource resource = 1;
61764
+ * repeated ScopeMetrics scope_metrics = 2;
61765
+ * string schema_url = 3;
61766
+ * }
61767
+ *
61768
+ * @internal
61769
+ */
61770
+ const encodeResourceMetrics = (resourceMetrics) => concat(messageField(1, encodeResource(resourceMetrics.resource)), repeatedField(2, resourceMetrics.scopeMetrics, encodeScopeMetrics), optionalStringField(3, resourceMetrics.schemaUrl));
61771
+ /**
61772
+ * Encodes a MetricsData message (top-level export request).
61773
+ *
61774
+ * message MetricsData {
61775
+ * repeated ResourceMetrics resource_metrics = 1;
61776
+ * }
61777
+ *
61778
+ * @internal
61779
+ */
61780
+ const encodeMetricsData = (metricsData) => repeatedField(1, metricsData.resourceMetrics, encodeResourceMetrics);
61781
+ /**
61782
+ * Encodes a LogRecord message.
61783
+ *
61784
+ * message LogRecord {
61785
+ * fixed64 time_unix_nano = 1;
61786
+ * fixed64 observed_time_unix_nano = 11;
61787
+ * SeverityNumber severity_number = 2;
61788
+ * string severity_text = 3;
61789
+ * AnyValue body = 5;
61790
+ * repeated KeyValue attributes = 6;
61791
+ * uint32 dropped_attributes_count = 7;
61792
+ * fixed32 flags = 8;
61793
+ * bytes trace_id = 9;
61794
+ * bytes span_id = 10;
61795
+ * }
61796
+ *
61797
+ * @internal
61798
+ */
61799
+ const encodeLogRecord = (record) => concat(fixed64Field(1, BigInt(record.timeUnixNano)), record.severityNumber !== void 0 ? varintField(2, record.severityNumber) : new Uint8Array(0), optionalStringField(3, record.severityText), record.body !== void 0 ? messageField(5, encodeAnyValue(record.body)) : new Uint8Array(0), repeatedField(6, record.attributes, encodeKeyValue), record.droppedAttributesCount !== void 0 && record.droppedAttributesCount > 0 ? varintField(7, record.droppedAttributesCount) : new Uint8Array(0), record.flags !== void 0 ? fixed32Field(8, record.flags) : new Uint8Array(0), record.traceId !== void 0 && record.traceId !== "" ? bytesFieldFromHex(9, record.traceId) : new Uint8Array(0), record.spanId !== void 0 && record.spanId !== "" ? bytesFieldFromHex(10, record.spanId) : new Uint8Array(0), record.observedTimeUnixNano !== void 0 ? fixed64Field(11, BigInt(record.observedTimeUnixNano)) : new Uint8Array(0));
61800
+ /**
61801
+ * Encodes a ScopeLogs message.
61802
+ *
61803
+ * message ScopeLogs {
61804
+ * InstrumentationScope scope = 1;
61805
+ * repeated LogRecord log_records = 2;
61806
+ * string schema_url = 3;
61807
+ * }
61808
+ *
61809
+ * @internal
61810
+ */
61811
+ const encodeScopeLogs = (scopeLogs) => concat(messageField(1, encodeInstrumentationScope(scopeLogs.scope)), repeatedField(2, scopeLogs.logRecords, encodeLogRecord), optionalStringField(3, scopeLogs.schemaUrl));
61812
+ /**
61813
+ * Encodes a ResourceLogs message.
61814
+ *
61815
+ * message ResourceLogs {
61816
+ * Resource resource = 1;
61817
+ * repeated ScopeLogs scope_logs = 2;
61818
+ * string schema_url = 3;
61819
+ * }
61820
+ *
61821
+ * @internal
61822
+ */
61823
+ const encodeResourceLogs = (resourceLogs) => concat(messageField(1, encodeResource(resourceLogs.resource)), repeatedField(2, resourceLogs.scopeLogs, encodeScopeLogs), optionalStringField(3, resourceLogs.schemaUrl));
61824
+ /**
61825
+ * Encodes a LogsData message (top-level export request).
61826
+ *
61827
+ * message LogsData {
61828
+ * repeated ResourceLogs resource_logs = 1;
61829
+ * }
61830
+ *
61831
+ * @internal
61832
+ */
61833
+ const encodeLogsData = (logsData) => repeatedField(1, logsData.resourceLogs, encodeResourceLogs);
61834
+ //#endregion
60746
61835
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/unstable/observability/OtlpSerialization.js
60747
61836
  /**
60748
61837
  * Serializes OTLP payloads into HTTP request bodies.
@@ -60772,6 +61861,18 @@ const layerJson$1 = /* @__PURE__ */ succeed$3(OtlpSerialization, {
60772
61861
  metrics: (metrics) => jsonUnsafe(metrics),
60773
61862
  logs: (logs) => jsonUnsafe(logs)
60774
61863
  });
61864
+ /**
61865
+ * Provides `OtlpSerialization` using protobuf-encoded OTLP bodies with the
61866
+ * `application/x-protobuf` content type.
61867
+ *
61868
+ * @category layers
61869
+ * @since 4.0.0
61870
+ */
61871
+ const layerProtobuf = /* @__PURE__ */ succeed$3(OtlpSerialization, {
61872
+ traces: (spans) => uint8Array(encodeTracesData(spans), "application/x-protobuf"),
61873
+ metrics: (metrics) => uint8Array(encodeMetricsData(metrics), "application/x-protobuf"),
61874
+ logs: (logs) => uint8Array(encodeLogsData(logs), "application/x-protobuf")
61875
+ });
60775
61876
  //#endregion
60776
61877
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.102/node_modules/effect/dist/unstable/observability/OtlpLogger.js
60777
61878
  /**
@@ -60944,7 +62045,7 @@ const make$5 = /* @__PURE__ */ fnUntraced(function* (options) {
60944
62045
  const temporality = options.temporality ?? "cumulative";
60945
62046
  const resource = yield* fromConfig(options.resource);
60946
62047
  const metricsScope = { name: serviceNameUnsafe(resource) };
60947
- const services = yield* context();
62048
+ const services = yield* context$1();
60948
62049
  let previousExportTimeNanos = startTimeNanos;
60949
62050
  const previousCounterState = /* @__PURE__ */ new Map();
60950
62051
  const previousHistogramState = /* @__PURE__ */ new Map();
@@ -61662,7 +62763,7 @@ const TypeId$1 = "~effect/Cache";
61662
62763
  */
61663
62764
  const makeWith = (lookup, options) => contextWith$1((context) => {
61664
62765
  const self = Object.create(Proto$1);
61665
- self.lookup = (key) => updateContext$1(lookup(key), (input) => merge$3(context, input));
62766
+ self.lookup = (key) => updateContext$1(lookup(key), (input) => merge$4(context, input));
61666
62767
  self.map = make$20();
61667
62768
  self.capacity = options.capacity;
61668
62769
  self.timeToLive = options.timeToLive ? (exit, key) => fromInputUnsafe(options.timeToLive(exit, key)) : defaultTimeToLive;
@@ -63745,6 +64846,11 @@ const REACT_ROUTER_CAPABILITY_THRESHOLDS = [
63745
64846
  }
63746
64847
  ];
63747
64848
  const SCORE_API_URL = "https://www.react.doctor/api/score";
64849
+ const AXIOM_TRACES_PATH = "/v1/traces";
64850
+ const AXIOM_METRICS_PATH = "/v1/metrics";
64851
+ const AXIOM_DATASET_HEADER = "X-Axiom-Dataset";
64852
+ const AXIOM_METRICS_DATASET_HEADER = "x-axiom-metrics-dataset";
64853
+ const TELEMETRY_SHUTDOWN_TIMEOUT_MS = 1e3;
63748
64854
  const FETCH_TIMEOUT_MS = 1e4;
63749
64855
  const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
63750
64856
  const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
@@ -64007,7 +65113,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
64007
65113
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
64008
65114
  const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
64009
65115
  const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
64010
- const CORE_PACKAGE_VERSION = "0.9.9";
65116
+ const CORE_PACKAGE_VERSION = "0.9.10";
64011
65117
  const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
64012
65118
  const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
64013
65119
  const SUPPLY_CHAIN_PLUGIN = "socket";
@@ -68224,11 +69330,304 @@ var ReactDoctorError = class extends TaggedErrorClass()("ReactDoctorError", { re
68224
69330
  };
68225
69331
  const isSplittableReactDoctorError = (error) => error instanceof ReactDoctorError && error.reason._tag === "OxlintBatchExceeded";
68226
69332
  const isReactDoctorError = (error) => error instanceof ReactDoctorError;
69333
+ const REDACTED_PLACEHOLDER = "<redacted>";
69334
+ const KEEP_PREFIX = `$1${REDACTED_PLACEHOLDER}`;
69335
+ const KNOWN_SECRET_RULES = [
69336
+ {
69337
+ pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
69338
+ replacement: REDACTED_PLACEHOLDER
69339
+ },
69340
+ {
69341
+ pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
69342
+ replacement: REDACTED_PLACEHOLDER
69343
+ },
69344
+ {
69345
+ pattern: /(?<=:\/\/)[^\s/:@]+:[^\s/@]+(?=@)/g,
69346
+ replacement: REDACTED_PLACEHOLDER
69347
+ },
69348
+ {
69349
+ pattern: /\b(AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA|A3T[A-Z0-9])[0-9A-Z]{16,}/g,
69350
+ replacement: KEEP_PREFIX
69351
+ },
69352
+ {
69353
+ pattern: /\b(gh[pousr]_)[A-Za-z0-9]{36,}/g,
69354
+ replacement: KEEP_PREFIX
69355
+ },
69356
+ {
69357
+ pattern: /\b(github_pat_)[A-Za-z0-9_]{22,}/g,
69358
+ replacement: KEEP_PREFIX
69359
+ },
69360
+ {
69361
+ pattern: /\b(glpat-)[A-Za-z0-9_-]{20,}/g,
69362
+ replacement: KEEP_PREFIX
69363
+ },
69364
+ {
69365
+ pattern: /\b(xox[baprs]-)[A-Za-z0-9-]{10,}/g,
69366
+ replacement: KEEP_PREFIX
69367
+ },
69368
+ {
69369
+ pattern: /(?<=hooks\.slack\.com\/services\/)[A-Za-z0-9/+_-]{20,}/g,
69370
+ replacement: REDACTED_PLACEHOLDER
69371
+ },
69372
+ {
69373
+ pattern: /\b((?:sk|rk)_(?:live|test)_)[0-9A-Za-z]{10,}/g,
69374
+ replacement: KEEP_PREFIX
69375
+ },
69376
+ {
69377
+ pattern: /\b(sk-(?:proj-|ant-)?)[A-Za-z0-9_-]{20,}/g,
69378
+ replacement: KEEP_PREFIX
69379
+ },
69380
+ {
69381
+ pattern: /\b(AIza)[0-9A-Za-z_-]{35,}/g,
69382
+ replacement: KEEP_PREFIX
69383
+ },
69384
+ {
69385
+ pattern: /\b(ya29\.)[0-9A-Za-z_-]{20,}/g,
69386
+ replacement: KEEP_PREFIX
69387
+ },
69388
+ {
69389
+ pattern: /\b(npm_)[A-Za-z0-9]{36,}/g,
69390
+ replacement: KEEP_PREFIX
69391
+ },
69392
+ {
69393
+ pattern: /\b(SG\.)[A-Za-z0-9_-]{22,}\.[A-Za-z0-9_-]{43,}/g,
69394
+ replacement: KEEP_PREFIX
69395
+ },
69396
+ {
69397
+ pattern: /\b(SK)[0-9a-fA-F]{32,}/g,
69398
+ replacement: KEEP_PREFIX
69399
+ },
69400
+ {
69401
+ pattern: /\b(dop_v1_)[a-f0-9]{64,}/g,
69402
+ replacement: KEEP_PREFIX
69403
+ },
69404
+ {
69405
+ pattern: /\b(shp(?:at|ca|pa|ss)_)[a-fA-F0-9]{32,}/g,
69406
+ replacement: KEEP_PREFIX
69407
+ },
69408
+ {
69409
+ pattern: /\b(sq0[a-z]{3}-)[0-9A-Za-z_-]{22,}/g,
69410
+ replacement: KEEP_PREFIX
69411
+ },
69412
+ {
69413
+ pattern: /\b([0-9]{8,10}:AA)[0-9A-Za-z_-]{32,}/g,
69414
+ replacement: KEEP_PREFIX
69415
+ },
69416
+ {
69417
+ pattern: /(?<=\bBearer\s)[A-Za-z0-9._~+/=-]{16,}/g,
69418
+ replacement: REDACTED_PLACEHOLDER
69419
+ },
69420
+ {
69421
+ pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
69422
+ replacement: REDACTED_PLACEHOLDER
69423
+ }
69424
+ ];
69425
+ const CANDIDATE_TOKEN_PATTERN = /[A-Za-z0-9_][A-Za-z0-9_-]*/g;
69426
+ const HEX_DIGEST_PATTERN = /^(?:[0-9a-f]{32}|[0-9a-f]{40}|[0-9a-f]{64})$/;
69427
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
69428
+ const HAS_LETTER_PATTERN = /[A-Za-z]/;
69429
+ const HAS_DIGIT_PATTERN = /[0-9]/;
69430
+ const shannonEntropyBits = (value) => {
69431
+ const counts = /* @__PURE__ */ new Map();
69432
+ for (const char of value) counts.set(char, (counts.get(char) ?? 0) + 1);
69433
+ let bits = 0;
69434
+ for (const count of counts.values()) {
69435
+ const probability = count / value.length;
69436
+ bits -= probability * Math.log2(probability);
69437
+ }
69438
+ return bits;
69439
+ };
69440
+ const looksLikeHighEntropySecret = (token) => {
69441
+ if (token.length < 32) return false;
69442
+ if (!HAS_LETTER_PATTERN.test(token) || !HAS_DIGIT_PATTERN.test(token)) return false;
69443
+ if (HEX_DIGEST_PATTERN.test(token) || UUID_PATTERN.test(token)) return false;
69444
+ return shannonEntropyBits(token) >= 3;
69445
+ };
69446
+ const redactHighEntropyTokens = (text) => text.replace(CANDIDATE_TOKEN_PATTERN, (token) => looksLikeHighEntropySecret(token) ? REDACTED_PLACEHOLDER : token);
69447
+ /**
69448
+ * Masks API keys, tokens, private keys, credentialed URLs, and emails
69449
+ * found anywhere inside a free-text string, returning the scrubbed text.
69450
+ * Applied to every diagnostic's `message` / `help` at construction time
69451
+ * so secrets never reach the terminal, the JSON report, or the score
69452
+ * API — react-doctor must never echo or transmit a user's secrets.
69453
+ *
69454
+ * Provider tokens keep their non-secret, type-identifying prefix (e.g.
69455
+ * `sk_live_<redacted>`, `ghp_<redacted>`, `AKIA<redacted>`) so the leaked
69456
+ * credential's type stays visible; structural or unknown-format secrets
69457
+ * with no meaningful prefix are masked whole.
69458
+ *
69459
+ * Runs the high-precision known-shape detectors first, then a generic
69460
+ * entropy-gated sweep for unknown-format secrets. Idempotent: the inert
69461
+ * `<redacted>` placeholder matches none of the detectors and is too
69462
+ * short for the generic sweep, so re-running leaves the text unchanged.
69463
+ *
69464
+ * Accepts `unknown` on purpose: callers feed it diagnostic `message` /
69465
+ * `help` that originate from oxlint JSON, which is only shape-checked at
69466
+ * the top level (the per-field `string` types are assumed, not validated).
69467
+ * A malformed non-string value returns `""` instead of throwing on
69468
+ * `.replace`, so one bad diagnostic can't abort parsing the whole batch.
69469
+ */
69470
+ const redactSensitiveText = (text) => {
69471
+ if (typeof text !== "string" || text === "") return "";
69472
+ let redacted = text;
69473
+ for (const rule of KNOWN_SECRET_RULES) redacted = redacted.replace(rule.pattern, rule.replacement);
69474
+ return redactHighEntropyTokens(redacted);
69475
+ };
69476
+ const HOME_DIRECTORY = os.homedir();
69477
+ const USER_HOME_PATTERNS = [/[A-Za-z]:[\\/]Users[\\/][^\\/]+/gi, /(?:\/Users\/|\/home\/)[^/\\]+/gi];
69478
+ const scrubSensitivePaths = (text) => {
69479
+ let scrubbed = text;
69480
+ if (HOME_DIRECTORY.length > 1) scrubbed = scrubbed.split(HOME_DIRECTORY).join("~");
69481
+ for (const pattern of USER_HOME_PATTERNS) scrubbed = scrubbed.replace(pattern, "~");
69482
+ return scrubbed;
69483
+ };
69484
+ /**
69485
+ * Free-text fields can carry both a home-directory path (the OS username) and a
69486
+ * secret/email echoed from user code, so run both scrubbers: strip the username
69487
+ * from paths, then mask any known credential/PII shape. Shared by the Sentry
69488
+ * event scrubber ({@link scrubSentryEvent}), the metric emitter, and the
69489
+ * scrubbing tracer that guards everything bound for OTLP.
69490
+ */
69491
+ const anonymizeText = (text) => redactSensitiveText(scrubSensitivePaths(text));
69492
+ /**
69493
+ * Recursively rewrites every string within an arbitrary value (object / array /
69494
+ * primitive) through {@link anonymizeText}, mutating in place. Used to sweep the
69495
+ * unstructured corners of a Sentry payload (event contexts/extra/tags, breadcrumb
69496
+ * data, span attributes, metric attributes) where a path or secret could hide.
69497
+ */
69498
+ const anonymizeInPlace = (value) => {
69499
+ if (Array.isArray(value)) {
69500
+ for (let index = 0; index < value.length; index += 1) {
69501
+ const item = value[index];
69502
+ if (typeof item === "string") value[index] = anonymizeText(item);
69503
+ else anonymizeInPlace(item);
69504
+ }
69505
+ return;
69506
+ }
69507
+ if (!isPlainObject(value)) return;
69508
+ for (const key of Object.keys(value)) {
69509
+ const inner = value[key];
69510
+ if (typeof inner === "string") value[key] = anonymizeText(inner);
69511
+ else anonymizeInPlace(inner);
69512
+ }
69513
+ };
69514
+ const scrubValue = (value) => typeof value === "string" ? anonymizeText(value) : value;
69515
+ /**
69516
+ * Anonymizes the failure a span ended with.
69517
+ *
69518
+ * The OTLP tracer runs `Cause.prettyErrors` over the exit and exports each
69519
+ * result as `exception.type` / `exception.message` / `exception.stacktrace`.
69520
+ * Stack traces are full of absolute paths, so a failing scan would ship the
69521
+ * user's home directory even though every attribute and event is scrubbed —
69522
+ * this is the one route into the payload that doesn't go through
69523
+ * `span.attribute`.
69524
+ *
69525
+ * Interrupt-only causes are left alone: the exporter emits a fixed
69526
+ * "Interrupted" label for them and never touches the error text.
69527
+ */
69528
+ const scrubExit = (exit) => {
69529
+ if (isSuccess(exit) || hasInterruptsOnly(exit.cause)) return exit;
69530
+ const errors = prettyErrors(exit.cause);
69531
+ if (errors.length === 0) return exit;
69532
+ return failCause$3(errors.map((error) => {
69533
+ const anonymized = new Error(anonymizeText(error.message));
69534
+ anonymized.name = anonymizeText(error.name);
69535
+ anonymized.stack = error.stack === void 0 ? void 0 : anonymizeText(error.stack);
69536
+ return fail$6(anonymized);
69537
+ }).reduce((left, right) => combine(left, right)));
69538
+ };
69539
+ const scrubAttributes = (attributes) => {
69540
+ if (!attributes) return attributes;
69541
+ const scrubbed = {};
69542
+ for (const [key, value] of Object.entries(attributes)) scrubbed[key] = scrubValue(value);
69543
+ return scrubbed;
69544
+ };
69545
+ /**
69546
+ * Wraps a {@link Tracer.Span} so every value that reaches the exporter has been
69547
+ * run through {@link anonymizeText} first.
69548
+ *
69549
+ * Delegation is explicit rather than prototype-based: the wrapper forwards each
69550
+ * method to the real span, so the exporter keeps tracking the span it created
69551
+ * and no call depends on `this` binding surviving the indirection.
69552
+ */
69553
+ const wrapSpan = (span) => ({
69554
+ _tag: "Span",
69555
+ get name() {
69556
+ return span.name;
69557
+ },
69558
+ get spanId() {
69559
+ return span.spanId;
69560
+ },
69561
+ get traceId() {
69562
+ return span.traceId;
69563
+ },
69564
+ get parent() {
69565
+ return span.parent;
69566
+ },
69567
+ get annotations() {
69568
+ return span.annotations;
69569
+ },
69570
+ get status() {
69571
+ return span.status;
69572
+ },
69573
+ get attributes() {
69574
+ return span.attributes;
69575
+ },
69576
+ get links() {
69577
+ return span.links;
69578
+ },
69579
+ get sampled() {
69580
+ return span.sampled;
69581
+ },
69582
+ get kind() {
69583
+ return span.kind;
69584
+ },
69585
+ end: (endTime, exit) => {
69586
+ span.end(endTime, scrubExit(exit));
69587
+ },
69588
+ attribute: (key, value) => {
69589
+ span.attribute(key, scrubValue(value));
69590
+ },
69591
+ event: (name, startTime, attributes) => {
69592
+ span.event(anonymizeText(name), startTime, scrubAttributes(attributes));
69593
+ },
69594
+ addLinks: (links) => {
69595
+ span.addLinks(links);
69596
+ }
69597
+ });
69598
+ /**
69599
+ * Wraps a tracer so span names, attributes, and event payloads are anonymized
69600
+ * on the way to the exporter.
69601
+ *
69602
+ * This is the OTLP replacement for Sentry's `beforeSendTransaction` hook. OTLP
69603
+ * exporters have no equivalent interception point, so without this the only
69604
+ * thing standing between a raw filesystem path and the wire would be every
69605
+ * individual call site remembering to scrub — which is exactly the guarantee
69606
+ * the Sentry scrubber existed to stop relying on. Call sites should still scrub
69607
+ * at the source; this is the backstop for the ones that don't, including any
69608
+ * added later.
69609
+ */
69610
+ const makeScrubbingTracer = (underlying) => make$33({
69611
+ span: (options) => wrapSpan(underlying.span({
69612
+ ...options,
69613
+ name: anonymizeText(options.name)
69614
+ })),
69615
+ ...underlying.context ? { context: underlying.context } : {}
69616
+ });
68227
69617
  const TRACER_PROJECT_NAME = "react-doctor";
69618
+ /**
69619
+ * Replaces an exporter layer's `Tracer` with a scrubbing wrapper around it.
69620
+ *
69621
+ * OTLP has no `beforeSend`-style interception point, so this is the one place
69622
+ * every exported span is guaranteed to pass through. Applied to the telemetry
69623
+ * layers rather than to individual call sites so an attribute added later can't
69624
+ * quietly bypass it.
69625
+ */
69626
+ const withScrubbedSpans = (exporter) => effect(Tracer, map$3(service(Tracer), makeScrubbingTracer)).pipe(provideMerge(exporter));
68228
69627
  const OTEL_ENDPOINT = string("REACT_DOCTOR_OTLP_ENDPOINT").pipe(option);
68229
69628
  const OTEL_AUTH_HEADER = redacted("REACT_DOCTOR_OTLP_AUTH_HEADER").pipe(option);
68230
69629
  /**
68231
- * Opt-in OpenTelemetry layer. The default `Effect.fn(...)` spans
69630
+ * Bring-your-own OpenTelemetry layer. The default `Effect.fn(...)` spans
68232
69631
  * already populate the in-process tracer; this layer plugs an OTLP
68233
69632
  * HTTP exporter into the runtime when the user opts in via:
68234
69633
  *
@@ -68244,7 +69643,7 @@ const OTEL_AUTH_HEADER = redacted("REACT_DOCTOR_OTLP_AUTH_HEADER").pipe(option);
68244
69643
  * inspect / diagnose orchestrators always run, this layer just
68245
69644
  * dictates whether the spans they emit get shipped to a backend.
68246
69645
  */
68247
- const layerOtlp = unwrap$3(gen(function* () {
69646
+ const layerUserOtlp = unwrap$3(gen(function* () {
68248
69647
  const endpoint = yield* OTEL_ENDPOINT;
68249
69648
  const authHeader = yield* OTEL_AUTH_HEADER;
68250
69649
  if (endpoint._tag === "None" || authHeader._tag === "None") return empty$7;
@@ -68256,6 +69655,85 @@ const layerOtlp = unwrap$3(gen(function* () {
68256
69655
  }).pipe(provide$2(layer$8));
68257
69656
  }).pipe(orDie));
68258
69657
  /**
69658
+ * First-party telemetry export to Axiom.
69659
+ *
69660
+ * Composes the tracer and metrics exporters by hand rather than using
69661
+ * `Otlp.layer`, because Axiom routes each signal to a different dataset via a
69662
+ * different header (see `AXIOM_DATASET_HEADER` / `AXIOM_METRICS_DATASET_HEADER`)
69663
+ * and `Otlp.layer` passes one `headers` object to every signal. Logs are
69664
+ * deliberately not wired up — nothing in the engine emits `Effect.log` as
69665
+ * telemetry today.
69666
+ *
69667
+ * Serialization is protobuf, not JSON: Axiom's `/v1/metrics` accepts
69668
+ * `application/x-protobuf` only. `layerProtobuf` ships with Effect and pulls in
69669
+ * no extra dependency.
69670
+ *
69671
+ * Traces and metrics are exposed as separate layers so each signal's exporter
69672
+ * can be reasoned about on its own, and `layerObservability` merges them for the
69673
+ * normal case. Whichever you use, build it **exactly once per process**:
69674
+ * Effect's delta-temporality bookkeeping lives on the metrics exporter
69675
+ * instance, so a second one starts with no previous state and re-reports every
69676
+ * counter's full value as its delta — double-counting everything.
69677
+ */
69678
+ const buildResource = (options) => ({
69679
+ serviceName: TRACER_PROJECT_NAME,
69680
+ serviceVersion: options.serviceVersion
69681
+ });
69682
+ const buildAuthorization = (options) => `Bearer ${value(options.token)}`;
69683
+ const normalizeDomain = (domain) => {
69684
+ let end = domain.length;
69685
+ while (end > 0 && domain.charCodeAt(end - 1) === 47) end -= 1;
69686
+ return domain.slice(0, end);
69687
+ };
69688
+ const layerAxiomTraces = (options) => layer$4({
69689
+ url: `${normalizeDomain(options.domain)}${AXIOM_TRACES_PATH}`,
69690
+ resource: buildResource(options),
69691
+ headers: {
69692
+ Authorization: buildAuthorization(options),
69693
+ [AXIOM_DATASET_HEADER]: options.tracesDataset
69694
+ },
69695
+ exportInterval: options.exportIntervalMs ?? 6e5,
69696
+ shutdownTimeout: TELEMETRY_SHUTDOWN_TIMEOUT_MS
69697
+ }).pipe(provide$2(layerProtobuf), provide$2(layer$8), withScrubbedSpans);
69698
+ /**
69699
+ * Metrics exporter. Instantiate **once per process** — see the double-counting
69700
+ * note above. Delta temporality is still the right choice for a short-lived CLI:
69701
+ * cumulative reports totals since a fixed start time, which for a process that
69702
+ * lives a few seconds is just the run total reported against a meaningless
69703
+ * window.
69704
+ */
69705
+ const layerAxiomMetrics = (options) => layer$5({
69706
+ url: `${normalizeDomain(options.domain)}${AXIOM_METRICS_PATH}`,
69707
+ resource: buildResource(options),
69708
+ headers: {
69709
+ Authorization: buildAuthorization(options),
69710
+ [AXIOM_METRICS_DATASET_HEADER]: options.metricsDataset
69711
+ },
69712
+ exportInterval: options.exportIntervalMs ?? 6e5,
69713
+ shutdownTimeout: TELEMETRY_SHUTDOWN_TIMEOUT_MS,
69714
+ temporality: "delta"
69715
+ }).pipe(provide$2(layerProtobuf), provide$2(layer$8));
69716
+ /**
69717
+ * The first-party telemetry layer: spans and metrics together.
69718
+ *
69719
+ * A user-configured OTLP endpoint wins over first-party Axiom: Effect has one
69720
+ * `Tracer` reference, so the two are mutually exclusive, and someone who went to
69721
+ * the trouble of pointing React Doctor at their own collector meant it.
69722
+ * Passing `null` (or omitting options) disables first-party export entirely,
69723
+ * which is how `--no-telemetry` and every library consumer stay silent.
69724
+ *
69725
+ * Build this **once per process** and share the resulting context — see
69726
+ * `telemetry-runtime.ts`. Effect keeps delta-temporality state on the metrics
69727
+ * exporter instance, so a second build starts with no previous snapshot and
69728
+ * re-reports every counter's full value, doubling every metric.
69729
+ */
69730
+ const layerObservability = (options = null) => unwrap$3(gen(function* () {
69731
+ const endpoint = yield* OTEL_ENDPOINT;
69732
+ const authHeader = yield* OTEL_AUTH_HEADER;
69733
+ if (endpoint._tag !== "None" && authHeader._tag !== "None") return layerUserOtlp;
69734
+ return options === null ? empty$7 : merge$3(layerAxiomTraces(options), layerAxiomMetrics(options));
69735
+ }).pipe(orDie));
69736
+ /**
68259
69737
  * Read a positive-millisecond timeout from an env var, falling back to
68260
69738
  * `defaultMs` when the var is unset, non-finite, or not strictly positive.
68261
69739
  */
@@ -73606,149 +75084,6 @@ const mapPreparedSourceLabels = (labels, preparedSourceMap) => {
73606
75084
  }
73607
75085
  return mappedLabels;
73608
75086
  };
73609
- const REDACTED_PLACEHOLDER = "<redacted>";
73610
- const KEEP_PREFIX = `$1${REDACTED_PLACEHOLDER}`;
73611
- const KNOWN_SECRET_RULES = [
73612
- {
73613
- pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
73614
- replacement: REDACTED_PLACEHOLDER
73615
- },
73616
- {
73617
- pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
73618
- replacement: REDACTED_PLACEHOLDER
73619
- },
73620
- {
73621
- pattern: /(?<=:\/\/)[^\s/:@]+:[^\s/@]+(?=@)/g,
73622
- replacement: REDACTED_PLACEHOLDER
73623
- },
73624
- {
73625
- pattern: /\b(AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA|A3T[A-Z0-9])[0-9A-Z]{16,}/g,
73626
- replacement: KEEP_PREFIX
73627
- },
73628
- {
73629
- pattern: /\b(gh[pousr]_)[A-Za-z0-9]{36,}/g,
73630
- replacement: KEEP_PREFIX
73631
- },
73632
- {
73633
- pattern: /\b(github_pat_)[A-Za-z0-9_]{22,}/g,
73634
- replacement: KEEP_PREFIX
73635
- },
73636
- {
73637
- pattern: /\b(glpat-)[A-Za-z0-9_-]{20,}/g,
73638
- replacement: KEEP_PREFIX
73639
- },
73640
- {
73641
- pattern: /\b(xox[baprs]-)[A-Za-z0-9-]{10,}/g,
73642
- replacement: KEEP_PREFIX
73643
- },
73644
- {
73645
- pattern: /(?<=hooks\.slack\.com\/services\/)[A-Za-z0-9/+_-]{20,}/g,
73646
- replacement: REDACTED_PLACEHOLDER
73647
- },
73648
- {
73649
- pattern: /\b((?:sk|rk)_(?:live|test)_)[0-9A-Za-z]{10,}/g,
73650
- replacement: KEEP_PREFIX
73651
- },
73652
- {
73653
- pattern: /\b(sk-(?:proj-|ant-)?)[A-Za-z0-9_-]{20,}/g,
73654
- replacement: KEEP_PREFIX
73655
- },
73656
- {
73657
- pattern: /\b(AIza)[0-9A-Za-z_-]{35,}/g,
73658
- replacement: KEEP_PREFIX
73659
- },
73660
- {
73661
- pattern: /\b(ya29\.)[0-9A-Za-z_-]{20,}/g,
73662
- replacement: KEEP_PREFIX
73663
- },
73664
- {
73665
- pattern: /\b(npm_)[A-Za-z0-9]{36,}/g,
73666
- replacement: KEEP_PREFIX
73667
- },
73668
- {
73669
- pattern: /\b(SG\.)[A-Za-z0-9_-]{22,}\.[A-Za-z0-9_-]{43,}/g,
73670
- replacement: KEEP_PREFIX
73671
- },
73672
- {
73673
- pattern: /\b(SK)[0-9a-fA-F]{32,}/g,
73674
- replacement: KEEP_PREFIX
73675
- },
73676
- {
73677
- pattern: /\b(dop_v1_)[a-f0-9]{64,}/g,
73678
- replacement: KEEP_PREFIX
73679
- },
73680
- {
73681
- pattern: /\b(shp(?:at|ca|pa|ss)_)[a-fA-F0-9]{32,}/g,
73682
- replacement: KEEP_PREFIX
73683
- },
73684
- {
73685
- pattern: /\b(sq0[a-z]{3}-)[0-9A-Za-z_-]{22,}/g,
73686
- replacement: KEEP_PREFIX
73687
- },
73688
- {
73689
- pattern: /\b([0-9]{8,10}:AA)[0-9A-Za-z_-]{32,}/g,
73690
- replacement: KEEP_PREFIX
73691
- },
73692
- {
73693
- pattern: /(?<=\bBearer\s)[A-Za-z0-9._~+/=-]{16,}/g,
73694
- replacement: REDACTED_PLACEHOLDER
73695
- },
73696
- {
73697
- pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
73698
- replacement: REDACTED_PLACEHOLDER
73699
- }
73700
- ];
73701
- const CANDIDATE_TOKEN_PATTERN = /[A-Za-z0-9_][A-Za-z0-9_-]*/g;
73702
- const HEX_DIGEST_PATTERN = /^(?:[0-9a-f]{32}|[0-9a-f]{40}|[0-9a-f]{64})$/;
73703
- const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
73704
- const HAS_LETTER_PATTERN = /[A-Za-z]/;
73705
- const HAS_DIGIT_PATTERN = /[0-9]/;
73706
- const shannonEntropyBits = (value) => {
73707
- const counts = /* @__PURE__ */ new Map();
73708
- for (const char of value) counts.set(char, (counts.get(char) ?? 0) + 1);
73709
- let bits = 0;
73710
- for (const count of counts.values()) {
73711
- const probability = count / value.length;
73712
- bits -= probability * Math.log2(probability);
73713
- }
73714
- return bits;
73715
- };
73716
- const looksLikeHighEntropySecret = (token) => {
73717
- if (token.length < 32) return false;
73718
- if (!HAS_LETTER_PATTERN.test(token) || !HAS_DIGIT_PATTERN.test(token)) return false;
73719
- if (HEX_DIGEST_PATTERN.test(token) || UUID_PATTERN.test(token)) return false;
73720
- return shannonEntropyBits(token) >= 3;
73721
- };
73722
- const redactHighEntropyTokens = (text) => text.replace(CANDIDATE_TOKEN_PATTERN, (token) => looksLikeHighEntropySecret(token) ? REDACTED_PLACEHOLDER : token);
73723
- /**
73724
- * Masks API keys, tokens, private keys, credentialed URLs, and emails
73725
- * found anywhere inside a free-text string, returning the scrubbed text.
73726
- * Applied to every diagnostic's `message` / `help` at construction time
73727
- * so secrets never reach the terminal, the JSON report, or the score
73728
- * API — react-doctor must never echo or transmit a user's secrets.
73729
- *
73730
- * Provider tokens keep their non-secret, type-identifying prefix (e.g.
73731
- * `sk_live_<redacted>`, `ghp_<redacted>`, `AKIA<redacted>`) so the leaked
73732
- * credential's type stays visible; structural or unknown-format secrets
73733
- * with no meaningful prefix are masked whole.
73734
- *
73735
- * Runs the high-precision known-shape detectors first, then a generic
73736
- * entropy-gated sweep for unknown-format secrets. Idempotent: the inert
73737
- * `<redacted>` placeholder matches none of the detectors and is too
73738
- * short for the generic sweep, so re-running leaves the text unchanged.
73739
- *
73740
- * Accepts `unknown` on purpose: callers feed it diagnostic `message` /
73741
- * `help` that originate from oxlint JSON, which is only shape-checked at
73742
- * the top level (the per-field `string` types are assumed, not validated).
73743
- * A malformed non-string value returns `""` instead of throwing on
73744
- * `.replace`, so one bad diagnostic can't abort parsing the whole batch.
73745
- */
73746
- const redactSensitiveText = (text) => {
73747
- if (typeof text !== "string" || text === "") return "";
73748
- let redacted = text;
73749
- for (const rule of KNOWN_SECRET_RULES) redacted = redacted.replace(rule.pattern, rule.replacement);
73750
- return redactHighEntropyTokens(redacted);
73751
- };
73752
75087
  const REACT_MODULE_SOURCE = "react";
73753
75088
  const REQUIRE_IDENTIFIER = "require";
73754
75089
  const USE_IDENTIFIER = "use";
@@ -75828,7 +77163,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
75828
77163
  static layerComposite = (backends) => succeed$3(Linter, Linter.of({ run: (input) => {
75829
77164
  if (backends.length === 0) return empty$4;
75830
77165
  let diagnostics = backends[0].run(input);
75831
- for (let index = 1; index < backends.length; index++) diagnostics = diagnostics.pipe(concat(backends[index].run(input)));
77166
+ for (let index = 1; index < backends.length; index++) diagnostics = diagnostics.pipe(concat$1(backends[index].run(input)));
75832
77167
  return diagnostics;
75833
77168
  } }));
75834
77169
  };
@@ -75942,14 +77277,6 @@ var Reporter = class Reporter extends Service()("react-doctor/Reporter") {
75942
77277
  });
75943
77278
  }));
75944
77279
  };
75945
- const HOME_DIRECTORY = os.homedir();
75946
- const USER_HOME_PATTERNS = [/[A-Za-z]:[\\/]Users[\\/][^\\/]+/gi, /(?:\/Users\/|\/home\/)[^/\\]+/gi];
75947
- const scrubSensitivePaths = (text) => {
75948
- let scrubbed = text;
75949
- if (HOME_DIRECTORY.length > 1) scrubbed = scrubbed.split(HOME_DIRECTORY).join("~");
75950
- for (const pattern of USER_HOME_PATTERNS) scrubbed = scrubbed.replace(pattern, "~");
75951
- return scrubbed;
75952
- };
75953
77280
  const ScoreApiResponseSchema = Struct({
75954
77281
  score: Number$1,
75955
77282
  label: String$1,
@@ -76911,7 +78238,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
76911
78238
  suppressedRuleCounts: transform.summarizeSuppressions()
76912
78239
  };
76913
78240
  }).pipe(withSpan("runInspect", { attributes: {
76914
- "inspect.directory": input.directory,
78241
+ "inspect.directory": scrubSensitivePaths(input.directory),
76915
78242
  "inspect.includePathCount": input.includePaths.length,
76916
78243
  "inspect.runDeadCode": input.runDeadCode,
76917
78244
  "inspect.isCi": input.isCi,
@@ -77210,7 +78537,7 @@ const runEditorScanEffect = (input) => gen(function* () {
77210
78537
  skipExplicitIncludePathFilter: true
77211
78538
  }).pipe(provide(layers))));
77212
78539
  }).pipe(withSpan("runEditorScan"));
77213
- const runEditorScan = (input) => runPromise(runEditorScanEffect(input).pipe(provide(layerOtlp)));
78540
+ const runEditorScan = (input) => runPromise(runEditorScanEffect(input).pipe(provide(layerUserOtlp)));
77214
78541
  const lookupOwnRule = (rule) => Object.hasOwn(REACT_DOCTOR_RULE_REGISTRY, rule) ? REACT_DOCTOR_RULE_REGISTRY[rule] : void 0;
77215
78542
  /**
77216
78543
  * Returns presentation metadata for `<plugin>/<rule>`, or `null` when the
@@ -77250,7 +78577,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
77250
78577
  /** Display name used in client-facing messages and progress titles. */
77251
78578
  const SERVER_DISPLAY_NAME = "React Doctor";
77252
78579
  /** Server version reported in `serverInfo`; injected at build, `dev` from source. */
77253
- const SERVER_VERSION = "0.9.9";
78580
+ const SERVER_VERSION = "0.9.10";
77254
78581
  /** `Diagnostic.source` shown next to every published diagnostic. */
77255
78582
  const DIAGNOSTIC_SOURCE = "react-doctor";
77256
78583
  /**
@@ -78956,7 +80283,29 @@ const startLanguageServer$1 = (options = {}) => {
78956
80283
  installProcessGuards(connection);
78957
80284
  createServer(connection, options);
78958
80285
  };
80286
+ const NANOSECONDS_PER_MILLISECOND = 1000000n;
80287
+ const LSP_TELEMETRY_EXPORT_INTERVAL_MS = 6e4;
78959
80288
  const SENTRY_FLUSH_TIMEOUT_MS = 2e3;
80289
+ const METRIC_DISTRIBUTION_BOUNDARIES = [
80290
+ 0,
80291
+ 1,
80292
+ 2,
80293
+ 5,
80294
+ 10,
80295
+ 25,
80296
+ 50,
80297
+ 100,
80298
+ 250,
80299
+ 500,
80300
+ 1e3,
80301
+ 2500,
80302
+ 5e3,
80303
+ 1e4,
80304
+ 3e4,
80305
+ 6e4,
80306
+ 3e5,
80307
+ 6e5
80308
+ ];
78960
80309
  const METRIC = {
78961
80310
  cliInvoked: "cli.invoked",
78962
80311
  cliError: "cli.error",
@@ -79028,36 +80377,399 @@ const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g
79028
80377
  //#region src/cli/utils/is-env-flag-enabled.ts
79029
80378
  const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
79030
80379
  //#endregion
79031
- //#region src/cli/utils/anonymize-text.ts
80380
+ //#region src/cli/utils/is-telemetry-enabled.ts
79032
80381
  /**
79033
- * Free-text fields can carry both a home-directory path (the OS username) and a
79034
- * secret/email echoed from user code, so run both scrubbers: strip the username
79035
- * from paths, then mask any known credential/PII shape. Shared by the Sentry
79036
- * event scrubber ({@link scrubSentryEvent}) and metric scrubber
79037
- * ({@link scrubSentryMetric}).
80382
+ * Whether this process may emit telemetry of any kind — Sentry crash reports and
80383
+ * Axiom traces/metrics alike.
80384
+ *
80385
+ * Reads raw `process.argv` rather than parsed Commander options because the
80386
+ * decision is needed before the CLI parses its arguments (Sentry initializes as
80387
+ * the first statement of `cli/index.ts`).
80388
+ *
80389
+ * `REACT_DOCTOR_NO_TELEMETRY` was previously honored only by the language
80390
+ * server. It applies to the CLI too, which is what lets this repo's own tooling
80391
+ * — the benchmark harness and the delta-audit runner — keep their scans out of
80392
+ * production telemetry now that disabling Sentry alone no longer silences
80393
+ * everything.
79038
80394
  */
79039
- const anonymizeText = (text) => redactSensitiveText(scrubSensitivePaths(text));
80395
+ const isTelemetryEnabled = () => {
80396
+ if (process.argv.includes("--no-score") || process.argv.includes("--no-telemetry")) return false;
80397
+ if (isEnvFlagEnabled(process.env.REACT_DOCTOR_NO_TELEMETRY)) return false;
80398
+ if (process.env.VITEST || process.env.NODE_ENV === "test") return false;
80399
+ return true;
80400
+ };
80401
+ //#endregion
80402
+ //#region src/cli/utils/is-ci-environment.ts
80403
+ const CI_ENVIRONMENT_VARIABLES = [
80404
+ "GITHUB_ACTIONS",
80405
+ "GITLAB_CI",
80406
+ "CIRCLECI"
80407
+ ];
80408
+ const CI_PROVIDER_BY_ENVIRONMENT_VARIABLE = [
80409
+ ["GITHUB_ACTIONS", "github-actions"],
80410
+ ["GITLAB_CI", "gitlab-ci"],
80411
+ ["CIRCLECI", "circleci"],
80412
+ ["BUILDKITE", "buildkite"],
80413
+ ["JENKINS_URL", "jenkins"],
80414
+ ["TF_BUILD", "azure-pipelines"],
80415
+ ["CODEBUILD_BUILD_ID", "aws-codebuild"],
80416
+ ["TEAMCITY_VERSION", "teamcity"],
80417
+ ["BITBUCKET_BUILD_NUMBER", "bitbucket"],
80418
+ ["TRAVIS", "travis"],
80419
+ ["DRONE", "drone"],
80420
+ ["SEMAPHORE", "semaphore"],
80421
+ ["APPVEYOR", "appveyor"],
80422
+ ["HARNESS_BUILD_ID", "harness"],
80423
+ ["BUDDY", "buddy"],
80424
+ ["CF_BUILD_ID", "codefresh"],
80425
+ ["NETLIFY", "netlify"],
80426
+ ["RAILWAY_SERVICE_ID", "railway"],
80427
+ ["VERCEL", "vercel"],
80428
+ ["CM_BUILD_ID", "codemagic"],
80429
+ ["PROW_JOB_ID", "prow"],
80430
+ ["AGOLA_GIT_REF", "agola"],
80431
+ ["CIRRUS_CI", "cirrus-ci"],
80432
+ ["BLACKSMITH_STICKYDISK_TOKEN", "blacksmith"],
80433
+ ["WARPBUILD_RUNNER_VERIFICATION_TOKEN", "warpbuild"],
80434
+ ["NSC_CACHE_PATH", "namespace"],
80435
+ ["UBICLOUD_CACHE_URL", "ubicloud"],
80436
+ ["RENDER", "render"],
80437
+ ["FLY_APP_NAME", "fly-io"]
80438
+ ];
80439
+ const GITHUB_ACTION_MARKER_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_GITHUB_ACTION";
80440
+ const CODING_AGENT_BY_ENVIRONMENT_VARIABLE = [
80441
+ ["CLAUDECODE", "claude-code"],
80442
+ ["CLAUDE_CODE", "claude-code"],
80443
+ ["CURSOR_AGENT", "cursor"],
80444
+ ["CODEX_CI", "codex"],
80445
+ ["CODEX_SANDBOX", "codex"],
80446
+ ["CODEX_SANDBOX_NETWORK_DISABLED", "codex"],
80447
+ ["OPENCODE", "opencode"],
80448
+ ["GOOSE_TERMINAL", "goose"],
80449
+ ["AMP_THREAD_ID", "amp"],
80450
+ ["CLINE_ACTIVE", "cline"],
80451
+ ["AUGMENT_AGENT", "augment"],
80452
+ ["TRAE_AI_SHELL_ID", "trae-ai"]
80453
+ ];
80454
+ const GENERIC_CODING_AGENT_ENVIRONMENT_VARIABLES = ["AGENT_SESSION_ID", "AGENT_THREAD_ID"];
80455
+ const CODING_AGENT_ENVIRONMENT_VALUE_VARIABLES = ["AGENT"];
80456
+ const CODING_AGENT_ENVIRONMENT_VALUES = { AGENT: ["amp", "goose"] };
80457
+ [...CODING_AGENT_BY_ENVIRONMENT_VARIABLE.map(([environmentVariable]) => environmentVariable), ...GENERIC_CODING_AGENT_ENVIRONMENT_VARIABLES];
80458
+ const FALSY_CI_FLAG_VALUES = new Set([
80459
+ "",
80460
+ "0",
80461
+ "false"
80462
+ ]);
80463
+ const isCiFlagSet = (value) => value !== void 0 && !FALSY_CI_FLAG_VALUES.has(value.toLowerCase());
80464
+ const isCiEnvironment = (env = process.env) => CI_ENVIRONMENT_VARIABLES.some((environmentVariable) => Boolean(env[environmentVariable])) || isCiFlagSet(env.CI);
80465
+ const detectCiProvider = () => {
80466
+ for (const [environmentVariable, provider] of CI_PROVIDER_BY_ENVIRONMENT_VARIABLE) if (process.env[environmentVariable]) return provider;
80467
+ return isCiFlagSet(process.env.CI) ? "unknown" : null;
80468
+ };
80469
+ const isOfficialGithubAction = () => Boolean(process.env[GITHUB_ACTION_MARKER_ENVIRONMENT_VARIABLE]);
80470
+ const detectCiEventName = () => process.env.GITHUB_EVENT_NAME?.trim() || null;
80471
+ const detectCodingAgentFromValue = () => {
80472
+ for (const environmentVariable of CODING_AGENT_ENVIRONMENT_VALUE_VARIABLES) {
80473
+ const value = process.env[environmentVariable]?.toLowerCase();
80474
+ if (value && CODING_AGENT_ENVIRONMENT_VALUES[environmentVariable].includes(value)) return value;
80475
+ }
80476
+ return null;
80477
+ };
80478
+ const detectCodingAgent = () => {
80479
+ for (const [environmentVariable, agent] of CODING_AGENT_BY_ENVIRONMENT_VARIABLE) if (process.env[environmentVariable]) return agent;
80480
+ const agentFromValue = detectCodingAgentFromValue();
80481
+ if (agentFromValue) return agentFromValue;
80482
+ if (GENERIC_CODING_AGENT_ENVIRONMENT_VARIABLES.some((environmentVariable) => process.env[environmentVariable])) return "unknown";
80483
+ return null;
80484
+ };
80485
+ const isCodingAgentEnvironment = () => detectCodingAgent() !== null;
80486
+ //#endregion
80487
+ //#region src/cli/utils/detect-terminal-kind.ts
80488
+ const TERMINAL_BY_TERM_PROGRAM = [
80489
+ ["vscode", "vscode"],
80490
+ ["iTerm.app", "iterm"],
80491
+ ["Apple_Terminal", "apple-terminal"],
80492
+ ["WezTerm", "wezterm"],
80493
+ ["ghostty", "ghostty"],
80494
+ ["Hyper", "hyper"],
80495
+ ["Tabby", "tabby"],
80496
+ ["rio", "rio"]
80497
+ ];
79040
80498
  /**
79041
- * Recursively rewrites every string within an arbitrary value (object / array /
79042
- * primitive) through {@link anonymizeText}, mutating in place. Used to sweep the
79043
- * unstructured corners of a Sentry payload (event contexts/extra/tags, breadcrumb
79044
- * data, span attributes, metric attributes) where a path or secret could hide.
80499
+ * Best-effort label for the terminal emulator / editor hosting the CLI,
80500
+ * derived from terminal-identity env vars. Recorded as the `terminalKind` run
80501
+ * tag so we can see where React Doctor is actually run (nvim, VS Code, iTerm,
80502
+ * …) — the split Sentry can't otherwise see. Low-cardinality and free of any
80503
+ * username/path/secret, so it's safe as a tag. Editor terminals (nvim/vim)
80504
+ * win over the outer emulator because that's the surface a user is reading in;
80505
+ * "ci" marks a run with no interactive terminal; "unknown" when nothing matches.
80506
+ */
80507
+ const detectTerminalKind = (env = process.env) => {
80508
+ if (env.NVIM) return "neovim";
80509
+ if (env.VIM_TERMINAL) return "vim";
80510
+ const termProgram = env.TERM_PROGRAM;
80511
+ if (termProgram) {
80512
+ for (const [marker, label] of TERMINAL_BY_TERM_PROGRAM) if (termProgram === marker) return label;
80513
+ }
80514
+ if (env.KITTY_WINDOW_ID || env.TERM === "xterm-kitty") return "kitty";
80515
+ if (env.WT_SESSION) return "windows-terminal";
80516
+ if (env.ALACRITTY_WINDOW_ID || env.TERM === "alacritty") return "alacritty";
80517
+ if (env.VTE_VERSION) return "vte";
80518
+ if (env.TMUX) return "tmux";
80519
+ if (isCiEnvironment(env)) return "ci";
80520
+ return "unknown";
80521
+ };
80522
+ //#endregion
80523
+ //#region src/cli/utils/is-debug-flag.ts
80524
+ /**
80525
+ * Whether the user passed `--debug` (surface the run's Sentry trace id, and
80526
+ * force performance tracing on so there's a trace to surface). Read straight
80527
+ * from argv rather than Commander's parsed flags because `initializeSentry()`
80528
+ * runs before Commander parses — the same reason `shouldEnableSentry()` reads
80529
+ * `--no-score` from argv. Sharing this one reader keeps the init-time sampling
80530
+ * override and the end-of-run print in agreement.
79045
80531
  */
79046
- const anonymizeInPlace = (value) => {
79047
- if (Array.isArray(value)) {
79048
- for (let index = 0; index < value.length; index += 1) {
79049
- const item = value[index];
79050
- if (typeof item === "string") value[index] = anonymizeText(item);
79051
- else anonymizeInPlace(item);
79052
- }
79053
- return;
80532
+ const isDebugFlagEnabled = (argv = process.argv) => argv.includes("--debug");
80533
+ //#endregion
80534
+ //#region src/cli/utils/is-git-hook-environment.ts
80535
+ const isGitHookEnvironment = () => Boolean(process.env.GIT_DIR);
80536
+ //#endregion
80537
+ //#region src/cli/utils/is-non-interactive-environment.ts
80538
+ const NON_INTERACTIVE_ENVIRONMENT_VARIABLES = [
80539
+ "CI",
80540
+ "GITHUB_ACTIONS",
80541
+ "GITLAB_CI",
80542
+ "BUILDKITE",
80543
+ "JENKINS_URL",
80544
+ "TF_BUILD",
80545
+ "CODEBUILD_BUILD_ID",
80546
+ "TEAMCITY_VERSION",
80547
+ "BITBUCKET_BUILD_NUMBER",
80548
+ "CIRCLECI",
80549
+ "TRAVIS",
80550
+ "DRONE",
80551
+ "GIT_DIR"
80552
+ ];
80553
+ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.some((envVariable) => Boolean(process.env[envVariable])) || isCodingAgentEnvironment();
80554
+ //#endregion
80555
+ //#region src/cli/utils/version.ts
80556
+ const VERSION = process.env.VERSION ?? "0.0.0";
80557
+ //#endregion
80558
+ //#region src/cli/utils/json-mode.ts
80559
+ let context = null;
80560
+ const isJsonModeActive = () => context !== null;
80561
+ //#endregion
80562
+ //#region src/cli/utils/run-id.ts
80563
+ let cachedRunId;
80564
+ const getRunId = () => {
80565
+ cachedRunId ??= randomUUID();
80566
+ return cachedRunId;
80567
+ };
80568
+ //#endregion
80569
+ //#region src/cli/utils/build-run-context.ts
80570
+ const LSP_COMMAND = "experimental-lsp";
80571
+ const ROOT_SUBCOMMANDS = new Set([
80572
+ "design",
80573
+ "install",
80574
+ "setup",
80575
+ LSP_COMMAND
80576
+ ]);
80577
+ const detectInvokedVia = () => {
80578
+ const userAgent = process.env.npm_config_user_agent;
80579
+ if (!userAgent) return "unknown";
80580
+ return userAgent.split("/", 1)[0]?.trim() || "unknown";
80581
+ };
80582
+ const detectNodeMajor = () => {
80583
+ const major = Number.parseInt(process.versions.node.split(".", 1)[0] ?? "", 10);
80584
+ return Number.isNaN(major) ? 0 : major;
80585
+ };
80586
+ const detectOrigin = (userArguments) => {
80587
+ if (detectCommand(userArguments) === LSP_COMMAND) return "lsp";
80588
+ if (isGitHookEnvironment()) return "git-hook";
80589
+ if (isCodingAgentEnvironment()) return "agent";
80590
+ if (isCiEnvironment()) return "ci";
80591
+ return "cli";
80592
+ };
80593
+ const detectCommand = (userArguments) => {
80594
+ for (const argument of userArguments) {
80595
+ if (argument === "--") break;
80596
+ if (argument.startsWith("-")) continue;
80597
+ return ROOT_SUBCOMMANDS.has(argument) ? argument : "inspect";
80598
+ }
80599
+ return "inspect";
80600
+ };
80601
+ /**
80602
+ * Snapshot of the current invocation, attached to Sentry events as the
80603
+ * `run` context to make crashes triage-able (which version, platform,
80604
+ * CI/agent, how it was invoked). Every field is cheap, synchronous, and
80605
+ * safe to read at any point — cwd reads fall back, env reads are
80606
+ * booleans — so it's rebuilt lazily at capture time when runtime-only
80607
+ * signals like `jsonMode` are finally known.
80608
+ */
80609
+ const buildRunContext = () => {
80610
+ const userArguments = process.argv.slice(2);
80611
+ return {
80612
+ version: VERSION,
80613
+ runId: getRunId(),
80614
+ origin: detectOrigin(userArguments),
80615
+ command: detectCommand(userArguments),
80616
+ argv: scrubSensitivePaths(userArguments.join(" ")),
80617
+ cwd: scrubSensitivePaths(process.cwd()),
80618
+ node: process.version,
80619
+ nodeMajor: detectNodeMajor(),
80620
+ platform: process.platform,
80621
+ arch: process.arch,
80622
+ ci: isCiEnvironment(),
80623
+ ciProvider: detectCiProvider(),
80624
+ eventName: detectCiEventName(),
80625
+ viaAction: isOfficialGithubAction(),
80626
+ codingAgent: detectCodingAgent(),
80627
+ interactive: !isNonInteractiveEnvironment(),
80628
+ terminalKind: detectTerminalKind(),
80629
+ jsonMode: isJsonModeActive(),
80630
+ debug: isDebugFlagEnabled(),
80631
+ invokedVia: detectInvokedVia(),
80632
+ lintBatchOrdering: resolveLintBatchOrdering()
80633
+ };
80634
+ };
80635
+ //#endregion
80636
+ //#region src/cli/utils/build-sentry-project-context.ts
80637
+ /**
80638
+ * Projects the {@link ProjectInfo} we already detect during a scan into the
80639
+ * Sentry scope shape: a handful of searchable `project.*` tags plus the
80640
+ * anonymous project *shape* as a `project` context block. Lets crash/transaction
80641
+ * triage answer "which kind of project hit this?" (framework, React/Expo
80642
+ * version, TypeScript, size) without sending source code — and deliberately
80643
+ * omits `projectName` and `rootDirectory`, the two identifying fields, so the
80644
+ * project can't be tied back to a specific company/repo.
80645
+ */
80646
+ const buildSentryProjectContext = (projectInfo) => ({
80647
+ tags: {
80648
+ "project.framework": projectInfo.framework,
80649
+ "project.reactMajor": projectInfo.reactMajorVersion,
80650
+ "project.typescript": projectInfo.hasTypeScript,
80651
+ "project.reactCompiler": projectInfo.hasReactCompiler,
80652
+ "project.expo": projectInfo.expoVersion !== null,
80653
+ "project.reactNative": projectInfo.hasReactNativeWorkspace
80654
+ },
80655
+ context: {
80656
+ framework: projectInfo.framework,
80657
+ reactVersion: projectInfo.reactVersion,
80658
+ reactMajorVersion: projectInfo.reactMajorVersion,
80659
+ hasTypeScript: projectInfo.hasTypeScript,
80660
+ hasReactCompiler: projectInfo.hasReactCompiler,
80661
+ tanstackQueryVersion: projectInfo.tanstackQueryVersion,
80662
+ mobxVersion: projectInfo.mobxVersion,
80663
+ styledComponentsVersion: projectInfo.styledComponentsVersion,
80664
+ tailwindVersion: projectInfo.tailwindVersion,
80665
+ zodVersion: projectInfo.zodVersion,
80666
+ preactVersion: projectInfo.preactVersion,
80667
+ hasReactNativeWorkspace: projectInfo.hasReactNativeWorkspace,
80668
+ expoVersion: projectInfo.expoVersion,
80669
+ hasReanimated: projectInfo.hasReanimated,
80670
+ sourceFileCount: projectInfo.sourceFileCount
79054
80671
  }
79055
- if (!isPlainObject(value)) return;
79056
- for (const key of Object.keys(value)) {
79057
- const inner = value[key];
79058
- if (typeof inner === "string") value[key] = anonymizeText(inner);
79059
- else anonymizeInPlace(inner);
80672
+ });
80673
+ let currentProjectInfo = null;
80674
+ const getSentryProjectInfo = () => currentProjectInfo;
80675
+ //#endregion
80676
+ //#region src/cli/utils/build-sentry-scope.ts
80677
+ /**
80678
+ * Projects a {@link RunContext} snapshot (plus the current run's
80679
+ * {@link getSentryProjectInfo project info}, when a scan has discovered it) into
80680
+ * the Sentry scope shape — the searchable `tags` that make crashes/transactions
80681
+ * filterable (which command, origin, CI provider, coding agent, Node major,
80682
+ * package manager, project framework/React major) plus the full `run` and
80683
+ * `project` context blocks for deep triage.
80684
+ *
80685
+ * Shared by `instrument.ts` (seeded as `initialScope` so *every* event,
80686
+ * including performance transactions, carries it) and `report-error.ts` (a
80687
+ * capture-time refresh, since runtime-only signals like `jsonMode` and the
80688
+ * scanned project are only known once a command has begun).
80689
+ */
80690
+ const buildSentryScope = (runContext = buildRunContext()) => {
80691
+ const tags = {
80692
+ origin: runContext.origin,
80693
+ command: runContext.command,
80694
+ ci: runContext.ci,
80695
+ ciProvider: runContext.ciProvider,
80696
+ eventName: runContext.eventName,
80697
+ viaAction: runContext.viaAction,
80698
+ codingAgent: runContext.codingAgent,
80699
+ interactive: runContext.interactive,
80700
+ terminalKind: runContext.terminalKind,
80701
+ jsonMode: runContext.jsonMode,
80702
+ debug: runContext.debug,
80703
+ invokedVia: runContext.invokedVia,
80704
+ nodeMajor: runContext.nodeMajor,
80705
+ lintBatchOrdering: runContext.lintBatchOrdering
80706
+ };
80707
+ const contexts = { run: { ...runContext } };
80708
+ const projectInfo = getSentryProjectInfo();
80709
+ if (projectInfo) {
80710
+ const project = buildSentryProjectContext(projectInfo);
80711
+ Object.assign(tags, project.tags);
80712
+ contexts.project = project.context;
79060
80713
  }
80714
+ return {
80715
+ tags,
80716
+ contexts
80717
+ };
80718
+ };
80719
+ //#endregion
80720
+ //#region src/cli/utils/to-span-attributes.ts
80721
+ /**
80722
+ * Converts a Sentry tag map (which permits `null`/`undefined` to denote an
80723
+ * absent signal) into Sentry/OTel span attributes, which only accept primitives.
80724
+ * Missing values are dropped rather than coerced, so an absent signal doesn't
80725
+ * become a misleading `"null"` or `"undefined"` attribute.
80726
+ */
80727
+ const toSpanAttributes = (tags) => {
80728
+ const attributes = {};
80729
+ for (const [key, value] of Object.entries(tags)) if (value != null) attributes[key] = value;
80730
+ return attributes;
80731
+ };
80732
+ //#endregion
80733
+ //#region src/cli/utils/record-metric.ts
80734
+ const withRunAttributes = (attributes) => {
80735
+ const merged = {
80736
+ ...toSpanAttributes(buildSentryScope().tags),
80737
+ ...toSpanAttributes(attributes ?? {})
80738
+ };
80739
+ const stringified = {};
80740
+ for (const [key, value] of Object.entries(merged)) stringified[key] = typeof value === "string" ? anonymizeText(value) : String(value);
80741
+ return stringified;
80742
+ };
80743
+ /**
80744
+ * Emits a counter into Effect's metric registry, where the OTLP exporter picks
80745
+ * it up at flush time.
80746
+ *
80747
+ * A guarded, swallow-on-throw no-op unless telemetry is enabled, so it's inert
80748
+ * under `--no-score` / `--no-telemetry`, in tests, and for the programmatic
80749
+ * `@react-doctor/api` library. Counters are independent of trace sampling, so
80750
+ * they are still recorded when tracing is off.
80751
+ */
80752
+ const recordCount = (name, value = 1, attributes) => {
80753
+ if (!isTelemetryEnabled()) return;
80754
+ try {
80755
+ counter(name).pipe(withAttributes(withRunAttributes(attributes))).updateUnsafe(value, empty$8());
80756
+ } catch {}
80757
+ };
80758
+ /**
80759
+ * Emits a distribution (value ranges — durations, sizes, scores). Same gating
80760
+ * and run-attribute handling as {@link recordCount}.
80761
+ *
80762
+ * `unit` is recorded as an attribute rather than as OTLP metric metadata:
80763
+ * Effect's histogram carries no unit field, and keeping the millisecond/count
80764
+ * distinction queryable matters more than where it is stored.
80765
+ */
80766
+ const recordDistribution = (name, value, options = {}) => {
80767
+ if (!isTelemetryEnabled()) return;
80768
+ try {
80769
+ const attributes = withRunAttributes(options.attributes);
80770
+ if (options.unit) attributes.unit = options.unit;
80771
+ histogram(name, { boundaries: METRIC_DISTRIBUTION_BOUNDARIES }).pipe(withAttributes(attributes)).updateUnsafe(value, empty$8());
80772
+ } catch {}
79061
80773
  };
79062
80774
  //#endregion
79063
80775
  //#region src/cli/utils/scrub-sentry-event.ts
@@ -79108,29 +80820,112 @@ const scrubSentryEvent = (event) => {
79108
80820
  }
79109
80821
  };
79110
80822
  //#endregion
79111
- //#region src/cli/utils/scrub-sentry-metric.ts
79112
- /**
79113
- * Anonymizes a Sentry metric before it leaves the machine, mirroring
79114
- * {@link scrubSentryEvent}. Drops the `server.address` default attribute (the
79115
- * hostname) and scrubs home-directory paths + known secrets/emails from every
79116
- * remaining attribute value (metric names are our own constants, so they're
79117
- * left intact to avoid splitting a series). Returns `null` on any failure so an
79118
- * un-anonymized metric is never sent. Wired into `beforeSendMetric`.
79119
- */
79120
- const scrubSentryMetric = (metric) => {
80823
+ //#region src/cli/utils/resolve-axiom-telemetry-options.ts
80824
+ /**
80825
+ * Resolves the credentials for first-party Axiom export, or `null` when this
80826
+ * process must not emit telemetry.
80827
+ *
80828
+ * Core deliberately does not read these itself — keeping the token on the CLI
80829
+ * side is what makes `@react-doctor/api` silent by construction rather than by a
80830
+ * runtime guard.
80831
+ *
80832
+ * Returning `null` for an empty token means an unconfigured build (or a
80833
+ * contributor's checkout without the token baked in) simply doesn't export,
80834
+ * instead of sending unauthenticated requests to Axiom on every run.
80835
+ *
80836
+ * The overrides are `REACT_DOCTOR_`-prefixed rather than the bare `AXIOM_TOKEN`
80837
+ * / `AXIOM_DATASET` names Axiom's own tooling uses. Those are common in the
80838
+ * environment of anyone who already runs Axiom, and reading them here would
80839
+ * silently redirect React Doctor's telemetry into an unrelated account — or
80840
+ * fail against a dataset that isn't shaped for it — without the user ever
80841
+ * asking for that.
80842
+ */
80843
+ const resolveAxiomTelemetryOptions = () => {
80844
+ if (!isTelemetryEnabled()) return null;
80845
+ const token = process.env.REACT_DOCTOR_AXIOM_TOKEN || "xaat-31b59107-855d-4917-8fab-6dc29fb459ce";
80846
+ if (!token) return null;
80847
+ return {
80848
+ token: make$30(token),
80849
+ domain: process.env.REACT_DOCTOR_AXIOM_DOMAIN || "https://api.axiom.co",
80850
+ tracesDataset: process.env.REACT_DOCTOR_AXIOM_DATASET || "react-doctor",
80851
+ metricsDataset: process.env.REACT_DOCTOR_AXIOM_METRICS_DATASET || "react-doctor-metrics",
80852
+ serviceVersion: VERSION
80853
+ };
80854
+ };
80855
+ //#endregion
80856
+ //#region src/cli/utils/telemetry-runtime.ts
80857
+ const isUserOtlpConfigured = () => Boolean(process.env.REACT_DOCTOR_OTLP_ENDPOINT) && Boolean(process.env.REACT_DOCTOR_OTLP_AUTH_HEADER);
80858
+ let telemetryScope = null;
80859
+ let telemetryContext = null;
80860
+ let isBuilt = false;
80861
+ let pendingShutdown = null;
80862
+ /**
80863
+ * Builds the telemetry layer once and hands back its context.
80864
+ *
80865
+ * The CLI is an imperative shell around several separate `Effect.runPromise`
80866
+ * calls — the scan program, the renderer, and the run root span all run
80867
+ * independently. Providing a fresh layer to each would create a separate set of
80868
+ * OTLP exporters per program: each holds its own span buffer and its own
80869
+ * shutdown flush, so exit latency would multiply, the root span would land in a
80870
+ * different export than its children, and — because Effect tracks
80871
+ * delta-temporality state per exporter instance — every counter would be
80872
+ * re-reported in full by each new metrics exporter.
80873
+ *
80874
+ * Building into a long-lived scope instead means one tracer and one metrics
80875
+ * exporter shared by all of them: child spans nest under the run span natively,
80876
+ * with no `ExternalSpan` stitching, and one flush at exit ships both signals.
80877
+ *
80878
+ * Returns `null` when telemetry is disabled or unconfigured, which is what keeps
80879
+ * `--no-telemetry` and unconfigured builds from opening a scope at all.
80880
+ *
80881
+ * `exportIntervalMs` lets a long-running process (the language server) ship
80882
+ * telemetry periodically instead of only at shutdown.
80883
+ */
80884
+ const getTelemetryContext = (overrides = {}) => {
80885
+ if (isBuilt) return telemetryContext;
80886
+ isBuilt = true;
80887
+ if (!isTelemetryEnabled()) return null;
80888
+ const options = resolveAxiomTelemetryOptions();
80889
+ if (options === null && !isUserOtlpConfigured()) return null;
79121
80890
  try {
79122
- if (metric.attributes) {
79123
- delete metric.attributes["server.address"];
79124
- anonymizeInPlace(metric.attributes);
79125
- }
79126
- return metric;
80891
+ const scope = makeUnsafe$5();
80892
+ telemetryContext = runSync(buildWithScope(layerObservability(options === null ? null : {
80893
+ ...options,
80894
+ ...overrides
80895
+ }), scope));
80896
+ telemetryScope = scope;
79127
80897
  } catch {
79128
- return null;
80898
+ telemetryContext = null;
80899
+ telemetryScope = null;
79129
80900
  }
80901
+ return telemetryContext;
80902
+ };
80903
+ /**
80904
+ * Closes the telemetry scope, which is what ships buffered spans and metrics.
80905
+ *
80906
+ * Both exporters flush during scope finalization, bounded by
80907
+ * `TELEMETRY_SHUTDOWN_TIMEOUT_MS`. Because the scope is built once and shared,
80908
+ * there is exactly one of each exporter per process — which is what keeps
80909
+ * metrics honest: Effect tracks delta-temporality state on the exporter
80910
+ * instance, so a second one would start with no previous snapshot and re-report
80911
+ * every counter's full value, doubling every metric.
80912
+ *
80913
+ * Memoized for the same reason: several exit paths can fire (a `--debug` user
80914
+ * error flushes, then the top-level catch flushes again), and repeat callers
80915
+ * must await the original shutdown rather than start a second one.
80916
+ *
80917
+ * Swallows failures so telemetry can never mask the user's result.
80918
+ */
80919
+ const shutdownTelemetry = async () => {
80920
+ if (pendingShutdown !== null) return pendingShutdown;
80921
+ getTelemetryContext();
80922
+ const scope = telemetryScope;
80923
+ if (scope === null) return;
80924
+ telemetryScope = null;
80925
+ telemetryContext = null;
80926
+ pendingShutdown = runPromise(close(scope, void_$1)).catch(() => {});
80927
+ return pendingShutdown;
79130
80928
  };
79131
- //#endregion
79132
- //#region src/cli/utils/version.ts
79133
- const VERSION = process.env.VERSION ?? "0.0.0";
79134
80929
  //#endregion
79135
80930
  //#region src/cli/utils/sentry-config.ts
79136
80931
  /**
@@ -79159,57 +80954,49 @@ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@$
79159
80954
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
79160
80955
  */
79161
80956
  const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion(VERSION) ? "development" : "production");
79162
- /**
79163
- * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
79164
- * (set to `0` to disable tracing) and falls back to
79165
- * {@link SENTRY_DEFAULT_TRACES_SAMPLE_RATE}. Invalid / out-of-range values fall
79166
- * back to the default rather than silently disabling tracing.
79167
- */
79168
- const resolveTracesSampleRate = () => {
79169
- const raw = process.env.SENTRY_TRACES_SAMPLE_RATE;
79170
- if (raw === void 0 || raw.trim() === "") return 1;
79171
- const parsed = Number(raw);
79172
- if (Number.isNaN(parsed) || parsed < 0 || parsed > 1) return 1;
79173
- return parsed;
79174
- };
79175
80957
  //#endregion
79176
80958
  //#region src/lsp-telemetry.ts
79177
80959
  /**
79178
- * Sentry telemetry for the editor language server (`react-doctor
79179
- * experimental-lsp`). Mirrors the CLI's model — a per-scan wide-event span
79180
- * (op `lsp.scan`) plus Application Metrics counters/distributions — but with
79181
- * an LSP-appropriate scope instead of the CLI run context, since the daemon
79182
- * isn't a one-shot command. Shares the CLI's DSN, release, and the
79183
- * anonymization scrubbers, so editor telemetry honors the same privacy
79184
- * contract (no IP, no paths/secrets).
80960
+ * Telemetry for the editor language server (`react-doctor experimental-lsp`).
80961
+ * Mirrors the CLI's model — a per-scan wide-event span plus counters and
80962
+ * distributions to Axiom, with crashes to Sentry — but with an LSP-appropriate
80963
+ * scope instead of the CLI run context, since the daemon isn't a one-shot
80964
+ * command. Shares the CLI's DSN, release, and the anonymization scrubbers, so
80965
+ * editor telemetry honors the same privacy contract (no IP, no paths/secrets).
79185
80966
  *
79186
- * Every emit is a guarded, swallow-on-throw no-op unless Sentry is live, so a
80967
+ * Every emit is a guarded, swallow-on-throw no-op unless telemetry is live, so a
79187
80968
  * telemetry failure (or an opted-out / test run) can never disrupt the editor
79188
80969
  * session.
80970
+ *
80971
+ * Unlike the one-shot CLI, this process can run for hours, so its exporters use
80972
+ * a short periodic interval — telemetry ships while the editor session is alive
80973
+ * rather than only when the server shuts down (which it may never cleanly do).
79189
80974
  */
79190
- let lspTracesSampleRate = 0;
79191
80975
  const nodeMajorVersion = () => Number.parseInt(process.versions.node.split(".", 1)[0] ?? "", 10) || 0;
79192
- const shouldEnableLspTelemetry = () => {
79193
- if (isEnvFlagEnabled(process.env.REACT_DOCTOR_NO_TELEMETRY)) return false;
79194
- if (process.argv.includes("--no-telemetry") || process.argv.includes("--no-score")) return false;
79195
- if (process.env.VITEST || process.env.NODE_ENV === "test") return false;
79196
- return true;
79197
- };
79198
- /** Whether wide-event spans will actually be recorded (Sentry live + sampling on). */
79199
- const isLspTracingEnabled = () => Sentry.isInitialized() && lspTracesSampleRate > 0;
80976
+ /**
80977
+ * Opens the shared telemetry runtime with the daemon's periodic export
80978
+ * interval. Called at startup rather than lazily from the first scan: session
80979
+ * metrics are recorded before any scan completes, and if the editor kills the
80980
+ * server before one does, a runtime that was never built means those counters
80981
+ * are never exported.
80982
+ */
80983
+ const getLspTelemetryContext = () => getTelemetryContext({ exportIntervalMs: LSP_TELEMETRY_EXPORT_INTERVAL_MS });
79200
80984
  /**
79201
80985
  * Initializes Sentry for the language server. Safe to call once at startup; a
79202
80986
  * no-op when already initialized or when telemetry is opted out / disabled.
80987
+ *
80988
+ * Performance tracing is off — the `lsp.scan` wide event is an Effect span
80989
+ * exported to Axiom, and Effect has a single `Tracer` reference.
79203
80990
  */
79204
80991
  const initializeLspSentry = (serverVersion) => {
79205
- if (Sentry.isInitialized() || !shouldEnableLspTelemetry()) return;
79206
- lspTracesSampleRate = resolveTracesSampleRate();
80992
+ if (Sentry.isInitialized() || !isTelemetryEnabled()) return;
80993
+ getLspTelemetryContext();
79207
80994
  Sentry.init({
79208
80995
  dsn: process.env.SENTRY_DSN || "https://f253d570240a59b8dbd77b7a548ef133@o4510226365743104.ingest.us.sentry.io/4511487817809920",
79209
80996
  release: resolveSentryRelease(),
79210
80997
  environment: resolveSentryEnvironment(),
79211
80998
  sendDefaultPii: false,
79212
- tracesSampleRate: lspTracesSampleRate,
80999
+ tracesSampleRate: 0,
79213
81000
  debug: isEnvFlagEnabled(process.env.SENTRY_DEBUG),
79214
81001
  initialScope: {
79215
81002
  tags: {
@@ -79226,9 +81013,7 @@ const initializeLspSentry = (serverVersion) => {
79226
81013
  arch: process.arch
79227
81014
  } }
79228
81015
  },
79229
- beforeSend: (event) => scrubSentryEvent(event),
79230
- beforeSendTransaction: (event) => scrubSentryEvent(event),
79231
- beforeSendMetric: (metric) => scrubSentryMetric(metric)
81016
+ beforeSend: (event) => scrubSentryEvent(event)
79232
81017
  });
79233
81018
  };
79234
81019
  /**
@@ -79254,36 +81039,45 @@ const buildLspScanEventAttributes = (scan) => {
79254
81039
  return attributes;
79255
81040
  };
79256
81041
  const emitSessionStart = (session) => {
79257
- if (!Sentry.isInitialized()) return;
79258
- try {
79259
- Sentry.metrics.count(METRIC.lspSessionStarted, 1, { attributes: {
79260
- nodeMajor: session.nodeMajor,
79261
- projectCount: session.projectCount,
79262
- workspaceFolderCount: session.workspaceFolderCount,
79263
- scanOnType: session.scanOnType,
79264
- lintAvailable: session.lintAvailable
79265
- } });
79266
- } catch {}
81042
+ recordCount(METRIC.lspSessionStarted, 1, {
81043
+ nodeMajor: session.nodeMajor,
81044
+ projectCount: session.projectCount,
81045
+ workspaceFolderCount: session.workspaceFolderCount,
81046
+ scanOnType: session.scanOnType,
81047
+ lintAvailable: session.lintAvailable
81048
+ });
79267
81049
  };
79268
81050
  const emitWorkspaceScan = (scan) => {
79269
- if (!Sentry.isInitialized()) return;
81051
+ recordCount(METRIC.lspScanCompleted, 1, {
81052
+ trigger: scan.trigger,
81053
+ lintDegraded: scan.lintDegraded
81054
+ });
81055
+ recordDistribution(METRIC.lspScanDuration, scan.durationMs, {
81056
+ unit: "millisecond",
81057
+ attributes: { trigger: scan.trigger }
81058
+ });
81059
+ recordDistribution(METRIC.lspScanDiagnostics, scan.totalDiagnostics, { attributes: { trigger: scan.trigger } });
81060
+ const telemetryContext = getLspTelemetryContext();
81061
+ if (telemetryContext === null) return;
79270
81062
  try {
79271
- Sentry.metrics.count(METRIC.lspScanCompleted, 1, { attributes: {
79272
- trigger: scan.trigger,
79273
- lintDegraded: scan.lintDegraded
79274
- } });
79275
- Sentry.metrics.distribution(METRIC.lspScanDuration, scan.durationMs, {
79276
- unit: "millisecond",
79277
- attributes: { trigger: scan.trigger }
79278
- });
79279
- Sentry.metrics.distribution(METRIC.lspScanDiagnostics, scan.totalDiagnostics, { attributes: { trigger: scan.trigger } });
79280
- if (isLspTracingEnabled()) Sentry.startInactiveSpan({
81063
+ const tracer = get$4(telemetryContext, Tracer);
81064
+ const startTime = BigInt(scan.startedAtEpochMs) * NANOSECONDS_PER_MILLISECOND;
81065
+ const span = tracer.span({
79281
81066
  name: "react-doctor experimental-lsp scan",
79282
- op: "lsp.scan",
79283
- forceTransaction: true,
79284
- startTime: new Date(scan.startedAtEpochMs),
79285
- attributes: buildLspScanEventAttributes(scan)
79286
- }).end();
81067
+ parent: none(),
81068
+ annotations: empty$8(),
81069
+ links: [],
81070
+ startTime,
81071
+ kind: "internal",
81072
+ root: true,
81073
+ sampled: true
81074
+ });
81075
+ const attributes = {
81076
+ ...toSpanAttributes(buildSentryScope().tags),
81077
+ ...buildLspScanEventAttributes(scan)
81078
+ };
81079
+ for (const [key, value] of Object.entries(attributes)) span.attribute(key, value);
81080
+ span.end(startTime + BigInt(scan.durationMs) * NANOSECONDS_PER_MILLISECOND, void_$1);
79287
81081
  } catch {}
79288
81082
  };
79289
81083
  /** Builds the {@link Telemetry} sink the server drives. */
@@ -79291,6 +81085,7 @@ const createLspTelemetry = () => ({
79291
81085
  recordSessionStart: emitSessionStart,
79292
81086
  recordWorkspaceScan: emitWorkspaceScan,
79293
81087
  flush: async () => {
81088
+ await shutdownTelemetry();
79294
81089
  if (!Sentry.isInitialized()) return;
79295
81090
  try {
79296
81091
  await Sentry.flush(SENTRY_FLUSH_TIMEOUT_MS);
@@ -79315,5 +81110,5 @@ const startLanguageServer = () => {
79315
81110
  };
79316
81111
  //#endregion
79317
81112
  export { startLanguageServer };
79318
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e0c59048-6e96-5cb2-8646-a3208057e466")}catch(e){}}();
79319
- //# debugId=e0c59048-6e96-5cb2-8646-a3208057e466
81113
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ff73316d-2f43-5645-89f2-a0eb447f3e2e")}catch(e){}}();
81114
+ //# debugId=ff73316d-2f43-5645-89f2-a0eb447f3e2e