@abaplint/runtime 1.8.40 → 2.0.2

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 (36) hide show
  1. package/README.md +2 -2
  2. package/build/src/builtin/index.d.ts +1 -0
  3. package/build/src/builtin/index.js +1 -0
  4. package/build/src/builtin/insert.d.ts +9 -0
  5. package/build/src/builtin/insert.js +15 -0
  6. package/build/src/builtin/sy.js +2 -0
  7. package/build/src/context.d.ts +13 -0
  8. package/build/src/context.js +18 -0
  9. package/build/src/db/db.d.ts +49 -0
  10. package/build/src/db/db.js +3 -0
  11. package/build/src/index.d.ts +11 -13
  12. package/build/src/index.js +17 -21
  13. package/build/src/offset_length.js +1 -1
  14. package/build/src/statements/call_function.d.ts +6 -1
  15. package/build/src/statements/call_function.js +19 -15
  16. package/build/src/statements/delete_database.d.ts +7 -2
  17. package/build/src/statements/delete_database.js +34 -33
  18. package/build/src/statements/get_time.d.ts +2 -0
  19. package/build/src/statements/get_time.js +11 -8
  20. package/build/src/statements/index.d.ts +21 -21
  21. package/build/src/statements/index.js +26 -13
  22. package/build/src/statements/insert_database.d.ts +6 -1
  23. package/build/src/statements/insert_database.js +23 -28
  24. package/build/src/statements/message.d.ts +6 -1
  25. package/build/src/statements/message.js +34 -33
  26. package/build/src/statements/modify_database.d.ts +7 -2
  27. package/build/src/statements/modify_database.js +27 -23
  28. package/build/src/statements/select.d.ts +6 -1
  29. package/build/src/statements/select.js +29 -46
  30. package/build/src/statements/update_database.d.ts +7 -2
  31. package/build/src/statements/update_database.js +39 -42
  32. package/build/src/statements/write.d.ts +7 -3
  33. package/build/src/statements/write.js +29 -24
  34. package/build/src/template_formatting.d.ts +1 -0
  35. package/build/src/template_formatting.js +6 -1
  36. package/package.json +39 -41
@@ -1,34 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.insertDatabase = void 0;
4
- function insertDatabase(table, options) {
5
- if (this.db === undefined) {
6
- throw new Error("Runtime, database not initialized");
3
+ exports.InsertDatabase = void 0;
4
+ class InsertDatabase {
5
+ constructor(context) {
6
+ this.context = context;
7
7
  }
8
- const columns = [];
9
- const values = [];
10
- const structure = options.values.get();
11
- for (const k of Object.keys(structure)) {
12
- columns.push(k);
13
- // todo, integers should not be surrounded by '"'?
14
- values.push('"' + structure[k].get() + '"');
8
+ async insertDatabase(table, options) {
9
+ const columns = [];
10
+ const values = [];
11
+ const structure = options.values.get();
12
+ for (const k of Object.keys(structure)) {
13
+ columns.push(k);
14
+ // todo, integers should not be surrounded by '"'?
15
+ values.push('"' + structure[k].get() + '"');
16
+ }
17
+ if (typeof table !== "string") {
18
+ table = table.get();
19
+ }
20
+ const { subrc, dbcnt } = await this.context.defaultDB().insert({ table, columns, values });
21
+ // @ts-ignore
22
+ abap.builtin.sy.get().subrc.set(subrc);
23
+ // @ts-ignore
24
+ abap.builtin.sy.get().dbcnt.set(dbcnt);
25
+ return subrc;
15
26
  }
16
- if (typeof table !== "string") {
17
- table = table.get();
18
- }
19
- const sql = `INSERT INTO ${table} (${columns.join(",")}) VALUES (${values.join(",")})`;
20
- // console.dir(sql);
21
- let subrc = 0;
22
- try {
23
- this.db.exec(sql);
24
- }
25
- catch (error) {
26
- // eg "UNIQUE constraint failed" errors
27
- subrc = 4;
28
- }
29
- // @ts-ignore
30
- abap.builtin.sy.get().subrc.set(subrc);
31
- return subrc;
32
27
  }
33
- exports.insertDatabase = insertDatabase;
28
+ exports.InsertDatabase = InsertDatabase;
34
29
  //# sourceMappingURL=insert_database.js.map
@@ -1,3 +1,4 @@
1
+ import { Context } from "../context";
1
2
  import { ICharacter } from "../types/_character";
2
3
  export interface IMessageOptions {
3
4
  id?: ICharacter | string;
@@ -6,4 +7,8 @@ export interface IMessageOptions {
6
7
  with?: (ICharacter | string)[];
7
8
  into?: ICharacter;
8
9
  }
9
- export declare function message(options: IMessageOptions): void;
10
+ export declare class MessageStatement {
11
+ private readonly context;
12
+ constructor(context: Context);
13
+ message(options: IMessageOptions): Promise<void>;
14
+ }
@@ -1,27 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.message = void 0;
4
- function message(options) {
5
- let arbgb = options.id;
6
- if (arbgb !== undefined && typeof arbgb !== "string") {
7
- arbgb = arbgb.get();
8
- }
9
- arbgb = arbgb === null || arbgb === void 0 ? void 0 : arbgb.toUpperCase();
10
- // @ts-ignore
11
- abap.builtin.sy.get().msgid.set(arbgb);
12
- let msgnr = options.number;
13
- if (msgnr !== undefined && typeof msgnr !== "string") {
14
- msgnr = msgnr.get();
15
- }
16
- // @ts-ignore
17
- abap.builtin.sy.get().msgno.set(msgnr);
18
- const text = findText.bind(this)(arbgb, msgnr);
19
- const replaced = replace(text, options.with);
20
- if (options.into) {
21
- options.into.set(replaced);
22
- }
23
- }
24
- exports.message = message;
3
+ exports.MessageStatement = void 0;
25
4
  function replace(text, w) {
26
5
  for (let i = 0; i < 6; i++) {
27
6
  const search = "&" + (i + 1);
@@ -44,19 +23,15 @@ function replace(text, w) {
44
23
  }
45
24
  return text.trim();
46
25
  }
47
- function findText(arbgb, msgnr) {
26
+ async function findText(context, arbgb, msgnr) {
48
27
  let text = undefined;
49
- const db = this.db;
50
- if (db && arbgb && msgnr) {
28
+ if (arbgb && msgnr) {
51
29
  try {
52
- const stmt = db.prepare("SELECT * FROM t100 WHERE sprsl=:sprsl AND arbgb=:arbgb AND msgnr=:msgnr LIMIT 1");
53
- const result = stmt.getAsObject({
54
- ":sprsl": "E",
55
- ":arbgb": arbgb,
56
- ":msgnr": msgnr,
57
- });
58
- if (result.text) {
59
- text = result.text;
30
+ // todo, sql injection?
31
+ const select = `SELECT * FROM t100 WHERE sprsl='E' AND arbgb='${arbgb}' AND msgnr='${msgnr}' LIMIT 1`;
32
+ const { rows: result } = await context.defaultDB().select({ select });
33
+ if (result[0]) {
34
+ text = result[0]["text"];
60
35
  }
61
36
  }
62
37
  catch (_a) {
@@ -69,4 +44,30 @@ function findText(arbgb, msgnr) {
69
44
  }
70
45
  return text;
71
46
  }
47
+ class MessageStatement {
48
+ constructor(context) {
49
+ this.context = context;
50
+ }
51
+ async message(options) {
52
+ let arbgb = options.id;
53
+ if (arbgb !== undefined && typeof arbgb !== "string") {
54
+ arbgb = arbgb.get();
55
+ }
56
+ arbgb = arbgb === null || arbgb === void 0 ? void 0 : arbgb.toUpperCase();
57
+ // @ts-ignore
58
+ abap.builtin.sy.get().msgid.set(arbgb);
59
+ let msgnr = options.number;
60
+ if (msgnr !== undefined && typeof msgnr !== "string") {
61
+ msgnr = msgnr.get();
62
+ }
63
+ // @ts-ignore
64
+ abap.builtin.sy.get().msgno.set(msgnr);
65
+ const text = await findText(this.context, arbgb, msgnr);
66
+ const replaced = replace(text, options.with);
67
+ if (options.into) {
68
+ options.into.set(replaced);
69
+ }
70
+ }
71
+ }
72
+ exports.MessageStatement = MessageStatement;
72
73
  //# sourceMappingURL=message.js.map
@@ -1,7 +1,12 @@
1
+ import { Context } from "../context";
1
2
  import { FieldSymbol, Structure, Table } from "../types";
2
3
  import { ICharacter } from "../types/_character";
3
- export interface IInsertDatabaseOptions {
4
+ export interface IModifyDatabaseOptions {
4
5
  from?: Structure | FieldSymbol;
5
6
  table?: Table | FieldSymbol;
6
7
  }
7
- export declare function modifyDatabase(table: string | ICharacter, options: IInsertDatabaseOptions): void;
8
+ export declare class ModifyDatabase {
9
+ private readonly context;
10
+ constructor(context: Context);
11
+ modifyDatabase(table: string | ICharacter, options: IModifyDatabaseOptions): Promise<void>;
12
+ }
@@ -1,36 +1,40 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.modifyDatabase = void 0;
3
+ exports.ModifyDatabase = void 0;
4
4
  const types_1 = require("../types");
5
5
  const insert_database_1 = require("./insert_database");
6
6
  const update_database_1 = require("./update_database");
7
- function modifyDatabase(table, options) {
8
- if (this.db === undefined) {
9
- throw new Error("Runtime, database not initialized");
7
+ class ModifyDatabase {
8
+ constructor(context) {
9
+ this.context = context;
10
10
  }
11
- if (options.table instanceof types_1.FieldSymbol) {
12
- options.table = options.table.getPointer();
13
- }
14
- if (options.from instanceof types_1.FieldSymbol) {
15
- options.from = options.from.getPointer();
16
- }
17
- if (options.table) {
18
- for (const row of options.table.array()) {
19
- const subrc = insert_database_1.insertDatabase.bind(this)(table, { values: row });
11
+ async modifyDatabase(table, options) {
12
+ if (options.table instanceof types_1.FieldSymbol) {
13
+ options.table = options.table.getPointer();
14
+ }
15
+ if (options.from instanceof types_1.FieldSymbol) {
16
+ options.from = options.from.getPointer();
17
+ }
18
+ const insert = new insert_database_1.InsertDatabase(this.context);
19
+ const update = new update_database_1.UpdateDatabase(this.context);
20
+ if (options.table) {
21
+ for (const row of options.table.array()) {
22
+ const subrc = await insert.insertDatabase(table, { values: row });
23
+ if (subrc !== 0) {
24
+ await update.updateDatabase(table, { from: row });
25
+ }
26
+ }
27
+ }
28
+ else if (options.from) {
29
+ const subrc = await insert.insertDatabase(table, { values: options.from });
20
30
  if (subrc !== 0) {
21
- update_database_1.updateDatabase.bind(this)(table, { from: row });
31
+ await update.updateDatabase(table, { from: options.from });
22
32
  }
23
33
  }
24
- }
25
- else if (options.from) {
26
- const subrc = insert_database_1.insertDatabase.bind(this)(table, { values: options.from });
27
- if (subrc !== 0) {
28
- update_database_1.updateDatabase.bind(this)(table, { from: options.from });
34
+ else {
35
+ throw "modifyDatabase todo";
29
36
  }
30
37
  }
31
- else {
32
- throw "modifyDatabase todo";
33
- }
34
38
  }
35
- exports.modifyDatabase = modifyDatabase;
39
+ exports.ModifyDatabase = ModifyDatabase;
36
40
  //# sourceMappingURL=modify_database.js.map
@@ -1,2 +1,7 @@
1
+ import { Context } from "../context";
1
2
  import { FieldSymbol, Structure, Table } from "../types";
2
- export declare function select(target: Structure | Table | FieldSymbol, select: string): void;
3
+ export declare class SelectDatabase {
4
+ private readonly context;
5
+ constructor(context: Context);
6
+ select(target: Structure | Table | FieldSymbol, select: string): Promise<void>;
7
+ }
@@ -1,70 +1,53 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.select = void 0;
3
+ exports.SelectDatabase = void 0;
4
4
  const clone_1 = require("../clone");
5
5
  const types_1 = require("../types");
6
- function select(target, select) {
7
- var _a, _b, _c;
8
- if (this.db === undefined) {
9
- throw new Error("Runtime, database not initialized");
6
+ class SelectDatabase {
7
+ constructor(context) {
8
+ this.context = context;
10
9
  }
11
- let res = undefined;
12
- try {
13
- res = this.db.exec(select);
14
- }
15
- catch (error) {
16
- // @ts-ignore
17
- if (abap.Classes["CX_SY_DYNAMIC_OSQL_SEMANTICS"] !== undefined) {
10
+ async select(target, select) {
11
+ var _a;
12
+ const { rows: rows } = await this.context.defaultDB().select({ select });
13
+ if (target instanceof types_1.FieldSymbol) {
14
+ if (target.isAssigned() === false) {
15
+ throw "select, fs not assigned";
16
+ }
18
17
  // @ts-ignore
19
- throw new abap.Classes["CX_SY_DYNAMIC_OSQL_SEMANTICS"]();
18
+ target = target.getPointer();
20
19
  }
21
- throw error;
22
- }
23
- if (target instanceof types_1.FieldSymbol) {
24
- // @ts-ignore
25
- target = target.getPointer();
26
- }
27
- target.clear();
28
- if (target instanceof types_1.Structure) {
29
- if (((_a = res[0]) === null || _a === void 0 ? void 0 : _a.values[0]) === undefined) {
20
+ target.clear();
21
+ if (rows.length === 0) {
30
22
  // @ts-ignore
31
23
  abap.builtin.sy.get().subrc.set(4);
24
+ return;
32
25
  }
33
- else {
34
- const columns = res[0].columns;
35
- const values = res[0].values[0];
26
+ if (target instanceof types_1.Structure) {
36
27
  const result = {};
37
- for (const c of columns) {
38
- result[c] = (0, clone_1.clone)(target.get()[c]).set(values.shift());
28
+ for (const column in rows[0]) {
29
+ result[column] = (0, clone_1.clone)(target.get()[column]).set(rows[0][column]);
39
30
  }
40
31
  target.set(new types_1.Structure(result));
41
- // @ts-ignore
42
- abap.builtin.sy.get().subrc.set(0);
43
- }
44
- }
45
- else if (target instanceof types_1.Table) {
46
- if (((_b = res[0]) === null || _b === void 0 ? void 0 : _b.values[0]) === undefined) {
47
- // @ts-ignore
48
- abap.builtin.sy.get().subrc.set(4);
49
32
  }
50
- else {
51
- for (const selectRow of res[0].values) {
33
+ else if (target instanceof types_1.Table) {
34
+ for (const row of rows) {
52
35
  const targetRow = (0, clone_1.clone)(target.getRowType());
53
- const values = selectRow;
54
- for (const c of res[0].columns) {
36
+ for (const columnName in row) {
37
+ // todo, non structured table = table with simple rows
55
38
  // @ts-ignore
56
- (_c = targetRow.get()[c]) === null || _c === void 0 ? void 0 : _c.set(values.shift());
39
+ (_a = targetRow.get()[columnName]) === null || _a === void 0 ? void 0 : _a.set(row[columnName]);
57
40
  }
58
41
  // @ts-ignore
59
42
  abap.statements.insertInternal({ table: target, data: targetRow });
60
43
  }
61
- // @ts-ignore
62
- abap.builtin.sy.get().subrc.set(0);
63
44
  }
64
- }
65
- else {
66
- throw new Error("Runtime, SELECT todo");
45
+ else {
46
+ throw new Error("Runtime, SELECT todo");
47
+ }
48
+ // @ts-ignore
49
+ abap.builtin.sy.get().subrc.set(0);
67
50
  }
68
51
  }
69
- exports.select = select;
52
+ exports.SelectDatabase = SelectDatabase;
70
53
  //# sourceMappingURL=select.js.map
@@ -1,7 +1,12 @@
1
+ import { Context } from "../context";
1
2
  import { FieldSymbol, Structure, Table } from "../types";
2
3
  import { ICharacter } from "../types/_character";
3
- export interface IInsertDatabaseOptions {
4
+ export interface IUpdateDatabaseOptions {
4
5
  from?: Structure | FieldSymbol;
5
6
  table?: Table | FieldSymbol;
6
7
  }
7
- export declare function updateDatabase(table: string | ICharacter, options: IInsertDatabaseOptions): number;
8
+ export declare class UpdateDatabase {
9
+ private readonly context;
10
+ constructor(context: Context);
11
+ updateDatabase(table: string | ICharacter, options: IUpdateDatabaseOptions): Promise<number>;
12
+ }
@@ -1,51 +1,48 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.updateDatabase = void 0;
3
+ exports.UpdateDatabase = void 0;
4
4
  const types_1 = require("../types");
5
- function updateDatabase(table, options) {
6
- if (this.db === undefined) {
7
- throw new Error("Runtime, database not initialized");
5
+ class UpdateDatabase {
6
+ constructor(context) {
7
+ this.context = context;
8
8
  }
9
- if (options.table instanceof types_1.FieldSymbol) {
10
- options.table = options.table.getPointer();
11
- }
12
- if (options.from instanceof types_1.FieldSymbol) {
13
- options.from = options.from.getPointer();
14
- }
15
- if (typeof table !== "string") {
16
- table = table.get();
17
- }
18
- // @ts-ignore
19
- const keys = abap.DDIC[table.toUpperCase()].keyFields;
20
- const where = [];
21
- const set = [];
22
- if (options.from) {
23
- const structure = options.from.get();
24
- for (const k of Object.keys(structure)) {
25
- // todo, integers should not be surrounded by '"'?
26
- const str = k + ' = "' + structure[k].get() + '"';
27
- if (keys.includes(k.toUpperCase())) {
28
- where.push(str);
29
- }
30
- else {
31
- set.push(str);
9
+ async updateDatabase(table, options) {
10
+ if (options.table instanceof types_1.FieldSymbol) {
11
+ options.table = options.table.getPointer();
12
+ }
13
+ if (options.from instanceof types_1.FieldSymbol) {
14
+ options.from = options.from.getPointer();
15
+ }
16
+ if (typeof table !== "string") {
17
+ table = table.get();
18
+ }
19
+ // @ts-ignore
20
+ const keys = abap.DDIC[table.toUpperCase()].keyFields;
21
+ const where = [];
22
+ const set = [];
23
+ if (options.from) {
24
+ const structure = options.from.get();
25
+ for (const k of Object.keys(structure)) {
26
+ // todo, integers should not be surrounded by '"'?
27
+ const str = k + ' = "' + structure[k].get() + '"';
28
+ if (keys.includes(k.toUpperCase())) {
29
+ where.push(str);
30
+ }
31
+ else {
32
+ set.push(str);
33
+ }
32
34
  }
33
35
  }
36
+ else {
37
+ throw "updateDatabase, todo";
38
+ }
39
+ const { subrc, dbcnt } = await this.context.defaultDB().update({ table, where: where.join(" AND "), set });
40
+ // @ts-ignore
41
+ abap.builtin.sy.get().subrc.set(subrc);
42
+ // @ts-ignore
43
+ abap.builtin.sy.get().dbcnt.set(dbcnt);
44
+ return subrc;
34
45
  }
35
- else {
36
- throw "updateDatabase, todo";
37
- }
38
- const sql = `UPDATE ${table} SET ${set.join(", ")} WHERE ${where.join(" AND ")}`;
39
- let subrc = 0;
40
- try {
41
- this.db.exec(sql);
42
- }
43
- catch (error) {
44
- subrc = 4;
45
- }
46
- // @ts-ignore
47
- abap.builtin.sy.get().subrc.set(subrc);
48
- return subrc;
49
46
  }
50
- exports.updateDatabase = updateDatabase;
47
+ exports.UpdateDatabase = UpdateDatabase;
51
48
  //# sourceMappingURL=update_database.js.map
@@ -1,10 +1,14 @@
1
+ import { Context } from "../context";
1
2
  import { FieldSymbol } from "../types/field_symbol";
2
3
  import { ICharacter } from "../types/_character";
3
4
  import { INumeric } from "../types/_numeric";
4
- interface IWriteOptions {
5
+ export interface IWriteOptions {
5
6
  newLine?: boolean;
6
7
  skipLine?: boolean;
7
8
  target?: ICharacter;
8
9
  }
9
- export declare function write(source: INumeric | ICharacter | FieldSymbol | string | number, options?: IWriteOptions): void;
10
- export {};
10
+ export declare class WriteStatement {
11
+ private readonly context;
12
+ constructor(context: Context);
13
+ write(source: INumeric | ICharacter | FieldSymbol | string | number, options?: IWriteOptions): void;
14
+ }
@@ -1,41 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.write = void 0;
3
+ exports.WriteStatement = void 0;
4
4
  const types_1 = require("../types");
5
- function write(source, options) {
6
- if ((options === null || options === void 0 ? void 0 : options.skipLine) === true) {
7
- this.console.add("\n");
5
+ class WriteStatement {
6
+ constructor(context) {
7
+ this.context = context;
8
8
  }
9
- else {
10
- if ((options === null || options === void 0 ? void 0 : options.newLine) === true && this.console.get().length > 0) {
11
- this.console.add("\n");
12
- }
13
- if (typeof source === "string" || typeof source === "number") {
14
- if (options === null || options === void 0 ? void 0 : options.target) {
15
- options.target.set(source.toString());
16
- }
17
- else {
18
- this.console.add(source.toString());
19
- }
9
+ write(source, options) {
10
+ if ((options === null || options === void 0 ? void 0 : options.skipLine) === true) {
11
+ this.context.console.add("\n");
20
12
  }
21
13
  else {
22
- if (source instanceof types_1.Structure) {
23
- const obj = source.get();
24
- for (const f in obj) {
25
- // @ts-ignore
26
- abap.statements.write(obj[f]);
14
+ if ((options === null || options === void 0 ? void 0 : options.newLine) === true && this.context.console.get().length > 0) {
15
+ this.context.console.add("\n");
16
+ }
17
+ if (typeof source === "string" || typeof source === "number") {
18
+ if (options === null || options === void 0 ? void 0 : options.target) {
19
+ options.target.set(source.toString());
20
+ }
21
+ else {
22
+ this.context.console.add(source.toString());
27
23
  }
28
24
  }
29
25
  else {
30
- if (options === null || options === void 0 ? void 0 : options.target) {
31
- options.target.set(source.get().toString());
26
+ if (source instanceof types_1.Structure) {
27
+ const obj = source.get();
28
+ for (const f in obj) {
29
+ // @ts-ignore
30
+ abap.statements.write(obj[f]);
31
+ }
32
32
  }
33
33
  else {
34
- this.console.add(source.get().toString());
34
+ if (options === null || options === void 0 ? void 0 : options.target) {
35
+ options.target.set(source.get().toString());
36
+ }
37
+ else {
38
+ this.context.console.add(source.get().toString());
39
+ }
35
40
  }
36
41
  }
37
42
  }
38
43
  }
39
44
  }
40
- exports.write = write;
45
+ exports.WriteStatement = WriteStatement;
41
46
  //# sourceMappingURL=write.js.map
@@ -7,6 +7,7 @@ declare type options = {
7
7
  pad?: string;
8
8
  width?: number;
9
9
  decimals?: number;
10
+ align?: "left" | "right";
10
11
  };
11
12
  export declare function templateFormatting(source: ICharacter | INumeric, options: options): string;
12
13
  export {};
@@ -14,7 +14,12 @@ function templateFormatting(source, options) {
14
14
  text = text.substr(0, 2) + ":" + text.substr(2, 2) + ":" + text.substr(4, 2);
15
15
  }
16
16
  if (options.width && options.pad) {
17
- text = text.trimEnd().padEnd(options.width, options.pad);
17
+ if (options.align === "right") {
18
+ text = text.trimEnd().padStart(options.width, options.pad);
19
+ }
20
+ else {
21
+ text = text.trimEnd().padEnd(options.width, options.pad);
22
+ }
18
23
  }
19
24
  else if (options.width) {
20
25
  text = text.trimEnd().padEnd(options.width, " ");