ig-serialize 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,13 +5,12 @@ This extends the default JSON serialization adding the following:
5
5
  - Sparse array serialization
6
6
  - `undefined`/`NaN` serialization
7
7
  - Serialization of `Infinity`, `BigInt`'s, `Set`'s, `Map`'s
8
- - Function serialization (off by default)
8
+ - Function serialization
9
9
  - Deep and partial-deep cleen object copy
10
10
 
11
- Possible differences to JSON output:
12
- - Repeating long strings and BigInts are referenced by default instead of
13
- being reincluded in the output.
14
-
11
+ Data not stored:
12
+ - Attributes on arrays, maps, sets, and functions,
13
+ - Function closures.
15
14
 
16
15
 
17
16
  ## Motivation
@@ -30,16 +29,53 @@ For basic use:
30
29
  $ npm install ig-serilaize
31
30
  ```
32
31
 
33
- Or just download and drop [serialize.js](serialize.js) into your code.
32
+ Or just download and drop [serialize.js](./serialize.js) into your code.
34
33
 
35
34
 
36
35
  ```javascript
37
- serialize = require('ig-serialize')
36
+ var serialize = require('ig-serialize')
38
37
  ```
39
38
 
40
39
 
41
40
  ## Introduction
42
41
 
42
+ `serialize.js` provides two toolsets:
43
+
44
+ 1. A means to serialize and deserialize complex data structures into an
45
+ extended JSON format.
46
+ ```javascript
47
+ var obj = {
48
+ sparse_array: [,,,,1],
49
+ bad_number: NaN,
50
+ really_large_number: 99999999999999999999999n,
51
+ }
52
+ obj.recursive = obj
53
+ obj.re_reference = obj.sparse_array
54
+
55
+ var str = serialize(obj)
56
+
57
+ // ...
58
+
59
+ var copy = deserialize(str)
60
+ ```
61
+ This is useful when requiering serialization of data structures more
62
+ complex than pure JSON can handle.
63
+ 2. A means to cleanly copy deep data structures with guaranteed isolation.
64
+ ```javascript
65
+ var obj = {
66
+ // ...
67
+ }
68
+
69
+ var copy = deepCopy(obj)
70
+ ```
71
+
72
+ ### Long strings and large `BigInt`'s
73
+
74
+ Repeating strings and `BigInt`'s longer that `MIN_LENGTH_REF` are stored
75
+ by reference by default.
76
+
77
+ See: [`MIN_LENGTH_REF`](#min_length_ref--optionsmin_length_ref)
78
+
43
79
 
44
80
  ### Serializing functions
45
81
 
@@ -58,6 +94,12 @@ Thus, care must be taken when serializing structures containing function.
58
94
 
59
95
  ## API
60
96
 
97
+ ### `eJSON`
98
+
99
+ An `JSON`-api-compatible object providing [`.stringify(..)`](#serialize--ejsonstringify) and [`.parse(..)`](#deserialize--ejsonparse)
100
+ static methods.
101
+
102
+
61
103
  ### `serialize(..)` / `eJSON.stringify(..)`
62
104
 
63
105
  Serialize a JavaScript value into a JSON/eJSON string.
@@ -147,23 +189,38 @@ eJSON.parse(<string>, {functions: <functions>})
147
189
 
148
190
  ### `deepCopy(..)`
149
191
 
192
+ Deep-copy an object.
193
+
150
194
  ```
151
195
  deepCopy(<value>)
152
196
  -> <value>
153
197
  ```
154
198
 
199
+ The returned object is a fully sanitized through serialization clean copy.
200
+
155
201
 
156
202
  ### `partialDeepCopy(..)`
157
203
 
204
+ Partially deep-copy and object, retaining only references to functions.
205
+
158
206
  ```
159
207
  partialDeepCopy(<value>)
160
208
  -> <value>
161
209
  ```
162
210
 
211
+ The returned object is a partially sanitized through serialization clean
212
+ copy with function references copied as-is from the input retaining and
213
+ "transferring" all associated function state like attributes and closures.
214
+
215
+ Note that this is by definition a _controlled state leak_ from input
216
+ object to copy, so care must be taken to _control_ object-specific state
217
+ in function closures and attributes -- keeping function state independent
218
+ from object state is _in general_ a good practice.
219
+
163
220
 
164
221
  ### `MIN_LENGTH_REF` / `<options>.min_length_ref`
165
222
 
166
- Defines the default minimum length of repeating string or bin-int to
223
+ Defines the default minimum length of repeating string or `BigInt`'s to
167
224
  include as a reference in the output.
168
225
 
169
226
  If set to `0`, referencing will be disabled.
@@ -171,9 +228,6 @@ If set to `0`, referencing will be disabled.
171
228
  Default: 96
172
229
 
173
230
 
174
- ### `DEBUG`
175
-
176
-
177
231
 
178
232
  ## Format
179
233
 
@@ -186,32 +240,34 @@ while the input format is a bit more relaxed than in several details.
186
240
  Paths are used for internal references in cases when objects are
187
241
  encountered multiple times, e.g. in recursion.
188
242
 
189
- A path is an array of keys, the meaning of each key depends on the data
243
+ A path is an array of keys, the semantics of each key depend on the data
190
244
  structure traversed:
191
- - array -> number
192
- - object -> string
193
- - set -> number -- item order in set
194
- - map -> pair of numbers -- the first indicates item order the second
195
- if 0 selects the key, if 1 selects the value.
196
-
197
- Note that string path items are unambiguous and are always treated as
198
- attributes.
245
+ - `Array` expects a number
246
+ - `Set` expects a number -- item order in set
247
+ - `Map` expects pair of consecutive numbers -- the first indicates item
248
+ order the second if `0` selects the key, if `1` selects the value.
249
+ - `Object` expects a string
199
250
 
200
251
  An empty path indicates the root object.
201
252
 
202
253
 
254
+ Notes:
255
+ - String path items are unambiguous and are always treated as attributes.
256
+ This enables referencing of attributes of any object like arrays, maps, ...etc.
257
+ - Map/set paths are structured as if sets and maps are represented by
258
+ arrays structured as their respective constructors expect as input.
259
+
260
+
203
261
  ### Referencing
204
262
 
205
263
  If an object is encountered for a second time it will be serialized as
206
264
  a reference by path to the first occurrence.
207
265
 
208
- Format:
266
+ Grammar:
209
267
  ```bnf
210
268
  <ref> ::=
211
- '<REF' <path> '>'
212
-
213
- <path> ::=
214
- '[' <path-items> ']'
269
+ '<REF[]>'
270
+ | '<REF[' <path-items> ']>
215
271
 
216
272
  <path-items> ::=
217
273
  <item>
@@ -260,7 +316,7 @@ serialize([null, undefined, NaN]) // -> '[null,undefined,NaN]'
260
316
  ### Sparse arrays
261
317
 
262
318
  Sparse arrays are represented in the same way JavaScript handles them
263
- syntactically.
319
+ syntactically -- with commas separating empty "positions".
264
320
 
265
321
  Example:
266
322
  ```javascript
@@ -268,29 +324,111 @@ serialize([,]) // -> '[,]'
268
324
  serialize([,,]) // -> '[,,]'
269
325
  ```
270
326
 
271
- Note that trailing commas are ignored in the same way JavaScript ignores
272
- them:
327
+ Trailing commas are handled in the same way JavaScript handles them:
273
328
  ```javascript
274
- // trailing comma...
329
+ // trailing commas are ignored...
275
330
  serialize([1,]) // -> '[1]'
276
331
 
277
332
  // sparse element...
278
333
  serialize([1,,]) // -> '[1,,]'
279
334
  ```
280
335
 
336
+
281
337
  ### BigInt
282
338
 
339
+ Serialized as represented in JavaScript.
340
+
341
+ ```javascript
342
+ serialize(9999999999n) // -> '9999999999n'
343
+ ```
344
+
345
+
283
346
  ### Infinity
284
347
 
348
+ Serialized as represented in JavaScript
349
+
350
+ ```javascript
351
+ serialize(Infinity) // -> 'Infinity'
352
+ serialize(-Infinity) // -> '-Infinity'
353
+ ```
354
+
355
+
285
356
  ### Map / Set
286
357
 
358
+ Maps and sets are stored in the same format as their respective constructor
359
+ calls, dropping the `new` keyword:
360
+
361
+ Grammar:
362
+ ```bnf
363
+ <map> ::=
364
+ 'Map([])
365
+ | 'Map([' <map-items> '])
366
+
367
+ <map-items> ::=
368
+ <map-item>
369
+ | <map-item> ',' <map-items>
370
+
371
+ <map-item> ::=
372
+ '[' <value> ',' <value> ']'
373
+ ```
374
+
375
+ ```bnf
376
+ <set> ::=
377
+ 'Set([])
378
+ | 'Set([' <set-items> '])
379
+
380
+ <set-items> ::=
381
+ <value>
382
+ | <value> ',' <set-items>
383
+ ```
384
+
385
+ Examples:
386
+ ```javascript
387
+ serialize(new Set([1,2,3])) // -> 'Set([1,2,3])'
388
+ serialize(new Map([['a', 1], ['b', 2]])) // -> 'Map([["a",1],["b",2]])'
389
+ ```
390
+
391
+
287
392
  ### Functions
288
393
 
394
+ A function can be stored in one of two ways:
395
+ 1. As code (default)
396
+ 2. As a function index, if an array to store function references is given.
397
+
398
+ Grammar:
399
+ ```bnf
400
+ <func> ::=
401
+ '<FUNC[' <length> ',(' <func-spec> ')]>
402
+
403
+ <func-spec> ::=
404
+ <code>
405
+ | <index>
406
+
407
+ <length> ::= <number>
408
+
409
+ <index> ::= <number>
410
+ ```
411
+
412
+ `<length>` is the length of the next block in chars, including the braces.
413
+
414
+
415
+ ```javascript
416
+ serialize(function(){}) // -> '<FUNC[14,(function(){})]>'
417
+
418
+ var functions = []
419
+ serialize(function(){}, {functions}) // -> '<FUNC[3,(0)]>'
420
+ ```
421
+
422
+ Note that deserializing functions is disabled by default as it can pose
423
+ a security risk if the input to `deserialize(..)` is not trusted.
424
+ (see: [`deserialize(..)`](#deserialize--ejsonparse))
289
425
 
290
426
 
291
427
 
292
428
  ## Running tests
293
429
 
430
+ `serialize.js` uses ['test.js'](/flynx/test.js) for testing.
431
+
294
432
  Get the development dependencies:
295
433
  ```shell
296
434
  $ npm install -D
@@ -303,10 +441,22 @@ $ npm test
303
441
 
304
442
  To run the tests directly:
305
443
  ```shell
306
- $ node ./test.js
444
+ $ ./test.js
445
+ ```
446
+
447
+ To run the tests with modifier chains of length 3:
448
+ ```shell
449
+ $ ./test.js -m 3
307
450
  ```
308
451
 
309
452
 
310
453
 
454
+ ## License
455
+
456
+ [BSD 3-Clause License](./LICENSE)
457
+
458
+ Copyright (c) 2014-2026, Alex A. Naanou,
459
+ All rights reserved.
460
+
311
461
 
312
462
  <!-- vim:set nowrap : -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ig-serialize",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "experimental extended json serializaion...",
5
5
  "main": "serialize.js",
6
6
  "scripts": {
package/serialize.js CHANGED
@@ -54,7 +54,6 @@
54
54
  /*********************************************************************/
55
55
 
56
56
 
57
- var EMPTY = '<empty>'
58
57
  var NULL = 'null'
59
58
  var UNDEFINED = 'undefined'
60
59
  var NAN = 'NaN'
@@ -334,7 +333,6 @@ module.eJSON = {
334
333
  undefined: undefined,
335
334
  NaN: NaN,
336
335
 
337
- '<empty>': 'empty',
338
336
  '<REF': 'reference',
339
337
 
340
338
  '<FUNC[': 'func',
@@ -558,15 +556,6 @@ module.eJSON = {
558
556
  initial instanceof Array
559
557
  && initial.length++
560
558
  continue }
561
- if(str.slice(i, i+EMPTY.length) == EMPTY){
562
- index++
563
- i += EMPTY.length
564
- if(str[i] == ','){
565
- i++ }
566
- // XXX this feels hackish -- can this be deligated to the handler???
567
- initial instanceof Array
568
- && initial.length++
569
- continue }
570
559
 
571
560
  // end of input...
572
561
  if(i >= str.length-1){
@@ -782,7 +771,7 @@ function(str, options){
782
771
 
783
772
  var deepCopy =
784
773
  module.deepCopy =
785
- function(obj, funcs){
774
+ function(obj, funcs=true){
786
775
  var options = {functions: funcs}
787
776
  return deserialize(
788
777
  serialize(obj, null, 0, options),
@@ -792,10 +781,7 @@ function(obj, funcs){
792
781
  var partialDeepCopy =
793
782
  module.partialDeepCopy =
794
783
  function(obj, funcs=[]){
795
- var options = {functions: funcs}
796
- return deserialize(
797
- serialize(obj, null, 0, options),
798
- options) }
784
+ return deepCopy(obj, funcs) }
799
785
 
800
786
 
801
787
 
package/test.js CHANGED
@@ -228,11 +228,6 @@ test.Cases({
228
228
 
229
229
  // arrays...
230
230
  ['[1,2,]', '[1,2]'],
231
-
232
- // sparse arrays...
233
- ['[,]', '[<empty>]'],
234
- ['[1,2,,]', '[1,2,<empty>]'],
235
- ['[1,2,<empty>,]', '[1,2,<empty>]'],
236
231
  ],
237
232
  'syntax-simplifications': function(assert){
238
233
  var aa, bb