@voxgig/sdkgen 0.25.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 +30 -11
- 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 +1113 -525
- 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,78 +982,134 @@ 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
|
+
}
|
|
1002
|
+
|
|
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 {
|
|
1010
|
+
|
|
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) ? [] : {}
|
|
762
1018
|
}
|
|
763
1019
|
|
|
764
|
-
//
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
cI = lenpath - 1
|
|
768
|
-
if (UNDEF === cur[cI]) {
|
|
769
|
-
cur[cI] = getpath(out, slice(path, 0, lenpath - 1))
|
|
1020
|
+
// Matching override and destination so continue with their values.
|
|
1021
|
+
else if (typify(val) === typify(tval)) {
|
|
1022
|
+
cur[pI] = tval
|
|
770
1023
|
}
|
|
771
1024
|
|
|
772
|
-
//
|
|
1025
|
+
// Override wins.
|
|
1026
|
+
else {
|
|
1027
|
+
cur[pI] = val
|
|
773
1028
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
cur[cI] = islist(parent) ? [] : {}
|
|
1029
|
+
// No need to descend when override wins (destination is discarded).
|
|
1030
|
+
val = NONE
|
|
777
1031
|
}
|
|
1032
|
+
}
|
|
778
1033
|
|
|
779
|
-
|
|
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))
|
|
780
1037
|
|
|
781
|
-
|
|
1038
|
+
return val
|
|
1039
|
+
}
|
|
782
1040
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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
|
|
1056
|
+
}
|
|
789
1057
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
1058
|
+
// Walk overriding node, creating paths in output as needed.
|
|
1059
|
+
out = walk(obj, before, after, maxdepth)
|
|
1060
|
+
// console.log('WALK-DONE', out, obj)
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
793
1063
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
}
|
|
1064
|
+
if (0 === md) {
|
|
1065
|
+
out = getelem(list, -1)
|
|
1066
|
+
out = islist(out) ? [] : ismap(out) ? {} : out
|
|
1067
|
+
}
|
|
799
1068
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
// console.log('DDD', cur[cI], key, val)
|
|
803
|
-
setprop(cur[cI], key, val)
|
|
804
|
-
}
|
|
1069
|
+
return out
|
|
1070
|
+
}
|
|
805
1071
|
|
|
806
|
-
return val
|
|
807
|
-
}
|
|
808
1072
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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)
|
|
812
1101
|
}
|
|
1102
|
+
parent = nextParent
|
|
813
1103
|
}
|
|
814
1104
|
|
|
815
|
-
|
|
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
|
|
816
1113
|
}
|
|
817
1114
|
|
|
818
1115
|
|
|
@@ -821,10 +1118,10 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
821
1118
|
// Operate on a string array.
|
|
822
1119
|
const parts = islist(path) ? path :
|
|
823
1120
|
'string' === typeof path ? path.split(S_DT) :
|
|
824
|
-
'number' === typeof path ? [strkey(path)] :
|
|
1121
|
+
'number' === typeof path ? [strkey(path)] : NONE
|
|
825
1122
|
|
|
826
|
-
if (
|
|
827
|
-
return
|
|
1123
|
+
if (NONE === parts) {
|
|
1124
|
+
return NONE
|
|
828
1125
|
}
|
|
829
1126
|
|
|
830
1127
|
// let root = store
|
|
@@ -856,7 +1153,7 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
856
1153
|
|
|
857
1154
|
const dpath = getprop(injdef, 'dpath')
|
|
858
1155
|
|
|
859
|
-
for (let pI = 0;
|
|
1156
|
+
for (let pI = 0; NONE !== val && pI < numparts; pI++) {
|
|
860
1157
|
let part = parts[pI]
|
|
861
1158
|
|
|
862
1159
|
if (injdef && S_DKEY === part) {
|
|
@@ -864,15 +1161,15 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
864
1161
|
}
|
|
865
1162
|
else if (injdef && part.startsWith('$GET:')) {
|
|
866
1163
|
// $GET:path$ -> get store value, use as path part (string)
|
|
867
|
-
part = stringify(getpath(src, part
|
|
1164
|
+
part = stringify(getpath(src, slice(part, 5, -1)))
|
|
868
1165
|
}
|
|
869
1166
|
else if (injdef && part.startsWith('$REF:')) {
|
|
870
1167
|
// $REF:refpath$ -> get spec value, use as path part (string)
|
|
871
|
-
part = stringify(getpath(getprop(store, S_DSPEC), part
|
|
1168
|
+
part = stringify(getpath(getprop(store, S_DSPEC), slice(part, 5, -1)))
|
|
872
1169
|
}
|
|
873
1170
|
else if (injdef && part.startsWith('$META:')) {
|
|
874
1171
|
// $META:metapath$ -> get meta value, use as path part (string)
|
|
875
|
-
part = stringify(getpath(getprop(injdef, 'meta'), part
|
|
1172
|
+
part = stringify(getpath(getprop(injdef, 'meta'), slice(part, 6, -1)))
|
|
876
1173
|
}
|
|
877
1174
|
|
|
878
1175
|
// $$ escapes $
|
|
@@ -895,14 +1192,16 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
895
1192
|
val = dparent
|
|
896
1193
|
}
|
|
897
1194
|
else {
|
|
898
|
-
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)])
|
|
899
1197
|
|
|
900
1198
|
if (ascends <= size(dpath)) {
|
|
901
1199
|
val = getpath(store, fullpath)
|
|
902
1200
|
}
|
|
903
1201
|
else {
|
|
904
|
-
val =
|
|
1202
|
+
val = NONE
|
|
905
1203
|
}
|
|
1204
|
+
|
|
906
1205
|
break
|
|
907
1206
|
}
|
|
908
1207
|
}
|
|
@@ -924,13 +1223,14 @@ function getpath(store: any, path: number | string | string[], injdef?: Partial<
|
|
|
924
1223
|
val = handler(injdef, val, ref, store)
|
|
925
1224
|
}
|
|
926
1225
|
|
|
1226
|
+
// console.log('GETPATH', path, val)
|
|
1227
|
+
|
|
927
1228
|
return val
|
|
928
1229
|
}
|
|
929
1230
|
|
|
930
1231
|
|
|
931
|
-
|
|
932
1232
|
// Inject values from a data store into a node recursively, resolving
|
|
933
|
-
// paths against the store, or current if they are local.
|
|
1233
|
+
// paths against the store, or current if they are local. The modify
|
|
934
1234
|
// argument allows custom modification of the result. The inj
|
|
935
1235
|
// (Injection) argument is used to maintain recursive state.
|
|
936
1236
|
function inject(
|
|
@@ -943,14 +1243,14 @@ function inject(
|
|
|
943
1243
|
|
|
944
1244
|
// Create state if at root of injection. The input value is placed
|
|
945
1245
|
// inside a virtual parent holder to simplify edge cases.
|
|
946
|
-
if (
|
|
1246
|
+
if (NONE === injdef || null == injdef.mode) {
|
|
947
1247
|
// Set up state assuming we are starting in the virtual parent.
|
|
948
1248
|
inj = new Injection(val, { [S_DTOP]: val })
|
|
949
1249
|
inj.dparent = store
|
|
950
1250
|
inj.errs = getprop(store, S_DERRS, [])
|
|
951
1251
|
inj.meta.__d = 0
|
|
952
1252
|
|
|
953
|
-
if (
|
|
1253
|
+
if (NONE !== injdef) {
|
|
954
1254
|
inj.modify = null == injdef.modify ? inj.modify : injdef.modify
|
|
955
1255
|
inj.extra = null == injdef.extra ? inj.extra : injdef.extra
|
|
956
1256
|
inj.meta = null == injdef.meta ? inj.meta : injdef.meta
|
|
@@ -960,6 +1260,9 @@ function inject(
|
|
|
960
1260
|
|
|
961
1261
|
inj.descend()
|
|
962
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
|
+
|
|
963
1266
|
// Descend into node.
|
|
964
1267
|
if (isnode(val)) {
|
|
965
1268
|
|
|
@@ -967,21 +1270,29 @@ function inject(
|
|
|
967
1270
|
// Injection transforms ($FOO) are processed *after* other keys.
|
|
968
1271
|
// NOTE: the optional digits suffix of the transform can thus be
|
|
969
1272
|
// used to order the transforms.
|
|
970
|
-
let nodekeys = ismap(val) ? [
|
|
971
|
-
...Object.keys(val).filter(k => !k.includes(S_DS)).sort(),
|
|
972
|
-
...Object.keys(val).filter(k => k.includes(S_DS)).sort(),
|
|
973
|
-
] : (val as any).map((_n: any, i: number) => i)
|
|
974
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
|
+
}
|
|
975
1286
|
|
|
976
1287
|
// Each child key-value pair is processed in three injection phases:
|
|
977
|
-
// 1. inj.mode=
|
|
978
|
-
// 2. inj.mode=
|
|
979
|
-
// 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.
|
|
980
1291
|
for (let nkI = 0; nkI < nodekeys.length; nkI++) {
|
|
981
1292
|
|
|
982
1293
|
const childinj = inj.child(nkI, nodekeys)
|
|
983
1294
|
const nodekey = childinj.key
|
|
984
|
-
childinj.mode =
|
|
1295
|
+
childinj.mode = M_KEYPRE
|
|
985
1296
|
|
|
986
1297
|
// Peform the key:pre mode injection on the child key.
|
|
987
1298
|
const prekey = _injectstr(nodekey, store, childinj)
|
|
@@ -991,9 +1302,9 @@ function inject(
|
|
|
991
1302
|
nodekeys = childinj.keys
|
|
992
1303
|
|
|
993
1304
|
// Prevent further processing by returning an undefined prekey
|
|
994
|
-
if (
|
|
1305
|
+
if (NONE !== prekey) {
|
|
995
1306
|
childinj.val = getprop(val, prekey)
|
|
996
|
-
childinj.mode =
|
|
1307
|
+
childinj.mode = M_VAL
|
|
997
1308
|
|
|
998
1309
|
// Perform the val mode injection on the child value.
|
|
999
1310
|
// NOTE: return value is not used.
|
|
@@ -1004,7 +1315,7 @@ function inject(
|
|
|
1004
1315
|
nodekeys = childinj.keys
|
|
1005
1316
|
|
|
1006
1317
|
// Peform the key:post mode injection on the child key.
|
|
1007
|
-
childinj.mode =
|
|
1318
|
+
childinj.mode = M_KEYPOST
|
|
1008
1319
|
_injectstr(nodekey, store, childinj)
|
|
1009
1320
|
|
|
1010
1321
|
// The injection may modify child processing.
|
|
@@ -1016,7 +1327,7 @@ function inject(
|
|
|
1016
1327
|
|
|
1017
1328
|
// Inject paths into string scalars.
|
|
1018
1329
|
else if (S_string === valtype) {
|
|
1019
|
-
inj.mode =
|
|
1330
|
+
inj.mode = M_VAL
|
|
1020
1331
|
val = _injectstr(val, store, inj)
|
|
1021
1332
|
if (SKIP !== val) {
|
|
1022
1333
|
inj.setval(val)
|
|
@@ -1038,6 +1349,8 @@ function inject(
|
|
|
1038
1349
|
)
|
|
1039
1350
|
}
|
|
1040
1351
|
|
|
1352
|
+
// console.log('INJ-VAL', val)
|
|
1353
|
+
|
|
1041
1354
|
inj.val = val
|
|
1042
1355
|
|
|
1043
1356
|
// Original val reference may no longer be correct.
|
|
@@ -1050,21 +1363,22 @@ function inject(
|
|
|
1050
1363
|
|
|
1051
1364
|
// Delete a key from a map or list.
|
|
1052
1365
|
const transform_DELETE: Injector = (inj: Injection) => {
|
|
1053
|
-
inj.setval(
|
|
1054
|
-
return
|
|
1366
|
+
inj.setval(NONE)
|
|
1367
|
+
return NONE
|
|
1055
1368
|
}
|
|
1056
1369
|
|
|
1057
1370
|
|
|
1058
1371
|
// Copy value from source data.
|
|
1059
1372
|
const transform_COPY: Injector = (inj: Injection, _val: any) => {
|
|
1060
|
-
const
|
|
1373
|
+
const ijname = 'COPY'
|
|
1061
1374
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
out = getprop(inj.dparent, key)
|
|
1065
|
-
inj.setval(out)
|
|
1375
|
+
if (!checkPlacement(M_VAL, ijname, T_any, inj)) {
|
|
1376
|
+
return NONE
|
|
1066
1377
|
}
|
|
1067
1378
|
|
|
1379
|
+
let out = getprop(inj.dparent, inj.key)
|
|
1380
|
+
inj.setval(out)
|
|
1381
|
+
|
|
1068
1382
|
return out
|
|
1069
1383
|
}
|
|
1070
1384
|
|
|
@@ -1074,29 +1388,30 @@ const transform_COPY: Injector = (inj: Injection, _val: any) => {
|
|
|
1074
1388
|
const transform_KEY: Injector = (inj: Injection) => {
|
|
1075
1389
|
const { mode, path, parent } = inj
|
|
1076
1390
|
|
|
1077
|
-
// Do nothing in val mode.
|
|
1078
|
-
if (
|
|
1079
|
-
return
|
|
1391
|
+
// Do nothing in val mode - not an error.
|
|
1392
|
+
if (M_VAL !== mode) {
|
|
1393
|
+
return NONE
|
|
1080
1394
|
}
|
|
1081
1395
|
|
|
1082
1396
|
// Key is defined by $KEY meta property.
|
|
1083
1397
|
const keyspec = getprop(parent, S_BKEY)
|
|
1084
|
-
if (
|
|
1398
|
+
if (NONE !== keyspec) {
|
|
1085
1399
|
delprop(parent, S_BKEY)
|
|
1086
1400
|
return getprop(inj.dparent, keyspec)
|
|
1087
1401
|
}
|
|
1088
1402
|
|
|
1089
1403
|
// Key is defined within general purpose $META object.
|
|
1090
|
-
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))
|
|
1091
1406
|
}
|
|
1092
1407
|
|
|
1093
1408
|
|
|
1094
|
-
//
|
|
1409
|
+
// Annotate node. Does nothing itself, just used by
|
|
1095
1410
|
// other injectors, and is removed when called.
|
|
1096
1411
|
const transform_ANNO: Injector = (inj: Injection) => {
|
|
1097
1412
|
const { parent } = inj
|
|
1098
1413
|
delprop(parent, S_BANNO)
|
|
1099
|
-
return
|
|
1414
|
+
return NONE
|
|
1100
1415
|
}
|
|
1101
1416
|
|
|
1102
1417
|
|
|
@@ -1109,29 +1424,27 @@ const transform_MERGE: Injector = (inj: Injection) => {
|
|
|
1109
1424
|
const { mode, key, parent } = inj
|
|
1110
1425
|
|
|
1111
1426
|
// Ensures $MERGE is removed from parent list (val mode).
|
|
1112
|
-
let out: any =
|
|
1427
|
+
let out: any = NONE
|
|
1113
1428
|
|
|
1114
|
-
if (
|
|
1429
|
+
if (M_KEYPRE === mode) {
|
|
1115
1430
|
out = key
|
|
1116
1431
|
}
|
|
1117
1432
|
|
|
1118
1433
|
// Operate after child values have been transformed.
|
|
1119
|
-
else if (
|
|
1434
|
+
else if (M_KEYPOST === mode) {
|
|
1120
1435
|
out = key
|
|
1121
1436
|
|
|
1122
1437
|
let args = getprop(parent, key)
|
|
1123
1438
|
args = Array.isArray(args) ? args : [args]
|
|
1124
1439
|
|
|
1125
1440
|
// Remove the $MERGE command from a parent map.
|
|
1126
|
-
inj.setval(
|
|
1441
|
+
inj.setval(NONE)
|
|
1127
1442
|
|
|
1128
1443
|
// Literals in the parent have precedence, but we still merge onto
|
|
1129
1444
|
// the parent object, so that node tree references are not changed.
|
|
1130
|
-
const mergelist = [parent,
|
|
1445
|
+
const mergelist = flatten([[parent], args, [clone(parent)]])
|
|
1131
1446
|
|
|
1132
1447
|
merge(mergelist)
|
|
1133
|
-
|
|
1134
|
-
// return key
|
|
1135
1448
|
}
|
|
1136
1449
|
|
|
1137
1450
|
return out
|
|
@@ -1146,65 +1459,63 @@ const transform_EACH: Injector = (
|
|
|
1146
1459
|
_ref: string,
|
|
1147
1460
|
store: any
|
|
1148
1461
|
) => {
|
|
1462
|
+
const ijname = 'EACH'
|
|
1149
1463
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
inj.keys.length = 1
|
|
1464
|
+
if (!checkPlacement(M_VAL, ijname, T_list, inj)) {
|
|
1465
|
+
return NONE
|
|
1153
1466
|
}
|
|
1154
1467
|
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
}
|
|
1468
|
+
// Remove remaining keys to avoid spurious processing.
|
|
1469
|
+
slice(inj.keys, 0, 1, true)
|
|
1158
1470
|
|
|
1159
|
-
//
|
|
1160
|
-
const srcpath =
|
|
1161
|
-
|
|
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
|
+
}
|
|
1162
1477
|
|
|
1163
1478
|
// Source data.
|
|
1164
1479
|
const srcstore = getprop(store, inj.base, store)
|
|
1165
1480
|
|
|
1166
1481
|
const src = getpath(srcstore, srcpath, inj)
|
|
1482
|
+
const srctype = typify(src)
|
|
1167
1483
|
|
|
1168
1484
|
// Create parallel data structures:
|
|
1169
1485
|
// source entries :: child templates
|
|
1170
1486
|
let tcur: any = []
|
|
1171
1487
|
let tval: any = []
|
|
1172
1488
|
|
|
1173
|
-
const tkey = inj.path
|
|
1174
|
-
const target = inj.nodes
|
|
1489
|
+
const tkey = getelem(inj.path, -2)
|
|
1490
|
+
const target = getelem(inj.nodes, - 2, () => getelem(inj.nodes, -1))
|
|
1175
1491
|
|
|
1176
1492
|
// Create clones of the child template for each value of the current soruce.
|
|
1177
|
-
if (
|
|
1178
|
-
tval = src
|
|
1493
|
+
if (0 < (T_list & srctype)) {
|
|
1494
|
+
tval = items(src, () => clone(child))
|
|
1179
1495
|
}
|
|
1180
|
-
else if (
|
|
1181
|
-
tval =
|
|
1182
|
-
|
|
1183
|
-
|
|
1496
|
+
else if (0 < (T_map & srctype)) {
|
|
1497
|
+
tval = items(src, (n => merge([
|
|
1498
|
+
clone(child),
|
|
1184
1499
|
// Make a note of the key for $KEY transforms.
|
|
1185
|
-
[S_BANNO]: { KEY: n[0] }
|
|
1186
|
-
|
|
1500
|
+
{ [S_BANNO]: { KEY: n[0] } }
|
|
1501
|
+
], 1)))
|
|
1187
1502
|
}
|
|
1188
1503
|
|
|
1189
1504
|
let rval = []
|
|
1190
1505
|
|
|
1191
1506
|
if (0 < size(tval)) {
|
|
1192
|
-
tcur = null == src ?
|
|
1507
|
+
tcur = null == src ? NONE : Object.values(src)
|
|
1193
1508
|
|
|
1194
1509
|
const ckey = getelem(inj.path, -2)
|
|
1195
1510
|
|
|
1196
1511
|
const tpath = slice(inj.path, -1)
|
|
1197
|
-
const dpath = [S_DTOP,
|
|
1198
|
-
|
|
1512
|
+
const dpath = flatten([S_DTOP, srcpath.split(S_DT), '$:' + ckey])
|
|
1199
1513
|
|
|
1200
1514
|
// Parent structure.
|
|
1201
|
-
|
|
1202
|
-
// const ckey = getelem(cpath, -1)
|
|
1203
1515
|
tcur = { [ckey]: tcur }
|
|
1204
1516
|
|
|
1205
|
-
if (1 < tpath
|
|
1517
|
+
if (1 < size(tpath)) {
|
|
1206
1518
|
const pkey = getelem(inj.path, -3, S_DTOP)
|
|
1207
|
-
// const pkey = getelem(cpath, -2, S_DTOP)
|
|
1208
1519
|
tcur = { [pkey]: tcur }
|
|
1209
1520
|
dpath.push('$:' + pkey)
|
|
1210
1521
|
}
|
|
@@ -1224,7 +1535,8 @@ const transform_EACH: Injector = (
|
|
|
1224
1535
|
rval = tinj.val
|
|
1225
1536
|
}
|
|
1226
1537
|
|
|
1227
|
-
_updateAncestors(inj, target, tkey, rval)
|
|
1538
|
+
// _updateAncestors(inj, target, tkey, rval)
|
|
1539
|
+
setprop(target, tkey, rval)
|
|
1228
1540
|
|
|
1229
1541
|
// Prevent callee from damaging first list entry (since we are in `val` mode).
|
|
1230
1542
|
return rval[0]
|
|
@@ -1232,7 +1544,7 @@ const transform_EACH: Injector = (
|
|
|
1232
1544
|
|
|
1233
1545
|
|
|
1234
1546
|
// Convert a node to a map.
|
|
1235
|
-
// Format: { '`$PACK`':['
|
|
1547
|
+
// Format: { '`$PACK`':['source-path', child-template]}
|
|
1236
1548
|
const transform_PACK: Injector = (
|
|
1237
1549
|
inj: Injection,
|
|
1238
1550
|
_val: any,
|
|
@@ -1241,78 +1553,105 @@ const transform_PACK: Injector = (
|
|
|
1241
1553
|
) => {
|
|
1242
1554
|
const { mode, key, path, parent, nodes } = inj
|
|
1243
1555
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1556
|
+
const ijname = 'EACH'
|
|
1557
|
+
|
|
1558
|
+
if (!checkPlacement(M_KEYPRE, ijname, T_map, inj)) {
|
|
1559
|
+
return NONE
|
|
1247
1560
|
}
|
|
1248
1561
|
|
|
1249
1562
|
// Get arguments.
|
|
1250
|
-
const args = parent
|
|
1251
|
-
const srcpath =
|
|
1252
|
-
|
|
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
|
+
}
|
|
1253
1569
|
|
|
1254
1570
|
// Find key and target node.
|
|
1255
|
-
const keyprop = child[S_BKEY]
|
|
1256
1571
|
const tkey = getelem(path, -2)
|
|
1257
|
-
const
|
|
1572
|
+
const pathsize = size(path)
|
|
1573
|
+
const target = getelem(nodes, pathsize - 2, () => getelem(nodes, pathsize - 1))
|
|
1258
1574
|
|
|
1259
1575
|
// Source data
|
|
1260
1576
|
const srcstore = getprop(store, inj.base, store)
|
|
1261
|
-
|
|
1262
1577
|
let src = getpath(srcstore, srcpath, inj)
|
|
1263
1578
|
|
|
1264
1579
|
// Prepare source as a list.
|
|
1265
|
-
|
|
1266
|
-
ismap(src)
|
|
1267
|
-
|
|
1268
|
-
(
|
|
1269
|
-
|
|
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
|
+
}
|
|
1270
1591
|
|
|
1271
1592
|
if (null == src) {
|
|
1272
|
-
return
|
|
1593
|
+
return NONE
|
|
1273
1594
|
}
|
|
1274
1595
|
|
|
1275
|
-
// Get
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
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)
|
|
1279
1601
|
|
|
1280
1602
|
// Build parallel target object.
|
|
1281
1603
|
let tval: any = {}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
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)
|
|
1289
1625
|
}
|
|
1290
1626
|
else {
|
|
1291
|
-
setprop(
|
|
1627
|
+
setprop(tchild, S_BANNO, anno)
|
|
1292
1628
|
}
|
|
1293
|
-
|
|
1294
|
-
}, tval)
|
|
1629
|
+
})
|
|
1295
1630
|
|
|
1296
1631
|
let rval = {}
|
|
1297
1632
|
|
|
1298
|
-
if (
|
|
1633
|
+
if (!isempty(tval)) {
|
|
1299
1634
|
|
|
1300
1635
|
// Build parallel source object.
|
|
1301
|
-
let
|
|
1302
|
-
src.reduce((a: any, n: any) => {
|
|
1303
|
-
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
|
+
|
|
1304
1643
|
setprop(a, kn, n)
|
|
1305
1644
|
return a
|
|
1306
|
-
},
|
|
1645
|
+
}, tsrc)
|
|
1307
1646
|
|
|
1308
1647
|
const tpath = slice(inj.path, -1)
|
|
1309
1648
|
|
|
1310
1649
|
const ckey = getelem(inj.path, -2)
|
|
1311
|
-
const dpath = [S_DTOP,
|
|
1650
|
+
const dpath = flatten([S_DTOP, srcpath.split(S_DT), '$:' + ckey])
|
|
1312
1651
|
|
|
1313
|
-
tcur = { [ckey]:
|
|
1652
|
+
let tcur = { [ckey]: tsrc }
|
|
1314
1653
|
|
|
1315
|
-
if (1 < tpath
|
|
1654
|
+
if (1 < size(tpath)) {
|
|
1316
1655
|
const pkey = getelem(inj.path, -3, S_DTOP)
|
|
1317
1656
|
tcur = { [pkey]: tcur }
|
|
1318
1657
|
dpath.push('$:' + pkey)
|
|
@@ -1322,7 +1661,6 @@ const transform_PACK: Injector = (
|
|
|
1322
1661
|
tinj.path = tpath
|
|
1323
1662
|
tinj.nodes = slice(inj.nodes, -1)
|
|
1324
1663
|
|
|
1325
|
-
// tinj.parent = tcur
|
|
1326
1664
|
tinj.parent = getelem(tinj.nodes, -1)
|
|
1327
1665
|
tinj.val = tval
|
|
1328
1666
|
|
|
@@ -1333,14 +1671,15 @@ const transform_PACK: Injector = (
|
|
|
1333
1671
|
rval = tinj.val
|
|
1334
1672
|
}
|
|
1335
1673
|
|
|
1336
|
-
_updateAncestors(inj, target, tkey, rval)
|
|
1674
|
+
// _updateAncestors(inj, target, tkey, rval)
|
|
1675
|
+
setprop(target, tkey, rval)
|
|
1337
1676
|
|
|
1338
1677
|
// Drop transform key.
|
|
1339
|
-
return
|
|
1678
|
+
return NONE
|
|
1340
1679
|
}
|
|
1341
1680
|
|
|
1342
1681
|
|
|
1343
|
-
// TODO: not found ref should removed key (setprop
|
|
1682
|
+
// TODO: not found ref should removed key (setprop NONE)
|
|
1344
1683
|
// Reference original spec (enables recursice transformations)
|
|
1345
1684
|
// Format: ['`$REF`', '`spec-path`']
|
|
1346
1685
|
const transform_REF: Injector = (
|
|
@@ -1351,21 +1690,24 @@ const transform_REF: Injector = (
|
|
|
1351
1690
|
) => {
|
|
1352
1691
|
const { nodes } = inj
|
|
1353
1692
|
|
|
1354
|
-
if (
|
|
1355
|
-
return
|
|
1693
|
+
if (M_VAL !== inj.mode) {
|
|
1694
|
+
return NONE
|
|
1356
1695
|
}
|
|
1357
1696
|
|
|
1358
1697
|
// Get arguments: ['`$REF`', 'ref-path'].
|
|
1359
1698
|
const refpath = getprop(inj.parent, 1)
|
|
1360
|
-
inj.keyI = inj.keys
|
|
1699
|
+
inj.keyI = size(inj.keys)
|
|
1361
1700
|
|
|
1362
1701
|
// Spec reference.
|
|
1363
1702
|
const spec = getprop(store, S_DSPEC)()
|
|
1364
1703
|
|
|
1704
|
+
const dpath = slice(inj.path, 1)
|
|
1365
1705
|
const ref = getpath(spec, refpath, {
|
|
1366
1706
|
// TODO: test relative refs
|
|
1367
|
-
dpath: inj.path.slice(1),
|
|
1368
|
-
|
|
1707
|
+
// dpath: inj.path.slice(1),
|
|
1708
|
+
dpath,
|
|
1709
|
+
// dparent: getpath(spec, inj.path.slice(1))
|
|
1710
|
+
dparent: getpath(spec, dpath),
|
|
1369
1711
|
})
|
|
1370
1712
|
|
|
1371
1713
|
let hasSubRef = false
|
|
@@ -1384,9 +1726,9 @@ const transform_REF: Injector = (
|
|
|
1384
1726
|
const tpath = slice(inj.path, -1)
|
|
1385
1727
|
let tcur = getpath(store, cpath)
|
|
1386
1728
|
let tval = getpath(store, tpath)
|
|
1387
|
-
let rval =
|
|
1729
|
+
let rval = NONE
|
|
1388
1730
|
|
|
1389
|
-
if (!hasSubRef ||
|
|
1731
|
+
if (!hasSubRef || NONE !== tval) {
|
|
1390
1732
|
const tinj = inj.child(0, [getelem(tpath, -1)])
|
|
1391
1733
|
|
|
1392
1734
|
tinj.path = tpath
|
|
@@ -1394,7 +1736,7 @@ const transform_REF: Injector = (
|
|
|
1394
1736
|
tinj.parent = getelem(nodes, -2)
|
|
1395
1737
|
tinj.val = tref
|
|
1396
1738
|
|
|
1397
|
-
tinj.dpath = [
|
|
1739
|
+
tinj.dpath = flatten([cpath])
|
|
1398
1740
|
tinj.dparent = tcur
|
|
1399
1741
|
|
|
1400
1742
|
inject(tref, store, tinj)
|
|
@@ -1402,7 +1744,7 @@ const transform_REF: Injector = (
|
|
|
1402
1744
|
rval = tinj.val
|
|
1403
1745
|
}
|
|
1404
1746
|
else {
|
|
1405
|
-
rval =
|
|
1747
|
+
rval = NONE
|
|
1406
1748
|
}
|
|
1407
1749
|
|
|
1408
1750
|
const grandparent = inj.setval(rval, 2)
|
|
@@ -1415,6 +1757,117 @@ const transform_REF: Injector = (
|
|
|
1415
1757
|
}
|
|
1416
1758
|
|
|
1417
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
|
+
|
|
1418
1871
|
// Transform data using spec.
|
|
1419
1872
|
// Only operates on static JSON-like data.
|
|
1420
1873
|
// Arrays are treated as if they are objects with indices as keys.
|
|
@@ -1428,51 +1881,66 @@ function transform(
|
|
|
1428
1881
|
spec = clone(origspec)
|
|
1429
1882
|
|
|
1430
1883
|
const extra = injdef?.extra
|
|
1431
|
-
|
|
1884
|
+
|
|
1885
|
+
const collect = null != injdef?.errs
|
|
1886
|
+
const errs = injdef?.errs || []
|
|
1432
1887
|
|
|
1433
1888
|
const extraTransforms: any = {}
|
|
1434
|
-
const extraData = null == extra ?
|
|
1889
|
+
const extraData = null == extra ? NONE : items(extra)
|
|
1435
1890
|
.reduce((a: any, n: any[]) =>
|
|
1436
1891
|
(n[0].startsWith(S_DS) ? extraTransforms[n[0]] = n[1] : (a[n[0]] = n[1]), a), {})
|
|
1437
1892
|
|
|
1438
1893
|
const dataClone = merge([
|
|
1439
|
-
isempty(extraData) ?
|
|
1894
|
+
isempty(extraData) ? NONE : clone(extraData),
|
|
1440
1895
|
clone(data),
|
|
1441
1896
|
])
|
|
1442
1897
|
|
|
1443
1898
|
// Define a top level store that provides transform operations.
|
|
1444
|
-
const store =
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
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
|
+
},
|
|
1450
1928
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
// Escape backtick (this also works inside backticks).
|
|
1454
|
-
$BT: () => S_BT,
|
|
1455
|
-
|
|
1456
|
-
// Escape dollar sign (this also works inside backticks).
|
|
1457
|
-
$DS: () => S_DS,
|
|
1929
|
+
// Custom extra transforms, if any.
|
|
1930
|
+
extraTransforms,
|
|
1458
1931
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1932
|
+
{
|
|
1933
|
+
$ERRS: errs,
|
|
1934
|
+
}
|
|
1935
|
+
], 1)
|
|
1461
1936
|
|
|
1462
|
-
|
|
1463
|
-
$COPY: transform_COPY,
|
|
1464
|
-
$KEY: transform_KEY,
|
|
1465
|
-
$ANNO: transform_ANNO,
|
|
1466
|
-
$MERGE: transform_MERGE,
|
|
1467
|
-
$EACH: transform_EACH,
|
|
1468
|
-
$PACK: transform_PACK,
|
|
1469
|
-
$REF: transform_REF,
|
|
1937
|
+
const out = inject(spec, store, injdef)
|
|
1470
1938
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1939
|
+
const generr = (0 < size(errs) && !collect)
|
|
1940
|
+
if (generr) {
|
|
1941
|
+
throw new Error(join(errs, ' | '))
|
|
1473
1942
|
}
|
|
1474
1943
|
|
|
1475
|
-
const out = inject(spec, store, injdef)
|
|
1476
1944
|
return out
|
|
1477
1945
|
}
|
|
1478
1946
|
|
|
@@ -1482,86 +1950,36 @@ const validate_STRING: Injector = (inj: Injection) => {
|
|
|
1482
1950
|
let out = getprop(inj.dparent, inj.key)
|
|
1483
1951
|
|
|
1484
1952
|
const t = typify(out)
|
|
1485
|
-
if (
|
|
1953
|
+
if (0 === (T_string & t)) {
|
|
1486
1954
|
let msg = _invalidTypeMsg(inj.path, S_string, t, out, 'V1010')
|
|
1487
1955
|
inj.errs.push(msg)
|
|
1488
|
-
return
|
|
1956
|
+
return NONE
|
|
1489
1957
|
}
|
|
1490
1958
|
|
|
1491
1959
|
if (S_MT === out) {
|
|
1492
1960
|
let msg = 'Empty string at ' + pathify(inj.path, 1)
|
|
1493
1961
|
inj.errs.push(msg)
|
|
1494
|
-
return
|
|
1962
|
+
return NONE
|
|
1495
1963
|
}
|
|
1496
1964
|
|
|
1497
1965
|
return out
|
|
1498
1966
|
}
|
|
1499
1967
|
|
|
1500
1968
|
|
|
1501
|
-
// A required number value (int or float).
|
|
1502
|
-
const validate_NUMBER: Injector = (inj: Injection) => {
|
|
1503
|
-
let out = getprop(inj.dparent, inj.key)
|
|
1504
1969
|
|
|
1505
|
-
const t = typify(out)
|
|
1506
|
-
if (S_number !== t) {
|
|
1507
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_number, t, out, 'V1020'))
|
|
1508
|
-
return UNDEF
|
|
1509
|
-
}
|
|
1510
1970
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
// A required boolean value.
|
|
1516
|
-
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))
|
|
1517
1974
|
let out = getprop(inj.dparent, inj.key)
|
|
1518
1975
|
|
|
1519
1976
|
const t = typify(out)
|
|
1520
|
-
if (S_boolean !== t) {
|
|
1521
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_boolean, t, out, 'V1030'))
|
|
1522
|
-
return UNDEF
|
|
1523
|
-
}
|
|
1524
1977
|
|
|
1525
|
-
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
// A required object (map) value (contents not validated).
|
|
1530
|
-
const validate_OBJECT: Injector = (inj: Injection) => {
|
|
1531
|
-
let out = getprop(inj.dparent, inj.key)
|
|
1978
|
+
// console.log('TYPE', tname, typev, tn(typev), 'O=', t, tn(t), out, 'C=', t & typev)
|
|
1532
1979
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
return UNDEF
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
return out
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
// A required array (list) value (contents not validated).
|
|
1544
|
-
const validate_ARRAY: Injector = (inj: Injection) => {
|
|
1545
|
-
let out = getprop(inj.dparent, inj.key)
|
|
1546
|
-
|
|
1547
|
-
const t = typify(out)
|
|
1548
|
-
if (t !== S_array) {
|
|
1549
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_array, t, out, 'V1050'))
|
|
1550
|
-
return UNDEF
|
|
1551
|
-
}
|
|
1552
|
-
|
|
1553
|
-
return out
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
// A required function value.
|
|
1558
|
-
const validate_FUNCTION: Injector = (inj: Injection) => {
|
|
1559
|
-
let out = getprop(inj.dparent, inj.key)
|
|
1560
|
-
|
|
1561
|
-
const t = typify(out)
|
|
1562
|
-
if (S_function !== t) {
|
|
1563
|
-
inj.errs.push(_invalidTypeMsg(inj.path, S_function, t, out, 'V1060'))
|
|
1564
|
-
return UNDEF
|
|
1980
|
+
if (0 === (t & typev)) {
|
|
1981
|
+
inj.errs.push(_invalidTypeMsg(inj.path, tname, t, out, 'V1001'))
|
|
1982
|
+
return NONE
|
|
1565
1983
|
}
|
|
1566
1984
|
|
|
1567
1985
|
return out
|
|
@@ -1585,20 +2003,20 @@ const validate_CHILD: Injector = (inj: Injection) => {
|
|
|
1585
2003
|
// Setup data structures for validation by cloning child template.
|
|
1586
2004
|
|
|
1587
2005
|
// Map syntax.
|
|
1588
|
-
if (
|
|
2006
|
+
if (M_KEYPRE === mode) {
|
|
1589
2007
|
const childtm = getprop(parent, key)
|
|
1590
2008
|
|
|
1591
2009
|
// Get corresponding current object.
|
|
1592
|
-
const pkey =
|
|
2010
|
+
const pkey = getelem(path, -2)
|
|
1593
2011
|
let tval = getprop(inj.dparent, pkey)
|
|
1594
2012
|
|
|
1595
|
-
if (
|
|
2013
|
+
if (NONE == tval) {
|
|
1596
2014
|
tval = {}
|
|
1597
2015
|
}
|
|
1598
2016
|
else if (!ismap(tval)) {
|
|
1599
2017
|
inj.errs.push(_invalidTypeMsg(
|
|
1600
2018
|
slice(inj.path, -1), S_object, typify(tval), tval), 'V0220')
|
|
1601
|
-
return
|
|
2019
|
+
return NONE
|
|
1602
2020
|
}
|
|
1603
2021
|
|
|
1604
2022
|
const ckeys = keysof(tval)
|
|
@@ -1610,52 +2028,56 @@ const validate_CHILD: Injector = (inj: Injection) => {
|
|
|
1610
2028
|
}
|
|
1611
2029
|
|
|
1612
2030
|
// Remove $CHILD to cleanup ouput.
|
|
1613
|
-
inj.setval(
|
|
1614
|
-
return
|
|
2031
|
+
inj.setval(NONE)
|
|
2032
|
+
return NONE
|
|
1615
2033
|
}
|
|
1616
2034
|
|
|
1617
2035
|
// List syntax.
|
|
1618
|
-
if (
|
|
2036
|
+
if (M_VAL === mode) {
|
|
1619
2037
|
|
|
1620
2038
|
if (!islist(parent)) {
|
|
1621
2039
|
// $CHILD was not inside a list.
|
|
1622
2040
|
inj.errs.push('Invalid $CHILD as value')
|
|
1623
|
-
return
|
|
2041
|
+
return NONE
|
|
1624
2042
|
}
|
|
1625
2043
|
|
|
1626
2044
|
const childtm = getprop(parent, 1)
|
|
1627
2045
|
|
|
1628
|
-
if (
|
|
2046
|
+
if (NONE === inj.dparent) {
|
|
1629
2047
|
// Empty list as default.
|
|
1630
|
-
parent.length = 0
|
|
1631
|
-
|
|
2048
|
+
// parent.length = 0
|
|
2049
|
+
slice(parent, 0, 0, true)
|
|
2050
|
+
return NONE
|
|
1632
2051
|
}
|
|
1633
2052
|
|
|
1634
2053
|
if (!islist(inj.dparent)) {
|
|
1635
2054
|
const msg = _invalidTypeMsg(
|
|
1636
|
-
slice(inj.path, -1),
|
|
2055
|
+
slice(inj.path, -1), S_list, typify(inj.dparent), inj.dparent, 'V0230')
|
|
1637
2056
|
inj.errs.push(msg)
|
|
1638
|
-
inj.keyI = parent
|
|
2057
|
+
inj.keyI = size(parent)
|
|
1639
2058
|
return inj.dparent
|
|
1640
2059
|
}
|
|
1641
2060
|
|
|
1642
2061
|
// Clone children abd reset inj key index.
|
|
1643
2062
|
// The inject child loop will now iterate over the cloned children,
|
|
1644
2063
|
// validating them againt the current list values.
|
|
1645
|
-
|
|
1646
|
-
inj.dparent.
|
|
1647
|
-
parent.length = inj.dparent.length
|
|
2064
|
+
items(inj.dparent, (n) => setprop(parent, n[0], clone(childtm)))
|
|
2065
|
+
slice(parent, 0, inj.dparent.length, true)
|
|
1648
2066
|
inj.keyI = 0
|
|
2067
|
+
|
|
1649
2068
|
const out = getprop(inj.dparent, 0)
|
|
1650
2069
|
return out
|
|
1651
2070
|
}
|
|
1652
2071
|
|
|
1653
|
-
return
|
|
2072
|
+
return NONE
|
|
1654
2073
|
}
|
|
1655
2074
|
|
|
1656
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
|
|
1657
2079
|
// Match at least one of the specified shapes.
|
|
1658
|
-
// Syntax: ['`$ONE`', alt0, alt1, ...]
|
|
2080
|
+
// Syntax: ['`$ONE`', alt0, alt1, ...]
|
|
1659
2081
|
const validate_ONE: Injector = (
|
|
1660
2082
|
inj: Injection,
|
|
1661
2083
|
_val: any,
|
|
@@ -1665,7 +2087,7 @@ const validate_ONE: Injector = (
|
|
|
1665
2087
|
const { mode, parent, keyI } = inj
|
|
1666
2088
|
|
|
1667
2089
|
// Only operate in val mode, since parent is a list.
|
|
1668
|
-
if (
|
|
2090
|
+
if (M_VAL === mode) {
|
|
1669
2091
|
if (!islist(parent) || 0 !== keyI) {
|
|
1670
2092
|
inj.errs.push('The $ONE validator at field ' +
|
|
1671
2093
|
pathify(inj.path, 1, 1) +
|
|
@@ -1673,7 +2095,7 @@ const validate_ONE: Injector = (
|
|
|
1673
2095
|
return
|
|
1674
2096
|
}
|
|
1675
2097
|
|
|
1676
|
-
inj.keyI = inj.keys
|
|
2098
|
+
inj.keyI = size(inj.keys)
|
|
1677
2099
|
|
|
1678
2100
|
// Clean up structure, replacing [$ONE, ...] with current
|
|
1679
2101
|
inj.setval(inj.dparent, 2)
|
|
@@ -1682,7 +2104,7 @@ const validate_ONE: Injector = (
|
|
|
1682
2104
|
inj.key = getelem(inj.path, -1)
|
|
1683
2105
|
|
|
1684
2106
|
let tvals = slice(parent, 1)
|
|
1685
|
-
if (0 === tvals
|
|
2107
|
+
if (0 === size(tvals)) {
|
|
1686
2108
|
inj.errs.push('The $ONE validator at field ' +
|
|
1687
2109
|
pathify(inj.path, 1, 1) +
|
|
1688
2110
|
' must have at least one argument.')
|
|
@@ -1695,7 +2117,7 @@ const validate_ONE: Injector = (
|
|
|
1695
2117
|
// If match, then errs.length = 0
|
|
1696
2118
|
let terrs: any[] = []
|
|
1697
2119
|
|
|
1698
|
-
const vstore = {
|
|
2120
|
+
const vstore = merge([{}, store], 1)
|
|
1699
2121
|
vstore.$TOP = inj.dparent
|
|
1700
2122
|
|
|
1701
2123
|
const vcurrent = validate(inj.dparent, tval, {
|
|
@@ -1707,21 +2129,19 @@ const validate_ONE: Injector = (
|
|
|
1707
2129
|
inj.setval(vcurrent, -2)
|
|
1708
2130
|
|
|
1709
2131
|
// Accept current value if there was a match
|
|
1710
|
-
if (0 === terrs
|
|
2132
|
+
if (0 === size(terrs)) {
|
|
1711
2133
|
return
|
|
1712
2134
|
}
|
|
1713
2135
|
}
|
|
1714
2136
|
|
|
1715
2137
|
// There was no match.
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
.join(', ')
|
|
1720
|
-
.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())
|
|
1721
2141
|
|
|
1722
2142
|
inj.errs.push(_invalidTypeMsg(
|
|
1723
2143
|
inj.path,
|
|
1724
|
-
(1 < tvals
|
|
2144
|
+
(1 < size(tvals) ? 'one of ' : '') + valdesc,
|
|
1725
2145
|
typify(inj.dparent), inj.dparent, 'V0210'))
|
|
1726
2146
|
}
|
|
1727
2147
|
}
|
|
@@ -1731,7 +2151,7 @@ const validate_EXACT: Injector = (inj: Injection) => {
|
|
|
1731
2151
|
const { mode, parent, key, keyI } = inj
|
|
1732
2152
|
|
|
1733
2153
|
// Only operate in val mode, since parent is a list.
|
|
1734
|
-
if (
|
|
2154
|
+
if (M_VAL === mode) {
|
|
1735
2155
|
if (!islist(parent) || 0 !== keyI) {
|
|
1736
2156
|
inj.errs.push('The $EXACT validator at field ' +
|
|
1737
2157
|
pathify(inj.path, 1, 1) +
|
|
@@ -1739,16 +2159,17 @@ const validate_EXACT: Injector = (inj: Injection) => {
|
|
|
1739
2159
|
return
|
|
1740
2160
|
}
|
|
1741
2161
|
|
|
1742
|
-
inj.keyI = inj.keys
|
|
2162
|
+
inj.keyI = size(inj.keys)
|
|
1743
2163
|
|
|
1744
2164
|
// Clean up structure, replacing [$EXACT, ...] with current data parent
|
|
1745
2165
|
inj.setval(inj.dparent, 2)
|
|
1746
2166
|
|
|
1747
|
-
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)
|
|
1748
2169
|
inj.key = getelem(inj.path, -1)
|
|
1749
2170
|
|
|
1750
2171
|
let tvals = slice(parent, 1)
|
|
1751
|
-
if (0 === tvals
|
|
2172
|
+
if (0 === size(tvals)) {
|
|
1752
2173
|
inj.errs.push('The $EXACT validator at field ' +
|
|
1753
2174
|
pathify(inj.path, 1, 1) +
|
|
1754
2175
|
' must have at least one argument.')
|
|
@@ -1771,15 +2192,15 @@ const validate_EXACT: Injector = (inj: Injection) => {
|
|
|
1771
2192
|
}
|
|
1772
2193
|
}
|
|
1773
2194
|
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
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())
|
|
1778
2199
|
|
|
1779
2200
|
inj.errs.push(_invalidTypeMsg(
|
|
1780
2201
|
inj.path,
|
|
1781
|
-
(1 < inj.path
|
|
1782
|
-
'exactly equal to ' + (1 === tvals
|
|
2202
|
+
(1 < size(inj.path) ? '' : 'value ') +
|
|
2203
|
+
'exactly equal to ' + (1 === size(tvals) ? '' : 'one of ') + valdesc,
|
|
1783
2204
|
typify(inj.dparent), inj.dparent, 'V0110'))
|
|
1784
2205
|
}
|
|
1785
2206
|
else {
|
|
@@ -1797,7 +2218,7 @@ const _validation: Modify = (
|
|
|
1797
2218
|
inj?: Injection,
|
|
1798
2219
|
) => {
|
|
1799
2220
|
|
|
1800
|
-
if (
|
|
2221
|
+
if (NONE === inj) {
|
|
1801
2222
|
return
|
|
1802
2223
|
}
|
|
1803
2224
|
|
|
@@ -1811,28 +2232,28 @@ const _validation: Modify = (
|
|
|
1811
2232
|
// Current val to verify.
|
|
1812
2233
|
const cval = getprop(inj.dparent, key)
|
|
1813
2234
|
|
|
1814
|
-
if (
|
|
2235
|
+
if (NONE === inj || (!exact && NONE === cval)) {
|
|
1815
2236
|
return
|
|
1816
2237
|
}
|
|
1817
2238
|
|
|
1818
2239
|
const ptype = typify(pval)
|
|
1819
2240
|
|
|
1820
2241
|
// Delete any special commands remaining.
|
|
1821
|
-
if (
|
|
2242
|
+
if (0 < (T_string & ptype) && pval.includes(S_DS)) {
|
|
1822
2243
|
return
|
|
1823
2244
|
}
|
|
1824
2245
|
|
|
1825
2246
|
const ctype = typify(cval)
|
|
1826
2247
|
|
|
1827
2248
|
// Type mismatch.
|
|
1828
|
-
if (ptype !== ctype &&
|
|
1829
|
-
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'))
|
|
1830
2251
|
return
|
|
1831
2252
|
}
|
|
1832
2253
|
|
|
1833
2254
|
if (ismap(cval)) {
|
|
1834
2255
|
if (!ismap(pval)) {
|
|
1835
|
-
inj.errs.push(_invalidTypeMsg(inj.path, ptype, ctype, cval, 'V0020'))
|
|
2256
|
+
inj.errs.push(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0020'))
|
|
1836
2257
|
return
|
|
1837
2258
|
}
|
|
1838
2259
|
|
|
@@ -1840,7 +2261,7 @@ const _validation: Modify = (
|
|
|
1840
2261
|
const pkeys = keysof(pval)
|
|
1841
2262
|
|
|
1842
2263
|
// Empty spec object {} means object can be open (any keys).
|
|
1843
|
-
if (0 < pkeys
|
|
2264
|
+
if (0 < size(pkeys) && true !== getprop(pval, '`$OPEN`')) {
|
|
1844
2265
|
const badkeys = []
|
|
1845
2266
|
for (let ckey of ckeys) {
|
|
1846
2267
|
if (!haskey(pval, ckey)) {
|
|
@@ -1849,9 +2270,9 @@ const _validation: Modify = (
|
|
|
1849
2270
|
}
|
|
1850
2271
|
|
|
1851
2272
|
// Closed object, so reject extra keys not in shape.
|
|
1852
|
-
if (0 < badkeys
|
|
2273
|
+
if (0 < size(badkeys)) {
|
|
1853
2274
|
const msg =
|
|
1854
|
-
'Unexpected keys at field ' + pathify(inj.path, 1) + S_VIZ +
|
|
2275
|
+
'Unexpected keys at field ' + pathify(inj.path, 1) + S_VIZ + join(badkeys, ', ')
|
|
1855
2276
|
inj.errs.push(msg)
|
|
1856
2277
|
}
|
|
1857
2278
|
}
|
|
@@ -1865,7 +2286,7 @@ const _validation: Modify = (
|
|
|
1865
2286
|
}
|
|
1866
2287
|
else if (islist(cval)) {
|
|
1867
2288
|
if (!islist(pval)) {
|
|
1868
|
-
inj.errs.push(_invalidTypeMsg(inj.path, ptype, ctype, cval, 'V0030'))
|
|
2289
|
+
inj.errs.push(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0030'))
|
|
1869
2290
|
}
|
|
1870
2291
|
}
|
|
1871
2292
|
else if (exact) {
|
|
@@ -1905,50 +2326,57 @@ function validate(
|
|
|
1905
2326
|
const collect = null != injdef?.errs
|
|
1906
2327
|
const errs = injdef?.errs || []
|
|
1907
2328
|
|
|
1908
|
-
const store =
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
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, {}),
|
|
1930
2358
|
|
|
1931
2359
|
// A special top level value to collect errors.
|
|
1932
|
-
// NOTE: collecterrs
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
2360
|
+
// NOTE: collecterrs parameter always wins.
|
|
2361
|
+
{
|
|
2362
|
+
$ERRS: errs,
|
|
2363
|
+
}
|
|
2364
|
+
], 1)
|
|
1937
2365
|
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
}
|
|
2366
|
+
let meta = getprop(injdef, 'meta', {})
|
|
2367
|
+
setprop(meta, S_BEXACT, getprop(meta, S_BEXACT, false))
|
|
1941
2368
|
|
|
1942
2369
|
const out = transform(data, spec, {
|
|
1943
2370
|
meta,
|
|
1944
2371
|
extra: store,
|
|
1945
2372
|
modify: _validation,
|
|
1946
|
-
handler: _validatehandler
|
|
2373
|
+
handler: _validatehandler,
|
|
2374
|
+
errs,
|
|
1947
2375
|
})
|
|
1948
2376
|
|
|
1949
|
-
const generr = (0 < errs
|
|
2377
|
+
const generr = (0 < size(errs) && !collect)
|
|
1950
2378
|
if (generr) {
|
|
1951
|
-
throw new Error(
|
|
2379
|
+
throw new Error(join(errs, ' | '))
|
|
1952
2380
|
}
|
|
1953
2381
|
|
|
1954
2382
|
return out
|
|
@@ -1956,18 +2384,16 @@ function validate(
|
|
|
1956
2384
|
|
|
1957
2385
|
|
|
1958
2386
|
const select_AND: Injector = (inj: Injection, _val: any, _ref: string, store: any) => {
|
|
1959
|
-
if (
|
|
2387
|
+
if (M_KEYPRE === inj.mode) {
|
|
1960
2388
|
const terms = getprop(inj.parent, inj.key)
|
|
1961
2389
|
|
|
1962
2390
|
const ppath = slice(inj.path, -1)
|
|
1963
2391
|
const point = getpath(store, ppath)
|
|
1964
2392
|
|
|
1965
|
-
const vstore = {
|
|
2393
|
+
const vstore = merge([{}, store], 1)
|
|
1966
2394
|
vstore.$TOP = point
|
|
1967
2395
|
|
|
1968
2396
|
for (let term of terms) {
|
|
1969
|
-
// setprop(term, '`$OPEN`', getprop(term, '`$OPEN`', true))
|
|
1970
|
-
|
|
1971
2397
|
let terrs: any[] = []
|
|
1972
2398
|
|
|
1973
2399
|
validate(point, term, {
|
|
@@ -1976,7 +2402,7 @@ const select_AND: Injector = (inj: Injection, _val: any, _ref: string, store: an
|
|
|
1976
2402
|
meta: inj.meta,
|
|
1977
2403
|
})
|
|
1978
2404
|
|
|
1979
|
-
if (0 != terrs
|
|
2405
|
+
if (0 != size(terrs)) {
|
|
1980
2406
|
inj.errs.push(
|
|
1981
2407
|
'AND:' + pathify(ppath) + S_VIZ + stringify(point) + ' fail:' + stringify(terms))
|
|
1982
2408
|
}
|
|
@@ -1990,13 +2416,13 @@ const select_AND: Injector = (inj: Injection, _val: any, _ref: string, store: an
|
|
|
1990
2416
|
|
|
1991
2417
|
|
|
1992
2418
|
const select_OR: Injector = (inj: Injection, _val: any, _ref: string, store: any) => {
|
|
1993
|
-
if (
|
|
2419
|
+
if (M_KEYPRE === inj.mode) {
|
|
1994
2420
|
const terms = getprop(inj.parent, inj.key)
|
|
1995
2421
|
|
|
1996
2422
|
const ppath = slice(inj.path, -1)
|
|
1997
2423
|
const point = getpath(store, ppath)
|
|
1998
2424
|
|
|
1999
|
-
const vstore = {
|
|
2425
|
+
const vstore = merge([{}, store], 1)
|
|
2000
2426
|
vstore.$TOP = point
|
|
2001
2427
|
|
|
2002
2428
|
for (let term of terms) {
|
|
@@ -2008,7 +2434,7 @@ const select_OR: Injector = (inj: Injection, _val: any, _ref: string, store: any
|
|
|
2008
2434
|
meta: inj.meta,
|
|
2009
2435
|
})
|
|
2010
2436
|
|
|
2011
|
-
if (0 === terrs
|
|
2437
|
+
if (0 === size(terrs)) {
|
|
2012
2438
|
const gkey = getelem(inj.path, -2)
|
|
2013
2439
|
const gp = getelem(inj.nodes, -2)
|
|
2014
2440
|
setprop(gp, gkey, point)
|
|
@@ -2024,13 +2450,13 @@ const select_OR: Injector = (inj: Injection, _val: any, _ref: string, store: any
|
|
|
2024
2450
|
|
|
2025
2451
|
|
|
2026
2452
|
const select_NOT: Injector = (inj: Injection, _val: any, _ref: string, store: any) => {
|
|
2027
|
-
if (
|
|
2453
|
+
if (M_KEYPRE === inj.mode) {
|
|
2028
2454
|
const term = getprop(inj.parent, inj.key)
|
|
2029
2455
|
|
|
2030
2456
|
const ppath = slice(inj.path, -1)
|
|
2031
2457
|
const point = getpath(store, ppath)
|
|
2032
2458
|
|
|
2033
|
-
const vstore = {
|
|
2459
|
+
const vstore = merge([{}, store], 1)
|
|
2034
2460
|
vstore.$TOP = point
|
|
2035
2461
|
|
|
2036
2462
|
let terrs: any[] = []
|
|
@@ -2041,7 +2467,7 @@ const select_NOT: Injector = (inj: Injection, _val: any, _ref: string, store: an
|
|
|
2041
2467
|
meta: inj.meta,
|
|
2042
2468
|
})
|
|
2043
2469
|
|
|
2044
|
-
if (0 == terrs
|
|
2470
|
+
if (0 == size(terrs)) {
|
|
2045
2471
|
inj.errs.push(
|
|
2046
2472
|
'NOT:' + pathify(ppath) + S_VIZ + stringify(point) + ' fail:' + stringify(term))
|
|
2047
2473
|
}
|
|
@@ -2054,7 +2480,7 @@ const select_NOT: Injector = (inj: Injection, _val: any, _ref: string, store: an
|
|
|
2054
2480
|
|
|
2055
2481
|
|
|
2056
2482
|
const select_CMP: Injector = (inj: Injection, _val: any, ref: string, store: any) => {
|
|
2057
|
-
if (
|
|
2483
|
+
if (M_KEYPRE === inj.mode) {
|
|
2058
2484
|
const term = getprop(inj.parent, inj.key)
|
|
2059
2485
|
// const src = getprop(store, inj.base, store)
|
|
2060
2486
|
const gkey = getelem(inj.path, -2)
|
|
@@ -2093,7 +2519,7 @@ const select_CMP: Injector = (inj: Injection, _val: any, ref: string, store: any
|
|
|
2093
2519
|
}
|
|
2094
2520
|
}
|
|
2095
2521
|
|
|
2096
|
-
return
|
|
2522
|
+
return NONE
|
|
2097
2523
|
}
|
|
2098
2524
|
|
|
2099
2525
|
|
|
@@ -2107,10 +2533,13 @@ function select(children: any, query: any): any[] {
|
|
|
2107
2533
|
}
|
|
2108
2534
|
|
|
2109
2535
|
if (ismap(children)) {
|
|
2110
|
-
children = items(children
|
|
2536
|
+
children = items(children, n => {
|
|
2537
|
+
setprop(n[1], S_DKEY, n[0])
|
|
2538
|
+
return n[1]
|
|
2539
|
+
})
|
|
2111
2540
|
}
|
|
2112
2541
|
else {
|
|
2113
|
-
children = (children
|
|
2542
|
+
children = items(children, (n) => (setprop(n[1], S_DKEY, +n[0]), n[1]))
|
|
2114
2543
|
}
|
|
2115
2544
|
|
|
2116
2545
|
const results: any[] = []
|
|
@@ -2152,10 +2581,9 @@ function select(children: any, query: any): any[] {
|
|
|
2152
2581
|
}
|
|
2153
2582
|
|
|
2154
2583
|
|
|
2155
|
-
|
|
2156
2584
|
// Injection state used for recursive injection into JSON - like data structures.
|
|
2157
2585
|
class Injection {
|
|
2158
|
-
mode: InjectMode // Injection mode:
|
|
2586
|
+
mode: InjectMode // Injection mode: M_KEYPRE, M_VAL, M_KEYPOST.
|
|
2159
2587
|
full: boolean // Transform escape was full key name.
|
|
2160
2588
|
keyI: number // Index of parent key in list of parent keys.
|
|
2161
2589
|
keys: string[] // List of parent keys.
|
|
@@ -2166,7 +2594,7 @@ class Injection {
|
|
|
2166
2594
|
nodes: any[] // Stack of ancestor nodes.
|
|
2167
2595
|
handler: Injector // Custom handler for injections.
|
|
2168
2596
|
errs: any[] // Error collector.
|
|
2169
|
-
meta: Record<string, any> // Custom meta data.
|
|
2597
|
+
meta: Record<string, any> // Custom meta data. NOTE: do not merge, values must remain as-is.
|
|
2170
2598
|
dparent: any // Current data parent node (contains current data value).
|
|
2171
2599
|
dpath: string[] // Current data value path
|
|
2172
2600
|
base?: string // Base key for data in store, if any.
|
|
@@ -2179,10 +2607,10 @@ class Injection {
|
|
|
2179
2607
|
this.parent = parent
|
|
2180
2608
|
this.errs = []
|
|
2181
2609
|
|
|
2182
|
-
this.dparent =
|
|
2610
|
+
this.dparent = NONE
|
|
2183
2611
|
this.dpath = [S_DTOP]
|
|
2184
2612
|
|
|
2185
|
-
this.mode =
|
|
2613
|
+
this.mode = M_VAL
|
|
2186
2614
|
this.full = false
|
|
2187
2615
|
this.keyI = 0
|
|
2188
2616
|
this.keys = [S_DTOP]
|
|
@@ -2198,7 +2626,7 @@ class Injection {
|
|
|
2198
2626
|
toString(prefix?: string) {
|
|
2199
2627
|
return 'INJ' + (null == prefix ? '' : S_FS + prefix) + S_CN +
|
|
2200
2628
|
pad(pathify(this.path, 1)) +
|
|
2201
|
-
this.mode + (this.full ? '/full' : '') + S_CN +
|
|
2629
|
+
MODENAME[this.mode] + (this.full ? '/full' : '') + S_CN +
|
|
2202
2630
|
'key=' + this.keyI + S_FS + this.key + S_FS + S_OS + this.keys + S_CS +
|
|
2203
2631
|
' p=' + stringify(this.parent, -1, 1) +
|
|
2204
2632
|
' m=' + stringify(this.meta, -1, 1) +
|
|
@@ -2212,12 +2640,12 @@ class Injection {
|
|
|
2212
2640
|
const parentkey = getelem(this.path, -2)
|
|
2213
2641
|
|
|
2214
2642
|
// Resolve current node in store for local paths.
|
|
2215
|
-
if (
|
|
2643
|
+
if (NONE === this.dparent) {
|
|
2216
2644
|
|
|
2217
2645
|
// Even if there's no data, dpath should continue to match path, so that
|
|
2218
2646
|
// relative paths work properly.
|
|
2219
|
-
if (1 < this.dpath
|
|
2220
|
-
this.dpath = [
|
|
2647
|
+
if (1 < size(this.dpath)) {
|
|
2648
|
+
this.dpath = flatten([this.dpath, parentkey])
|
|
2221
2649
|
}
|
|
2222
2650
|
}
|
|
2223
2651
|
else {
|
|
@@ -2230,11 +2658,12 @@ class Injection {
|
|
|
2230
2658
|
this.dpath = slice(this.dpath, -1)
|
|
2231
2659
|
}
|
|
2232
2660
|
else {
|
|
2233
|
-
this.dpath = [
|
|
2661
|
+
this.dpath = flatten([this.dpath, parentkey])
|
|
2234
2662
|
}
|
|
2235
2663
|
}
|
|
2236
2664
|
}
|
|
2237
2665
|
|
|
2666
|
+
// TODO: is this needed?
|
|
2238
2667
|
return this.dparent
|
|
2239
2668
|
}
|
|
2240
2669
|
|
|
@@ -2248,8 +2677,8 @@ class Injection {
|
|
|
2248
2677
|
cinj.keys = keys
|
|
2249
2678
|
cinj.key = key
|
|
2250
2679
|
|
|
2251
|
-
cinj.path = [
|
|
2252
|
-
cinj.nodes = [
|
|
2680
|
+
cinj.path = flatten([getdef(this.path, []), key])
|
|
2681
|
+
cinj.nodes = flatten([getdef(this.nodes, []), [val]])
|
|
2253
2682
|
|
|
2254
2683
|
cinj.mode = this.mode
|
|
2255
2684
|
cinj.handler = this.handler
|
|
@@ -2259,7 +2688,7 @@ class Injection {
|
|
|
2259
2688
|
cinj.errs = this.errs
|
|
2260
2689
|
cinj.prior = this
|
|
2261
2690
|
|
|
2262
|
-
cinj.dpath = [
|
|
2691
|
+
cinj.dpath = flatten([this.dpath])
|
|
2263
2692
|
cinj.dparent = this.dparent
|
|
2264
2693
|
|
|
2265
2694
|
return cinj
|
|
@@ -2267,18 +2696,22 @@ class Injection {
|
|
|
2267
2696
|
|
|
2268
2697
|
|
|
2269
2698
|
setval(val: any, ancestor?: number) {
|
|
2699
|
+
let parent = NONE
|
|
2270
2700
|
if (null == ancestor || ancestor < 2) {
|
|
2271
|
-
|
|
2272
|
-
delprop(this.parent, this.key) :
|
|
2701
|
+
parent = NONE === val ?
|
|
2702
|
+
this.parent = delprop(this.parent, this.key) :
|
|
2273
2703
|
setprop(this.parent, this.key, val)
|
|
2274
2704
|
}
|
|
2275
2705
|
else {
|
|
2276
2706
|
const aval = getelem(this.nodes, 0 - ancestor)
|
|
2277
2707
|
const akey = getelem(this.path, 0 - ancestor)
|
|
2278
|
-
|
|
2708
|
+
parent = NONE === val ?
|
|
2279
2709
|
delprop(aval, akey) :
|
|
2280
2710
|
setprop(aval, akey, val)
|
|
2281
2711
|
}
|
|
2712
|
+
|
|
2713
|
+
// console.log('SETVAL', val, this.key, this.parent)
|
|
2714
|
+
return parent
|
|
2282
2715
|
}
|
|
2283
2716
|
}
|
|
2284
2717
|
|
|
@@ -2287,21 +2720,21 @@ class Injection {
|
|
|
2287
2720
|
// ==================
|
|
2288
2721
|
|
|
2289
2722
|
|
|
2290
|
-
// Update all references to target in inj.nodes.
|
|
2291
|
-
function _updateAncestors(_inj: Injection, target: any, tkey: any, tval: any) {
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
}
|
|
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
|
+
// }
|
|
2295
2728
|
|
|
2296
2729
|
|
|
2297
2730
|
// Build a type validation error message.
|
|
2298
|
-
function _invalidTypeMsg(path: any, needtype: string, vt:
|
|
2731
|
+
function _invalidTypeMsg(path: any, needtype: string, vt: number, v: any, _whence?: string) {
|
|
2299
2732
|
let vs = null == v ? 'no value' : stringify(v)
|
|
2300
2733
|
|
|
2301
2734
|
return 'Expected ' +
|
|
2302
|
-
(1 < path
|
|
2735
|
+
(1 < size(path) ? ('field ' + pathify(path, 1) + ' to be ') : '') +
|
|
2303
2736
|
needtype + ', but found ' +
|
|
2304
|
-
(null != v ? vt + S_VIZ : '') + vs +
|
|
2737
|
+
(null != v ? typename(vt) + S_VIZ : '') + vs +
|
|
2305
2738
|
|
|
2306
2739
|
// Uncomment to help debug validation errors.
|
|
2307
2740
|
// ' [' + _whence + ']' +
|
|
@@ -2319,7 +2752,7 @@ const _injecthandler: Injector = (
|
|
|
2319
2752
|
store: any
|
|
2320
2753
|
): any => {
|
|
2321
2754
|
let out = val
|
|
2322
|
-
const iscmd = isfunc(val) && (
|
|
2755
|
+
const iscmd = isfunc(val) && (NONE === ref || ref.startsWith(S_DS))
|
|
2323
2756
|
|
|
2324
2757
|
// Only call val function if it is a special command ($NAME format).
|
|
2325
2758
|
// TODO: OR if meta.'$CALL'
|
|
@@ -2329,7 +2762,7 @@ const _injecthandler: Injector = (
|
|
|
2329
2762
|
}
|
|
2330
2763
|
|
|
2331
2764
|
// Update parent with value. Ensures references remain in node tree.
|
|
2332
|
-
else if (
|
|
2765
|
+
else if (M_VAL === inj.mode && inj.full) {
|
|
2333
2766
|
inj.setval(val)
|
|
2334
2767
|
}
|
|
2335
2768
|
|
|
@@ -2399,9 +2832,9 @@ function _injectstr(
|
|
|
2399
2832
|
let pathref = m[1]
|
|
2400
2833
|
|
|
2401
2834
|
// Special escapes inside injection.
|
|
2402
|
-
|
|
2403
|
-
pathref.replace(R_BT_ESCAPE, S_BT).replace(R_DS_ESCAPE, S_DS)
|
|
2404
|
-
|
|
2835
|
+
if (3 < size(pathref)) {
|
|
2836
|
+
pathref = pathref.replace(R_BT_ESCAPE, S_BT).replace(R_DS_ESCAPE, S_DS)
|
|
2837
|
+
}
|
|
2405
2838
|
|
|
2406
2839
|
// Get the extracted path reference.
|
|
2407
2840
|
out = getpath(store, pathref, inj)
|
|
@@ -2411,14 +2844,19 @@ function _injectstr(
|
|
|
2411
2844
|
// Check for injections within the string.
|
|
2412
2845
|
const partial = (_m: string, ref: string) => {
|
|
2413
2846
|
// Special escapes inside injection.
|
|
2414
|
-
|
|
2847
|
+
|
|
2848
|
+
if (3 < size(ref)) {
|
|
2849
|
+
ref = ref.replace(R_BT_ESCAPE, S_BT).replace(R_DS_ESCAPE, S_DS)
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2415
2852
|
if (inj) {
|
|
2416
2853
|
inj.full = false
|
|
2417
2854
|
}
|
|
2855
|
+
|
|
2418
2856
|
const found = getpath(store, ref, inj)
|
|
2419
2857
|
|
|
2420
2858
|
// Ensure inject value is a string.
|
|
2421
|
-
return
|
|
2859
|
+
return NONE === found ? S_MT : S_string === typeof found ? found : JSON.stringify(found)
|
|
2422
2860
|
}
|
|
2423
2861
|
|
|
2424
2862
|
out = val.replace(R_INJECTION_PARTIAL, partial)
|
|
@@ -2435,11 +2873,101 @@ function _injectstr(
|
|
|
2435
2873
|
}
|
|
2436
2874
|
|
|
2437
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
|
+
|
|
2438
2963
|
class StructUtility {
|
|
2439
2964
|
clone = clone
|
|
2440
2965
|
delprop = delprop
|
|
2441
2966
|
escre = escre
|
|
2442
2967
|
escurl = escurl
|
|
2968
|
+
filter = filter
|
|
2969
|
+
flatten = flatten
|
|
2970
|
+
getdef = getdef
|
|
2443
2971
|
getelem = getelem
|
|
2444
2972
|
getpath = getpath
|
|
2445
2973
|
getprop = getprop
|
|
@@ -2452,13 +2980,14 @@ class StructUtility {
|
|
|
2452
2980
|
ismap = ismap
|
|
2453
2981
|
isnode = isnode
|
|
2454
2982
|
items = items
|
|
2455
|
-
|
|
2983
|
+
join = join
|
|
2456
2984
|
jsonify = jsonify
|
|
2457
2985
|
keysof = keysof
|
|
2458
2986
|
merge = merge
|
|
2459
2987
|
pad = pad
|
|
2460
2988
|
pathify = pathify
|
|
2461
2989
|
select = select
|
|
2990
|
+
setpath = setpath
|
|
2462
2991
|
setprop = setprop
|
|
2463
2992
|
size = size
|
|
2464
2993
|
slice = slice
|
|
@@ -2466,11 +2995,36 @@ class StructUtility {
|
|
|
2466
2995
|
stringify = stringify
|
|
2467
2996
|
transform = transform
|
|
2468
2997
|
typify = typify
|
|
2998
|
+
typename = typename
|
|
2469
2999
|
validate = validate
|
|
2470
3000
|
walk = walk
|
|
2471
3001
|
|
|
2472
|
-
|
|
2473
|
-
|
|
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
|
|
2474
3028
|
}
|
|
2475
3029
|
|
|
2476
3030
|
export {
|
|
@@ -2479,6 +3033,9 @@ export {
|
|
|
2479
3033
|
delprop,
|
|
2480
3034
|
escre,
|
|
2481
3035
|
escurl,
|
|
3036
|
+
filter,
|
|
3037
|
+
flatten,
|
|
3038
|
+
getdef,
|
|
2482
3039
|
getelem,
|
|
2483
3040
|
getpath,
|
|
2484
3041
|
getprop,
|
|
@@ -2491,13 +3048,14 @@ export {
|
|
|
2491
3048
|
ismap,
|
|
2492
3049
|
isnode,
|
|
2493
3050
|
items,
|
|
2494
|
-
|
|
3051
|
+
join,
|
|
2495
3052
|
jsonify,
|
|
2496
3053
|
keysof,
|
|
2497
3054
|
merge,
|
|
2498
3055
|
pad,
|
|
2499
3056
|
pathify,
|
|
2500
3057
|
select,
|
|
3058
|
+
setpath,
|
|
2501
3059
|
setprop,
|
|
2502
3060
|
size,
|
|
2503
3061
|
slice,
|
|
@@ -2505,11 +3063,41 @@ export {
|
|
|
2505
3063
|
stringify,
|
|
2506
3064
|
transform,
|
|
2507
3065
|
typify,
|
|
3066
|
+
typename,
|
|
2508
3067
|
validate,
|
|
2509
3068
|
walk,
|
|
2510
3069
|
|
|
2511
|
-
|
|
2512
|
-
|
|
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,
|
|
2513
3101
|
}
|
|
2514
3102
|
|
|
2515
3103
|
export type {
|