2021-05-21 16:49:18 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
**********************************************/ /* c8 ignore next 2 */
|
|
|
|
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
|
|
|
|
(function(require){ var module={} // make module AMD/node compatible...
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
var types = require('ig-types')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
/*********************************************************************/
|
|
|
|
|
//
|
|
|
|
|
// XXX thinks this needs to do:
|
|
|
|
|
// - walk object tree - DONE
|
|
|
|
|
// - generate a spec - DONE
|
|
|
|
|
// - serializable
|
|
|
|
|
// - support props
|
|
|
|
|
// - build object via spec
|
|
|
|
|
// - update object via spec
|
|
|
|
|
// - subtract specs (diff)
|
|
|
|
|
// - full
|
|
|
|
|
// - relaxed -- ignore item order
|
2021-06-07 15:45:49 +03:00
|
|
|
// - modes:
|
|
|
|
|
// - JSON
|
|
|
|
|
// - reconstructable
|
|
|
|
|
// - full
|
|
|
|
|
// - not reconstructable -- if some types are used (functions, ...)
|
|
|
|
|
// - compare protocol
|
|
|
|
|
// - reconstruct protocol
|
2021-06-07 00:42:14 +03:00
|
|
|
//
|
|
|
|
|
//
|
2021-05-21 16:49:18 +03:00
|
|
|
/*********************************************************************/
|
|
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// XXX need a way to uniquely serilaize this to a string path...
|
|
|
|
|
// ...or some other way to use it in a convinient manner...
|
|
|
|
|
var CONTENT =
|
|
|
|
|
module.CONTENT =
|
|
|
|
|
//Symbol.CONTENT =
|
|
|
|
|
Symbol('CONTENT')
|
2021-05-31 16:46:59 +03:00
|
|
|
|
|
|
|
|
|
2021-06-07 15:45:49 +03:00
|
|
|
//
|
|
|
|
|
// Format:
|
|
|
|
|
// {
|
|
|
|
|
// <name>: {
|
|
|
|
|
// // optional
|
|
|
|
|
// final: <bool>,
|
|
|
|
|
//
|
|
|
|
|
// match: <name> | <func>,
|
|
|
|
|
//
|
|
|
|
|
// handle: <name> | <func>,
|
|
|
|
|
// },
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// NOTE: this is more of a grammar than a set of object handlers, nother
|
|
|
|
|
// way to think of this is as a set of handlrs of aspects of objects
|
|
|
|
|
// and not full objects...
|
|
|
|
|
// XXX not sure if this is how this is going to continue though as
|
|
|
|
|
// we'll need to organize constructors preferably within this
|
|
|
|
|
// structure and keep it extensible...
|
|
|
|
|
//
|
2021-06-07 18:49:14 +03:00
|
|
|
// XXX might be nice to have conditional stopping...
|
|
|
|
|
// a-la event.preventDefault()
|
|
|
|
|
// XXX need option threading...
|
2021-05-30 11:46:44 +03:00
|
|
|
// XXX need to deal with functions...
|
2021-05-21 16:49:18 +03:00
|
|
|
var HANDLERS =
|
|
|
|
|
module.HANDLERS = {
|
2021-06-07 00:42:14 +03:00
|
|
|
|
|
|
|
|
// null...
|
|
|
|
|
//
|
2021-05-24 15:22:44 +03:00
|
|
|
null: {
|
|
|
|
|
final: true,
|
|
|
|
|
match: function(obj){
|
|
|
|
|
return obj === null },
|
2021-06-07 15:45:49 +03:00
|
|
|
handle: 'value', },
|
2021-05-24 15:22:44 +03:00
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// Functions...
|
|
|
|
|
//
|
|
|
|
|
// XXX STUB...
|
|
|
|
|
func: {
|
|
|
|
|
match: function(obj){
|
|
|
|
|
return typeof(obj) == 'function' },
|
2021-06-07 15:45:49 +03:00
|
|
|
handle: 'object', },
|
2021-06-07 00:42:14 +03:00
|
|
|
|
2021-06-07 18:49:14 +03:00
|
|
|
// Text...
|
|
|
|
|
//
|
|
|
|
|
// XXX EXPERIMENTAL...
|
|
|
|
|
text: {
|
|
|
|
|
final: true,
|
|
|
|
|
match: function(obj){
|
|
|
|
|
return typeof(obj) == 'string'
|
|
|
|
|
// XXX make this more optimal...
|
|
|
|
|
&& obj.includes('\n') },
|
|
|
|
|
handle: function(obj){
|
|
|
|
|
return [[],
|
|
|
|
|
{
|
|
|
|
|
type: 'Text',
|
|
|
|
|
source: obj,
|
|
|
|
|
},
|
|
|
|
|
obj.split(/\n/g)
|
|
|
|
|
.map(function(line, i){
|
|
|
|
|
return [[module.CONTENT, i], line] }) ] }, },
|
|
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// Non-Objects...
|
|
|
|
|
//
|
|
|
|
|
// NOTE: this will include undefined and NaN...
|
2021-05-21 16:49:18 +03:00
|
|
|
value: {
|
2021-06-07 00:42:14 +03:00
|
|
|
final: true,
|
2021-05-21 16:49:18 +03:00
|
|
|
match: function(obj){
|
2021-06-07 00:42:14 +03:00
|
|
|
return typeof(obj) != 'object'
|
|
|
|
|
&& typeof(obj) != 'function' },
|
2021-05-21 16:49:18 +03:00
|
|
|
handle: function(obj){
|
2021-06-07 00:42:14 +03:00
|
|
|
return [[], obj] }, },
|
2021-05-21 16:49:18 +03:00
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// Base objects...
|
|
|
|
|
//
|
2021-05-21 16:49:18 +03:00
|
|
|
object: {
|
|
|
|
|
match: function(obj){
|
|
|
|
|
return typeof(obj) == 'object' },
|
|
|
|
|
handle: function(obj){
|
|
|
|
|
return [[], {
|
2021-06-07 00:42:14 +03:00
|
|
|
// XXX need to check if a constructor is built-in...
|
2021-05-21 16:49:18 +03:00
|
|
|
type: obj.constructor.name,
|
|
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// Object generations:
|
|
|
|
|
// 1 - directly constructed objects
|
|
|
|
|
// 2 - objects at least one level deeper than gen 1
|
|
|
|
|
gen: obj.constructor.prototype === obj.__proto__ ? 1 : 2,
|
|
|
|
|
|
|
|
|
|
// XXX
|
|
|
|
|
source: obj,
|
|
|
|
|
}] }, },
|
|
|
|
|
// special keys...
|
|
|
|
|
proto: {
|
|
|
|
|
match: function(obj){
|
|
|
|
|
return typeof(obj) == 'object'
|
|
|
|
|
&& obj.constructor.prototype !== obj.__proto__ },
|
2021-05-24 15:22:44 +03:00
|
|
|
handle: function(obj){
|
2021-06-07 00:42:14 +03:00
|
|
|
return [[ [['__proto__'], obj.__proto__], ]] }, },
|
|
|
|
|
// XXX any other special keys???
|
|
|
|
|
// - non-iterable?
|
|
|
|
|
|
2021-05-24 15:22:44 +03:00
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// Entries / Non-attribute (encapsulated) content...
|
|
|
|
|
//
|
2021-05-30 11:46:44 +03:00
|
|
|
setEntries: {
|
|
|
|
|
match: function(obj){
|
|
|
|
|
return obj instanceof Set },
|
|
|
|
|
// NOTE: we are indexing sets...
|
|
|
|
|
handle: function(obj){
|
|
|
|
|
return [ obj.values()
|
|
|
|
|
.map(function(v, i){
|
2021-06-07 00:42:14 +03:00
|
|
|
return [[module.CONTENT, i], v] })
|
|
|
|
|
.toArray() ] }, },
|
2021-05-30 11:46:44 +03:00
|
|
|
mapEntries: {
|
2021-05-21 16:49:18 +03:00
|
|
|
match: function(obj){
|
2021-05-30 11:46:44 +03:00
|
|
|
return obj instanceof Map },
|
2021-05-31 16:46:59 +03:00
|
|
|
handle: function(obj, path, options){
|
2021-05-30 11:46:44 +03:00
|
|
|
return [ obj.entries()
|
|
|
|
|
.map(function([k, v], i){
|
|
|
|
|
return [
|
2021-06-07 00:42:14 +03:00
|
|
|
[[module.CONTENT, i +'@key'], k],
|
|
|
|
|
[[module.CONTENT, i], v],
|
2021-05-30 11:46:44 +03:00
|
|
|
] })
|
|
|
|
|
.flat()
|
2021-06-07 00:42:14 +03:00
|
|
|
.toArray() ] }, },
|
2021-05-21 16:49:18 +03:00
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// Keys / Attributes...
|
|
|
|
|
//
|
|
|
|
|
// NOTE: this includes array items...
|
|
|
|
|
//
|
2021-05-23 23:44:28 +03:00
|
|
|
// XXX do we need to treat array keys as a special case???
|
2021-05-28 16:53:17 +03:00
|
|
|
// ...the best approach could be to simply:
|
|
|
|
|
// - prioretize handlers -- already done
|
|
|
|
|
// - skip repeating keys
|
|
|
|
|
// ...this could be done on the root handler level...
|
2021-05-24 15:22:44 +03:00
|
|
|
// XXX need to optionally handle props...
|
2021-05-21 16:49:18 +03:00
|
|
|
keys: {
|
2021-06-07 00:42:14 +03:00
|
|
|
//match: 'object',
|
|
|
|
|
//match: ['object', 'func'],
|
|
|
|
|
match: function(obj, handlers){
|
|
|
|
|
return handlers.object.match(obj)
|
|
|
|
|
|| handlers.func.match(obj) },
|
2021-05-21 16:49:18 +03:00
|
|
|
handle: function(obj){
|
|
|
|
|
return [ Object.entries(obj)
|
|
|
|
|
.map(function([k, v]){
|
2021-06-07 00:42:14 +03:00
|
|
|
return [[k], v] }), ] }, },
|
|
|
|
|
/* XXX
|
|
|
|
|
props: {
|
|
|
|
|
//match: 'object',
|
|
|
|
|
//match: ['object', 'func'],
|
|
|
|
|
match: function(obj, handlers){
|
|
|
|
|
return handlers.object.match(obj)
|
|
|
|
|
|| handlers.func.match(obj) },
|
|
|
|
|
handle: function(obj){
|
|
|
|
|
return [key, value, next] }, },
|
|
|
|
|
//*/
|
|
|
|
|
|
2021-05-21 16:49:18 +03:00
|
|
|
|
2021-06-07 15:45:49 +03:00
|
|
|
/* XXX not sure about this...
|
|
|
|
|
// Service stuff...
|
|
|
|
|
//
|
|
|
|
|
error: {
|
|
|
|
|
match: function(obj){},
|
|
|
|
|
},
|
|
|
|
|
//*/
|
|
|
|
|
|
2021-05-21 16:49:18 +03:00
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// Testing...
|
|
|
|
|
//
|
|
|
|
|
// XXX alias loop...
|
|
|
|
|
//alias_loop: { match: 'alias_loop' },
|
|
|
|
|
//alias_loop_a: { match: 'alias_loop_b' },
|
|
|
|
|
//alias_loop_b: { match: 'alias_loop_a' },
|
|
|
|
|
// XXX orphaned alias...
|
|
|
|
|
//alias_orphan: { match: 'orphan' },
|
|
|
|
|
//false: { match: false },
|
2021-05-21 16:49:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
|
2021-05-28 19:37:48 +03:00
|
|
|
// XXX use STOP...
|
2021-06-07 00:42:14 +03:00
|
|
|
// XXX might be good to cache output via some citeria (type?)...
|
|
|
|
|
// ...this criteria needs to be consistent with how .match(..) works...
|
|
|
|
|
// XXX does .match(..) need options???
|
|
|
|
|
// XXX do we warn of orphans???
|
2021-05-21 16:49:18 +03:00
|
|
|
var getHandlers =
|
|
|
|
|
module.getHandlers =
|
|
|
|
|
function(obj, handlers=module.HANDLERS){
|
2021-05-28 19:37:48 +03:00
|
|
|
return [...Object.entries(handlers)
|
2021-06-07 00:42:14 +03:00
|
|
|
.iter()
|
|
|
|
|
.filter(function([k, v]){
|
|
|
|
|
var stop = !!v.final
|
|
|
|
|
// expand aliases...
|
|
|
|
|
var seen = new Set()
|
|
|
|
|
while(v && typeof(v.match) == 'string'){
|
|
|
|
|
var n = v.match
|
|
|
|
|
if(seen.has(n)){
|
|
|
|
|
throw new Error('.match(..): alias loop detected:\n\t'
|
|
|
|
|
+ [...seen, n].join('\n \t -> ')) }
|
|
|
|
|
seen.add(n)
|
|
|
|
|
v = handlers[n] }
|
|
|
|
|
// orphan or falsy .match...
|
|
|
|
|
if(!v){
|
|
|
|
|
return false }
|
|
|
|
|
// handle .final/final...
|
|
|
|
|
if(stop
|
|
|
|
|
&& v.match
|
|
|
|
|
&& v.match(obj, handlers)){
|
|
|
|
|
throw types.STOP(true) }
|
|
|
|
|
// normal match...
|
|
|
|
|
return v.match
|
|
|
|
|
&& v.match(obj, handlers) })
|
|
|
|
|
.map(function([k, v]){
|
|
|
|
|
return v })] }
|
2021-05-21 16:49:18 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-05-30 11:46:44 +03:00
|
|
|
// Format:
|
|
|
|
|
// [
|
|
|
|
|
// [<path>, {type: <name>}],
|
|
|
|
|
//
|
|
|
|
|
// [<path>, ['LINK', <path>]],
|
|
|
|
|
//
|
|
|
|
|
// [<path>, <value>],
|
|
|
|
|
// ]
|
|
|
|
|
//
|
2021-05-21 16:49:18 +03:00
|
|
|
// XXX need a way to index the path...
|
|
|
|
|
// ...and to filter paths by pattern...
|
2021-06-07 00:42:14 +03:00
|
|
|
// XXX might be a good idea to generate "structural hashes" for objects...
|
2021-05-21 16:49:18 +03:00
|
|
|
var handle =
|
|
|
|
|
module.handle =
|
|
|
|
|
function*(obj, path=[], options={}){
|
|
|
|
|
// handle recursive structures...
|
|
|
|
|
var seen = options.seen =
|
|
|
|
|
options.seen || new Map()
|
|
|
|
|
if(seen.has(obj)){
|
2021-05-25 01:43:33 +03:00
|
|
|
yield [path, ['LINK', seen.get(obj)]]
|
2021-05-21 16:49:18 +03:00
|
|
|
return }
|
|
|
|
|
typeof(obj) == 'object'
|
|
|
|
|
&& seen.set(obj, path)
|
|
|
|
|
|
|
|
|
|
// get compatible handler list...
|
|
|
|
|
var cache = options.cache =
|
|
|
|
|
options.cache || new Map()
|
2021-06-07 15:45:49 +03:00
|
|
|
var HANDLERS = options.handlers || module.HANDLERS
|
|
|
|
|
var handlers = module.getHandlers(obj, HANDLERS)
|
2021-05-21 16:49:18 +03:00
|
|
|
|
|
|
|
|
// XXX might be a good idea to move this up (or into options) so as
|
|
|
|
|
// not to define this on each call...
|
|
|
|
|
// ...we'll need to somehow curry in the path which is now passed
|
|
|
|
|
// via a closure...
|
|
|
|
|
var subtree = function*(data){
|
|
|
|
|
// a handler just returned a list of next objects to handle...
|
|
|
|
|
if(data.length == 1){
|
|
|
|
|
var next = data.pop()
|
|
|
|
|
var p = path
|
|
|
|
|
// a normal handler...
|
|
|
|
|
} else {
|
|
|
|
|
var [k, v, next] = data
|
|
|
|
|
var p = path.concat(k)
|
|
|
|
|
yield [p, v] }
|
|
|
|
|
// process queued/next objects...
|
|
|
|
|
yield* (next || [])
|
|
|
|
|
.iter()
|
|
|
|
|
.map(function*([k, v]){
|
|
|
|
|
yield* handle(v, p.concat(k), options) }) }
|
|
|
|
|
|
|
|
|
|
// apply the handlers...
|
|
|
|
|
yield* handlers
|
|
|
|
|
.iter()
|
2021-06-07 00:42:14 +03:00
|
|
|
.filter(function(handler){
|
|
|
|
|
return !!handler.handle })
|
2021-05-21 16:49:18 +03:00
|
|
|
.map(function*(handler){
|
2021-06-07 15:45:49 +03:00
|
|
|
var h = handler
|
|
|
|
|
// expand aliases...
|
|
|
|
|
while(h && typeof(h.handle) == 'string'){
|
|
|
|
|
h = HANDLERS[h.handle] }
|
|
|
|
|
yield* h.handle instanceof types.Generator ?
|
|
|
|
|
// XXX should .handle(..) be called in the context of h or handler???
|
|
|
|
|
h.handle.call(handler, obj, path, options)
|
2021-05-21 16:49:18 +03:00
|
|
|
.map(subtree)
|
2021-06-07 15:45:49 +03:00
|
|
|
: subtree(h.handle.call(handler, obj, path, options)) }) }
|
2021-05-21 16:49:18 +03:00
|
|
|
|
|
|
|
|
|
2021-05-28 19:37:48 +03:00
|
|
|
|
2021-06-07 00:42:14 +03:00
|
|
|
// XXX need to figure out a way to avoid clashes with module.CONTENT in
|
|
|
|
|
// path with actual attribute keys...
|
|
|
|
|
// ways to do this:
|
|
|
|
|
// - serialize CONTENT in a cleaver way
|
|
|
|
|
// - add a different path separator to indicate content and quote
|
|
|
|
|
// it in strings -- ':'???
|
|
|
|
|
var serializePathElem = function(p, i, l){
|
|
|
|
|
return typeof(p) == 'object' ?
|
|
|
|
|
JSON.stringify(p)
|
|
|
|
|
// quote special chars...
|
|
|
|
|
: typeof(p) == 'string' ?
|
|
|
|
|
p.replace(/([\/:])/g, '\\$1')
|
|
|
|
|
: p }
|
|
|
|
|
var serializePath = function(p){
|
|
|
|
|
return '/'+ p
|
|
|
|
|
.map(serializePathElem)
|
|
|
|
|
.reduce(function(res, e){
|
|
|
|
|
e = e === module.CONTENT ?
|
|
|
|
|
res.pop() + ':CONTENT'
|
|
|
|
|
: e
|
|
|
|
|
res.push(e)
|
|
|
|
|
return res }, [])
|
|
|
|
|
.join('/') }
|
|
|
|
|
/*/ XXX might also be a good idea to serialize the path into an
|
|
|
|
|
// arbitrary length as we always have exactly one value, e.g.:
|
|
|
|
|
// [ '/path/to/map', 'CONTENT', 'path/in/content', 123]
|
|
|
|
|
var serializePathElem = function(p, i, l){
|
2021-05-25 01:43:33 +03:00
|
|
|
return typeof(p) == 'object' ?
|
2021-06-07 00:42:14 +03:00
|
|
|
JSON.stringify(p)
|
|
|
|
|
// quote special chars...
|
|
|
|
|
: typeof(p) == 'string' ?
|
|
|
|
|
p.replace(/([\/])/g, '\\$1')
|
2021-05-25 01:43:33 +03:00
|
|
|
: p }
|
|
|
|
|
var serializePath = function(p){
|
2021-06-07 00:42:14 +03:00
|
|
|
return p
|
|
|
|
|
.map(serializePathElem)
|
|
|
|
|
.reduce(function(res, e){
|
|
|
|
|
e === module.CONTENT ?
|
|
|
|
|
res.splice(res.length, 0, 'CONTENT', '')
|
|
|
|
|
: (res[res.length-1] += '/'+ e)
|
|
|
|
|
return res }, ['']) }
|
|
|
|
|
//*/
|
2021-05-28 19:37:48 +03:00
|
|
|
var serializePaths =
|
|
|
|
|
module.serializePaths =
|
|
|
|
|
types.generator.iter
|
2021-05-21 16:49:18 +03:00
|
|
|
.map(function([p, v]){
|
2021-06-07 00:42:14 +03:00
|
|
|
return v instanceof Array && v[0] == 'LINK' ?
|
|
|
|
|
// link...
|
|
|
|
|
[serializePath(p),
|
|
|
|
|
'LINK', serializePath(v[1])]
|
|
|
|
|
: [serializePath(p), v] })
|
|
|
|
|
|
2021-05-30 11:46:44 +03:00
|
|
|
|
2021-06-07 18:49:14 +03:00
|
|
|
// remove attributes from object metadata...
|
|
|
|
|
//
|
|
|
|
|
// stripAttr(attr, ...)
|
|
|
|
|
// -> <chainable>
|
|
|
|
|
//
|
|
|
|
|
// <chainable>(<input>)
|
|
|
|
|
// -> <generator>
|
|
|
|
|
//
|
|
|
|
|
// <input>.chain(<chainable>)
|
|
|
|
|
//
|
2021-06-07 15:45:49 +03:00
|
|
|
var stripAttr =
|
|
|
|
|
module.stripAttr =
|
|
|
|
|
function(...attrs){
|
|
|
|
|
return types.generator.iter
|
|
|
|
|
.map(function([p, v]){
|
|
|
|
|
if(v && typeof(v) == 'object'){
|
|
|
|
|
// keep things non-destructive...
|
|
|
|
|
v = Object.assign({}, v)
|
|
|
|
|
attrs
|
|
|
|
|
.forEach(function(attr){
|
|
|
|
|
attr in v
|
|
|
|
|
&& (delete v[attr]) }) }
|
|
|
|
|
return [p, v] }) }
|
|
|
|
|
|
2021-05-30 11:46:44 +03:00
|
|
|
|
|
|
|
|
|
2021-05-21 16:49:18 +03:00
|
|
|
|
2021-05-24 15:22:44 +03:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
// XXX move to test...
|
|
|
|
|
|
|
|
|
|
var o = {
|
|
|
|
|
number: 123,
|
|
|
|
|
string: 'abc',
|
|
|
|
|
|
|
|
|
|
// XXX add a mode to unify these...
|
|
|
|
|
'null': null,
|
|
|
|
|
'undefined': undefined,
|
2021-06-07 00:42:14 +03:00
|
|
|
'NaN': NaN,
|
2021-05-24 15:22:44 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
empty_array: [],
|
2021-05-30 11:46:44 +03:00
|
|
|
array: [1, 2, 3,,,,'N'],
|
2021-05-24 15:22:44 +03:00
|
|
|
|
2021-05-25 01:43:33 +03:00
|
|
|
// XXX set key is the object itself, is this what we want???
|
2021-05-30 11:46:44 +03:00
|
|
|
set: new Set([
|
|
|
|
|
1,
|
|
|
|
|
[],
|
|
|
|
|
{a:1},
|
|
|
|
|
]),
|
|
|
|
|
map: new Map([
|
|
|
|
|
[[9,8,7], 123],
|
|
|
|
|
[321, {x: 123}],
|
|
|
|
|
]),
|
2021-05-24 15:22:44 +03:00
|
|
|
|
|
|
|
|
object: {
|
|
|
|
|
x: {},
|
|
|
|
|
},
|
2021-06-07 00:42:14 +03:00
|
|
|
object_gen2: Object.assign(
|
|
|
|
|
Object.create({
|
|
|
|
|
x: 'parent',
|
|
|
|
|
z: 'shadowed',
|
|
|
|
|
}),
|
|
|
|
|
{
|
|
|
|
|
y: 'local',
|
|
|
|
|
z: 'shadowing',
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
// XXX
|
|
|
|
|
func: function(){},
|
|
|
|
|
func_with_attrs: Object.assign(
|
|
|
|
|
function(){},
|
|
|
|
|
{
|
|
|
|
|
x: 333,
|
|
|
|
|
}),
|
2021-05-28 17:12:11 +03:00
|
|
|
|
|
|
|
|
array_with_attrs: Object.assign(
|
2021-05-30 11:46:44 +03:00
|
|
|
[1, 2, 3],
|
2021-05-28 17:12:11 +03:00
|
|
|
{
|
2021-05-30 11:46:44 +03:00
|
|
|
a: 'some value',
|
|
|
|
|
b: 'some other value',
|
2021-05-28 17:12:11 +03:00
|
|
|
// will overwrite 2...
|
|
|
|
|
1: 333,
|
2021-06-07 00:42:14 +03:00
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
'special/character\\in:key': [],
|
2021-06-07 18:49:14 +03:00
|
|
|
|
|
|
|
|
text: `this
|
|
|
|
|
is
|
|
|
|
|
a
|
|
|
|
|
multi-line
|
|
|
|
|
block of text...`,
|
2021-05-24 15:22:44 +03:00
|
|
|
}
|
2021-05-30 11:46:44 +03:00
|
|
|
|
|
|
|
|
// clone...
|
|
|
|
|
// NOTE: JSON does not support:
|
|
|
|
|
// - sparse arrays
|
|
|
|
|
// = sets/maps
|
|
|
|
|
// - loops
|
|
|
|
|
oo = JSON.parse(JSON.stringify(o))
|
|
|
|
|
|
2021-05-24 15:22:44 +03:00
|
|
|
// loop...
|
2021-05-30 11:46:44 +03:00
|
|
|
// NOTE: we are creating the loop before we pass it to JSON because JSON
|
|
|
|
|
// does not support loops in objects...
|
2021-05-24 15:22:44 +03:00
|
|
|
o.object.y = o.object
|
|
|
|
|
|
2021-05-28 19:37:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log([
|
|
|
|
|
...handle(o)
|
2021-06-07 15:45:49 +03:00
|
|
|
.chain(
|
|
|
|
|
serializePaths,
|
2021-06-07 18:49:14 +03:00
|
|
|
// make the output a bit more compact...
|
|
|
|
|
stripAttr('source'),
|
|
|
|
|
)])
|
2021-05-24 15:22:44 +03:00
|
|
|
|
2021-05-30 11:46:44 +03:00
|
|
|
//console.log([...handle(o)])
|
2021-05-24 15:22:44 +03:00
|
|
|
|
2021-05-21 16:49:18 +03:00
|
|
|
|
2021-05-25 01:43:33 +03:00
|
|
|
|
|
|
|
|
|
2021-05-21 16:49:18 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
* vim:set ts=4 sw=4 : */ return module })
|