@voxgig/sdkgen 0.24.0 → 0.26.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/bin/voxgig-sdkgen +6 -6
- package/dist/action/action.js +1 -2
- package/dist/action/action.js.map +1 -1
- package/dist/action/feature.js +4 -2
- package/dist/action/feature.js.map +1 -1
- package/dist/action/target.js +4 -2
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Entity.js +2 -1
- package/dist/cmp/Entity.js.map +1 -1
- package/dist/cmp/FeatureHook.js +2 -1
- package/dist/cmp/FeatureHook.js.map +1 -1
- package/dist/cmp/Main.js.map +1 -1
- package/dist/cmp/ReadmeEntity.js +2 -1
- package/dist/cmp/ReadmeEntity.js.map +1 -1
- package/dist/cmp/Test.d.ts +2 -0
- package/dist/cmp/Test.js +17 -0
- package/dist/cmp/Test.js.map +1 -0
- package/dist/sdkgen.d.ts +2 -1
- package/dist/sdkgen.js +17 -28
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +3 -1
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -1
- package/model/sdkgen.jsonic +8 -8
- package/package.json +8 -6
- package/project/.sdk/model/feature/log.jsonic +2 -2
- package/project/.sdk/model/feature/test.jsonic +7 -2
- package/project/.sdk/model/target/js.jsonic +2 -2
- package/project/.sdk/model/target/ts.jsonic +2 -2
- package/project/.sdk/src/cmp/js/Main_js.ts +3 -3
- package/project/.sdk/src/cmp/js/Quick_js.ts +1 -1
- package/project/.sdk/src/cmp/ts/Config_ts.ts +53 -6
- package/project/.sdk/src/cmp/ts/EntityOperation_ts.ts +3 -21
- package/project/.sdk/src/cmp/ts/Entity_ts.ts +3 -5
- package/project/.sdk/src/cmp/ts/Main_ts.ts +22 -12
- package/project/.sdk/src/cmp/ts/Package_ts.ts +32 -12
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +341 -2
- package/project/.sdk/src/cmp/ts/TestMain_ts.ts +0 -3
- package/project/.sdk/src/cmp/ts/Test_ts.ts +21 -3
- package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +38 -5
- package/project/.sdk/src/cmp/ts/fragment/Entity.fragment.ts +50 -38
- package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -7
- package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +47 -31
- package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +49 -31
- package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +50 -33
- package/project/.sdk/src/cmp/ts/fragment/EntityRemoveOp.fragment.ts +50 -31
- package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +51 -31
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +70 -34
- package/project/.sdk/src/cmp/ts/tsconfig.json +15 -0
- package/project/.sdk/src/cmp/ts/utility_ts.ts +55 -1
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +158 -104
- package/project/.sdk/tm/ts/src/types.ts +164 -34
- package/project/.sdk/tm/ts/src/utility/AuthUtility.ts +8 -2
- package/project/.sdk/tm/ts/src/utility/BodyUtility.ts +9 -6
- package/project/.sdk/tm/ts/src/utility/CleanUtility.ts +17 -31
- package/project/.sdk/tm/ts/src/utility/ContextUtility.ts +14 -54
- package/project/.sdk/tm/ts/src/utility/DoneUtility.ts +1 -1
- package/project/.sdk/tm/ts/src/utility/ErrorUtility.ts +7 -3
- package/project/.sdk/tm/ts/src/utility/FeaturehookUtility.ts +8 -5
- package/project/.sdk/tm/ts/src/utility/FetcherUtility.ts +18 -2
- package/project/.sdk/tm/ts/src/utility/FindparamUtility.ts +12 -5
- package/project/.sdk/tm/ts/src/utility/FullurlUtility.ts +21 -5
- package/project/.sdk/tm/ts/src/utility/InitfeatureUtility.ts +3 -1
- package/project/.sdk/tm/ts/src/utility/OperationUtility.ts +23 -0
- package/project/.sdk/tm/ts/src/utility/OptionsUtility.ts +25 -3
- package/project/.sdk/tm/ts/src/utility/ParamsUtility.ts +10 -10
- package/project/.sdk/tm/ts/src/utility/PreparePathUtility.ts +18 -0
- package/project/.sdk/tm/ts/src/utility/QueryUtility.ts +2 -2
- package/project/.sdk/tm/ts/src/utility/ReqformUtility.ts +2 -2
- package/project/.sdk/tm/ts/src/utility/RequestUtility.ts +20 -3
- package/project/.sdk/tm/ts/src/utility/ResbasicUtility.ts +1 -1
- package/project/.sdk/tm/ts/src/utility/ResbodyUtility.ts +5 -3
- package/project/.sdk/tm/ts/src/utility/ResformUtility.ts +3 -3
- package/project/.sdk/tm/ts/src/utility/ResheadersUtility.ts +9 -7
- package/project/.sdk/tm/ts/src/utility/ResponseUtility.ts +35 -4
- package/project/.sdk/tm/ts/src/utility/ResultUtility.ts +24 -4
- package/project/.sdk/tm/ts/src/utility/SelectionUtility.ts +78 -0
- package/project/.sdk/tm/ts/src/utility/SpecUtility.ts +34 -27
- package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1117 -509
- package/project/.sdk/tm/ts/src/utility/Utility.ts +10 -8
- package/project/.sdk/tm/ts/test/exists.test.ts +0 -1
- package/project/.sdk/tm/ts/test/runner.ts +12 -10
- package/project/.sdk/tm/ts/test/utility/Custom.test.ts +30 -29
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +53 -47
- package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +433 -189
- package/src/action/action.ts +1 -2
- package/src/action/feature.ts +7 -2
- package/src/action/target.ts +7 -7
- package/src/cmp/Entity.ts +7 -1
- package/src/cmp/FeatureHook.ts +6 -1
- package/src/cmp/Main.ts +4 -0
- package/src/cmp/ReadmeEntity.ts +6 -1
- package/src/cmp/Test.ts +31 -0
- package/src/sdkgen.ts +19 -34
- package/src/types.ts +10 -1
- package/project/.sdk/src/cmp/ts/EntityTest_ts.ts +0 -180
- package/project/.sdk/tm/ts/src/utility/OperatorUtility.ts +0 -90
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
/* Copyright (c) 2025 Voxgig Ltd. MIT LICENSE. */
|
|
1
|
+
/* Copyright (c) 2025-2026 Voxgig Ltd. MIT LICENSE. */
|
|
2
|
+
|
|
3
|
+
// VERSION: @voxgig/struct 0.0.10
|
|
2
4
|
|
|
3
5
|
/* Voxgig Struct
|
|
4
6
|
* =============
|
|
@@ -31,13 +33,15 @@
|
|
|
31
33
|
* - stringify: human-friendly string version of a value.
|
|
32
34
|
* - escre: escape a regular expresion string.
|
|
33
35
|
* - escurl: escape a url.
|
|
34
|
-
* -
|
|
36
|
+
* - join: join parts of a url, merging forward slashes.
|
|
35
37
|
*
|
|
36
38
|
* This set of functions and supporting utilities is designed to work
|
|
37
39
|
* uniformly across many languages, meaning that some code that may be
|
|
38
40
|
* functionally redundant in specific languages is still retained to
|
|
39
41
|
* keep the code human comparable.
|
|
40
42
|
*
|
|
43
|
+
* NOTE: Lists are assumed to be mutable and reference stable.
|
|
44
|
+
*
|
|
41
45
|
* NOTE: In this code JSON nulls are in general *not* considered the
|
|
42
46
|
* same as the undefined value in the given language. However most
|
|
43
47
|
* JSON parsers do use the undefined value to represent JSON
|
|
@@ -52,16 +56,16 @@
|
|
|
52
56
|
|
|
53
57
|
// String constants are explicitly defined.
|
|
54
58
|
|
|
55
|
-
// Mode value for inject step.
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const S_MKEY = 'key'
|
|
59
|
+
// Mode value for inject step (bitfield).
|
|
60
|
+
const M_KEYPRE = 1
|
|
61
|
+
const M_KEYPOST = 2
|
|
62
|
+
const M_VAL = 4
|
|
60
63
|
|
|
61
|
-
// Special
|
|
64
|
+
// Special strings.
|
|
62
65
|
const S_BKEY = '`$KEY`'
|
|
63
66
|
const S_BANNO = '`$ANNO`'
|
|
64
67
|
const S_BEXACT = '`$EXACT`'
|
|
68
|
+
const S_BVAL = '`$VAL`'
|
|
65
69
|
|
|
66
70
|
const S_DKEY = '$KEY'
|
|
67
71
|
const S_DTOP = '$TOP'
|
|
@@ -69,33 +73,87 @@ const S_DERRS = '$ERRS'
|
|
|
69
73
|
const S_DSPEC = '$SPEC'
|
|
70
74
|
|
|
71
75
|
// General strings.
|
|
72
|
-
const
|
|
76
|
+
const S_list = 'list'
|
|
73
77
|
const S_base = 'base'
|
|
74
78
|
const S_boolean = 'boolean'
|
|
75
79
|
const S_function = 'function'
|
|
80
|
+
const S_symbol = 'symbol'
|
|
81
|
+
const S_instance = 'instance'
|
|
82
|
+
const S_key = 'key'
|
|
83
|
+
const S_any = 'any'
|
|
84
|
+
const S_nil = 'nil'
|
|
85
|
+
const S_null = 'null'
|
|
76
86
|
const S_number = 'number'
|
|
77
87
|
const S_object = 'object'
|
|
78
88
|
const S_string = 'string'
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
89
|
+
const S_decimal = 'decimal'
|
|
90
|
+
const S_integer = 'integer'
|
|
91
|
+
const S_map = 'map'
|
|
92
|
+
const S_scalar = 'scalar'
|
|
93
|
+
const S_node = 'node'
|
|
94
|
+
|
|
95
|
+
// Character strings.
|
|
82
96
|
const S_BT = '`'
|
|
97
|
+
const S_CN = ':'
|
|
98
|
+
const S_CS = ']'
|
|
83
99
|
const S_DS = '$'
|
|
84
100
|
const S_DT = '.'
|
|
85
|
-
const S_CN = ':'
|
|
86
101
|
const S_FS = '/'
|
|
102
|
+
const S_KEY = 'KEY'
|
|
103
|
+
const S_MT = ''
|
|
87
104
|
const S_OS = '['
|
|
88
|
-
const S_CS = ']'
|
|
89
105
|
const S_SP = ' '
|
|
90
|
-
const
|
|
106
|
+
const S_CM = ','
|
|
91
107
|
const S_VIZ = ': '
|
|
92
108
|
|
|
109
|
+
// Types
|
|
110
|
+
let t = 31
|
|
111
|
+
const T_any = (1 << t--) - 1
|
|
112
|
+
const T_noval = 1 << t-- // Means property absent, undefined. Also NOT a scalar!
|
|
113
|
+
const T_boolean = 1 << t--
|
|
114
|
+
const T_decimal = 1 << t--
|
|
115
|
+
const T_integer = 1 << t--
|
|
116
|
+
const T_number = 1 << t--
|
|
117
|
+
const T_string = 1 << t--
|
|
118
|
+
const T_function = 1 << t--
|
|
119
|
+
const T_symbol = 1 << t--
|
|
120
|
+
const T_null = 1 << t-- // The actual JSON null value.
|
|
121
|
+
t -= 7
|
|
122
|
+
const T_list = 1 << t--
|
|
123
|
+
const T_map = 1 << t--
|
|
124
|
+
const T_instance = 1 << t--
|
|
125
|
+
t -= 4
|
|
126
|
+
const T_scalar = 1 << t--
|
|
127
|
+
const T_node = 1 << t--
|
|
128
|
+
|
|
129
|
+
const TYPENAME = [
|
|
130
|
+
S_any,
|
|
131
|
+
S_nil,
|
|
132
|
+
S_boolean,
|
|
133
|
+
S_decimal,
|
|
134
|
+
S_integer,
|
|
135
|
+
S_number,
|
|
136
|
+
S_string,
|
|
137
|
+
S_function,
|
|
138
|
+
S_symbol,
|
|
139
|
+
S_null,
|
|
140
|
+
'', '', '',
|
|
141
|
+
'', '', '', '',
|
|
142
|
+
S_list,
|
|
143
|
+
S_map,
|
|
144
|
+
S_instance,
|
|
145
|
+
'', '', '', '',
|
|
146
|
+
S_scalar,
|
|
147
|
+
S_node,
|
|
148
|
+
]
|
|
93
149
|
|
|
94
150
|
// The standard undefined value for this language.
|
|
95
|
-
const
|
|
151
|
+
const NONE = undefined
|
|
96
152
|
|
|
97
|
-
// Private
|
|
153
|
+
// Private markers
|
|
98
154
|
const SKIP = { '`$SKIP`': true }
|
|
155
|
+
const DELETE = { '`$DELETE`': true }
|
|
156
|
+
|
|
99
157
|
|
|
100
158
|
// Regular expression constants
|
|
101
159
|
const R_INTEGER_KEY = /^[-0-9]+$/ // Match integer keys (including <0).
|
|
@@ -105,7 +163,7 @@ const R_LEADING_TRAILING_SLASH = /([^\/])\/+/ // Multiple slashes in UR
|
|
|
105
163
|
const R_LEADING_SLASH = /^\/+/ // Leading slashes in URLs.
|
|
106
164
|
const R_QUOTES = /"/g // Double quotes for removal.
|
|
107
165
|
const R_DOT = /\./g // Dots in path strings.
|
|
108
|
-
const
|
|
166
|
+
const R_CLONE_REF = /^`\$REF:([0-9]+)`$/ // Copy reference in cloning.
|
|
109
167
|
const R_META_PATH = /^([^$]+)\$([=~])(.+)$/ // Meta path syntax.
|
|
110
168
|
const R_DOUBLE_DOLLAR = /\$\$/g // Double dollar escape sequence.
|
|
111
169
|
const R_TRANSFORM_NAME = /`\$([A-Z]+)`/g // Transform command names.
|
|
@@ -114,6 +172,8 @@ const R_BT_ESCAPE = /\$BT/g // Backtick escape sequen
|
|
|
114
172
|
const R_DS_ESCAPE = /\$DS/g // Dollar sign escape sequence.
|
|
115
173
|
const R_INJECTION_PARTIAL = /`([^`]+)`/g // Partial string injection pattern.
|
|
116
174
|
|
|
175
|
+
// Default max depth (for walk etc).
|
|
176
|
+
const MAXDEPTH = 32
|
|
117
177
|
|
|
118
178
|
// Keys are strings for maps, or integers for lists.
|
|
119
179
|
type PropKey = string | number
|
|
@@ -125,8 +185,7 @@ type Indexable = { [key: string]: any } & { [key: number]: any }
|
|
|
125
185
|
// For each key in a node (map or list), perform value injections in
|
|
126
186
|
// three phases: on key value, before child, and then on key value again.
|
|
127
187
|
// This mode is passed via the Injection structure.
|
|
128
|
-
type InjectMode =
|
|
129
|
-
|
|
188
|
+
type InjectMode = number
|
|
130
189
|
|
|
131
190
|
// Handle value injections using backtick escape sequences:
|
|
132
191
|
// - `a.b.c`: insert value at {a:{b:{c:1}}}
|
|
@@ -138,7 +197,6 @@ type Injector = (
|
|
|
138
197
|
store: any, // Current source root value.
|
|
139
198
|
) => any
|
|
140
199
|
|
|
141
|
-
|
|
142
200
|
// Apply a custom modification to injections.
|
|
143
201
|
type Modify = (
|
|
144
202
|
val: any, // Value.
|
|
@@ -148,11 +206,10 @@ type Modify = (
|
|
|
148
206
|
store?: any, // Store, if any
|
|
149
207
|
) => void
|
|
150
208
|
|
|
151
|
-
|
|
152
209
|
// Function applied to each node and leaf when walking a node structure depth first.
|
|
153
210
|
// For {a:{b:1}} the call sequence args will be: b, 1, {b:1}, [a,b].
|
|
154
211
|
type WalkApply = (
|
|
155
|
-
// Map keys are strings, list keys are numbers, top key is
|
|
212
|
+
// Map keys are strings, list keys are numbers, top key is NONE
|
|
156
213
|
key: string | number | undefined,
|
|
157
214
|
val: any,
|
|
158
215
|
parent: any,
|
|
@@ -160,6 +217,20 @@ type WalkApply = (
|
|
|
160
217
|
) => any
|
|
161
218
|
|
|
162
219
|
|
|
220
|
+
// Return type string for narrowest type.
|
|
221
|
+
function typename(t: number) {
|
|
222
|
+
return getelem(TYPENAME, Math.clz32(t), TYPENAME[0])
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
// Get a defined value. Returns alt if val is undefined.
|
|
227
|
+
function getdef(val: any, alt: any) {
|
|
228
|
+
if (NONE === val) {
|
|
229
|
+
return alt
|
|
230
|
+
}
|
|
231
|
+
return val
|
|
232
|
+
}
|
|
233
|
+
|
|
163
234
|
|
|
164
235
|
// Value is a node - defined, and a map (hash) or list (array).
|
|
165
236
|
// NOTE: typescript
|
|
@@ -229,11 +300,14 @@ function size(val: any): number {
|
|
|
229
300
|
}
|
|
230
301
|
|
|
231
302
|
|
|
232
|
-
// Extract part of an array or string into a new value, from the start
|
|
233
|
-
// If no end is specified, extract to the
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
|
|
303
|
+
// Extract part of an array or string into a new value, from the start
|
|
304
|
+
// point to the end point. If no end is specified, extract to the
|
|
305
|
+
// full length of the value. Negative arguments count from the end of
|
|
306
|
+
// the value. For numbers, perform min and max bounding, where start
|
|
307
|
+
// is inclusive, and end is *exclusive*.
|
|
308
|
+
// NOTE: input lists are not mutated by default. Use the mutate
|
|
309
|
+
// argument to mutate lists in place.
|
|
310
|
+
function slice<V extends any>(val: V, start?: number, end?: number, mutate?: boolean): V {
|
|
237
311
|
if (S_number === typeof val) {
|
|
238
312
|
start = null == start || S_number !== typeof start ? Number.MIN_SAFE_INTEGER : start
|
|
239
313
|
end = (null == end || S_number !== typeof end ? Number.MAX_SAFE_INTEGER : end) - 1
|
|
@@ -277,7 +351,15 @@ function slice<V extends any>(val: V, start?: number, end?: number): V {
|
|
|
277
351
|
|
|
278
352
|
if (-1 < start && start <= end && end <= vlen) {
|
|
279
353
|
if (islist(val)) {
|
|
280
|
-
|
|
354
|
+
if (mutate) {
|
|
355
|
+
for (let i = 0, j = start; j < end; i++, j++) {
|
|
356
|
+
val[i] = val[j]
|
|
357
|
+
}
|
|
358
|
+
val.length = (end - start)
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
val = val.slice(start, end) as V
|
|
362
|
+
}
|
|
281
363
|
}
|
|
282
364
|
else if (S_string === typeof val) {
|
|
283
365
|
val = (val as string).substring(start, end) as V
|
|
@@ -297,6 +379,7 @@ function slice<V extends any>(val: V, start?: number, end?: number): V {
|
|
|
297
379
|
}
|
|
298
380
|
|
|
299
381
|
|
|
382
|
+
// String padding.
|
|
300
383
|
function pad(str: any, padding?: number, padchar?: string): string {
|
|
301
384
|
str = S_string === typeof str ? str : stringify(str)
|
|
302
385
|
padding = null == padding ? 44 : padding
|
|
@@ -305,34 +388,71 @@ function pad(str: any, padding?: number, padchar?: string): string {
|
|
|
305
388
|
}
|
|
306
389
|
|
|
307
390
|
|
|
308
|
-
// Determine the type of a value as a
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
return S_null
|
|
391
|
+
// Determine the type of a value as a bit code.
|
|
392
|
+
function typify(value: any): number {
|
|
393
|
+
|
|
394
|
+
if (undefined === value) {
|
|
395
|
+
return T_noval
|
|
314
396
|
}
|
|
315
397
|
|
|
316
|
-
const
|
|
398
|
+
const typestr = typeof value
|
|
399
|
+
|
|
400
|
+
if (null === value) {
|
|
401
|
+
return T_scalar | T_null
|
|
402
|
+
}
|
|
403
|
+
else if (S_number === typestr) {
|
|
404
|
+
if (Number.isInteger(value)) {
|
|
405
|
+
return T_scalar | T_number | T_integer
|
|
406
|
+
}
|
|
407
|
+
else if (isNaN(value)) {
|
|
408
|
+
return T_noval
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
return T_scalar | T_number | T_decimal
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
else if (S_string === typestr) {
|
|
415
|
+
return T_scalar | T_string
|
|
416
|
+
}
|
|
417
|
+
else if (S_boolean === typestr) {
|
|
418
|
+
return T_scalar | T_boolean
|
|
419
|
+
}
|
|
420
|
+
else if (S_function === typestr) {
|
|
421
|
+
return T_scalar | T_function
|
|
422
|
+
}
|
|
317
423
|
|
|
318
|
-
|
|
319
|
-
|
|
424
|
+
// For languages that have symbolic atoms.
|
|
425
|
+
else if (S_symbol === typestr) {
|
|
426
|
+
return T_scalar | T_symbol
|
|
320
427
|
}
|
|
321
428
|
|
|
322
|
-
if (
|
|
323
|
-
return
|
|
429
|
+
else if (Array.isArray(value)) {
|
|
430
|
+
return T_node | T_list
|
|
324
431
|
}
|
|
325
432
|
|
|
326
|
-
|
|
433
|
+
else if (S_object === typestr) {
|
|
434
|
+
|
|
435
|
+
if (value.constructor instanceof Function) {
|
|
436
|
+
let cname = value.constructor.name
|
|
437
|
+
if ('Object' !== cname && 'Array' !== cname) {
|
|
438
|
+
return T_node | T_instance
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return T_node | T_map
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Anything else (e.g. bigint) is considered T_any
|
|
446
|
+
return T_any
|
|
327
447
|
}
|
|
328
448
|
|
|
329
449
|
|
|
330
450
|
// Get a list element. The key should be an integer, or a string
|
|
331
451
|
// that can parse to an integer only. Negative integers count from the end of the list.
|
|
332
452
|
function getelem(val: any, key: any, alt?: any) {
|
|
333
|
-
let out =
|
|
453
|
+
let out = NONE
|
|
334
454
|
|
|
335
|
-
if (
|
|
455
|
+
if (NONE === val || NONE === key) {
|
|
336
456
|
return alt
|
|
337
457
|
}
|
|
338
458
|
|
|
@@ -346,8 +466,8 @@ function getelem(val: any, key: any, alt?: any) {
|
|
|
346
466
|
}
|
|
347
467
|
}
|
|
348
468
|
|
|
349
|
-
if (
|
|
350
|
-
return alt
|
|
469
|
+
if (NONE === out) {
|
|
470
|
+
return 0 < (T_function & typify(alt)) ? alt() : alt
|
|
351
471
|
}
|
|
352
472
|
|
|
353
473
|
return out
|
|
@@ -359,7 +479,7 @@ function getelem(val: any, key: any, alt?: any) {
|
|
|
359
479
|
function getprop(val: any, key: any, alt?: any) {
|
|
360
480
|
let out = alt
|
|
361
481
|
|
|
362
|
-
if (
|
|
482
|
+
if (NONE === val || NONE === key) {
|
|
363
483
|
return alt
|
|
364
484
|
}
|
|
365
485
|
|
|
@@ -367,7 +487,7 @@ function getprop(val: any, key: any, alt?: any) {
|
|
|
367
487
|
out = val[key]
|
|
368
488
|
}
|
|
369
489
|
|
|
370
|
-
if (
|
|
490
|
+
if (NONE === out) {
|
|
371
491
|
return alt
|
|
372
492
|
}
|
|
373
493
|
|
|
@@ -380,20 +500,20 @@ function getprop(val: any, key: any, alt?: any) {
|
|
|
380
500
|
// Number keys are converted to strings.
|
|
381
501
|
// Floats are truncated to integers.
|
|
382
502
|
// Booleans, objects, arrays, null, undefined all return empty string.
|
|
383
|
-
function strkey(key: any =
|
|
384
|
-
if (
|
|
503
|
+
function strkey(key: any = NONE): string {
|
|
504
|
+
if (NONE === key) {
|
|
385
505
|
return S_MT
|
|
386
506
|
}
|
|
387
507
|
|
|
388
|
-
|
|
508
|
+
const t = typify(key)
|
|
509
|
+
|
|
510
|
+
if (0 < (T_string & t)) {
|
|
389
511
|
return key
|
|
390
512
|
}
|
|
391
|
-
|
|
392
|
-
if (typeof key === S_boolean) {
|
|
513
|
+
else if (0 < (T_boolean & t)) {
|
|
393
514
|
return S_MT
|
|
394
515
|
}
|
|
395
|
-
|
|
396
|
-
if (typeof key === S_number) {
|
|
516
|
+
else if (0 < (T_number & t)) {
|
|
397
517
|
return key % 1 === 0 ? String(key) : String(Math.floor(key))
|
|
398
518
|
}
|
|
399
519
|
|
|
@@ -401,7 +521,8 @@ function strkey(key: any = UNDEF): string {
|
|
|
401
521
|
}
|
|
402
522
|
|
|
403
523
|
|
|
404
|
-
// Sorted keys of a map, or indexes of a list.
|
|
524
|
+
// Sorted keys of a map, or indexes (as strings) of a list.
|
|
525
|
+
// Root utility - only uses language facilities.
|
|
405
526
|
function keysof(val: any): string[] {
|
|
406
527
|
return !isnode(val) ? [] :
|
|
407
528
|
ismap(val) ? Object.keys(val).sort() : (val as any).map((_n: any, i: number) => S_MT + i)
|
|
@@ -409,22 +530,60 @@ function keysof(val: any): string[] {
|
|
|
409
530
|
|
|
410
531
|
|
|
411
532
|
// Value of property with name key in node val is defined.
|
|
533
|
+
// Root utility - only uses language facilities.
|
|
412
534
|
function haskey(val: any, key: any) {
|
|
413
|
-
return
|
|
535
|
+
return NONE !== getprop(val, key)
|
|
414
536
|
}
|
|
415
537
|
|
|
416
538
|
|
|
417
539
|
// List the sorted keys of a map or list as an array of tuples of the form [key, value].
|
|
418
|
-
//
|
|
419
|
-
|
|
420
|
-
|
|
540
|
+
// As with keysof, list indexes are converted to strings.
|
|
541
|
+
// Root utility - only uses language facilities.
|
|
542
|
+
function items(val: any): [string, any][];
|
|
543
|
+
function items<T>(val: any, apply: (item: [string, any]) => T): T[];
|
|
544
|
+
function items(
|
|
545
|
+
val: any,
|
|
546
|
+
apply?: (item: [string, any]) => any
|
|
547
|
+
): any[] {
|
|
548
|
+
let out: [string, any][] = keysof(val).map((k: any) => [k, val[k]])
|
|
549
|
+
if (null != apply) {
|
|
550
|
+
out = out.map(apply)
|
|
551
|
+
}
|
|
552
|
+
return out
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
// To replicate the array spread operator:
|
|
557
|
+
// a=1, b=[2,3], c=[4,5]
|
|
558
|
+
// [a,...b,c] -> [1,2,3,[4,5]]
|
|
559
|
+
// flatten([a,b,[c]]) -> [1,2,3,[4,5]]
|
|
560
|
+
// NOTE: [c] ensures c is not expanded
|
|
561
|
+
function flatten(list: any[], depth?: number) {
|
|
562
|
+
if (!islist(list)) {
|
|
563
|
+
return list
|
|
564
|
+
}
|
|
565
|
+
return list.flat(getdef(depth, 1))
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
// Filter item values using check function.
|
|
570
|
+
function filter(val: any, check: (item: [string, any]) => boolean): any[] {
|
|
571
|
+
let all = items(val)
|
|
572
|
+
let numall = size(all)
|
|
573
|
+
let out = []
|
|
574
|
+
for (let i = 0; i < numall; i++) {
|
|
575
|
+
if (check(all[i])) {
|
|
576
|
+
out.push(all[i][1])
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return out
|
|
421
580
|
}
|
|
422
581
|
|
|
423
582
|
|
|
424
583
|
// Escape regular expression.
|
|
425
584
|
function escre(s: string) {
|
|
426
|
-
s = null == s ? S_MT : s
|
|
427
|
-
return
|
|
585
|
+
// s = null == s ? S_MT : s
|
|
586
|
+
return replace(s, R_ESCAPE_REGEXP, '\\$&')
|
|
428
587
|
}
|
|
429
588
|
|
|
430
589
|
|
|
@@ -435,16 +594,59 @@ function escurl(s: string) {
|
|
|
435
594
|
}
|
|
436
595
|
|
|
437
596
|
|
|
438
|
-
//
|
|
439
|
-
function
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
597
|
+
// Replace a search string (all), or a regexp, in a source string.
|
|
598
|
+
function replace(s: string, from: string | RegExp, to: any) {
|
|
599
|
+
let rs = s
|
|
600
|
+
let ts = typify(s)
|
|
601
|
+
if (0 === (T_string & ts)) {
|
|
602
|
+
rs = stringify(s)
|
|
603
|
+
}
|
|
604
|
+
else if (0 < ((T_noval | T_null) & ts)) {
|
|
605
|
+
rs = S_MT
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
rs = stringify(s)
|
|
609
|
+
}
|
|
610
|
+
return rs.replace(from, to)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
// Concatenate url part strings, merging sep char as needed.
|
|
615
|
+
function join(arr: any[], sep?: string, url?: boolean) {
|
|
616
|
+
const sarr = size(arr)
|
|
617
|
+
const sepdef = getdef(sep, S_CM)
|
|
618
|
+
const sepre = 1 === size(sepdef) ? escre(sepdef) : NONE
|
|
619
|
+
const out = filter(
|
|
620
|
+
items(
|
|
621
|
+
// filter(arr, (n) => null != n[1] && S_MT !== n[1]),
|
|
622
|
+
filter(arr, (n) => (0 < (T_string & typify(n[1]))) && S_MT !== n[1]),
|
|
623
|
+
(n) => {
|
|
624
|
+
let i = +n[0]
|
|
625
|
+
let s = n[1]
|
|
626
|
+
|
|
627
|
+
if (NONE !== sepre && S_MT !== sepre) {
|
|
628
|
+
if (url && 0 === i) {
|
|
629
|
+
s = replace(s, RegExp(sepre + '+$'), S_MT)
|
|
630
|
+
return s
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (0 < i) {
|
|
634
|
+
s = replace(s, RegExp('^' + sepre + '+'), S_MT)
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (i < sarr - 1 || !url) {
|
|
638
|
+
s = replace(s, RegExp(sepre + '+$'), S_MT)
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
s = replace(s, RegExp('([^' + sepre + '])' + sepre + '+([^' + sepre + '])'),
|
|
642
|
+
'$1' + sepdef + '$2')
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return s
|
|
646
|
+
}), (n) => S_MT !== n[1])
|
|
647
|
+
.join(sepdef)
|
|
648
|
+
|
|
649
|
+
return out
|
|
448
650
|
}
|
|
449
651
|
|
|
450
652
|
|
|
@@ -453,19 +655,27 @@ function joinurl(sarr: any[]) {
|
|
|
453
655
|
// In general, the behaivor of of JavaScript's JSON.stringify(val,null,2) is followed.
|
|
454
656
|
function jsonify(val: any, flags?: { indent?: number, offset?: number }) {
|
|
455
657
|
let str = S_null
|
|
658
|
+
|
|
456
659
|
if (null != val) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
str
|
|
660
|
+
try {
|
|
661
|
+
const indent = getprop(flags, 'indent', 2)
|
|
662
|
+
str = JSON.stringify(val, null, indent)
|
|
663
|
+
if (NONE === str) {
|
|
664
|
+
str = S_null
|
|
665
|
+
}
|
|
666
|
+
const offset = getprop(flags, 'offset', 0)
|
|
667
|
+
if (0 < offset) {
|
|
668
|
+
// Left offset entire indented JSON so that it aligns with surrounding code
|
|
669
|
+
// indented by offset. Assume first brace is on line with asignment, so not offset.
|
|
670
|
+
str = '{\n' +
|
|
671
|
+
join(
|
|
672
|
+
items(
|
|
673
|
+
slice(str.split('\n'), 1),
|
|
674
|
+
(n: any) => pad(n[1], 0 - offset - size(n[1]))), '\n')
|
|
675
|
+
}
|
|
461
676
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
// Left offset entire indented JSON so that it aligns with surrounding code
|
|
465
|
-
// indented by offset.
|
|
466
|
-
str = '{\n' + str.split('\n').slice(1)
|
|
467
|
-
.map(n => pad(n, 0 - offset - size(n)))
|
|
468
|
-
.join('\n')
|
|
677
|
+
catch (e: any) {
|
|
678
|
+
str = '__JSONIFY_FAILED__'
|
|
469
679
|
}
|
|
470
680
|
}
|
|
471
681
|
|
|
@@ -478,7 +688,7 @@ function stringify(val: any, maxlen?: number, pretty?: any): string {
|
|
|
478
688
|
let valstr = S_MT
|
|
479
689
|
pretty = !!pretty
|
|
480
690
|
|
|
481
|
-
if (
|
|
691
|
+
if (NONE === val) {
|
|
482
692
|
return pretty ? '<>' : valstr
|
|
483
693
|
}
|
|
484
694
|
|
|
@@ -494,9 +704,9 @@ function stringify(val: any, maxlen?: number, pretty?: any): string {
|
|
|
494
704
|
!Array.isArray(val)
|
|
495
705
|
) {
|
|
496
706
|
const sortedObj: any = {}
|
|
497
|
-
|
|
498
|
-
sortedObj[
|
|
499
|
-
}
|
|
707
|
+
items(val, (n) => {
|
|
708
|
+
sortedObj[n[0]] = val[n[0]]
|
|
709
|
+
})
|
|
500
710
|
return sortedObj
|
|
501
711
|
}
|
|
502
712
|
return val
|
|
@@ -504,7 +714,7 @@ function stringify(val: any, maxlen?: number, pretty?: any): string {
|
|
|
504
714
|
valstr = valstr.replace(R_QUOTES, S_MT)
|
|
505
715
|
}
|
|
506
716
|
catch (err: any) {
|
|
507
|
-
valstr =
|
|
717
|
+
valstr = '__STRINGIFY_FAILED__'
|
|
508
718
|
}
|
|
509
719
|
}
|
|
510
720
|
|
|
@@ -515,8 +725,9 @@ function stringify(val: any, maxlen?: number, pretty?: any): string {
|
|
|
515
725
|
|
|
516
726
|
if (pretty) {
|
|
517
727
|
// Indicate deeper JSON levels with different terminal colors (simplistic wrt strings).
|
|
518
|
-
let c =
|
|
519
|
-
|
|
728
|
+
let c = items(
|
|
729
|
+
[81, 118, 213, 39, 208, 201, 45, 190, 129, 51, 160, 121, 226, 33, 207, 69],
|
|
730
|
+
(n) => '\x1b[38;5;' + n[1] + 'm'),
|
|
520
731
|
r = '\x1b[0m', d = 0, o = c[0], t = o
|
|
521
732
|
for (const ch of valstr) {
|
|
522
733
|
if (ch === '{' || ch === '[') {
|
|
@@ -537,34 +748,34 @@ function stringify(val: any, maxlen?: number, pretty?: any): string {
|
|
|
537
748
|
|
|
538
749
|
// Build a human friendly path string.
|
|
539
750
|
function pathify(val: any, startin?: number, endin?: number) {
|
|
540
|
-
let pathstr: string | undefined =
|
|
751
|
+
let pathstr: string | undefined = NONE
|
|
541
752
|
|
|
542
753
|
let path: any[] | undefined = islist(val) ? val :
|
|
543
754
|
S_string == typeof val ? [val] :
|
|
544
755
|
S_number == typeof val ? [val] :
|
|
545
|
-
|
|
756
|
+
NONE
|
|
546
757
|
|
|
547
758
|
const start = null == startin ? 0 : -1 < startin ? startin : 0
|
|
548
759
|
const end = null == endin ? 0 : -1 < endin ? endin : 0
|
|
549
760
|
|
|
550
|
-
if (
|
|
761
|
+
if (NONE != path && 0 <= start) {
|
|
551
762
|
path = slice(path, start, path.length - end)
|
|
552
763
|
if (0 === path.length) {
|
|
553
764
|
pathstr = '<root>'
|
|
554
765
|
}
|
|
555
766
|
else {
|
|
556
|
-
pathstr =
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
767
|
+
pathstr = join(
|
|
768
|
+
items(
|
|
769
|
+
filter(path, (n) => iskey(n[1])), (n) => {
|
|
770
|
+
let p = n[1]
|
|
771
|
+
return S_number === typeof p ? S_MT + Math.floor(p) :
|
|
772
|
+
p.replace(R_DOT, S_MT)
|
|
773
|
+
}), S_DT)
|
|
563
774
|
}
|
|
564
775
|
}
|
|
565
776
|
|
|
566
|
-
if (
|
|
567
|
-
pathstr = '<unknown-path' + (
|
|
777
|
+
if (NONE === pathstr) {
|
|
778
|
+
pathstr = '<unknown-path' + (NONE === val ? S_MT : S_CN + stringify(val, 47)) + '>'
|
|
568
779
|
}
|
|
569
780
|
|
|
570
781
|
return pathstr
|
|
@@ -572,19 +783,21 @@ function pathify(val: any, startin?: number, endin?: number) {
|
|
|
572
783
|
|
|
573
784
|
|
|
574
785
|
// Clone a JSON-like data structure.
|
|
575
|
-
// NOTE: function
|
|
786
|
+
// NOTE: function and instance values are copied, *not* cloned.
|
|
576
787
|
function clone(val: any): any {
|
|
577
788
|
const refs: any[] = []
|
|
578
|
-
const
|
|
579
|
-
|
|
789
|
+
const reftype = T_function | T_instance
|
|
790
|
+
const replacer: any = (_k: any, v: any) => 0 < (reftype & typify(v)) ?
|
|
791
|
+
(refs.push(v), '`$REF:' + (refs.length - 1) + '`') : v
|
|
580
792
|
const reviver: any = (_k: any, v: any, m: any) => S_string === typeof v ?
|
|
581
|
-
(m = v.match(
|
|
582
|
-
|
|
793
|
+
(m = v.match(R_CLONE_REF), m ? refs[m[1]] : v) : v
|
|
794
|
+
const out = NONE === val ? NONE : JSON.parse(JSON.stringify(val, replacer), reviver)
|
|
795
|
+
return out
|
|
583
796
|
}
|
|
584
797
|
|
|
585
798
|
|
|
586
799
|
// Define a JSON Object using function arguments.
|
|
587
|
-
function
|
|
800
|
+
function jm(...kv: any[]): Record<string, any> {
|
|
588
801
|
const kvsize = size(kv)
|
|
589
802
|
const o: any = {}
|
|
590
803
|
for (let i = 0; i < kvsize; i += 2) {
|
|
@@ -597,7 +810,7 @@ function jo(...kv: any[]): Record<string, any> {
|
|
|
597
810
|
|
|
598
811
|
|
|
599
812
|
// Define a JSON Array using function arguments.
|
|
600
|
-
function
|
|
813
|
+
function jt(...v: any[]): any[] {
|
|
601
814
|
const vsize = size(v)
|
|
602
815
|
const a: any = new Array(vsize)
|
|
603
816
|
for (let i = 0; i < vsize; i++) {
|
|
@@ -607,19 +820,18 @@ function ja(...v: any[]): any[] {
|
|
|
607
820
|
}
|
|
608
821
|
|
|
609
822
|
|
|
610
|
-
|
|
611
823
|
// Safely delete a property from an object or array element.
|
|
612
824
|
// Undefined arguments and invalid keys are ignored.
|
|
613
825
|
// Returns the (possibly modified) parent.
|
|
614
826
|
// For objects, the property is deleted using the delete operator.
|
|
615
827
|
// For arrays, the element at the index is removed and remaining elements are shifted down.
|
|
828
|
+
// NOTE: parent list may be new list, thus update references.
|
|
616
829
|
function delprop<PARENT>(parent: PARENT, key: any): PARENT {
|
|
617
830
|
if (!iskey(key)) {
|
|
618
831
|
return parent
|
|
619
832
|
}
|
|
620
833
|
|
|
621
834
|
if (ismap(parent)) {
|
|
622
|
-
// key = S_MT + key
|
|
623
835
|
key = strkey(key)
|
|
624
836
|
delete (parent as any)[key]
|
|
625
837
|
}
|
|
@@ -634,10 +846,12 @@ function delprop<PARENT>(parent: PARENT, key: any): PARENT {
|
|
|
634
846
|
keyI = Math.floor(keyI)
|
|
635
847
|
|
|
636
848
|
// Delete list element at position keyI, shifting later elements down.
|
|
637
|
-
|
|
638
|
-
|
|
849
|
+
const psize = size(parent)
|
|
850
|
+
if (0 <= keyI && keyI < psize) {
|
|
851
|
+
for (let pI = keyI; pI < psize - 1; pI++) {
|
|
639
852
|
parent[pI] = parent[pI + 1]
|
|
640
853
|
}
|
|
854
|
+
|
|
641
855
|
parent.length = parent.length - 1
|
|
642
856
|
}
|
|
643
857
|
}
|
|
@@ -650,6 +864,7 @@ function delprop<PARENT>(parent: PARENT, key: any): PARENT {
|
|
|
650
864
|
// Returns the (possibly modified) parent.
|
|
651
865
|
// If the parent is a list, and the key is negative, prepend the value.
|
|
652
866
|
// NOTE: If the key is above the list size, append the value; below, prepend.
|
|
867
|
+
// NOTE: parent list may be new list, thus update references.
|
|
653
868
|
function setprop<PARENT>(parent: PARENT, key: any, val: any): PARENT {
|
|
654
869
|
if (!iskey(key)) {
|
|
655
870
|
return parent
|
|
@@ -670,9 +885,11 @@ function setprop<PARENT>(parent: PARENT, key: any, val: any): PARENT {
|
|
|
670
885
|
|
|
671
886
|
keyI = Math.floor(keyI)
|
|
672
887
|
|
|
888
|
+
// TODO: DELETE list element
|
|
889
|
+
|
|
673
890
|
// Set or append value at position keyI, or append if keyI out of bounds.
|
|
674
891
|
if (0 <= keyI) {
|
|
675
|
-
parent[
|
|
892
|
+
parent[slice(keyI, 0, size(parent) + 1)] = val
|
|
676
893
|
}
|
|
677
894
|
|
|
678
895
|
// Prepend value if keyI is negative
|
|
@@ -689,22 +906,44 @@ function setprop<PARENT>(parent: PARENT, key: any, val: any): PARENT {
|
|
|
689
906
|
function walk(
|
|
690
907
|
// These arguments are the public interface.
|
|
691
908
|
val: any,
|
|
692
|
-
|
|
909
|
+
|
|
910
|
+
// Before descending into a node.
|
|
911
|
+
before?: WalkApply,
|
|
912
|
+
|
|
913
|
+
// After descending into a node.
|
|
914
|
+
after?: WalkApply,
|
|
915
|
+
|
|
916
|
+
// Maximum recursive depth, default: 32. Use null for infinite depth.
|
|
917
|
+
maxdepth?: number,
|
|
693
918
|
|
|
694
919
|
// These areguments are used for recursive state.
|
|
695
920
|
key?: string | number,
|
|
696
921
|
parent?: any,
|
|
697
922
|
path?: string[]
|
|
698
923
|
): any {
|
|
699
|
-
if (
|
|
700
|
-
|
|
701
|
-
|
|
924
|
+
if (NONE === path) {
|
|
925
|
+
path = []
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
let out = null == before ? val : before(key, val, parent, path)
|
|
929
|
+
|
|
930
|
+
maxdepth = null != maxdepth && 0 <= maxdepth ? maxdepth : MAXDEPTH
|
|
931
|
+
if (0 === maxdepth || (null != path && 0 < maxdepth && maxdepth <= path.length)) {
|
|
932
|
+
return out
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (isnode(out)) {
|
|
936
|
+
for (let [ckey, child] of items(out)) {
|
|
937
|
+
setprop(out, ckey, walk(
|
|
938
|
+
child, before, after, maxdepth, ckey, out,
|
|
939
|
+
flatten([getdef(path, []), S_MT + ckey])
|
|
940
|
+
))
|
|
702
941
|
}
|
|
703
942
|
}
|
|
704
943
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
return
|
|
944
|
+
out = null == after ? out : after(key, out, parent, path)
|
|
945
|
+
|
|
946
|
+
return out
|
|
708
947
|
}
|
|
709
948
|
|
|
710
949
|
|
|
@@ -712,8 +951,10 @@ function walk(
|
|
|
712
951
|
// precedence. Nodes override scalars. Node kinds (list or map)
|
|
713
952
|
// override each other, and do *not* merge. The first element is
|
|
714
953
|
// modified.
|
|
715
|
-
function merge(val: any): any {
|
|
716
|
-
|
|
954
|
+
function merge(val: any, maxdepth?: number): any {
|
|
955
|
+
// const md: number = null == maxdepth ? MAXDEPTH : maxdepth < 0 ? 0 : maxdepth
|
|
956
|
+
const md: number = slice(maxdepth ?? MAXDEPTH, 0)
|
|
957
|
+
let out: any = NONE
|
|
717
958
|
|
|
718
959
|
// Handle edge cases.
|
|
719
960
|
if (!islist(val)) {
|
|
@@ -724,7 +965,7 @@ function merge(val: any): any {
|
|
|
724
965
|
const lenlist = list.length
|
|
725
966
|
|
|
726
967
|
if (0 === lenlist) {
|
|
727
|
-
return
|
|
968
|
+
return NONE
|
|
728
969
|
}
|
|
729
970
|
else if (1 === lenlist) {
|
|
730
971
|
return list[0]
|
|
@@ -741,72 +982,146 @@ function merge(val: any): any {
|
|
|
741
982
|
out = obj
|
|
742
983
|
}
|
|
743
984
|
else {
|
|
744
|
-
//
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
) {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
}
|
|
985
|
+
// Current value at path end in overriding node.
|
|
986
|
+
let cur: any[] = [out]
|
|
987
|
+
|
|
988
|
+
// Current value at path end in destination node.
|
|
989
|
+
let dst: any[] = [out]
|
|
990
|
+
|
|
991
|
+
function before(
|
|
992
|
+
key: string | number | undefined,
|
|
993
|
+
val: any,
|
|
994
|
+
_parent: any,
|
|
995
|
+
path: string[]
|
|
996
|
+
) {
|
|
997
|
+
const pI = size(path)
|
|
998
|
+
|
|
999
|
+
if (md <= pI) {
|
|
1000
|
+
setprop(cur[pI - 1], key, val)
|
|
1001
|
+
}
|
|
762
1002
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
1003
|
+
// Scalars just override directly.
|
|
1004
|
+
else if (!isnode(val)) {
|
|
1005
|
+
cur[pI] = val
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// Descend into override node - Set up correct target in `after` function.
|
|
1009
|
+
else {
|
|
770
1010
|
|
|
771
|
-
//
|
|
772
|
-
|
|
773
|
-
|
|
1011
|
+
// Descend into destination node using same key.
|
|
1012
|
+
dst[pI] = 0 < pI ? getprop(dst[pI - 1], key) : dst[pI]
|
|
1013
|
+
const tval = dst[pI]
|
|
1014
|
+
|
|
1015
|
+
// Destination empty, so create node (unless override is class instance).
|
|
1016
|
+
if (NONE === tval && 0 === (T_instance & typify(val))) {
|
|
1017
|
+
cur[pI] = islist(val) ? [] : {}
|
|
774
1018
|
}
|
|
775
1019
|
|
|
776
|
-
//
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
setprop(cur[cI], key, cur[cI + 1])
|
|
780
|
-
cur[cI + 1] = UNDEF
|
|
1020
|
+
// Matching override and destination so continue with their values.
|
|
1021
|
+
else if (typify(val) === typify(tval)) {
|
|
1022
|
+
cur[pI] = tval
|
|
781
1023
|
}
|
|
782
1024
|
|
|
783
|
-
//
|
|
1025
|
+
// Override wins.
|
|
784
1026
|
else {
|
|
785
|
-
|
|
786
|
-
}
|
|
1027
|
+
cur[pI] = val
|
|
787
1028
|
|
|
788
|
-
|
|
1029
|
+
// No need to descend when override wins (destination is discarded).
|
|
1030
|
+
val = NONE
|
|
1031
|
+
}
|
|
789
1032
|
}
|
|
790
1033
|
|
|
791
|
-
//
|
|
792
|
-
|
|
1034
|
+
// console.log('BEFORE-END', pathify(path), '@', pI, key,
|
|
1035
|
+
// stringify(val, -1, 1), stringify(parent, -1, 1),
|
|
1036
|
+
// 'CUR=', stringify(cur, -1, 1), 'DST=', stringify(dst, -1, 1))
|
|
1037
|
+
|
|
1038
|
+
return val
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function after(
|
|
1042
|
+
key: string | number | undefined,
|
|
1043
|
+
_val: any,
|
|
1044
|
+
_parent: any,
|
|
1045
|
+
path: string[]
|
|
1046
|
+
) {
|
|
1047
|
+
const cI = size(path)
|
|
1048
|
+
const target = cur[cI - 1]
|
|
1049
|
+
const value = cur[cI]
|
|
1050
|
+
|
|
1051
|
+
// console.log('AFTER-PREP', pathify(path), '@', cI, cur, '|',
|
|
1052
|
+
// stringify(key, -1, 1), stringify(value, -1, 1), 'T=', stringify(target, -1, 1))
|
|
1053
|
+
|
|
1054
|
+
setprop(target, key, value)
|
|
1055
|
+
return value
|
|
793
1056
|
}
|
|
1057
|
+
|
|
1058
|
+
// Walk overriding node, creating paths in output as needed.
|
|
1059
|
+
out = walk(obj, before, after, maxdepth)
|
|
1060
|
+
// console.log('WALK-DONE', out, obj)
|
|
794
1061
|
}
|
|
795
1062
|
}
|
|
796
1063
|
|
|
1064
|
+
if (0 === md) {
|
|
1065
|
+
out = getelem(list, -1)
|
|
1066
|
+
out = islist(out) ? [] : ismap(out) ? {} : out
|
|
1067
|
+
}
|
|
1068
|
+
|
|
797
1069
|
return out
|
|
798
1070
|
}
|
|
799
1071
|
|
|
800
1072
|
|
|
1073
|
+
// Set a value using a path. Missing path parts are created.
|
|
1074
|
+
// String paths create only maps. Use a string list to create list parts.
|
|
1075
|
+
function setpath(
|
|
1076
|
+
store: any,
|
|
1077
|
+
path: number | string | string[],
|
|
1078
|
+
val: any,
|
|
1079
|
+
injdef?: Partial<Injection>
|
|
1080
|
+
) {
|
|
1081
|
+
const pathType = typify(path)
|
|
1082
|
+
|
|
1083
|
+
const parts = 0 < (T_list & pathType) ? path :
|
|
1084
|
+
0 < (T_string & pathType) ? (path as string).split(S_DT) :
|
|
1085
|
+
0 < (T_number & pathType) ? [path] : NONE
|
|
1086
|
+
|
|
1087
|
+
if (NONE === parts) {
|
|
1088
|
+
return NONE
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
const base = getprop(injdef, S_base)
|
|
1092
|
+
const numparts = size(parts)
|
|
1093
|
+
let parent = getprop(store, base, store)
|
|
1094
|
+
|
|
1095
|
+
for (let pI = 0; pI < numparts - 1; pI++) {
|
|
1096
|
+
const partKey = getelem(parts, pI)
|
|
1097
|
+
let nextParent = getprop(parent, partKey)
|
|
1098
|
+
if (!isnode(nextParent)) {
|
|
1099
|
+
nextParent = 0 < (T_number & typify(getelem(parts, pI + 1))) ? [] : {}
|
|
1100
|
+
setprop(parent, partKey, nextParent)
|
|
1101
|
+
}
|
|
1102
|
+
parent = nextParent
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
if (DELETE === val) {
|
|
1106
|
+
delprop(parent, getelem(parts, -1))
|
|
1107
|
+
}
|
|
1108
|
+
else {
|
|
1109
|
+
setprop(parent, getelem(parts, -1), val)
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
return parent
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
|
|
801
1116
|
function getpath(store: any, path: number | string | string[], injdef?: Partial<Injection>) {
|
|
802
1117
|
|
|
803
1118
|
// Operate on a string array.
|
|
804
1119
|
const parts = islist(path) ? path :
|
|
805
1120
|
'string' === typeof path ? path.split(S_DT) :
|
|
806
|
-
'number' === typeof path ? [strkey(path)] :
|
|
1121
|
+
'number' === typeof path ? [strkey(path)] : NONE
|
|
807
1122
|
|
|
808
|
-
if (
|
|
809
|
-
return
|
|
1123
|
+
if (NONE === parts) {
|
|
1124
|
+
return NONE
|
|
810
1125
|
}
|
|
811
1126
|
|
|
812
1127
|
// let root = store
|
|
@@ -838,7 +1153,7 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
838
1153
|
|
|
839
1154
|
const dpath = getprop(injdef, 'dpath')
|
|
840
1155
|
|
|
841
|
-
for (let pI = 0;
|
|
1156
|
+
for (let pI = 0; NONE !== val && pI < numparts; pI++) {
|
|
842
1157
|
let part = parts[pI]
|
|
843
1158
|
|
|
844
1159
|
if (injdef && S_DKEY === part) {
|
|
@@ -846,15 +1161,15 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
846
1161
|
}
|
|
847
1162
|
else if (injdef && part.startsWith('$GET:')) {
|
|
848
1163
|
// $GET:path$ -> get store value, use as path part (string)
|
|
849
|
-
part = stringify(getpath(src, part
|
|
1164
|
+
part = stringify(getpath(src, slice(part, 5, -1)))
|
|
850
1165
|
}
|
|
851
1166
|
else if (injdef && part.startsWith('$REF:')) {
|
|
852
1167
|
// $REF:refpath$ -> get spec value, use as path part (string)
|
|
853
|
-
part = stringify(getpath(getprop(store, S_DSPEC), part
|
|
1168
|
+
part = stringify(getpath(getprop(store, S_DSPEC), slice(part, 5, -1)))
|
|
854
1169
|
}
|
|
855
1170
|
else if (injdef && part.startsWith('$META:')) {
|
|
856
1171
|
// $META:metapath$ -> get meta value, use as path part (string)
|
|
857
|
-
part = stringify(getpath(getprop(injdef, 'meta'), part
|
|
1172
|
+
part = stringify(getpath(getprop(injdef, 'meta'), slice(part, 6, -1)))
|
|
858
1173
|
}
|
|
859
1174
|
|
|
860
1175
|
// $$ escapes $
|
|
@@ -877,14 +1192,16 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
877
1192
|
val = dparent
|
|
878
1193
|
}
|
|
879
1194
|
else {
|
|
880
|
-
const fullpath = slice(dpath, 0 - ascends).concat(parts.slice(pI + 1))
|
|
1195
|
+
// const fullpath = slice(dpath, 0 - ascends).concat(parts.slice(pI + 1))
|
|
1196
|
+
const fullpath = flatten([slice(dpath, 0 - ascends), parts.slice(pI + 1)])
|
|
881
1197
|
|
|
882
1198
|
if (ascends <= size(dpath)) {
|
|
883
1199
|
val = getpath(store, fullpath)
|
|
884
1200
|
}
|
|
885
1201
|
else {
|
|
886
|
-
val =
|
|
1202
|
+
val = NONE
|
|
887
1203
|
}
|
|
1204
|
+
|
|
888
1205
|
break
|
|
889
1206
|
}
|
|
890
1207
|
}
|
|
@@ -906,13 +1223,14 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
906
1223
|
val = handler(injdef, val, ref, store)
|
|
907
1224
|
}
|
|
908
1225
|
|
|
1226
|
+
// console.log('GETPATH', path, val)
|
|
1227
|
+
|
|
909
1228
|
return val
|
|
910
1229
|
}
|
|
911
1230
|
|
|
912
1231
|
|
|
913
|
-
|
|
914
1232
|
// Inject values from a data store into a node recursively, resolving
|
|
915
|
-
// paths against the store, or current if they are local.
|
|
1233
|
+
// paths against the store, or current if they are local. The modify
|
|
916
1234
|
// argument allows custom modification of the result. The inj
|
|
917
1235
|
// (Injection) argument is used to maintain recursive state.
|
|
918
1236
|
function inject(
|
|
@@ -925,14 +1243,14 @@ function inject(
|
|
|
925
1243
|
|
|
926
1244
|
// Create state if at root of injection. The input value is placed
|
|
927
1245
|
// inside a virtual parent holder to simplify edge cases.
|
|
928
|
-
if (
|
|
1246
|
+
if (NONE === injdef || null == injdef.mode) {
|
|
929
1247
|
// Set up state assuming we are starting in the virtual parent.
|
|
930
1248
|
inj = new Injection(val, { [S_DTOP]: val })
|
|
931
1249
|
inj.dparent = store
|
|
932
1250
|
inj.errs = getprop(store, S_DERRS, [])
|
|
933
1251
|
inj.meta.__d = 0
|
|
934
1252
|
|
|
935
|
-
if (
|
|
1253
|
+
if (NONE !== injdef) {
|
|
936
1254
|
inj.modify = null == injdef.modify ? inj.modify : injdef.modify
|
|
937
1255
|
inj.extra = null == injdef.extra ? inj.extra : injdef.extra
|
|
938
1256
|
inj.meta = null == injdef.meta ? inj.meta : injdef.meta
|
|
@@ -942,6 +1260,9 @@ function inject(
|
|
|
942
1260
|
|
|
943
1261
|
inj.descend()
|
|
944
1262
|
|
|
1263
|
+
// console.log('INJ-START', val, inj.mode, inj.key, inj.val,
|
|
1264
|
+
// 't=', inj.path, 'P=', inj.parent, 'dp=', inj.dparent, 'ST=', store.$TOP)
|
|
1265
|
+
|
|
945
1266
|
// Descend into node.
|
|
946
1267
|
if (isnode(val)) {
|
|
947
1268
|
|
|
@@ -949,21 +1270,29 @@ function inject(
|
|
|
949
1270
|
// Injection transforms ($FOO) are processed *after* other keys.
|
|
950
1271
|
// NOTE: the optional digits suffix of the transform can thus be
|
|
951
1272
|
// used to order the transforms.
|
|
952
|
-
let nodekeys = ismap(val) ? [
|
|
953
|
-
...Object.keys(val).filter(k => !k.includes(S_DS)).sort(),
|
|
954
|
-
...Object.keys(val).filter(k => k.includes(S_DS)).sort(),
|
|
955
|
-
] : (val as any).map((_n: any, i: number) => i)
|
|
956
1273
|
|
|
1274
|
+
let nodekeys: any[]
|
|
1275
|
+
nodekeys = keysof(val)
|
|
1276
|
+
|
|
1277
|
+
if (ismap(val)) {
|
|
1278
|
+
nodekeys = flatten([
|
|
1279
|
+
filter(nodekeys, (n => !n[1].includes(S_DS))),
|
|
1280
|
+
filter(nodekeys, (n => n[1].includes(S_DS))),
|
|
1281
|
+
])
|
|
1282
|
+
}
|
|
1283
|
+
else {
|
|
1284
|
+
nodekeys = keysof(val)
|
|
1285
|
+
}
|
|
957
1286
|
|
|
958
1287
|
// Each child key-value pair is processed in three injection phases:
|
|
959
|
-
// 1. inj.mode=
|
|
960
|
-
// 2. inj.mode=
|
|
961
|
-
// 3. inj.mode=
|
|
1288
|
+
// 1. inj.mode=M_KEYPRE - Key string is injected, returning a possibly altered key.
|
|
1289
|
+
// 2. inj.mode=M_VAL - The child value is injected.
|
|
1290
|
+
// 3. inj.mode=M_KEYPOST - Key string is injected again, allowing child mutation.
|
|
962
1291
|
for (let nkI = 0; nkI < nodekeys.length; nkI++) {
|
|
963
1292
|
|
|
964
1293
|
const childinj = inj.child(nkI, nodekeys)
|
|
965
1294
|
const nodekey = childinj.key
|
|
966
|
-
childinj.mode =
|
|
1295
|
+
childinj.mode = M_KEYPRE
|
|
967
1296
|
|
|
968
1297
|
// Peform the key:pre mode injection on the child key.
|
|
969
1298
|
const prekey = _injectstr(nodekey, store, childinj)
|
|
@@ -973,9 +1302,9 @@ function inject(
|
|
|
973
1302
|
nodekeys = childinj.keys
|
|
974
1303
|
|
|
975
1304
|
// Prevent further processing by returning an undefined prekey
|
|
976
|
-
if (
|
|
1305
|
+
if (NONE !== prekey) {
|
|
977
1306
|
childinj.val = getprop(val, prekey)
|
|
978
|
-
childinj.mode =
|
|
1307
|
+
childinj.mode = M_VAL
|
|
979
1308
|
|
|
980
1309
|
// Perform the val mode injection on the child value.
|
|
981
1310
|
// NOTE: return value is not used.
|
|
@@ -986,7 +1315,7 @@ function inject(
|
|
|
986
1315
|
nodekeys = childinj.keys
|
|
987
1316
|
|
|
988
1317
|
// Peform the key:post mode injection on the child key.
|
|
989
|
-
childinj.mode =
|
|
1318
|
+
childinj.mode = M_KEYPOST
|
|
990
1319
|
_injectstr(nodekey, store, childinj)
|
|
991
1320
|
|
|
992
1321
|
// The injection may modify child processing.
|
|
@@ -998,7 +1327,7 @@ function inject(
|
|
|
998
1327
|
|
|
999
1328
|
// Inject paths into string scalars.
|
|
1000
1329
|
else if (S_string === valtype) {
|
|
1001
|
-
inj.mode =
|
|
1330
|
+
inj.mode = M_VAL
|
|
1002
1331
|
val = _injectstr(val, store, inj)
|
|
1003
1332
|
if (SKIP !== val) {
|
|
1004
1333
|
inj.setval(val)
|
|
@@ -1020,6 +1349,8 @@ function inject(
|
|
|
1020
1349
|
)
|
|
1021
1350
|
}
|
|
1022
1351
|
|
|
1352
|
+
// console.log('INJ-VAL', val)
|
|
1353
|
+
|
|
1023
1354
|
inj.val = val
|
|
1024
1355
|
|
|
1025
1356
|
// Original val reference may no longer be correct.
|
|
@@ -1032,21 +1363,22 @@ function inject(
|
|
|
1032
1363
|
|
|
1033
1364
|
// Delete a key from a map or list.
|
|
1034
1365
|
const transform_DELETE: Injector = (inj: Injection) => {
|
|
1035
|
-
inj.setval(
|
|
1036
|
-
return
|
|
1366
|
+
inj.setval(NONE)
|
|
1367
|
+
return NONE
|
|
1037
1368
|
}
|
|
1038
1369
|
|
|
1039
1370
|
|
|
1040
1371
|
// Copy value from source data.
|
|
1041
1372
|
const transform_COPY: Injector = (inj: Injection, _val: any) => {
|
|
1042
|
-
const
|
|
1373
|
+
const ijname = 'COPY'
|
|
1043
1374
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
out = getprop(inj.dparent, key)
|
|
1047
|
-
inj.setval(out)
|
|
1375
|
+
if (!checkPlacement(M_VAL, ijname, T_any, inj)) {
|
|
1376
|
+
return NONE
|
|
1048
1377
|
}
|
|
1049
1378
|
|
|
1379
|
+
let out = getprop(inj.dparent, inj.key)
|
|
1380
|
+
inj.setval(out)
|
|
1381
|
+
|
|
1050
1382
|
return out
|
|
1051
1383
|
}
|
|
1052
1384
|
|
|
@@ -1056,29 +1388,30 @@ const transform_COPY: Injector = (inj: Injection, _val: any) => {
|
|
|
1056
1388
|
const transform_KEY: Injector = (inj: Injection) => {
|
|
1057
1389
|
const { mode, path, parent } = inj
|
|
1058
1390
|
|
|
1059
|
-
// Do nothing in val mode.
|
|
1060
|
-
if (
|
|
1061
|
-
return
|
|
1391
|
+
// Do nothing in val mode - not an error.
|
|
1392
|
+
if (M_VAL !== mode) {
|
|
1393
|
+
return NONE
|
|
1062
1394
|
}
|
|
1063
1395
|
|
|
1064
1396
|
// Key is defined by $KEY meta property.
|
|
1065
1397
|
const keyspec = getprop(parent, S_BKEY)
|
|
1066
|
-
if (
|
|
1398
|
+
if (NONE !== keyspec) {
|
|
1067
1399
|
delprop(parent, S_BKEY)
|
|
1068
1400
|
return getprop(inj.dparent, keyspec)
|
|
1069
1401
|
}
|
|
1070
1402
|
|
|
1071
1403
|
// Key is defined within general purpose $META object.
|
|
1072
|
-
return getprop(getprop(parent, S_BANNO), S_KEY, getprop(path, path.length - 2))
|
|
1404
|
+
// return getprop(getprop(parent, S_BANNO), S_KEY, getprop(path, path.length - 2))
|
|
1405
|
+
return getprop(getprop(parent, S_BANNO), S_KEY, getelem(path, -2))
|
|
1073
1406
|
}
|
|
1074
1407
|
|
|
1075
1408
|
|
|
1076
|
-
//
|
|
1409
|
+
// Annotate node. Does nothing itself, just used by
|
|
1077
1410
|
// other injectors, and is removed when called.
|
|
1078
1411
|
const transform_ANNO: Injector = (inj: Injection) => {
|
|
1079
1412
|
const { parent } = inj
|
|
1080
1413
|
delprop(parent, S_BANNO)
|
|
1081
|
-
return
|
|
1414
|
+
return NONE
|
|
1082
1415
|
}
|
|
1083
1416
|
|
|
1084
1417
|
|
|
@@ -1091,29 +1424,27 @@ const transform_MERGE: Injector = (inj: Injection) => {
|
|
|
1091
1424
|
const { mode, key, parent } = inj
|
|
1092
1425
|
|
|
1093
1426
|
// Ensures $MERGE is removed from parent list (val mode).
|
|
1094
|
-
let out: any =
|
|
1427
|
+
let out: any = NONE
|
|
1095
1428
|
|
|
1096
|
-
if (
|
|
1429
|
+
if (M_KEYPRE === mode) {
|
|
1097
1430
|
out = key
|
|
1098
1431
|
}
|
|
1099
1432
|
|
|
1100
1433
|
// Operate after child values have been transformed.
|
|
1101
|
-
else if (
|
|
1434
|
+
else if (M_KEYPOST === mode) {
|
|
1102
1435
|
out = key
|
|
1103
1436
|
|
|
1104
1437
|
let args = getprop(parent, key)
|
|
1105
1438
|
args = Array.isArray(args) ? args : [args]
|
|
1106
1439
|
|
|
1107
1440
|
// Remove the $MERGE command from a parent map.
|
|
1108
|
-
inj.setval(
|
|
1441
|
+
inj.setval(NONE)
|
|
1109
1442
|
|
|
1110
1443
|
// Literals in the parent have precedence, but we still merge onto
|
|
1111
1444
|
// the parent object, so that node tree references are not changed.
|
|
1112
|
-
const mergelist = [parent,
|
|
1445
|
+
const mergelist = flatten([[parent], args, [clone(parent)]])
|
|
1113
1446
|
|
|
1114
1447
|
merge(mergelist)
|
|
1115
|
-
|
|
1116
|
-
// return key
|
|
1117
1448
|
}
|
|
1118
1449
|
|
|
1119
1450
|
return out
|
|
@@ -1128,65 +1459,63 @@ const transform_EACH: Injector = (
|
|
|
1128
1459
|
_ref: string,
|
|
1129
1460
|
store: any
|
|
1130
1461
|
) => {
|
|
1462
|
+
const ijname = 'EACH'
|
|
1131
1463
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
inj.keys.length = 1
|
|
1464
|
+
if (!checkPlacement(M_VAL, ijname, T_list, inj)) {
|
|
1465
|
+
return NONE
|
|
1135
1466
|
}
|
|
1136
1467
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
1468
|
+
// Remove remaining keys to avoid spurious processing.
|
|
1469
|
+
slice(inj.keys, 0, 1, true)
|
|
1140
1470
|
|
|
1141
|
-
//
|
|
1142
|
-
const srcpath =
|
|
1143
|
-
|
|
1471
|
+
// const [err, srcpath, child] = injectorArgs([T_string, T_any], inj)
|
|
1472
|
+
const [err, srcpath, child] = injectorArgs([T_string, T_any], slice(inj.parent, 1))
|
|
1473
|
+
if (NONE !== err) {
|
|
1474
|
+
inj.errs.push('$' + ijname + ': ' + err)
|
|
1475
|
+
return NONE
|
|
1476
|
+
}
|
|
1144
1477
|
|
|
1145
1478
|
// Source data.
|
|
1146
1479
|
const srcstore = getprop(store, inj.base, store)
|
|
1147
1480
|
|
|
1148
1481
|
const src = getpath(srcstore, srcpath, inj)
|
|
1482
|
+
const srctype = typify(src)
|
|
1149
1483
|
|
|
1150
1484
|
// Create parallel data structures:
|
|
1151
1485
|
// source entries :: child templates
|
|
1152
1486
|
let tcur: any = []
|
|
1153
1487
|
let tval: any = []
|
|
1154
1488
|
|
|
1155
|
-
const tkey = inj.path
|
|
1156
|
-
const target = inj.nodes
|
|
1489
|
+
const tkey = getelem(inj.path, -2)
|
|
1490
|
+
const target = getelem(inj.nodes, - 2, () => getelem(inj.nodes, -1))
|
|
1157
1491
|
|
|
1158
1492
|
// Create clones of the child template for each value of the current soruce.
|
|
1159
|
-
if (
|
|
1160
|
-
tval = src
|
|
1493
|
+
if (0 < (T_list & srctype)) {
|
|
1494
|
+
tval = items(src, () => clone(child))
|
|
1161
1495
|
}
|
|
1162
|
-
else if (
|
|
1163
|
-
tval =
|
|
1164
|
-
|
|
1165
|
-
|
|
1496
|
+
else if (0 < (T_map & srctype)) {
|
|
1497
|
+
tval = items(src, (n => merge([
|
|
1498
|
+
clone(child),
|
|
1166
1499
|
// Make a note of the key for $KEY transforms.
|
|
1167
|
-
[S_BANNO]: { KEY: n[0] }
|
|
1168
|
-
|
|
1500
|
+
{ [S_BANNO]: { KEY: n[0] } }
|
|
1501
|
+
], 1)))
|
|
1169
1502
|
}
|
|
1170
1503
|
|
|
1171
1504
|
let rval = []
|
|
1172
1505
|
|
|
1173
1506
|
if (0 < size(tval)) {
|
|
1174
|
-
tcur = null == src ?
|
|
1507
|
+
tcur = null == src ? NONE : Object.values(src)
|
|
1175
1508
|
|
|
1176
1509
|
const ckey = getelem(inj.path, -2)
|
|
1177
1510
|
|
|
1178
1511
|
const tpath = slice(inj.path, -1)
|
|
1179
|
-
const dpath = [S_DTOP,
|
|
1180
|
-
|
|
1512
|
+
const dpath = flatten([S_DTOP, srcpath.split(S_DT), '$:' + ckey])
|
|
1181
1513
|
|
|
1182
1514
|
// Parent structure.
|
|
1183
|
-
|
|
1184
|
-
// const ckey = getelem(cpath, -1)
|
|
1185
1515
|
tcur = { [ckey]: tcur }
|
|
1186
1516
|
|
|
1187
|
-
if (1 < tpath
|
|
1517
|
+
if (1 < size(tpath)) {
|
|
1188
1518
|
const pkey = getelem(inj.path, -3, S_DTOP)
|
|
1189
|
-
// const pkey = getelem(cpath, -2, S_DTOP)
|
|
1190
1519
|
tcur = { [pkey]: tcur }
|
|
1191
1520
|
dpath.push('$:' + pkey)
|
|
1192
1521
|
}
|
|
@@ -1206,7 +1535,8 @@ const transform_EACH: Injector = (
|
|
|
1206
1535
|
rval = tinj.val
|
|
1207
1536
|
}
|
|
1208
1537
|
|
|
1209
|
-
_updateAncestors(inj, target, tkey, rval)
|
|
1538
|
+
// _updateAncestors(inj, target, tkey, rval)
|
|
1539
|
+
setprop(target, tkey, rval)
|
|
1210
1540
|
|
|
1211
1541
|
// Prevent callee from damaging first list entry (since we are in `val` mode).
|
|
1212
1542
|
return rval[0]
|
|
@@ -1214,7 +1544,7 @@ const transform_EACH: Injector = (
|
|
|
1214
1544
|
|
|
1215
1545
|
|
|
1216
1546
|
// Convert a node to a map.
|
|
1217
|
-
// Format: { '`$PACK`':['
|
|
1547
|
+
// Format: { '`$PACK`':['source-path', child-template]}
|
|
1218
1548
|
const transform_PACK: Injector = (
|
|
1219
1549
|
inj: Injection,
|
|
1220
1550
|
_val: any,
|
|
@@ -1223,78 +1553,105 @@ const transform_PACK: Injector = (
|
|
|
1223
1553
|
) => {
|
|
1224
1554
|
const { mode, key, path, parent, nodes } = inj
|
|
1225
1555
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1556
|
+
const ijname = 'EACH'
|
|
1557
|
+
|
|
1558
|
+
if (!checkPlacement(M_KEYPRE, ijname, T_map, inj)) {
|
|
1559
|
+
return NONE
|
|
1229
1560
|
}
|
|
1230
1561
|
|
|
1231
1562
|
// Get arguments.
|
|
1232
|
-
const args = parent
|
|
1233
|
-
const srcpath =
|
|
1234
|
-
|
|
1563
|
+
const args = getprop(parent, key)
|
|
1564
|
+
const [err, srcpath, origchildspec] = injectorArgs([T_string, T_any], args)
|
|
1565
|
+
if (NONE !== err) {
|
|
1566
|
+
inj.errs.push('$' + ijname + ': ' + err)
|
|
1567
|
+
return NONE
|
|
1568
|
+
}
|
|
1235
1569
|
|
|
1236
1570
|
// Find key and target node.
|
|
1237
|
-
const keyprop = child[S_BKEY]
|
|
1238
1571
|
const tkey = getelem(path, -2)
|
|
1239
|
-
const
|
|
1572
|
+
const pathsize = size(path)
|
|
1573
|
+
const target = getelem(nodes, pathsize - 2, () => getelem(nodes, pathsize - 1))
|
|
1240
1574
|
|
|
1241
1575
|
// Source data
|
|
1242
1576
|
const srcstore = getprop(store, inj.base, store)
|
|
1243
|
-
|
|
1244
1577
|
let src = getpath(srcstore, srcpath, inj)
|
|
1245
1578
|
|
|
1246
1579
|
// Prepare source as a list.
|
|
1247
|
-
|
|
1248
|
-
ismap(src)
|
|
1249
|
-
|
|
1250
|
-
(
|
|
1251
|
-
|
|
1580
|
+
if (!islist(src)) {
|
|
1581
|
+
if (ismap(src)) {
|
|
1582
|
+
src = items(src, (item: [string, any]) => {
|
|
1583
|
+
setprop(item[1], S_BANNO, { KEY: item[0] })
|
|
1584
|
+
return item[1]
|
|
1585
|
+
})
|
|
1586
|
+
}
|
|
1587
|
+
else {
|
|
1588
|
+
src = NONE
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1252
1591
|
|
|
1253
1592
|
if (null == src) {
|
|
1254
|
-
return
|
|
1593
|
+
return NONE
|
|
1255
1594
|
}
|
|
1256
1595
|
|
|
1257
|
-
// Get
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1596
|
+
// Get keypath.
|
|
1597
|
+
const keypath = getprop(origchildspec, S_BKEY)
|
|
1598
|
+
const childspec = delprop(origchildspec, S_BKEY)
|
|
1599
|
+
|
|
1600
|
+
const child = getprop(childspec, S_BVAL, childspec)
|
|
1261
1601
|
|
|
1262
1602
|
// Build parallel target object.
|
|
1263
1603
|
let tval: any = {}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1604
|
+
|
|
1605
|
+
items(src, (item: [string, any]) => {
|
|
1606
|
+
const srckey = item[0]
|
|
1607
|
+
const srcnode = item[1]
|
|
1608
|
+
|
|
1609
|
+
let key: string = srckey
|
|
1610
|
+
if (NONE !== keypath) {
|
|
1611
|
+
if (keypath.startsWith('`')) {
|
|
1612
|
+
key = inject(keypath, merge([{}, store, { $TOP: srcnode }], 1))
|
|
1613
|
+
}
|
|
1614
|
+
else {
|
|
1615
|
+
key = getpath(srcnode, keypath, inj)
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
const tchild = clone(child)
|
|
1620
|
+
setprop(tval, key, tchild)
|
|
1621
|
+
|
|
1622
|
+
const anno = getprop(srcnode, S_BANNO)
|
|
1623
|
+
if (NONE === anno) {
|
|
1624
|
+
delprop(tchild, S_BANNO)
|
|
1271
1625
|
}
|
|
1272
1626
|
else {
|
|
1273
|
-
setprop(
|
|
1627
|
+
setprop(tchild, S_BANNO, anno)
|
|
1274
1628
|
}
|
|
1275
|
-
|
|
1276
|
-
}, tval)
|
|
1629
|
+
})
|
|
1277
1630
|
|
|
1278
1631
|
let rval = {}
|
|
1279
1632
|
|
|
1280
|
-
if (
|
|
1633
|
+
if (!isempty(tval)) {
|
|
1281
1634
|
|
|
1282
1635
|
// Build parallel source object.
|
|
1283
|
-
let
|
|
1284
|
-
src.reduce((a: any, n: any) => {
|
|
1285
|
-
let kn =
|
|
1636
|
+
let tsrc: any = {}
|
|
1637
|
+
src.reduce((a: any, n: any, i: any) => {
|
|
1638
|
+
let kn = null == keypath ? i :
|
|
1639
|
+
keypath.startsWith('`') ?
|
|
1640
|
+
inject(keypath, merge([{}, store, { $TOP: n }], 1)) :
|
|
1641
|
+
getpath(n, keypath, inj)
|
|
1642
|
+
|
|
1286
1643
|
setprop(a, kn, n)
|
|
1287
1644
|
return a
|
|
1288
|
-
},
|
|
1645
|
+
}, tsrc)
|
|
1289
1646
|
|
|
1290
1647
|
const tpath = slice(inj.path, -1)
|
|
1291
1648
|
|
|
1292
1649
|
const ckey = getelem(inj.path, -2)
|
|
1293
|
-
const dpath = [S_DTOP,
|
|
1650
|
+
const dpath = flatten([S_DTOP, srcpath.split(S_DT), '$:' + ckey])
|
|
1294
1651
|
|
|
1295
|
-
tcur = { [ckey]:
|
|
1652
|
+
let tcur = { [ckey]: tsrc }
|
|
1296
1653
|
|
|
1297
|
-
if (1 < tpath
|
|
1654
|
+
if (1 < size(tpath)) {
|
|
1298
1655
|
const pkey = getelem(inj.path, -3, S_DTOP)
|
|
1299
1656
|
tcur = { [pkey]: tcur }
|
|
1300
1657
|
dpath.push('$:' + pkey)
|
|
@@ -1304,7 +1661,6 @@ const transform_PACK: Injector = (
|
|
|
1304
1661
|
tinj.path = tpath
|
|
1305
1662
|
tinj.nodes = slice(inj.nodes, -1)
|
|
1306
1663
|
|
|
1307
|
-
// tinj.parent = tcur
|
|
1308
1664
|
tinj.parent = getelem(tinj.nodes, -1)
|
|
1309
1665
|
tinj.val = tval
|
|
1310
1666
|
|
|
@@ -1315,14 +1671,15 @@ const transform_PACK: Injector = (
|
|
|
1315
1671
|
rval = tinj.val
|
|
1316
1672
|
}
|
|
1317
1673
|
|
|
1318
|
-
_updateAncestors(inj, target, tkey, rval)
|
|
1674
|
+
// _updateAncestors(inj, target, tkey, rval)
|
|
1675
|
+
setprop(target, tkey, rval)
|
|
1319
1676
|
|
|
1320
1677
|
// Drop transform key.
|
|
1321
|
-
return
|
|
1678
|
+
return NONE
|
|
1322
1679
|
}
|
|
1323
1680
|
|
|
1324
1681
|
|
|
1325
|
-
// TODO: not found ref should removed key (setprop
|
|
1682
|
+
// TODO: not found ref should removed key (setprop NONE)
|
|
1326
1683
|
// Reference original spec (enables recursice transformations)
|
|
1327
1684
|
// Format: ['`$REF`', '`spec-path`']
|
|
1328
1685
|
const transform_REF: Injector = (
|
|
@@ -1333,21 +1690,24 @@ const transform_REF: Injector = (
|
|
|
1333
1690
|
) => {
|
|
1334
1691
|
const { nodes } = inj
|
|
1335
1692
|
|
|
1336
|
-
if (
|
|
1337
|
-
return
|
|
1693
|
+
if (M_VAL !== inj.mode) {
|
|
1694
|
+
return NONE
|
|
1338
1695
|
}
|
|
1339
1696
|
|
|
1340
1697
|
// Get arguments: ['`$REF`', 'ref-path'].
|
|
1341
1698
|
const refpath = getprop(inj.parent, 1)
|
|
1342
|
-
inj.keyI = inj.keys
|
|
1699
|
+
inj.keyI = size(inj.keys)
|
|
1343
1700
|
|
|
1344
1701
|
// Spec reference.
|
|
1345
1702
|
const spec = getprop(store, S_DSPEC)()
|
|
1346
1703
|
|
|
1704
|
+
const dpath = slice(inj.path, 1)
|
|
1347
1705
|
const ref = getpath(spec, refpath, {
|
|
1348
1706
|
// TODO: test relative refs
|
|
1349
|
-
dpath: inj.path.slice(1),
|
|
1350
|
-
|
|
1707
|
+
// dpath: inj.path.slice(1),
|
|
1708
|
+
dpath,
|
|
1709
|
+
// dparent: getpath(spec, inj.path.slice(1))
|
|
1710
|
+
dparent: getpath(spec, dpath),
|
|
1351
1711
|
})
|
|
1352
1712
|
|
|
1353
1713
|
let hasSubRef = false
|
|
@@ -1366,9 +1726,9 @@ const transform_REF: Injector = (
|
|
|
1366
1726
|
const tpath = slice(inj.path, -1)
|
|
1367
1727
|
let tcur = getpath(store, cpath)
|
|
1368
1728
|
let tval = getpath(store, tpath)
|
|
1369
|
-
let rval =
|
|
1729
|
+
let rval = NONE
|
|
1370
1730
|
|
|
1371
|
-
if (!hasSubRef ||
|
|
1731
|
+
if (!hasSubRef || NONE !== tval) {
|
|
1372
1732
|
const tinj = inj.child(0, [getelem(tpath, -1)])
|
|
1373
1733
|
|
|
1374
1734
|
tinj.path = tpath
|
|
@@ -1376,7 +1736,7 @@ const transform_REF: Injector = (
|
|
|
1376
1736
|
tinj.parent = getelem(nodes, -2)
|
|
1377
1737
|
tinj.val = tref
|
|
1378
1738
|
|
|
1379
|
-
tinj.dpath = [
|
|
1739
|
+
tinj.dpath = flatten([cpath])
|
|
1380
1740
|
tinj.dparent = tcur
|
|
1381
1741
|
|
|
1382
1742
|
inject(tref, store, tinj)
|
|
@@ -1384,7 +1744,7 @@ const transform_REF: Injector = (
|
|
|
1384
1744
|
rval = tinj.val
|
|
1385
1745
|
}
|
|
1386
1746
|
else {
|
|
1387
|
-
rval =
|
|
1747
|
+
rval = NONE
|
|
1388
1748
|
}
|
|
1389
1749
|
|
|
1390
1750
|
const grandparent = inj.setval(rval, 2)
|
|
@@ -1397,6 +1757,117 @@ const transform_REF: Injector = (
|
|
|
1397
1757
|
}
|
|
1398
1758
|
|
|
1399
1759
|
|
|
1760
|
+
const transform_FORMAT: Injector = (
|
|
1761
|
+
inj: Injection,
|
|
1762
|
+
_val: any,
|
|
1763
|
+
_ref: string,
|
|
1764
|
+
store: any
|
|
1765
|
+
) => {
|
|
1766
|
+
// console.log('FORMAT-START', inj, _val)
|
|
1767
|
+
|
|
1768
|
+
// Remove remaining keys to avoid spurious processing.
|
|
1769
|
+
slice(inj.keys, 0, 1, true)
|
|
1770
|
+
|
|
1771
|
+
if (M_VAL !== inj.mode) {
|
|
1772
|
+
return NONE
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
// Get arguments: ['`$FORMAT`', 'name', child].
|
|
1776
|
+
// TODO: EACH and PACK should accept customm functions too
|
|
1777
|
+
const name = getprop(inj.parent, 1)
|
|
1778
|
+
const child = getprop(inj.parent, 2)
|
|
1779
|
+
|
|
1780
|
+
// Source data.
|
|
1781
|
+
const tkey = getelem(inj.path, -2)
|
|
1782
|
+
const target = getelem(inj.nodes, - 2, () => getelem(inj.nodes, -1))
|
|
1783
|
+
|
|
1784
|
+
const cinj = injectChild(child, store, inj)
|
|
1785
|
+
const resolved = cinj.val
|
|
1786
|
+
|
|
1787
|
+
let formatter = 0 < (T_function & typify(name)) ? name : getprop(FORMATTER, name)
|
|
1788
|
+
|
|
1789
|
+
if (NONE === formatter) {
|
|
1790
|
+
inj.errs.push('$FORMAT: unknown format: ' + name + '.')
|
|
1791
|
+
return NONE
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
let out = walk(resolved, formatter)
|
|
1795
|
+
|
|
1796
|
+
setprop(target, tkey, out)
|
|
1797
|
+
// _updateAncestors(inj, target, tkey, out)
|
|
1798
|
+
|
|
1799
|
+
return out
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
|
|
1803
|
+
const FORMATTER: Record<string, WalkApply> = {
|
|
1804
|
+
identity: (_k: any, v: any) => v,
|
|
1805
|
+
upper: (_k: any, v: any) => isnode(v) ? v : ('' + v).toUpperCase(),
|
|
1806
|
+
lower: (_k: any, v: any) => isnode(v) ? v : ('' + v).toLowerCase(),
|
|
1807
|
+
string: (_k: any, v: any) => isnode(v) ? v : ('' + v),
|
|
1808
|
+
number: (_k: any, v: any) => {
|
|
1809
|
+
if (isnode(v)) {
|
|
1810
|
+
return v
|
|
1811
|
+
}
|
|
1812
|
+
else {
|
|
1813
|
+
let n = Number(v)
|
|
1814
|
+
if (isNaN(n)) {
|
|
1815
|
+
n = 0
|
|
1816
|
+
}
|
|
1817
|
+
return n
|
|
1818
|
+
}
|
|
1819
|
+
},
|
|
1820
|
+
integer: (_k: any, v: any) => {
|
|
1821
|
+
if (isnode(v)) {
|
|
1822
|
+
return v
|
|
1823
|
+
}
|
|
1824
|
+
else {
|
|
1825
|
+
let n = Number(v)
|
|
1826
|
+
if (isNaN(n)) {
|
|
1827
|
+
n = 0
|
|
1828
|
+
}
|
|
1829
|
+
return n | 0
|
|
1830
|
+
}
|
|
1831
|
+
},
|
|
1832
|
+
concat: (k: any, v: any) =>
|
|
1833
|
+
null == k && islist(v) ? join(items(v, (n => isnode(n[1]) ? S_MT : (S_MT + n[1]))), S_MT) : v
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
|
|
1837
|
+
|
|
1838
|
+
const transform_APPLY: Injector = (
|
|
1839
|
+
inj: Injection,
|
|
1840
|
+
_val: any,
|
|
1841
|
+
_ref: string,
|
|
1842
|
+
store: any
|
|
1843
|
+
) => {
|
|
1844
|
+
const ijname = 'APPLY'
|
|
1845
|
+
|
|
1846
|
+
if (!checkPlacement(M_VAL, ijname, T_list, inj)) {
|
|
1847
|
+
return NONE
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
// const [err, apply, child] = injectorArgs([T_function, T_any], inj)
|
|
1851
|
+
const [err, apply, child] = injectorArgs([T_function, T_any], slice(inj.parent, 1))
|
|
1852
|
+
if (NONE !== err) {
|
|
1853
|
+
inj.errs.push('$' + ijname + ': ' + err)
|
|
1854
|
+
return NONE
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
const tkey = getelem(inj.path, -2)
|
|
1858
|
+
const target = getelem(inj.nodes, - 2, () => getelem(inj.nodes, -1))
|
|
1859
|
+
|
|
1860
|
+
const cinj = injectChild(child, store, inj)
|
|
1861
|
+
const resolved = cinj.val
|
|
1862
|
+
|
|
1863
|
+
const out = apply(resolved, store, cinj)
|
|
1864
|
+
|
|
1865
|
+
setprop(target, tkey, out)
|
|
1866
|
+
|
|
1867
|
+
return out
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
|
|
1400
1871
|
// Transform data using spec.
|
|
1401
1872
|
// Only operates on static JSON-like data.
|
|
1402
1873
|
// Arrays are treated as if they are objects with indices as keys.
|
|
@@ -1410,51 +1881,66 @@ function transform(
|
|
|
1410
1881
|
spec = clone(origspec)
|
|
1411
1882
|
|
|
1412
1883
|
const extra = injdef?.extra
|
|
1413
|
-
|
|
1884
|
+
|
|
1885
|
+
const collect = null != injdef?.errs
|
|
1886
|
+
const errs = injdef?.errs || []
|
|
1414
1887
|
|
|
1415
1888
|
const extraTransforms: any = {}
|
|
1416
|
-
const extraData = null == extra ?
|
|
1889
|
+
const extraData = null == extra ? NONE : items(extra)
|
|
1417
1890
|
.reduce((a: any, n: any[]) =>
|
|
1418
1891
|
(n[0].startsWith(S_DS) ? extraTransforms[n[0]] = n[1] : (a[n[0]] = n[1]), a), {})
|
|
1419
1892
|
|
|
1420
1893
|
const dataClone = merge([
|
|
1421
|
-
isempty(extraData) ?
|
|
1894
|
+
isempty(extraData) ? NONE : clone(extraData),
|
|
1422
1895
|
clone(data),
|
|
1423
1896
|
])
|
|
1424
1897
|
|
|
1425
1898
|
// Define a top level store that provides transform operations.
|
|
1426
|
-
const store =
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1899
|
+
const store = merge([
|
|
1900
|
+
{
|
|
1901
|
+
// The inject function recognises this special location for the root of the source data.
|
|
1902
|
+
// NOTE: to escape data that contains "`$FOO`" keys at the top level,
|
|
1903
|
+
// place that data inside a holding map: { myholder: mydata }.
|
|
1904
|
+
$TOP: dataClone,
|
|
1905
|
+
|
|
1906
|
+
$SPEC: () => origspec,
|
|
1907
|
+
|
|
1908
|
+
// Escape backtick (this also works inside backticks).
|
|
1909
|
+
$BT: () => S_BT,
|
|
1910
|
+
|
|
1911
|
+
// Escape dollar sign (this also works inside backticks).
|
|
1912
|
+
$DS: () => S_DS,
|
|
1913
|
+
|
|
1914
|
+
// Insert current date and time as an ISO string.
|
|
1915
|
+
$WHEN: () => new Date().toISOString(),
|
|
1916
|
+
|
|
1917
|
+
$DELETE: transform_DELETE,
|
|
1918
|
+
$COPY: transform_COPY,
|
|
1919
|
+
$KEY: transform_KEY,
|
|
1920
|
+
$ANNO: transform_ANNO,
|
|
1921
|
+
$MERGE: transform_MERGE,
|
|
1922
|
+
$EACH: transform_EACH,
|
|
1923
|
+
$PACK: transform_PACK,
|
|
1924
|
+
$REF: transform_REF,
|
|
1925
|
+
$FORMAT: transform_FORMAT,
|
|
1926
|
+
$APPLY: transform_APPLY,
|
|
1927
|
+
},
|
|
1434
1928
|
|
|
1435
|
-
//
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
// Escape dollar sign (this also works inside backticks).
|
|
1439
|
-
$DS: () => S_DS,
|
|
1929
|
+
// Custom extra transforms, if any.
|
|
1930
|
+
extraTransforms,
|
|
1440
1931
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1932
|
+
{
|
|
1933
|
+
$ERRS: errs,
|
|
1934
|
+
}
|
|
1935
|
+
], 1)
|
|
1443
1936
|
|
|
1444
|
-
|
|
1445
|
-
$COPY: transform_COPY,
|
|
1446
|
-
$KEY: transform_KEY,
|
|
1447
|
-
$ANNO: transform_ANNO,
|
|
1448
|
-
$MERGE: transform_MERGE,
|
|
1449
|
-
$EACH: transform_EACH,
|
|
1450
|
-
$PACK: transform_PACK,
|
|
1451
|
-
$REF: transform_REF,
|
|
1937
|
+
const out = inject(spec, store, injdef)
|
|
1452
1938
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1939
|
+
const generr = (0 < size(errs) && !collect)
|
|
1940
|
+
if (generr) {
|
|
1941
|
+
throw new Error(join(errs, ' | '))
|
|
1455
1942
|
}
|
|
1456
1943
|
|
|
1457
|
-
const out = inject(spec, store, injdef)
|
|
1458
1944
|
return out
|
|
1459
1945
|
}
|
|
1460
1946
|
|
|
@@ -1464,86 +1950,36 @@ const validate_STRING: Injector = (inj: Injection) => {
|
|
|
1464
1950
|
let out = getprop(inj.dparent, inj.key)
|
|
1465
1951
|
|
|
1466
1952
|
const t = typify(out)
|
|
1467
|
-
if (
|
|
1953
|
+
if (0 === (T_string & t)) {
|
|
1468
1954
|
let msg = _invalidTypeMsg(inj.path, S_string, t, out, 'V1010')
|
|
1469
1955
|
inj.errs.push(msg)
|
|
1470
|
-
return
|
|
1956
|
+
return NONE
|
|
1471
1957
|
}
|
|
1472
1958
|
|
|
1473
1959
|
if (S_MT === out) {
|
|
1474
1960
|
let msg = 'Empty string at ' + pathify(inj.path, 1)
|
|
1475
1961
|
inj.errs.push(msg)
|
|
1476
|
-
return
|
|
1962
|
+
return NONE
|
|
1477
1963
|
}
|
|
1478
1964
|
|
|
1479
1965
|
return out
|
|
1480
1966
|
}
|
|
1481
1967
|
|
|
1482
1968
|
|
|
1483
|
-
// A required number value (int or float).
|
|
1484
|
-
const validate_NUMBER: Injector = (inj: Injection) => {
|
|
1485
|
-
let out = getprop(inj.dparent, inj.key)
|
|
1486
1969
|
|
|
1487
|
-
const t = typify(out)
|
|
1488
|
-
if (S_number !== t) {
|
|
1489
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_number, t, out, 'V1020'))
|
|
1490
|
-
return UNDEF
|
|
1491
|
-
}
|
|
1492
1970
|
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
// A required boolean value.
|
|
1498
|
-
const validate_BOOLEAN: Injector = (inj: Injection) => {
|
|
1971
|
+
const validate_TYPE: Injector = (inj: Injection, _val: any, ref: string) => {
|
|
1972
|
+
const tname = slice(ref, 1).toLowerCase()
|
|
1973
|
+
const typev = 1 << (31 - TYPENAME.indexOf(tname))
|
|
1499
1974
|
let out = getprop(inj.dparent, inj.key)
|
|
1500
1975
|
|
|
1501
1976
|
const t = typify(out)
|
|
1502
|
-
if (S_boolean !== t) {
|
|
1503
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_boolean, t, out, 'V1030'))
|
|
1504
|
-
return UNDEF
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
return out
|
|
1508
|
-
}
|
|
1509
1977
|
|
|
1978
|
+
// console.log('TYPE', tname, typev, tn(typev), 'O=', t, tn(t), out, 'C=', t & typev)
|
|
1510
1979
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
const t = typify(out)
|
|
1516
|
-
if (t !== S_object) {
|
|
1517
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_object, t, out, 'V1040'))
|
|
1518
|
-
return UNDEF
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
return out
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
// A required array (list) value (contents not validated).
|
|
1526
|
-
const validate_ARRAY: Injector = (inj: Injection) => {
|
|
1527
|
-
let out = getprop(inj.dparent, inj.key)
|
|
1528
|
-
|
|
1529
|
-
const t = typify(out)
|
|
1530
|
-
if (t !== S_array) {
|
|
1531
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_array, t, out, 'V1050'))
|
|
1532
|
-
return UNDEF
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
return out
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
// A required function value.
|
|
1540
|
-
const validate_FUNCTION: Injector = (inj: Injection) => {
|
|
1541
|
-
let out = getprop(inj.dparent, inj.key)
|
|
1542
|
-
|
|
1543
|
-
const t = typify(out)
|
|
1544
|
-
if (S_function !== t) {
|
|
1545
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_function, t, out, 'V1060'))
|
|
1546
|
-
return UNDEF
|
|
1980
|
+
if (0 === (t & typev)) {
|
|
1981
|
+
inj.errs.push(_invalidTypeMsg(inj.path, tname, t, out, 'V1001'))
|
|
1982
|
+
return NONE
|
|
1547
1983
|
}
|
|
1548
1984
|
|
|
1549
1985
|
return out
|
|
@@ -1567,20 +2003,20 @@ const validate_CHILD: Injector = (inj: Injection) => {
|
|
|
1567
2003
|
// Setup data structures for validation by cloning child template.
|
|
1568
2004
|
|
|
1569
2005
|
// Map syntax.
|
|
1570
|
-
if (
|
|
2006
|
+
if (M_KEYPRE === mode) {
|
|
1571
2007
|
const childtm = getprop(parent, key)
|
|
1572
2008
|
|
|
1573
2009
|
// Get corresponding current object.
|
|
1574
|
-
const pkey =
|
|
2010
|
+
const pkey = getelem(path, -2)
|
|
1575
2011
|
let tval = getprop(inj.dparent, pkey)
|
|
1576
2012
|
|
|
1577
|
-
if (
|
|
2013
|
+
if (NONE == tval) {
|
|
1578
2014
|
tval = {}
|
|
1579
2015
|
}
|
|
1580
2016
|
else if (!ismap(tval)) {
|
|
1581
2017
|
inj.errs.push(_invalidTypeMsg(
|
|
1582
2018
|
slice(inj.path, -1), S_object, typify(tval), tval), 'V0220')
|
|
1583
|
-
return
|
|
2019
|
+
return NONE
|
|
1584
2020
|
}
|
|
1585
2021
|
|
|
1586
2022
|
const ckeys = keysof(tval)
|
|
@@ -1592,52 +2028,56 @@ const validate_CHILD: Injector = (inj: Injection) => {
|
|
|
1592
2028
|
}
|
|
1593
2029
|
|
|
1594
2030
|
// Remove $CHILD to cleanup ouput.
|
|
1595
|
-
inj.setval(
|
|
1596
|
-
return
|
|
2031
|
+
inj.setval(NONE)
|
|
2032
|
+
return NONE
|
|
1597
2033
|
}
|
|
1598
2034
|
|
|
1599
2035
|
// List syntax.
|
|
1600
|
-
if (
|
|
2036
|
+
if (M_VAL === mode) {
|
|
1601
2037
|
|
|
1602
2038
|
if (!islist(parent)) {
|
|
1603
2039
|
// $CHILD was not inside a list.
|
|
1604
2040
|
inj.errs.push('Invalid $CHILD as value')
|
|
1605
|
-
return
|
|
2041
|
+
return NONE
|
|
1606
2042
|
}
|
|
1607
2043
|
|
|
1608
2044
|
const childtm = getprop(parent, 1)
|
|
1609
2045
|
|
|
1610
|
-
if (
|
|
2046
|
+
if (NONE === inj.dparent) {
|
|
1611
2047
|
// Empty list as default.
|
|
1612
|
-
parent.length = 0
|
|
1613
|
-
|
|
2048
|
+
// parent.length = 0
|
|
2049
|
+
slice(parent, 0, 0, true)
|
|
2050
|
+
return NONE
|
|
1614
2051
|
}
|
|
1615
2052
|
|
|
1616
2053
|
if (!islist(inj.dparent)) {
|
|
1617
2054
|
const msg = _invalidTypeMsg(
|
|
1618
|
-
slice(inj.path, -1),
|
|
2055
|
+
slice(inj.path, -1), S_list, typify(inj.dparent), inj.dparent, 'V0230')
|
|
1619
2056
|
inj.errs.push(msg)
|
|
1620
|
-
inj.keyI = parent
|
|
2057
|
+
inj.keyI = size(parent)
|
|
1621
2058
|
return inj.dparent
|
|
1622
2059
|
}
|
|
1623
2060
|
|
|
1624
2061
|
// Clone children abd reset inj key index.
|
|
1625
2062
|
// The inject child loop will now iterate over the cloned children,
|
|
1626
2063
|
// validating them againt the current list values.
|
|
1627
|
-
|
|
1628
|
-
inj.dparent.
|
|
1629
|
-
parent.length = inj.dparent.length
|
|
2064
|
+
items(inj.dparent, (n) => setprop(parent, n[0], clone(childtm)))
|
|
2065
|
+
slice(parent, 0, inj.dparent.length, true)
|
|
1630
2066
|
inj.keyI = 0
|
|
2067
|
+
|
|
1631
2068
|
const out = getprop(inj.dparent, 0)
|
|
1632
2069
|
return out
|
|
1633
2070
|
}
|
|
1634
2071
|
|
|
1635
|
-
return
|
|
2072
|
+
return NONE
|
|
1636
2073
|
}
|
|
1637
2074
|
|
|
1638
2075
|
|
|
2076
|
+
// TODO: implement SOME, ALL
|
|
2077
|
+
// FIX: ONE should mean exactly one, not at least one (=SOME)
|
|
2078
|
+
// TODO: implement a generate validate_ALT to do all of these
|
|
1639
2079
|
// Match at least one of the specified shapes.
|
|
1640
|
-
// Syntax: ['`$ONE`', alt0, alt1, ...]
|
|
2080
|
+
// Syntax: ['`$ONE`', alt0, alt1, ...]
|
|
1641
2081
|
const validate_ONE: Injector = (
|
|
1642
2082
|
inj: Injection,
|
|
1643
2083
|
_val: any,
|
|
@@ -1647,7 +2087,7 @@ const validate_ONE: Injector = (
|
|
|
1647
2087
|
const { mode, parent, keyI } = inj
|
|
1648
2088
|
|
|
1649
2089
|
// Only operate in val mode, since parent is a list.
|
|
1650
|
-
if (
|
|
2090
|
+
if (M_VAL === mode) {
|
|
1651
2091
|
if (!islist(parent) || 0 !== keyI) {
|
|
1652
2092
|
inj.errs.push('The $ONE validator at field ' +
|
|
1653
2093
|
pathify(inj.path, 1, 1) +
|
|
@@ -1655,7 +2095,7 @@ const validate_ONE: Injector = (
|
|
|
1655
2095
|
return
|
|
1656
2096
|
}
|
|
1657
2097
|
|
|
1658
|
-
inj.keyI = inj.keys
|
|
2098
|
+
inj.keyI = size(inj.keys)
|
|
1659
2099
|
|
|
1660
2100
|
// Clean up structure, replacing [$ONE, ...] with current
|
|
1661
2101
|
inj.setval(inj.dparent, 2)
|
|
@@ -1664,7 +2104,7 @@ const validate_ONE: Injector = (
|
|
|
1664
2104
|
inj.key = getelem(inj.path, -1)
|
|
1665
2105
|
|
|
1666
2106
|
let tvals = slice(parent, 1)
|
|
1667
|
-
if (0 === tvals
|
|
2107
|
+
if (0 === size(tvals)) {
|
|
1668
2108
|
inj.errs.push('The $ONE validator at field ' +
|
|
1669
2109
|
pathify(inj.path, 1, 1) +
|
|
1670
2110
|
' must have at least one argument.')
|
|
@@ -1677,7 +2117,7 @@ const validate_ONE: Injector = (
|
|
|
1677
2117
|
// If match, then errs.length = 0
|
|
1678
2118
|
let terrs: any[] = []
|
|
1679
2119
|
|
|
1680
|
-
const vstore = {
|
|
2120
|
+
const vstore = merge([{}, store], 1)
|
|
1681
2121
|
vstore.$TOP = inj.dparent
|
|
1682
2122
|
|
|
1683
2123
|
const vcurrent = validate(inj.dparent, tval, {
|
|
@@ -1689,21 +2129,19 @@ const validate_ONE: Injector = (
|
|
|
1689
2129
|
inj.setval(vcurrent, -2)
|
|
1690
2130
|
|
|
1691
2131
|
// Accept current value if there was a match
|
|
1692
|
-
if (0 === terrs
|
|
2132
|
+
if (0 === size(terrs)) {
|
|
1693
2133
|
return
|
|
1694
2134
|
}
|
|
1695
2135
|
}
|
|
1696
2136
|
|
|
1697
2137
|
// There was no match.
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
.join(', ')
|
|
1702
|
-
.replace(R_TRANSFORM_NAME, (_m: any, p1: string) => p1.toLowerCase())
|
|
2138
|
+
const valdesc =
|
|
2139
|
+
replace(join(items(tvals, (n) => stringify(n[1])), ', '),
|
|
2140
|
+
R_TRANSFORM_NAME, (_m: any, p1: string) => p1.toLowerCase())
|
|
1703
2141
|
|
|
1704
2142
|
inj.errs.push(_invalidTypeMsg(
|
|
1705
2143
|
inj.path,
|
|
1706
|
-
(1 < tvals
|
|
2144
|
+
(1 < size(tvals) ? 'one of ' : '') + valdesc,
|
|
1707
2145
|
typify(inj.dparent), inj.dparent, 'V0210'))
|
|
1708
2146
|
}
|
|
1709
2147
|
}
|
|
@@ -1713,7 +2151,7 @@ const validate_EXACT: Injector = (inj: Injection) => {
|
|
|
1713
2151
|
const { mode, parent, key, keyI } = inj
|
|
1714
2152
|
|
|
1715
2153
|
// Only operate in val mode, since parent is a list.
|
|
1716
|
-
if (
|
|
2154
|
+
if (M_VAL === mode) {
|
|
1717
2155
|
if (!islist(parent) || 0 !== keyI) {
|
|
1718
2156
|
inj.errs.push('The $EXACT validator at field ' +
|
|
1719
2157
|
pathify(inj.path, 1, 1) +
|
|
@@ -1721,16 +2159,17 @@ const validate_EXACT: Injector = (inj: Injection) => {
|
|
|
1721
2159
|
return
|
|
1722
2160
|
}
|
|
1723
2161
|
|
|
1724
|
-
inj.keyI = inj.keys
|
|
2162
|
+
inj.keyI = size(inj.keys)
|
|
1725
2163
|
|
|
1726
2164
|
// Clean up structure, replacing [$EXACT, ...] with current data parent
|
|
1727
2165
|
inj.setval(inj.dparent, 2)
|
|
1728
2166
|
|
|
1729
|
-
inj.path = slice(inj.path, 0, inj.path
|
|
2167
|
+
// inj.path = slice(inj.path, 0, size(inj.path) - 1)
|
|
2168
|
+
inj.path = slice(inj.path, 0, -1)
|
|
1730
2169
|
inj.key = getelem(inj.path, -1)
|
|
1731
2170
|
|
|
1732
2171
|
let tvals = slice(parent, 1)
|
|
1733
|
-
if (0 === tvals
|
|
2172
|
+
if (0 === size(tvals)) {
|
|
1734
2173
|
inj.errs.push('The $EXACT validator at field ' +
|
|
1735
2174
|
pathify(inj.path, 1, 1) +
|
|
1736
2175
|
' must have at least one argument.')
|
|
@@ -1753,15 +2192,15 @@ const validate_EXACT: Injector = (inj: Injection) => {
|
|
|
1753
2192
|
}
|
|
1754
2193
|
}
|
|
1755
2194
|
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2195
|
+
// There was no match.
|
|
2196
|
+
const valdesc =
|
|
2197
|
+
replace(join(items(tvals, (n) => stringify(n[1])), ', '),
|
|
2198
|
+
R_TRANSFORM_NAME, (_m: any, p1: string) => p1.toLowerCase())
|
|
1760
2199
|
|
|
1761
2200
|
inj.errs.push(_invalidTypeMsg(
|
|
1762
2201
|
inj.path,
|
|
1763
|
-
(1 < inj.path
|
|
1764
|
-
'exactly equal to ' + (1 === tvals
|
|
2202
|
+
(1 < size(inj.path) ? '' : 'value ') +
|
|
2203
|
+
'exactly equal to ' + (1 === size(tvals) ? '' : 'one of ') + valdesc,
|
|
1765
2204
|
typify(inj.dparent), inj.dparent, 'V0110'))
|
|
1766
2205
|
}
|
|
1767
2206
|
else {
|
|
@@ -1779,7 +2218,7 @@ const _validation: Modify = (
|
|
|
1779
2218
|
inj?: Injection,
|
|
1780
2219
|
) => {
|
|
1781
2220
|
|
|
1782
|
-
if (
|
|
2221
|
+
if (NONE === inj) {
|
|
1783
2222
|
return
|
|
1784
2223
|
}
|
|
1785
2224
|
|
|
@@ -1793,28 +2232,28 @@ const _validation: Modify = (
|
|
|
1793
2232
|
// Current val to verify.
|
|
1794
2233
|
const cval = getprop(inj.dparent, key)
|
|
1795
2234
|
|
|
1796
|
-
if (
|
|
2235
|
+
if (NONE === inj || (!exact && NONE === cval)) {
|
|
1797
2236
|
return
|
|
1798
2237
|
}
|
|
1799
2238
|
|
|
1800
2239
|
const ptype = typify(pval)
|
|
1801
2240
|
|
|
1802
2241
|
// Delete any special commands remaining.
|
|
1803
|
-
if (
|
|
2242
|
+
if (0 < (T_string & ptype) && pval.includes(S_DS)) {
|
|
1804
2243
|
return
|
|
1805
2244
|
}
|
|
1806
2245
|
|
|
1807
2246
|
const ctype = typify(cval)
|
|
1808
2247
|
|
|
1809
2248
|
// Type mismatch.
|
|
1810
|
-
if (ptype !== ctype &&
|
|
1811
|
-
inj.errs.push(_invalidTypeMsg(inj.path, ptype, ctype, cval, 'V0010'))
|
|
2249
|
+
if (ptype !== ctype && NONE !== pval) {
|
|
2250
|
+
inj.errs.push(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0010'))
|
|
1812
2251
|
return
|
|
1813
2252
|
}
|
|
1814
2253
|
|
|
1815
2254
|
if (ismap(cval)) {
|
|
1816
2255
|
if (!ismap(pval)) {
|
|
1817
|
-
inj.errs.push(_invalidTypeMsg(inj.path, ptype, ctype, cval, 'V0020'))
|
|
2256
|
+
inj.errs.push(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0020'))
|
|
1818
2257
|
return
|
|
1819
2258
|
}
|
|
1820
2259
|
|
|
@@ -1822,7 +2261,7 @@ const _validation: Modify = (
|
|
|
1822
2261
|
const pkeys = keysof(pval)
|
|
1823
2262
|
|
|
1824
2263
|
// Empty spec object {} means object can be open (any keys).
|
|
1825
|
-
if (0 < pkeys
|
|
2264
|
+
if (0 < size(pkeys) && true !== getprop(pval, '`$OPEN`')) {
|
|
1826
2265
|
const badkeys = []
|
|
1827
2266
|
for (let ckey of ckeys) {
|
|
1828
2267
|
if (!haskey(pval, ckey)) {
|
|
@@ -1831,9 +2270,9 @@ const _validation: Modify = (
|
|
|
1831
2270
|
}
|
|
1832
2271
|
|
|
1833
2272
|
// Closed object, so reject extra keys not in shape.
|
|
1834
|
-
if (0 < badkeys
|
|
2273
|
+
if (0 < size(badkeys)) {
|
|
1835
2274
|
const msg =
|
|
1836
|
-
'Unexpected keys at field ' + pathify(inj.path, 1) + S_VIZ +
|
|
2275
|
+
'Unexpected keys at field ' + pathify(inj.path, 1) + S_VIZ + join(badkeys, ', ')
|
|
1837
2276
|
inj.errs.push(msg)
|
|
1838
2277
|
}
|
|
1839
2278
|
}
|
|
@@ -1847,7 +2286,7 @@ const _validation: Modify = (
|
|
|
1847
2286
|
}
|
|
1848
2287
|
else if (islist(cval)) {
|
|
1849
2288
|
if (!islist(pval)) {
|
|
1850
|
-
inj.errs.push(_invalidTypeMsg(inj.path, ptype, ctype, cval, 'V0030'))
|
|
2289
|
+
inj.errs.push(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0030'))
|
|
1851
2290
|
}
|
|
1852
2291
|
}
|
|
1853
2292
|
else if (exact) {
|
|
@@ -1887,50 +2326,57 @@ function validate(
|
|
|
1887
2326
|
const collect = null != injdef?.errs
|
|
1888
2327
|
const errs = injdef?.errs || []
|
|
1889
2328
|
|
|
1890
|
-
const store =
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2329
|
+
const store = merge([
|
|
2330
|
+
{
|
|
2331
|
+
// Remove the transform commands.
|
|
2332
|
+
$DELETE: null,
|
|
2333
|
+
$COPY: null,
|
|
2334
|
+
$KEY: null,
|
|
2335
|
+
$META: null,
|
|
2336
|
+
$MERGE: null,
|
|
2337
|
+
$EACH: null,
|
|
2338
|
+
$PACK: null,
|
|
2339
|
+
|
|
2340
|
+
$STRING: validate_STRING,
|
|
2341
|
+
$NUMBER: validate_TYPE,
|
|
2342
|
+
$INTEGER: validate_TYPE,
|
|
2343
|
+
$DECIMAL: validate_TYPE,
|
|
2344
|
+
$BOOLEAN: validate_TYPE,
|
|
2345
|
+
$NULL: validate_TYPE,
|
|
2346
|
+
$NIL: validate_TYPE,
|
|
2347
|
+
$MAP: validate_TYPE,
|
|
2348
|
+
$LIST: validate_TYPE,
|
|
2349
|
+
$FUNCTION: validate_TYPE,
|
|
2350
|
+
$INSTANCE: validate_TYPE,
|
|
2351
|
+
$ANY: validate_ANY,
|
|
2352
|
+
$CHILD: validate_CHILD,
|
|
2353
|
+
$ONE: validate_ONE,
|
|
2354
|
+
$EXACT: validate_EXACT,
|
|
2355
|
+
},
|
|
2356
|
+
|
|
2357
|
+
getdef(extra, {}),
|
|
1912
2358
|
|
|
1913
2359
|
// A special top level value to collect errors.
|
|
1914
|
-
// NOTE: collecterrs
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2360
|
+
// NOTE: collecterrs parameter always wins.
|
|
2361
|
+
{
|
|
2362
|
+
$ERRS: errs,
|
|
2363
|
+
}
|
|
2364
|
+
], 1)
|
|
1919
2365
|
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
}
|
|
2366
|
+
let meta = getprop(injdef, 'meta', {})
|
|
2367
|
+
setprop(meta, S_BEXACT, getprop(meta, S_BEXACT, false))
|
|
1923
2368
|
|
|
1924
2369
|
const out = transform(data, spec, {
|
|
1925
2370
|
meta,
|
|
1926
2371
|
extra: store,
|
|
1927
2372
|
modify: _validation,
|
|
1928
|
-
handler: _validatehandler
|
|
2373
|
+
handler: _validatehandler,
|
|
2374
|
+
errs,
|
|
1929
2375
|
})
|
|
1930
2376
|
|
|
1931
|
-
const generr = (0 < errs
|
|
2377
|
+
const generr = (0 < size(errs) && !collect)
|
|
1932
2378
|
if (generr) {
|
|
1933
|
-
throw new Error(
|
|
2379
|
+
throw new Error(join(errs, ' | '))
|
|
1934
2380
|
}
|
|
1935
2381
|
|
|
1936
2382
|
return out
|
|
@@ -1938,18 +2384,16 @@ function validate(
|
|
|
1938
2384
|
|
|
1939
2385
|
|
|
1940
2386
|
const select_AND: Injector = (inj: Injection, _val: any, _ref: string, store: any) => {
|
|
1941
|
-
if (
|
|
2387
|
+
if (M_KEYPRE === inj.mode) {
|
|
1942
2388
|
const terms = getprop(inj.parent, inj.key)
|
|
1943
2389
|
|
|
1944
2390
|
const ppath = slice(inj.path, -1)
|
|
1945
2391
|
const point = getpath(store, ppath)
|
|
1946
2392
|
|
|
1947
|
-
const vstore = {
|
|
2393
|
+
const vstore = merge([{}, store], 1)
|
|
1948
2394
|
vstore.$TOP = point
|
|
1949
2395
|
|
|
1950
2396
|
for (let term of terms) {
|
|
1951
|
-
// setprop(term, '`$OPEN`', getprop(term, '`$OPEN`', true))
|
|
1952
|
-
|
|
1953
2397
|
let terrs: any[] = []
|
|
1954
2398
|
|
|
1955
2399
|
validate(point, term, {
|
|
@@ -1958,7 +2402,7 @@ const select_AND: Injector = (inj: Injection, _val: any, _ref: string, store: an
|
|
|
1958
2402
|
meta: inj.meta,
|
|
1959
2403
|
})
|
|
1960
2404
|
|
|
1961
|
-
if (0 != terrs
|
|
2405
|
+
if (0 != size(terrs)) {
|
|
1962
2406
|
inj.errs.push(
|
|
1963
2407
|
'AND:' + pathify(ppath) + S_VIZ + stringify(point) + ' fail:' + stringify(terms))
|
|
1964
2408
|
}
|
|
@@ -1972,13 +2416,13 @@ const select_AND: Injector = (inj: Injection, _val: any, _ref: string, store: an
|
|
|
1972
2416
|
|
|
1973
2417
|
|
|
1974
2418
|
const select_OR: Injector = (inj: Injection, _val: any, _ref: string, store: any) => {
|
|
1975
|
-
if (
|
|
2419
|
+
if (M_KEYPRE === inj.mode) {
|
|
1976
2420
|
const terms = getprop(inj.parent, inj.key)
|
|
1977
2421
|
|
|
1978
2422
|
const ppath = slice(inj.path, -1)
|
|
1979
2423
|
const point = getpath(store, ppath)
|
|
1980
2424
|
|
|
1981
|
-
const vstore = {
|
|
2425
|
+
const vstore = merge([{}, store], 1)
|
|
1982
2426
|
vstore.$TOP = point
|
|
1983
2427
|
|
|
1984
2428
|
for (let term of terms) {
|
|
@@ -1990,7 +2434,7 @@ const select_OR: Injector = (inj: Injection, _val: any, _ref: string, store: any
|
|
|
1990
2434
|
meta: inj.meta,
|
|
1991
2435
|
})
|
|
1992
2436
|
|
|
1993
|
-
if (0 === terrs
|
|
2437
|
+
if (0 === size(terrs)) {
|
|
1994
2438
|
const gkey = getelem(inj.path, -2)
|
|
1995
2439
|
const gp = getelem(inj.nodes, -2)
|
|
1996
2440
|
setprop(gp, gkey, point)
|
|
@@ -2006,13 +2450,13 @@ const select_OR: Injector = (inj: Injection, _val: any, _ref: string, store: any
|
|
|
2006
2450
|
|
|
2007
2451
|
|
|
2008
2452
|
const select_NOT: Injector = (inj: Injection, _val: any, _ref: string, store: any) => {
|
|
2009
|
-
if (
|
|
2453
|
+
if (M_KEYPRE === inj.mode) {
|
|
2010
2454
|
const term = getprop(inj.parent, inj.key)
|
|
2011
2455
|
|
|
2012
2456
|
const ppath = slice(inj.path, -1)
|
|
2013
2457
|
const point = getpath(store, ppath)
|
|
2014
2458
|
|
|
2015
|
-
const vstore = {
|
|
2459
|
+
const vstore = merge([{}, store], 1)
|
|
2016
2460
|
vstore.$TOP = point
|
|
2017
2461
|
|
|
2018
2462
|
let terrs: any[] = []
|
|
@@ -2023,7 +2467,7 @@ const select_NOT: Injector = (inj: Injection, _val: any, _ref: string, store: an
|
|
|
2023
2467
|
meta: inj.meta,
|
|
2024
2468
|
})
|
|
2025
2469
|
|
|
2026
|
-
if (0 == terrs
|
|
2470
|
+
if (0 == size(terrs)) {
|
|
2027
2471
|
inj.errs.push(
|
|
2028
2472
|
'NOT:' + pathify(ppath) + S_VIZ + stringify(point) + ' fail:' + stringify(term))
|
|
2029
2473
|
}
|
|
@@ -2036,7 +2480,7 @@ const select_NOT: Injector = (inj: Injection, _val: any, _ref: string, store: an
|
|
|
2036
2480
|
|
|
2037
2481
|
|
|
2038
2482
|
const select_CMP: Injector = (inj: Injection, _val: any, ref: string, store: any) => {
|
|
2039
|
-
if (
|
|
2483
|
+
if (M_KEYPRE === inj.mode) {
|
|
2040
2484
|
const term = getprop(inj.parent, inj.key)
|
|
2041
2485
|
// const src = getprop(store, inj.base, store)
|
|
2042
2486
|
const gkey = getelem(inj.path, -2)
|
|
@@ -2075,7 +2519,7 @@ const select_CMP: Injector = (inj: Injection, _val: any, ref: string, store: any
|
|
|
2075
2519
|
}
|
|
2076
2520
|
}
|
|
2077
2521
|
|
|
2078
|
-
return
|
|
2522
|
+
return NONE
|
|
2079
2523
|
}
|
|
2080
2524
|
|
|
2081
2525
|
|
|
@@ -2089,10 +2533,13 @@ function select(children: any, query: any): any[] {
|
|
|
2089
2533
|
}
|
|
2090
2534
|
|
|
2091
2535
|
if (ismap(children)) {
|
|
2092
|
-
children = items(children
|
|
2536
|
+
children = items(children, n => {
|
|
2537
|
+
setprop(n[1], S_DKEY, n[0])
|
|
2538
|
+
return n[1]
|
|
2539
|
+
})
|
|
2093
2540
|
}
|
|
2094
2541
|
else {
|
|
2095
|
-
children = (children
|
|
2542
|
+
children = items(children, (n) => (setprop(n[1], S_DKEY, +n[0]), n[1]))
|
|
2096
2543
|
}
|
|
2097
2544
|
|
|
2098
2545
|
const results: any[] = []
|
|
@@ -2134,10 +2581,9 @@ function select(children: any, query: any): any[] {
|
|
|
2134
2581
|
}
|
|
2135
2582
|
|
|
2136
2583
|
|
|
2137
|
-
|
|
2138
2584
|
// Injection state used for recursive injection into JSON - like data structures.
|
|
2139
2585
|
class Injection {
|
|
2140
|
-
mode: InjectMode // Injection mode:
|
|
2586
|
+
mode: InjectMode // Injection mode: M_KEYPRE, M_VAL, M_KEYPOST.
|
|
2141
2587
|
full: boolean // Transform escape was full key name.
|
|
2142
2588
|
keyI: number // Index of parent key in list of parent keys.
|
|
2143
2589
|
keys: string[] // List of parent keys.
|
|
@@ -2148,7 +2594,7 @@ class Injection {
|
|
|
2148
2594
|
nodes: any[] // Stack of ancestor nodes.
|
|
2149
2595
|
handler: Injector // Custom handler for injections.
|
|
2150
2596
|
errs: any[] // Error collector.
|
|
2151
|
-
meta: Record<string, any> // Custom meta data.
|
|
2597
|
+
meta: Record<string, any> // Custom meta data. NOTE: do not merge, values must remain as-is.
|
|
2152
2598
|
dparent: any // Current data parent node (contains current data value).
|
|
2153
2599
|
dpath: string[] // Current data value path
|
|
2154
2600
|
base?: string // Base key for data in store, if any.
|
|
@@ -2161,10 +2607,10 @@ class Injection {
|
|
|
2161
2607
|
this.parent = parent
|
|
2162
2608
|
this.errs = []
|
|
2163
2609
|
|
|
2164
|
-
this.dparent =
|
|
2610
|
+
this.dparent = NONE
|
|
2165
2611
|
this.dpath = [S_DTOP]
|
|
2166
2612
|
|
|
2167
|
-
this.mode =
|
|
2613
|
+
this.mode = M_VAL
|
|
2168
2614
|
this.full = false
|
|
2169
2615
|
this.keyI = 0
|
|
2170
2616
|
this.keys = [S_DTOP]
|
|
@@ -2180,7 +2626,7 @@ class Injection {
|
|
|
2180
2626
|
toString(prefix?: string) {
|
|
2181
2627
|
return 'INJ' + (null == prefix ? '' : S_FS + prefix) + S_CN +
|
|
2182
2628
|
pad(pathify(this.path, 1)) +
|
|
2183
|
-
this.mode + (this.full ? '/full' : '') + S_CN +
|
|
2629
|
+
MODENAME[this.mode] + (this.full ? '/full' : '') + S_CN +
|
|
2184
2630
|
'key=' + this.keyI + S_FS + this.key + S_FS + S_OS + this.keys + S_CS +
|
|
2185
2631
|
' p=' + stringify(this.parent, -1, 1) +
|
|
2186
2632
|
' m=' + stringify(this.meta, -1, 1) +
|
|
@@ -2194,12 +2640,12 @@ class Injection {
|
|
|
2194
2640
|
const parentkey = getelem(this.path, -2)
|
|
2195
2641
|
|
|
2196
2642
|
// Resolve current node in store for local paths.
|
|
2197
|
-
if (
|
|
2643
|
+
if (NONE === this.dparent) {
|
|
2198
2644
|
|
|
2199
2645
|
// Even if there's no data, dpath should continue to match path, so that
|
|
2200
2646
|
// relative paths work properly.
|
|
2201
|
-
if (1 < this.dpath
|
|
2202
|
-
this.dpath = [
|
|
2647
|
+
if (1 < size(this.dpath)) {
|
|
2648
|
+
this.dpath = flatten([this.dpath, parentkey])
|
|
2203
2649
|
}
|
|
2204
2650
|
}
|
|
2205
2651
|
else {
|
|
@@ -2212,11 +2658,12 @@ class Injection {
|
|
|
2212
2658
|
this.dpath = slice(this.dpath, -1)
|
|
2213
2659
|
}
|
|
2214
2660
|
else {
|
|
2215
|
-
this.dpath = [
|
|
2661
|
+
this.dpath = flatten([this.dpath, parentkey])
|
|
2216
2662
|
}
|
|
2217
2663
|
}
|
|
2218
2664
|
}
|
|
2219
2665
|
|
|
2666
|
+
// TODO: is this needed?
|
|
2220
2667
|
return this.dparent
|
|
2221
2668
|
}
|
|
2222
2669
|
|
|
@@ -2230,8 +2677,8 @@ class Injection {
|
|
|
2230
2677
|
cinj.keys = keys
|
|
2231
2678
|
cinj.key = key
|
|
2232
2679
|
|
|
2233
|
-
cinj.path = [
|
|
2234
|
-
cinj.nodes = [
|
|
2680
|
+
cinj.path = flatten([getdef(this.path, []), key])
|
|
2681
|
+
cinj.nodes = flatten([getdef(this.nodes, []), [val]])
|
|
2235
2682
|
|
|
2236
2683
|
cinj.mode = this.mode
|
|
2237
2684
|
cinj.handler = this.handler
|
|
@@ -2241,7 +2688,7 @@ class Injection {
|
|
|
2241
2688
|
cinj.errs = this.errs
|
|
2242
2689
|
cinj.prior = this
|
|
2243
2690
|
|
|
2244
|
-
cinj.dpath = [
|
|
2691
|
+
cinj.dpath = flatten([this.dpath])
|
|
2245
2692
|
cinj.dparent = this.dparent
|
|
2246
2693
|
|
|
2247
2694
|
return cinj
|
|
@@ -2249,18 +2696,22 @@ class Injection {
|
|
|
2249
2696
|
|
|
2250
2697
|
|
|
2251
2698
|
setval(val: any, ancestor?: number) {
|
|
2699
|
+
let parent = NONE
|
|
2252
2700
|
if (null == ancestor || ancestor < 2) {
|
|
2253
|
-
|
|
2254
|
-
delprop(this.parent, this.key) :
|
|
2701
|
+
parent = NONE === val ?
|
|
2702
|
+
this.parent = delprop(this.parent, this.key) :
|
|
2255
2703
|
setprop(this.parent, this.key, val)
|
|
2256
2704
|
}
|
|
2257
2705
|
else {
|
|
2258
2706
|
const aval = getelem(this.nodes, 0 - ancestor)
|
|
2259
2707
|
const akey = getelem(this.path, 0 - ancestor)
|
|
2260
|
-
|
|
2708
|
+
parent = NONE === val ?
|
|
2261
2709
|
delprop(aval, akey) :
|
|
2262
2710
|
setprop(aval, akey, val)
|
|
2263
2711
|
}
|
|
2712
|
+
|
|
2713
|
+
// console.log('SETVAL', val, this.key, this.parent)
|
|
2714
|
+
return parent
|
|
2264
2715
|
}
|
|
2265
2716
|
}
|
|
2266
2717
|
|
|
@@ -2269,21 +2720,21 @@ class Injection {
|
|
|
2269
2720
|
// ==================
|
|
2270
2721
|
|
|
2271
2722
|
|
|
2272
|
-
// Update all references to target in inj.nodes.
|
|
2273
|
-
function _updateAncestors(_inj: Injection, target: any, tkey: any, tval: any) {
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
}
|
|
2723
|
+
// // Update all references to target in inj.nodes.
|
|
2724
|
+
// function _updateAncestors(_inj: Injection, target: any, tkey: any, tval: any) {
|
|
2725
|
+
// // SetProp is sufficient in TypeScript as target reference remains consistent even for lists.
|
|
2726
|
+
// setprop(target, tkey, tval)
|
|
2727
|
+
// }
|
|
2277
2728
|
|
|
2278
2729
|
|
|
2279
2730
|
// Build a type validation error message.
|
|
2280
|
-
function _invalidTypeMsg(path: any, needtype: string, vt:
|
|
2731
|
+
function _invalidTypeMsg(path: any, needtype: string, vt: number, v: any, _whence?: string) {
|
|
2281
2732
|
let vs = null == v ? 'no value' : stringify(v)
|
|
2282
2733
|
|
|
2283
2734
|
return 'Expected ' +
|
|
2284
|
-
(1 < path
|
|
2735
|
+
(1 < size(path) ? ('field ' + pathify(path, 1) + ' to be ') : '') +
|
|
2285
2736
|
needtype + ', but found ' +
|
|
2286
|
-
(null != v ? vt + S_VIZ : '') + vs +
|
|
2737
|
+
(null != v ? typename(vt) + S_VIZ : '') + vs +
|
|
2287
2738
|
|
|
2288
2739
|
// Uncomment to help debug validation errors.
|
|
2289
2740
|
// ' [' + _whence + ']' +
|
|
@@ -2301,15 +2752,17 @@ const _injecthandler: Injector = (
|
|
|
2301
2752
|
store: any
|
|
2302
2753
|
): any => {
|
|
2303
2754
|
let out = val
|
|
2304
|
-
const iscmd = isfunc(val) && (
|
|
2755
|
+
const iscmd = isfunc(val) && (NONE === ref || ref.startsWith(S_DS))
|
|
2305
2756
|
|
|
2306
2757
|
// Only call val function if it is a special command ($NAME format).
|
|
2758
|
+
// TODO: OR if meta.'$CALL'
|
|
2759
|
+
|
|
2307
2760
|
if (iscmd) {
|
|
2308
2761
|
out = (val as Injector)(inj, val, ref, store)
|
|
2309
2762
|
}
|
|
2310
2763
|
|
|
2311
2764
|
// Update parent with value. Ensures references remain in node tree.
|
|
2312
|
-
else if (
|
|
2765
|
+
else if (M_VAL === inj.mode && inj.full) {
|
|
2313
2766
|
inj.setval(val)
|
|
2314
2767
|
}
|
|
2315
2768
|
|
|
@@ -2379,9 +2832,9 @@ function _injectstr(
|
|
|
2379
2832
|
let pathref = m[1]
|
|
2380
2833
|
|
|
2381
2834
|
// Special escapes inside injection.
|
|
2382
|
-
|
|
2383
|
-
pathref.replace(R_BT_ESCAPE, S_BT).replace(R_DS_ESCAPE, S_DS)
|
|
2384
|
-
|
|
2835
|
+
if (3 < size(pathref)) {
|
|
2836
|
+
pathref = pathref.replace(R_BT_ESCAPE, S_BT).replace(R_DS_ESCAPE, S_DS)
|
|
2837
|
+
}
|
|
2385
2838
|
|
|
2386
2839
|
// Get the extracted path reference.
|
|
2387
2840
|
out = getpath(store, pathref, inj)
|
|
@@ -2391,14 +2844,19 @@ function _injectstr(
|
|
|
2391
2844
|
// Check for injections within the string.
|
|
2392
2845
|
const partial = (_m: string, ref: string) => {
|
|
2393
2846
|
// Special escapes inside injection.
|
|
2394
|
-
|
|
2847
|
+
|
|
2848
|
+
if (3 < size(ref)) {
|
|
2849
|
+
ref = ref.replace(R_BT_ESCAPE, S_BT).replace(R_DS_ESCAPE, S_DS)
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2395
2852
|
if (inj) {
|
|
2396
2853
|
inj.full = false
|
|
2397
2854
|
}
|
|
2855
|
+
|
|
2398
2856
|
const found = getpath(store, ref, inj)
|
|
2399
2857
|
|
|
2400
2858
|
// Ensure inject value is a string.
|
|
2401
|
-
return
|
|
2859
|
+
return NONE === found ? S_MT : S_string === typeof found ? found : JSON.stringify(found)
|
|
2402
2860
|
}
|
|
2403
2861
|
|
|
2404
2862
|
out = val.replace(R_INJECTION_PARTIAL, partial)
|
|
@@ -2415,11 +2873,101 @@ function _injectstr(
|
|
|
2415
2873
|
}
|
|
2416
2874
|
|
|
2417
2875
|
|
|
2876
|
+
// Handler Utilities
|
|
2877
|
+
// =================
|
|
2878
|
+
|
|
2879
|
+
|
|
2880
|
+
const MODENAME: any = {
|
|
2881
|
+
[M_VAL]: 'val',
|
|
2882
|
+
[M_KEYPRE]: 'key:pre',
|
|
2883
|
+
[M_KEYPOST]: 'key:post',
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
const PLACEMENT: any = {
|
|
2887
|
+
[M_VAL]: 'value',
|
|
2888
|
+
[M_KEYPRE]: S_key,
|
|
2889
|
+
[M_KEYPOST]: S_key,
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
function checkPlacement(
|
|
2893
|
+
modes: InjectMode,
|
|
2894
|
+
ijname: string,
|
|
2895
|
+
parentTypes: number,
|
|
2896
|
+
inj: Injection
|
|
2897
|
+
): boolean {
|
|
2898
|
+
if (0 === (modes & inj.mode)) {
|
|
2899
|
+
inj.errs.push('$' + ijname + ': invalid placement as ' + PLACEMENT[inj.mode] +
|
|
2900
|
+
', expected: ' + join(items(
|
|
2901
|
+
[M_KEYPRE, M_KEYPOST, M_VAL].filter(m => modes & m),
|
|
2902
|
+
(n: any) => PLACEMENT[n[1]]), ',') + '.')
|
|
2903
|
+
return false
|
|
2904
|
+
}
|
|
2905
|
+
if (!isempty(parentTypes)) {
|
|
2906
|
+
const ptype = typify(inj.parent)
|
|
2907
|
+
if (0 === (parentTypes & ptype)) {
|
|
2908
|
+
inj.errs.push('$' + ijname + ': invalid placement in parent ' + typename(ptype) +
|
|
2909
|
+
', expected: ' + typename(parentTypes) + '.')
|
|
2910
|
+
return false
|
|
2911
|
+
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
return true
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
|
|
2918
|
+
// function injectorArgs(argTypes: number[], inj: Injection): any {
|
|
2919
|
+
function injectorArgs(argTypes: number[], args: any[]): any {
|
|
2920
|
+
const numargs = size(argTypes)
|
|
2921
|
+
const found = new Array(1 + numargs)
|
|
2922
|
+
found[0] = NONE
|
|
2923
|
+
for (let argI = 0; argI < numargs; argI++) {
|
|
2924
|
+
// const arg = inj.parent[1 + argI]
|
|
2925
|
+
const arg = args[argI]
|
|
2926
|
+
const argType = typify(arg)
|
|
2927
|
+
if (0 === (argTypes[argI] & argType)) {
|
|
2928
|
+
found[0] = 'invalid argument: ' + stringify(arg, 22) +
|
|
2929
|
+
' (' + typename(argType) + ' at position ' + (1 + argI) +
|
|
2930
|
+
') is not of type: ' + typename(argTypes[argI]) + '.'
|
|
2931
|
+
break
|
|
2932
|
+
}
|
|
2933
|
+
found[1 + argI] = arg
|
|
2934
|
+
}
|
|
2935
|
+
return found
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
|
|
2939
|
+
function injectChild(child: any, store: any, inj: Injection): Injection {
|
|
2940
|
+
let cinj = inj
|
|
2941
|
+
|
|
2942
|
+
// Replace ['`$FORMAT`',...] with child
|
|
2943
|
+
if (null != inj.prior) {
|
|
2944
|
+
if (null != inj.prior.prior) {
|
|
2945
|
+
cinj = inj.prior.prior.child(inj.prior.keyI, inj.prior.keys)
|
|
2946
|
+
cinj.val = child
|
|
2947
|
+
setprop(cinj.parent, inj.prior.key, child)
|
|
2948
|
+
}
|
|
2949
|
+
else {
|
|
2950
|
+
cinj = inj.prior.child(inj.keyI, inj.keys)
|
|
2951
|
+
cinj.val = child
|
|
2952
|
+
setprop(cinj.parent, inj.key, child)
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
// console.log('FORMAT-INJECT-CHILD', child)
|
|
2957
|
+
inject(child, store, cinj)
|
|
2958
|
+
|
|
2959
|
+
return cinj
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
|
|
2418
2963
|
class StructUtility {
|
|
2419
2964
|
clone = clone
|
|
2420
2965
|
delprop = delprop
|
|
2421
2966
|
escre = escre
|
|
2422
2967
|
escurl = escurl
|
|
2968
|
+
filter = filter
|
|
2969
|
+
flatten = flatten
|
|
2970
|
+
getdef = getdef
|
|
2423
2971
|
getelem = getelem
|
|
2424
2972
|
getpath = getpath
|
|
2425
2973
|
getprop = getprop
|
|
@@ -2432,13 +2980,14 @@ class StructUtility {
|
|
|
2432
2980
|
ismap = ismap
|
|
2433
2981
|
isnode = isnode
|
|
2434
2982
|
items = items
|
|
2435
|
-
|
|
2983
|
+
join = join
|
|
2436
2984
|
jsonify = jsonify
|
|
2437
2985
|
keysof = keysof
|
|
2438
2986
|
merge = merge
|
|
2439
2987
|
pad = pad
|
|
2440
2988
|
pathify = pathify
|
|
2441
2989
|
select = select
|
|
2990
|
+
setpath = setpath
|
|
2442
2991
|
setprop = setprop
|
|
2443
2992
|
size = size
|
|
2444
2993
|
slice = slice
|
|
@@ -2446,11 +2995,36 @@ class StructUtility {
|
|
|
2446
2995
|
stringify = stringify
|
|
2447
2996
|
transform = transform
|
|
2448
2997
|
typify = typify
|
|
2998
|
+
typename = typename
|
|
2449
2999
|
validate = validate
|
|
2450
3000
|
walk = walk
|
|
2451
3001
|
|
|
2452
|
-
|
|
2453
|
-
|
|
3002
|
+
SKIP = SKIP
|
|
3003
|
+
DELETE = DELETE
|
|
3004
|
+
|
|
3005
|
+
jm = jm
|
|
3006
|
+
jt = jt
|
|
3007
|
+
tn = typename
|
|
3008
|
+
|
|
3009
|
+
T_any = T_any
|
|
3010
|
+
T_noval = T_noval
|
|
3011
|
+
T_boolean = T_boolean
|
|
3012
|
+
T_decimal = T_decimal
|
|
3013
|
+
T_integer = T_integer
|
|
3014
|
+
T_number = T_number
|
|
3015
|
+
T_string = T_string
|
|
3016
|
+
T_function = T_function
|
|
3017
|
+
T_symbol = T_symbol
|
|
3018
|
+
T_null = T_null
|
|
3019
|
+
T_list = T_list
|
|
3020
|
+
T_map = T_map
|
|
3021
|
+
T_instance = T_instance
|
|
3022
|
+
T_scalar = T_scalar
|
|
3023
|
+
T_node = T_node
|
|
3024
|
+
|
|
3025
|
+
checkPlacement = checkPlacement
|
|
3026
|
+
injectorArgs = injectorArgs
|
|
3027
|
+
injectChild = injectChild
|
|
2454
3028
|
}
|
|
2455
3029
|
|
|
2456
3030
|
export {
|
|
@@ -2459,6 +3033,9 @@ export {
|
|
|
2459
3033
|
delprop,
|
|
2460
3034
|
escre,
|
|
2461
3035
|
escurl,
|
|
3036
|
+
filter,
|
|
3037
|
+
flatten,
|
|
3038
|
+
getdef,
|
|
2462
3039
|
getelem,
|
|
2463
3040
|
getpath,
|
|
2464
3041
|
getprop,
|
|
@@ -2471,13 +3048,14 @@ export {
|
|
|
2471
3048
|
ismap,
|
|
2472
3049
|
isnode,
|
|
2473
3050
|
items,
|
|
2474
|
-
|
|
3051
|
+
join,
|
|
2475
3052
|
jsonify,
|
|
2476
3053
|
keysof,
|
|
2477
3054
|
merge,
|
|
2478
3055
|
pad,
|
|
2479
3056
|
pathify,
|
|
2480
3057
|
select,
|
|
3058
|
+
setpath,
|
|
2481
3059
|
setprop,
|
|
2482
3060
|
size,
|
|
2483
3061
|
slice,
|
|
@@ -2485,11 +3063,41 @@ export {
|
|
|
2485
3063
|
stringify,
|
|
2486
3064
|
transform,
|
|
2487
3065
|
typify,
|
|
3066
|
+
typename,
|
|
2488
3067
|
validate,
|
|
2489
3068
|
walk,
|
|
2490
3069
|
|
|
2491
|
-
|
|
2492
|
-
|
|
3070
|
+
SKIP,
|
|
3071
|
+
DELETE,
|
|
3072
|
+
|
|
3073
|
+
jm,
|
|
3074
|
+
jt,
|
|
3075
|
+
|
|
3076
|
+
T_any,
|
|
3077
|
+
T_noval,
|
|
3078
|
+
T_boolean,
|
|
3079
|
+
T_decimal,
|
|
3080
|
+
T_integer,
|
|
3081
|
+
T_number,
|
|
3082
|
+
T_string,
|
|
3083
|
+
T_function,
|
|
3084
|
+
T_symbol,
|
|
3085
|
+
T_null,
|
|
3086
|
+
T_list,
|
|
3087
|
+
T_map,
|
|
3088
|
+
T_instance,
|
|
3089
|
+
T_scalar,
|
|
3090
|
+
T_node,
|
|
3091
|
+
|
|
3092
|
+
M_KEYPRE,
|
|
3093
|
+
M_KEYPOST,
|
|
3094
|
+
M_VAL,
|
|
3095
|
+
|
|
3096
|
+
MODENAME,
|
|
3097
|
+
|
|
3098
|
+
checkPlacement,
|
|
3099
|
+
injectorArgs,
|
|
3100
|
+
injectChild,
|
|
2493
3101
|
}
|
|
2494
3102
|
|
|
2495
3103
|
export type {
|