@sasjs/utils 4.1.2 → 4.3.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
@@ -20,6 +20,7 @@ import { ServerType } from '@sasjs/utils/types'
20
20
  import { prefixMessage } from '@sasjs/utils/error'
21
21
  import { isSpecialMissing } from '@sasjs/utils/input/validators'
22
22
  import { bytesToSize } from '@sasjs/utils/utils'
23
+ import { parseLog } from '@sasjs/utils/logs'
23
24
  ```
24
25
 
25
26
  The hash and sync helpers - `getHash`, `compareHashes`, `extractHashArray`,
@@ -29,6 +30,20 @@ folders on disk, so they are Node-only, and the barrel does not re-export them:
29
30
  bundle that imports the barrel for anything else no longer reaches `crypto`, which used to
30
31
  arrive with the whole AES/RSA/ECDSA polyfill suite behind it.
31
32
 
33
+ `@sasjs/utils/logs` parses a SAS log: `parseLog(text)` returns every `ERROR`,
34
+ `WARNING` and notable `NOTE` as an issue addressed by line and column, and
35
+ `nextIssue(issues, line, direction)` walks that list for a next/previous problem
36
+ control. It is a pair of pure functions with no imports at all, so it runs
37
+ unchanged in Node, in a browser bundle and in an editor extension, and it lives
38
+ on a subpath so a browser never reaches the barrel's Node dependencies.
39
+
40
+ Only the notes the log-checking literature chases are reported - an
41
+ uninitialized variable, invalid data, a W.D format too small, values converted to
42
+ another type, repeats of BY values - because a log carries thousands of ordinary
43
+ notes and none of them is what a reader is looking for. The list is
44
+ `NOTE_KEYWORDS` in `src/logs/sasLog.ts`, and adding a keyword is how the scope
45
+ grows.
46
+
32
47
  ## Contributors ✨
33
48
 
34
49
  Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
package/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './file';
3
3
  export * from './formatter';
4
4
  export * from './input';
5
5
  export * from './logger';
6
+ export * from './logs';
6
7
  export * from './sasjsCli';
7
8
  export * from './time';
8
9
  export * from './types';
package/index.js CHANGED
@@ -15,6 +15,7 @@ __exportStar(require("./file"), exports);
15
15
  __exportStar(require("./formatter"), exports);
16
16
  __exportStar(require("./input"), exports);
17
17
  __exportStar(require("./logger"), exports);
18
+ __exportStar(require("./logs"), exports);
18
19
  __exportStar(require("./sasjsCli"), exports);
19
20
  __exportStar(require("./time"), exports);
20
21
  __exportStar(require("./types"), exports);
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAAsB;AACtB,yCAAsB;AACtB,8CAA2B;AAC3B,0CAAuB;AACvB,2CAAwB;AACxB,6CAA0B;AAC1B,yCAAsB;AACtB,0CAAuB;AACvB,0CAAuB;AACvB,gDAA6B;AAC7B,yEAAyE;AACzE,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,uBAAuB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,yCAAsB;AACtB,yCAAsB;AACtB,8CAA2B;AAC3B,0CAAuB;AACvB,2CAAwB;AACxB,yCAAsB;AACtB,6CAA0B;AAC1B,yCAAsB;AACtB,0CAAuB;AACvB,0CAAuB;AACvB,gDAA6B;AAC7B,yEAAyE;AACzE,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,uBAAuB"}
@@ -0,0 +1 @@
1
+ export * from './sasLog';
package/logs/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ __exportStar(require("./sasLog"), exports);
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/logs/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAwB"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * SAS log parsing.
3
+ *
4
+ * A SAS log is a stream of statements and messages, and only the messages
5
+ * matter to a reader looking for trouble: the ones SAS prints at the start of a
6
+ * line as `ERROR`, `WARNING` or `NOTE`. This module finds those messages and
7
+ * reports where they are, as line and column indexes into the text it was given,
8
+ * so a host can underline them without re-scanning the log and without rewriting
9
+ * it.
10
+ *
11
+ * What is recognised - the marker in capitals, beginning the line:
12
+ *
13
+ * ERROR: Libref X is not assigned.
14
+ * ERROR Libref Y is not assigned.
15
+ * ERROR 22-322: Syntax error, expecting one of the following: ;, CANCEL,
16
+ * RUN, QUIT.
17
+ * WARNING 1-322: Assuming the symbol Y was misspelled.
18
+ * NOTE: Variable x is uninitialized.
19
+ *
20
+ * An error or a warning needs a colon, or a space, after the marker; `ERROR` on
21
+ * its own is not a message, and neither is every line that happens to begin with
22
+ * the word. The numeric code between the marker and the colon is SAS's own
23
+ * message identifier and is reported separately, so a host can group by it.
24
+ *
25
+ * NOTES are reported only when they are the ones the log-checking literature
26
+ * flags - see NOTE_KEYWORDS - and only in their colon form, because the notes
27
+ * stream is endless and the notable ones are a closed list. A coded note
28
+ * (`NOTE 49-169:`) is one of them and keeps its code. INFO lines are not
29
+ * reported at all.
30
+ *
31
+ * {@link isSasLog} answers the different question a host asks before publishing
32
+ * anything: whether this file is a SAS log at all. A Python log and a shell build
33
+ * log both write `ERROR:` at the start of a line, so the marker alone does not
34
+ * say so - the banner SAS prints about itself does, and so do its message codes
35
+ * and its padded step numbers.
36
+ *
37
+ * Lines SAS wrapped are folded into the message they belong to, so one problem
38
+ * is one issue rather than one issue per wrapped line, and the keyword search
39
+ * runs on the folded message - a note whose keyword lands on the second line is
40
+ * still found. Nothing indented counts as a marker, and neither does a marker in
41
+ * lower case: the real one starts the line, which keeps a print of a data set,
42
+ * or a message quoting another log, out of the results.
43
+ *
44
+ * ODS HTML logs wrap every line in markup. A tag sequence before the marker is
45
+ * skipped and tags are stripped from the reported message, so the same parse
46
+ * serves a plain log and an HTML one. The line and column indexes stay those of
47
+ * the text as supplied, because they are what the caller needs to address.
48
+ *
49
+ * Nothing here touches the log: the caller renders from the issue list, which is
50
+ * what lets one log be shown in an editor, in a browser panel and in a report
51
+ * from the same parse.
52
+ */
53
+ /**
54
+ * The severities a log issue can carry. `note` is only ever a notable note - see
55
+ * NOTE_KEYWORDS - never an ordinary one.
56
+ */
57
+ export declare type LogSeverity = 'error' | 'warning' | 'note';
58
+ /** One ERROR, WARNING or notable NOTE in a SAS log, addressed in the text it was parsed from. */
59
+ export interface LogIssue {
60
+ /** Zero-based index of the line carrying the marker. */
61
+ line: number;
62
+ /** Zero-based index of the last line SAS wrapped into this message. */
63
+ endLine: number;
64
+ /** Zero-based column where the marker starts on `line`. */
65
+ column: number;
66
+ /** Zero-based column one past the end of `line`, for underlining it. */
67
+ endColumn: number;
68
+ /** The marker that introduced the issue. */
69
+ severity: LogSeverity;
70
+ /** SAS's message identifier, e.g. `22-322` for `ERROR 22-322:`. */
71
+ code?: string;
72
+ /** The message text, with wrapped lines joined and markup stripped. */
73
+ message: string;
74
+ }
75
+ /** Direction of travel for {@link nextIssue}. */
76
+ export declare type IssueDirection = 'next' | 'previous';
77
+ /**
78
+ * Whether a text is a SAS log rather than somebody else's log file.
79
+ *
80
+ * A marker alone does not say so: Python's logging writes `ERROR:root:boom` and
81
+ * a shell build log writes `ERROR: Command failed with exit code 1`, both at the
82
+ * start of a line. Nor does the word SAS - build logs of this estate's own
83
+ * repositories mention it in prose - so the test is SAS's own vocabulary: the
84
+ * banner it prints about itself, its message codes, and its padded step numbers.
85
+ * Each of those is written by SAS and by nothing else.
86
+ *
87
+ * This is a heuristic, and one way only: a true answer means SAS wrote the text,
88
+ * while a false answer does not prove it did not - a trimmed excerpt of a log,
89
+ * pasted with only its `ERROR:` lines, carries none of the three and reads as
90
+ * somebody else's. A host that publishes diagnostics should therefore treat false
91
+ * as "not obviously SAS" rather than "not SAS".
92
+ *
93
+ * Counting marker lines instead was considered and rejected: two `ERROR:` lines is
94
+ * what a Python log is made of, so it would readmit exactly the files this
95
+ * predicate exists to keep out.
96
+ *
97
+ * The check is deliberately cheap and runs before a host publishes anything, so
98
+ * a `.log` from another tool is left alone.
99
+ */
100
+ export declare const isSasLog: (text: string) => boolean;
101
+ /** Whether a note's message is one of the notable ones. */
102
+ export declare const isNotableNote: (message: string) => boolean;
103
+ /**
104
+ * Finds every ERROR and WARNING in a SAS log, and every notable note.
105
+ *
106
+ * The issue list is in log order. `line` and `column` address the text as
107
+ * supplied, so a caller can offset a decoration or a diagnostic by them
108
+ * directly. A host that wants errors only filters on `severity`; nothing is
109
+ * behind an option, because a notable note is worth reporting wherever the log
110
+ * is shown.
111
+ */
112
+ export declare const parseLog: (text: string) => LogIssue[];
113
+ /**
114
+ * The issue after (or before) a given line, wrapping around, which is the
115
+ * behaviour both a "next problem" button and a "previous problem" button need.
116
+ *
117
+ * A line of -1 starts at the beginning, and a line past the end starts again
118
+ * from the beginning, so a caller does not have to special-case either end.
119
+ * Returns undefined only when the log holds no issues.
120
+ */
121
+ export declare const nextIssue: (issues: LogIssue[], line: number, direction?: IssueDirection) => LogIssue | undefined;
package/logs/sasLog.js ADDED
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ /**
3
+ * SAS log parsing.
4
+ *
5
+ * A SAS log is a stream of statements and messages, and only the messages
6
+ * matter to a reader looking for trouble: the ones SAS prints at the start of a
7
+ * line as `ERROR`, `WARNING` or `NOTE`. This module finds those messages and
8
+ * reports where they are, as line and column indexes into the text it was given,
9
+ * so a host can underline them without re-scanning the log and without rewriting
10
+ * it.
11
+ *
12
+ * What is recognised - the marker in capitals, beginning the line:
13
+ *
14
+ * ERROR: Libref X is not assigned.
15
+ * ERROR Libref Y is not assigned.
16
+ * ERROR 22-322: Syntax error, expecting one of the following: ;, CANCEL,
17
+ * RUN, QUIT.
18
+ * WARNING 1-322: Assuming the symbol Y was misspelled.
19
+ * NOTE: Variable x is uninitialized.
20
+ *
21
+ * An error or a warning needs a colon, or a space, after the marker; `ERROR` on
22
+ * its own is not a message, and neither is every line that happens to begin with
23
+ * the word. The numeric code between the marker and the colon is SAS's own
24
+ * message identifier and is reported separately, so a host can group by it.
25
+ *
26
+ * NOTES are reported only when they are the ones the log-checking literature
27
+ * flags - see NOTE_KEYWORDS - and only in their colon form, because the notes
28
+ * stream is endless and the notable ones are a closed list. A coded note
29
+ * (`NOTE 49-169:`) is one of them and keeps its code. INFO lines are not
30
+ * reported at all.
31
+ *
32
+ * {@link isSasLog} answers the different question a host asks before publishing
33
+ * anything: whether this file is a SAS log at all. A Python log and a shell build
34
+ * log both write `ERROR:` at the start of a line, so the marker alone does not
35
+ * say so - the banner SAS prints about itself does, and so do its message codes
36
+ * and its padded step numbers.
37
+ *
38
+ * Lines SAS wrapped are folded into the message they belong to, so one problem
39
+ * is one issue rather than one issue per wrapped line, and the keyword search
40
+ * runs on the folded message - a note whose keyword lands on the second line is
41
+ * still found. Nothing indented counts as a marker, and neither does a marker in
42
+ * lower case: the real one starts the line, which keeps a print of a data set,
43
+ * or a message quoting another log, out of the results.
44
+ *
45
+ * ODS HTML logs wrap every line in markup. A tag sequence before the marker is
46
+ * skipped and tags are stripped from the reported message, so the same parse
47
+ * serves a plain log and an HTML one. The line and column indexes stay those of
48
+ * the text as supplied, because they are what the caller needs to address.
49
+ *
50
+ * Nothing here touches the log: the caller renders from the issue list, which is
51
+ * what lets one log be shown in an editor, in a browser panel and in a report
52
+ * from the same parse.
53
+ */
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.nextIssue = exports.parseLog = exports.isNotableNote = exports.isSasLog = void 0;
56
+ /**
57
+ * A marker at the start of a line: SAS writes `ERROR:` and `WARNING:` with a
58
+ * colon, and also writes them with no colon at all - `ERROR Libref X is not
59
+ * assigned.` - so a run of whitespace ends those two as well. The marker is
60
+ * uppercase and begins the line, which is what keeps a print of a data set, or a
61
+ * quotation of another log, out of the results.
62
+ *
63
+ * `NOTE` is a separate shape inside the same expression because it only counts
64
+ * with its colon: matching any line that begins NOTE would put the whole notes
65
+ * stream into the results. A coded note is a note - `NOTE 49-169:` carries the
66
+ * same kind of identifier an error does, and two of the notable keywords are
67
+ * written for coded notes.
68
+ *
69
+ * The tags of an ODS HTML log may lead the marker, with or without padding after
70
+ * them - they are markup the reader never sees, and the marker still begins the
71
+ * line's text. Padding without markup is not allowed: an indented marker is text
72
+ * in a listing, not a message. The prefix is captured so the marker's column is
73
+ * exact, and the code and message are captured separately.
74
+ */
75
+ const MARKER = /^((?:<[^>]*>)+[ \t]*|)(?:(ERROR|WARNING)(?:\s+(\d+-\d+))?(:[ \t]?|[ \t]+)|(NOTE)(?:\s+(\d+-\d+))?:[ \t]?)(.*)$/;
76
+ /** A line SAS numbers at the start, e.g. `1 data want;` - unlike a sequence number, it is padded. */
77
+ const NUMBERED_STEP = /^\d+ {2,}\S/;
78
+ /**
79
+ * The phrases SAS prints about itself, and nothing else does: the version banner,
80
+ * the licence note, the platform note, the work-area conversion warning, the page
81
+ * header of a listing and the step footer. Every SAS log carries at least one;
82
+ * a log that has lost its head usually keeps the step footer.
83
+ */
84
+ const SAS_BANNER = /(?:The SAS System|^You are running SAS \d|^NOTE: (?:SAS|Copyright .*\bSAS Institute|This session is executing|DATA statement used))/m;
85
+ /** A coded marker, e.g. `ERROR 22-322:` - SAS's message identifiers, again its own. */
86
+ const CODED_MARKER = /^((?:<[^>]*>)+[ \t]*|)(?:ERROR|WARNING|NOTE) +\d+-\d+/;
87
+ /**
88
+ * Whether a text is a SAS log rather than somebody else's log file.
89
+ *
90
+ * A marker alone does not say so: Python's logging writes `ERROR:root:boom` and
91
+ * a shell build log writes `ERROR: Command failed with exit code 1`, both at the
92
+ * start of a line. Nor does the word SAS - build logs of this estate's own
93
+ * repositories mention it in prose - so the test is SAS's own vocabulary: the
94
+ * banner it prints about itself, its message codes, and its padded step numbers.
95
+ * Each of those is written by SAS and by nothing else.
96
+ *
97
+ * This is a heuristic, and one way only: a true answer means SAS wrote the text,
98
+ * while a false answer does not prove it did not - a trimmed excerpt of a log,
99
+ * pasted with only its `ERROR:` lines, carries none of the three and reads as
100
+ * somebody else's. A host that publishes diagnostics should therefore treat false
101
+ * as "not obviously SAS" rather than "not SAS".
102
+ *
103
+ * Counting marker lines instead was considered and rejected: two `ERROR:` lines is
104
+ * what a Python log is made of, so it would readmit exactly the files this
105
+ * predicate exists to keep out.
106
+ *
107
+ * The check is deliberately cheap and runs before a host publishes anything, so
108
+ * a `.log` from another tool is left alone.
109
+ */
110
+ const isSasLog = (text) => SAS_BANNER.test(text) ||
111
+ text
112
+ .split(/\r?\n/)
113
+ .some((line) => CODED_MARKER.test(line) || NUMBERED_STEP.test(line));
114
+ exports.isSasLog = isSasLog;
115
+ /**
116
+ * The notes worth reporting, matched case-insensitively against the folded
117
+ * message. Each is a message SAS prints when something has gone wrong quietly -
118
+ * a variable never set, a row that failed input, a value silently converted, a
119
+ * format too small to hold a number - and each is chased by the log checkers
120
+ * published in the SAS user-group papers: "Log Checks Made Easy" (SAS Global
121
+ * Forum 2012, 042-2012), "Check Please: An Automated Approach to Log Checking"
122
+ * (MWSUG 2017, TT02), "You've Got E-Mail: Automatic Log Checking via E-Mail
123
+ * Notification" (SUGI 31, 128-31) and the note table behind SASLogChecker.
124
+ *
125
+ * Adding a keyword widens what every host reports, so the list is deliberately
126
+ * short: a message belongs here when a reviewer would want to see it even though
127
+ * SAS called it a note.
128
+ */
129
+ const NOTE_KEYWORDS = [
130
+ 'is uninitialized',
131
+ 'Missing values were generated',
132
+ 'Division by zero',
133
+ 'Mathematical operations could not be performed',
134
+ 'Invalid data',
135
+ 'Invalid argument',
136
+ 'Invalid third argument',
137
+ 'have been converted to',
138
+ 'The meaning of an identifier',
139
+ 'too small for',
140
+ 'Unreferenced label',
141
+ 'WHERE clause has been replaced',
142
+ 'stopped due to looping',
143
+ 'will be overwritten by data set',
144
+ 'repeats of BY values',
145
+ 'was not found',
146
+ 'reached past the end of a line',
147
+ 'LOST CARD',
148
+ 'NOEXEC',
149
+ 'A CASE expression has no ELSE clause',
150
+ 'have been thinned',
151
+ 'discarded because neither',
152
+ 'Processing on disk occurred during summarization',
153
+ 'log axis cannot support',
154
+ 'lines were truncated',
155
+ 'outside the axis range',
156
+ 'infinite likelihood',
157
+ 'not positive definite',
158
+ 'The SAS System stopped processing this step because of errors'
159
+ ];
160
+ const NOTABLE_NOTE_KEYWORDS = NOTE_KEYWORDS.map((keyword) => keyword.toLowerCase());
161
+ /** How many wrapped lines one message may absorb, so a malformed log cannot run away. */
162
+ const MAX_WRAPPED_LINES = 10;
163
+ /** Whether a note's message is one of the notable ones. */
164
+ const isNotableNote = (message) => {
165
+ const lower = message.toLowerCase();
166
+ return NOTABLE_NOTE_KEYWORDS.some((keyword) => lower.includes(keyword));
167
+ };
168
+ exports.isNotableNote = isNotableNote;
169
+ /** A line SAS wrapped: indented, and not one of the pointer lines it prints under source. */
170
+ const isWrappedLine = (line) => /^\s+\S/.test(line) &&
171
+ !/^\s*[-_]+\s*$/.test(line) &&
172
+ !/^\s*\d+\s*$/.test(line);
173
+ /** Removes markup and collapses the whitespace a wrapped message carries. */
174
+ const clean = (text) => text
175
+ .replace(/<[^>]*>/g, '')
176
+ .replace(/\s+/g, ' ')
177
+ .trim();
178
+ /**
179
+ * Finds every ERROR and WARNING in a SAS log, and every notable note.
180
+ *
181
+ * The issue list is in log order. `line` and `column` address the text as
182
+ * supplied, so a caller can offset a decoration or a diagnostic by them
183
+ * directly. A host that wants errors only filters on `severity`; nothing is
184
+ * behind an option, because a notable note is worth reporting wherever the log
185
+ * is shown.
186
+ */
187
+ const parseLog = (text) => {
188
+ var _a, _b, _c;
189
+ if (!text) {
190
+ return [];
191
+ }
192
+ const lines = text.split(/\r?\n/);
193
+ const issues = [];
194
+ for (let line = 0; line < lines.length; line++) {
195
+ const match = MARKER.exec(lines[line]);
196
+ if (!match) {
197
+ continue;
198
+ }
199
+ const markerLine = line;
200
+ const parts = [clean((_a = match[7]) !== null && _a !== void 0 ? _a : '')];
201
+ let endLine = markerLine;
202
+ let wrapped = 0;
203
+ while (wrapped < MAX_WRAPPED_LINES &&
204
+ endLine + 1 < lines.length &&
205
+ isWrappedLine(lines[endLine + 1])) {
206
+ endLine++;
207
+ wrapped++;
208
+ parts.push(clean(lines[endLine]));
209
+ }
210
+ const severity = ((_b = match[2]) !== null && _b !== void 0 ? _b : match[5]).toLowerCase();
211
+ const message = parts.filter((part) => part !== '').join(' ');
212
+ // The wrapped lines belong to this message whether or not it is reported, so
213
+ // the scan resumes after them either way.
214
+ line = endLine;
215
+ // A marker with nothing after it is not a message: SAS does not print a bare
216
+ // `ERROR` or `ERROR:` line, and an empty issue would reach a host as a blank
217
+ // line in its problem list.
218
+ if (message === '') {
219
+ continue;
220
+ }
221
+ if (severity === 'note' && !(0, exports.isNotableNote)(message)) {
222
+ continue;
223
+ }
224
+ const code = (_c = match[3]) !== null && _c !== void 0 ? _c : match[6];
225
+ issues.push(Object.assign(Object.assign({ line: markerLine, endLine, column: match[1].length, endColumn: lines[markerLine].length, severity }, (code ? { code } : {})), { message }));
226
+ }
227
+ return issues;
228
+ };
229
+ exports.parseLog = parseLog;
230
+ /**
231
+ * The issue after (or before) a given line, wrapping around, which is the
232
+ * behaviour both a "next problem" button and a "previous problem" button need.
233
+ *
234
+ * A line of -1 starts at the beginning, and a line past the end starts again
235
+ * from the beginning, so a caller does not have to special-case either end.
236
+ * Returns undefined only when the log holds no issues.
237
+ */
238
+ const nextIssue = (issues, line, direction = 'next') => {
239
+ var _a;
240
+ if (issues.length === 0) {
241
+ return undefined;
242
+ }
243
+ if (direction === 'next') {
244
+ return (_a = issues.find((issue) => issue.line > line)) !== null && _a !== void 0 ? _a : issues[0];
245
+ }
246
+ for (let index = issues.length - 1; index >= 0; index--) {
247
+ if (issues[index].line < line) {
248
+ return issues[index];
249
+ }
250
+ }
251
+ return issues[issues.length - 1];
252
+ };
253
+ exports.nextIssue = nextIssue;
254
+ //# sourceMappingURL=sasLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sasLog.js","sourceRoot":"","sources":["../../src/logs/sasLog.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -46,6 +46,7 @@
46
46
  "fs/**",
47
47
  "input/**",
48
48
  "logger/**",
49
+ "logs/**",
49
50
  "macros/**",
50
51
  "sasjsCli/**",
51
52
  "time/**",
@@ -95,5 +96,5 @@
95
96
  "jwt-decode": "4.0.0",
96
97
  "prompts": "2.4.2"
97
98
  },
98
- "version": "4.1.2"
99
+ "version": "4.3.0"
99
100
  }