effect-errors 1.3.12 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -146,31 +146,35 @@ Effect.fail({ _tag: 'SucksToBeMe', message: 'Yeah...' });
146
146
  You might want to apply your own logic to reported errors data; for example if you want to display errors in html. You can do so using `captureErrors`. The function has the following signature:
147
147
 
148
148
  ```typescript
149
- export interface ErrorSpan {
149
+ interface ErrorSpan {
150
150
  name: string;
151
151
  attributes: ReadonlyMap<string, unknown>;
152
152
  status: SpanStatus;
153
153
  }
154
154
 
155
- export interface ErrorData {
155
+ interface ErrorData {
156
156
  errorType: unknown;
157
157
  message: unknown;
158
158
  stack?: string;
159
+ effectStacktrace?: string;
159
160
  spans?: ErrorSpan[];
160
161
  isPlainString: boolean;
161
162
  }
162
163
 
163
- export interface CapturedErrors {
164
+ interface CapturedErrors {
164
165
  interrupted: boolean;
165
166
  errors: ErrorData[];
166
167
  }
167
168
 
168
- export interface CaptureErrorsOptions {
169
+ interface CaptureErrorsOptions {
169
170
  reverseSpans?: boolean;
170
171
  stripCwd?: boolean;
171
172
  }
172
173
 
173
- type captureErrorsFunction: <E>(cause: Cause<E>, options: CaptureErrorsOptions) => CapturedErrors
174
+ const captureErrors: <E>(
175
+ cause: Cause<E>,
176
+ { reverseSpans, stripCwd }?: CaptureErrorsOptions,
177
+ ) => CapturedErrors;
174
178
  ```
175
179
 
176
180
  You can use `captureErrors` like so:
@@ -9,6 +9,7 @@ export interface ErrorData {
9
9
  errorType: unknown;
10
10
  message: unknown;
11
11
  stack?: string;
12
+ effectStacktrace?: string;
12
13
  spans?: ErrorSpan[];
13
14
  isPlainString: boolean;
14
15
  }
package/capture-errors.js CHANGED
@@ -1,9 +1,35 @@
1
1
  "use strict";
2
+ var __read = (this && this.__read) || function (o, n) {
3
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
4
+ if (!m) return o;
5
+ var i = m.call(o), r, ar = [], e;
6
+ try {
7
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
+ }
9
+ catch (error) { e = { error: error }; }
10
+ finally {
11
+ try {
12
+ if (r && !r.done && (m = i["return"])) m.call(i);
13
+ }
14
+ finally { if (e) throw e.error; }
15
+ }
16
+ return ar;
17
+ };
18
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
19
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
20
+ if (ar || !(i in from)) {
21
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
22
+ ar[i] = from[i];
23
+ }
24
+ }
25
+ return to.concat(ar || Array.prototype.slice.call(from));
26
+ };
2
27
  Object.defineProperty(exports, "__esModule", { value: true });
3
28
  exports.captureErrors = void 0;
4
29
  var effect_1 = require("effect");
5
30
  var Cause_1 = require("effect/Cause");
6
31
  var capture_errors_from_cause_1 = require("./logic/errors/capture-errors-from-cause");
32
+ var split_spans_attributes_by_type_1 = require("./logic/spans/split-spans-attributes-by-type");
7
33
  var strip_cwd_path_1 = require("./logic/strip-cwd-path");
8
34
  var captureErrors = function (cause, _a) {
9
35
  var _b = _a === void 0 ? {
@@ -18,11 +44,14 @@ var captureErrors = function (cause, _a) {
18
44
  }
19
45
  var errors = (0, capture_errors_from_cause_1.captureErrorsFrom)(cause).map(function (_a) {
20
46
  var message = _a.message, maybeStack = _a.stack, span = _a.span, errorType = _a.errorType, isPlainString = _a.isPlainString;
47
+ var effectStacktrace = [];
21
48
  var spans = [];
22
49
  if (span !== undefined) {
23
50
  var current = span;
24
51
  while (current !== undefined && current._tag === 'Span') {
25
- var name_1 = current.name, attributes = current.attributes, status_1 = current.status;
52
+ var name_1 = current.name, allAttributes = current.attributes, status_1 = current.status;
53
+ var _b = (0, split_spans_attributes_by_type_1.splitSpansAttributesByTypes)(allAttributes), attributes = _b.attributes, stacktrace = _b.stacktrace;
54
+ effectStacktrace.push.apply(effectStacktrace, __spreadArray([], __read(stacktrace), false));
26
55
  spans.push({
27
56
  name: name_1,
28
57
  attributes: attributes,
@@ -39,6 +68,9 @@ var captureErrors = function (cause, _a) {
39
68
  errorType: errorType,
40
69
  message: message,
41
70
  stack: stack,
71
+ effectStacktrace: effectStacktrace.length > 0
72
+ ? effectStacktrace.join('\r\n')
73
+ : undefined,
42
74
  spans: reverseSpans === true ? spans.toReversed() : spans,
43
75
  isPlainString: isPlainString,
44
76
  };
@@ -47,7 +47,7 @@ void (0, console_mock_1.mockConsole)({
47
47
  });
48
48
  (0, vitest_1.describe)('captureErrors function', function () {
49
49
  (0, vitest_1.it)('should capture errors from promises', function () { return __awaiter(void 0, void 0, void 0, function () {
50
- var cause, result, _a, errorType, isPlainString, message, spans, stack;
50
+ var cause, result, _a, errorType, isPlainString, message, spans, effectStacktrace, stack;
51
51
  return __generator(this, function (_b) {
52
52
  switch (_b.label) {
53
53
  case 0: return [4 /*yield*/, (0, effect_cause_1.effectCause)(from_promise_1.fromPromiseTask)];
@@ -59,7 +59,7 @@ void (0, console_mock_1.mockConsole)({
59
59
  });
60
60
  (0, vitest_1.expect)(result.interrupted).toBe(false);
61
61
  (0, vitest_1.expect)(result.errors).toHaveLength(1);
62
- _a = result.errors[0], errorType = _a.errorType, isPlainString = _a.isPlainString, message = _a.message, spans = _a.spans, stack = _a.stack;
62
+ _a = result.errors[0], errorType = _a.errorType, isPlainString = _a.isPlainString, message = _a.message, spans = _a.spans, effectStacktrace = _a.effectStacktrace, stack = _a.stack;
63
63
  (0, vitest_1.expect)(errorType).toBe('FetchError');
64
64
  (0, vitest_1.expect)(isPlainString).toBe(false);
65
65
  (0, vitest_1.expect)(message.toString()).toStrictEqual('TypeError: fetch failed');
@@ -73,6 +73,7 @@ void (0, console_mock_1.mockConsole)({
73
73
  ]);
74
74
  (0, vitest_1.expect)(spans === null || spans === void 0 ? void 0 : spans[1].attributes).toHaveAttributes([]);
75
75
  (0, vitest_1.expect)(stack).not.toHaveLength(0);
76
+ (0, vitest_1.expect)(effectStacktrace).toContain('at fetchTask');
76
77
  return [2 /*return*/];
77
78
  }
78
79
  });
@@ -113,6 +114,8 @@ void (0, console_mock_1.mockConsole)({
113
114
  ]);
114
115
  (0, vitest_1.expect)((_e = firstError.spans) === null || _e === void 0 ? void 0 : _e[2].name).toBe('withParallelErrorsTask');
115
116
  (0, vitest_1.expect)((_f = firstError.spans) === null || _f === void 0 ? void 0 : _f[2].attributes).toHaveAttributes([]);
117
+ (0, vitest_1.expect)(firstError.effectStacktrace).toContain('at readUser');
118
+ (0, vitest_1.expect)(firstError.effectStacktrace).toContain('at parallelGet');
116
119
  secondError = result.errors[1];
117
120
  (0, vitest_1.expect)(secondError.errorType).toBe('UserNotFound');
118
121
  (0, vitest_1.expect)(secondError.isPlainString).toBe(false);
@@ -134,6 +137,8 @@ void (0, console_mock_1.mockConsole)({
134
137
  ]);
135
138
  (0, vitest_1.expect)((_l = secondError.spans) === null || _l === void 0 ? void 0 : _l[2].name).toBe('withParallelErrorsTask');
136
139
  (0, vitest_1.expect)((_m = secondError.spans) === null || _m === void 0 ? void 0 : _m[2].attributes).toHaveAttributes([]);
140
+ (0, vitest_1.expect)(secondError.effectStacktrace).toContain('at readUser');
141
+ (0, vitest_1.expect)(secondError.effectStacktrace).toContain('at parallelGet');
137
142
  thirdError = result.errors[2];
138
143
  (0, vitest_1.expect)(thirdError.errorType).toBe('UserNotFound');
139
144
  (0, vitest_1.expect)(thirdError.isPlainString).toBe(false);
@@ -155,6 +160,8 @@ void (0, console_mock_1.mockConsole)({
155
160
  ]);
156
161
  (0, vitest_1.expect)((_s = thirdError.spans) === null || _s === void 0 ? void 0 : _s[2].name).toBe('withParallelErrorsTask');
157
162
  (0, vitest_1.expect)((_t = thirdError.spans) === null || _t === void 0 ? void 0 : _t[2].attributes).toHaveAttributes([]);
163
+ (0, vitest_1.expect)(thirdError.effectStacktrace).toContain('at readUser');
164
+ (0, vitest_1.expect)(thirdError.effectStacktrace).toContain('at parallelGet');
158
165
  return [2 /*return*/];
159
166
  }
160
167
  });
@@ -63,11 +63,7 @@ var readUser = effect_1.Effect.withSpan('readUser')(effect_1.Effect.tryPromise({
63
63
  catch: function (e) { return new file_error_1.FileError({ cause: e }); },
64
64
  }));
65
65
  var fetchTask = function (userId) {
66
- return effect_1.Effect.withSpan('fetchUser', {
67
- attributes: {
68
- userId: userId,
69
- },
70
- })(effect_1.Effect.tryPromise({
66
+ return effect_1.Effect.withSpan('fetchUser', { attributes: { userId: userId } })(effect_1.Effect.tryPromise({
71
67
  try: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
72
68
  switch (_a.label) {
73
69
  case 0: return [4 /*yield*/, fetch("https://yolo-bro-oh-no.org/users/".concat(userId))];
@@ -1 +1,4 @@
1
- export declare const getSpanAttributes: (attributes: ReadonlyMap<string, unknown>, isLastEntry: boolean) => string;
1
+ export declare const getSpanAttributes: (allAttributes: ReadonlyMap<string, unknown>, isLastEntry: boolean) => {
2
+ formattedAttributes: string;
3
+ stack: string[];
4
+ };
@@ -21,16 +21,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.getSpanAttributes = void 0;
23
23
  var chalk_1 = __importDefault(require("chalk"));
24
- var getSpanAttributes = function (attributes, isLastEntry) {
25
- if (attributes.size === 0) {
26
- return '';
24
+ var split_spans_attributes_by_type_1 = require("./split-spans-attributes-by-type");
25
+ var maybePrintPipe = function (isLastEntry) {
26
+ return isLastEntry ? ' ' : chalk_1.default.gray('│');
27
+ };
28
+ var getSpanAttributes = function (allAttributes, isLastEntry) {
29
+ if (allAttributes.size === 0) {
30
+ return { formattedAttributes: '', stack: [] };
27
31
  }
28
- var formattedAttributes = Array.from(attributes.entries())
32
+ var _a = (0, split_spans_attributes_by_type_1.splitSpansAttributesByTypes)(allAttributes), attributes = _a.attributes, stacktrace = _a.stacktrace;
33
+ var formattedAttributes = Array.from(attributes)
29
34
  .map(function (_a) {
30
35
  var _b = __read(_a, 2), key = _b[0], value = _b[1];
31
- return "".concat(isLastEntry ? ' ' : chalk_1.default.gray('│'), " ").concat(chalk_1.default.blue(key)).concat(chalk_1.default.gray(':'), " ").concat(value);
36
+ return "".concat(maybePrintPipe(isLastEntry), " ").concat(chalk_1.default.blue(key)).concat(chalk_1.default.gray(':'), " ").concat(value);
32
37
  })
33
38
  .join('\r\n');
34
- return "\r\n".concat(formattedAttributes);
39
+ return {
40
+ formattedAttributes: "\r\n".concat(formattedAttributes),
41
+ stack: stacktrace,
42
+ };
35
43
  };
36
44
  exports.getSpanAttributes = getSpanAttributes;
@@ -0,0 +1,6 @@
1
+ interface FilteredEffectAttributes {
2
+ stacktrace: string[];
3
+ attributes: ReadonlyMap<string, unknown>;
4
+ }
5
+ export declare const splitSpansAttributesByTypes: (attributes: ReadonlyMap<string, unknown>) => FilteredEffectAttributes;
6
+ export {};
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __read = (this && this.__read) || function (o, n) {
3
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
4
+ if (!m) return o;
5
+ var i = m.call(o), r, ar = [], e;
6
+ try {
7
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
+ }
9
+ catch (error) { e = { error: error }; }
10
+ finally {
11
+ try {
12
+ if (r && !r.done && (m = i["return"])) m.call(i);
13
+ }
14
+ finally { if (e) throw e.error; }
15
+ }
16
+ return ar;
17
+ };
18
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
19
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
20
+ if (ar || !(i in from)) {
21
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
22
+ ar[i] = from[i];
23
+ }
24
+ }
25
+ return to.concat(ar || Array.prototype.slice.call(from));
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.splitSpansAttributesByTypes = void 0;
29
+ var splitSpansAttributesByTypes = function (attributes) {
30
+ return Array.from(attributes.entries()).reduce(function (prev, _a) {
31
+ var _b = __read(_a, 2), key = _b[0], value = _b[1];
32
+ if (key === 'code.stacktrace') {
33
+ return {
34
+ attributes: prev.attributes,
35
+ stacktrace: __spreadArray(__spreadArray([], __read(prev.stacktrace), false), [value], false),
36
+ };
37
+ }
38
+ return {
39
+ attributes: new Map(__spreadArray(__spreadArray([], __read(prev.attributes), false), [[key, value]], false)),
40
+ stacktrace: prev.stacktrace,
41
+ };
42
+ }, {
43
+ stacktrace: [],
44
+ attributes: new Map(),
45
+ });
46
+ };
47
+ exports.splitSpansAttributesByTypes = splitSpansAttributesByTypes;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "repository": "https://github.com/jpb06/effect-errors.git",
3
3
  "main": "index.js",
4
4
  "name": "effect-errors",
5
- "version": "1.3.12",
5
+ "version": "1.4.0",
6
6
  "author": "jpb06 <jp.bois.06@outlook.fr>",
7
7
  "description": "A POC for errors reporting in Effect",
8
8
  "keywords": [],
@@ -27,13 +27,13 @@
27
27
  "run-examples": "bun run ./src/examples/util/run-all"
28
28
  },
29
29
  "dependencies": {
30
- "@effect/schema": "^0.67.3",
30
+ "@effect/schema": "^0.67.9",
31
31
  "chalk": "<5",
32
- "effect": "^3.1.5"
32
+ "effect": "^3.2.1"
33
33
  },
34
34
  "devDependencies": {
35
- "@eslint/eslintrc": "^3.0.2",
36
- "@eslint/js": "^9.2.0",
35
+ "@eslint/eslintrc": "^3.1.0",
36
+ "@eslint/js": "^9.3.0",
37
37
  "@stylistic/eslint-plugin": "^2.1.0",
38
38
  "@stylistic/eslint-plugin-ts": "^2.1.0",
39
39
  "@types/eslint": "^8.56.10",
@@ -44,7 +44,7 @@
44
44
  "@vitest/coverage-v8": "^1.6.0",
45
45
  "copyfiles": "^2.4.1",
46
46
  "del-cli": "^5.1.0",
47
- "eslint": "^9.2.0",
47
+ "eslint": "^9.3.0",
48
48
  "eslint-config-prettier": "^9.1.0",
49
49
  "eslint-config-standard-with-typescript": "^43.0.1",
50
50
  "eslint-import-resolver-typescript": "^3.6.1",
@@ -55,7 +55,7 @@
55
55
  "eslint-plugin-promise": "^6.1.1",
56
56
  "eslint-plugin-vitest": "^0.5.4",
57
57
  "fs-extra": "^11.2.0",
58
- "globals": "^15.2.0",
58
+ "globals": "^15.3.0",
59
59
  "prettier": "^3.2.5",
60
60
  "readme-package-icons": "^1.1.14",
61
61
  "typescript": "*",
package/pretty-print.js CHANGED
@@ -1,4 +1,29 @@
1
1
  "use strict";
2
+ var __read = (this && this.__read) || function (o, n) {
3
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
4
+ if (!m) return o;
5
+ var i = m.call(o), r, ar = [], e;
6
+ try {
7
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
+ }
9
+ catch (error) { e = { error: error }; }
10
+ finally {
11
+ try {
12
+ if (r && !r.done && (m = i["return"])) m.call(i);
13
+ }
14
+ finally { if (e) throw e.error; }
15
+ }
16
+ return ar;
17
+ };
18
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
19
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
20
+ if (ar || !(i in from)) {
21
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
22
+ ar[i] = from[i];
23
+ }
24
+ }
25
+ return to.concat(ar || Array.prototype.slice.call(from));
26
+ };
2
27
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
28
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
29
  };
@@ -35,6 +60,7 @@ var prettyPrint = function (cause, _a) {
35
60
  if (isPlainString) {
36
61
  message += "\r\n".concat(chalk_1.default.gray('ℹ️ You used a plain string to represent a failure in the error channel (E). You should consider using tagged objects (with a _tag field), or yieldable errors such as Data.TaggedError and Schema.TaggedError for better handling experience.'));
37
62
  }
63
+ var spanAttributesStack = [];
38
64
  if (span !== undefined) {
39
65
  var current = span;
40
66
  var spans_1 = [];
@@ -49,21 +75,27 @@ var prettyPrint = function (cause, _a) {
49
75
  var isFirstEntry = index === 0;
50
76
  var isLastEntry = index === spans_1.length - 1;
51
77
  var filePath = " at ".concat(stripCwd !== undefined ? (0, strip_cwd_path_1.stripCwdPath)(name) : name);
78
+ var _b = (0, get_span_attributes_1.getSpanAttributes)(attributes, isLastEntry), formattedAttributes = _b.formattedAttributes, stack = _b.stack;
79
+ spanAttributesStack.push.apply(spanAttributesStack, __spreadArray([], __read(stack), false));
52
80
  return chalk_1.default.whiteBright((isFirstEntry ? "\r\n".concat(chalk_1.default.gray('◯')) : '') +
53
81
  '\r\n' +
54
82
  (0, spans_stack_trailing_char_1.spanStackTrailingChar)(isLastEntry) +
55
83
  chalk_1.default.gray('─') +
56
84
  filePath +
57
85
  (0, get_span_duration_1.getSpanDuration)(status, isLastEntry) +
58
- (0, get_span_attributes_1.getSpanAttributes)(attributes, isLastEntry));
86
+ formattedAttributes);
59
87
  })
60
88
  .join('');
61
89
  }
62
90
  else if (!isPlainString) {
63
91
  message += "\r\n".concat(chalk_1.default.gray('ℹ️ Consider using spans to improve errors reporting.\r\n'));
64
92
  }
93
+ if (spanAttributesStack.length > 0) {
94
+ var cleanedStack = "\u2502 ".concat((0, filter_stack_1.filterStack)(spanAttributesStack.join('\r\n│ '), stripCwd === true));
95
+ message += "\r\n\uD83D\uDEA8 Effect Stacktrace\r\n".concat(chalk_1.default.red(cleanedStack));
96
+ }
65
97
  if (stack !== undefined) {
66
- message += "\r\n".concat(span !== undefined ? '\r\n' : '', "\uD83D\uDEA8 Stacktrace\r\n").concat(chalk_1.default.red((0, filter_stack_1.filterStack)(stack, stripCwd === true)));
98
+ message += "\r\n".concat(span !== undefined ? '\r\n' : '', "\uD83D\uDEA8 Node Stacktrace\r\n").concat(chalk_1.default.red((0, filter_stack_1.filterStack)(stack, stripCwd === true)));
67
99
  }
68
100
  else if (!isPlainString) {
69
101
  message += "\r\n\r\n".concat(chalk_1.default.gray('ℹ️ Consider using a yieldable error such as Data.TaggedError and Schema.TaggedError to get a stacktrace.'));