node-opcua-xml2json 2.142.0 → 2.143.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.
@@ -0,0 +1,259 @@
1
+ 'use strict';
2
+
3
+ import { EventEmitter } from "events";
4
+ import { unescapeXML } from "../escape";
5
+
6
+ const STATE_TEXT = 0;
7
+ const STATE_IGNORE_COMMENT = 1;
8
+ const STATE_IGNORE_INSTRUCTION = 2;
9
+ const STATE_TAG_NAME = 3;
10
+ const STATE_TAG = 4;
11
+ const STATE_ATTR_NAME = 5;
12
+ const STATE_ATTR_EQ = 6;
13
+ const STATE_ATTR_QUOT = 7;
14
+ const STATE_ATTR_VALUE = 8;
15
+ const STATE_CDATA = 9;
16
+ const STATE_IGNORE_CDATA = 10;
17
+
18
+ export class SaxLtx extends EventEmitter {
19
+
20
+
21
+ #write: (data: string) => void;
22
+ constructor() {
23
+ super();
24
+
25
+ function _handleTagOpening(
26
+ this: SaxLtx,
27
+ endTag: boolean| undefined,
28
+ tagName: string|undefined,
29
+ attrs: string
30
+ ) {
31
+ if (!endTag) {
32
+ this.emit("startElement", tagName, attrs);
33
+ if (selfClosing) {
34
+ this.emit("endElement", tagName, true);
35
+ }
36
+ } else {
37
+ this.emit("endElement", tagName, false);
38
+ }
39
+ };
40
+ let state = STATE_TEXT;
41
+ let remainder: string | null = null;
42
+ let parseRemainder: boolean = false;
43
+ let tagName: string | undefined;
44
+ let attrs: string | {} | undefined;
45
+ let endTag: boolean|undefined;
46
+ let selfClosing: boolean | undefined;
47
+ let attrQuote: number;
48
+ let attrQuoteChar: string;
49
+ let recordStart: number | undefined = 0;
50
+ let attrName: string|undefined;
51
+
52
+ this.#write = function write(data: string ) {
53
+ let pos = 0;
54
+
55
+ /* Anything from previous write()? */
56
+ if (remainder) {
57
+ data = remainder + data;
58
+ pos += !parseRemainder ? remainder.length : 0;
59
+ parseRemainder = false;
60
+ remainder = null;
61
+ }
62
+
63
+ function endRecording(): undefined | string {
64
+ if (typeof recordStart === "number") {
65
+ const recorded = data.slice(recordStart, pos);
66
+ recordStart = undefined;
67
+ return recorded;
68
+ }
69
+ return undefined;
70
+ }
71
+
72
+ for (; pos < data.length; pos++) {
73
+ switch (state) {
74
+ case STATE_TEXT: {
75
+ // if we're looping through text, fast-forward using indexOf to
76
+ // the next '<' character
77
+ const lt = data.indexOf("<", pos);
78
+ if (lt !== -1 && pos !== lt) {
79
+ pos = lt;
80
+ }
81
+
82
+ break;
83
+ }
84
+ case STATE_ATTR_VALUE: {
85
+ // if we're looping through an attribute, fast-forward using
86
+ // indexOf to the next end quote character
87
+ const quot = data.indexOf(attrQuoteChar, pos);
88
+ if (quot !== -1) {
89
+ pos = quot;
90
+ }
91
+
92
+ break;
93
+ }
94
+ case STATE_IGNORE_COMMENT: {
95
+ // if we're looping through a comment, fast-forward using
96
+ // indexOf to the first end-comment character
97
+ const endcomment = data.indexOf("-->", pos);
98
+ if (endcomment !== -1) {
99
+ pos = endcomment + 2; // target the '>' character
100
+ }
101
+
102
+ break;
103
+ }
104
+ case STATE_IGNORE_CDATA: {
105
+ // if we're looping through a CDATA, fast-forward using
106
+ // indexOf to the first end-CDATA character ]]>
107
+ const endCDATA = data.indexOf("]]>", pos);
108
+ if (endCDATA !== -1) {
109
+ pos = endCDATA + 2; // target the '>' character
110
+ }
111
+
112
+ break;
113
+ }
114
+ // No default
115
+ }
116
+
117
+ const c = data.charCodeAt(pos);
118
+ switch (state) {
119
+ case STATE_TEXT:
120
+ if (c === 60 /* < */) {
121
+ const text = endRecording();
122
+ if (text) {
123
+ this.emit("text", unescapeXML(text));
124
+ }
125
+ state = STATE_TAG_NAME;
126
+ recordStart = pos + 1;
127
+ attrs = {};
128
+ }
129
+ break;
130
+ case STATE_CDATA:
131
+ if (c === 93 /* ] */) {
132
+ if (data.substring(pos + 1, 2) === "]>") {
133
+ const cData = endRecording();
134
+ if (cData) {
135
+ this.emit("text", cData);
136
+ }
137
+ state = STATE_TEXT;
138
+ } else if (data.length < pos + 2) {
139
+ parseRemainder = true;
140
+ pos = data.length;
141
+ }
142
+ }
143
+ break;
144
+ case STATE_TAG_NAME:
145
+ if (c === 47 /* / */ && recordStart === pos) {
146
+ recordStart = pos + 1;
147
+ endTag = true;
148
+ } else if (c === 33 /* ! */) {
149
+ if (data.substring(pos + 1, 7) === "[CDATA[") {
150
+ recordStart = pos + 8;
151
+ state = STATE_CDATA;
152
+ } else if (
153
+ data.length < pos + 8 &&
154
+ "[CDATA[".startsWith(data.slice(pos + 1))
155
+ ) {
156
+ // We potentially have CDATA, but the chunk is ending; stop here and let the next write() decide
157
+ parseRemainder = true;
158
+ pos = data.length;
159
+ } else {
160
+ recordStart = undefined;
161
+ state = STATE_IGNORE_COMMENT;
162
+ }
163
+ } else if (c === 63 /* ? */) {
164
+ recordStart = undefined;
165
+ state = STATE_IGNORE_INSTRUCTION;
166
+ } else if (c <= 32 || c === 47 /* / */ || c === 62 /* > */) {
167
+ tagName = endRecording();
168
+ pos--;
169
+ state = STATE_TAG;
170
+ }
171
+ break;
172
+ case STATE_IGNORE_COMMENT:
173
+ if (c === 62 /* > */) {
174
+ const prevFirst = data.charCodeAt(pos - 1);
175
+ const prevSecond = data.charCodeAt(pos - 2);
176
+ if (
177
+ (prevFirst === 45 /* - */ && prevSecond === 45) /* - */ ||
178
+ (prevFirst === 93 /* ] */ && prevSecond === 93) /* ] */
179
+ ) {
180
+ state = STATE_TEXT;
181
+ }
182
+ }
183
+ break;
184
+ case STATE_IGNORE_INSTRUCTION:
185
+ if (c === 62 /* > */) {
186
+ const prev = data.charCodeAt(pos - 1);
187
+ if (prev === 63 /* ? */) {
188
+ state = STATE_TEXT;
189
+ }
190
+ }
191
+ break;
192
+ case STATE_TAG:
193
+ if (c === 62 /* > */) {
194
+ _handleTagOpening.call(this, endTag, tagName, attrs as any);
195
+ tagName = undefined;
196
+ attrs = undefined;
197
+ endTag = undefined;
198
+ selfClosing = undefined;
199
+ state = STATE_TEXT;
200
+ recordStart = pos + 1;
201
+ } else if (c === 47 /* / */) {
202
+ selfClosing = true;
203
+ } else if (c > 32) {
204
+ recordStart = pos;
205
+ state = STATE_ATTR_NAME;
206
+ }
207
+ break;
208
+ case STATE_ATTR_NAME:
209
+ if (c <= 32 || c === 61 /* = */) {
210
+ attrName = endRecording();
211
+ pos--;
212
+ state = STATE_ATTR_EQ;
213
+ }
214
+ break;
215
+ case STATE_ATTR_EQ:
216
+ if (c === 61 /* = */) {
217
+ state = STATE_ATTR_QUOT;
218
+ }
219
+ break;
220
+ case STATE_ATTR_QUOT:
221
+ if (c === 34 /* " */ || c === 39 /* ' */) {
222
+ attrQuote = c;
223
+ attrQuoteChar = c === 34 ? '"' : "'";
224
+ state = STATE_ATTR_VALUE;
225
+ recordStart = pos + 1;
226
+ }
227
+ break;
228
+ case STATE_ATTR_VALUE:
229
+ if (c === attrQuote) {
230
+ const value = unescapeXML(endRecording()!);
231
+ (attrs as any)[attrName!] = value;
232
+ attrName = undefined;
233
+ state = STATE_TAG;
234
+ }
235
+ break;
236
+ }
237
+ }
238
+
239
+ if (typeof recordStart === "number" && recordStart <= data.length) {
240
+ remainder = data.slice(recordStart);
241
+ recordStart = 0;
242
+ }
243
+ };
244
+ }
245
+
246
+
247
+ public write(data: string | Buffer) {
248
+ this.#write(data.toString());
249
+ }
250
+ public end(data?: string | undefined) {
251
+ if (data) {
252
+ this.write(data);
253
+ }
254
+
255
+ /* Uh, yeah */
256
+ this.write = function write() { };
257
+ }
258
+ }
259
+
@@ -3,29 +3,13 @@
3
3
  * node -> see if https://github.com/isaacs/sax-js could be used instead
4
4
  */
5
5
 
6
- // tslint:disable:max-classes-per-file
7
- // tslint:disable:no-var-requires
8
- // tslint:disable:unified-signatures
9
6
 
10
7
  import { assert } from "node-opcua-assert";
11
- const LtxParser = require("ltx/lib/parsers/ltx.js");
8
+ import { SaxLtx } from "./thirdparties/parser/lts";
12
9
 
13
10
  export type SimpleCallback = (err?: Error) => void;
14
11
  export type Callback<T> = (err?: Error | null, result?: T) => void;
15
12
 
16
- declare interface LtxParser {
17
- write(str: string): void;
18
-
19
- end(): void;
20
-
21
- on(eventName: "startElement", eventHandler: (name: string, attrs: XmlAttributes) => void): void;
22
-
23
- on(eventName: "endElement", eventHandler: (name: string) => void): void;
24
-
25
- on(eventName: "text", eventHandler: (name: string) => void): void;
26
-
27
- on(eventName: "close", eventHandler: () => void): void;
28
- }
29
13
 
30
14
  export interface Parser {
31
15
  [key: string]: ReaderState;
@@ -88,8 +72,8 @@ export interface IReaderState {
88
72
  _on_text(text: string): void;
89
73
  }
90
74
 
91
- export class ReaderStateBase {}
92
- export interface ReaderStateBase extends IReaderState {}
75
+ export class ReaderStateBase { }
76
+ export interface ReaderStateBase extends IReaderState { }
93
77
  /**
94
78
  * @private
95
79
  */
@@ -281,22 +265,8 @@ export class Xml2Json {
281
265
  this._promote(state, 0);
282
266
  }
283
267
 
284
- public parseStringSync(xml_text: string): Record<string, unknown> {
285
- let retValue: Record<string, unknown> = {};
286
- const parser = this._prepareParser((err: Error | null | undefined, r: Record<string, unknown>) => (retValue = r));
287
- parser.write(xml_text);
288
- parser.end();
289
- return retValue;
290
- }
291
- /**
292
- * @deprecated
293
- */
294
- public parseString(xml_text: string): Promise<any>;
295
- public parseString(xml_text: string, callback: Callback<any> | SimpleCallback): void;
296
- public parseString(xml_text: string, callback?: Callback<any> | SimpleCallback): any {
297
- const parser = this._prepareParser(callback!);
298
- parser.write(xml_text);
299
- parser.end();
268
+ public parseString(xml_text: string): Record<string, unknown> {
269
+ return this.__parseInternal(xml_text);
300
270
  }
301
271
  /**
302
272
  * @private
@@ -331,9 +301,8 @@ export class Xml2Json {
331
301
  * @private
332
302
  * @internal
333
303
  */
334
- protected _prepareParser(callback: Callback<any> | SimpleCallback): LtxParser {
335
- assert(typeof callback === "function");
336
- const parser = new LtxParser();
304
+ protected __parseInternal(data: string): Record<string, unknown> {
305
+ const parser = new SaxLtx();
337
306
  this.currentLevel = 0;
338
307
  parser.on("startElement", (name: string, attrs: XmlAttributes) => {
339
308
  const tag_ns = resolve_namespace(name);
@@ -361,16 +330,16 @@ export class Xml2Json {
361
330
  this.current_state._on_text(text);
362
331
  }
363
332
  });
364
- parser.once("close", () => {
365
- if (callback) {
366
- (callback as any)(null, (this.current_state! as any)._pojo);
367
- }
368
- });
369
- return parser;
333
+ parser.write(data);
334
+ parser.end("");
335
+ return (this.current_state! as any)._pojo;
336
+ /*
337
+ return await new Promise((resolve) => {
338
+ parser.once("close", () => {
339
+ resolve((this.current_state! as any)._pojo);
340
+ });
341
+ //parser.write(data);
342
+ parser.end(data);
343
+ })*/
370
344
  }
371
345
  }
372
-
373
- // tslint:disable:no-var-requires
374
- import { withCallback } from "thenify-ex";
375
- const opts = { multiArgs: false };
376
- Xml2Json.prototype.parseString = withCallback(Xml2Json.prototype.parseString, opts);