smipper 16.0.0 → 17.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +256 -135
- package/index.ts +373 -0
- package/package.json +9 -4
- package/prettier.config.js +6 -0
- package/tsconfig.json +28 -0
package/index.js
CHANGED
|
@@ -1,59 +1,104 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
13
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
14
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
15
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
16
|
+
function step(op) {
|
|
17
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
18
|
+
while (_) try {
|
|
19
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
20
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
21
|
+
switch (op[0]) {
|
|
22
|
+
case 0: case 1: t = op; break;
|
|
23
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
24
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
25
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
26
|
+
default:
|
|
27
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
28
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
29
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
30
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
31
|
+
if (t[2]) _.ops.pop();
|
|
32
|
+
_.trys.pop(); continue;
|
|
33
|
+
}
|
|
34
|
+
op = body.call(thisArg, _);
|
|
35
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
36
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
exports.__esModule = true;
|
|
40
|
+
var fs = require("fs");
|
|
41
|
+
var got = require("got");
|
|
42
|
+
var path = require("path");
|
|
43
|
+
var sourceMap = require("source-map");
|
|
44
|
+
var url = require("url");
|
|
45
|
+
var verbose = false;
|
|
46
|
+
var stack;
|
|
47
|
+
var json = false;
|
|
48
|
+
var retries = 3;
|
|
49
|
+
var jsc = 0;
|
|
50
|
+
for (var i = 2; i < process.argv.length; ++i) {
|
|
18
51
|
try {
|
|
19
|
-
|
|
20
|
-
if (verbose)
|
|
52
|
+
var arg = process.argv[i] || "";
|
|
53
|
+
if (verbose) {
|
|
21
54
|
console.error("got arg", arg);
|
|
55
|
+
}
|
|
22
56
|
if (arg === "-f" || arg === "--file") {
|
|
23
57
|
stack = fs.readFileSync(process.argv[++i]).toString();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
58
|
+
}
|
|
59
|
+
else if (arg.startsWith("-f")) {
|
|
60
|
+
stack = fs.readFileSync(arg.substring(2)).toString();
|
|
61
|
+
}
|
|
62
|
+
else if (arg.startsWith("--file=")) {
|
|
63
|
+
stack = fs.readFileSync(arg.substring(7)).toString();
|
|
64
|
+
}
|
|
65
|
+
else if (arg === "--verbose" || arg === "-v") {
|
|
29
66
|
verbose = true;
|
|
30
|
-
}
|
|
67
|
+
}
|
|
68
|
+
else if (arg === "--version") {
|
|
31
69
|
console.log(JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8")).version);
|
|
32
70
|
process.exit(0);
|
|
33
|
-
}
|
|
71
|
+
}
|
|
72
|
+
else if (arg === "--json") {
|
|
34
73
|
json = true;
|
|
35
|
-
}
|
|
74
|
+
}
|
|
75
|
+
else if (arg === "--jsc") {
|
|
76
|
+
// jsc has the wrong column for some reason
|
|
36
77
|
jsc = 1;
|
|
37
|
-
}
|
|
38
|
-
|
|
78
|
+
}
|
|
79
|
+
else if (arg.startsWith("--retries")) {
|
|
80
|
+
retries = parseInt(arg.substring(9));
|
|
39
81
|
if (isNaN(retries) || retries < 0) {
|
|
40
82
|
console.error("smipper [stack|-h|--help|-v|--verbose|--version|--retries=<number>|--jsc|--json|-f=@FILE@|-");
|
|
41
83
|
process.exit(1);
|
|
42
84
|
}
|
|
43
|
-
}
|
|
85
|
+
}
|
|
86
|
+
else if (arg === "-h" || arg === "--help") {
|
|
44
87
|
console.error("smipper [stack|-h|--help|-v|--verbose|--version|--retries=<number>|--jsc|--json|-f=@FILE@|-");
|
|
45
88
|
process.exit(0);
|
|
46
|
-
}
|
|
89
|
+
}
|
|
90
|
+
else if (arg === "-") {
|
|
47
91
|
// stdin
|
|
48
|
-
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
49
94
|
stack = arg;
|
|
50
95
|
}
|
|
51
|
-
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
52
98
|
console.error("Error: " + err.toString());
|
|
53
99
|
process.exit(1);
|
|
54
100
|
}
|
|
55
101
|
}
|
|
56
|
-
|
|
57
102
|
if (!stack) {
|
|
58
103
|
stack = fs.readFileSync("/dev/stdin").toString();
|
|
59
104
|
if (!stack) {
|
|
@@ -61,14 +106,51 @@ if (!stack) {
|
|
|
61
106
|
process.exit(0);
|
|
62
107
|
}
|
|
63
108
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
109
|
+
function build(functionName, url, line, column) {
|
|
110
|
+
return "".concat(functionName ? "at " + functionName + " " : "").concat(url, ":").concat(line, ":").concat(column);
|
|
111
|
+
}
|
|
112
|
+
function findFile(dir, fn) {
|
|
113
|
+
var list = fs.readdirSync(dir);
|
|
114
|
+
for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {
|
|
115
|
+
var file = list_1[_i];
|
|
116
|
+
var match = file === fn;
|
|
117
|
+
file = dir + "/" + file;
|
|
118
|
+
var stat = fs.statSync(file);
|
|
119
|
+
if (stat && stat.isDirectory()) {
|
|
120
|
+
var ret = findFile(file, fn);
|
|
121
|
+
if (ret) {
|
|
122
|
+
return ret;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else if (match) {
|
|
126
|
+
return "file://" + file;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
function rewriteLocalControl(url) {
|
|
132
|
+
var found = findFile(process.cwd(), path.basename(url.substring(6)));
|
|
133
|
+
if (found) {
|
|
134
|
+
return found;
|
|
135
|
+
}
|
|
136
|
+
throw new Error("Couldn't resolve localcontrol " + url);
|
|
137
|
+
}
|
|
138
|
+
function load(path) {
|
|
139
|
+
return new Promise(function (resolve, reject) {
|
|
140
|
+
if (path.startsWith("http://localcontrol.netflix.com/")) {
|
|
141
|
+
try {
|
|
142
|
+
path = rewriteLocalControl(path);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
reject(err);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
68
149
|
if (path.startsWith("file:///")) {
|
|
69
150
|
try {
|
|
70
|
-
resolve(fs.readFileSync(path.
|
|
71
|
-
}
|
|
151
|
+
resolve(fs.readFileSync(path.substring(7), "utf8"));
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
72
154
|
reject(err);
|
|
73
155
|
}
|
|
74
156
|
return;
|
|
@@ -77,146 +159,173 @@ function load(path)
|
|
|
77
159
|
reject(new Error("Not a url"));
|
|
78
160
|
return;
|
|
79
161
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
162
|
+
// let retryIndex = 0;
|
|
163
|
+
function get() {
|
|
164
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
165
|
+
var response;
|
|
166
|
+
return __generator(this, function (_a) {
|
|
167
|
+
switch (_a.label) {
|
|
168
|
+
case 0: return [4 /*yield*/, got.get(path, { timeout: 10000 })];
|
|
169
|
+
case 1:
|
|
170
|
+
response = _a.sent();
|
|
171
|
+
console.log(response.headers);
|
|
172
|
+
return [2 /*return*/, response.body || ""];
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
93
176
|
}
|
|
94
|
-
|
|
95
177
|
get().then(resolve, reject);
|
|
96
178
|
});
|
|
97
179
|
}
|
|
98
|
-
|
|
99
|
-
const sourceMaps = {};
|
|
180
|
+
var sourceMaps = {};
|
|
100
181
|
function loadUri(path) {
|
|
182
|
+
var _this = this;
|
|
101
183
|
if (path[0] === "/") {
|
|
102
184
|
path = "file://" + path;
|
|
103
185
|
}
|
|
104
|
-
return new Promise((resolve, reject)
|
|
105
|
-
if (verbose)
|
|
186
|
+
return new Promise(function (resolve, reject) {
|
|
187
|
+
if (verbose) {
|
|
106
188
|
console.log("loading", path);
|
|
189
|
+
}
|
|
107
190
|
if (!(path in sourceMaps)) {
|
|
108
191
|
sourceMaps[path] = {
|
|
109
|
-
resolvers: [
|
|
110
|
-
rejecters: [
|
|
192
|
+
resolvers: [resolve],
|
|
193
|
+
rejecters: [reject]
|
|
111
194
|
};
|
|
112
|
-
load(path)
|
|
113
|
-
|
|
114
|
-
|
|
195
|
+
load(path)
|
|
196
|
+
.then(function (jsData) {
|
|
197
|
+
var idx = jsData.lastIndexOf("//# sourceMappingURL=");
|
|
198
|
+
if (verbose) {
|
|
115
199
|
console.error("Got the file", jsData.length, idx);
|
|
116
|
-
|
|
200
|
+
}
|
|
201
|
+
if (idx == -1) {
|
|
117
202
|
return path + ".map";
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
203
|
+
}
|
|
204
|
+
var mapUrl = jsData.substring(idx + 21);
|
|
205
|
+
var match = /^(data:.+\/.+;)base64,/.exec(mapUrl);
|
|
121
206
|
if (match) {
|
|
122
|
-
return mapUrl.
|
|
207
|
+
return mapUrl.substring(match[1].length);
|
|
123
208
|
}
|
|
124
209
|
if (mapUrl.indexOf("://") != -1) {
|
|
125
210
|
return mapUrl;
|
|
126
211
|
}
|
|
127
212
|
// console.log(mapUrl);
|
|
128
|
-
return
|
|
129
|
-
})
|
|
130
|
-
|
|
213
|
+
return new url.URL(mapUrl, path).href;
|
|
214
|
+
})
|
|
215
|
+
.then(function (mapUrl) {
|
|
216
|
+
// console.log(mapUrl.substring(0, 20));
|
|
131
217
|
if (mapUrl.startsWith("base64,")) {
|
|
132
|
-
return Buffer.from(mapUrl.
|
|
218
|
+
return Buffer.from(mapUrl.substring(7), "base64").toString();
|
|
133
219
|
}
|
|
134
220
|
return load(mapUrl);
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
221
|
+
})
|
|
222
|
+
.then(function (sourceMapData) { return __awaiter(_this, void 0, void 0, function () {
|
|
223
|
+
var parsed, smap, pending;
|
|
224
|
+
return __generator(this, function (_a) {
|
|
225
|
+
switch (_a.label) {
|
|
226
|
+
case 0:
|
|
227
|
+
parsed = JSON.parse(sourceMapData);
|
|
228
|
+
return [4 /*yield*/, new sourceMap.SourceMapConsumer(parsed)];
|
|
229
|
+
case 1:
|
|
230
|
+
smap = _a.sent();
|
|
231
|
+
pending = sourceMaps[path];
|
|
232
|
+
// sourceMaps.delete(path);
|
|
233
|
+
if (pending) {
|
|
234
|
+
pending.resolvers.forEach(function (func) {
|
|
235
|
+
func(smap);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return [2 /*return*/];
|
|
239
|
+
}
|
|
141
240
|
});
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
pending
|
|
241
|
+
}); })["catch"](function (err) {
|
|
242
|
+
var pending = sourceMaps[path];
|
|
243
|
+
if (!pending) {
|
|
244
|
+
throw new Error("Gotta have pending");
|
|
245
|
+
}
|
|
246
|
+
// sourceMaps.delete(path);
|
|
247
|
+
pending.rejecters.forEach(function (func) {
|
|
145
248
|
func(err);
|
|
146
249
|
});
|
|
147
250
|
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
var cur = sourceMaps[path];
|
|
254
|
+
if (verbose) {
|
|
151
255
|
console.error("path is in source maps already", cur);
|
|
256
|
+
}
|
|
257
|
+
if (!cur) {
|
|
258
|
+
throw new Error("Gotta have cur");
|
|
259
|
+
}
|
|
152
260
|
cur.resolvers.push(resolve);
|
|
153
261
|
cur.rejecters.push(reject);
|
|
154
262
|
}
|
|
155
263
|
});
|
|
156
264
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
if (verbose)
|
|
265
|
+
function processFrame(functionName, url, line, column) {
|
|
266
|
+
if (verbose) {
|
|
161
267
|
console.log("got frame", functionName, url, line, column);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
268
|
+
}
|
|
269
|
+
if (functionName.endsWith("@")) {
|
|
270
|
+
functionName = functionName.substring(0, functionName.length - 1);
|
|
271
|
+
}
|
|
272
|
+
return new Promise(function (resolve) {
|
|
273
|
+
var newUrl, newLine, newColumn;
|
|
274
|
+
if (verbose) {
|
|
167
275
|
console.error("calling loadUri", url);
|
|
276
|
+
}
|
|
168
277
|
if (!url.includes("://") && url[0] !== "/" && fs.existsSync(url)) {
|
|
169
|
-
url =
|
|
278
|
+
url = "file://".concat(process.cwd(), "/").concat(url);
|
|
170
279
|
}
|
|
171
|
-
return loadUri(url)
|
|
172
|
-
|
|
280
|
+
return loadUri(url)
|
|
281
|
+
.then(function (smap) {
|
|
282
|
+
if (verbose) {
|
|
173
283
|
console.error("got map", url, Object.keys(smap));
|
|
174
|
-
|
|
284
|
+
}
|
|
175
285
|
// it appears that we're supposed to reduce the column
|
|
176
286
|
// number by 1 when we get this from jsc
|
|
177
|
-
|
|
287
|
+
var pos = smap.originalPositionFor({ line: line, column: column - jsc });
|
|
178
288
|
if (!pos.source) {
|
|
179
|
-
if (verbose)
|
|
289
|
+
if (verbose) {
|
|
180
290
|
console.error("nothing here", pos);
|
|
291
|
+
}
|
|
181
292
|
throw new Error("Mapping not found");
|
|
182
293
|
}
|
|
183
|
-
|
|
184
294
|
// smc.sourceContentFor(pos.source);
|
|
185
|
-
|
|
186
295
|
newUrl = pos.source;
|
|
187
|
-
newLine = pos.line;
|
|
188
|
-
newColumn = pos.column;
|
|
189
|
-
})
|
|
190
|
-
if (verbose)
|
|
296
|
+
newLine = pos.line || 0;
|
|
297
|
+
newColumn = pos.column || 0;
|
|
298
|
+
})["catch"](function (err) {
|
|
299
|
+
if (verbose) {
|
|
191
300
|
console.error("didn't get map", url, err);
|
|
301
|
+
}
|
|
192
302
|
// console.error(err);
|
|
193
|
-
})
|
|
303
|
+
})["finally"](function () {
|
|
194
304
|
if (json) {
|
|
195
|
-
|
|
305
|
+
var ret = {
|
|
196
306
|
functionName: functionName,
|
|
197
307
|
sourceURL: newUrl ? newUrl : url,
|
|
198
308
|
line: newUrl ? newLine : line,
|
|
199
309
|
column: newUrl ? newColumn : column
|
|
200
310
|
};
|
|
201
|
-
if (newUrl) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
311
|
+
// if (newUrl) {
|
|
312
|
+
// ret.oldSourceURL = url;
|
|
313
|
+
// ret.oldLine = line;
|
|
314
|
+
// ret.oldColumn = column;
|
|
315
|
+
// }
|
|
206
316
|
// if (newUrl) {
|
|
207
317
|
// ret.newUrl = newUrl;
|
|
208
318
|
// ret.newLine = newLine;
|
|
209
319
|
// ret.newColumn = newColumn;
|
|
210
320
|
// }
|
|
211
321
|
resolve(ret);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
let str;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
var str = void 0;
|
|
217
325
|
if (newUrl) {
|
|
218
|
-
str =
|
|
219
|
-
}
|
|
326
|
+
str = "".concat(build(functionName, newUrl, newLine, newColumn), " (").concat(build("", url, line, column), ")");
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
220
329
|
str = build(functionName, url, line, column);
|
|
221
330
|
}
|
|
222
331
|
resolve(str);
|
|
@@ -224,56 +333,68 @@ function processFrame(functionName, url, line, column)
|
|
|
224
333
|
});
|
|
225
334
|
});
|
|
226
335
|
}
|
|
227
|
-
|
|
228
336
|
if (json) {
|
|
229
|
-
|
|
337
|
+
var parsed = void 0;
|
|
230
338
|
try {
|
|
231
339
|
parsed = JSON.parse(stack);
|
|
232
|
-
if (!Array.isArray(parsed))
|
|
340
|
+
if (!Array.isArray(parsed)) {
|
|
233
341
|
throw new Error("Expected array");
|
|
234
|
-
|
|
235
|
-
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
console.error("Can't parse json ".concat(err));
|
|
236
346
|
process.exit(1);
|
|
237
347
|
}
|
|
238
|
-
Promise.all(parsed.map((frame)
|
|
239
|
-
if (verbose)
|
|
348
|
+
Promise.all(parsed.map(function (frame) {
|
|
349
|
+
if (verbose) {
|
|
240
350
|
console.error("got frame frame", frame);
|
|
351
|
+
}
|
|
241
352
|
return processFrame(frame.functionName || "", frame.sourceURL || "", frame.line, frame.column);
|
|
242
|
-
}))
|
|
243
|
-
|
|
353
|
+
}))
|
|
354
|
+
.then(function (results) {
|
|
355
|
+
results.forEach(function (frame, index) {
|
|
356
|
+
if (typeof frame !== "object") {
|
|
357
|
+
throw new Error("Huh?");
|
|
358
|
+
}
|
|
244
359
|
frame.index = index;
|
|
245
360
|
});
|
|
246
361
|
// console.log("shit", results);
|
|
247
362
|
console.log(JSON.stringify(results, null, 4));
|
|
248
|
-
})
|
|
363
|
+
})["catch"](function (error) {
|
|
249
364
|
console.error("Got an error", error);
|
|
250
365
|
process.exit(2);
|
|
251
366
|
});
|
|
252
|
-
}
|
|
253
|
-
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
Promise.all(stack
|
|
370
|
+
.split("\n")
|
|
371
|
+
.filter(function (x) { return x; })
|
|
372
|
+
.map(function (x) {
|
|
254
373
|
x = x.trim();
|
|
255
|
-
|
|
374
|
+
var match = / *at *([^ ]*).* \(?([^ ]+):([0-9]+):([0-9]+)/.exec(x);
|
|
256
375
|
if (!match) {
|
|
257
376
|
match = /([^ ]+@)?(.*):([0-9]+):([0-9]+)/.exec(x);
|
|
258
377
|
}
|
|
259
|
-
if (verbose)
|
|
378
|
+
if (verbose) {
|
|
260
379
|
console.error(x, " => ", match);
|
|
380
|
+
}
|
|
261
381
|
if (!match) {
|
|
262
|
-
|
|
382
|
+
var nolinecol = /([^ ]*)(.*)/.exec(x);
|
|
263
383
|
if (nolinecol) {
|
|
264
384
|
return nolinecol[0];
|
|
265
385
|
}
|
|
266
386
|
return x;
|
|
267
387
|
}
|
|
268
388
|
return processFrame(match[1] || "", match[2], parseInt(match[3]), parseInt(match[4]));
|
|
269
|
-
}))
|
|
389
|
+
}))
|
|
390
|
+
.then(function (results) {
|
|
270
391
|
if (!results.length) {
|
|
271
392
|
throw new Error("Empty output");
|
|
272
393
|
}
|
|
273
|
-
results.forEach(str
|
|
394
|
+
results.forEach(function (str) {
|
|
274
395
|
console.log(str);
|
|
275
396
|
});
|
|
276
|
-
})
|
|
397
|
+
})["catch"](function (error) {
|
|
277
398
|
console.error("Got an error", error);
|
|
278
399
|
process.exit(3);
|
|
279
400
|
});
|
package/index.ts
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as got from "got";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as sourceMap from "source-map";
|
|
8
|
+
import * as url from "url";
|
|
9
|
+
|
|
10
|
+
let verbose = false;
|
|
11
|
+
let stack;
|
|
12
|
+
let json = false;
|
|
13
|
+
let retries = 3;
|
|
14
|
+
let jsc = 0;
|
|
15
|
+
|
|
16
|
+
for (let i = 2; i < process.argv.length; ++i) {
|
|
17
|
+
try {
|
|
18
|
+
const arg = process.argv[i] || "";
|
|
19
|
+
if (verbose) {
|
|
20
|
+
console.error("got arg", arg);
|
|
21
|
+
}
|
|
22
|
+
if (arg === "-f" || arg === "--file") {
|
|
23
|
+
stack = fs.readFileSync(process.argv[++i]).toString();
|
|
24
|
+
} else if (arg.startsWith("-f")) {
|
|
25
|
+
stack = fs.readFileSync(arg.substring(2)).toString();
|
|
26
|
+
} else if (arg.startsWith("--file=")) {
|
|
27
|
+
stack = fs.readFileSync(arg.substring(7)).toString();
|
|
28
|
+
} else if (arg === "--verbose" || arg === "-v") {
|
|
29
|
+
verbose = true;
|
|
30
|
+
} else if (arg === "--version") {
|
|
31
|
+
console.log(JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8")).version);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
} else if (arg === "--json") {
|
|
34
|
+
json = true;
|
|
35
|
+
} else if (arg === "--jsc") {
|
|
36
|
+
// jsc has the wrong column for some reason
|
|
37
|
+
jsc = 1;
|
|
38
|
+
} else if (arg.startsWith("--retries")) {
|
|
39
|
+
retries = parseInt(arg.substring(9));
|
|
40
|
+
if (isNaN(retries) || retries < 0) {
|
|
41
|
+
console.error(
|
|
42
|
+
"smipper [stack|-h|--help|-v|--verbose|--version|--retries=<number>|--jsc|--json|-f=@FILE@|-"
|
|
43
|
+
);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
47
|
+
console.error(
|
|
48
|
+
"smipper [stack|-h|--help|-v|--verbose|--version|--retries=<number>|--jsc|--json|-f=@FILE@|-"
|
|
49
|
+
);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
} else if (arg === "-") {
|
|
52
|
+
// stdin
|
|
53
|
+
} else {
|
|
54
|
+
stack = arg;
|
|
55
|
+
}
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.error("Error: " + err.toString());
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!stack) {
|
|
63
|
+
stack = fs.readFileSync("/dev/stdin").toString();
|
|
64
|
+
if (!stack) {
|
|
65
|
+
console.error("Nothing to do");
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function build(functionName: string, url: string, line: number, column: number): string {
|
|
71
|
+
return `${functionName ? "at " + functionName + " " : ""}${url}:${line}:${column}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function findFile(dir: string, fn: string): string | undefined {
|
|
75
|
+
const list = fs.readdirSync(dir);
|
|
76
|
+
for (let file of list) {
|
|
77
|
+
const match = file === fn;
|
|
78
|
+
file = dir + "/" + file;
|
|
79
|
+
const stat = fs.statSync(file);
|
|
80
|
+
if (stat && stat.isDirectory()) {
|
|
81
|
+
const ret = findFile(file, fn);
|
|
82
|
+
if (ret) {
|
|
83
|
+
return ret;
|
|
84
|
+
}
|
|
85
|
+
} else if (match) {
|
|
86
|
+
return "file://" + file;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function rewriteLocalControl(url: string) {
|
|
93
|
+
const found = findFile(process.cwd(), path.basename(url.substring(6)));
|
|
94
|
+
if (found) {
|
|
95
|
+
return found;
|
|
96
|
+
}
|
|
97
|
+
throw new Error("Couldn't resolve localcontrol " + url);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function load(path: string): Promise<string> {
|
|
101
|
+
return new Promise<string>((resolve, reject) => {
|
|
102
|
+
if (path.startsWith("http://localcontrol.netflix.com/")) {
|
|
103
|
+
try {
|
|
104
|
+
path = rewriteLocalControl(path);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
reject(err);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (path.startsWith("file:///")) {
|
|
112
|
+
try {
|
|
113
|
+
resolve(fs.readFileSync(path.substring(7), "utf8"));
|
|
114
|
+
} catch (err) {
|
|
115
|
+
reject(err);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!path.startsWith("http://") && !path.startsWith("https://")) {
|
|
121
|
+
reject(new Error("Not a url"));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// let retryIndex = 0;
|
|
126
|
+
async function get(): Promise<string> {
|
|
127
|
+
const response = await got.get(path, { timeout: 10000 });
|
|
128
|
+
console.log(response.headers);
|
|
129
|
+
return response.body || "";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get().then(resolve, reject);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
type SourceMapResolve = (res: sourceMap.SourceMapConsumer) => void;
|
|
137
|
+
type Rejecter = (err: Error) => void;
|
|
138
|
+
type SourceMapData = {
|
|
139
|
+
resolvers: SourceMapResolve[];
|
|
140
|
+
rejecters: Rejecter[];
|
|
141
|
+
};
|
|
142
|
+
const sourceMaps: Record<string, SourceMapData> = {};
|
|
143
|
+
function loadUri(path: string): Promise<sourceMap.SourceMapConsumer> {
|
|
144
|
+
if (path[0] === "/") {
|
|
145
|
+
path = "file://" + path;
|
|
146
|
+
}
|
|
147
|
+
return new Promise((resolve: SourceMapResolve, reject: Rejecter) => {
|
|
148
|
+
if (verbose) {
|
|
149
|
+
console.log("loading", path);
|
|
150
|
+
}
|
|
151
|
+
if (!(path in sourceMaps)) {
|
|
152
|
+
sourceMaps[path] = {
|
|
153
|
+
resolvers: [resolve],
|
|
154
|
+
rejecters: [reject]
|
|
155
|
+
};
|
|
156
|
+
load(path)
|
|
157
|
+
.then((jsData: string) => {
|
|
158
|
+
const idx = jsData.lastIndexOf("//# sourceMappingURL=");
|
|
159
|
+
if (verbose) {
|
|
160
|
+
console.error("Got the file", jsData.length, idx);
|
|
161
|
+
}
|
|
162
|
+
if (idx == -1) {
|
|
163
|
+
return path + ".map";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const mapUrl = jsData.substring(idx + 21);
|
|
167
|
+
const match = /^(data:.+\/.+;)base64,/.exec(mapUrl);
|
|
168
|
+
if (match) {
|
|
169
|
+
return mapUrl.substring(match[1].length);
|
|
170
|
+
}
|
|
171
|
+
if (mapUrl.indexOf("://") != -1) {
|
|
172
|
+
return mapUrl;
|
|
173
|
+
}
|
|
174
|
+
// console.log(mapUrl);
|
|
175
|
+
return new url.URL(mapUrl, path).href;
|
|
176
|
+
})
|
|
177
|
+
.then((mapUrl) => {
|
|
178
|
+
// console.log(mapUrl.substring(0, 20));
|
|
179
|
+
if (mapUrl.startsWith("base64,")) {
|
|
180
|
+
return Buffer.from(mapUrl.substring(7), "base64").toString();
|
|
181
|
+
}
|
|
182
|
+
return load(mapUrl);
|
|
183
|
+
})
|
|
184
|
+
.then(async (sourceMapData: string) => {
|
|
185
|
+
const parsed = JSON.parse(sourceMapData);
|
|
186
|
+
const smap: sourceMap.SourceMapConsumer = await new sourceMap.SourceMapConsumer(parsed);
|
|
187
|
+
const pending = sourceMaps[path];
|
|
188
|
+
// sourceMaps.delete(path);
|
|
189
|
+
if (pending) {
|
|
190
|
+
pending.resolvers.forEach((func: SourceMapResolve) => {
|
|
191
|
+
func(smap);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
.catch((err) => {
|
|
196
|
+
const pending = sourceMaps[path];
|
|
197
|
+
if (!pending) {
|
|
198
|
+
throw new Error("Gotta have pending");
|
|
199
|
+
}
|
|
200
|
+
// sourceMaps.delete(path);
|
|
201
|
+
pending.rejecters.forEach((func: (err: Error) => void) => {
|
|
202
|
+
func(err);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
} else {
|
|
206
|
+
const cur = sourceMaps[path];
|
|
207
|
+
if (verbose) {
|
|
208
|
+
console.error("path is in source maps already", cur);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!cur) {
|
|
212
|
+
throw new Error("Gotta have cur");
|
|
213
|
+
}
|
|
214
|
+
cur.resolvers.push(resolve);
|
|
215
|
+
cur.rejecters.push(reject);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
type Frame = {
|
|
221
|
+
functionName: string;
|
|
222
|
+
sourceURL: string;
|
|
223
|
+
line: number;
|
|
224
|
+
column: number;
|
|
225
|
+
index?: number;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
function processFrame(functionName: string, url: string, line: number, column: number): Promise<Frame | string> {
|
|
229
|
+
if (verbose) {
|
|
230
|
+
console.log("got frame", functionName, url, line, column);
|
|
231
|
+
}
|
|
232
|
+
if (functionName.endsWith("@")) {
|
|
233
|
+
functionName = functionName.substring(0, functionName.length - 1);
|
|
234
|
+
}
|
|
235
|
+
return new Promise((resolve) => {
|
|
236
|
+
let newUrl: string, newLine: number, newColumn: number;
|
|
237
|
+
if (verbose) {
|
|
238
|
+
console.error("calling loadUri", url);
|
|
239
|
+
}
|
|
240
|
+
if (!url.includes("://") && url[0] !== "/" && fs.existsSync(url)) {
|
|
241
|
+
url = `file://${process.cwd()}/${url}`;
|
|
242
|
+
}
|
|
243
|
+
return loadUri(url)
|
|
244
|
+
.then((smap) => {
|
|
245
|
+
if (verbose) {
|
|
246
|
+
console.error("got map", url, Object.keys(smap));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// it appears that we're supposed to reduce the column
|
|
250
|
+
// number by 1 when we get this from jsc
|
|
251
|
+
const pos = smap.originalPositionFor({ line, column: column - jsc });
|
|
252
|
+
if (!pos.source) {
|
|
253
|
+
if (verbose) {
|
|
254
|
+
console.error("nothing here", pos);
|
|
255
|
+
}
|
|
256
|
+
throw new Error("Mapping not found");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// smc.sourceContentFor(pos.source);
|
|
260
|
+
|
|
261
|
+
newUrl = pos.source;
|
|
262
|
+
|
|
263
|
+
newLine = pos.line || 0;
|
|
264
|
+
newColumn = pos.column || 0;
|
|
265
|
+
})
|
|
266
|
+
.catch((err) => {
|
|
267
|
+
if (verbose) {
|
|
268
|
+
console.error("didn't get map", url, err);
|
|
269
|
+
}
|
|
270
|
+
// console.error(err);
|
|
271
|
+
})
|
|
272
|
+
.finally(() => {
|
|
273
|
+
if (json) {
|
|
274
|
+
const ret = {
|
|
275
|
+
functionName: functionName,
|
|
276
|
+
sourceURL: newUrl ? newUrl : url,
|
|
277
|
+
line: newUrl ? newLine : line,
|
|
278
|
+
column: newUrl ? newColumn : column
|
|
279
|
+
};
|
|
280
|
+
// if (newUrl) {
|
|
281
|
+
// ret.oldSourceURL = url;
|
|
282
|
+
// ret.oldLine = line;
|
|
283
|
+
// ret.oldColumn = column;
|
|
284
|
+
// }
|
|
285
|
+
// if (newUrl) {
|
|
286
|
+
// ret.newUrl = newUrl;
|
|
287
|
+
// ret.newLine = newLine;
|
|
288
|
+
// ret.newColumn = newColumn;
|
|
289
|
+
// }
|
|
290
|
+
resolve(ret);
|
|
291
|
+
} else {
|
|
292
|
+
let str;
|
|
293
|
+
if (newUrl) {
|
|
294
|
+
str = `${build(functionName, newUrl, newLine, newColumn)} (${build("", url, line, column)})`;
|
|
295
|
+
} else {
|
|
296
|
+
str = build(functionName, url, line, column);
|
|
297
|
+
}
|
|
298
|
+
resolve(str);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (json) {
|
|
305
|
+
let parsed;
|
|
306
|
+
try {
|
|
307
|
+
parsed = JSON.parse(stack);
|
|
308
|
+
if (!Array.isArray(parsed)) {
|
|
309
|
+
throw new Error("Expected array");
|
|
310
|
+
}
|
|
311
|
+
} catch (err) {
|
|
312
|
+
console.error(`Can't parse json ${err}`);
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
Promise.all(
|
|
316
|
+
parsed.map((frame) => {
|
|
317
|
+
if (verbose) {
|
|
318
|
+
console.error("got frame frame", frame);
|
|
319
|
+
}
|
|
320
|
+
return processFrame(frame.functionName || "", frame.sourceURL || "", frame.line, frame.column);
|
|
321
|
+
})
|
|
322
|
+
)
|
|
323
|
+
.then((results) => {
|
|
324
|
+
results.forEach((frame: Frame | string, index: number) => {
|
|
325
|
+
if (typeof frame !== "object") {
|
|
326
|
+
throw new Error("Huh?");
|
|
327
|
+
}
|
|
328
|
+
frame.index = index;
|
|
329
|
+
});
|
|
330
|
+
// console.log("shit", results);
|
|
331
|
+
console.log(JSON.stringify(results, null, 4));
|
|
332
|
+
})
|
|
333
|
+
.catch((error) => {
|
|
334
|
+
console.error("Got an error", error);
|
|
335
|
+
process.exit(2);
|
|
336
|
+
});
|
|
337
|
+
} else {
|
|
338
|
+
Promise.all(
|
|
339
|
+
stack
|
|
340
|
+
.split("\n")
|
|
341
|
+
.filter((x) => x)
|
|
342
|
+
.map((x) => {
|
|
343
|
+
x = x.trim();
|
|
344
|
+
let match = / *at *([^ ]*).* \(?([^ ]+):([0-9]+):([0-9]+)/.exec(x);
|
|
345
|
+
if (!match) {
|
|
346
|
+
match = /([^ ]+@)?(.*):([0-9]+):([0-9]+)/.exec(x);
|
|
347
|
+
}
|
|
348
|
+
if (verbose) {
|
|
349
|
+
console.error(x, " => ", match);
|
|
350
|
+
}
|
|
351
|
+
if (!match) {
|
|
352
|
+
const nolinecol = /([^ ]*)(.*)/.exec(x);
|
|
353
|
+
if (nolinecol) {
|
|
354
|
+
return nolinecol[0];
|
|
355
|
+
}
|
|
356
|
+
return x;
|
|
357
|
+
}
|
|
358
|
+
return processFrame(match[1] || "", match[2], parseInt(match[3]), parseInt(match[4]));
|
|
359
|
+
})
|
|
360
|
+
)
|
|
361
|
+
.then((results) => {
|
|
362
|
+
if (!results.length) {
|
|
363
|
+
throw new Error("Empty output");
|
|
364
|
+
}
|
|
365
|
+
results.forEach((str) => {
|
|
366
|
+
console.log(str);
|
|
367
|
+
});
|
|
368
|
+
})
|
|
369
|
+
.catch((error) => {
|
|
370
|
+
console.error("Got an error", error);
|
|
371
|
+
process.exit(3);
|
|
372
|
+
});
|
|
373
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smipper",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"build": "node ./node_modules/.bin/tsc index.ts && chmod +x ./index.js",
|
|
7
8
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
9
|
},
|
|
9
|
-
"bin": {
|
|
10
|
-
"smipper": "index.js"
|
|
11
|
-
},
|
|
12
10
|
"author": "",
|
|
13
11
|
"license": "ISC",
|
|
14
12
|
"dependencies": {
|
|
15
13
|
"got": "^9.6.0",
|
|
16
14
|
"source-map": "^0.7.4"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/got": "^9.6.12",
|
|
18
|
+
"@types/node": "^18.7.18",
|
|
19
|
+
"@types/source-map": "^0.5.7",
|
|
20
|
+
"prettier": "^2.7.1",
|
|
21
|
+
"typescript": "^4.8.3"
|
|
17
22
|
}
|
|
18
23
|
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"exclude": ["**/.*/", "examples/**", "autobahn/**", "node_modules/**", "dist/**", "*.def.ts"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "es2020",
|
|
5
|
+
"typeRoots": ["./src/typings", "./node_modules/@nf-types"],
|
|
6
|
+
"noUncheckedIndexedAccess": true,
|
|
7
|
+
"strictBindCallApply": true,
|
|
8
|
+
"newLine": "LF",
|
|
9
|
+
"module": "es6",
|
|
10
|
+
"declaration": false,
|
|
11
|
+
"moduleResolution": "node",
|
|
12
|
+
"pretty": true,
|
|
13
|
+
"noImplicitAny": true,
|
|
14
|
+
"esModuleInterop": true,
|
|
15
|
+
"strictNullChecks": true,
|
|
16
|
+
"strictPropertyInitialization": true,
|
|
17
|
+
"downlevelIteration": true,
|
|
18
|
+
"strict": true,
|
|
19
|
+
"sourceMap": true,
|
|
20
|
+
"incremental": true,
|
|
21
|
+
"lib": ["es2019"],
|
|
22
|
+
"baseUrl": ".",
|
|
23
|
+
"paths": {
|
|
24
|
+
"~/*": ["src/*"]
|
|
25
|
+
},
|
|
26
|
+
"plugins": [{ "transform": "@zerollup/ts-transform-paths" }]
|
|
27
|
+
}
|
|
28
|
+
}
|