cvdl-ts 1.0.12 → 1.0.14

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.
@@ -34,4 +34,4 @@ export declare class FontDict {
34
34
  load_fonts_from_schema(schema: LayoutSchema, storage: Storage): Promise<void>;
35
35
  get_font(name: string): fontkit.Font;
36
36
  }
37
- export declare function render({ resume, layout_schemas, data_schemas, resume_layout, storage, fontDict }: RenderProps): Promise<[FontDict, Layout.RenderedLayout[]]>;
37
+ export declare function render({ resume, layout_schemas, data_schemas, resume_layout, storage, fontDict }: RenderProps): Promise<Layout.RenderedLayout[]>;
package/dist/AnyLayout.js CHANGED
@@ -54,8 +54,6 @@ class FontDict {
54
54
  }
55
55
  exports.FontDict = FontDict;
56
56
  async function render({ resume, layout_schemas, data_schemas, resume_layout, storage, fontDict }) {
57
- // Each box contains a set of elements(positioned by 0x0 and projected into its bounding box)
58
- const font_dict = fontDict !== null && fontDict !== void 0 ? fontDict : new FontDict();
59
57
  // Compute the total usable width by subtracting the margins from the document width
60
58
  const width = resume_layout.width - (resume_layout.margin.left + resume_layout.margin.right);
61
59
  // If the resume is double column, then the usable width is halved
@@ -65,7 +63,6 @@ async function render({ resume, layout_schemas, data_schemas, resume_layout, sto
65
63
  const layouts = [];
66
64
  console.error("Rendering sections...");
67
65
  for (const section of resume.sections) {
68
- console.error("Print section:", section);
69
66
  // Render Section Header
70
67
  // 1. Find the layout schema for the section
71
68
  console.info("Computing section: ", section.section_name);
@@ -75,48 +72,34 @@ async function render({ resume, layout_schemas, data_schemas, resume_layout, sto
75
72
  throw new Error(`Could not find layout schema ${section.layout_schema}`);
76
73
  }
77
74
  let start_time = Date.now();
78
- await font_dict.load_fonts_from_schema(layout_schema, storage);
75
+ await fontDict.load_fonts_from_schema(layout_schema, storage);
79
76
  let end_time = Date.now();
80
77
  console.info(`Font loading time: ${end_time - start_time}ms for section ${section.section_name}`);
81
78
  // 2. Find the data schema for the section
82
- const _data_schema = data_schemas.find(s => s.schema_name === section.data_schema);
83
- if (_data_schema === undefined) {
79
+ const data_schema = data_schemas.find(s => s.schema_name === section.data_schema);
80
+ if (data_schema === undefined) {
84
81
  throw new Error(`Could not find data schema ${section.data_schema}`);
85
82
  }
86
83
  start_time = Date.now();
87
84
  // 3. Render the header
88
- const layout = _1.Layout.computeBoxes(_1.Layout.normalize(_1.Layout.instantiate(layout_schema.header_layout_schema, section.data), column_width, font_dict), font_dict);
89
- console.error("Header is computed");
85
+ const layout = _1.Layout.computeBoxes(_1.Layout.normalize(_1.Layout.instantiate(layout_schema.header_layout_schema, section.data, data_schema.header_schema), column_width, fontDict), fontDict);
86
+ layout.path = { tag: 'section', section: section.section_name };
87
+ console.info("Header is computed");
90
88
  layouts.push(layout);
91
89
  end_time = Date.now();
92
90
  console.info(`Header rendering time: ${end_time - start_time}ms for section ${section.section_name}`);
93
91
  start_time = Date.now();
94
92
  // Render Section Items
95
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
96
- // @ts-nocheck
97
93
  for (const [index, item] of section.items.entries()) {
98
- console.log("Computing item");
99
- console.error("Item:", item);
100
- // 1. Find the layout schema for the section
101
- const layout_schema = layout_schemas
102
- .find((s) => s.schema_name == section.layout_schema);
103
- if (layout_schema == undefined) {
104
- throw new Error(`Could not find layout schema ${section.layout_schema}`);
105
- }
106
- await font_dict.load_fonts_from_schema(layout_schema, storage);
107
- console.log("Fonts are loaded");
108
- // 2. Find the data schema for the section
109
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
110
- const _data_schema = data_schemas
111
- .find((s) => s.schema_name == section.data_schema);
112
94
  // 3. Render the item
113
- const layout = _1.Layout.computeBoxes(_1.Layout.normalize(_1.Layout.instantiate(layout_schema.item_layout_schema, item.fields), column_width, font_dict), font_dict);
95
+ const layout = _1.Layout.computeBoxes(_1.Layout.normalize(_1.Layout.instantiate(layout_schema.item_layout_schema, item.fields, data_schema.item_schema), column_width, fontDict), fontDict);
96
+ layout.path = { tag: 'item', section: section.section_name, item: index };
114
97
  layouts.push(layout);
115
98
  }
116
99
  end_time = Date.now();
117
100
  console.info(`Item rendering time: ${end_time - start_time}ms for section ${section.section_name}`);
118
101
  }
119
102
  console.log("Position calculations are completed.");
120
- return [font_dict, layouts];
103
+ return layouts;
121
104
  }
122
105
  exports.render = render;
@@ -1,32 +1,44 @@
1
- export type DocumentDataType = {
2
- tag: "Date";
3
- } | {
4
- tag: "String";
5
- } | {
6
- tag: "MarkdownString";
7
- } | {
8
- tag: "Type";
9
- value: string;
10
- } | {
11
- tag: "List";
12
- value: DocumentDataType;
13
- } | {
14
- tag: "Types";
15
- value: DocumentDataType[];
16
- };
17
- export type Field = {
18
- name: string;
19
- data_type: DocumentDataType;
20
- };
1
+ export declare namespace DocumentDataType {
2
+ type t = {
3
+ tag: "Date";
4
+ } | {
5
+ tag: "String";
6
+ } | {
7
+ tag: "MarkdownString";
8
+ } | {
9
+ tag: "Number";
10
+ } | {
11
+ tag: "Type";
12
+ value: string;
13
+ } | {
14
+ tag: "List";
15
+ value: t;
16
+ } | {
17
+ tag: "Types";
18
+ value: t[];
19
+ };
20
+ type DocumentDataType = t;
21
+ function parse(s: string): DocumentDataType;
22
+ function print(d: DocumentDataType): string;
23
+ }
24
+ export declare namespace Field {
25
+ type t = {
26
+ name: string;
27
+ type: DocumentDataType.t;
28
+ };
29
+ type Field = t;
30
+ function fromJson(json: unknown): Field;
31
+ function toJson(f: Field): unknown;
32
+ }
21
33
  export declare class DataSchema {
22
34
  schema_name: string;
23
- header_schema: Field[];
24
- item_schema: Field[];
25
- constructor(schema_name: string, header_schema: Field[], item_schema: Field[]);
35
+ header_schema: Field.t[];
36
+ item_schema: Field.t[];
37
+ constructor(schema_name: string, header_schema: Field.t[], item_schema: Field.t[]);
26
38
  static fromJson(json: unknown): DataSchema;
27
39
  toJson(): {
28
40
  schema_name: string;
29
- header_schema: Field[];
30
- item_schema: Field[];
41
+ header_schema: unknown[];
42
+ item_schema: unknown[];
31
43
  };
32
44
  }
@@ -1,6 +1,81 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DataSchema = void 0;
3
+ exports.DataSchema = exports.Field = exports.DocumentDataType = void 0;
4
+ var DocumentDataType;
5
+ (function (DocumentDataType) {
6
+ function parse(s) {
7
+ if (s === "Date") {
8
+ return { tag: "Date" };
9
+ }
10
+ else if (s === "String") {
11
+ return { tag: "String" };
12
+ }
13
+ else if (s === "MarkdownString") {
14
+ return { tag: "MarkdownString" };
15
+ }
16
+ else if (s === "Number") {
17
+ return { tag: "Number" };
18
+ }
19
+ else if (s.startsWith("List")) {
20
+ return { tag: "List", value: parse(s.slice(5, -1).trim()) };
21
+ }
22
+ else if (s.includes("|")) {
23
+ return { tag: "Types", value: s.split("|").map((s) => s.trim()).map(parse) };
24
+ }
25
+ else {
26
+ throw new Error("Invalid DocumentDataType: " + s);
27
+ }
28
+ }
29
+ DocumentDataType.parse = parse;
30
+ function print(d) {
31
+ switch (d.tag) {
32
+ case "Date":
33
+ return "Date";
34
+ case "String":
35
+ return "String";
36
+ case "MarkdownString":
37
+ return "MarkdownString";
38
+ case "Number":
39
+ return "Number";
40
+ case "Type":
41
+ return d.value;
42
+ case "List":
43
+ return "List<" + print(d.value) + ">";
44
+ case "Types":
45
+ return d.value.map(print).join(" | ");
46
+ }
47
+ }
48
+ DocumentDataType.print = print;
49
+ })(DocumentDataType || (exports.DocumentDataType = DocumentDataType = {}));
50
+ var Field;
51
+ (function (Field) {
52
+ function fromJson(json) {
53
+ if (typeof json !== "object" || json === null) {
54
+ throw new Error("Field must be an object");
55
+ }
56
+ if (!("name" in json) || !("type" in json)) {
57
+ throw new Error("Field must have a name and type");
58
+ }
59
+ if (typeof json.name !== "string") {
60
+ throw new Error("Field name must be a string");
61
+ }
62
+ if (typeof json.type !== "string") {
63
+ throw new Error("Field type must be a string");
64
+ }
65
+ return {
66
+ name: json.name,
67
+ type: DocumentDataType.parse(json.type),
68
+ };
69
+ }
70
+ Field.fromJson = fromJson;
71
+ function toJson(f) {
72
+ return {
73
+ name: f.name,
74
+ type: DocumentDataType.print(f.type),
75
+ };
76
+ }
77
+ Field.toJson = toJson;
78
+ })(Field || (exports.Field = Field = {}));
4
79
  class DataSchema {
5
80
  constructor(schema_name, header_schema, item_schema) {
6
81
  this.schema_name = schema_name;
@@ -14,13 +89,13 @@ class DataSchema {
14
89
  if (!("schema_name" in json) || !("header_schema" in json) || !("item_schema" in json)) {
15
90
  throw new Error("DataSchema must have a schema_name, header_schema, and item_schema");
16
91
  }
17
- return new DataSchema(json.schema_name, json.header_schema, json.item_schema);
92
+ return new DataSchema(json.schema_name, json.header_schema.map(Field.fromJson), json.item_schema.map(Field.fromJson));
18
93
  }
19
94
  toJson() {
20
95
  return {
21
96
  schema_name: this.schema_name,
22
- header_schema: this.header_schema,
23
- item_schema: this.item_schema,
97
+ header_schema: this.header_schema.map(Field.toJson),
98
+ item_schema: this.item_schema.map(Field.toJson),
24
99
  };
25
100
  }
26
101
  }
package/dist/Elem.d.ts CHANGED
@@ -4,12 +4,29 @@ import { Color } from "./Layout";
4
4
  import { FontDict } from "./AnyLayout";
5
5
  import * as Row from "./Row";
6
6
  import { ItemContent } from "./Resume";
7
+ import { Field } from "./DataSchema";
8
+ import { Optional } from "./Utils";
9
+ import { Box } from "./Box";
10
+ export type Span = {
11
+ is_italic: boolean;
12
+ is_bold: boolean;
13
+ is_code: boolean;
14
+ text: string;
15
+ link: string | null;
16
+ font?: Font.t;
17
+ width?: number;
18
+ line?: number;
19
+ bbox?: Box;
20
+ };
7
21
  export type t = {
8
22
  tag: "Elem";
9
23
  item: string;
24
+ text?: string;
25
+ spans?: Span[];
10
26
  url: string | null;
11
27
  is_ref: boolean;
12
28
  is_fill: boolean;
29
+ is_markdown: boolean;
13
30
  text_width: Width.t;
14
31
  font: Font.t;
15
32
  margin: Margin.t;
@@ -18,13 +35,16 @@ export type t = {
18
35
  background_color: Color;
19
36
  };
20
37
  type Elem = t;
21
- export declare function elem(item: string, url: string | null, is_ref: boolean, is_fill: boolean, text_width: Width.t, font: Font.t, margin: Margin.t, alignment: Alignment.t, width: Width.t, background_color: Color): Elem;
38
+ export declare function elem(item: string, url: string | null, is_ref: boolean, is_fill: boolean, is_markdown: boolean, text_width: Width.t, font: Font.t, margin: Margin.t, alignment: Alignment.t, width: Width.t, background_color: Color): Elem;
22
39
  export declare function copy(e: Elem): {
23
40
  tag: "Elem";
24
41
  item: string;
42
+ text?: string;
43
+ spans?: Span[];
25
44
  url: string;
26
45
  is_ref: boolean;
27
46
  is_fill: boolean;
47
+ is_markdown: boolean;
28
48
  text_width: Width.t;
29
49
  font: Font.t;
30
50
  margin: Margin.t;
@@ -33,10 +53,6 @@ export declare function copy(e: Elem): {
33
53
  background_color: Layout.Color;
34
54
  };
35
55
  export declare function default_(): Elem;
36
- export type Optional<T> = {
37
- [P in keyof T]?: T[P];
38
- };
39
- export declare function with_(e: Elem, w: Optional<Elem>): Elem;
40
56
  export declare function from(w: Optional<Elem>): Elem;
41
57
  export declare function withItem(e: Elem, item: string): Elem;
42
58
  export declare function withUrl(e: Elem, url: string | null): Elem;
@@ -50,9 +66,10 @@ export declare function withAlignment(e: Elem, alignment: Alignment.t): Elem;
50
66
  export declare function withWidth(e: Elem, width: Width.t): Elem;
51
67
  export declare function withBackgroundColor(e: Elem, background_color: Color): Elem;
52
68
  export declare function scaleWidth(e: Elem, scale: number): Elem;
69
+ export declare function parseMarkdownItem(item: string): Span[];
53
70
  export declare function fillFonts(e: Elem, fonts: FontDict): Elem;
54
71
  export declare function justifiedLines(e: Elem, lines: Elem[], font_dict: FontDict): Row.t[];
55
72
  export declare function break_lines(e: Elem, font_dict: FontDict): Layout.t[];
56
73
  export declare function boundWidth(e: Elem, width: number): Elem;
57
- export declare function instantiate(e: Elem, section: Map<string, ItemContent>): Elem;
74
+ export declare function instantiate(e: Elem, section: Map<string, ItemContent>, fields: Field.t[]): Elem;
58
75
  export {};
package/dist/Elem.js CHANGED
@@ -23,18 +23,30 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.instantiate = exports.boundWidth = exports.break_lines = exports.justifiedLines = exports.fillFonts = exports.scaleWidth = exports.withBackgroundColor = exports.withWidth = exports.withAlignment = exports.withMargin = exports.withFont = exports.withTextWidth = exports.withIsFill = exports.asRef = exports.withIsRef = exports.withUrl = exports.withItem = exports.from = exports.with_ = exports.default_ = exports.copy = exports.elem = void 0;
26
+ exports.instantiate = exports.boundWidth = exports.break_lines = exports.justifiedLines = exports.fillFonts = exports.parseMarkdownItem = exports.scaleWidth = exports.withBackgroundColor = exports.withWidth = exports.withAlignment = exports.withMargin = exports.withFont = exports.withTextWidth = exports.withIsFill = exports.asRef = exports.withIsRef = exports.withUrl = exports.withItem = exports.from = exports.default_ = exports.copy = exports.elem = void 0;
27
27
  const Font = __importStar(require("./Font"));
28
28
  const _1 = require(".");
29
29
  const Row_1 = require("./Row");
30
30
  const Resume_1 = require("./Resume");
31
- function elem(item, url, is_ref, is_fill, text_width, font, margin, alignment, width, background_color) {
31
+ const marked = __importStar(require("marked"));
32
+ const ts_pattern_1 = require("ts-pattern");
33
+ const Utils_1 = require("./Utils");
34
+ function defaultSpanProps() {
35
+ return {
36
+ is_italic: false,
37
+ is_bold: false,
38
+ is_code: false,
39
+ is_link: false,
40
+ };
41
+ }
42
+ function elem(item, url, is_ref, is_fill, is_markdown, text_width, font, margin, alignment, width, background_color) {
32
43
  return {
33
44
  tag: "Elem",
34
45
  item,
35
46
  url,
36
47
  is_ref,
37
48
  is_fill,
49
+ is_markdown,
38
50
  text_width,
39
51
  font,
40
52
  margin,
@@ -55,6 +67,7 @@ function default_() {
55
67
  url: null,
56
68
  is_ref: false,
57
69
  is_fill: false,
70
+ is_markdown: false,
58
71
  text_width: _1.Width.default_(),
59
72
  font: Font.default_(),
60
73
  margin: _1.Margin.default_(),
@@ -64,10 +77,6 @@ function default_() {
64
77
  };
65
78
  }
66
79
  exports.default_ = default_;
67
- function with_(e, w) {
68
- return { ...e, ...w };
69
- }
70
- exports.with_ = with_;
71
80
  function from(w) {
72
81
  return { ...default_(), ...w };
73
82
  }
@@ -120,13 +129,94 @@ function scaleWidth(e, scale) {
120
129
  return withWidth(e, _1.Width.scale(e.width, scale));
121
130
  }
122
131
  exports.scaleWidth = scaleWidth;
132
+ function flatten(ts, sp) {
133
+ const spans = [];
134
+ for (const t of ts) {
135
+ spans.push(...flattenToken(t, sp));
136
+ }
137
+ return spans;
138
+ }
139
+ function flattenToken(t, sp) {
140
+ return (0, ts_pattern_1.match)(t)
141
+ .returnType()
142
+ .with({ type: "paragraph", tokens: ts_pattern_1.P.select("tokens") }, ({ tokens }) => {
143
+ return flatten(tokens, sp);
144
+ })
145
+ .with({ type: "strong", tokens: ts_pattern_1.P.select("tokens") }, ({ tokens }) => {
146
+ return flatten(tokens, { ...sp, is_bold: true });
147
+ })
148
+ .with({ type: "em", tokens: ts_pattern_1.P.select("tokens") }, ({ tokens }) => {
149
+ return flatten(tokens, { ...sp, is_italic: true });
150
+ })
151
+ .with({ type: "codespan", text: ts_pattern_1.P.select("text") }, ({ text }) => {
152
+ return [{ ...sp, is_code: true, text, link: null }];
153
+ })
154
+ .with({ type: "text", tokens: ts_pattern_1.P.select("tokens") }, ({ tokens }) => {
155
+ return flatten(tokens, sp);
156
+ })
157
+ .with({ type: "text", text: ts_pattern_1.P.select("text") }, ({ text }) => {
158
+ const result = [];
159
+ if (text.startsWith(" ")) {
160
+ result.push({ ...sp, text: " ", link: null });
161
+ }
162
+ result.push({ ...sp, text: text.trim(), link: null });
163
+ if (text.endsWith(" ")) {
164
+ result.push({ ...sp, text: " ", link: null });
165
+ }
166
+ else if (text.endsWith("\n")) {
167
+ result.push({ ...sp, text: "\n", link: null });
168
+ }
169
+ return result;
170
+ })
171
+ .otherwise((e) => {
172
+ // console.log(`Unknown token type: ${JSON.stringify(e)}`);
173
+ return [{ ...defaultSpanProps(), text: e.raw, link: null }];
174
+ });
175
+ }
176
+ function parseMarkdownItem(item) {
177
+ const spans = [];
178
+ for (const token of marked.lexer(item)) {
179
+ spans.push(...flatten([token], defaultSpanProps()));
180
+ }
181
+ return spans;
182
+ }
183
+ exports.parseMarkdownItem = parseMarkdownItem;
123
184
  function fillFonts(e, fonts) {
124
- const text_width_with_font = Font.get_width(e.font, e.item, fonts);
185
+ const simpleSpans = e.is_markdown ? parseMarkdownItem(e.text) : [{ ...defaultSpanProps(), text: e.text, font: e.font, link: null }];
186
+ const spans = [];
187
+ for (const span of simpleSpans) {
188
+ const font = e.is_markdown ? (0, Utils_1.with_)(e.font, ({
189
+ // style: span.is_italic ? "Italic" : "Normal",
190
+ weight: span.is_bold ? "Bold" : "Medium",
191
+ })) : e.font;
192
+ if (span.text === " ") {
193
+ const width = Font.get_width(font, "-", fonts);
194
+ spans.push({ ...span, font, width });
195
+ continue;
196
+ }
197
+ if (span.text === "\n\n") {
198
+ spans.push({ ...span, font, width: 0 });
199
+ continue;
200
+ }
201
+ const words = span.text.split(/\s+/);
202
+ words.forEach((word, index) => {
203
+ const width = Font.get_width(font, word, fonts);
204
+ spans.push({ ...span, text: word, font, width });
205
+ if (index < words.length - 1) {
206
+ spans.push({ ...span, text: " ", font, width: Font.get_width(font, " ", fonts) });
207
+ }
208
+ });
209
+ }
210
+ const text_width = spans.reduce((acc, span) => acc + span.width, 0);
125
211
  if (e.is_fill) {
126
- return withTextWidth(withWidth(e, _1.Width.absolute(Math.min(_1.Width.get_fixed_unchecked(e.width), text_width_with_font))), _1.Width.absolute(text_width_with_font));
212
+ return (0, Utils_1.with_)(e, {
213
+ width: _1.Width.absolute(Math.min(_1.Width.get_fixed_unchecked(e.width), text_width)),
214
+ text_width: _1.Width.absolute(text_width),
215
+ spans
216
+ });
127
217
  }
128
218
  else {
129
- return withTextWidth(e, _1.Width.absolute(text_width_with_font));
219
+ return (0, Utils_1.with_)(e, { text_width: _1.Width.absolute(text_width), spans });
130
220
  }
131
221
  }
132
222
  exports.fillFonts = fillFonts;
@@ -137,7 +227,7 @@ function justifiedLines(e, lines, font_dict) {
137
227
  const r = (0, Row_1.row)([], line.margin, line.alignment, line.width, false, false);
138
228
  words.forEach(word => {
139
229
  const word_width = Font.get_width(e.font, word, font_dict);
140
- r.elements.push(elem(word, null, false, false, _1.Width.absolute(word_width), this.font, _1.Margin.default_(), _1.Alignment.default_(), _1.Width.absolute(word_width), this.background_color));
230
+ r.elements.push(elem(word, null, false, false, false, _1.Width.absolute(word_width), this.font, _1.Margin.default_(), _1.Alignment.default_(), _1.Width.absolute(word_width), this.background_color));
141
231
  });
142
232
  rowLines.push(Row_1.row);
143
233
  }
@@ -152,7 +242,7 @@ function break_lines(e, font_dict) {
152
242
  const lines = [];
153
243
  // todo: I'm sure this implementation is pretty buggy. Note to future me, fix
154
244
  // this.
155
- const words = e.item.split(/\s+/);
245
+ const words = e.text.split(/\s+/);
156
246
  const widths = words.map((word) => Font.get_width(e.font, word, font_dict));
157
247
  const space_width = Font.get_width(e.font, " ", font_dict);
158
248
  let start = 0;
@@ -189,20 +279,25 @@ function boundWidth(e, width) {
189
279
  }
190
280
  }
191
281
  exports.boundWidth = boundWidth;
192
- function instantiate(e, section) {
282
+ function instantiate(e, section, fields) {
193
283
  if (!e.is_ref) {
194
284
  return e;
195
285
  }
286
+ const itemType = fields.find(f => f.name === e.item);
287
+ if (itemType.type.tag === "MarkdownString") {
288
+ e.is_markdown = true;
289
+ }
196
290
  const text = section.get(e.item);
291
+ console.log(`Instantiating ${e.item} with ${JSON.stringify(text)}`);
197
292
  if (text === undefined) {
198
- return withIsRef(withItem(e, ""), false);
293
+ return (0, Utils_1.with_)(e, { is_ref: false, text: "" });
199
294
  }
200
295
  else {
201
296
  if (text.tag === "Url") {
202
- return withIsRef(withUrl(withItem(e, text.value.text), text.value.url), false);
297
+ return (0, Utils_1.with_)(e, { is_ref: false, text: text.value.text, url: text.value.url });
203
298
  }
204
299
  else {
205
- return withIsRef(withItem(e, Resume_1.ItemContent.toString(text)), false);
300
+ return (0, Utils_1.with_)(e, { is_ref: false, text: Resume_1.ItemContent.toString(text) });
206
301
  }
207
302
  }
208
303
  }
package/dist/Layout.d.ts CHANGED
@@ -4,11 +4,12 @@ import * as Width from "./Width";
4
4
  import * as Font from "./Font";
5
5
  import { ItemContent } from "./Resume";
6
6
  import { Box } from "./Box";
7
- import { FontDict } from "./AnyLayout";
7
+ import { ElementPath, FontDict } from "./AnyLayout";
8
8
  import { Point } from "./Point";
9
9
  import * as Stack from "./Stack";
10
10
  import * as Row from "./Row";
11
11
  import * as Elem from "./Elem";
12
+ import { Field } from "./DataSchema";
12
13
  export type Container = Stack.t | Row.t;
13
14
  export type t = Stack.t | Row.t | Elem.t;
14
15
  type Layout = t;
@@ -23,7 +24,9 @@ export type RenderedRow = Row.t & {
23
24
  export type RenderedElem = Elem.t & {
24
25
  bounding_box: Box;
25
26
  };
26
- export type RenderedLayout = RenderedStack | RenderedRow | RenderedElem;
27
+ export type RenderedLayout = (RenderedStack | RenderedRow | RenderedElem) & {
28
+ path?: ElementPath;
29
+ };
27
30
  export declare function default_(tag: string): Stack.t | Row.t | Elem.t;
28
31
  export declare function empty(): Layout;
29
32
  export declare function fromJson(json: unknown): Layout;
@@ -39,7 +42,7 @@ export declare function withAlignment(l: Layout, alignment: Alignment.t): Layout
39
42
  export declare function withWidth(l: Layout, width: Width.t): Layout;
40
43
  export declare function totalElementsWidth(l: Layout): number;
41
44
  export declare function isInstantiated(l: Layout): boolean;
42
- export declare function instantiate(l: Layout, section: Map<string, ItemContent>): Layout;
45
+ export declare function instantiate(l: Layout, section: Map<string, ItemContent>, fields: Field.t[]): Layout;
43
46
  export declare function boundWidth(l: Layout, width: number): Layout;
44
47
  export declare function scaleWidth(l: Layout, document_width: number): Layout;
45
48
  export declare function normalize(l: Layout, width: number, font_dict: FontDict): Layout;
package/dist/Layout.js CHANGED
@@ -63,6 +63,7 @@ function fromJson(json) {
63
63
  case 'Text': {
64
64
  const inner = default_(key);
65
65
  inner.item = json[key].item;
66
+ inner.text = json[key].item;
66
67
  inner.margin = json[key].margin;
67
68
  inner.alignment = json[key].alignment;
68
69
  inner.width = Width.fromJson(json[key].width);
@@ -209,14 +210,14 @@ function isInstantiated(l) {
209
210
  }
210
211
  }
211
212
  exports.isInstantiated = isInstantiated;
212
- function instantiate(l, section) {
213
+ function instantiate(l, section, fields) {
213
214
  switch (l.tag) {
214
215
  case "Stack":
215
- return Stack.instantiate(l, section);
216
+ return Stack.withElements(l, l.elements.map(e => instantiate(e, section, fields)));
216
217
  case "Row":
217
- return Row.instantiate(l, section);
218
+ return Row.withElements(l, l.elements.map(e => instantiate(e, section, fields)));
218
219
  case "Elem":
219
- return Elem.instantiate(l, section);
220
+ return Elem.instantiate(l, section, fields);
220
221
  }
221
222
  }
222
223
  exports.instantiate = instantiate;
@@ -266,9 +267,10 @@ function normalize(l, width, font_dict) {
266
267
  console.debug("Widths are bounded. Filling fonts...");
267
268
  const font_filled_layout = fillFonts(bounded_layout, font_dict);
268
269
  console.debug("Fonts filled. Breaking lines...");
269
- const broken_layout = breakLines(font_filled_layout, font_dict);
270
+ // const broken_layout = breakLines(font_filled_layout, font_dict);
270
271
  console.debug("Lines broken.");
271
- return broken_layout;
272
+ // return broken_layout;
273
+ return font_filled_layout;
272
274
  }
273
275
  exports.normalize = normalize;
274
276
  function fillFonts(l, font_dict) {
@@ -354,7 +356,11 @@ function computeTextboxPositions(l, top_left, font_dict) {
354
356
  top_left = top_left.move_y_to(depth);
355
357
  renderedElements.push(result.renderedLayout);
356
358
  }
357
- return { depth, renderedLayout: { ...l, bounding_box: new Box_1.Box(originalTopLeft, top_left.move_x_by(Width.get_fixed_unchecked(stack.width))), elements: renderedElements } };
359
+ return {
360
+ depth, renderedLayout: {
361
+ ...l, bounding_box: new Box_1.Box(originalTopLeft, top_left.move_x_by(Width.get_fixed_unchecked(stack.width))), elements: renderedElements
362
+ }
363
+ };
358
364
  }
359
365
  case "Row": {
360
366
  const row = l;
@@ -380,29 +386,41 @@ function computeTextboxPositions(l, top_left, font_dict) {
380
386
  top_left.move_x_by(Width.get_fixed_unchecked(element.width) + per_elem_space);
381
387
  renderedElements.push(result.renderedLayout);
382
388
  }
383
- return { depth, renderedLayout: { ...l, bounding_box: new Box_1.Box(originalTopLeft, originalTopLeft.move_y_by(depth).move_x_by(Width.get_fixed_unchecked(row.width))), elements: renderedElements } };
389
+ return {
390
+ depth, renderedLayout: {
391
+ ...l, bounding_box: new Box_1.Box(originalTopLeft, originalTopLeft.move_y_by(depth).move_x_by(Width.get_fixed_unchecked(row.width))), elements: renderedElements
392
+ }
393
+ };
384
394
  }
385
395
  case "Elem": {
386
396
  const elem = l;
387
397
  if (elem.is_ref) {
388
398
  throw new Error("Cannot compute textbox positions of uninstantiated layout");
389
399
  }
390
- if (elem.item === "") {
391
- return { depth, renderedLayout: { ...l, bounding_box: new Box_1.Box(top_left, top_left) } };
392
- }
393
400
  const height = Font.get_height(elem.font, font_dict);
394
401
  const width = Width.get_fixed_unchecked(elem.text_width);
395
402
  top_left = top_left.move_y_by(elem.margin.top).move_x_by(elem.margin.left);
396
- switch (elem.alignment) {
397
- case "Center":
398
- top_left = top_left.move_x_by((Width.get_fixed_unchecked(elem.width) - width) / 2.0);
399
- break;
400
- case "Right":
401
- top_left = top_left.move_x_by(Width.get_fixed_unchecked(elem.width) - width);
402
- break;
403
- }
404
- const textbox = new Box_1.Box(top_left, top_left.move_x_by(width).move_y_by(height));
405
- return { depth: top_left.y + height, renderedLayout: { ...l, bounding_box: textbox } };
403
+ let line = 1;
404
+ let cursor = top_left.x;
405
+ elem.spans.forEach(span => {
406
+ if (cursor - top_left.x + span.width > Width.get_fixed_unchecked(elem.width) - elem.margin.right || span.text === "\n\n") {
407
+ cursor = top_left.x;
408
+ line += 1;
409
+ }
410
+ span.bbox = new Box_1.Box(new Point_1.Point(cursor - top_left.x, (line - 1) * height), new Point_1.Point(cursor + span.width, line * height));
411
+ span.line = line;
412
+ cursor += span.width;
413
+ });
414
+ // switch (elem.alignment) {
415
+ // case "Center":
416
+ // top_left = top_left.move_x_by((Width.get_fixed_unchecked(elem.width) - width) / 2.0);
417
+ // break;
418
+ // case "Right":
419
+ // top_left = top_left.move_x_by(Width.get_fixed_unchecked(elem.width) - width);
420
+ // break;
421
+ // }
422
+ const textbox = new Box_1.Box(top_left, top_left.move_x_by(width).move_y_by(height * line));
423
+ return { depth: top_left.y + height * line, renderedLayout: { ...l, bounding_box: textbox } };
406
424
  }
407
425
  }
408
426
  }
@@ -5,6 +5,7 @@ import { DataSchema } from "./DataSchema";
5
5
  import { LayoutSchema } from "./LayoutSchema";
6
6
  import { ResumeLayout } from "./ResumeLayout";
7
7
  import { Storage } from "./Storage";
8
+ import * as Elem from "./Elem";
8
9
  import { Layout } from ".";
9
10
  export type RenderResult = {
10
11
  blob: Blob;
@@ -20,5 +21,6 @@ export type RenderProps = {
20
21
  fontDict?: FontDict;
21
22
  debug: boolean;
22
23
  };
23
- export declare const render: ({ resume_name, resume, data_schemas, layout_schemas, resume_layout, storage, fontDict, debug }: RenderProps) => Promise<RenderResult>;
24
+ export declare const render: ({ resume_name, resume, data_schemas, layout_schemas, resume_layout, storage, fontDict }: RenderProps) => Promise<RenderResult>;
25
+ export declare const mergeSpans: (spans: Elem.Span[]) => Elem.Span[];
24
26
  export declare const renderSectionLayout: (layout: Layout.RenderedLayout, resume_layout: ResumeLayout, current_height: number, doc: PDFKit.PDFDocument) => void;
package/dist/PdfLayout.js CHANGED
@@ -3,12 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.renderSectionLayout = exports.render = void 0;
6
+ exports.renderSectionLayout = exports.mergeSpans = exports.render = void 0;
7
7
  const blob_stream_1 = __importDefault(require("blob-stream"));
8
8
  const AnyLayout_1 = require("./AnyLayout");
9
9
  const pdfkit_1 = __importDefault(require("pdfkit"));
10
10
  const _1 = require(".");
11
- const render = async ({ resume_name, resume, data_schemas, layout_schemas, resume_layout, storage, fontDict, debug = false }) => {
11
+ const render = async ({ resume_name, resume, data_schemas, layout_schemas, resume_layout, storage, fontDict }) => {
12
12
  let start_time = Date.now();
13
13
  if (!resume && !resume_name) {
14
14
  throw "Rendering requires either resume_name or resume";
@@ -37,7 +37,7 @@ const render = async ({ resume_name, resume, data_schemas, layout_schemas, resum
37
37
  // doc.pipe(fs.createWriteStream('output.pdf'));
38
38
  const stream = doc.pipe((0, blob_stream_1.default)());
39
39
  start_time = Date.now();
40
- const [font_dict, layouts] = await (0, AnyLayout_1.render)({ layout_schemas, resume, data_schemas, resume_layout, storage, fontDict });
40
+ const layouts = await (0, AnyLayout_1.render)({ layout_schemas, resume, data_schemas, resume_layout, storage, fontDict });
41
41
  end_time = Date.now();
42
42
  console.info(`Rendering time: ${end_time - start_time}ms`);
43
43
  console.log("Constructing printpdf font dictionary...");
@@ -46,7 +46,7 @@ const render = async ({ resume_name, resume, data_schemas, layout_schemas, resum
46
46
  // "/Users/akeles/Programming/projects/cvdl/cvdl/assets/Exo/static/Exo-Medium.ttf");
47
47
  try {
48
48
  console.log("Registering fonts...");
49
- for (const [font_name, font] of font_dict.fonts.entries()) {
49
+ for (const [font_name, font] of fontDict.fonts.entries()) {
50
50
  console.log(`Registering font ${font_name}`);
51
51
  // @ts-ignore
52
52
  doc.registerFont(font_name, font.stream.buffer);
@@ -56,36 +56,16 @@ const render = async ({ resume_name, resume, data_schemas, layout_schemas, resum
56
56
  console.error(e);
57
57
  }
58
58
  console.log("Rendering the document...");
59
- // Render the boxes
60
- // for (const [index, boxes] of pages.entries()) {
61
- // if (index > 0) {
62
- // doc.addPage();
63
- // }
64
- // boxes.forEach((box) => {
65
- // const elements = box.elements;
66
- // // if (debug) {
67
- // // doc.rect(box.bounding_box.top_left.x, box.bounding_box.top_left.y, box.bounding_box.width(), box.bounding_box.height()).stroke();
68
- // // }
69
- // for (const [box_, element] of elements) {
70
- // console.log(
71
- // `(${box_.top_left.x}, ${box_.top_left.y})(${box_.bottom_right.x}, ${box_.bottom_right.y}): ${element.item}`
72
- // );
73
- // if (element.background_color !== "Transparent") {
74
- // doc.rect(box_.top_left.x, box_.top_left.y, box_.width(), box_.height()).fillAndStroke(ColorMap[element.background_color], ColorMap[element.background_color]);
75
- // }
76
- // // Make this more generic
77
- // doc.fillColor("black");
78
- // doc.
79
- // font(element.font.full_name()).
80
- // fontSize(element.font.size).
81
- // text(element.item, box_.top_left.x, box_.top_left.y, { lineBreak: false });
82
- // if (debug) {
83
- // // doc.rect(box_.top_left.x, box_.top_left.y, box_.width(), box_.height()).stroke();
84
- // }
85
- // }
86
- // });
87
- // }
88
59
  let current_height = 0;
60
+ // Add rulers to the document at every 10 pixels
61
+ doc.strokeColor("grey");
62
+ for (let i = 0; i < resume_layout.height; i += 10) {
63
+ doc.moveTo(0, i).lineTo(resume_layout.width, i).stroke();
64
+ }
65
+ for (let i = 0; i < resume_layout.width; i += 10) {
66
+ doc.moveTo(i, 0).lineTo(i, resume_layout.height).stroke();
67
+ }
68
+ doc.strokeColor("black");
89
69
  for (const layout of layouts) {
90
70
  (0, exports.renderSectionLayout)(layout, resume_layout, current_height, doc);
91
71
  current_height += layout.bounding_box.height() + layout.margin.top + layout.margin.bottom;
@@ -103,6 +83,27 @@ const render = async ({ resume_name, resume, data_schemas, layout_schemas, resum
103
83
  });
104
84
  };
105
85
  exports.render = render;
86
+ const mergeSpans = (spans) => {
87
+ const merged_spans = [];
88
+ let currentSpan = spans[0];
89
+ for (let i = 1; i < spans.length; i++) {
90
+ if (currentSpan.bbox.top_left.y === spans[i].bbox.top_left.y
91
+ && currentSpan.font === spans[i].font
92
+ && currentSpan.is_code === spans[i].is_code
93
+ && currentSpan.is_bold === spans[i].is_bold
94
+ && currentSpan.is_italic === spans[i].is_italic) {
95
+ currentSpan.text += spans[i].text;
96
+ currentSpan.bbox.bottom_right = spans[i].bbox.bottom_right;
97
+ }
98
+ else {
99
+ merged_spans.push(currentSpan);
100
+ currentSpan = spans[i];
101
+ }
102
+ }
103
+ merged_spans.push(currentSpan);
104
+ return merged_spans;
105
+ };
106
+ exports.mergeSpans = mergeSpans;
106
107
  const renderSectionLayout = (layout, resume_layout, current_height, doc) => {
107
108
  switch (layout.tag) {
108
109
  case "Stack": {
@@ -121,10 +122,31 @@ const renderSectionLayout = (layout, resume_layout, current_height, doc) => {
121
122
  }
122
123
  case "Elem": {
123
124
  const elem = layout;
124
- doc.
125
- font(_1.Font.full_name(elem.font)).
126
- fontSize(elem.font.size).
127
- text(elem.item, layout.bounding_box.top_left.x + resume_layout.margin.left, layout.bounding_box.top_left.y + resume_layout.margin.top + current_height, { lineBreak: false });
125
+ elem.spans.forEach((span) => {
126
+ console.log("Rendering span:", span);
127
+ doc.
128
+ font(_1.Font.full_name(span.font)).
129
+ fontSize(span.font.size).
130
+ text(span.text, layout.bounding_box.top_left.x
131
+ + resume_layout.margin.left
132
+ + span.bbox.top_left.x, layout.bounding_box.top_left.y
133
+ + resume_layout.margin.top
134
+ + current_height
135
+ + span.bbox.top_left.y, { lineBreak: false });
136
+ if (span.is_code) {
137
+ // Add a rounded rectangle around the code
138
+ doc.roundedRect(layout.bounding_box.top_left.x + resume_layout.margin.left + span.bbox.top_left.x - span.font.size / 5, layout.bounding_box.top_left.y + resume_layout.margin.top + current_height + span.bbox.top_left.y, span.bbox.width() + span.font.size / 5 * 2, span.bbox.height(), 5).stroke();
139
+ // Add a background color to the code
140
+ doc.fillColor("black");
141
+ doc.fillOpacity(0.05);
142
+ doc.rect(layout.bounding_box.top_left.x + resume_layout.margin.left + span.bbox.top_left.x - span.font.size / 5, layout.bounding_box.top_left.y + resume_layout.margin.top + current_height + span.bbox.top_left.y, span.bbox.width() + span.font.size / 5 * 2, span.bbox.height()).fill();
143
+ doc.fillOpacity(1);
144
+ }
145
+ });
146
+ // doc.
147
+ // font(Font.full_name(elem.font)).
148
+ // fontSize(elem.font.size).
149
+ // text(elem.item, layout.bounding_box.top_left.x + resume_layout.margin.left, layout.bounding_box.top_left.y + resume_layout.margin.top + current_height, { lineBreak: false });
128
150
  break;
129
151
  }
130
152
  }
package/dist/Row.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { Alignment, Margin, Width } from ".";
2
2
  import { FontDict } from "./AnyLayout";
3
- import { Optional } from "./Elem";
3
+ import { Optional } from "./Utils";
4
4
  import * as Layout from "./Layout";
5
- import { ItemContent } from "./Resume";
6
5
  export type t = {
7
6
  tag: "Row";
8
7
  elements: Layout.t[];
@@ -13,12 +12,10 @@ export type t = {
13
12
  is_fill: boolean;
14
13
  };
15
14
  type Row = t;
16
- export declare function with_(e: Row, w: Optional<Row>): Row;
17
15
  export declare function from(w: Optional<Row>): Row;
18
16
  export declare function row(elements: Layout.t[], margin: Margin.t, alignment: Alignment.t, width: Width.t, is_frozen: boolean, is_fill: boolean): Row;
19
17
  export declare function copy(r: Row): Row;
20
18
  export declare function default_(): Row;
21
- export declare function instantiate(r: Row, section: Map<string, ItemContent>): Row;
22
19
  export declare function withElements(r: Row, elements: Layout.t[]): Row;
23
20
  export declare function withMargin(r: Row, margin: Margin.t): Row;
24
21
  export declare function withAlignment(r: Row, alignment: Alignment.t): Row;
package/dist/Row.js CHANGED
@@ -23,13 +23,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.breakLines = exports.scaleWidth = exports.boundWidth = exports.elementsWidth = exports.withFill = exports.withFrozen = exports.withWidth = exports.withAlignment = exports.withMargin = exports.withElements = exports.instantiate = exports.default_ = exports.copy = exports.row = exports.from = exports.with_ = void 0;
26
+ exports.breakLines = exports.scaleWidth = exports.boundWidth = exports.elementsWidth = exports.withFill = exports.withFrozen = exports.withWidth = exports.withAlignment = exports.withMargin = exports.withElements = exports.default_ = exports.copy = exports.row = exports.from = void 0;
27
27
  const _1 = require(".");
28
28
  const Layout = __importStar(require("./Layout"));
29
- function with_(e, w) {
30
- return { ...e, ...w };
31
- }
32
- exports.with_ = with_;
33
29
  function from(w) {
34
30
  return { ...default_(), ...w };
35
31
  }
@@ -64,10 +60,6 @@ function default_() {
64
60
  };
65
61
  }
66
62
  exports.default_ = default_;
67
- function instantiate(r, section) {
68
- return withElements(r, r.elements.map(e => Layout.instantiate(e, section)));
69
- }
70
- exports.instantiate = instantiate;
71
63
  function withElements(r, elements) {
72
64
  return {
73
65
  ...r,
package/dist/Stack.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Alignment, Layout, Margin, Width } from ".";
2
- import { Optional } from "./Elem";
3
- import { ItemContent } from "./Resume";
2
+ import { Optional } from "./Utils";
4
3
  export type t = {
5
4
  tag: "Stack";
6
5
  elements: Layout.t[];
@@ -10,12 +9,10 @@ export type t = {
10
9
  is_fill: boolean;
11
10
  };
12
11
  type Stack = t;
13
- export declare function with_(e: Stack, w: Optional<Stack>): Stack;
14
12
  export declare function from(w: Optional<Stack>): Stack;
15
13
  export declare function stack(elements: Layout.t[], margin: Margin.t, alignment: Alignment.t, width: Width.t, is_fill: boolean): Stack;
16
14
  export declare function copy(s: Stack): Stack;
17
15
  export declare function default_(): Stack;
18
- export declare function instantiate(s: Stack, section: Map<string, ItemContent>): Stack;
19
16
  export declare function withElements(s: Stack, elements: Layout.t[]): Stack;
20
17
  export declare function withMargin(s: Stack, margin: Margin.t): Stack;
21
18
  export declare function withAlignment(s: Stack, alignment: Alignment.t): Stack;
package/dist/Stack.js CHANGED
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scaleWidth = exports.boundWidth = exports.withIsFill = exports.withWidth = exports.withAlignment = exports.withMargin = exports.withElements = exports.instantiate = exports.default_ = exports.copy = exports.stack = exports.from = exports.with_ = void 0;
3
+ exports.scaleWidth = exports.boundWidth = exports.withIsFill = exports.withWidth = exports.withAlignment = exports.withMargin = exports.withElements = exports.default_ = exports.copy = exports.stack = exports.from = void 0;
4
4
  const _1 = require(".");
5
- function with_(e, w) {
6
- return { ...e, ...w };
7
- }
8
- exports.with_ = with_;
9
5
  function from(w) {
10
6
  return { ...default_(), ...w };
11
7
  }
@@ -38,10 +34,6 @@ function default_() {
38
34
  };
39
35
  }
40
36
  exports.default_ = default_;
41
- function instantiate(s, section) {
42
- return withElements(s, s.elements.map(e => _1.Layout.instantiate(e, section)));
43
- }
44
- exports.instantiate = instantiate;
45
37
  function withElements(s, elements) {
46
38
  return {
47
39
  ...s,
@@ -0,0 +1,5 @@
1
+ export type Optional<T> = {
2
+ [P in keyof T]?: T[P];
3
+ };
4
+ export declare function with_<T>(e: T, w: Optional<T>): T;
5
+ export declare const EPSILON = 5;
package/dist/Utils.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EPSILON = exports.with_ = void 0;
4
+ function with_(e, w) {
5
+ return { ...e, ...w };
6
+ }
7
+ exports.with_ = with_;
8
+ exports.EPSILON = 5;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cvdl-ts",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Typescript Implementation of CVDL Compiler",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,13 +13,19 @@
13
13
  "@types/pdfkit": "^0.13.1",
14
14
  "blob-stream": "^0.1.3",
15
15
  "fontkit": "^2.0.2",
16
- "pdfkit": "^0.13.0"
16
+ "marked": "^13.0.2",
17
+ "pdfkit": "^0.13.0",
18
+ "ts-pattern": "^5.2.0"
17
19
  },
18
20
  "devDependencies": {
21
+ "@eslint/compat": "^1.1.1",
22
+ "@eslint/js": "^9.7.0",
19
23
  "@types/node": "^20.8.7",
20
24
  "@typescript-eslint/eslint-plugin": "^6.8.0",
21
25
  "@typescript-eslint/parser": "^6.8.0",
22
- "eslint": "^8.52.0"
26
+ "eslint-plugin-react": "^7.34.4",
27
+ "globals": "^15.8.0",
28
+ "typescript-eslint": "^7.16.1"
23
29
  },
24
30
  "scripts": {
25
31
  "lint": "eslint . --ext .ts",