@dra2020/baseclient 1.0.13 → 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.
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/all/all.d.ts +36 -0
- package/dist/all/allclient.d.ts +18 -0
- package/dist/base.js +33010 -0
- package/dist/base.js.map +1 -0
- package/dist/baseclient.js +8991 -0
- package/dist/baseclient.js.map +1 -0
- package/dist/context/all.d.ts +1 -0
- package/dist/context/context.d.ts +13 -0
- package/dist/dbabstract/all.d.ts +1 -0
- package/dist/dbabstract/db.d.ts +83 -0
- package/dist/dbdynamo/all.d.ts +1 -0
- package/dist/dbdynamo/dbdynamo.d.ts +190 -0
- package/dist/filterexpr/all.d.ts +1 -0
- package/dist/filterexpr/filterexpr.d.ts +64 -0
- package/dist/fsm/all.d.ts +1 -0
- package/dist/fsm/fsm.d.ts +118 -0
- package/dist/fsmfile/all.d.ts +1 -0
- package/dist/fsmfile/fsmfile.d.ts +47 -0
- package/dist/jsonstream/all.d.ts +1 -0
- package/dist/jsonstream/jsonstream.d.ts +130 -0
- package/dist/lambda/all.d.ts +1 -0
- package/dist/lambda/env.d.ts +10 -0
- package/dist/lambda/lambda.d.ts +18 -0
- package/dist/logabstract/all.d.ts +1 -0
- package/dist/logabstract/log.d.ts +26 -0
- package/dist/logclient/all.d.ts +1 -0
- package/dist/logclient/log.d.ts +6 -0
- package/dist/logserver/all.d.ts +5 -0
- package/dist/logserver/log.d.ts +11 -0
- package/dist/logserver/logaccum.d.ts +154 -0
- package/dist/logserver/logblob.d.ts +24 -0
- package/dist/logserver/logconcat.d.ts +55 -0
- package/dist/logserver/logkey.d.ts +28 -0
- package/dist/memsqs/all.d.ts +4 -0
- package/dist/memsqs/client.d.ts +13 -0
- package/dist/memsqs/loopback.d.ts +11 -0
- package/dist/memsqs/orderedlist.d.ts +19 -0
- package/dist/memsqs/queue.d.ts +84 -0
- package/dist/memsqs/server.d.ts +37 -0
- package/dist/ot-editutil/all.d.ts +2 -0
- package/dist/ot-editutil/oteditutil.d.ts +14 -0
- package/dist/ot-editutil/otmaputil.d.ts +21 -0
- package/dist/ot-js/all.d.ts +9 -0
- package/dist/ot-js/otarray.d.ts +111 -0
- package/dist/ot-js/otclientengine.d.ts +38 -0
- package/dist/ot-js/otcomposite.d.ts +37 -0
- package/dist/ot-js/otcounter.d.ts +17 -0
- package/dist/ot-js/otengine.d.ts +22 -0
- package/dist/ot-js/otmap.d.ts +19 -0
- package/dist/ot-js/otserverengine.d.ts +38 -0
- package/dist/ot-js/otsession.d.ts +111 -0
- package/dist/ot-js/ottypes.d.ts +29 -0
- package/dist/poly/all.d.ts +15 -0
- package/dist/poly/blend.d.ts +1 -0
- package/dist/poly/boundbox.d.ts +16 -0
- package/dist/poly/cartesian.d.ts +5 -0
- package/dist/poly/graham-scan.d.ts +8 -0
- package/dist/poly/hash.d.ts +1 -0
- package/dist/poly/matrix.d.ts +24 -0
- package/dist/poly/minbound.d.ts +1 -0
- package/dist/poly/poly.d.ts +52 -0
- package/dist/poly/polybin.d.ts +5 -0
- package/dist/poly/polylabel.d.ts +7 -0
- package/dist/poly/polypack.d.ts +30 -0
- package/dist/poly/polyround.d.ts +1 -0
- package/dist/poly/polysimplify.d.ts +1 -0
- package/dist/poly/quad.d.ts +48 -0
- package/dist/poly/selfintersect.d.ts +1 -0
- package/dist/poly/shamos.d.ts +1 -0
- package/dist/poly/simplify.d.ts +2 -0
- package/dist/poly/topo.d.ts +46 -0
- package/dist/poly/union.d.ts +48 -0
- package/dist/storage/all.d.ts +4 -0
- package/dist/storage/datablob.d.ts +9 -0
- package/dist/storage/env.d.ts +10 -0
- package/dist/storage/splitsblob.d.ts +13 -0
- package/dist/storage/storage.d.ts +166 -0
- package/dist/storages3/all.d.ts +1 -0
- package/dist/storages3/s3.d.ts +62 -0
- package/dist/util/all.d.ts +5 -0
- package/dist/util/bintrie.d.ts +93 -0
- package/dist/util/countedhash.d.ts +19 -0
- package/dist/util/gradient.d.ts +15 -0
- package/dist/util/indexedarray.d.ts +15 -0
- package/dist/util/util.d.ts +68 -0
- package/docs/context.md +2 -0
- package/docs/dbabstract.md +2 -0
- package/docs/dbdynamo.md +2 -0
- package/docs/fsm.md +243 -0
- package/docs/fsmfile.md +2 -0
- package/docs/jsonstream.md +44 -0
- package/docs/lambda.md +2 -0
- package/docs/logabstract.md +2 -0
- package/docs/logclient.md +2 -0
- package/docs/logserver.md +2 -0
- package/docs/ot-editutil.md +2 -0
- package/docs/ot-js.md +95 -0
- package/docs/poly.md +103 -0
- package/docs/storage.md +2 -0
- package/docs/storages3.md +2 -0
- package/docs/util.md +2 -0
- package/lib/all/all.ts +41 -0
- package/lib/all/allclient.ts +19 -0
- package/lib/context/all.ts +1 -0
- package/lib/context/context.ts +82 -0
- package/lib/dbabstract/all.ts +1 -0
- package/lib/dbabstract/db.ts +246 -0
- package/lib/dbdynamo/all.ts +1 -0
- package/lib/dbdynamo/dbdynamo.ts +1551 -0
- package/lib/filterexpr/all.ts +1 -0
- package/lib/filterexpr/filterexpr.ts +625 -0
- package/lib/fsm/all.ts +1 -0
- package/lib/fsm/fsm.ts +549 -0
- package/lib/fsmfile/all.ts +1 -0
- package/lib/fsmfile/fsmfile.ts +236 -0
- package/lib/jsonstream/all.ts +1 -0
- package/lib/jsonstream/jsonstream.ts +940 -0
- package/lib/lambda/all.ts +1 -0
- package/lib/lambda/env.ts +13 -0
- package/lib/lambda/lambda.ts +120 -0
- package/lib/logabstract/all.ts +1 -0
- package/lib/logabstract/log.ts +55 -0
- package/lib/logclient/all.ts +1 -0
- package/lib/logclient/log.ts +105 -0
- package/lib/logserver/all.ts +5 -0
- package/lib/logserver/log.ts +565 -0
- package/lib/logserver/logaccum.ts +1445 -0
- package/lib/logserver/logblob.ts +84 -0
- package/lib/logserver/logconcat.ts +313 -0
- package/lib/logserver/logkey.ts +125 -0
- package/lib/memsqs/all.ts +4 -0
- package/lib/memsqs/client.ts +268 -0
- package/lib/memsqs/loopback.ts +64 -0
- package/lib/memsqs/orderedlist.ts +74 -0
- package/lib/memsqs/queue.ts +395 -0
- package/lib/memsqs/server.ts +262 -0
- package/lib/ot-editutil/all.ts +2 -0
- package/lib/ot-editutil/oteditutil.ts +180 -0
- package/lib/ot-editutil/otmaputil.ts +209 -0
- package/lib/ot-js/all.ts +9 -0
- package/lib/ot-js/otarray.ts +1168 -0
- package/lib/ot-js/otclientengine.ts +327 -0
- package/lib/ot-js/otcomposite.ts +247 -0
- package/lib/ot-js/otcounter.ts +145 -0
- package/lib/ot-js/otengine.ts +71 -0
- package/lib/ot-js/otmap.ts +144 -0
- package/lib/ot-js/otserverengine.ts +329 -0
- package/lib/ot-js/otsession.ts +199 -0
- package/lib/ot-js/ottypes.ts +98 -0
- package/lib/poly/all.ts +15 -0
- package/lib/poly/blend.ts +27 -0
- package/lib/poly/boundbox.ts +102 -0
- package/lib/poly/cartesian.ts +130 -0
- package/lib/poly/graham-scan.ts +401 -0
- package/lib/poly/hash.ts +15 -0
- package/lib/poly/matrix.ts +309 -0
- package/lib/poly/minbound.ts +211 -0
- package/lib/poly/poly.ts +767 -0
- package/lib/poly/polybin.ts +218 -0
- package/lib/poly/polylabel.ts +204 -0
- package/lib/poly/polypack.ts +458 -0
- package/lib/poly/polyround.ts +30 -0
- package/lib/poly/polysimplify.ts +24 -0
- package/lib/poly/quad.ts +272 -0
- package/lib/poly/selfintersect.ts +87 -0
- package/lib/poly/shamos.ts +297 -0
- package/lib/poly/simplify.ts +119 -0
- package/lib/poly/topo.ts +525 -0
- package/lib/poly/union.ts +371 -0
- package/lib/storage/all.ts +4 -0
- package/lib/storage/datablob.ts +36 -0
- package/lib/storage/env.ts +14 -0
- package/lib/storage/splitsblob.ts +63 -0
- package/lib/storage/storage.ts +604 -0
- package/lib/storages3/all.ts +1 -0
- package/lib/storages3/s3.ts +576 -0
- package/lib/util/all.ts +5 -0
- package/lib/util/bintrie.ts +603 -0
- package/lib/util/countedhash.ts +83 -0
- package/lib/util/gradient.ts +108 -0
- package/lib/util/indexedarray.ts +80 -0
- package/lib/util/util.ts +695 -0
- package/package.json +8 -8
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
// Node libraries
|
|
2
|
+
import * as Stream from 'stream';
|
|
3
|
+
import * as Events from 'events';
|
|
4
|
+
|
|
5
|
+
// Shared libraries
|
|
6
|
+
import * as Util from '../util/all';
|
|
7
|
+
|
|
8
|
+
// Acts like Stream.Readable, emits 'error', 'close', 'data', 'end'
|
|
9
|
+
export class Readable extends Events.EventEmitter
|
|
10
|
+
{
|
|
11
|
+
constructor()
|
|
12
|
+
{
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Acts like Stream.Writable, emits 'error', 'close', 'drain', 'finish''
|
|
18
|
+
export class Writable extends Events.EventEmitter
|
|
19
|
+
{
|
|
20
|
+
constructor()
|
|
21
|
+
{
|
|
22
|
+
super();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// override
|
|
26
|
+
write(b: any, encoding?: BufferEncoding, cb?: () => void): boolean
|
|
27
|
+
{
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// override
|
|
32
|
+
end(): void
|
|
33
|
+
{
|
|
34
|
+
this.emit('finish');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Useful class for having JSONStreamWriter generate a Buffer
|
|
39
|
+
export class BufferWritable extends Writable
|
|
40
|
+
{
|
|
41
|
+
_bufs: Buffer[];
|
|
42
|
+
_buf: Buffer;
|
|
43
|
+
|
|
44
|
+
constructor()
|
|
45
|
+
{
|
|
46
|
+
super();
|
|
47
|
+
this._bufs = [];
|
|
48
|
+
this._buf = null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
write(b: any, encoding?: BufferEncoding, cb?: () => void): boolean
|
|
52
|
+
{
|
|
53
|
+
if (typeof b === 'string')
|
|
54
|
+
b = Buffer.from(b as string, encoding);
|
|
55
|
+
this._bufs.push(b as Buffer);
|
|
56
|
+
if (this._bufs.length > 100) this._bufs = [ Buffer.concat(this._bufs) ];
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
end(): void
|
|
61
|
+
{
|
|
62
|
+
this._buf = Buffer.concat(this._bufs);
|
|
63
|
+
this.emit('finish', this._buf);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Useful class for having JSONStreamReader read from an existing Buffer
|
|
68
|
+
export class BufferReadable extends Stream.Readable
|
|
69
|
+
{
|
|
70
|
+
_b: Buffer;
|
|
71
|
+
|
|
72
|
+
constructor(b: Buffer)
|
|
73
|
+
{
|
|
74
|
+
super({});
|
|
75
|
+
this._b = b;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_read(): void
|
|
79
|
+
{
|
|
80
|
+
if (this._b)
|
|
81
|
+
{
|
|
82
|
+
this.push(this._b);
|
|
83
|
+
this.push(null);
|
|
84
|
+
this._b = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class MultiBufferReadable extends Stream.Readable
|
|
90
|
+
{
|
|
91
|
+
constructor()
|
|
92
|
+
{
|
|
93
|
+
super({});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
_read(): void
|
|
97
|
+
{
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
more(buf: Buffer): void
|
|
101
|
+
{
|
|
102
|
+
this.push(buf);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
end(): void
|
|
106
|
+
{
|
|
107
|
+
this.push(null);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
enum ReadState {
|
|
112
|
+
Start,
|
|
113
|
+
ObjectPropertyStart,
|
|
114
|
+
ObjectPropertyEnd,
|
|
115
|
+
ValueStart,
|
|
116
|
+
ValueEnd,
|
|
117
|
+
InName,
|
|
118
|
+
InNameBackslash,
|
|
119
|
+
NeedColon,
|
|
120
|
+
InNumber,
|
|
121
|
+
InString,
|
|
122
|
+
InStringBackslash,
|
|
123
|
+
InTrue,
|
|
124
|
+
InFalse,
|
|
125
|
+
InNull,
|
|
126
|
+
InUndefined,
|
|
127
|
+
Error,
|
|
128
|
+
Ended
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const CurlyOpen = 123;
|
|
132
|
+
const CurlyClose = 125;
|
|
133
|
+
const SquareOpen = 91;
|
|
134
|
+
const SquareClose = 93;
|
|
135
|
+
const Comma = 44;
|
|
136
|
+
const Colon = 58;
|
|
137
|
+
const Quote = 34;
|
|
138
|
+
const Backslash = 92;
|
|
139
|
+
const Forwardslash = 47
|
|
140
|
+
const Zero = 48;
|
|
141
|
+
const Nine = 57;
|
|
142
|
+
const Minus = 45;
|
|
143
|
+
const Plus = 43;
|
|
144
|
+
const Period = 46;
|
|
145
|
+
const UpperA = 65;
|
|
146
|
+
const LowerA = 97;
|
|
147
|
+
const UpperB = 66;
|
|
148
|
+
const LowerB = 98;
|
|
149
|
+
const UpperE = 69;
|
|
150
|
+
const LowerE = 101;
|
|
151
|
+
const UpperF = 70;
|
|
152
|
+
const LowerF = 102;
|
|
153
|
+
const UpperN = 78;
|
|
154
|
+
const LowerN = 110;
|
|
155
|
+
const UpperR = 82;
|
|
156
|
+
const LowerR = 114;
|
|
157
|
+
const UpperT = 84;
|
|
158
|
+
const LowerT = 116;
|
|
159
|
+
const UpperU = 85;
|
|
160
|
+
const LowerU = 117;
|
|
161
|
+
const UpperZ = 90;
|
|
162
|
+
const LowerZ = 122;
|
|
163
|
+
const Space = 32;
|
|
164
|
+
const Backspace = 8;
|
|
165
|
+
const Tab = 9;
|
|
166
|
+
const Newline = 10;
|
|
167
|
+
const CR = 13;
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
function isWhite(c: number): boolean
|
|
171
|
+
{
|
|
172
|
+
return c == Space || c == Newline || c == Tab || c == CR;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function isEscapable(c: number): number
|
|
176
|
+
{
|
|
177
|
+
switch (c)
|
|
178
|
+
{
|
|
179
|
+
case Quote: return Quote;
|
|
180
|
+
case Backslash: return Backslash;
|
|
181
|
+
case Forwardslash: return Forwardslash;
|
|
182
|
+
case UpperB:
|
|
183
|
+
case LowerB: return Backspace;
|
|
184
|
+
case UpperT:
|
|
185
|
+
case LowerT: return Tab;
|
|
186
|
+
case UpperN:
|
|
187
|
+
case LowerN: return Newline;
|
|
188
|
+
case UpperR:
|
|
189
|
+
case LowerR: return CR;
|
|
190
|
+
|
|
191
|
+
// TODO: Really should support unicode \uHHHH
|
|
192
|
+
default:
|
|
193
|
+
return -1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function isNumeric(c: number): boolean
|
|
198
|
+
{
|
|
199
|
+
return (c >= Zero && c <= Nine) || c == Minus || c == Plus || c == UpperE || c == LowerE || c == Period;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function isAlpha(c: number): boolean
|
|
203
|
+
{
|
|
204
|
+
return (c >= UpperA && c <= UpperZ) || (c >= LowerA && c <= LowerZ);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
enum TokType { TTNone, TTArray, TTObject, TTName, TTString, TTNumber, TTTrue, TTFalse, TTNull, TTUndefined };
|
|
208
|
+
|
|
209
|
+
interface Token
|
|
210
|
+
{
|
|
211
|
+
tt: TokType;
|
|
212
|
+
v: any;
|
|
213
|
+
emit?: string;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface JSONStreamOptions
|
|
217
|
+
{
|
|
218
|
+
maxTokenSize?: number;
|
|
219
|
+
outBufferSize?: number;
|
|
220
|
+
encoding?: BufferEncoding;
|
|
221
|
+
syncChunkSize?: number;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const DefaultStreamOptions: JSONStreamOptions =
|
|
225
|
+
{
|
|
226
|
+
maxTokenSize: 10000,
|
|
227
|
+
outBufferSize: 10000,
|
|
228
|
+
encoding: 'utf8',
|
|
229
|
+
syncChunkSize: 10000,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export class JSONStreamReader extends Events.EventEmitter
|
|
233
|
+
{
|
|
234
|
+
_s: Readable;
|
|
235
|
+
_charCount: number;
|
|
236
|
+
_lineCount: number;
|
|
237
|
+
_state: ReadState;
|
|
238
|
+
_tok: Buffer;
|
|
239
|
+
_tokLen: number;
|
|
240
|
+
_stack: Token[];
|
|
241
|
+
_result: any;
|
|
242
|
+
_incr: any;
|
|
243
|
+
_options: JSONStreamOptions;
|
|
244
|
+
_chunks: Buffer[];
|
|
245
|
+
_chunkCur: number;
|
|
246
|
+
_chunkIndex: number;
|
|
247
|
+
_chunkScheduled: boolean;
|
|
248
|
+
_ended: boolean;
|
|
249
|
+
_closed: boolean;
|
|
250
|
+
|
|
251
|
+
constructor(options?: JSONStreamOptions)
|
|
252
|
+
{
|
|
253
|
+
super();
|
|
254
|
+
this.onData = this.onData.bind(this);
|
|
255
|
+
this.onClose = this.onClose.bind(this);
|
|
256
|
+
this.onEnd = this.onEnd.bind(this);
|
|
257
|
+
this.onError = this.onError.bind(this);
|
|
258
|
+
this.nextChunk = this.nextChunk.bind(this);
|
|
259
|
+
this._state = ReadState.Start;
|
|
260
|
+
this._charCount = 1;
|
|
261
|
+
this._lineCount = 1;
|
|
262
|
+
this._stack = [];
|
|
263
|
+
this._result = undefined;
|
|
264
|
+
this._incr = {};
|
|
265
|
+
this._options = Util.shallowAssignImmutable(DefaultStreamOptions, options);
|
|
266
|
+
this._tok = Buffer.alloc(this._options.maxTokenSize);
|
|
267
|
+
this._tokLen = 0;
|
|
268
|
+
this._s = undefined;
|
|
269
|
+
this._chunks = [];
|
|
270
|
+
this._chunkCur = 0;
|
|
271
|
+
this._chunkIndex = 0;
|
|
272
|
+
this._chunkScheduled = false;
|
|
273
|
+
this._ended = false;
|
|
274
|
+
this._closed = false;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
start(s: Readable): void
|
|
278
|
+
{
|
|
279
|
+
this._s = s;
|
|
280
|
+
this._s.on('close', this.onClose);
|
|
281
|
+
this._s.on('end', this.onEnd);
|
|
282
|
+
this._s.on('error', this.onError);
|
|
283
|
+
this._s.on('data', this.onData);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
finish(): void
|
|
287
|
+
{
|
|
288
|
+
this._s.off('close', this.onClose);
|
|
289
|
+
this._s.off('end', this.onEnd);
|
|
290
|
+
this._s.off('error', this.onError);
|
|
291
|
+
this._s.off('data', this.onData);
|
|
292
|
+
this._s = undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
emitIncremental(name: string): void
|
|
296
|
+
{
|
|
297
|
+
this._incr[name] = true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pushToken(tok: Token): void
|
|
301
|
+
{
|
|
302
|
+
this._stack.push(tok);
|
|
303
|
+
if (tok.tt == TokType.TTArray || tok.tt == TokType.TTObject)
|
|
304
|
+
{
|
|
305
|
+
let prevTok: Token = this._stack.length > 1 ? this._stack[this._stack.length - 2] : null;
|
|
306
|
+
if (prevTok && prevTok.tt == TokType.TTName && this._incr[prevTok.v] == true)
|
|
307
|
+
tok.emit = prevTok.v;
|
|
308
|
+
else if (prevTok == null && this._incr[''] == true) // special-case top-level object
|
|
309
|
+
tok.emit = '';
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private badJSON(detail?: string): void
|
|
314
|
+
{
|
|
315
|
+
this._state = ReadState.Error;
|
|
316
|
+
if (detail !== undefined)
|
|
317
|
+
this.emit('error', `Invalid JSON (${detail}) at line ${this._lineCount}, character ${this._charCount}`);
|
|
318
|
+
else
|
|
319
|
+
this.emit('error', `Invalid JSON at line ${this._lineCount}, character ${this._charCount}`);
|
|
320
|
+
this.finish();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private startTok(s: ReadState, c?: number): void
|
|
324
|
+
{
|
|
325
|
+
this._state = s;
|
|
326
|
+
if (c !== undefined)
|
|
327
|
+
this.addToTok(c);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private addToTok(c: number): void
|
|
331
|
+
{
|
|
332
|
+
if (this._tokLen == this._options.maxTokenSize)
|
|
333
|
+
this.badJSON('token too long');
|
|
334
|
+
else
|
|
335
|
+
{
|
|
336
|
+
this._tok.writeUInt8(c, this._tokLen);
|
|
337
|
+
this._tokLen++;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private consumeTok(): string
|
|
342
|
+
{
|
|
343
|
+
let s: string = this._tok.toString(this._options.encoding, 0, this._tokLen);
|
|
344
|
+
this._tokLen = 0;
|
|
345
|
+
return s;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private produceValue(tok: Token): void
|
|
349
|
+
{
|
|
350
|
+
if (this._stack.length == 0)
|
|
351
|
+
{
|
|
352
|
+
this._state = ReadState.Ended;
|
|
353
|
+
this._result = tok.v;
|
|
354
|
+
}
|
|
355
|
+
else
|
|
356
|
+
{
|
|
357
|
+
this._state = ReadState.ValueEnd;
|
|
358
|
+
let topTok: Token = this._stack[this._stack.length - 1];
|
|
359
|
+
if (topTok.tt == TokType.TTArray)
|
|
360
|
+
{
|
|
361
|
+
if (topTok.emit !== undefined)
|
|
362
|
+
this.emit('array', topTok.emit, tok.v);
|
|
363
|
+
else
|
|
364
|
+
topTok.v.push(tok.v);
|
|
365
|
+
}
|
|
366
|
+
else if (topTok.tt == TokType.TTName)
|
|
367
|
+
{
|
|
368
|
+
let p: string = topTok.v;
|
|
369
|
+
this._stack.pop();
|
|
370
|
+
topTok = this._stack[this._stack.length - 1];
|
|
371
|
+
if (topTok.tt != TokType.TTObject)
|
|
372
|
+
this.badJSON();
|
|
373
|
+
else
|
|
374
|
+
{
|
|
375
|
+
if (topTok.emit !== undefined)
|
|
376
|
+
this.emit('object', topTok.emit, p, tok.v);
|
|
377
|
+
else
|
|
378
|
+
topTok.v[p] = tok.v;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private produceValidatedValue(tok: Token): void
|
|
385
|
+
{
|
|
386
|
+
let s: string = tok.v;
|
|
387
|
+
switch (tok.tt)
|
|
388
|
+
{
|
|
389
|
+
case TokType.TTNumber:
|
|
390
|
+
// TODO: Constrained parsing: [-]{0 | [1-9][0-9]*}[.[0-9]+][{e|E}[+-][0-9]+]
|
|
391
|
+
// Optional leading minus, either zero or set of digits not starting with zero.
|
|
392
|
+
// Optional decimal point with required digit sequence
|
|
393
|
+
// Optional exponent, e or E, optional sign, required digit sequence
|
|
394
|
+
tok.v = parseFloat(s);
|
|
395
|
+
break;
|
|
396
|
+
|
|
397
|
+
case TokType.TTNull:
|
|
398
|
+
if (s != 'null')
|
|
399
|
+
this.badJSON('bad value');
|
|
400
|
+
else
|
|
401
|
+
tok.v = null;
|
|
402
|
+
break;
|
|
403
|
+
|
|
404
|
+
case TokType.TTUndefined:
|
|
405
|
+
if (s != 'undefined')
|
|
406
|
+
this.badJSON('bad value');
|
|
407
|
+
else
|
|
408
|
+
tok.v = undefined;
|
|
409
|
+
break;
|
|
410
|
+
|
|
411
|
+
case TokType.TTTrue:
|
|
412
|
+
if (s != 'true')
|
|
413
|
+
this.badJSON('bad value');
|
|
414
|
+
else
|
|
415
|
+
tok.v = true;
|
|
416
|
+
break;
|
|
417
|
+
|
|
418
|
+
case TokType.TTFalse:
|
|
419
|
+
if (s != 'false')
|
|
420
|
+
this.badJSON('bad value');
|
|
421
|
+
else
|
|
422
|
+
tok.v = false;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (this._state != ReadState.Error)
|
|
427
|
+
this.produceValue(tok);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private onData(chunk: Buffer): void
|
|
431
|
+
{
|
|
432
|
+
this._chunks.push(chunk);
|
|
433
|
+
this.nextChunk();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private scheduleNext(): void
|
|
437
|
+
{
|
|
438
|
+
if (this._state != ReadState.Error && this._chunkCur < this._chunks.length)
|
|
439
|
+
{
|
|
440
|
+
if (! this._chunkScheduled)
|
|
441
|
+
{
|
|
442
|
+
this._chunkScheduled = true;
|
|
443
|
+
setImmediate(this.nextChunk);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
else if (this._closed)
|
|
447
|
+
this.doClose();
|
|
448
|
+
else if (this._ended)
|
|
449
|
+
this.doEnd();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private nextChunk(): void
|
|
453
|
+
{
|
|
454
|
+
this._chunkScheduled = false;
|
|
455
|
+
if (this._chunkCur >= this._chunks.length)
|
|
456
|
+
{
|
|
457
|
+
this.scheduleNext();
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let chunk = this._chunks[this._chunkCur];
|
|
462
|
+
let i = this._chunkIndex;
|
|
463
|
+
let e = i + this._options.syncChunkSize;
|
|
464
|
+
if (e > chunk.length) e = chunk.length;
|
|
465
|
+
|
|
466
|
+
for (; this._state != ReadState.Error && i < e; i++)
|
|
467
|
+
{
|
|
468
|
+
let c: number = chunk[i];
|
|
469
|
+
this._charCount++;
|
|
470
|
+
if (c == 10) { this._lineCount++; this._charCount = 1; }
|
|
471
|
+
|
|
472
|
+
switch (this._state)
|
|
473
|
+
{
|
|
474
|
+
case ReadState.Start:
|
|
475
|
+
if (! isWhite(c))
|
|
476
|
+
if (c === CurlyOpen)
|
|
477
|
+
{
|
|
478
|
+
this.pushToken({ tt: TokType.TTObject, v: {} });
|
|
479
|
+
this._state = ReadState.ObjectPropertyStart;
|
|
480
|
+
}
|
|
481
|
+
else
|
|
482
|
+
this.badJSON(`Unexpected character: ${String.fromCharCode(c)} in chunk ${chunk.toString('utf8')}`);
|
|
483
|
+
break;
|
|
484
|
+
|
|
485
|
+
case ReadState.ObjectPropertyStart:
|
|
486
|
+
if (! isWhite(c))
|
|
487
|
+
switch (c)
|
|
488
|
+
{
|
|
489
|
+
case Quote:
|
|
490
|
+
this.startTok(ReadState.InName);
|
|
491
|
+
break;
|
|
492
|
+
case CurlyClose:
|
|
493
|
+
this.produceValue(this._stack.pop());
|
|
494
|
+
break;
|
|
495
|
+
default:
|
|
496
|
+
this.badJSON();
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
|
|
500
|
+
case ReadState.ValueEnd:
|
|
501
|
+
if (! isWhite(c))
|
|
502
|
+
{
|
|
503
|
+
let topTok: Token = this._stack[this._stack.length - 1];
|
|
504
|
+
if (c == Comma)
|
|
505
|
+
this._state = topTok.tt == TokType.TTArray ? ReadState.ValueStart : ReadState.ObjectPropertyStart;
|
|
506
|
+
else if ((c == SquareClose && topTok.tt == TokType.TTArray) || (c == CurlyClose && topTok.tt == TokType.TTObject))
|
|
507
|
+
this.produceValue(this._stack.pop());
|
|
508
|
+
else
|
|
509
|
+
this.badJSON(`missing comma or ending ${topTok.tt == TokType.TTArray ? 'square' : 'curly'} bracket`);
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
|
|
513
|
+
case ReadState.ValueStart:
|
|
514
|
+
if (! isWhite(c))
|
|
515
|
+
{
|
|
516
|
+
if (c == CurlyOpen)
|
|
517
|
+
{
|
|
518
|
+
this.pushToken({ tt: TokType.TTObject, v: {} });
|
|
519
|
+
this._state = ReadState.ObjectPropertyStart;
|
|
520
|
+
}
|
|
521
|
+
else if (c == SquareOpen)
|
|
522
|
+
{
|
|
523
|
+
this.pushToken({ tt: TokType.TTArray, v: [] });
|
|
524
|
+
this._state = ReadState.ValueStart;
|
|
525
|
+
}
|
|
526
|
+
else if (c == SquareClose)
|
|
527
|
+
{
|
|
528
|
+
// Could be in array (good) or expecting value of object property (bad)
|
|
529
|
+
let topTok: Token = this._stack[this._stack.length - 1];
|
|
530
|
+
if (topTok.tt == TokType.TTArray)
|
|
531
|
+
this.produceValue(this._stack.pop());
|
|
532
|
+
else
|
|
533
|
+
this.badJSON(`Unexpected character: ${String.fromCharCode(c)} in chunk ${chunk.toString('utf8')}`);
|
|
534
|
+
}
|
|
535
|
+
else if (c == Quote)
|
|
536
|
+
this.startTok(ReadState.InString);
|
|
537
|
+
else if (c == UpperT || c == LowerT)
|
|
538
|
+
this.startTok(ReadState.InTrue, c);
|
|
539
|
+
else if (c == UpperF || c == LowerF)
|
|
540
|
+
this.startTok(ReadState.InFalse, c);
|
|
541
|
+
else if (c == UpperN || c == LowerN)
|
|
542
|
+
this.startTok(ReadState.InNull, c);
|
|
543
|
+
else if (c == UpperU || c == LowerU)
|
|
544
|
+
this.startTok(ReadState.InUndefined, c);
|
|
545
|
+
else if (isNumeric(c))
|
|
546
|
+
this.startTok(ReadState.InNumber, c);
|
|
547
|
+
else
|
|
548
|
+
this.badJSON();
|
|
549
|
+
}
|
|
550
|
+
break;
|
|
551
|
+
|
|
552
|
+
case ReadState.InName:
|
|
553
|
+
if (c == Backslash)
|
|
554
|
+
this._state = ReadState.InNameBackslash;
|
|
555
|
+
else if (c != Quote)
|
|
556
|
+
this.addToTok(c);
|
|
557
|
+
else
|
|
558
|
+
{
|
|
559
|
+
this.pushToken({ tt: TokType.TTName, v: this.consumeTok() });
|
|
560
|
+
this._state = ReadState.NeedColon;
|
|
561
|
+
}
|
|
562
|
+
break;
|
|
563
|
+
|
|
564
|
+
case ReadState.InNameBackslash:
|
|
565
|
+
{
|
|
566
|
+
// TODO: add hex encoding support
|
|
567
|
+
let escC = isEscapable(c);
|
|
568
|
+
if (escC < 0)
|
|
569
|
+
this.badJSON(`Illegal escaped character: ${String.fromCharCode(c)}`);
|
|
570
|
+
this.addToTok(escC);
|
|
571
|
+
this._state = ReadState.InName;
|
|
572
|
+
}
|
|
573
|
+
break;
|
|
574
|
+
|
|
575
|
+
case ReadState.NeedColon:
|
|
576
|
+
if (! isWhite(c))
|
|
577
|
+
if (c !== Colon)
|
|
578
|
+
this.badJSON('expected colon');
|
|
579
|
+
else
|
|
580
|
+
this._state = ReadState.ValueStart;
|
|
581
|
+
break;
|
|
582
|
+
|
|
583
|
+
case ReadState.InNumber:
|
|
584
|
+
if (isNumeric(c))
|
|
585
|
+
this.addToTok(c);
|
|
586
|
+
else
|
|
587
|
+
{
|
|
588
|
+
this.produceValidatedValue({ tt: TokType.TTNumber, v: this.consumeTok() });
|
|
589
|
+
i--;
|
|
590
|
+
}
|
|
591
|
+
break;
|
|
592
|
+
|
|
593
|
+
case ReadState.InString:
|
|
594
|
+
if (c == Backslash)
|
|
595
|
+
this._state = ReadState.InStringBackslash;
|
|
596
|
+
else if (c != Quote)
|
|
597
|
+
this.addToTok(c);
|
|
598
|
+
else
|
|
599
|
+
this.produceValue({ tt: TokType.TTString, v: this.consumeTok() });
|
|
600
|
+
break;
|
|
601
|
+
|
|
602
|
+
case ReadState.InStringBackslash:
|
|
603
|
+
{
|
|
604
|
+
// TODO: add hex encoding support
|
|
605
|
+
let escC = isEscapable(c);
|
|
606
|
+
if (escC < 0)
|
|
607
|
+
this.badJSON(`Illegal escaped character: ${String.fromCharCode(c)}`);
|
|
608
|
+
this.addToTok(escC);
|
|
609
|
+
this._state = ReadState.InString;
|
|
610
|
+
}
|
|
611
|
+
break;
|
|
612
|
+
|
|
613
|
+
case ReadState.InTrue:
|
|
614
|
+
if (isAlpha(c))
|
|
615
|
+
this.addToTok(c);
|
|
616
|
+
else
|
|
617
|
+
{
|
|
618
|
+
this.produceValidatedValue({ tt: TokType.TTTrue, v: this.consumeTok() });
|
|
619
|
+
i--;
|
|
620
|
+
}
|
|
621
|
+
break;
|
|
622
|
+
|
|
623
|
+
case ReadState.InFalse:
|
|
624
|
+
if (isAlpha(c))
|
|
625
|
+
this.addToTok(c);
|
|
626
|
+
else
|
|
627
|
+
{
|
|
628
|
+
this.produceValidatedValue({ tt: TokType.TTFalse, v: this.consumeTok() });
|
|
629
|
+
i--;
|
|
630
|
+
}
|
|
631
|
+
break;
|
|
632
|
+
|
|
633
|
+
case ReadState.InNull:
|
|
634
|
+
if (isAlpha(c))
|
|
635
|
+
this.addToTok(c);
|
|
636
|
+
else
|
|
637
|
+
{
|
|
638
|
+
this.produceValidatedValue({ tt: TokType.TTNull, v: this.consumeTok() });
|
|
639
|
+
i--;
|
|
640
|
+
}
|
|
641
|
+
break;
|
|
642
|
+
|
|
643
|
+
case ReadState.InUndefined:
|
|
644
|
+
if (isAlpha(c))
|
|
645
|
+
this.addToTok(c);
|
|
646
|
+
else
|
|
647
|
+
{
|
|
648
|
+
this.produceValidatedValue({ tt: TokType.TTUndefined, v: this.consumeTok() });
|
|
649
|
+
i--;
|
|
650
|
+
}
|
|
651
|
+
break;
|
|
652
|
+
|
|
653
|
+
case ReadState.Ended:
|
|
654
|
+
if (! isWhite(c))
|
|
655
|
+
this.badJSON('unexpected character after end of object');
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Potentially advance to next chunk and schedule next processing slice
|
|
661
|
+
if (this._state === ReadState.Error)
|
|
662
|
+
{
|
|
663
|
+
this._chunks = [];
|
|
664
|
+
this._chunkCur = 0;
|
|
665
|
+
this._chunkIndex = 0;
|
|
666
|
+
}
|
|
667
|
+
else
|
|
668
|
+
{
|
|
669
|
+
if (e === chunk.length)
|
|
670
|
+
{
|
|
671
|
+
this._chunkIndex = 0;
|
|
672
|
+
if (this._chunkCur === this._chunks.length - 1)
|
|
673
|
+
{
|
|
674
|
+
this._chunks = [];
|
|
675
|
+
this._chunkCur = 0;
|
|
676
|
+
}
|
|
677
|
+
else
|
|
678
|
+
{
|
|
679
|
+
this._chunkCur++;
|
|
680
|
+
if (this._chunkCur >= 100)
|
|
681
|
+
{
|
|
682
|
+
this._chunks.splice(0, this._chunkCur);
|
|
683
|
+
this._chunkCur = 0;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
else
|
|
688
|
+
this._chunkIndex = e;
|
|
689
|
+
}
|
|
690
|
+
this.scheduleNext();
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private onClose(): void
|
|
694
|
+
{
|
|
695
|
+
this._closed = true;
|
|
696
|
+
this.scheduleNext();
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
private doClose(): void
|
|
700
|
+
{
|
|
701
|
+
this._closed = undefined;
|
|
702
|
+
this.doEnd();
|
|
703
|
+
this.emit('close');
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
private onError(e: Error): void
|
|
707
|
+
{
|
|
708
|
+
this._state = ReadState.Error;
|
|
709
|
+
let s: string = 'stream error';
|
|
710
|
+
if (e && typeof e.message === 'string')
|
|
711
|
+
s = e.message;
|
|
712
|
+
console.log(`jsonreader: emitting error: ${s}`);
|
|
713
|
+
this.emit('error', s);
|
|
714
|
+
this.finish();
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
private onEnd(): void
|
|
718
|
+
{
|
|
719
|
+
this._ended = true;
|
|
720
|
+
this.scheduleNext();
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
private doEnd(): void
|
|
724
|
+
{
|
|
725
|
+
this._ended = undefined;
|
|
726
|
+
if (this._state == ReadState.Ended)
|
|
727
|
+
{
|
|
728
|
+
// Only emit once
|
|
729
|
+
if (this._result !== undefined)
|
|
730
|
+
{
|
|
731
|
+
this.emit('end', this._result);
|
|
732
|
+
this._result = undefined;
|
|
733
|
+
this.finish();
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
else if (this._state !== ReadState.Error)
|
|
737
|
+
this.badJSON('Unexpected end of input');
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
interface ObjectWriteState
|
|
742
|
+
{
|
|
743
|
+
o: any;
|
|
744
|
+
keys: string[];
|
|
745
|
+
index: number;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
export class JSONStreamWriter extends Events.EventEmitter
|
|
749
|
+
{
|
|
750
|
+
_s: Writable;
|
|
751
|
+
_stack: ObjectWriteState[];
|
|
752
|
+
_chunk: Buffer;
|
|
753
|
+
_chunkLen: number;
|
|
754
|
+
_options: JSONStreamOptions;
|
|
755
|
+
_bDone: boolean;
|
|
756
|
+
|
|
757
|
+
constructor(options?: JSONStreamOptions)
|
|
758
|
+
{
|
|
759
|
+
super();
|
|
760
|
+
this._bDone = false;
|
|
761
|
+
this._s = null;
|
|
762
|
+
this._stack = [];
|
|
763
|
+
this.onDrain = this.onDrain.bind(this);
|
|
764
|
+
this.onError = this.onError.bind(this);
|
|
765
|
+
this.onFinish = this.onFinish.bind(this);
|
|
766
|
+
this.onClose = this.onClose.bind(this);
|
|
767
|
+
this._options = Util.shallowAssignImmutable(DefaultStreamOptions, options);
|
|
768
|
+
this._chunk = Buffer.alloc(this._options.outBufferSize);
|
|
769
|
+
this._chunkLen = 0;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
start(o: any, s: Writable): void
|
|
773
|
+
{
|
|
774
|
+
this._s = s;
|
|
775
|
+
this._s.on('drain', this.onDrain);
|
|
776
|
+
this._stack.push({ o: o, keys: Array.isArray(o) ? null : Object.keys(o), index: 0 });
|
|
777
|
+
this.onDrain();
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
addToChunk(s: string): boolean
|
|
781
|
+
{
|
|
782
|
+
let b = Buffer.from(s as string, this._options.encoding);
|
|
783
|
+
let bContinue: boolean = true;
|
|
784
|
+
|
|
785
|
+
if (b.length + this._chunkLen > this._options.outBufferSize)
|
|
786
|
+
bContinue = this.drainChunk();
|
|
787
|
+
|
|
788
|
+
if (b.length > this._options.outBufferSize)
|
|
789
|
+
throw 'Internal error: atom larger than buffer size';
|
|
790
|
+
|
|
791
|
+
b.copy(this._chunk, this._chunkLen);
|
|
792
|
+
this._chunkLen += b.length;
|
|
793
|
+
return bContinue;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
drainChunk(): boolean
|
|
797
|
+
{
|
|
798
|
+
if (this._chunkLen > 0)
|
|
799
|
+
{
|
|
800
|
+
let b: Buffer = this._chunk.slice(0, this._chunkLen);
|
|
801
|
+
|
|
802
|
+
// false just indicates backpressure - write still succeeded.
|
|
803
|
+
let bDraining: boolean = this._s.write(b);
|
|
804
|
+
|
|
805
|
+
// Allocate new write buffer to prevent overwriting output buffer before output stream
|
|
806
|
+
// gets chance to copy out the data.
|
|
807
|
+
this._chunk = Buffer.alloc(this._options.outBufferSize);
|
|
808
|
+
this._chunkLen = 0;
|
|
809
|
+
|
|
810
|
+
return bDraining;
|
|
811
|
+
}
|
|
812
|
+
return true;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
onDrain(): void
|
|
816
|
+
{
|
|
817
|
+
let bContinue: boolean;
|
|
818
|
+
|
|
819
|
+
while (! this._bDone && this._stack.length > 0)
|
|
820
|
+
{
|
|
821
|
+
let top: ObjectWriteState = this._stack[this._stack.length - 1];
|
|
822
|
+
|
|
823
|
+
// Object
|
|
824
|
+
if (top.keys)
|
|
825
|
+
{
|
|
826
|
+
if (top.keys.length == 0)
|
|
827
|
+
bContinue = this.addToChunk('{');
|
|
828
|
+
let bRecurse = false;
|
|
829
|
+
// Even index means propname not written
|
|
830
|
+
while (! this._bDone && top.index < top.keys.length * 2)
|
|
831
|
+
{
|
|
832
|
+
let ii: number = Math.floor(top.index / 2);
|
|
833
|
+
if ((top.index % 2) == 0)
|
|
834
|
+
{
|
|
835
|
+
let s: string = `${top.index == 0 ? '{' : ','}"${top.keys[ii]}":`;
|
|
836
|
+
bContinue = this.addToChunk(s);
|
|
837
|
+
top.index++;
|
|
838
|
+
if (! bContinue)
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
let oo: any = top.o[top.keys[ii]];
|
|
842
|
+
if (oo && typeof oo === 'object')
|
|
843
|
+
{
|
|
844
|
+
bRecurse = true;
|
|
845
|
+
top.index++;
|
|
846
|
+
this._stack.push({ o: oo, keys: Array.isArray(oo) ? null : Object.keys(oo), index: 0 });
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
else
|
|
850
|
+
{
|
|
851
|
+
let s: string = JSON.stringify(oo === undefined ? null : oo);
|
|
852
|
+
bContinue = this.addToChunk(s);
|
|
853
|
+
top.index++;
|
|
854
|
+
if (! bContinue)
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
if (! bRecurse)
|
|
860
|
+
{
|
|
861
|
+
bContinue = this.addToChunk('}');
|
|
862
|
+
this._stack.pop();
|
|
863
|
+
if (! bContinue)
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// Array
|
|
869
|
+
else
|
|
870
|
+
{
|
|
871
|
+
if (top.o.length == 0)
|
|
872
|
+
bContinue = this.addToChunk('[');
|
|
873
|
+
let bRecurse = false;
|
|
874
|
+
|
|
875
|
+
// Even index means separator not written
|
|
876
|
+
while (! this._bDone && top.index < top.o.length * 2)
|
|
877
|
+
{
|
|
878
|
+
let ii: number = Math.floor(top.index / 2);
|
|
879
|
+
if ((top.index % 2) == 0)
|
|
880
|
+
{
|
|
881
|
+
let s: string = top.index == 0 ? '[' : ',';
|
|
882
|
+
bContinue = this.addToChunk(s);
|
|
883
|
+
top.index++;
|
|
884
|
+
if (! bContinue)
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
let oo: any = top.o[ii];
|
|
888
|
+
if (oo && typeof oo === 'object')
|
|
889
|
+
{
|
|
890
|
+
bRecurse = true;
|
|
891
|
+
top.index++;
|
|
892
|
+
this._stack.push({ o: oo, keys: Array.isArray(oo) ? null : Object.keys(oo), index: 0 });
|
|
893
|
+
break;
|
|
894
|
+
}
|
|
895
|
+
else
|
|
896
|
+
{
|
|
897
|
+
let s: string = JSON.stringify(oo === undefined ? null : oo);
|
|
898
|
+
bContinue = this.addToChunk(s);
|
|
899
|
+
top.index++;
|
|
900
|
+
if (! bContinue)
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (! bRecurse)
|
|
906
|
+
{
|
|
907
|
+
bContinue = this.addToChunk(']');
|
|
908
|
+
this._stack.pop();
|
|
909
|
+
if (! bContinue)
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
this.drainChunk();
|
|
916
|
+
this._s.end();
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
onError(e: Error): void
|
|
920
|
+
{
|
|
921
|
+
this._bDone = true;
|
|
922
|
+
let s: string = 'stream error';
|
|
923
|
+
if (e && typeof e.message === 'string')
|
|
924
|
+
s = e.message;
|
|
925
|
+
console.log(`jsonwriter: emitting error: ${s}`);
|
|
926
|
+
this.emit('error', s);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
onClose(): void
|
|
930
|
+
{
|
|
931
|
+
this._bDone = true;
|
|
932
|
+
this.emit('close');
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
onFinish(): void
|
|
936
|
+
{
|
|
937
|
+
this._bDone = true;
|
|
938
|
+
this.emit('finish');
|
|
939
|
+
}
|
|
940
|
+
}
|