@kerebron/extension-codemirror 0.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/esm/utils.js ADDED
@@ -0,0 +1,199 @@
1
+ // From prosemirror guide
2
+ import { Selection, TextSelection, } from 'prosemirror-state';
3
+ import { setBlockType } from '@kerebron/editor/commands';
4
+ export const CodeBlockNodeName = 'code_block';
5
+ function nonEmpty(value) {
6
+ return value !== null && value !== undefined;
7
+ }
8
+ export function computeChange(oldVal, newVal) {
9
+ if (oldVal === newVal)
10
+ return null;
11
+ let start = 0;
12
+ let oldEnd = oldVal.length;
13
+ let newEnd = newVal.length;
14
+ while (start < oldEnd &&
15
+ oldVal.charCodeAt(start) === newVal.charCodeAt(start)) {
16
+ start += 1;
17
+ }
18
+ while (oldEnd > start &&
19
+ newEnd > start &&
20
+ oldVal.charCodeAt(oldEnd - 1) === newVal.charCodeAt(newEnd - 1)) {
21
+ oldEnd -= 1;
22
+ newEnd -= 1;
23
+ }
24
+ return { from: start, to: oldEnd, text: newVal.slice(start, newEnd) };
25
+ }
26
+ export const asProseMirrorSelection = (pmDoc, cmView, getPos) => {
27
+ const offset = (typeof getPos === 'function' ? getPos() || 0 : 0) + 1;
28
+ const anchor = cmView.state.selection.main.from + offset;
29
+ const head = cmView.state.selection.main.to + offset;
30
+ return TextSelection.create(pmDoc, anchor, head);
31
+ };
32
+ export const forwardSelection = (cmView, pmView, getPos) => {
33
+ if (!cmView.hasFocus)
34
+ return;
35
+ const selection = asProseMirrorSelection(pmView.state.doc, cmView, getPos);
36
+ if (!selection.eq(pmView.state.selection)) {
37
+ pmView.dispatch(pmView.state.tr.setSelection(selection));
38
+ }
39
+ };
40
+ export const valueChanged = (textUpdate, node, getPos, view) => {
41
+ const change = computeChange(node.textContent, textUpdate);
42
+ if (change && typeof getPos === 'function') {
43
+ const start = getPos() + 1;
44
+ let pmTr = view.state.tr;
45
+ pmTr = pmTr.replaceWith(start + change.from, start + change.to, change.text ? view.state.schema.text(change.text) : []);
46
+ view.dispatch(pmTr);
47
+ }
48
+ };
49
+ export const maybeEscape = (unit, dir, cm, view, getPos) => {
50
+ const sel = cm.state.selection.main;
51
+ const line = cm.state.doc.lineAt(sel.from);
52
+ const lastLine = cm.state.doc.lines;
53
+ if (sel.to !== sel.from ||
54
+ line.number !== (dir < 0 ? 1 : lastLine) ||
55
+ (unit === 'char' && sel.from !== (dir < 0 ? 0 : line.to)) ||
56
+ typeof getPos !== 'function') {
57
+ return false;
58
+ }
59
+ view.focus();
60
+ const node = view.state.doc.nodeAt(getPos());
61
+ if (!node)
62
+ return false;
63
+ const targetPos = getPos() + (dir < 0 ? 0 : node.nodeSize);
64
+ const selection = Selection.near(view.state.doc.resolve(targetPos), dir);
65
+ view.dispatch(view.state.tr.setSelection(selection).scrollIntoView());
66
+ view.focus();
67
+ return true;
68
+ };
69
+ export const backspaceHandler = (pmView, view) => {
70
+ const { selection } = view.state;
71
+ if (selection.main.empty && selection.main.from === 0) {
72
+ setBlockType(pmView.state.schema.nodes.paragraph)(pmView.state, pmView.dispatch);
73
+ setTimeout(() => pmView.focus(), 20);
74
+ return true;
75
+ }
76
+ return false;
77
+ };
78
+ export const setMode = async (lang, cmView, settings, languageConf) => {
79
+ const support = await settings.languageLoaders?.[lang]?.();
80
+ if (support) {
81
+ cmView.dispatch({
82
+ effects: languageConf.reconfigure(support),
83
+ });
84
+ }
85
+ };
86
+ const isTheme = (theme) => {
87
+ if (!Array.isArray(theme)) {
88
+ return false;
89
+ }
90
+ return theme.every((item) => item !== undefined &&
91
+ typeof item.extension === 'object' && // or whatever type Extension is
92
+ typeof item.name === 'string');
93
+ };
94
+ export const setTheme = async (cmView, themeConfig, theme) => {
95
+ if (isTheme(theme)) {
96
+ cmView.dispatch({
97
+ effects: themeConfig.reconfigure(theme),
98
+ });
99
+ }
100
+ };
101
+ const arrowHandler = (dir) => (state, dispatch, view) => {
102
+ if (state.selection.empty && view?.endOfTextblock(dir)) {
103
+ const side = dir === 'left' || dir === 'up' ? -1 : 1;
104
+ const { $head } = state.selection;
105
+ const nextPos = Selection.near(state.doc.resolve(side > 0 ? $head.after() : $head.before()), side);
106
+ if (nextPos.$head &&
107
+ nextPos.$head.parent.type.name === CodeBlockNodeName) {
108
+ dispatch?.(state.tr.setSelection(nextPos));
109
+ return true;
110
+ }
111
+ }
112
+ return false;
113
+ };
114
+ export const createCodeBlock = (state, dispatch, attributes) => {
115
+ const { $from, $to } = state.selection;
116
+ //if we are in a CodeBlock node we do nothing
117
+ const parentNode = $from.node($from.depth);
118
+ if (parentNode && parentNode.type.name === CodeBlockNodeName) {
119
+ return false;
120
+ }
121
+ //if from and to in the same paragraph
122
+ if ($from.parentOffset === $to.parentOffset &&
123
+ $from.parent.type.name === 'paragraph') {
124
+ const text = $from.parent.textContent;
125
+ const tr = state.tr;
126
+ const newNode = state.schema.nodes[CodeBlockNodeName].createAndFill(attributes, text ? [state.schema.text($from.parent.textContent)] : []);
127
+ if (newNode && dispatch) {
128
+ const pos = $from.before($from.depth);
129
+ tr.delete(pos, pos + $from.parent.nodeSize - 1);
130
+ tr.insert(pos, newNode);
131
+ tr.setSelection(TextSelection.create(tr.doc, $from.pos));
132
+ dispatch(tr);
133
+ }
134
+ return true;
135
+ }
136
+ if (dispatch) {
137
+ const tr = state.tr;
138
+ const slice = state.doc.slice($from.before($from.depth), $to.after($to.depth), true);
139
+ const content = slice.content.textBetween(0, slice.content.size, '\n');
140
+ const newNode = state.schema.nodes[CodeBlockNodeName].createAndFill(attributes, state.schema.text(content));
141
+ if (newNode) {
142
+ tr.delete($from.before(slice.openStart + 1), $to.after(slice.openEnd + 1));
143
+ tr.insert($from.before(slice.openStart + 1), newNode);
144
+ tr.setSelection(TextSelection.create(tr.doc, $from.pos, $from.pos + newNode.nodeSize - 2));
145
+ dispatch(tr);
146
+ }
147
+ }
148
+ return true;
149
+ };
150
+ export const removeCodeBlock = (state, dispatch) => {
151
+ const { $from } = state.selection;
152
+ const parentNode = $from.node($from.depth);
153
+ if (parentNode && parentNode.type.name === CodeBlockNodeName) {
154
+ const children = [];
155
+ parentNode.forEach((child) => {
156
+ children.push(child);
157
+ });
158
+ const childrenNodes = children
159
+ .map((child) => {
160
+ return state.schema.nodes.paragraph.createAndFill({}, [child]);
161
+ })
162
+ .filter(nonEmpty);
163
+ const tr = state.tr;
164
+ const pos = $from.before($from.depth);
165
+ tr.delete(pos, pos + parentNode.nodeSize - 1);
166
+ tr.insert(pos, childrenNodes);
167
+ tr.setSelection(TextSelection.create(tr.doc, $from.pos - 1));
168
+ if (dispatch) {
169
+ dispatch(tr.scrollIntoView());
170
+ }
171
+ }
172
+ return false;
173
+ };
174
+ export const toggleCodeBlock = (state, dispatch, attributes) => {
175
+ const { $from } = state.selection;
176
+ if ($from.pos === 0) {
177
+ return false;
178
+ }
179
+ const parentNode = $from.node($from.depth);
180
+ if (parentNode && parentNode.type.name === CodeBlockNodeName) {
181
+ return removeCodeBlock(state, dispatch);
182
+ }
183
+ else {
184
+ return createCodeBlock(state, dispatch, attributes);
185
+ }
186
+ };
187
+ export const codeBlockArrowHandlers = {
188
+ ArrowLeft: arrowHandler('left'),
189
+ ArrowRight: arrowHandler('right'),
190
+ ArrowUp: arrowHandler('up'),
191
+ ArrowDown: arrowHandler('down'),
192
+ };
193
+ export const codeBlockToggleShortcut = {
194
+ 'Mod-Alt-c': toggleCodeBlock,
195
+ };
196
+ export const codeBlockKeymap = {
197
+ ...codeBlockToggleShortcut,
198
+ ...codeBlockArrowHandlers,
199
+ };
@@ -0,0 +1,14 @@
1
+ import * as cmView from '@codemirror/view';
2
+ import { YSyncConfig } from './y-sync.js';
3
+ export declare const yRemoteSelectionsTheme: any;
4
+ export declare class YRemoteSelectionsPluginValue {
5
+ conf: YSyncConfig;
6
+ private _listener;
7
+ private _awareness;
8
+ decorations: cmView.DecorationSet;
9
+ constructor(view: cmView.EditorView);
10
+ destroy(): void;
11
+ update(update: cmView.ViewUpdate): void;
12
+ }
13
+ export declare const yRemoteSelections: any;
14
+ //# sourceMappingURL=y-remote-selections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"y-remote-selections.d.ts","sourceRoot":"","sources":["../src/y-remote-selections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAS3C,OAAO,EAAE,WAAW,EAAc,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,sBAAsB,KAuDjC,CAAC;AA8CH,qBAAa,4BAA4B;IACvC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,CAAC,SAAS,CAQP;IACV,OAAO,CAAC,UAAU,CAA8B;IAChD,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC;gBAEtB,IAAI,EAAE,MAAM,CAAC,UAAU;IAiBnC,OAAO;IAIP,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU;CA8HjC;AAED,eAAO,MAAM,iBAAiB,KAK7B,CAAC"}
@@ -0,0 +1,270 @@
1
+ import * as cmView from '@codemirror/view';
2
+ import * as cmState from '@codemirror/state';
3
+ import * as dom from 'lib0/dom';
4
+ import * as pair from 'lib0/pair';
5
+ import * as math from 'lib0/math';
6
+ import { ySyncFacet } from './y-sync.js';
7
+ export const yRemoteSelectionsTheme = cmView.EditorView.baseTheme({
8
+ '.cm-ySelection': {},
9
+ '.cm-yLineSelection': {
10
+ padding: 0,
11
+ margin: '0px 2px 0px 4px',
12
+ },
13
+ '.cm-ySelectionCaret': {
14
+ position: 'relative',
15
+ borderLeft: '1px solid black',
16
+ borderRight: '1px solid black',
17
+ marginLeft: '-1px',
18
+ marginRight: '-1px',
19
+ boxSizing: 'border-box',
20
+ display: 'inline',
21
+ },
22
+ '.cm-ySelectionCaretDot': {
23
+ borderRadius: '50%',
24
+ position: 'absolute',
25
+ width: '.4em',
26
+ height: '.4em',
27
+ top: '-.2em',
28
+ left: '-.2em',
29
+ backgroundColor: 'inherit',
30
+ transition: 'transform .3s ease-in-out',
31
+ boxSizing: 'border-box',
32
+ },
33
+ '.cm-ySelectionCaret:hover > .cm-ySelectionCaretDot': {
34
+ transformOrigin: 'bottom center',
35
+ transform: 'scale(0)',
36
+ },
37
+ '.cm-ySelectionInfo': {
38
+ position: 'absolute',
39
+ top: '-1.05em',
40
+ left: '-1px',
41
+ fontSize: '.75em',
42
+ fontFamily: 'serif',
43
+ fontStyle: 'normal',
44
+ fontWeight: 'normal',
45
+ lineHeight: 'normal',
46
+ userSelect: 'none',
47
+ color: 'white',
48
+ paddingLeft: '2px',
49
+ paddingRight: '2px',
50
+ zIndex: 101,
51
+ transition: 'opacity .3s ease-in-out',
52
+ backgroundColor: 'inherit',
53
+ // these should be separate
54
+ opacity: 0,
55
+ transitionDelay: '0s',
56
+ whiteSpace: 'nowrap',
57
+ },
58
+ '.cm-ySelectionCaret:hover > .cm-ySelectionInfo': {
59
+ opacity: 1,
60
+ transitionDelay: '0s',
61
+ },
62
+ });
63
+ const yRemoteSelectionsAnnotation = cmState.Annotation.define();
64
+ class YRemoteCaretWidget extends cmView.WidgetType {
65
+ constructor(color, name) {
66
+ super();
67
+ Object.defineProperty(this, "color", {
68
+ enumerable: true,
69
+ configurable: true,
70
+ writable: true,
71
+ value: color
72
+ });
73
+ Object.defineProperty(this, "name", {
74
+ enumerable: true,
75
+ configurable: true,
76
+ writable: true,
77
+ value: name
78
+ });
79
+ }
80
+ toDOM() {
81
+ return (dom.element('span', [
82
+ pair.create('class', 'ProseMirror-yjs-cursor ProseMirror-widget'),
83
+ pair.create('style', `border-color: ${this.color}; position: fixed;`),
84
+ ], [
85
+ dom.text('\u2060'),
86
+ dom.element('div', [
87
+ pair.create('style', `background-color: ${this.color}`),
88
+ ], [
89
+ dom.text(this.name),
90
+ ]),
91
+ dom.text('\u2060'),
92
+ ]));
93
+ }
94
+ eq(widget) {
95
+ return widget.color === this.color;
96
+ }
97
+ compare(widget) {
98
+ return widget.color === this.color;
99
+ }
100
+ updateDOM() {
101
+ return false;
102
+ }
103
+ get estimatedHeight() {
104
+ return -1;
105
+ }
106
+ ignoreEvent() {
107
+ return true;
108
+ }
109
+ }
110
+ export class YRemoteSelectionsPluginValue {
111
+ constructor(view) {
112
+ Object.defineProperty(this, "conf", {
113
+ enumerable: true,
114
+ configurable: true,
115
+ writable: true,
116
+ value: void 0
117
+ });
118
+ Object.defineProperty(this, "_listener", {
119
+ enumerable: true,
120
+ configurable: true,
121
+ writable: true,
122
+ value: void 0
123
+ });
124
+ Object.defineProperty(this, "_awareness", {
125
+ enumerable: true,
126
+ configurable: true,
127
+ writable: true,
128
+ value: void 0
129
+ });
130
+ Object.defineProperty(this, "decorations", {
131
+ enumerable: true,
132
+ configurable: true,
133
+ writable: true,
134
+ value: void 0
135
+ });
136
+ this.conf = view.state.facet(ySyncFacet);
137
+ this._listener = ({ added, updated, removed }, s, t) => {
138
+ const clients = added.concat(updated).concat(removed);
139
+ if (clients.findIndex((id) => id !== this.conf.awareness.doc.clientID) >= 0) {
140
+ view.dispatch({ annotations: [yRemoteSelectionsAnnotation.of([])] });
141
+ }
142
+ };
143
+ this._awareness = this.conf.awareness;
144
+ this._awareness.on('change', this._listener);
145
+ this.decorations = cmState.RangeSet.of([]);
146
+ }
147
+ destroy() {
148
+ this._awareness.off('change', this._listener);
149
+ }
150
+ update(update) {
151
+ const awareness = this.conf.awareness;
152
+ const decorations = [];
153
+ const localAwarenessState = this.conf.awareness.getLocalState();
154
+ // set local awareness state (update cursors)
155
+ if (localAwarenessState != null) {
156
+ const hasFocus = update.view.hasFocus &&
157
+ update.view.dom.ownerDocument.hasFocus();
158
+ const sel = hasFocus ? update.state.selection.main : null;
159
+ if (sel != null && 'function' === typeof this.conf.getPmPos) {
160
+ const nodePos = this.conf.getPmPos();
161
+ const currentAnchor = localAwarenessState['cm-cursor'] == null
162
+ ? -1
163
+ : localAwarenessState['cm-cursor'].anchor - nodePos;
164
+ const currentHead = localAwarenessState['cm-cursor'] == null
165
+ ? -1
166
+ : localAwarenessState['cm-cursor'].head - nodePos;
167
+ const anchor = nodePos + sel.anchor;
168
+ const head = nodePos + sel.head;
169
+ if (localAwarenessState['cm-cursor'] == null ||
170
+ (currentAnchor != anchor) || (currentHead != head)) {
171
+ awareness.setLocalStateField('cm-cursor', {
172
+ anchor,
173
+ head,
174
+ });
175
+ }
176
+ }
177
+ else if (localAwarenessState['cm-cursor'] != null && hasFocus) {
178
+ awareness.setLocalStateField('cm-cursor', null);
179
+ }
180
+ }
181
+ // update decorations (remote selections)
182
+ awareness.getStates().forEach((state, clientId) => {
183
+ if (clientId === awareness.doc.clientID) {
184
+ return;
185
+ }
186
+ const cursor = state['cm-cursor'];
187
+ if (cursor == null || cursor.anchor == null || cursor.head == null) {
188
+ return;
189
+ }
190
+ if ('function' === typeof this.conf.getPmPos) {
191
+ const nodeAnchor = this.conf.getPmPos();
192
+ const nodeHead = this.conf.getPmPos() + this.conf.getNode().nodeSize;
193
+ if (cursor.anchor >= nodeAnchor && cursor.anchor <= nodeHead) {
194
+ const anchor = { index: cursor.anchor - nodeAnchor };
195
+ const head = { index: cursor.head - nodeAnchor };
196
+ try {
197
+ const { color = '#ffa500', name = `User: ${clientId}` } = state.user || {};
198
+ const colorLight = (state.user && state.user.colorLight) ||
199
+ color + '33';
200
+ const start = math.min(anchor.index, head.index);
201
+ const end = math.max(anchor.index, head.index);
202
+ const startLine = update.view.state.doc.lineAt(start);
203
+ const endLine = update.view.state.doc.lineAt(end);
204
+ if (startLine.number === endLine.number) {
205
+ // selected content in a single line.
206
+ decorations.push({
207
+ from: start,
208
+ to: end,
209
+ value: cmView.Decoration.mark({
210
+ attributes: { style: `background-color: ${colorLight}` },
211
+ class: 'cm-ySelection',
212
+ }),
213
+ });
214
+ }
215
+ else {
216
+ // selected content in multiple lines
217
+ // first, render text-selection in the first line
218
+ decorations.push({
219
+ from: start,
220
+ to: startLine.from + startLine.length,
221
+ value: cmView.Decoration.mark({
222
+ attributes: { style: `background-color: ${colorLight}` },
223
+ class: 'cm-ySelection',
224
+ }),
225
+ });
226
+ // render text-selection in the last line
227
+ decorations.push({
228
+ from: endLine.from,
229
+ to: end,
230
+ value: cmView.Decoration.mark({
231
+ attributes: { style: `background-color: ${colorLight}` },
232
+ class: 'cm-ySelection',
233
+ }),
234
+ });
235
+ for (let i = startLine.number + 1; i < endLine.number; i++) {
236
+ const linePos = update.view.state.doc.line(i).from;
237
+ decorations.push({
238
+ from: linePos,
239
+ to: linePos,
240
+ value: cmView.Decoration.line({
241
+ attributes: {
242
+ style: `background-color: ${colorLight}`,
243
+ class: 'cm-yLineSelection',
244
+ },
245
+ }),
246
+ });
247
+ }
248
+ }
249
+ decorations.push({
250
+ from: head.index,
251
+ to: head.index,
252
+ value: cmView.Decoration.widget({
253
+ side: head.index - anchor.index > 0 ? -1 : 1, // the local cursor should be rendered outside the remote selection
254
+ block: false,
255
+ widget: new YRemoteCaretWidget(color, name),
256
+ }),
257
+ });
258
+ }
259
+ catch (err) {
260
+ console.warn(err, `User: ${clientId}`);
261
+ }
262
+ }
263
+ }
264
+ });
265
+ this.decorations = cmView.Decoration.set(decorations, true);
266
+ }
267
+ }
268
+ export const yRemoteSelections = cmView.ViewPlugin.fromClass(YRemoteSelectionsPluginValue, {
269
+ decorations: (v) => v.decorations,
270
+ });
@@ -0,0 +1,11 @@
1
+ import * as cmState from '@codemirror/state';
2
+ import { Node } from 'prosemirror-model';
3
+ import * as awarenessProtocol from 'y-protocols/awareness';
4
+ export declare class YSyncConfig {
5
+ getNode: () => Node;
6
+ getPmPos: boolean | (() => number);
7
+ awareness: awarenessProtocol.Awareness;
8
+ constructor(getNode: () => Node, getPmPos: boolean | (() => number), awareness: awarenessProtocol.Awareness);
9
+ }
10
+ export declare const ySyncFacet: cmState.Facet<YSyncConfig, YSyncConfig>;
11
+ //# sourceMappingURL=y-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"y-sync.d.ts","sourceRoot":"","sources":["../src/y-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAE3D,qBAAa,WAAW;IAEb,OAAO,EAAE,MAAM,IAAI;IACnB,QAAQ,EAAE,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC;IAClC,SAAS,EAAE,iBAAiB,CAAC,SAAS;gBAFtC,OAAO,EAAE,MAAM,IAAI,EACnB,QAAQ,EAAE,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,EAClC,SAAS,EAAE,iBAAiB,CAAC,SAAS;CAGhD;AAED,eAAO,MAAM,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,CAK3D,CAAC"}
package/esm/y-sync.js ADDED
@@ -0,0 +1,29 @@
1
+ import * as cmState from '@codemirror/state'; // eslint-disable-line
2
+ export class YSyncConfig {
3
+ constructor(getNode, getPmPos, awareness) {
4
+ Object.defineProperty(this, "getNode", {
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true,
8
+ value: getNode
9
+ });
10
+ Object.defineProperty(this, "getPmPos", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: getPmPos
15
+ });
16
+ Object.defineProperty(this, "awareness", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: awareness
21
+ });
22
+ }
23
+ }
24
+ export const ySyncFacet = cmState.Facet
25
+ .define({
26
+ combine(inputs) {
27
+ return inputs[inputs.length - 1];
28
+ },
29
+ });
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@kerebron/extension-codemirror",
3
+ "version": "0.0.1",
4
+ "license": "MIT",
5
+ "module": "./esm/ExtensionCodeMirror.js",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./esm/ExtensionCodeMirror.js"
9
+ },
10
+ "./NodeDocumentCode": {
11
+ "import": "./esm/NodeDocumentCode.js"
12
+ }
13
+ },
14
+ "devDependencies": {
15
+ "@types/node": "^20.9.0"
16
+ },
17
+ "_generatedBy": "dnt@dev"
18
+ }