manyfest 1.0.43 → 1.0.46
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/CONTRIBUTING.md +50 -0
- package/README.md +97 -254
- package/docs/.nojekyll +0 -0
- package/docs/README.md +108 -0
- package/docs/_sidebar.md +17 -0
- package/docs/address-notation.md +244 -0
- package/docs/cover.md +11 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/hash-translation.md +202 -0
- package/docs/index.html +39 -0
- package/docs/quickstart.md +203 -0
- package/docs/reading.md +339 -0
- package/docs/retold-catalog.json +105 -0
- package/docs/retold-keyword-index.json +19 -0
- package/docs/schema-manipulation.md +186 -0
- package/docs/schema.md +319 -0
- package/docs/validating.md +344 -0
- package/docs/writing.md +300 -0
- package/package.json +3 -3
- package/source/Manyfest-ObjectAddress-CheckAddressExists.js +3 -4
- package/source/Manyfest-ObjectAddress-DeleteValue.js +7 -5
- package/source/Manyfest-ObjectAddress-GetValue.js +2 -6
- package/source/Manyfest-ObjectAddress-Parser.js +3 -3
- package/source/Manyfest-ObjectAddress-SetValue.js +7 -4
- package/source/Manyfest-ParseConditionals.js +0 -13
- package/source/Manyfest.js +28 -15
- package/dist/manyfest.compatible.js +0 -3010
- package/dist/manyfest.compatible.js.map +0 -1
- package/dist/manyfest.compatible.min.js +0 -2
- package/dist/manyfest.compatible.min.js.map +0 -1
- package/dist/manyfest.js +0 -2894
- package/dist/manyfest.js.map +0 -1
- package/dist/manyfest.min.js +0 -2
- package/dist/manyfest.min.js.map +0 -1
package/dist/manyfest.js
DELETED
|
@@ -1,2894 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
4
|
-
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
5
|
-
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
6
|
-
(function (f) {
|
|
7
|
-
if (typeof exports === "object" && typeof module !== "undefined") {
|
|
8
|
-
module.exports = f();
|
|
9
|
-
} else if (typeof define === "function" && define.amd) {
|
|
10
|
-
define([], f);
|
|
11
|
-
} else {
|
|
12
|
-
var g;
|
|
13
|
-
if (typeof window !== "undefined") {
|
|
14
|
-
g = window;
|
|
15
|
-
} else if (typeof global !== "undefined") {
|
|
16
|
-
g = global;
|
|
17
|
-
} else if (typeof self !== "undefined") {
|
|
18
|
-
g = self;
|
|
19
|
-
} else {
|
|
20
|
-
g = this;
|
|
21
|
-
}
|
|
22
|
-
g.Manyfest = f();
|
|
23
|
-
}
|
|
24
|
-
})(function () {
|
|
25
|
-
var define, module, exports;
|
|
26
|
-
return function () {
|
|
27
|
-
function r(e, n, t) {
|
|
28
|
-
function o(i, f) {
|
|
29
|
-
if (!n[i]) {
|
|
30
|
-
if (!e[i]) {
|
|
31
|
-
var c = "function" == typeof require && require;
|
|
32
|
-
if (!f && c) return c(i, !0);
|
|
33
|
-
if (u) return u(i, !0);
|
|
34
|
-
var a = new Error("Cannot find module '" + i + "'");
|
|
35
|
-
throw a.code = "MODULE_NOT_FOUND", a;
|
|
36
|
-
}
|
|
37
|
-
var p = n[i] = {
|
|
38
|
-
exports: {}
|
|
39
|
-
};
|
|
40
|
-
e[i][0].call(p.exports, function (r) {
|
|
41
|
-
var n = e[i][1][r];
|
|
42
|
-
return o(n || r);
|
|
43
|
-
}, p, p.exports, r, e, n, t);
|
|
44
|
-
}
|
|
45
|
-
return n[i].exports;
|
|
46
|
-
}
|
|
47
|
-
for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) o(t[i]);
|
|
48
|
-
return o;
|
|
49
|
-
}
|
|
50
|
-
return r;
|
|
51
|
-
}()({
|
|
52
|
-
1: [function (require, module, exports) {
|
|
53
|
-
module.exports = {
|
|
54
|
-
"name": "fable-serviceproviderbase",
|
|
55
|
-
"version": "3.0.15",
|
|
56
|
-
"description": "Simple base classes for fable services.",
|
|
57
|
-
"main": "source/Fable-ServiceProviderBase.js",
|
|
58
|
-
"scripts": {
|
|
59
|
-
"start": "node source/Fable-ServiceProviderBase.js",
|
|
60
|
-
"test": "npx mocha -u tdd -R spec",
|
|
61
|
-
"tests": "npx mocha -u tdd --exit -R spec --grep",
|
|
62
|
-
"coverage": "npx nyc --reporter=lcov --reporter=text-lcov npx mocha -- -u tdd -R spec",
|
|
63
|
-
"build": "npx quack build"
|
|
64
|
-
},
|
|
65
|
-
"mocha": {
|
|
66
|
-
"diff": true,
|
|
67
|
-
"extension": ["js"],
|
|
68
|
-
"package": "./package.json",
|
|
69
|
-
"reporter": "spec",
|
|
70
|
-
"slow": "75",
|
|
71
|
-
"timeout": "5000",
|
|
72
|
-
"ui": "tdd",
|
|
73
|
-
"watch-files": ["source/**/*.js", "test/**/*.js"],
|
|
74
|
-
"watch-ignore": ["lib/vendor"]
|
|
75
|
-
},
|
|
76
|
-
"repository": {
|
|
77
|
-
"type": "git",
|
|
78
|
-
"url": "https://github.com/stevenvelozo/fable-serviceproviderbase.git"
|
|
79
|
-
},
|
|
80
|
-
"keywords": ["entity", "behavior"],
|
|
81
|
-
"author": "Steven Velozo <steven@velozo.com> (http://velozo.com/)",
|
|
82
|
-
"license": "MIT",
|
|
83
|
-
"bugs": {
|
|
84
|
-
"url": "https://github.com/stevenvelozo/fable-serviceproviderbase/issues"
|
|
85
|
-
},
|
|
86
|
-
"homepage": "https://github.com/stevenvelozo/fable-serviceproviderbase",
|
|
87
|
-
"devDependencies": {
|
|
88
|
-
"fable": "^3.0.143",
|
|
89
|
-
"quackage": "^1.0.33"
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
}, {}],
|
|
93
|
-
2: [function (require, module, exports) {
|
|
94
|
-
/**
|
|
95
|
-
* Fable Service Base
|
|
96
|
-
* @author <steven@velozo.com>
|
|
97
|
-
*/
|
|
98
|
-
|
|
99
|
-
const libPackage = require('../package.json');
|
|
100
|
-
class FableServiceProviderBase {
|
|
101
|
-
// The constructor can be used in two ways:
|
|
102
|
-
// 1) With a fable, options object and service hash (the options object and service hash are optional)
|
|
103
|
-
// 2) With an object or nothing as the first parameter, where it will be treated as the options object
|
|
104
|
-
constructor(pFable, pOptions, pServiceHash) {
|
|
105
|
-
// Check if a fable was passed in; connect it if so
|
|
106
|
-
if (typeof pFable === 'object' && pFable.isFable) {
|
|
107
|
-
this.connectFable(pFable);
|
|
108
|
-
} else {
|
|
109
|
-
this.fable = false;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Initialize the services map if it wasn't passed in
|
|
113
|
-
/** @type {Object} */
|
|
114
|
-
this._PackageFableServiceProvider = libPackage;
|
|
115
|
-
|
|
116
|
-
// initialize options and UUID based on whether the fable was passed in or not.
|
|
117
|
-
if (this.fable) {
|
|
118
|
-
this.UUID = pFable.getUUID();
|
|
119
|
-
this.options = typeof pOptions === 'object' ? pOptions : {};
|
|
120
|
-
} else {
|
|
121
|
-
// With no fable, check to see if there was an object passed into either of the first two
|
|
122
|
-
// Parameters, and if so, treat it as the options object
|
|
123
|
-
this.options = typeof pFable === 'object' && !pFable.isFable ? pFable : typeof pOptions === 'object' ? pOptions : {};
|
|
124
|
-
this.UUID = "CORE-SVC-".concat(Math.floor(Math.random() * (99999 - 10000) + 10000));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// It's expected that the deriving class will set this
|
|
128
|
-
this.serviceType = "Unknown-".concat(this.UUID);
|
|
129
|
-
|
|
130
|
-
// The service hash is used to identify the specific instantiation of the service in the services map
|
|
131
|
-
this.Hash = typeof pServiceHash === 'string' ? pServiceHash : !this.fable && typeof pOptions === 'string' ? pOptions : "".concat(this.UUID);
|
|
132
|
-
}
|
|
133
|
-
connectFable(pFable) {
|
|
134
|
-
if (typeof pFable !== 'object' || !pFable.isFable) {
|
|
135
|
-
let tmpErrorMessage = "Fable Service Provider Base: Cannot connect to Fable, invalid Fable object passed in. The pFable parameter was a [".concat(typeof pFable, "].}");
|
|
136
|
-
console.log(tmpErrorMessage);
|
|
137
|
-
return new Error(tmpErrorMessage);
|
|
138
|
-
}
|
|
139
|
-
if (!this.fable) {
|
|
140
|
-
this.fable = pFable;
|
|
141
|
-
}
|
|
142
|
-
if (!this.log) {
|
|
143
|
-
this.log = this.fable.Logging;
|
|
144
|
-
}
|
|
145
|
-
if (!this.services) {
|
|
146
|
-
this.services = this.fable.services;
|
|
147
|
-
}
|
|
148
|
-
if (!this.servicesMap) {
|
|
149
|
-
this.servicesMap = this.fable.servicesMap;
|
|
150
|
-
}
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
_defineProperty(FableServiceProviderBase, "isFableService", true);
|
|
155
|
-
module.exports = FableServiceProviderBase;
|
|
156
|
-
|
|
157
|
-
// This is left here in case we want to go back to having different code/base class for "core" services
|
|
158
|
-
module.exports.CoreServiceProviderBase = FableServiceProviderBase;
|
|
159
|
-
}, {
|
|
160
|
-
"../package.json": 1
|
|
161
|
-
}],
|
|
162
|
-
3: [function (require, module, exports) {
|
|
163
|
-
// When a boxed property is passed in, it should have quotes of some
|
|
164
|
-
// kind around it.
|
|
165
|
-
//
|
|
166
|
-
// For instance:
|
|
167
|
-
// MyValues['Name']
|
|
168
|
-
// MyValues["Age"]
|
|
169
|
-
// MyValues[`Cost`]
|
|
170
|
-
//
|
|
171
|
-
// This function removes the wrapping quotes.
|
|
172
|
-
//
|
|
173
|
-
// Please note it *DOES NOT PARSE* template literals, so backticks just
|
|
174
|
-
// end up doing the same thing as other quote types.
|
|
175
|
-
//
|
|
176
|
-
// TODO: Should template literals be processed? If so what state do they have access to? That should happen here if so.
|
|
177
|
-
// TODO: Make a simple class include library with these
|
|
178
|
-
/**
|
|
179
|
-
* @param {string} pCharacter - The character to remove from the start and end of the string
|
|
180
|
-
* @param {string} pString - The string to clean
|
|
181
|
-
*
|
|
182
|
-
* @return {string} The cleaned string
|
|
183
|
-
*/
|
|
184
|
-
const cleanWrapCharacters = (pCharacter, pString) => {
|
|
185
|
-
if (pString.startsWith(pCharacter) && pString.endsWith(pCharacter)) {
|
|
186
|
-
return pString.substring(1, pString.length - 1);
|
|
187
|
-
} else {
|
|
188
|
-
return pString;
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
module.exports = cleanWrapCharacters;
|
|
192
|
-
}, {}],
|
|
193
|
-
4: [function (require, module, exports) {
|
|
194
|
-
/**
|
|
195
|
-
* @author <steven@velozo.com>
|
|
196
|
-
*/
|
|
197
|
-
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Hash Translation
|
|
201
|
-
*
|
|
202
|
-
* This is a very simple translation table for hashes, which allows the same schema to resolve
|
|
203
|
-
* differently based on a loaded translation table.
|
|
204
|
-
*
|
|
205
|
-
* This is to prevent the requirement for mutating schemas over and over again when we want to
|
|
206
|
-
* reuse the structure but look up data elements by different addresses.
|
|
207
|
-
*
|
|
208
|
-
* One side-effect of this is that a translation table can "override" the built-in hashes, since
|
|
209
|
-
* this is always used to resolve hashes before any of the functionCallByHash(pHash, ...) perform
|
|
210
|
-
* their lookups by hash.
|
|
211
|
-
*
|
|
212
|
-
* @class ManyfestHashTranslation
|
|
213
|
-
*/
|
|
214
|
-
class ManyfestHashTranslation {
|
|
215
|
-
/**
|
|
216
|
-
* @param {function} [pInfoLog] - (optional) A logging function for info messages
|
|
217
|
-
* @param {function} [pErrorLog] - (optional) A logging function for error messages
|
|
218
|
-
*/
|
|
219
|
-
constructor(pInfoLog, pErrorLog) {
|
|
220
|
-
// Wire in logging
|
|
221
|
-
this.logInfo = typeof pInfoLog === 'function' ? pInfoLog : libSimpleLog;
|
|
222
|
-
this.logError = typeof pErrorLog === 'function' ? pErrorLog : libSimpleLog;
|
|
223
|
-
this.translationTable = {};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* @return {number} The number of translations in the table
|
|
228
|
-
*/
|
|
229
|
-
translationCount() {
|
|
230
|
-
return Object.keys(this.translationTable).length;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* @param {object} pTranslation - An object containing source:destination hash pairs to add to the translation table
|
|
235
|
-
*/
|
|
236
|
-
addTranslation(pTranslation) {
|
|
237
|
-
// This adds a translation in the form of:
|
|
238
|
-
// { "SourceHash": "DestinationHash", "SecondSourceHash":"SecondDestinationHash" }
|
|
239
|
-
if (typeof pTranslation != 'object') {
|
|
240
|
-
this.logError("Hash translation addTranslation expected a translation be type object but was passed in ".concat(typeof pTranslation));
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
let tmpTranslationSources = Object.keys(pTranslation);
|
|
244
|
-
tmpTranslationSources.forEach(pTranslationSource => {
|
|
245
|
-
if (typeof pTranslation[pTranslationSource] != 'string') {
|
|
246
|
-
this.logError("Hash translation addTranslation expected a translation destination hash for [".concat(pTranslationSource, "] to be a string but the referrant was a ").concat(typeof pTranslation[pTranslationSource]));
|
|
247
|
-
} else {
|
|
248
|
-
this.translationTable[pTranslationSource] = pTranslation[pTranslationSource];
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* @param {string} pTranslationHash - The source hash to remove from the translation table
|
|
255
|
-
*/
|
|
256
|
-
removeTranslationHash(pTranslationHash) {
|
|
257
|
-
delete this.translationTable[pTranslationHash];
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* This removes translations.
|
|
262
|
-
* If passed a string, just removes the single one.
|
|
263
|
-
* If passed an object, it does all the source keys.
|
|
264
|
-
*
|
|
265
|
-
* @param {string|object} pTranslation - Either a source hash string to remove, or an object containing source:destination hash pairs to remove
|
|
266
|
-
*
|
|
267
|
-
* @return {boolean} True if the removal was successful, false otherwise
|
|
268
|
-
*/
|
|
269
|
-
removeTranslation(pTranslation) {
|
|
270
|
-
if (typeof pTranslation == 'string') {
|
|
271
|
-
this.removeTranslationHash(pTranslation);
|
|
272
|
-
return true;
|
|
273
|
-
} else if (typeof pTranslation == 'object') {
|
|
274
|
-
let tmpTranslationSources = Object.keys(pTranslation);
|
|
275
|
-
tmpTranslationSources.forEach(pTranslationSource => {
|
|
276
|
-
this.removeTranslation(pTranslationSource);
|
|
277
|
-
});
|
|
278
|
-
return true;
|
|
279
|
-
} else {
|
|
280
|
-
this.logError("Hash translation removeTranslation expected either a string or an object but the passed-in translation was type ".concat(typeof pTranslation));
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
clearTranslations() {
|
|
285
|
-
this.translationTable = {};
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* @param {string} pTranslation - The source hash to translate
|
|
290
|
-
*
|
|
291
|
-
* @return {string} The translated hash, or the original if no translation exists
|
|
292
|
-
*/
|
|
293
|
-
translate(pTranslation) {
|
|
294
|
-
if (pTranslation in this.translationTable) {
|
|
295
|
-
return this.translationTable[pTranslation];
|
|
296
|
-
} else {
|
|
297
|
-
return pTranslation;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
module.exports = ManyfestHashTranslation;
|
|
302
|
-
}, {
|
|
303
|
-
"./Manyfest-LogToConsole.js": 5
|
|
304
|
-
}],
|
|
305
|
-
5: [function (require, module, exports) {
|
|
306
|
-
/**
|
|
307
|
-
* @author <steven@velozo.com>
|
|
308
|
-
*/
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Manyfest simple logging shim (for browser and dependency-free running)
|
|
312
|
-
*/
|
|
313
|
-
|
|
314
|
-
const logToConsole = (pLogLine, pLogObject) => {
|
|
315
|
-
let tmpLogLine = typeof pLogLine === 'string' ? pLogLine : '';
|
|
316
|
-
console.log("[Manyfest] ".concat(tmpLogLine));
|
|
317
|
-
if (pLogObject) console.log(JSON.stringify(pLogObject));
|
|
318
|
-
};
|
|
319
|
-
module.exports = logToConsole;
|
|
320
|
-
}, {}],
|
|
321
|
-
6: [function (require, module, exports) {
|
|
322
|
-
/**
|
|
323
|
-
* @author <steven@velozo.com>
|
|
324
|
-
*/
|
|
325
|
-
const libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
326
|
-
// This is for resolving functions mid-address
|
|
327
|
-
const libGetObjectValue = require('./Manyfest-ObjectAddress-GetValue.js');
|
|
328
|
-
const fCleanWrapCharacters = require('./Manyfest-CleanWrapCharacters.js');
|
|
329
|
-
|
|
330
|
-
// TODO: Just until this is a fable service.
|
|
331
|
-
let _MockFable = {
|
|
332
|
-
DataFormat: require('./Manyfest-ObjectAddress-Parser.js')
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Object Address Resolver
|
|
337
|
-
*
|
|
338
|
-
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
339
|
-
* be extremely clear what is going on in the recursion for
|
|
340
|
-
* each of the three address resolution functions.
|
|
341
|
-
*
|
|
342
|
-
* Although there is some opportunity to repeat ourselves a
|
|
343
|
-
* bit less in this codebase (e.g. with detection of arrays
|
|
344
|
-
* versus objects versus direct properties), it can make
|
|
345
|
-
* debugging.. challenging. The minified version of the code
|
|
346
|
-
* optimizes out almost anything repeated in here. So please
|
|
347
|
-
* be kind and rewind... meaning please keep the codebase less
|
|
348
|
-
* terse and more verbose so humans can comprehend it.
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
* @class ManyfestObjectAddressResolverCheckAddressExists
|
|
352
|
-
*/
|
|
353
|
-
class ManyfestObjectAddressResolverCheckAddressExists {
|
|
354
|
-
/**
|
|
355
|
-
* @param {function} [pInfoLog] - (optional) Function to use for info logging
|
|
356
|
-
* @param {function} [pErrorLog] - (optional) Function to use for error logging
|
|
357
|
-
*/
|
|
358
|
-
constructor(pInfoLog, pErrorLog) {
|
|
359
|
-
// Wire in logging
|
|
360
|
-
this.logInfo = typeof pInfoLog == 'function' ? pInfoLog : libSimpleLog;
|
|
361
|
-
this.logError = typeof pErrorLog == 'function' ? pErrorLog : libSimpleLog;
|
|
362
|
-
this.getObjectValueClass = new libGetObjectValue(this.logInfo, this.logError);
|
|
363
|
-
this.cleanWrapCharacters = fCleanWrapCharacters;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Check if an address exists.
|
|
368
|
-
*
|
|
369
|
-
* This is necessary because the getValueAtAddress function is ambiguous on
|
|
370
|
-
* whether the element/property is actually there or not (it returns
|
|
371
|
-
* undefined whether the property exists or not). This function checks for
|
|
372
|
-
* existance and returns true or false dependent.
|
|
373
|
-
*
|
|
374
|
-
* @param {object} pObject - The object to check within
|
|
375
|
-
* @param {string} pAddress - The address to check for
|
|
376
|
-
* @param {object} [pRootObject] - (optional) The root object for function resolution context
|
|
377
|
-
*
|
|
378
|
-
* @return {boolean} - True if the address exists, false if it does not
|
|
379
|
-
*/
|
|
380
|
-
checkAddressExists(pObject, pAddress, pRootObject) {
|
|
381
|
-
// TODO: Should these throw an error?
|
|
382
|
-
// Make sure pObject is an object
|
|
383
|
-
if (typeof pObject != 'object') return false;
|
|
384
|
-
// Make sure pAddress is a string
|
|
385
|
-
if (typeof pAddress != 'string') return false;
|
|
386
|
-
|
|
387
|
-
// Set the root object to the passed-in object if it isn't set yet. This is expected to be the root object.
|
|
388
|
-
// NOTE: This was added to support functions mid-stream
|
|
389
|
-
let tmpRootObject = typeof pRootObject == 'undefined' ? pObject : pRootObject;
|
|
390
|
-
|
|
391
|
-
// DONE: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
|
|
392
|
-
let tmpAddressPartBeginning = _MockFable.DataFormat.stringGetFirstSegment(pAddress);
|
|
393
|
-
|
|
394
|
-
// This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
|
|
395
|
-
if (tmpAddressPartBeginning.length == pAddress.length) {
|
|
396
|
-
// Check if the address refers to a boxed property
|
|
397
|
-
let tmpBracketStartIndex = pAddress.indexOf('[');
|
|
398
|
-
let tmpBracketStopIndex = pAddress.indexOf(']');
|
|
399
|
-
|
|
400
|
-
// Check if there is a function somewhere in the address... parenthesis start should only be in a function
|
|
401
|
-
let tmpFunctionStartIndex = pAddress.indexOf('(');
|
|
402
|
-
|
|
403
|
-
// NOTE THAT FUNCTIONS MUST RESOLVE FIRST
|
|
404
|
-
// Functions look like this
|
|
405
|
-
// MyFunction()
|
|
406
|
-
// MyFunction(Some.Address)
|
|
407
|
-
// MyFunction(Some.Address,Some.Other.Address)
|
|
408
|
-
// MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
|
|
409
|
-
//
|
|
410
|
-
// This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
|
|
411
|
-
// To heck with that. This is a simple function call.
|
|
412
|
-
//
|
|
413
|
-
// The requirements to detect a function are:
|
|
414
|
-
// 1) The start bracket is after character 0
|
|
415
|
-
if (tmpFunctionStartIndex > 0
|
|
416
|
-
// 2) The end bracket is after the start bracket
|
|
417
|
-
&& _MockFable.DataFormat.stringCountEnclosures(pAddress) > 0) {
|
|
418
|
-
let tmpFunctionAddress = pAddress.substring(0, tmpFunctionStartIndex).trim();
|
|
419
|
-
if (tmpFunctionAddress in pObject && typeof pObject[tmpFunctionAddress] == 'function') {
|
|
420
|
-
return true;
|
|
421
|
-
} else {
|
|
422
|
-
// The address suggests it is a function, but it is not.
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
// Boxed elements look like this:
|
|
427
|
-
// MyValues[10]
|
|
428
|
-
// MyValues['Name']
|
|
429
|
-
// MyValues["Age"]
|
|
430
|
-
// MyValues[`Cost`]
|
|
431
|
-
//
|
|
432
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
433
|
-
// The requirements to detect a boxed element are:
|
|
434
|
-
// 1) The start bracket is after character 0
|
|
435
|
-
else if (tmpBracketStartIndex > 0
|
|
436
|
-
// 2) The end bracket has something between them
|
|
437
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
438
|
-
// 3) There is data
|
|
439
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex > 1) {
|
|
440
|
-
// The "Name" of the Object contained too the left of the bracket
|
|
441
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
442
|
-
|
|
443
|
-
// If the subproperty doesn't test as a proper Object, none of the rest of this is possible.
|
|
444
|
-
// This is a rare case where Arrays testing as Objects is useful
|
|
445
|
-
if (typeof pObject[tmpBoxedPropertyName] !== 'object') {
|
|
446
|
-
return false;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// The "Reference" to the property within it, either an array element or object property
|
|
450
|
-
let tmpBoxedPropertyReference = pAddress.substring(tmpBracketStartIndex + 1, tmpBracketStopIndex).trim();
|
|
451
|
-
// Attempt to parse the reference as a number, which will be used as an array element
|
|
452
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
453
|
-
|
|
454
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
455
|
-
// This seems confusing to me at first read, so explaination:
|
|
456
|
-
// Is the Boxed Object an Array? TRUE
|
|
457
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
458
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
459
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber)) {
|
|
460
|
-
return false;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
464
|
-
// otherwise we will try to treat it as a dynamic object property.
|
|
465
|
-
if (isNaN(tmpBoxedPropertyNumber)) {
|
|
466
|
-
// This isn't a number ... let's treat it as a dynamic object property.
|
|
467
|
-
// We would expect the property to be wrapped in some kind of quotes so strip them
|
|
468
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
469
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
470
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
471
|
-
|
|
472
|
-
// Check if the property exists.
|
|
473
|
-
return tmpBoxedPropertyReference in pObject[tmpBoxedPropertyName];
|
|
474
|
-
} else {
|
|
475
|
-
// Use the new in operator to see if the element is in the array
|
|
476
|
-
return tmpBoxedPropertyNumber in pObject[tmpBoxedPropertyName];
|
|
477
|
-
}
|
|
478
|
-
} else {
|
|
479
|
-
// Check if the property exists
|
|
480
|
-
return pAddress in pObject;
|
|
481
|
-
}
|
|
482
|
-
} else {
|
|
483
|
-
let tmpSubObjectName = tmpAddressPartBeginning;
|
|
484
|
-
let tmpNewAddress = pAddress.substring(tmpAddressPartBeginning.length + 1);
|
|
485
|
-
|
|
486
|
-
// Test if the tmpNewAddress is an array or object
|
|
487
|
-
// Check if it's a boxed property
|
|
488
|
-
let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
|
|
489
|
-
let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
|
|
490
|
-
|
|
491
|
-
// Check if there is a function somewhere in the address... parenthesis start should only be in a function
|
|
492
|
-
let tmpFunctionStartIndex = tmpSubObjectName.indexOf('(');
|
|
493
|
-
|
|
494
|
-
// NOTE THAT FUNCTIONS MUST RESOLVE FIRST
|
|
495
|
-
// Functions look like this
|
|
496
|
-
// MyFunction()
|
|
497
|
-
// MyFunction(Some.Address)
|
|
498
|
-
// MyFunction(Some.Address,Some.Other.Address)
|
|
499
|
-
// MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
|
|
500
|
-
//
|
|
501
|
-
// This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
|
|
502
|
-
// To heck with that. This is a simple function call.
|
|
503
|
-
//
|
|
504
|
-
// The requirements to detect a function are:
|
|
505
|
-
// 1) The start bracket is after character 0
|
|
506
|
-
if (tmpFunctionStartIndex > 0
|
|
507
|
-
// 2) The end bracket is after the start bracket
|
|
508
|
-
&& _MockFable.DataFormat.stringCountEnclosures(tmpSubObjectName) > 0) {
|
|
509
|
-
let tmpFunctionAddress = tmpSubObjectName.substring(0, tmpFunctionStartIndex).trim();
|
|
510
|
-
//tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpSubObjectName}`;
|
|
511
|
-
|
|
512
|
-
if (typeof pObject[tmpFunctionAddress] !== 'function') {
|
|
513
|
-
// The address suggests it is a function, but it is not.
|
|
514
|
-
return false;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Now see if the function has arguments.
|
|
518
|
-
// Implementation notes: * ARGUMENTS MUST SHARE THE SAME ROOT OBJECT CONTEXT *
|
|
519
|
-
let tmpFunctionArguments = _MockFable.DataFormat.stringGetSegments(_MockFable.DataFormat.stringGetEnclosureValueByIndex(tmpSubObjectName.substring(tmpFunctionAddress.length), 0), ',');
|
|
520
|
-
if (tmpFunctionArguments.length == 0 || tmpFunctionArguments[0] == '') {
|
|
521
|
-
// No arguments... just call the function (bound to the scope of the object it is contained withing)
|
|
522
|
-
if (tmpFunctionAddress in pObject) {
|
|
523
|
-
try {
|
|
524
|
-
return this.checkAddressExists(pObject[tmpFunctionAddress].apply(pObject), tmpNewAddress, tmpRootObject);
|
|
525
|
-
} catch (pError) {
|
|
526
|
-
// The function call failed, so the address doesn't exist
|
|
527
|
-
libSimpleLog("Error calling function ".concat(tmpFunctionAddress, " (address [").concat(pAddress, "]): ").concat(pError.message));
|
|
528
|
-
return false;
|
|
529
|
-
}
|
|
530
|
-
} else {
|
|
531
|
-
// The function doesn't exist, so the address doesn't exist
|
|
532
|
-
libSimpleLog("Function ".concat(tmpFunctionAddress, " does not exist (address [").concat(pAddress, "])"));
|
|
533
|
-
return false;
|
|
534
|
-
}
|
|
535
|
-
} else {
|
|
536
|
-
let tmpArgumentValues = [];
|
|
537
|
-
let tmpRootObject = typeof pRootObject == 'undefined' ? pObject : pRootObject;
|
|
538
|
-
|
|
539
|
-
// Now get the value for each argument
|
|
540
|
-
for (let i = 0; i < tmpFunctionArguments.length; i++) {
|
|
541
|
-
// Resolve the values for each subsequent entry
|
|
542
|
-
// NOTE: This is where the resolves get really tricky. Recursion within recursion. Programming gom jabbar, yo.
|
|
543
|
-
tmpArgumentValues.push(this.getObjectValueClass.getValueAtAddress(tmpRootObject, tmpFunctionArguments[i]));
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
//return this.checkAddressExists(pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues), tmpNewAddress, tmpRootObject);
|
|
547
|
-
if (tmpFunctionAddress in pObject) {
|
|
548
|
-
try {
|
|
549
|
-
return this.checkAddressExists(pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues), tmpNewAddress, tmpRootObject);
|
|
550
|
-
} catch (pError) {
|
|
551
|
-
// The function call failed, so the address doesn't exist
|
|
552
|
-
libSimpleLog("Error calling function ".concat(tmpFunctionAddress, " (address [").concat(pAddress, "]): ").concat(pError.message));
|
|
553
|
-
return false;
|
|
554
|
-
}
|
|
555
|
-
} else {
|
|
556
|
-
// The function doesn't exist, so the address doesn't exist
|
|
557
|
-
libSimpleLog("Function ".concat(tmpFunctionAddress, " does not exist (address [").concat(pAddress, "])"));
|
|
558
|
-
return false;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
// Boxed elements look like this:
|
|
563
|
-
// MyValues[42]
|
|
564
|
-
// MyValues['Color']
|
|
565
|
-
// MyValues["Weight"]
|
|
566
|
-
// MyValues[`Diameter`]
|
|
567
|
-
//
|
|
568
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
569
|
-
// The requirements to detect a boxed element are:
|
|
570
|
-
// 1) The start bracket is after character 0
|
|
571
|
-
else if (tmpBracketStartIndex > 0
|
|
572
|
-
// 2) The end bracket has something between them
|
|
573
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
574
|
-
// 3) There is data
|
|
575
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex > 1) {
|
|
576
|
-
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
577
|
-
let tmpBoxedPropertyReference = tmpSubObjectName.substring(tmpBracketStartIndex + 1, tmpBracketStopIndex).trim();
|
|
578
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
579
|
-
|
|
580
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
581
|
-
// This seems confusing to me at first read, so explaination:
|
|
582
|
-
// Is the Boxed Object an Array? TRUE
|
|
583
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
584
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
585
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
586
|
-
// StudentData.Sections.Algebra.Students[1].Tardy
|
|
587
|
-
// BUT
|
|
588
|
-
// StudentData.Sections.Algebra.Students is an object, so the [1].Tardy is not possible to access
|
|
589
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
590
|
-
// StudentData.Sections.Algebra.Students["JaneDoe"].Grade
|
|
591
|
-
// BUT
|
|
592
|
-
// StudentData.Sections.Algebra.Students is an array, so the ["JaneDoe"].Grade is not possible to access
|
|
593
|
-
// TODO: Should this be an error or something? Should we keep a log of failures like this?
|
|
594
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber)) {
|
|
595
|
-
// Because this is an impossible address, the property doesn't exist
|
|
596
|
-
// TODO: Should we throw an error in this condition?
|
|
597
|
-
return false;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
//This is a bracketed value
|
|
601
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
602
|
-
// otherwise we will try to reat it as a dynamic object property.
|
|
603
|
-
if (isNaN(tmpBoxedPropertyNumber)) {
|
|
604
|
-
// This isn't a number ... let's treat it as a dynanmic object property.
|
|
605
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
606
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
607
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
608
|
-
|
|
609
|
-
// Recurse directly into the subobject
|
|
610
|
-
return this.checkAddressExists(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress, tmpRootObject);
|
|
611
|
-
} else {
|
|
612
|
-
// We parsed a valid number out of the boxed property name, so recurse into the array
|
|
613
|
-
return this.checkAddressExists(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress, tmpRootObject);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// If there is an object property already named for the sub object, but it isn't an object
|
|
618
|
-
// then the system can't set the value in there. Error and abort!
|
|
619
|
-
if (tmpSubObjectName in pObject && typeof pObject[tmpSubObjectName] !== 'object') {
|
|
620
|
-
return false;
|
|
621
|
-
} else if (tmpSubObjectName in pObject) {
|
|
622
|
-
// If there is already a subobject pass that to the recursive thingy
|
|
623
|
-
return this.checkAddressExists(pObject[tmpSubObjectName], tmpNewAddress, tmpRootObject);
|
|
624
|
-
} else {
|
|
625
|
-
// Create a subobject and then pass that
|
|
626
|
-
pObject[tmpSubObjectName] = {};
|
|
627
|
-
return this.checkAddressExists(pObject[tmpSubObjectName], tmpNewAddress, tmpRootObject);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
module.exports = ManyfestObjectAddressResolverCheckAddressExists;
|
|
633
|
-
}, {
|
|
634
|
-
"./Manyfest-CleanWrapCharacters.js": 3,
|
|
635
|
-
"./Manyfest-LogToConsole.js": 5,
|
|
636
|
-
"./Manyfest-ObjectAddress-GetValue.js": 8,
|
|
637
|
-
"./Manyfest-ObjectAddress-Parser.js": 9
|
|
638
|
-
}],
|
|
639
|
-
7: [function (require, module, exports) {
|
|
640
|
-
/**
|
|
641
|
-
* @author <steven@velozo.com>
|
|
642
|
-
*/
|
|
643
|
-
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
644
|
-
let fCleanWrapCharacters = require('./Manyfest-CleanWrapCharacters.js');
|
|
645
|
-
let fParseConditionals = require("../source/Manyfest-ParseConditionals.js");
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Object Address Resolver - DeleteValue
|
|
649
|
-
*
|
|
650
|
-
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
651
|
-
* be extremely clear what is going on in the recursion for
|
|
652
|
-
* each of the three address resolution functions.
|
|
653
|
-
*
|
|
654
|
-
* Although there is some opportunity to repeat ourselves a
|
|
655
|
-
* bit less in this codebase (e.g. with detection of arrays
|
|
656
|
-
* versus objects versus direct properties), it can make
|
|
657
|
-
* debugging.. challenging. The minified version of the code
|
|
658
|
-
* optimizes out almost anything repeated in here. So please
|
|
659
|
-
* be kind and rewind... meaning please keep the codebase less
|
|
660
|
-
* terse and more verbose so humans can comprehend it.
|
|
661
|
-
*
|
|
662
|
-
* TODO: Once we validate this pattern is good to go, break these out into
|
|
663
|
-
* three separate modules.
|
|
664
|
-
*
|
|
665
|
-
* @class ManyfestObjectAddressResolverDeleteValue
|
|
666
|
-
*/
|
|
667
|
-
class ManyfestObjectAddressResolverDeleteValue {
|
|
668
|
-
/**
|
|
669
|
-
* @param {function} [pInfoLog] - (optional) A logging function for info messages
|
|
670
|
-
* @param {function} [pErrorLog] - (optional) A logging function for error messages
|
|
671
|
-
*/
|
|
672
|
-
constructor(pInfoLog, pErrorLog) {
|
|
673
|
-
// Wire in logging
|
|
674
|
-
this.logInfo = typeof pInfoLog == 'function' ? pInfoLog : libSimpleLog;
|
|
675
|
-
this.logError = typeof pErrorLog == 'function' ? pErrorLog : libSimpleLog;
|
|
676
|
-
this.cleanWrapCharacters = fCleanWrapCharacters;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
// TODO: Dry me
|
|
680
|
-
/**
|
|
681
|
-
* @param {string} pAddress - The address being evaluated
|
|
682
|
-
* @param {object} pRecord - The record being evaluated
|
|
683
|
-
*
|
|
684
|
-
* @return {boolean} True if the record passes the filters, false if it does not
|
|
685
|
-
*/
|
|
686
|
-
checkRecordFilters(pAddress, pRecord) {
|
|
687
|
-
return fParseConditionals(this, pAddress, pRecord);
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
/**
|
|
691
|
-
* Delete the value of an element at an address
|
|
692
|
-
*
|
|
693
|
-
* @param {object} pObject - The object to delete the value from
|
|
694
|
-
* @param {string} pAddress - The address to delete the value at
|
|
695
|
-
* @param {string} [pParentAddress] - (optional) The parent address for recursion
|
|
696
|
-
*
|
|
697
|
-
* @return {boolean|object|undefined} - True if the value was deleted, false if it could not be deleted, undefined on error
|
|
698
|
-
*/
|
|
699
|
-
deleteValueAtAddress(pObject, pAddress, pParentAddress) {
|
|
700
|
-
// Make sure pObject (the object we are meant to be recursing) is an object (which could be an array or object)
|
|
701
|
-
if (typeof pObject != 'object') return undefined;
|
|
702
|
-
// Make sure pAddress (the address we are resolving) is a string
|
|
703
|
-
if (typeof pAddress != 'string') return undefined;
|
|
704
|
-
// Stash the parent address for later resolution
|
|
705
|
-
let tmpParentAddress = "";
|
|
706
|
-
if (typeof pParentAddress == 'string') {
|
|
707
|
-
tmpParentAddress = pParentAddress;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// TODO: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
|
|
711
|
-
let tmpSeparatorIndex = pAddress.indexOf('.');
|
|
712
|
-
|
|
713
|
-
// This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
|
|
714
|
-
if (tmpSeparatorIndex == -1) {
|
|
715
|
-
// Check if the address refers to a boxed property
|
|
716
|
-
let tmpBracketStartIndex = pAddress.indexOf('[');
|
|
717
|
-
let tmpBracketStopIndex = pAddress.indexOf(']');
|
|
718
|
-
|
|
719
|
-
// Check for the Object Set Type marker.
|
|
720
|
-
// Note this will not work with a bracket in the same address box set
|
|
721
|
-
let tmpObjectTypeMarkerIndex = pAddress.indexOf('{}');
|
|
722
|
-
|
|
723
|
-
// Boxed elements look like this:
|
|
724
|
-
// MyValues[10]
|
|
725
|
-
// MyValues['Name']
|
|
726
|
-
// MyValues["Age"]
|
|
727
|
-
// MyValues[`Cost`]
|
|
728
|
-
//
|
|
729
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
730
|
-
// The requirements to detect a boxed element are:
|
|
731
|
-
// 1) The start bracket is after character 0
|
|
732
|
-
if (tmpBracketStartIndex > 0
|
|
733
|
-
// 2) The end bracket has something between them
|
|
734
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
735
|
-
// 3) There is data
|
|
736
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex > 1) {
|
|
737
|
-
// The "Name" of the Object contained too the left of the bracket
|
|
738
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
739
|
-
|
|
740
|
-
// If the subproperty doesn't test as a proper Object, none of the rest of this is possible.
|
|
741
|
-
// This is a rare case where Arrays testing as Objects is useful
|
|
742
|
-
if (typeof pObject[tmpBoxedPropertyName] !== 'object') {
|
|
743
|
-
return false;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// The "Reference" to the property within it, either an array element or object property
|
|
747
|
-
let tmpBoxedPropertyReference = pAddress.substring(tmpBracketStartIndex + 1, tmpBracketStopIndex).trim();
|
|
748
|
-
// Attempt to parse the reference as a number, which will be used as an array element
|
|
749
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
750
|
-
|
|
751
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
752
|
-
// This seems confusing to me at first read, so explaination:
|
|
753
|
-
// Is the Boxed Object an Array? TRUE
|
|
754
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
755
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
756
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber)) {
|
|
757
|
-
return false;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
761
|
-
// otherwise we will try to treat it as a dynamic object property.
|
|
762
|
-
if (isNaN(tmpBoxedPropertyNumber)) {
|
|
763
|
-
// This isn't a number ... let's treat it as a dynamic object property.
|
|
764
|
-
// We would expect the property to be wrapped in some kind of quotes so strip them
|
|
765
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
766
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
767
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
768
|
-
|
|
769
|
-
// Return the value in the property
|
|
770
|
-
delete pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference];
|
|
771
|
-
return true;
|
|
772
|
-
} else {
|
|
773
|
-
delete pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber];
|
|
774
|
-
return true;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
// The requirements to detect a boxed set element are:
|
|
778
|
-
// 1) The start bracket is after character 0
|
|
779
|
-
else if (tmpBracketStartIndex > 0
|
|
780
|
-
// 2) The end bracket is after the start bracket
|
|
781
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
782
|
-
// 3) There is nothing in the brackets
|
|
783
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex == 1) {
|
|
784
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
785
|
-
if (!Array.isArray(pObject[tmpBoxedPropertyName])) {
|
|
786
|
-
// We asked for a set from an array but it isnt' an array.
|
|
787
|
-
return false;
|
|
788
|
-
}
|
|
789
|
-
let tmpInputArray = pObject[tmpBoxedPropertyName];
|
|
790
|
-
// Count from the end to the beginning so splice doesn't %&%#$ up the array
|
|
791
|
-
for (let i = tmpInputArray.length - 1; i >= 0; i--) {
|
|
792
|
-
// The filtering is complex but allows config-based metaprogramming directly from schema
|
|
793
|
-
let tmpKeepRecord = this.checkRecordFilters(pAddress, tmpInputArray[i]);
|
|
794
|
-
if (tmpKeepRecord) {
|
|
795
|
-
// Delete elements end to beginning
|
|
796
|
-
tmpInputArray.splice(i, 1);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
return true;
|
|
800
|
-
}
|
|
801
|
-
// The object has been flagged as an object set, so treat it as such
|
|
802
|
-
else if (tmpObjectTypeMarkerIndex > 0) {
|
|
803
|
-
let tmpObjectPropertyName = pAddress.substring(0, tmpObjectTypeMarkerIndex).trim();
|
|
804
|
-
if (typeof pObject[tmpObjectPropertyName] != 'object') {
|
|
805
|
-
// We asked for a set from an array but it isnt' an array.
|
|
806
|
-
return false;
|
|
807
|
-
}
|
|
808
|
-
delete pObject[tmpObjectPropertyName];
|
|
809
|
-
return true;
|
|
810
|
-
} else {
|
|
811
|
-
// Now is the point in recursion to return the value in the address
|
|
812
|
-
delete pObject[pAddress];
|
|
813
|
-
return true;
|
|
814
|
-
}
|
|
815
|
-
} else {
|
|
816
|
-
let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
|
|
817
|
-
let tmpNewAddress = pAddress.substring(tmpSeparatorIndex + 1);
|
|
818
|
-
|
|
819
|
-
// BOXED ELEMENTS
|
|
820
|
-
// Test if the tmpNewAddress is an array or object
|
|
821
|
-
// Check if it's a boxed property
|
|
822
|
-
let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
|
|
823
|
-
let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
|
|
824
|
-
// Boxed elements look like this:
|
|
825
|
-
// MyValues[42]
|
|
826
|
-
// MyValues['Color']
|
|
827
|
-
// MyValues["Weight"]
|
|
828
|
-
// MyValues[`Diameter`]
|
|
829
|
-
//
|
|
830
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
831
|
-
// The requirements to detect a boxed element are:
|
|
832
|
-
// 1) The start bracket is after character 0
|
|
833
|
-
if (tmpBracketStartIndex > 0
|
|
834
|
-
// 2) The end bracket has something between them
|
|
835
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
836
|
-
// 3) There is data
|
|
837
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex > 1) {
|
|
838
|
-
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
839
|
-
let tmpBoxedPropertyReference = tmpSubObjectName.substring(tmpBracketStartIndex + 1, tmpBracketStopIndex).trim();
|
|
840
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
841
|
-
|
|
842
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
843
|
-
// This seems confusing to me at first read, so explaination:
|
|
844
|
-
// Is the Boxed Object an Array? TRUE
|
|
845
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
846
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
847
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
848
|
-
// StudentData.Sections.Algebra.Students[1].Tardy
|
|
849
|
-
// BUT
|
|
850
|
-
// StudentData.Sections.Algebra.Students is an object, so the [1].Tardy is not possible to access
|
|
851
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
852
|
-
// StudentData.Sections.Algebra.Students["JaneDoe"].Grade
|
|
853
|
-
// BUT
|
|
854
|
-
// StudentData.Sections.Algebra.Students is an array, so the ["JaneDoe"].Grade is not possible to access
|
|
855
|
-
// TODO: Should this be an error or something? Should we keep a log of failures like this?
|
|
856
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber)) {
|
|
857
|
-
return false;
|
|
858
|
-
}
|
|
859
|
-
// Check if the boxed property is an object.
|
|
860
|
-
if (typeof pObject[tmpBoxedPropertyName] != 'object') {
|
|
861
|
-
return false;
|
|
862
|
-
}
|
|
863
|
-
//This is a bracketed value
|
|
864
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
865
|
-
// otherwise we will try to reat it as a dynamic object property.
|
|
866
|
-
if (isNaN(tmpBoxedPropertyNumber)) {
|
|
867
|
-
// This isn't a number ... let's treat it as a dynanmic object property.
|
|
868
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
869
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
870
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
871
|
-
|
|
872
|
-
// Continue to manage the parent address for recursion
|
|
873
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
874
|
-
// Recurse directly into the subobject
|
|
875
|
-
return this.deleteValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress, tmpParentAddress);
|
|
876
|
-
} else {
|
|
877
|
-
// Continue to manage the parent address for recursion
|
|
878
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
879
|
-
// We parsed a valid number out of the boxed property name, so recurse into the array
|
|
880
|
-
return this.deleteValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress, tmpParentAddress);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
// The requirements to detect a boxed set element are:
|
|
884
|
-
// 1) The start bracket is after character 0
|
|
885
|
-
else if (tmpBracketStartIndex > 0
|
|
886
|
-
// 2) The end bracket is after the start bracket
|
|
887
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
888
|
-
// 3) There is nothing in the brackets
|
|
889
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex == 1) {
|
|
890
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
891
|
-
if (!Array.isArray(pObject[tmpBoxedPropertyName])) {
|
|
892
|
-
// We asked for a set from an array but it isnt' an array.
|
|
893
|
-
return false;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// We need to enumerate the array and grab the addresses from there.
|
|
897
|
-
let tmpArrayProperty = pObject[tmpBoxedPropertyName];
|
|
898
|
-
// Managing the parent address is a bit more complex here -- the box will be added for each element.
|
|
899
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpBoxedPropertyName);
|
|
900
|
-
// The container object is where we have the "Address":SOMEVALUE pairs
|
|
901
|
-
let tmpContainerObject = {};
|
|
902
|
-
for (let i = 0; i < tmpArrayProperty.length; i++) {
|
|
903
|
-
let tmpPropertyParentAddress = "".concat(tmpParentAddress, "[").concat(i, "]");
|
|
904
|
-
let tmpValue = this.deleteValueAtAddress(pObject[tmpBoxedPropertyName][i], tmpNewAddress, tmpPropertyParentAddress);
|
|
905
|
-
tmpContainerObject["".concat(tmpPropertyParentAddress, ".").concat(tmpNewAddress)] = tmpValue;
|
|
906
|
-
}
|
|
907
|
-
return tmpContainerObject;
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
// OBJECT SET
|
|
911
|
-
// Note this will not work with a bracket in the same address box set
|
|
912
|
-
let tmpObjectTypeMarkerIndex = pAddress.indexOf('{}');
|
|
913
|
-
if (tmpObjectTypeMarkerIndex > 0) {
|
|
914
|
-
let tmpObjectPropertyName = pAddress.substring(0, tmpObjectTypeMarkerIndex).trim();
|
|
915
|
-
if (typeof pObject[tmpObjectPropertyName] != 'object') {
|
|
916
|
-
// We asked for a set from an array but it isnt' an array.
|
|
917
|
-
return false;
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// We need to enumerate the Object and grab the addresses from there.
|
|
921
|
-
let tmpObjectProperty = pObject[tmpObjectPropertyName];
|
|
922
|
-
let tmpObjectPropertyKeys = Object.keys(tmpObjectProperty);
|
|
923
|
-
// Managing the parent address is a bit more complex here -- the box will be added for each element.
|
|
924
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpObjectPropertyName);
|
|
925
|
-
// The container object is where we have the "Address":SOMEVALUE pairs
|
|
926
|
-
let tmpContainerObject = {};
|
|
927
|
-
for (let i = 0; i < tmpObjectPropertyKeys.length; i++) {
|
|
928
|
-
let tmpPropertyParentAddress = "".concat(tmpParentAddress, ".").concat(tmpObjectPropertyKeys[i]);
|
|
929
|
-
let tmpValue = this.deleteValueAtAddress(pObject[tmpObjectPropertyName][tmpObjectPropertyKeys[i]], tmpNewAddress, tmpPropertyParentAddress);
|
|
930
|
-
|
|
931
|
-
// The filtering is complex but allows config-based metaprogramming directly from schema
|
|
932
|
-
let tmpKeepRecord = this.checkRecordFilters(pAddress, tmpValue);
|
|
933
|
-
if (tmpKeepRecord) {
|
|
934
|
-
tmpContainerObject["".concat(tmpPropertyParentAddress, ".").concat(tmpNewAddress)] = tmpValue;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
return tmpContainerObject;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
// If there is an object property already named for the sub object, but it isn't an object
|
|
941
|
-
// then the system can't set the value in there. Error and abort!
|
|
942
|
-
if (tmpSubObjectName in pObject && typeof pObject[tmpSubObjectName] !== 'object') {
|
|
943
|
-
return undefined;
|
|
944
|
-
} else if (tmpSubObjectName in pObject) {
|
|
945
|
-
// If there is already a subobject pass that to the recursive thingy
|
|
946
|
-
// Continue to manage the parent address for recursion
|
|
947
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
948
|
-
return this.deleteValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, tmpParentAddress);
|
|
949
|
-
} else {
|
|
950
|
-
// Create a subobject and then pass that
|
|
951
|
-
// Continue to manage the parent address for recursion
|
|
952
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
953
|
-
pObject[tmpSubObjectName] = {};
|
|
954
|
-
return this.deleteValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, tmpParentAddress);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
;
|
|
960
|
-
module.exports = ManyfestObjectAddressResolverDeleteValue;
|
|
961
|
-
}, {
|
|
962
|
-
"../source/Manyfest-ParseConditionals.js": 12,
|
|
963
|
-
"./Manyfest-CleanWrapCharacters.js": 3,
|
|
964
|
-
"./Manyfest-LogToConsole.js": 5
|
|
965
|
-
}],
|
|
966
|
-
8: [function (require, module, exports) {
|
|
967
|
-
/**
|
|
968
|
-
* @author <steven@velozo.com>
|
|
969
|
-
*/
|
|
970
|
-
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
971
|
-
let fCleanWrapCharacters = require('./Manyfest-CleanWrapCharacters.js');
|
|
972
|
-
let fParseConditionals = require("../source/Manyfest-ParseConditionals.js");
|
|
973
|
-
let _MockFable = {
|
|
974
|
-
DataFormat: require('./Manyfest-ObjectAddress-Parser.js')
|
|
975
|
-
};
|
|
976
|
-
|
|
977
|
-
/**
|
|
978
|
-
* Object Address Resolver - GetValue
|
|
979
|
-
*
|
|
980
|
-
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
981
|
-
* be extremely clear what is going on in the recursion for
|
|
982
|
-
* each of the three address resolution functions.
|
|
983
|
-
*
|
|
984
|
-
* Although there is some opportunity to repeat ourselves a
|
|
985
|
-
* bit less in this codebase (e.g. with detection of arrays
|
|
986
|
-
* versus objects versus direct properties), it can make
|
|
987
|
-
* debugging.. challenging. The minified version of the code
|
|
988
|
-
* optimizes out almost anything repeated in here. So please
|
|
989
|
-
* be kind and rewind... meaning please keep the codebase less
|
|
990
|
-
* terse and more verbose so humans can comprehend it.
|
|
991
|
-
*
|
|
992
|
-
* TODO: Once we validate this pattern is good to go, break these out into
|
|
993
|
-
* three separate modules.
|
|
994
|
-
*
|
|
995
|
-
* @class ManyfestObjectAddressResolverGetValue
|
|
996
|
-
*/
|
|
997
|
-
class ManyfestObjectAddressResolverGetValue {
|
|
998
|
-
/**
|
|
999
|
-
* @param {function} [pInfoLog] - (optional) A logging function for info messages
|
|
1000
|
-
* @param {function} [pErrorLog] - (optional) A logging function for error messages
|
|
1001
|
-
*/
|
|
1002
|
-
constructor(pInfoLog, pErrorLog) {
|
|
1003
|
-
// Wire in logging
|
|
1004
|
-
this.logInfo = typeof pInfoLog == 'function' ? pInfoLog : libSimpleLog;
|
|
1005
|
-
this.logError = typeof pErrorLog == 'function' ? pErrorLog : libSimpleLog;
|
|
1006
|
-
this.cleanWrapCharacters = fCleanWrapCharacters;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
/**
|
|
1010
|
-
* @param {string} pAddress - The address of the record to check
|
|
1011
|
-
* @param {object} pRecord - The record to check against the filters
|
|
1012
|
-
*
|
|
1013
|
-
* @return {boolean} - True if the record passes the filters, false otherwise
|
|
1014
|
-
*/
|
|
1015
|
-
checkRecordFilters(pAddress, pRecord) {
|
|
1016
|
-
return fParseConditionals(this, pAddress, pRecord);
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
/**
|
|
1020
|
-
* Get the value of an element at an address
|
|
1021
|
-
*
|
|
1022
|
-
* @param {object} pObject - The object to resolve the address against
|
|
1023
|
-
* @param {string} pAddress - The address to resolve
|
|
1024
|
-
* @param {string} [pParentAddress] - (optional) The parent address for back-navigation
|
|
1025
|
-
* @param {object} [pRootObject] - (optional) The root object for function argument resolution
|
|
1026
|
-
*
|
|
1027
|
-
* @return {any} The value at the address, or undefined if not found
|
|
1028
|
-
*/
|
|
1029
|
-
getValueAtAddress(pObject, pAddress, pParentAddress, pRootObject) {
|
|
1030
|
-
// Make sure pObject (the object we are meant to be recursing) is an object (which could be an array or object)
|
|
1031
|
-
if (typeof pObject != 'object') {
|
|
1032
|
-
return undefined;
|
|
1033
|
-
}
|
|
1034
|
-
if (pObject === null) {
|
|
1035
|
-
return undefined;
|
|
1036
|
-
}
|
|
1037
|
-
// Make sure pAddress (the address we are resolving) is a string
|
|
1038
|
-
if (typeof pAddress != 'string') {
|
|
1039
|
-
return undefined;
|
|
1040
|
-
}
|
|
1041
|
-
// Stash the parent address for later resolution
|
|
1042
|
-
let tmpParentAddress = "";
|
|
1043
|
-
if (typeof pParentAddress == 'string') {
|
|
1044
|
-
tmpParentAddress = pParentAddress;
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
// Set the root object to the passed-in object if it isn't set yet. This is expected to be the root object.
|
|
1048
|
-
let tmpRootObject = typeof pRootObject == 'undefined' ? pObject : pRootObject;
|
|
1049
|
-
|
|
1050
|
-
// DONE: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
|
|
1051
|
-
let tmpAddressPartBeginning = _MockFable.DataFormat.stringGetFirstSegment(pAddress);
|
|
1052
|
-
|
|
1053
|
-
// Adding simple back-navigation in objects
|
|
1054
|
-
if (tmpAddressPartBeginning == '') {
|
|
1055
|
-
// Given an address of "Bundle.Contract.IDContract...Project.IDProject" the ... would be interpreted as two back-navigations from IDContract.
|
|
1056
|
-
// When the address is passed in, though, the first . is already eliminated. So we can count the dots.
|
|
1057
|
-
let tmpParentAddressParts = _MockFable.DataFormat.stringGetSegments(tmpParentAddress);
|
|
1058
|
-
let tmpBackNavigationCount = 0;
|
|
1059
|
-
|
|
1060
|
-
// Count the number of dots
|
|
1061
|
-
for (let i = 0; i < pAddress.length; i++) {
|
|
1062
|
-
if (pAddress.charAt(i) != '.') {
|
|
1063
|
-
break;
|
|
1064
|
-
}
|
|
1065
|
-
tmpBackNavigationCount++;
|
|
1066
|
-
}
|
|
1067
|
-
let tmpParentAddressLength = tmpParentAddressParts.length - tmpBackNavigationCount;
|
|
1068
|
-
if (tmpParentAddressLength < 0) {
|
|
1069
|
-
// We are trying to back navigate more than we can.
|
|
1070
|
-
// TODO: Should this be undefined or should we bank out at the bottom and try to go forward?
|
|
1071
|
-
// This seems safest for now.
|
|
1072
|
-
return undefined;
|
|
1073
|
-
} else {
|
|
1074
|
-
// We are trying to back navigate to a parent object.
|
|
1075
|
-
// Recurse with the back-propagated parent address, and, the new address without the back-navigation dots.
|
|
1076
|
-
let tmpRecurseAddress = pAddress.slice(tmpBackNavigationCount);
|
|
1077
|
-
if (tmpParentAddressLength > 0) {
|
|
1078
|
-
tmpRecurseAddress = "".concat(tmpParentAddressParts.slice(0, tmpParentAddressLength).join('.'), ".").concat(tmpRecurseAddress);
|
|
1079
|
-
}
|
|
1080
|
-
this.logInfo("Back-navigation detected. Recursing back to address [".concat(tmpRecurseAddress, "]"));
|
|
1081
|
-
return this.getValueAtAddress(tmpRootObject, tmpRecurseAddress);
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
// This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
|
|
1086
|
-
if (tmpAddressPartBeginning.length == pAddress.length) {
|
|
1087
|
-
// TODO: Optimize this by having these calls only happen when the previous fails.
|
|
1088
|
-
// TODO: Alternatively look for all markers in one pass?
|
|
1089
|
-
// Check if the address refers to a boxed property
|
|
1090
|
-
let tmpBracketStartIndex = pAddress.indexOf('[');
|
|
1091
|
-
let tmpBracketStopIndex = pAddress.indexOf(']');
|
|
1092
|
-
|
|
1093
|
-
// Check for the Object Set Type marker.
|
|
1094
|
-
// Note this will not work with a bracket in the same address box set
|
|
1095
|
-
let tmpObjectTypeMarkerIndex = pAddress.indexOf('{}');
|
|
1096
|
-
|
|
1097
|
-
// Check if there is a function somewhere in the address... parenthesis start should only be in a function
|
|
1098
|
-
let tmpFunctionStartIndex = pAddress.indexOf('(');
|
|
1099
|
-
|
|
1100
|
-
// NOTE THAT FUNCTIONS MUST RESOLVE FIRST
|
|
1101
|
-
// Functions look like this
|
|
1102
|
-
// MyFunction()
|
|
1103
|
-
// MyFunction(Some.Address)
|
|
1104
|
-
// MyFunction(Some.Address,Some.Other.Address)
|
|
1105
|
-
// MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
|
|
1106
|
-
//
|
|
1107
|
-
// This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
|
|
1108
|
-
// To heck with that. This is a simple function call.
|
|
1109
|
-
//
|
|
1110
|
-
// The requirements to detect a function are:
|
|
1111
|
-
// 1) The start bracket is after character 0
|
|
1112
|
-
if (tmpFunctionStartIndex > 0
|
|
1113
|
-
// 2) The end bracket is after the start bracket
|
|
1114
|
-
&& _MockFable.DataFormat.stringCountEnclosures(pAddress) > 0) {
|
|
1115
|
-
let tmpFunctionAddress = pAddress.substring(0, tmpFunctionStartIndex).trim();
|
|
1116
|
-
if (typeof pObject[tmpFunctionAddress] !== 'function') {
|
|
1117
|
-
// The address suggests it is a function, but it is not.
|
|
1118
|
-
return false;
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
// Now see if the function has arguments.
|
|
1122
|
-
// Implementation notes: * ARGUMENTS MUST SHARE THE SAME ROOT OBJECT CONTEXT *
|
|
1123
|
-
let tmpFunctionArguments = _MockFable.DataFormat.stringGetSegments(_MockFable.DataFormat.stringGetEnclosureValueByIndex(pAddress.substring(tmpFunctionAddress.length), 0), ',');
|
|
1124
|
-
if (tmpFunctionArguments.length == 0 || tmpFunctionArguments[0] == '') {
|
|
1125
|
-
// No arguments... just call the function (bound to the scope of the object it is contained withing)
|
|
1126
|
-
if (tmpFunctionAddress in pObject) {
|
|
1127
|
-
try {
|
|
1128
|
-
return pObject[tmpFunctionAddress].apply(pObject);
|
|
1129
|
-
} catch (pError) {
|
|
1130
|
-
// The function call failed, so the address doesn't exist
|
|
1131
|
-
console.log("Error in getValueAtAddress calling function ".concat(tmpFunctionAddress, " (address [").concat(pAddress, "]): ").concat(pError.message));
|
|
1132
|
-
return false;
|
|
1133
|
-
}
|
|
1134
|
-
} else {
|
|
1135
|
-
// The function doesn't exist, so the address doesn't exist
|
|
1136
|
-
console.log("Function ".concat(tmpFunctionAddress, " does not exist (address [").concat(pAddress, "])"));
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
} else {
|
|
1140
|
-
let tmpArgumentValues = [];
|
|
1141
|
-
let tmpRootObject = typeof pRootObject == 'undefined' ? pObject : pRootObject;
|
|
1142
|
-
|
|
1143
|
-
// Now get the value for each argument
|
|
1144
|
-
for (let i = 0; i < tmpFunctionArguments.length; i++) {
|
|
1145
|
-
// Resolve the values for each subsequent entry
|
|
1146
|
-
// Check if the argument value is a string literal or a reference to an address
|
|
1147
|
-
if (tmpFunctionArguments[i].length >= 2 && (tmpFunctionArguments[i].charAt(0) == '"' || tmpFunctionArguments[i].charAt(0) == "'" || tmpFunctionArguments[i].charAt(0) == "`") && (tmpFunctionArguments[i].charAt(tmpFunctionArguments[i].length - 1) == '"' || tmpFunctionArguments[i].charAt(tmpFunctionArguments[i].length - 1) == "'" || tmpFunctionArguments[i].charAt(tmpFunctionArguments[i].length - 1) == "`")) {
|
|
1148
|
-
// This is a string literal
|
|
1149
|
-
tmpArgumentValues.push(tmpFunctionArguments[i].substring(1, tmpFunctionArguments[i].length - 1));
|
|
1150
|
-
} else {
|
|
1151
|
-
// This is a hash address
|
|
1152
|
-
tmpArgumentValues.push(this.getValueAtAddress(tmpRootObject, tmpFunctionArguments[i]));
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
if (tmpFunctionAddress in pObject) {
|
|
1156
|
-
try {
|
|
1157
|
-
return pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues);
|
|
1158
|
-
} catch (pError) {
|
|
1159
|
-
// The function call failed, so the address doesn't exist
|
|
1160
|
-
console.log("Error in getValueAtAddress calling function ".concat(tmpFunctionAddress, " (address [").concat(pAddress, "]): ").concat(pError.message));
|
|
1161
|
-
return false;
|
|
1162
|
-
}
|
|
1163
|
-
} else {
|
|
1164
|
-
// The function doesn't exist, so the address doesn't exist
|
|
1165
|
-
console.log("Function ".concat(tmpFunctionAddress, " does not exist (address [").concat(pAddress, "])"));
|
|
1166
|
-
return false;
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
// Boxed elements look like this:
|
|
1171
|
-
// MyValues[10]
|
|
1172
|
-
// MyValues['Name']
|
|
1173
|
-
// MyValues["Age"]
|
|
1174
|
-
// MyValues[`Cost`]
|
|
1175
|
-
//
|
|
1176
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
1177
|
-
// The requirements to detect a boxed element are:
|
|
1178
|
-
// 1) The start bracket is after character 0
|
|
1179
|
-
else if (tmpBracketStartIndex > 0
|
|
1180
|
-
// 2) The end bracket has something between them
|
|
1181
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
1182
|
-
// 3) There is data
|
|
1183
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex > 1) {
|
|
1184
|
-
// The "Name" of the Object contained too the left of the bracket
|
|
1185
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
1186
|
-
|
|
1187
|
-
// If the subproperty doesn't test as a proper Object, none of the rest of this is possible.
|
|
1188
|
-
// This is a rare case where Arrays testing as Objects is useful
|
|
1189
|
-
if (typeof pObject[tmpBoxedPropertyName] !== 'object') {
|
|
1190
|
-
return undefined;
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
// The "Reference" to the property within it, either an array element or object property
|
|
1194
|
-
let tmpBoxedPropertyReference = pAddress.substring(tmpBracketStartIndex + 1, tmpBracketStopIndex).trim();
|
|
1195
|
-
// Attempt to parse the reference as a number, which will be used as an array element
|
|
1196
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
1197
|
-
|
|
1198
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
1199
|
-
// This seems confusing to me at first read, so explaination:
|
|
1200
|
-
// Is the Boxed Object an Array? TRUE
|
|
1201
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
1202
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
1203
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber)) {
|
|
1204
|
-
return undefined;
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
1208
|
-
// otherwise we will try to treat it as a dynamic object property.
|
|
1209
|
-
if (isNaN(tmpBoxedPropertyNumber)) {
|
|
1210
|
-
// This isn't a number ... let's treat it as a dynamic object property.
|
|
1211
|
-
// We would expect the property to be wrapped in some kind of quotes so strip them
|
|
1212
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
1213
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
1214
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
1215
|
-
|
|
1216
|
-
// Return the value in the property
|
|
1217
|
-
return pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference];
|
|
1218
|
-
} else {
|
|
1219
|
-
return pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber];
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
// The requirements to detect a boxed set element are:
|
|
1223
|
-
// 1) The start bracket is after character 0
|
|
1224
|
-
else if (tmpBracketStartIndex > 0
|
|
1225
|
-
// 2) The end bracket is after the start bracket
|
|
1226
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
1227
|
-
// 3) There is nothing in the brackets
|
|
1228
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex == 1) {
|
|
1229
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
1230
|
-
if (!Array.isArray(pObject[tmpBoxedPropertyName])) {
|
|
1231
|
-
// We asked for a set from an array but it isnt' an array.
|
|
1232
|
-
return false;
|
|
1233
|
-
}
|
|
1234
|
-
let tmpInputArray = pObject[tmpBoxedPropertyName];
|
|
1235
|
-
let tmpOutputArray = [];
|
|
1236
|
-
for (let i = 0; i < tmpInputArray.length; i++) {
|
|
1237
|
-
// The filtering is complex but allows config-based metaprogramming directly from schema
|
|
1238
|
-
let tmpKeepRecord = this.checkRecordFilters(pAddress, tmpInputArray[i]);
|
|
1239
|
-
if (tmpKeepRecord) {
|
|
1240
|
-
tmpOutputArray.push(tmpInputArray[i]);
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
return tmpOutputArray;
|
|
1244
|
-
}
|
|
1245
|
-
// The object has been flagged as an object set, so treat it as such
|
|
1246
|
-
else if (tmpObjectTypeMarkerIndex > 0) {
|
|
1247
|
-
let tmpObjectPropertyName = pAddress.substring(0, tmpObjectTypeMarkerIndex).trim();
|
|
1248
|
-
if (typeof pObject[tmpObjectPropertyName] != 'object') {
|
|
1249
|
-
// We asked for a set from an array but it isnt' an array.
|
|
1250
|
-
return false;
|
|
1251
|
-
}
|
|
1252
|
-
return pObject[tmpObjectPropertyName];
|
|
1253
|
-
} else {
|
|
1254
|
-
// Now is the point in recursion to return the value in the address
|
|
1255
|
-
if (typeof pObject[pAddress] != null) {
|
|
1256
|
-
return pObject[pAddress];
|
|
1257
|
-
} else {
|
|
1258
|
-
return null;
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
} else {
|
|
1262
|
-
//let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
|
|
1263
|
-
//let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
|
|
1264
|
-
let tmpSubObjectName = tmpAddressPartBeginning;
|
|
1265
|
-
let tmpNewAddress = pAddress.substring(tmpAddressPartBeginning.length + 1);
|
|
1266
|
-
|
|
1267
|
-
// BOXED ELEMENTS
|
|
1268
|
-
// Test if the tmpNewAddress is an array or object
|
|
1269
|
-
// Check if it's a boxed property
|
|
1270
|
-
let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
|
|
1271
|
-
let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
|
|
1272
|
-
|
|
1273
|
-
// Check if there is a function somewhere in the address... parenthesis start should only be in a function
|
|
1274
|
-
let tmpFunctionStartIndex = tmpSubObjectName.indexOf('(');
|
|
1275
|
-
|
|
1276
|
-
// NOTE THAT FUNCTIONS MUST RESOLVE FIRST
|
|
1277
|
-
// Functions look like this
|
|
1278
|
-
// MyFunction()
|
|
1279
|
-
// MyFunction(Some.Address)
|
|
1280
|
-
// MyFunction(Some.Address,Some.Other.Address)
|
|
1281
|
-
// MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
|
|
1282
|
-
//
|
|
1283
|
-
// This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
|
|
1284
|
-
// To heck with that. This is a simple function call.
|
|
1285
|
-
//
|
|
1286
|
-
// The requirements to detect a function are:
|
|
1287
|
-
// 1) The start bracket is after character 0
|
|
1288
|
-
if (tmpFunctionStartIndex > 0
|
|
1289
|
-
// 2) The end bracket is after the start bracket
|
|
1290
|
-
&& _MockFable.DataFormat.stringCountEnclosures(tmpSubObjectName) > 0) {
|
|
1291
|
-
let tmpFunctionAddress = tmpSubObjectName.substring(0, tmpFunctionStartIndex).trim();
|
|
1292
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
1293
|
-
if (typeof pObject[tmpFunctionAddress] !== 'function') {
|
|
1294
|
-
// The address suggests it is a function, but it is not.
|
|
1295
|
-
return false;
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
// Now see if the function has arguments.
|
|
1299
|
-
// Implementation notes: * ARGUMENTS MUST SHARE THE SAME ROOT OBJECT CONTEXT *
|
|
1300
|
-
let tmpFunctionArguments = _MockFable.DataFormat.stringGetSegments(_MockFable.DataFormat.stringGetEnclosureValueByIndex(tmpSubObjectName.substring(tmpFunctionAddress.length), 0), ',');
|
|
1301
|
-
if (tmpFunctionArguments.length == 0 || tmpFunctionArguments[0] == '') {
|
|
1302
|
-
// No arguments... just call the function (bound to the scope of the object it is contained withing)
|
|
1303
|
-
if (tmpFunctionAddress in pObject) {
|
|
1304
|
-
try {
|
|
1305
|
-
return this.getValueAtAddress(pObject[tmpFunctionAddress].apply(pObject), tmpNewAddress, tmpParentAddress, tmpRootObject);
|
|
1306
|
-
} catch (pError) {
|
|
1307
|
-
// The function call failed, so the address doesn't exist
|
|
1308
|
-
console.log("Error in getValueAtAddress calling function ".concat(tmpFunctionAddress, " (address [").concat(pAddress, "]): ").concat(pError.message));
|
|
1309
|
-
return false;
|
|
1310
|
-
}
|
|
1311
|
-
} else {
|
|
1312
|
-
// The function doesn't exist, so the address doesn't exist
|
|
1313
|
-
console.log("Function ".concat(tmpFunctionAddress, " does not exist (address [").concat(pAddress, "])"));
|
|
1314
|
-
return false;
|
|
1315
|
-
}
|
|
1316
|
-
} else {
|
|
1317
|
-
let tmpArgumentValues = [];
|
|
1318
|
-
let tmpRootObject = typeof pRootObject == 'undefined' ? pObject : pRootObject;
|
|
1319
|
-
|
|
1320
|
-
// Now get the value for each argument
|
|
1321
|
-
for (let i = 0; i < tmpFunctionArguments.length; i++) {
|
|
1322
|
-
// Resolve the values for each subsequent entry
|
|
1323
|
-
// Check if the argument value is a string literal or a reference to an address
|
|
1324
|
-
if (tmpFunctionArguments[i].length >= 2 && (tmpFunctionArguments[i].charAt(0) == '"' || tmpFunctionArguments[i].charAt(0) == "'" || tmpFunctionArguments[i].charAt(0) == "`") && (tmpFunctionArguments[i].charAt(tmpFunctionArguments[i].length - 1) == '"' || tmpFunctionArguments[i].charAt(tmpFunctionArguments[i].length - 1) == "'" || tmpFunctionArguments[i].charAt(tmpFunctionArguments[i].length - 1) == "`")) {
|
|
1325
|
-
// This is a string literal
|
|
1326
|
-
tmpArgumentValues.push(tmpFunctionArguments[i].substring(1, tmpFunctionArguments[i].length - 1));
|
|
1327
|
-
} else {
|
|
1328
|
-
// This is a hash address
|
|
1329
|
-
tmpArgumentValues.push(this.getValueAtAddress(tmpRootObject, tmpFunctionArguments[i]));
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
if (tmpFunctionAddress in pObject) {
|
|
1333
|
-
try {
|
|
1334
|
-
return this.getValueAtAddress(pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues), tmpNewAddress, tmpParentAddress, tmpRootObject);
|
|
1335
|
-
} catch (pError) {
|
|
1336
|
-
// The function call failed, so the address doesn't exist
|
|
1337
|
-
console.log("Error in getValueAtAddress calling function ".concat(tmpFunctionAddress, " (address [").concat(pAddress, "]): ").concat(pError.message));
|
|
1338
|
-
return false;
|
|
1339
|
-
}
|
|
1340
|
-
} else {
|
|
1341
|
-
// The function doesn't exist, so the address doesn't exist
|
|
1342
|
-
console.log("Function ".concat(tmpFunctionAddress, " does not exist (address [").concat(pAddress, "])"));
|
|
1343
|
-
return false;
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
// Boxed elements look like this:
|
|
1348
|
-
// MyValues[42]
|
|
1349
|
-
// MyValues['Color']
|
|
1350
|
-
// MyValues["Weight"]
|
|
1351
|
-
// MyValues[`Diameter`]
|
|
1352
|
-
//
|
|
1353
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
1354
|
-
// The requirements to detect a boxed element are:
|
|
1355
|
-
// 1) The start bracket is after character 0
|
|
1356
|
-
else if (tmpBracketStartIndex > 0
|
|
1357
|
-
// 2) The end bracket has something between them
|
|
1358
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
1359
|
-
// 3) There is data
|
|
1360
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex > 1) {
|
|
1361
|
-
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
1362
|
-
let tmpBoxedPropertyReference = tmpSubObjectName.substring(tmpBracketStartIndex + 1, tmpBracketStopIndex).trim();
|
|
1363
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
1364
|
-
|
|
1365
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
1366
|
-
// This seems confusing to me at first read, so explaination:
|
|
1367
|
-
// Is the Boxed Object an Array? TRUE
|
|
1368
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
1369
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
1370
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
1371
|
-
// StudentData.Sections.Algebra.Students[1].Tardy
|
|
1372
|
-
// BUT
|
|
1373
|
-
// StudentData.Sections.Algebra.Students is an object, so the [1].Tardy is not possible to access
|
|
1374
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
1375
|
-
// StudentData.Sections.Algebra.Students["JaneDoe"].Grade
|
|
1376
|
-
// BUT
|
|
1377
|
-
// StudentData.Sections.Algebra.Students is an array, so the ["JaneDoe"].Grade is not possible to access
|
|
1378
|
-
// TODO: Should this be an error or something? Should we keep a log of failures like this?
|
|
1379
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber)) {
|
|
1380
|
-
return undefined;
|
|
1381
|
-
}
|
|
1382
|
-
// Check if the boxed property is an object.
|
|
1383
|
-
if (typeof pObject[tmpBoxedPropertyName] != 'object') {
|
|
1384
|
-
return undefined;
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
//This is a bracketed value
|
|
1388
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
1389
|
-
// otherwise we will try to reat it as a dynamic object property.
|
|
1390
|
-
if (isNaN(tmpBoxedPropertyNumber)) {
|
|
1391
|
-
// This isn't a number ... let's treat it as a dynanmic object property.
|
|
1392
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
1393
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
1394
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
1395
|
-
|
|
1396
|
-
// Continue to manage the parent address for recursion
|
|
1397
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
1398
|
-
// Recurse directly into the subobject
|
|
1399
|
-
return this.getValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress, tmpParentAddress, tmpRootObject);
|
|
1400
|
-
} else {
|
|
1401
|
-
// Continue to manage the parent address for recursion
|
|
1402
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
1403
|
-
// We parsed a valid number out of the boxed property name, so recurse into the array
|
|
1404
|
-
return this.getValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress, tmpParentAddress, tmpRootObject);
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
// The requirements to detect a boxed set element are:
|
|
1408
|
-
// 1) The start bracket is after character 0
|
|
1409
|
-
else if (tmpBracketStartIndex > 0
|
|
1410
|
-
// 2) The end bracket is after the start bracket
|
|
1411
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
1412
|
-
// 3) There is nothing in the brackets
|
|
1413
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex == 1) {
|
|
1414
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
1415
|
-
if (!Array.isArray(pObject[tmpBoxedPropertyName])) {
|
|
1416
|
-
// We asked for a set from an array but it isnt' an array.
|
|
1417
|
-
return false;
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
// We need to enumerate the array and grab the addresses from there.
|
|
1421
|
-
let tmpArrayProperty = pObject[tmpBoxedPropertyName];
|
|
1422
|
-
// Managing the parent address is a bit more complex here -- the box will be added for each element.
|
|
1423
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpBoxedPropertyName);
|
|
1424
|
-
// The container object is where we have the "Address":SOMEVALUE pairs
|
|
1425
|
-
let tmpContainerObject = {};
|
|
1426
|
-
for (let i = 0; i < tmpArrayProperty.length; i++) {
|
|
1427
|
-
let tmpPropertyParentAddress = "".concat(tmpParentAddress, "[").concat(i, "]");
|
|
1428
|
-
let tmpValue = this.getValueAtAddress(pObject[tmpBoxedPropertyName][i], tmpNewAddress, tmpPropertyParentAddress, tmpRootObject);
|
|
1429
|
-
tmpContainerObject["".concat(tmpPropertyParentAddress, ".").concat(tmpNewAddress)] = tmpValue;
|
|
1430
|
-
}
|
|
1431
|
-
return tmpContainerObject;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
// OBJECT SET
|
|
1435
|
-
// Note this will not work with a bracket in the same address box set
|
|
1436
|
-
let tmpObjectTypeMarkerIndex = pAddress.indexOf('{}');
|
|
1437
|
-
if (tmpObjectTypeMarkerIndex > 0) {
|
|
1438
|
-
let tmpObjectPropertyName = pAddress.substring(0, tmpObjectTypeMarkerIndex).trim();
|
|
1439
|
-
if (typeof pObject[tmpObjectPropertyName] != 'object') {
|
|
1440
|
-
// We asked for a set from an array but it isnt' an array.
|
|
1441
|
-
return false;
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
// We need to enumerate the Object and grab the addresses from there.
|
|
1445
|
-
let tmpObjectProperty = pObject[tmpObjectPropertyName];
|
|
1446
|
-
let tmpObjectPropertyKeys = Object.keys(tmpObjectProperty);
|
|
1447
|
-
// Managing the parent address is a bit more complex here -- the box will be added for each element.
|
|
1448
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpObjectPropertyName);
|
|
1449
|
-
// The container object is where we have the "Address":SOMEVALUE pairs
|
|
1450
|
-
let tmpContainerObject = {};
|
|
1451
|
-
for (let i = 0; i < tmpObjectPropertyKeys.length; i++) {
|
|
1452
|
-
let tmpPropertyParentAddress = "".concat(tmpParentAddress, ".").concat(tmpObjectPropertyKeys[i]);
|
|
1453
|
-
let tmpValue = this.getValueAtAddress(pObject[tmpObjectPropertyName][tmpObjectPropertyKeys[i]], tmpNewAddress, tmpPropertyParentAddress, tmpRootObject);
|
|
1454
|
-
|
|
1455
|
-
// The filtering is complex but allows config-based metaprogramming directly from schema
|
|
1456
|
-
let tmpKeepRecord = this.checkRecordFilters(pAddress, tmpValue);
|
|
1457
|
-
if (tmpKeepRecord) {
|
|
1458
|
-
tmpContainerObject["".concat(tmpPropertyParentAddress, ".").concat(tmpNewAddress)] = tmpValue;
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
return tmpContainerObject;
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
// If there is an object property already named for the sub object, but it isn't an object
|
|
1465
|
-
// then the system can't set the value in there. Error and abort!
|
|
1466
|
-
if (tmpSubObjectName in pObject && typeof pObject[tmpSubObjectName] !== 'object') {
|
|
1467
|
-
return undefined;
|
|
1468
|
-
} else if (tmpSubObjectName in pObject) {
|
|
1469
|
-
// If there is already a subobject pass that to the recursive thingy
|
|
1470
|
-
// Continue to manage the parent address for recursion
|
|
1471
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
1472
|
-
return this.getValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, tmpParentAddress, tmpRootObject);
|
|
1473
|
-
} else {
|
|
1474
|
-
// Create a subobject and then pass that
|
|
1475
|
-
// Continue to manage the parent address for recursion
|
|
1476
|
-
tmpParentAddress = "".concat(tmpParentAddress).concat(tmpParentAddress.length > 0 ? '.' : '').concat(tmpSubObjectName);
|
|
1477
|
-
pObject[tmpSubObjectName] = {};
|
|
1478
|
-
return this.getValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, tmpParentAddress, tmpRootObject);
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
;
|
|
1484
|
-
module.exports = ManyfestObjectAddressResolverGetValue;
|
|
1485
|
-
}, {
|
|
1486
|
-
"../source/Manyfest-ParseConditionals.js": 12,
|
|
1487
|
-
"./Manyfest-CleanWrapCharacters.js": 3,
|
|
1488
|
-
"./Manyfest-LogToConsole.js": 5,
|
|
1489
|
-
"./Manyfest-ObjectAddress-Parser.js": 9
|
|
1490
|
-
}],
|
|
1491
|
-
9: [function (require, module, exports) {
|
|
1492
|
-
// TODO: This is an inelegant solution to delay the rewrite of Manyfest.
|
|
1493
|
-
|
|
1494
|
-
// Fable 3.0 has a service for data formatting that deals well with nested enclosures.
|
|
1495
|
-
|
|
1496
|
-
// The Manyfest library predates fable 3.0 and the services structure of it, so the functions
|
|
1497
|
-
// are more or less pure javascript and as functional as they can be made to be.
|
|
1498
|
-
|
|
1499
|
-
// Until we shift Manyfest to be a fable service, these three functions were pulled out of
|
|
1500
|
-
// fable to aid in parsing functions with nested enclosures.
|
|
1501
|
-
|
|
1502
|
-
const DEFAULT_START_SYMBOL_MAP = {
|
|
1503
|
-
'{': 0,
|
|
1504
|
-
'[': 1,
|
|
1505
|
-
'(': 2
|
|
1506
|
-
};
|
|
1507
|
-
const DEFAULT_END_SYMBOL_MAP = {
|
|
1508
|
-
'}': 0,
|
|
1509
|
-
']': 1,
|
|
1510
|
-
')': 2
|
|
1511
|
-
};
|
|
1512
|
-
module.exports = {
|
|
1513
|
-
/**
|
|
1514
|
-
* Count the number of segments in a string, respecting enclosures
|
|
1515
|
-
*
|
|
1516
|
-
* @param {string} pString
|
|
1517
|
-
* @param {string} [pSeparator]
|
|
1518
|
-
* @param {Record<string, number>} [pEnclosureStartSymbolMap]
|
|
1519
|
-
* @param {Record<string, number>} [pEnclosureEndSymbolMap]
|
|
1520
|
-
*
|
|
1521
|
-
* @return {number} - The number of segments in the string
|
|
1522
|
-
*/
|
|
1523
|
-
stringCountSegments: (pString, pSeparator, pEnclosureStartSymbolMap, pEnclosureEndSymbolMap) => {
|
|
1524
|
-
let tmpString = typeof pString == 'string' ? pString : '';
|
|
1525
|
-
let tmpSeparator = typeof pSeparator == 'string' ? pSeparator : '.';
|
|
1526
|
-
let tmpEnclosureStartSymbolMap = typeof pEnclosureStartSymbolMap == 'object' ? pEnclosureStartSymbolMap : DEFAULT_START_SYMBOL_MAP;
|
|
1527
|
-
let tmpEnclosureEndSymbolMap = typeof pEnclosureEndSymbolMap == 'object' ? pEnclosureEndSymbolMap : DEFAULT_END_SYMBOL_MAP;
|
|
1528
|
-
if (pString.length < 1) {
|
|
1529
|
-
return 0;
|
|
1530
|
-
}
|
|
1531
|
-
let tmpSegmentCount = 1;
|
|
1532
|
-
let tmpEnclosureStack = [];
|
|
1533
|
-
for (let i = 0; i < tmpString.length; i++) {
|
|
1534
|
-
// IF This is the start of a segment
|
|
1535
|
-
if (tmpString[i] == tmpSeparator
|
|
1536
|
-
// AND we are not in a nested portion of the string
|
|
1537
|
-
&& tmpEnclosureStack.length == 0) {
|
|
1538
|
-
// Increment the segment count
|
|
1539
|
-
tmpSegmentCount++;
|
|
1540
|
-
}
|
|
1541
|
-
// IF This is the start of an enclosure
|
|
1542
|
-
else if (tmpString[i] in tmpEnclosureStartSymbolMap) {
|
|
1543
|
-
// Add it to the stack!
|
|
1544
|
-
tmpEnclosureStack.push(tmpEnclosureStartSymbolMap[tmpString[i]]);
|
|
1545
|
-
}
|
|
1546
|
-
// IF This is the end of an enclosure
|
|
1547
|
-
else if (tmpString[i] in tmpEnclosureEndSymbolMap
|
|
1548
|
-
// AND it matches the current nest level symbol
|
|
1549
|
-
&& tmpEnclosureEndSymbolMap[tmpString[i]] == tmpEnclosureStack[tmpEnclosureStack.length - 1]) {
|
|
1550
|
-
// Pop it off the stack!
|
|
1551
|
-
tmpEnclosureStack.pop();
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
return tmpSegmentCount;
|
|
1555
|
-
},
|
|
1556
|
-
/**
|
|
1557
|
-
* Get the first segment in a string, respecting enclosures
|
|
1558
|
-
*
|
|
1559
|
-
* @param {string} pString
|
|
1560
|
-
* @param {string} [pSeparator]
|
|
1561
|
-
* @param {Record<string, number>} [pEnclosureStartSymbolMap]
|
|
1562
|
-
* @param {Record<string, number>} [pEnclosureEndSymbolMap]
|
|
1563
|
-
*
|
|
1564
|
-
* @return {string} - the first segment in the string as a string
|
|
1565
|
-
*/
|
|
1566
|
-
stringGetFirstSegment: (pString, pSeparator, pEnclosureStartSymbolMap, pEnclosureEndSymbolMap) => {
|
|
1567
|
-
let tmpString = typeof pString == 'string' ? pString : '';
|
|
1568
|
-
let tmpSeparator = typeof pSeparator == 'string' ? pSeparator : '.';
|
|
1569
|
-
let tmpEnclosureStartSymbolMap = typeof pEnclosureStartSymbolMap == 'object' ? pEnclosureStartSymbolMap : DEFAULT_START_SYMBOL_MAP;
|
|
1570
|
-
let tmpEnclosureEndSymbolMap = typeof pEnclosureEndSymbolMap == 'object' ? pEnclosureEndSymbolMap : DEFAULT_END_SYMBOL_MAP;
|
|
1571
|
-
if (pString.length < 1) {
|
|
1572
|
-
return '';
|
|
1573
|
-
}
|
|
1574
|
-
let tmpEnclosureStack = [];
|
|
1575
|
-
for (let i = 0; i < tmpString.length; i++) {
|
|
1576
|
-
// IF This is the start of a segment
|
|
1577
|
-
if (tmpString[i] == tmpSeparator
|
|
1578
|
-
// AND we are not in a nested portion of the string
|
|
1579
|
-
&& tmpEnclosureStack.length == 0) {
|
|
1580
|
-
// Return the segment
|
|
1581
|
-
return tmpString.substring(0, i);
|
|
1582
|
-
}
|
|
1583
|
-
// IF This is the start of an enclosure
|
|
1584
|
-
else if (tmpString[i] in tmpEnclosureStartSymbolMap) {
|
|
1585
|
-
// Add it to the stack!
|
|
1586
|
-
tmpEnclosureStack.push(tmpEnclosureStartSymbolMap[tmpString[i]]);
|
|
1587
|
-
}
|
|
1588
|
-
// IF This is the end of an enclosure
|
|
1589
|
-
else if (tmpString[i] in tmpEnclosureEndSymbolMap
|
|
1590
|
-
// AND it matches the current nest level symbol
|
|
1591
|
-
&& tmpEnclosureEndSymbolMap[tmpString[i]] == tmpEnclosureStack[tmpEnclosureStack.length - 1]) {
|
|
1592
|
-
// Pop it off the stack!
|
|
1593
|
-
tmpEnclosureStack.pop();
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
return tmpString;
|
|
1597
|
-
},
|
|
1598
|
-
/**
|
|
1599
|
-
* Get all segments in a string, respecting enclosures
|
|
1600
|
-
*
|
|
1601
|
-
* @param {string} pString
|
|
1602
|
-
* @param {string} [pSeparator]
|
|
1603
|
-
* @param {Record<string, number>} [pEnclosureStartSymbolMap]
|
|
1604
|
-
* @param {Record<string, number>} [pEnclosureEndSymbolMap]
|
|
1605
|
-
*
|
|
1606
|
-
* @return {Array<string>} - the segments in the string as an array of strings
|
|
1607
|
-
*/
|
|
1608
|
-
stringGetSegments: (pString, pSeparator, pEnclosureStartSymbolMap, pEnclosureEndSymbolMap) => {
|
|
1609
|
-
let tmpString = typeof pString == 'string' ? pString : '';
|
|
1610
|
-
let tmpSeparator = typeof pSeparator == 'string' ? pSeparator : '.';
|
|
1611
|
-
let tmpEnclosureStartSymbolMap = typeof pEnclosureStartSymbolMap == 'object' ? pEnclosureStartSymbolMap : DEFAULT_START_SYMBOL_MAP;
|
|
1612
|
-
let tmpEnclosureEndSymbolMap = typeof pEnclosureEndSymbolMap == 'object' ? pEnclosureEndSymbolMap : DEFAULT_END_SYMBOL_MAP;
|
|
1613
|
-
let tmpCurrentSegmentStart = 0;
|
|
1614
|
-
let tmpSegmentList = [];
|
|
1615
|
-
if (pString.length < 1) {
|
|
1616
|
-
return tmpSegmentList;
|
|
1617
|
-
}
|
|
1618
|
-
let tmpEnclosureStack = [];
|
|
1619
|
-
for (let i = 0; i < tmpString.length; i++) {
|
|
1620
|
-
// IF This is the start of a segment
|
|
1621
|
-
if (tmpString[i] == tmpSeparator
|
|
1622
|
-
// AND we are not in a nested portion of the string
|
|
1623
|
-
&& tmpEnclosureStack.length == 0) {
|
|
1624
|
-
// Return the segment
|
|
1625
|
-
tmpSegmentList.push(tmpString.substring(tmpCurrentSegmentStart, i));
|
|
1626
|
-
tmpCurrentSegmentStart = i + 1;
|
|
1627
|
-
}
|
|
1628
|
-
// IF This is the start of an enclosure
|
|
1629
|
-
else if (tmpString[i] in tmpEnclosureStartSymbolMap) {
|
|
1630
|
-
// Add it to the stack!
|
|
1631
|
-
tmpEnclosureStack.push(tmpEnclosureStartSymbolMap[tmpString[i]]);
|
|
1632
|
-
}
|
|
1633
|
-
// IF This is the end of an enclosure
|
|
1634
|
-
else if (tmpString[i] in tmpEnclosureEndSymbolMap
|
|
1635
|
-
// AND it matches the current nest level symbol
|
|
1636
|
-
&& tmpEnclosureEndSymbolMap[tmpString[i]] == tmpEnclosureStack[tmpEnclosureStack.length - 1]) {
|
|
1637
|
-
// Pop it off the stack!
|
|
1638
|
-
tmpEnclosureStack.pop();
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
if (tmpCurrentSegmentStart < tmpString.length) {
|
|
1642
|
-
tmpSegmentList.push(tmpString.substring(tmpCurrentSegmentStart));
|
|
1643
|
-
}
|
|
1644
|
-
return tmpSegmentList;
|
|
1645
|
-
},
|
|
1646
|
-
/**
|
|
1647
|
-
* Count the number of enclosures in a string based on the start and end characters.
|
|
1648
|
-
*
|
|
1649
|
-
* If no start or end characters are specified, it will default to parentheses. If the string is not a string, it will return 0.
|
|
1650
|
-
*
|
|
1651
|
-
* @param {string} pString
|
|
1652
|
-
* @param {string} [pEnclosureStart]
|
|
1653
|
-
* @param {string} [pEnclosureEnd]
|
|
1654
|
-
* @returns the count of full in the string
|
|
1655
|
-
*/
|
|
1656
|
-
stringCountEnclosures: (pString, pEnclosureStart, pEnclosureEnd) => {
|
|
1657
|
-
let tmpString = typeof pString == 'string' ? pString : '';
|
|
1658
|
-
let tmpEnclosureStart = typeof pEnclosureStart == 'string' ? pEnclosureStart : '(';
|
|
1659
|
-
let tmpEnclosureEnd = typeof pEnclosureEnd == 'string' ? pEnclosureEnd : ')';
|
|
1660
|
-
let tmpEnclosureCount = 0;
|
|
1661
|
-
let tmpEnclosureDepth = 0;
|
|
1662
|
-
for (let i = 0; i < tmpString.length; i++) {
|
|
1663
|
-
// This is the start of an enclosure
|
|
1664
|
-
if (tmpString[i] == tmpEnclosureStart) {
|
|
1665
|
-
if (tmpEnclosureDepth == 0) {
|
|
1666
|
-
tmpEnclosureCount++;
|
|
1667
|
-
}
|
|
1668
|
-
tmpEnclosureDepth++;
|
|
1669
|
-
} else if (tmpString[i] == tmpEnclosureEnd) {
|
|
1670
|
-
tmpEnclosureDepth--;
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
return tmpEnclosureCount;
|
|
1674
|
-
},
|
|
1675
|
-
/**
|
|
1676
|
-
* Get the value of the enclosure at the specified index.
|
|
1677
|
-
*
|
|
1678
|
-
* If the index is not a number, it will default to 0. If the string is not a string, it will return an empty string. If the enclosure is not found, it will return an empty string. If the enclosure
|
|
1679
|
-
*
|
|
1680
|
-
* @param {string} pString
|
|
1681
|
-
* @param {number} pEnclosureIndexToGet
|
|
1682
|
-
* @param {string} [pEnclosureStart]
|
|
1683
|
-
* @param {string} [pEnclosureEnd]
|
|
1684
|
-
*
|
|
1685
|
-
* @return {string} - The value of the enclosure at the specified index
|
|
1686
|
-
*/
|
|
1687
|
-
stringGetEnclosureValueByIndex: (pString, pEnclosureIndexToGet, pEnclosureStart, pEnclosureEnd) => {
|
|
1688
|
-
let tmpString = typeof pString == 'string' ? pString : '';
|
|
1689
|
-
let tmpEnclosureIndexToGet = typeof pEnclosureIndexToGet == 'number' ? pEnclosureIndexToGet : 0;
|
|
1690
|
-
let tmpEnclosureStart = typeof pEnclosureStart == 'string' ? pEnclosureStart : '(';
|
|
1691
|
-
let tmpEnclosureEnd = typeof pEnclosureEnd == 'string' ? pEnclosureEnd : ')';
|
|
1692
|
-
let tmpEnclosureCount = 0;
|
|
1693
|
-
let tmpEnclosureDepth = 0;
|
|
1694
|
-
let tmpMatchedEnclosureIndex = false;
|
|
1695
|
-
let tmpEnclosedValueStartIndex = 0;
|
|
1696
|
-
let tmpEnclosedValueEndIndex = 0;
|
|
1697
|
-
for (let i = 0; i < tmpString.length; i++) {
|
|
1698
|
-
// This is the start of an enclosure
|
|
1699
|
-
if (tmpString[i] == tmpEnclosureStart) {
|
|
1700
|
-
tmpEnclosureDepth++;
|
|
1701
|
-
|
|
1702
|
-
// Only count enclosures at depth 1, but still this parses both pairs of all of them.
|
|
1703
|
-
if (tmpEnclosureDepth == 1) {
|
|
1704
|
-
tmpEnclosureCount++;
|
|
1705
|
-
if (tmpEnclosureIndexToGet == tmpEnclosureCount - 1) {
|
|
1706
|
-
// This is the start of *the* enclosure
|
|
1707
|
-
tmpMatchedEnclosureIndex = true;
|
|
1708
|
-
tmpEnclosedValueStartIndex = i;
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
// This is the end of an enclosure
|
|
1713
|
-
else if (tmpString[i] == tmpEnclosureEnd) {
|
|
1714
|
-
tmpEnclosureDepth--;
|
|
1715
|
-
|
|
1716
|
-
// Again, only count enclosures at depth 1, but still this parses both pairs of all of them.
|
|
1717
|
-
if (tmpEnclosureDepth == 0 && tmpMatchedEnclosureIndex && tmpEnclosedValueEndIndex <= tmpEnclosedValueStartIndex) {
|
|
1718
|
-
tmpEnclosedValueEndIndex = i;
|
|
1719
|
-
tmpMatchedEnclosureIndex = false;
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
if (tmpEnclosureCount <= tmpEnclosureIndexToGet) {
|
|
1724
|
-
// Return an empty string if the enclosure is not found
|
|
1725
|
-
return '';
|
|
1726
|
-
}
|
|
1727
|
-
if (tmpEnclosedValueEndIndex > 0 && tmpEnclosedValueEndIndex > tmpEnclosedValueStartIndex) {
|
|
1728
|
-
return tmpString.substring(tmpEnclosedValueStartIndex + 1, tmpEnclosedValueEndIndex);
|
|
1729
|
-
} else {
|
|
1730
|
-
return tmpString.substring(tmpEnclosedValueStartIndex + 1);
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
};
|
|
1734
|
-
}, {}],
|
|
1735
|
-
10: [function (require, module, exports) {
|
|
1736
|
-
/**
|
|
1737
|
-
* @author <steven@velozo.com>
|
|
1738
|
-
*/
|
|
1739
|
-
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
1740
|
-
let fCleanWrapCharacters = require('./Manyfest-CleanWrapCharacters.js');
|
|
1741
|
-
|
|
1742
|
-
/**
|
|
1743
|
-
* Object Address Resolver - SetValue
|
|
1744
|
-
*
|
|
1745
|
-
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
1746
|
-
* be extremely clear what is going on in the recursion for
|
|
1747
|
-
* each of the three address resolution functions.
|
|
1748
|
-
*
|
|
1749
|
-
* Although there is some opportunity to repeat ourselves a
|
|
1750
|
-
* bit less in this codebase (e.g. with detection of arrays
|
|
1751
|
-
* versus objects versus direct properties), it can make
|
|
1752
|
-
* debugging.. challenging. The minified version of the code
|
|
1753
|
-
* optimizes out almost anything repeated in here. So please
|
|
1754
|
-
* be kind and rewind... meaning please keep the codebase less
|
|
1755
|
-
* terse and more verbose so humans can comprehend it.
|
|
1756
|
-
*
|
|
1757
|
-
*
|
|
1758
|
-
* @class ManyfestObjectAddressSetValue
|
|
1759
|
-
*/
|
|
1760
|
-
class ManyfestObjectAddressSetValue {
|
|
1761
|
-
/**
|
|
1762
|
-
* @param {function} [pInfoLog] - (optional) A logging function for info messages
|
|
1763
|
-
* @param {function} [pErrorLog] - (optional) A logging function for error messages
|
|
1764
|
-
*/
|
|
1765
|
-
constructor(pInfoLog, pErrorLog) {
|
|
1766
|
-
// Wire in logging
|
|
1767
|
-
this.logInfo = typeof pInfoLog == 'function' ? pInfoLog : libSimpleLog;
|
|
1768
|
-
this.logError = typeof pErrorLog == 'function' ? pErrorLog : libSimpleLog;
|
|
1769
|
-
this.cleanWrapCharacters = fCleanWrapCharacters;
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
/**
|
|
1773
|
-
* Set the value of an element at an address
|
|
1774
|
-
*
|
|
1775
|
-
* @param {object} pObject - The object to set the value in
|
|
1776
|
-
* @param {string} pAddress - The address to set the value at
|
|
1777
|
-
* @param {any} pValue - The value to set at the address
|
|
1778
|
-
*
|
|
1779
|
-
* @return {boolean} True if the value was set, false otherwise
|
|
1780
|
-
*/
|
|
1781
|
-
setValueAtAddress(pObject, pAddress, pValue) {
|
|
1782
|
-
// Make sure pObject is an object
|
|
1783
|
-
if (typeof pObject != 'object') return false;
|
|
1784
|
-
// Make sure pAddress is a string
|
|
1785
|
-
if (typeof pAddress != 'string') return false;
|
|
1786
|
-
let tmpSeparatorIndex = pAddress.indexOf('.');
|
|
1787
|
-
if (tmpSeparatorIndex == -1) {
|
|
1788
|
-
// Check if it's a boxed property
|
|
1789
|
-
let tmpBracketStartIndex = pAddress.indexOf('[');
|
|
1790
|
-
let tmpBracketStopIndex = pAddress.indexOf(']');
|
|
1791
|
-
// Boxed elements look like this:
|
|
1792
|
-
// MyValues[10]
|
|
1793
|
-
// MyValues['Name']
|
|
1794
|
-
// MyValues["Age"]
|
|
1795
|
-
// MyValues[`Cost`]
|
|
1796
|
-
//
|
|
1797
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
1798
|
-
// The requirements to detect a boxed element are:
|
|
1799
|
-
// 1) The start bracket is after character 0
|
|
1800
|
-
if (tmpBracketStartIndex > 0
|
|
1801
|
-
// 2) The end bracket has something between them
|
|
1802
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
1803
|
-
// 3) There is data
|
|
1804
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex > 1) {
|
|
1805
|
-
// The "Name" of the Object contained too the left of the bracket
|
|
1806
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
1807
|
-
|
|
1808
|
-
// The "Reference" to the property within it, either an array element or object property
|
|
1809
|
-
let tmpBoxedPropertyReference = pAddress.substring(tmpBracketStartIndex + 1, tmpBracketStopIndex).trim();
|
|
1810
|
-
// Attempt to parse the reference as a number, which will be used as an array element
|
|
1811
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
1812
|
-
let tmpIndexIsNumeric = !isNaN(tmpBoxedPropertyNumber);
|
|
1813
|
-
if (pObject[tmpBoxedPropertyName] == null) {
|
|
1814
|
-
if (tmpIndexIsNumeric) {
|
|
1815
|
-
pObject[tmpBoxedPropertyName] = [];
|
|
1816
|
-
} else {
|
|
1817
|
-
pObject[tmpBoxedPropertyName] = {};
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
// If the subproperty doesn't test as a proper Object, none of the rest of this is possible.
|
|
1822
|
-
// This is a rare case where Arrays testing as Objects is useful
|
|
1823
|
-
if (typeof pObject[tmpBoxedPropertyName] !== 'object') {
|
|
1824
|
-
return false;
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
1828
|
-
// This seems confusing to me at first read, so explaination:
|
|
1829
|
-
// Is the Boxed Object an Array? TRUE
|
|
1830
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
1831
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
1832
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber)) {
|
|
1833
|
-
return false;
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
1837
|
-
// otherwise we will try to treat it as a dynamic object property.
|
|
1838
|
-
if (isNaN(tmpBoxedPropertyNumber)) {
|
|
1839
|
-
// This isn't a number ... let's treat it as a dynamic object property.
|
|
1840
|
-
// We would expect the property to be wrapped in some kind of quotes so strip them
|
|
1841
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
1842
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
1843
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
1844
|
-
if (!(tmpBoxedPropertyReference in pObject[tmpBoxedPropertyName])) {
|
|
1845
|
-
// If the subobject doesn't exist, create it
|
|
1846
|
-
pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference] = {};
|
|
1847
|
-
}
|
|
1848
|
-
|
|
1849
|
-
// Return the value in the property
|
|
1850
|
-
//TODO: For cases where we have chained [][] properties, this needs to recurse somehow
|
|
1851
|
-
pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference] = pValue;
|
|
1852
|
-
return true;
|
|
1853
|
-
} else {
|
|
1854
|
-
while (pObject[tmpBoxedPropertyName].length < tmpBoxedPropertyNumber + 1) {
|
|
1855
|
-
// If the subobject doesn't exist, create it
|
|
1856
|
-
pObject[tmpBoxedPropertyName].push({});
|
|
1857
|
-
}
|
|
1858
|
-
pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber] = pValue;
|
|
1859
|
-
return true;
|
|
1860
|
-
}
|
|
1861
|
-
} else {
|
|
1862
|
-
// Now is the time in recursion to set the value in the object
|
|
1863
|
-
pObject[pAddress] = pValue;
|
|
1864
|
-
return true;
|
|
1865
|
-
}
|
|
1866
|
-
} else {
|
|
1867
|
-
let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
|
|
1868
|
-
let tmpNewAddress = pAddress.substring(tmpSeparatorIndex + 1);
|
|
1869
|
-
|
|
1870
|
-
// Test if the tmpNewAddress is an array or object
|
|
1871
|
-
// Check if it's a boxed property
|
|
1872
|
-
let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
|
|
1873
|
-
let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
|
|
1874
|
-
// Boxed elements look like this:
|
|
1875
|
-
// MyValues[42]
|
|
1876
|
-
// MyValues['Color']
|
|
1877
|
-
// MyValues["Weight"]
|
|
1878
|
-
// MyValues[`Diameter`]
|
|
1879
|
-
//
|
|
1880
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
1881
|
-
// The requirements to detect a boxed element are:
|
|
1882
|
-
// 1) The start bracket is after character 0
|
|
1883
|
-
if (tmpBracketStartIndex > 0
|
|
1884
|
-
// 2) The end bracket has something between them
|
|
1885
|
-
&& tmpBracketStopIndex > tmpBracketStartIndex
|
|
1886
|
-
// 3) There is data
|
|
1887
|
-
&& tmpBracketStopIndex - tmpBracketStartIndex > 1) {
|
|
1888
|
-
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
1889
|
-
let tmpBoxedPropertyReference = tmpSubObjectName.substring(tmpBracketStartIndex + 1, tmpBracketStopIndex).trim();
|
|
1890
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
1891
|
-
let tmpIndexIsNumeric = !isNaN(tmpBoxedPropertyNumber);
|
|
1892
|
-
|
|
1893
|
-
//if (typeof(pObject[tmpBoxedPropertyName]) !== 'object')
|
|
1894
|
-
if (pObject[tmpBoxedPropertyName] == null) {
|
|
1895
|
-
if (tmpIndexIsNumeric) {
|
|
1896
|
-
pObject[tmpBoxedPropertyName] = [];
|
|
1897
|
-
} else {
|
|
1898
|
-
pObject[tmpBoxedPropertyName] = {};
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
1903
|
-
// This seems confusing to me at first read, so explaination:
|
|
1904
|
-
// Is the Boxed Object an Array? TRUE
|
|
1905
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
1906
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
1907
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
1908
|
-
// StudentData.Sections.Algebra.Students[1].Tardy
|
|
1909
|
-
// BUT
|
|
1910
|
-
// StudentData.Sections.Algebra.Students is an object, so the [1].Tardy is not possible to access
|
|
1911
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
1912
|
-
// StudentData.Sections.Algebra.Students["JaneDoe"].Grade
|
|
1913
|
-
// BUT
|
|
1914
|
-
// StudentData.Sections.Algebra.Students is an array, so the ["JaneDoe"].Grade is not possible to access
|
|
1915
|
-
// TODO: Should this be an error or something? Should we keep a log of failures like this?
|
|
1916
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) != tmpIndexIsNumeric) {
|
|
1917
|
-
return false;
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
//This is a bracketed value
|
|
1921
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
1922
|
-
// otherwise we will try to reat it as a dynamic object property.
|
|
1923
|
-
if (isNaN(tmpBoxedPropertyNumber)) {
|
|
1924
|
-
// This isn't a number ... let's treat it as a dynanmic object property.
|
|
1925
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
1926
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
1927
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
1928
|
-
if (!(tmpBoxedPropertyReference in pObject[tmpBoxedPropertyName])) {
|
|
1929
|
-
// If the subobject doesn't exist, create it
|
|
1930
|
-
pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference] = {};
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
// Recurse directly into the subobject
|
|
1934
|
-
return this.setValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress, pValue);
|
|
1935
|
-
} else {
|
|
1936
|
-
while (pObject[tmpBoxedPropertyName].length < tmpBoxedPropertyNumber + 1) {
|
|
1937
|
-
// If the subobject doesn't exist, create it
|
|
1938
|
-
pObject[tmpBoxedPropertyName].push({});
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
// We parsed a valid number out of the boxed property name, so recurse into the array
|
|
1942
|
-
return this.setValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress, pValue);
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
|
|
1946
|
-
// If there is an object property already named for the sub object, but it isn't an object
|
|
1947
|
-
// then the system can't set the value in there. Error and abort!
|
|
1948
|
-
if (tmpSubObjectName in pObject && typeof pObject[tmpSubObjectName] !== 'object') {
|
|
1949
|
-
if (!('__ERROR' in pObject)) pObject['__ERROR'] = {};
|
|
1950
|
-
// Put it in an error object so data isn't lost
|
|
1951
|
-
pObject['__ERROR'][pAddress] = pValue;
|
|
1952
|
-
return false;
|
|
1953
|
-
} else if (tmpSubObjectName in pObject) {
|
|
1954
|
-
// If there is already a subobject pass that to the recursive thingy
|
|
1955
|
-
return this.setValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, pValue);
|
|
1956
|
-
} else {
|
|
1957
|
-
// Create a subobject and then pass that
|
|
1958
|
-
pObject[tmpSubObjectName] = {};
|
|
1959
|
-
return this.setValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, pValue);
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
;
|
|
1965
|
-
module.exports = ManyfestObjectAddressSetValue;
|
|
1966
|
-
}, {
|
|
1967
|
-
"./Manyfest-CleanWrapCharacters.js": 3,
|
|
1968
|
-
"./Manyfest-LogToConsole.js": 5
|
|
1969
|
-
}],
|
|
1970
|
-
11: [function (require, module, exports) {
|
|
1971
|
-
/**
|
|
1972
|
-
* @author <steven@velozo.com>
|
|
1973
|
-
*/
|
|
1974
|
-
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
1975
|
-
|
|
1976
|
-
/**
|
|
1977
|
-
* Object Address Generation
|
|
1978
|
-
*
|
|
1979
|
-
* Automagically generate addresses and properties based on a passed-in object,
|
|
1980
|
-
* to be used for easy creation of schemas. Meant to simplify the lives of
|
|
1981
|
-
* developers wanting to create schemas without typing a bunch of stuff.
|
|
1982
|
-
*
|
|
1983
|
-
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
1984
|
-
* be extremely clear what is going on in the recursion for
|
|
1985
|
-
* each of the three address resolution functions.
|
|
1986
|
-
*
|
|
1987
|
-
* Although there is some opportunity to repeat ourselves a
|
|
1988
|
-
* bit less in this codebase (e.g. with detection of arrays
|
|
1989
|
-
* versus objects versus direct properties), it can make
|
|
1990
|
-
* debugging.. challenging. The minified version of the code
|
|
1991
|
-
* optimizes out almost anything repeated in here. So please
|
|
1992
|
-
* be kind and rewind... meaning please keep the codebase less
|
|
1993
|
-
* terse and more verbose so humans can comprehend it.
|
|
1994
|
-
*
|
|
1995
|
-
*
|
|
1996
|
-
* @class ManyfestObjectAddressGeneration
|
|
1997
|
-
*/
|
|
1998
|
-
class ManyfestObjectAddressGeneration {
|
|
1999
|
-
/**
|
|
2000
|
-
* @param {function} [pInfoLog] - (optional) A logging function for info messages
|
|
2001
|
-
* @param {function} [pErrorLog] - (optional) A logging function for error messages
|
|
2002
|
-
*/
|
|
2003
|
-
constructor(pInfoLog, pErrorLog) {
|
|
2004
|
-
// Wire in logging
|
|
2005
|
-
this.logInfo = typeof pInfoLog == 'function' ? pInfoLog : libSimpleLog;
|
|
2006
|
-
this.logError = typeof pErrorLog == 'function' ? pErrorLog : libSimpleLog;
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
/**
|
|
2010
|
-
* generateAddressses
|
|
2011
|
-
*
|
|
2012
|
-
* This flattens an object into a set of key:value pairs for *EVERY SINGLE
|
|
2013
|
-
* POSSIBLE ADDRESS* in the object. It can get ... really insane really
|
|
2014
|
-
* quickly. This is not meant to be used directly to generate schemas, but
|
|
2015
|
-
* instead as a starting point for scripts or UIs.
|
|
2016
|
-
*
|
|
2017
|
-
* This will return a mega set of key:value pairs with all possible schema
|
|
2018
|
-
* permutations and default values (when not an object) and everything else.
|
|
2019
|
-
*
|
|
2020
|
-
* @param {any} pObject - The object to generate addresses for
|
|
2021
|
-
* @param {string} [pBaseAddress] - (optional) The base address to start from
|
|
2022
|
-
* @param {object} [pSchema] - (optional) The schema object to append to
|
|
2023
|
-
*
|
|
2024
|
-
* @return {object} The generated schema object
|
|
2025
|
-
*/
|
|
2026
|
-
generateAddressses(pObject, pBaseAddress, pSchema) {
|
|
2027
|
-
let tmpBaseAddress = typeof pBaseAddress == 'string' ? pBaseAddress : '';
|
|
2028
|
-
let tmpSchema = typeof pSchema == 'object' ? pSchema : {};
|
|
2029
|
-
let tmpObjectType = typeof pObject;
|
|
2030
|
-
let tmpSchemaObjectEntry = {
|
|
2031
|
-
Address: tmpBaseAddress,
|
|
2032
|
-
Hash: tmpBaseAddress,
|
|
2033
|
-
Name: tmpBaseAddress,
|
|
2034
|
-
// This is so scripts and UI controls can force a developer to opt-in.
|
|
2035
|
-
InSchema: false
|
|
2036
|
-
};
|
|
2037
|
-
if (tmpObjectType == 'object' && pObject == null) {
|
|
2038
|
-
tmpObjectType = 'undefined';
|
|
2039
|
-
}
|
|
2040
|
-
switch (tmpObjectType) {
|
|
2041
|
-
case 'string':
|
|
2042
|
-
tmpSchemaObjectEntry.DataType = 'String';
|
|
2043
|
-
tmpSchemaObjectEntry.Default = pObject;
|
|
2044
|
-
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
2045
|
-
break;
|
|
2046
|
-
case 'number':
|
|
2047
|
-
case 'bigint':
|
|
2048
|
-
tmpSchemaObjectEntry.DataType = 'Number';
|
|
2049
|
-
tmpSchemaObjectEntry.Default = pObject;
|
|
2050
|
-
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
2051
|
-
break;
|
|
2052
|
-
case 'undefined':
|
|
2053
|
-
tmpSchemaObjectEntry.DataType = 'Any';
|
|
2054
|
-
tmpSchemaObjectEntry.Default = pObject;
|
|
2055
|
-
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
2056
|
-
break;
|
|
2057
|
-
case 'object':
|
|
2058
|
-
if (Array.isArray(pObject)) {
|
|
2059
|
-
tmpSchemaObjectEntry.DataType = 'Array';
|
|
2060
|
-
if (tmpBaseAddress != '') {
|
|
2061
|
-
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
2062
|
-
}
|
|
2063
|
-
for (let i = 0; i < pObject.length; i++) {
|
|
2064
|
-
this.generateAddressses(pObject[i], "".concat(tmpBaseAddress, "[").concat(i, "]"), tmpSchema);
|
|
2065
|
-
}
|
|
2066
|
-
} else {
|
|
2067
|
-
tmpSchemaObjectEntry.DataType = 'Object';
|
|
2068
|
-
if (tmpBaseAddress != '') {
|
|
2069
|
-
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
2070
|
-
tmpBaseAddress += '.';
|
|
2071
|
-
}
|
|
2072
|
-
let tmpObjectProperties = Object.keys(pObject);
|
|
2073
|
-
for (let i = 0; i < tmpObjectProperties.length; i++) {
|
|
2074
|
-
this.generateAddressses(pObject[tmpObjectProperties[i]], "".concat(tmpBaseAddress).concat(tmpObjectProperties[i]), tmpSchema);
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2077
|
-
break;
|
|
2078
|
-
case 'symbol':
|
|
2079
|
-
case 'function':
|
|
2080
|
-
// Symbols and functions neither recurse nor get added to the schema
|
|
2081
|
-
break;
|
|
2082
|
-
}
|
|
2083
|
-
return tmpSchema;
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
;
|
|
2087
|
-
module.exports = ManyfestObjectAddressGeneration;
|
|
2088
|
-
}, {
|
|
2089
|
-
"./Manyfest-LogToConsole.js": 5
|
|
2090
|
-
}],
|
|
2091
|
-
12: [function (require, module, exports) {
|
|
2092
|
-
// Given a string, parse out any conditional expressions and set whether or not to keep the record.
|
|
2093
|
-
//
|
|
2094
|
-
// For instance:
|
|
2095
|
-
// 'files[]<<~?format,==,Thumbnail?~>>'
|
|
2096
|
-
// 'files[]<<~?format,==,Metadata?~>>'
|
|
2097
|
-
// 'files[]<<~?size,>,4000?~>>'
|
|
2098
|
-
//
|
|
2099
|
-
// The wrapping parts are the <<~? and ?~>> megabrackets.
|
|
2100
|
-
//
|
|
2101
|
-
// The function does not need to alter the string -- just check the conditionals within.
|
|
2102
|
-
|
|
2103
|
-
// TODO: Consider making this an es6 class
|
|
2104
|
-
|
|
2105
|
-
// Let's use indexOf since it is apparently the fastest.
|
|
2106
|
-
const _ConditionalStanzaStart = '<<~?';
|
|
2107
|
-
const _ConditionalStanzaStartLength = _ConditionalStanzaStart.length;
|
|
2108
|
-
const _ConditionalStanzaEnd = '?~>>';
|
|
2109
|
-
const _ConditionalStanzaEndLength = _ConditionalStanzaEnd.length;
|
|
2110
|
-
|
|
2111
|
-
// Ugh dependency injection. Can't wait to make these all fable services.
|
|
2112
|
-
//let libObjectAddressCheckAddressExists = new (require('./Manyfest-ObjectAddress-CheckAddressExists.js'))();
|
|
2113
|
-
|
|
2114
|
-
// Test the condition of a value in a record
|
|
2115
|
-
const testCondition = (pManyfest, pRecord, pSearchAddress, pSearchComparator, pValue) => {
|
|
2116
|
-
switch (pSearchComparator) {
|
|
2117
|
-
case 'TRUE':
|
|
2118
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) === true;
|
|
2119
|
-
break;
|
|
2120
|
-
case 'FALSE':
|
|
2121
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) === false;
|
|
2122
|
-
break;
|
|
2123
|
-
case 'LNGT':
|
|
2124
|
-
case 'LENGTH_GREATER_THAN':
|
|
2125
|
-
switch (typeof pManyfest.getValueAtAddress(pRecord, pSearchAddress)) {
|
|
2126
|
-
case 'string':
|
|
2127
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress).length > pValue;
|
|
2128
|
-
break;
|
|
2129
|
-
case 'object':
|
|
2130
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress).length > pValue;
|
|
2131
|
-
break;
|
|
2132
|
-
default:
|
|
2133
|
-
return false;
|
|
2134
|
-
break;
|
|
2135
|
-
}
|
|
2136
|
-
break;
|
|
2137
|
-
case 'LNLT':
|
|
2138
|
-
case 'LENGTH_LESS_THAN':
|
|
2139
|
-
switch (typeof pManyfest.getValueAtAddress(pRecord, pSearchAddress)) {
|
|
2140
|
-
case 'string':
|
|
2141
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress).length < pValue;
|
|
2142
|
-
break;
|
|
2143
|
-
case 'object':
|
|
2144
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress).length < pValue;
|
|
2145
|
-
break;
|
|
2146
|
-
default:
|
|
2147
|
-
return false;
|
|
2148
|
-
break;
|
|
2149
|
-
}
|
|
2150
|
-
break;
|
|
2151
|
-
// TODO: Welcome to dependency hell. This fixes itself when we move to fable services.
|
|
2152
|
-
// case 'EX':
|
|
2153
|
-
// case 'EXISTS':
|
|
2154
|
-
// return libObjectAddressCheckAddressExists.checkAddressExists(pRecord, pSearchAddress);
|
|
2155
|
-
// break;
|
|
2156
|
-
// case 'DNEX':
|
|
2157
|
-
// case 'DOES_NOT_EXIST':
|
|
2158
|
-
// return !libObjectAddressCheckAddressExists.checkAddressExists(pRecord, pSearchAddress);
|
|
2159
|
-
// break;
|
|
2160
|
-
case '!=':
|
|
2161
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) != pValue;
|
|
2162
|
-
break;
|
|
2163
|
-
case '<':
|
|
2164
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) < pValue;
|
|
2165
|
-
break;
|
|
2166
|
-
case '>':
|
|
2167
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) > pValue;
|
|
2168
|
-
break;
|
|
2169
|
-
case '<=':
|
|
2170
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) <= pValue;
|
|
2171
|
-
break;
|
|
2172
|
-
case '>=':
|
|
2173
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) >= pValue;
|
|
2174
|
-
break;
|
|
2175
|
-
case '===':
|
|
2176
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) === pValue;
|
|
2177
|
-
break;
|
|
2178
|
-
case '==':
|
|
2179
|
-
default:
|
|
2180
|
-
return pManyfest.getValueAtAddress(pRecord, pSearchAddress) == pValue;
|
|
2181
|
-
break;
|
|
2182
|
-
}
|
|
2183
|
-
};
|
|
2184
|
-
const parseConditionals = (pManyfest, pAddress, pRecord) => {
|
|
2185
|
-
let tmpKeepRecord = true;
|
|
2186
|
-
|
|
2187
|
-
/*
|
|
2188
|
-
Algorithm is simple:
|
|
2189
|
-
1. Enuerate start points
|
|
2190
|
-
2. Find stop points within each start point
|
|
2191
|
-
3. Check the conditional
|
|
2192
|
-
*/
|
|
2193
|
-
let tmpStartIndex = pAddress.indexOf(_ConditionalStanzaStart);
|
|
2194
|
-
while (tmpStartIndex != -1) {
|
|
2195
|
-
let tmpStopIndex = pAddress.indexOf(_ConditionalStanzaEnd, tmpStartIndex + _ConditionalStanzaStartLength);
|
|
2196
|
-
if (tmpStopIndex != -1) {
|
|
2197
|
-
let tmpMagicComparisonPatternSet = pAddress.substring(tmpStartIndex + _ConditionalStanzaStartLength, tmpStopIndex).split(',');
|
|
2198
|
-
|
|
2199
|
-
// The address to search for
|
|
2200
|
-
let tmpSearchAddress = tmpMagicComparisonPatternSet[0];
|
|
2201
|
-
|
|
2202
|
-
// The copmparison expression (EXISTS as default)
|
|
2203
|
-
let tmpSearchComparator = 'EXISTS';
|
|
2204
|
-
if (tmpMagicComparisonPatternSet.length > 1) {
|
|
2205
|
-
tmpSearchComparator = tmpMagicComparisonPatternSet[1];
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
// The value to search for
|
|
2209
|
-
let tmpSearchValue = false;
|
|
2210
|
-
if (tmpMagicComparisonPatternSet.length > 2) {
|
|
2211
|
-
tmpSearchValue = tmpMagicComparisonPatternSet[2];
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
// Process the piece
|
|
2215
|
-
tmpKeepRecord = tmpKeepRecord && testCondition(pManyfest, pRecord, tmpSearchAddress, tmpSearchComparator, tmpSearchValue);
|
|
2216
|
-
tmpStartIndex = pAddress.indexOf(_ConditionalStanzaStart, tmpStopIndex + _ConditionalStanzaEndLength);
|
|
2217
|
-
} else {
|
|
2218
|
-
tmpStartIndex = -1;
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
return tmpKeepRecord;
|
|
2222
|
-
};
|
|
2223
|
-
module.exports = parseConditionals;
|
|
2224
|
-
}, {}],
|
|
2225
|
-
13: [function (require, module, exports) {
|
|
2226
|
-
/**
|
|
2227
|
-
* @author <steven@velozo.com>
|
|
2228
|
-
*/
|
|
2229
|
-
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
2230
|
-
|
|
2231
|
-
/**
|
|
2232
|
-
* Schema Manipulation Functions
|
|
2233
|
-
*
|
|
2234
|
-
* @class ManyfestSchemaManipulation
|
|
2235
|
-
*/
|
|
2236
|
-
class ManyfestSchemaManipulation {
|
|
2237
|
-
/**
|
|
2238
|
-
* @param {function} [pInfoLog] - (optional) A logging function for info messages
|
|
2239
|
-
* @param {function} [pErrorLog] - (optional) A logging function for error messages
|
|
2240
|
-
*/
|
|
2241
|
-
constructor(pInfoLog, pErrorLog) {
|
|
2242
|
-
// Wire in logging
|
|
2243
|
-
this.logInfo = typeof pInfoLog === 'function' ? pInfoLog : libSimpleLog;
|
|
2244
|
-
this.logError = typeof pErrorLog === 'function' ? pErrorLog : libSimpleLog;
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
|
-
/**
|
|
2248
|
-
* This translates the default address mappings to something different.
|
|
2249
|
-
*
|
|
2250
|
-
* For instance you can pass in manyfest schema descriptor object:
|
|
2251
|
-
* {
|
|
2252
|
-
* "Address.Of.a": { "Hash": "a", "Type": "Number" },
|
|
2253
|
-
* "Address.Of.b": { "Hash": "b", "Type": "Number" }
|
|
2254
|
-
* }
|
|
2255
|
-
*
|
|
2256
|
-
*
|
|
2257
|
-
* And then an address mapping (basically a Hash->Address map)
|
|
2258
|
-
* {
|
|
2259
|
-
* "a": "New.Address.Of.a",
|
|
2260
|
-
* "b": "New.Address.Of.b"
|
|
2261
|
-
* }
|
|
2262
|
-
*
|
|
2263
|
-
* NOTE: This mutates the schema object permanently, altering the base hash.
|
|
2264
|
-
* If there is a collision with an existing address, it can lead to overwrites.
|
|
2265
|
-
* TODO: Discuss what should happen on collisions.
|
|
2266
|
-
*
|
|
2267
|
-
* @param {object} pManyfestSchemaDescriptors - The manyfest schema descriptors to resolve address mappings for
|
|
2268
|
-
* @param {object} pAddressMapping - The address mapping object to use for remapping
|
|
2269
|
-
*
|
|
2270
|
-
* @return {boolean} True if successful, false if there was an error
|
|
2271
|
-
*/
|
|
2272
|
-
resolveAddressMappings(pManyfestSchemaDescriptors, pAddressMapping) {
|
|
2273
|
-
if (typeof pManyfestSchemaDescriptors != 'object') {
|
|
2274
|
-
this.logError("Attempted to resolve address mapping but the descriptor was not an object.");
|
|
2275
|
-
return false;
|
|
2276
|
-
}
|
|
2277
|
-
if (typeof pAddressMapping != 'object') {
|
|
2278
|
-
// No mappings were passed in
|
|
2279
|
-
return true;
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
// Get the arrays of both the schema definition and the hash mapping
|
|
2283
|
-
let tmpManyfestAddresses = Object.keys(pManyfestSchemaDescriptors);
|
|
2284
|
-
let tmpHashMapping = {};
|
|
2285
|
-
tmpManyfestAddresses.forEach(pAddress => {
|
|
2286
|
-
if ('Hash' in pManyfestSchemaDescriptors[pAddress]) {
|
|
2287
|
-
tmpHashMapping[pManyfestSchemaDescriptors[pAddress].Hash] = pAddress;
|
|
2288
|
-
}
|
|
2289
|
-
});
|
|
2290
|
-
let tmpAddressMappingSet = Object.keys(pAddressMapping);
|
|
2291
|
-
tmpAddressMappingSet.forEach(pInputAddress => {
|
|
2292
|
-
let tmpNewDescriptorAddress = pAddressMapping[pInputAddress];
|
|
2293
|
-
let tmpOldDescriptorAddress = null;
|
|
2294
|
-
let tmpDescriptor;
|
|
2295
|
-
|
|
2296
|
-
// See if there is a matching descriptor either by Address directly or Hash
|
|
2297
|
-
if (pInputAddress in pManyfestSchemaDescriptors) {
|
|
2298
|
-
tmpOldDescriptorAddress = pInputAddress;
|
|
2299
|
-
} else if (pInputAddress in tmpHashMapping) {
|
|
2300
|
-
tmpOldDescriptorAddress = tmpHashMapping[pInputAddress];
|
|
2301
|
-
}
|
|
2302
|
-
|
|
2303
|
-
// If there was a matching descriptor in the manifest, store it in the temporary descriptor
|
|
2304
|
-
if (tmpOldDescriptorAddress) {
|
|
2305
|
-
tmpDescriptor = pManyfestSchemaDescriptors[tmpOldDescriptorAddress];
|
|
2306
|
-
delete pManyfestSchemaDescriptors[tmpOldDescriptorAddress];
|
|
2307
|
-
} else {
|
|
2308
|
-
// Create a new descriptor! Map it to the input address.
|
|
2309
|
-
tmpDescriptor = {
|
|
2310
|
-
Hash: pInputAddress
|
|
2311
|
-
};
|
|
2312
|
-
}
|
|
2313
|
-
|
|
2314
|
-
// Now re-add the descriptor to the manyfest schema
|
|
2315
|
-
pManyfestSchemaDescriptors[tmpNewDescriptorAddress] = tmpDescriptor;
|
|
2316
|
-
});
|
|
2317
|
-
return true;
|
|
2318
|
-
}
|
|
2319
|
-
|
|
2320
|
-
/**
|
|
2321
|
-
* @param {object} pManyfestSchemaDescriptors - The manyfest schema descriptors to resolve address mappings for
|
|
2322
|
-
* @param {object} pAddressMapping - The address mapping object to use for remapping
|
|
2323
|
-
*
|
|
2324
|
-
* @return {object} A new object containing the remapped schema descriptors
|
|
2325
|
-
*/
|
|
2326
|
-
safeResolveAddressMappings(pManyfestSchemaDescriptors, pAddressMapping) {
|
|
2327
|
-
// This returns the descriptors as a new object, safely remapping without mutating the original schema Descriptors
|
|
2328
|
-
let tmpManyfestSchemaDescriptors = JSON.parse(JSON.stringify(pManyfestSchemaDescriptors));
|
|
2329
|
-
this.resolveAddressMappings(tmpManyfestSchemaDescriptors, pAddressMapping);
|
|
2330
|
-
return tmpManyfestSchemaDescriptors;
|
|
2331
|
-
}
|
|
2332
|
-
|
|
2333
|
-
/**
|
|
2334
|
-
* @param {object} pManyfestSchemaDescriptorsDestination - The destination manyfest schema descriptors
|
|
2335
|
-
* @param {object} pManyfestSchemaDescriptorsSource - The source manyfest schema descriptors
|
|
2336
|
-
*
|
|
2337
|
-
* @return {object} A new object containing the merged schema descriptors
|
|
2338
|
-
*/
|
|
2339
|
-
mergeAddressMappings(pManyfestSchemaDescriptorsDestination, pManyfestSchemaDescriptorsSource) {
|
|
2340
|
-
if (typeof pManyfestSchemaDescriptorsSource != 'object' || typeof pManyfestSchemaDescriptorsDestination != 'object') {
|
|
2341
|
-
this.logError("Attempted to merge two schema descriptors but both were not objects.");
|
|
2342
|
-
return false;
|
|
2343
|
-
}
|
|
2344
|
-
let tmpSource = JSON.parse(JSON.stringify(pManyfestSchemaDescriptorsSource));
|
|
2345
|
-
let tmpNewManyfestSchemaDescriptors = JSON.parse(JSON.stringify(pManyfestSchemaDescriptorsDestination));
|
|
2346
|
-
|
|
2347
|
-
// The first passed-in set of descriptors takes precedence.
|
|
2348
|
-
let tmpDescriptorAddresses = Object.keys(tmpSource);
|
|
2349
|
-
tmpDescriptorAddresses.forEach(pDescriptorAddress => {
|
|
2350
|
-
if (!(pDescriptorAddress in tmpNewManyfestSchemaDescriptors)) {
|
|
2351
|
-
tmpNewManyfestSchemaDescriptors[pDescriptorAddress] = tmpSource[pDescriptorAddress];
|
|
2352
|
-
}
|
|
2353
|
-
});
|
|
2354
|
-
return tmpNewManyfestSchemaDescriptors;
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
module.exports = ManyfestSchemaManipulation;
|
|
2358
|
-
}, {
|
|
2359
|
-
"./Manyfest-LogToConsole.js": 5
|
|
2360
|
-
}],
|
|
2361
|
-
14: [function (require, module, exports) {
|
|
2362
|
-
/**
|
|
2363
|
-
* @author <steven@velozo.com>
|
|
2364
|
-
*/
|
|
2365
|
-
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2366
|
-
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
2367
|
-
let libHashTranslation = require('./Manyfest-HashTranslation.js');
|
|
2368
|
-
let libObjectAddressCheckAddressExists = require('./Manyfest-ObjectAddress-CheckAddressExists.js');
|
|
2369
|
-
let libObjectAddressGetValue = require('./Manyfest-ObjectAddress-GetValue.js');
|
|
2370
|
-
let libObjectAddressSetValue = require('./Manyfest-ObjectAddress-SetValue.js');
|
|
2371
|
-
let libObjectAddressDeleteValue = require('./Manyfest-ObjectAddress-DeleteValue.js');
|
|
2372
|
-
let libObjectAddressGeneration = require('./Manyfest-ObjectAddressGeneration.js');
|
|
2373
|
-
let libSchemaManipulation = require('./Manyfest-SchemaManipulation.js');
|
|
2374
|
-
const _DefaultConfiguration = {
|
|
2375
|
-
Scope: 'DEFAULT',
|
|
2376
|
-
Descriptors: {}
|
|
2377
|
-
};
|
|
2378
|
-
|
|
2379
|
-
/**
|
|
2380
|
-
* @typedef {{
|
|
2381
|
-
* Hash?: string,
|
|
2382
|
-
* Name?: string,
|
|
2383
|
-
* DataType?: string,
|
|
2384
|
-
* Required?: boolean,
|
|
2385
|
-
* Address?: string,
|
|
2386
|
-
* Description?: string,
|
|
2387
|
-
* [key: string]: any,
|
|
2388
|
-
* }} ManifestDescriptor
|
|
2389
|
-
*/
|
|
2390
|
-
|
|
2391
|
-
/**
|
|
2392
|
-
* Manyfest object address-based descriptions and manipulations.
|
|
2393
|
-
*
|
|
2394
|
-
* @class Manyfest
|
|
2395
|
-
*/
|
|
2396
|
-
class Manyfest extends libFableServiceProviderBase {
|
|
2397
|
-
constructor(pFable, pManifest, pServiceHash) {
|
|
2398
|
-
if (pFable === undefined) {
|
|
2399
|
-
super({});
|
|
2400
|
-
} else {
|
|
2401
|
-
super(pFable, pManifest, pServiceHash);
|
|
2402
|
-
}
|
|
2403
|
-
|
|
2404
|
-
/** @type {import('fable')} */
|
|
2405
|
-
this.fable;
|
|
2406
|
-
/** @type {Record<string, any>} */
|
|
2407
|
-
this.options;
|
|
2408
|
-
/** @type {string} */
|
|
2409
|
-
this.Hash;
|
|
2410
|
-
/** @type {string} */
|
|
2411
|
-
this.UUID;
|
|
2412
|
-
this.serviceType = 'Manifest';
|
|
2413
|
-
|
|
2414
|
-
// Wire in logging
|
|
2415
|
-
this.logInfo = libSimpleLog;
|
|
2416
|
-
this.logError = libSimpleLog;
|
|
2417
|
-
|
|
2418
|
-
// Create an object address resolver and map in the functions
|
|
2419
|
-
this.objectAddressCheckAddressExists = new libObjectAddressCheckAddressExists(this.logInfo, this.logError);
|
|
2420
|
-
this.objectAddressGetValue = new libObjectAddressGetValue(this.logInfo, this.logError);
|
|
2421
|
-
this.objectAddressSetValue = new libObjectAddressSetValue(this.logInfo, this.logError);
|
|
2422
|
-
this.objectAddressDeleteValue = new libObjectAddressDeleteValue(this.logInfo, this.logError);
|
|
2423
|
-
if (!('defaultValues' in this.options)) {
|
|
2424
|
-
this.options.defaultValues = {
|
|
2425
|
-
"String": "",
|
|
2426
|
-
"Number": 0,
|
|
2427
|
-
"Float": 0.0,
|
|
2428
|
-
"Integer": 0,
|
|
2429
|
-
"PreciseNumber": "0.0",
|
|
2430
|
-
"Boolean": false,
|
|
2431
|
-
"Binary": 0,
|
|
2432
|
-
"DateTime": 0,
|
|
2433
|
-
"Array": [],
|
|
2434
|
-
"Object": {},
|
|
2435
|
-
"Null": null
|
|
2436
|
-
};
|
|
2437
|
-
}
|
|
2438
|
-
if (!('strict' in this.options)) {
|
|
2439
|
-
this.options.strict = false;
|
|
2440
|
-
}
|
|
2441
|
-
|
|
2442
|
-
/** @type {string} */
|
|
2443
|
-
this.scope = undefined;
|
|
2444
|
-
/** @type {Array<string>} */
|
|
2445
|
-
this.elementAddresses = undefined;
|
|
2446
|
-
/** @type {Record<string, string>} */
|
|
2447
|
-
this.elementHashes = undefined;
|
|
2448
|
-
/** @type {Record<string, ManifestDescriptor>} */
|
|
2449
|
-
this.elementDescriptors = undefined;
|
|
2450
|
-
this.reset();
|
|
2451
|
-
if (typeof this.options === 'object') {
|
|
2452
|
-
this.loadManifest(this.options);
|
|
2453
|
-
}
|
|
2454
|
-
this.schemaManipulations = new libSchemaManipulation(this.logInfo, this.logError);
|
|
2455
|
-
this.objectAddressGeneration = new libObjectAddressGeneration(this.logInfo, this.logError);
|
|
2456
|
-
this.hashTranslations = new libHashTranslation(this.logInfo, this.logError);
|
|
2457
|
-
this.numberRegex = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
/*************************************************************************
|
|
2461
|
-
* Schema Manifest Loading, Reading, Manipulation and Serialization Functions
|
|
2462
|
-
*/
|
|
2463
|
-
|
|
2464
|
-
// Reset critical manifest properties
|
|
2465
|
-
reset() {
|
|
2466
|
-
this.scope = 'DEFAULT';
|
|
2467
|
-
this.elementAddresses = [];
|
|
2468
|
-
this.elementHashes = {};
|
|
2469
|
-
this.elementDescriptors = {};
|
|
2470
|
-
}
|
|
2471
|
-
clone() {
|
|
2472
|
-
// Make a copy of the options in-place
|
|
2473
|
-
let tmpNewOptions = JSON.parse(JSON.stringify(this.options));
|
|
2474
|
-
let tmpNewManyfest = new Manyfest(this.fable, tmpNewOptions, this.Hash);
|
|
2475
|
-
tmpNewManyfest.logInfo = this.logInfo;
|
|
2476
|
-
tmpNewManyfest.logError = this.logError;
|
|
2477
|
-
//FIXME: mostly written by co-pilot
|
|
2478
|
-
const {
|
|
2479
|
-
Scope,
|
|
2480
|
-
Descriptors,
|
|
2481
|
-
HashTranslations
|
|
2482
|
-
} = this.getManifest();
|
|
2483
|
-
tmpNewManyfest.scope = Scope;
|
|
2484
|
-
tmpNewManyfest.elementDescriptors = Descriptors;
|
|
2485
|
-
tmpNewManyfest.elementAddresses = Object.keys(Descriptors);
|
|
2486
|
-
// Rebuild the element hashes
|
|
2487
|
-
for (let i = 0; i < tmpNewManyfest.elementAddresses.length; i++) {
|
|
2488
|
-
let tmpAddress = tmpNewManyfest.elementAddresses[i];
|
|
2489
|
-
let tmpDescriptor = tmpNewManyfest.elementDescriptors[tmpAddress];
|
|
2490
|
-
tmpNewManyfest.elementHashes[tmpAddress] = tmpAddress;
|
|
2491
|
-
if ('Hash' in tmpDescriptor) {
|
|
2492
|
-
tmpNewManyfest.elementHashes[tmpDescriptor.Hash] = tmpAddress;
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
// Import the hash translations
|
|
2497
|
-
tmpNewManyfest.hashTranslations.addTranslation(this.hashTranslations.translationTable);
|
|
2498
|
-
return tmpNewManyfest;
|
|
2499
|
-
}
|
|
2500
|
-
|
|
2501
|
-
// Deserialize a Manifest from a string
|
|
2502
|
-
/**
|
|
2503
|
-
* @param {string} pManifestString - The manifest string to deserialize
|
|
2504
|
-
*
|
|
2505
|
-
* @return {Manyfest} The deserialized manifest
|
|
2506
|
-
*/
|
|
2507
|
-
deserialize(pManifestString) {
|
|
2508
|
-
// TODO: Add guards for bad manifest string
|
|
2509
|
-
this.loadManifest(JSON.parse(pManifestString));
|
|
2510
|
-
return this;
|
|
2511
|
-
}
|
|
2512
|
-
|
|
2513
|
-
// Load a manifest from an object
|
|
2514
|
-
loadManifest(pManifest) {
|
|
2515
|
-
if (typeof pManifest !== 'object') {
|
|
2516
|
-
this.logError("(".concat(this.scope, ") Error loading manifest; expecting an object but parameter was type ").concat(typeof pManifest, "."));
|
|
2517
|
-
}
|
|
2518
|
-
let tmpManifest = typeof pManifest == 'object' ? pManifest : {};
|
|
2519
|
-
let tmpDescriptorKeys = Object.keys(_DefaultConfiguration);
|
|
2520
|
-
for (let i = 0; i < tmpDescriptorKeys.length; i++) {
|
|
2521
|
-
if (!(tmpDescriptorKeys[i] in tmpManifest)) {
|
|
2522
|
-
tmpManifest[tmpDescriptorKeys[i]] = JSON.parse(JSON.stringify(_DefaultConfiguration[tmpDescriptorKeys[i]]));
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
2525
|
-
if ('Scope' in tmpManifest) {
|
|
2526
|
-
if (typeof tmpManifest.Scope === 'string') {
|
|
2527
|
-
this.scope = tmpManifest.Scope;
|
|
2528
|
-
} else {
|
|
2529
|
-
this.logError("(".concat(this.scope, ") Error loading scope from manifest; expecting a string but property was type ").concat(typeof tmpManifest.Scope, "."), tmpManifest);
|
|
2530
|
-
}
|
|
2531
|
-
} else {
|
|
2532
|
-
this.logError("(".concat(this.scope, ") Error loading scope from manifest object. Property \"Scope\" does not exist in the root of the object."), tmpManifest);
|
|
2533
|
-
}
|
|
2534
|
-
if ('Descriptors' in tmpManifest) {
|
|
2535
|
-
if (typeof tmpManifest.Descriptors === 'object') {
|
|
2536
|
-
let tmpDescriptionAddresses = Object.keys(tmpManifest.Descriptors);
|
|
2537
|
-
for (let i = 0; i < tmpDescriptionAddresses.length; i++) {
|
|
2538
|
-
this.addDescriptor(tmpDescriptionAddresses[i], tmpManifest.Descriptors[tmpDescriptionAddresses[i]]);
|
|
2539
|
-
}
|
|
2540
|
-
} else {
|
|
2541
|
-
this.logError("(".concat(this.scope, ") Error loading description object from manifest object. Expecting an object in 'Manifest.Descriptors' but the property was type ").concat(typeof tmpManifest.Descriptors, "."), tmpManifest);
|
|
2542
|
-
}
|
|
2543
|
-
} else {
|
|
2544
|
-
this.logError("(".concat(this.scope, ") Error loading object description from manifest object. Property \"Descriptors\" does not exist in the root of the Manifest object."), tmpManifest);
|
|
2545
|
-
}
|
|
2546
|
-
if ('HashTranslations' in tmpManifest) {
|
|
2547
|
-
if (typeof tmpManifest.HashTranslations === 'object') {
|
|
2548
|
-
for (let i = 0; i < tmpManifest.HashTranslations.length; i++) {
|
|
2549
|
-
// Each translation is
|
|
2550
|
-
//FIXME: ?????????
|
|
2551
|
-
}
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
}
|
|
2555
|
-
|
|
2556
|
-
/**
|
|
2557
|
-
* Serialize the Manifest to a string
|
|
2558
|
-
*
|
|
2559
|
-
* @return {string} - The serialized manifest
|
|
2560
|
-
*/
|
|
2561
|
-
serialize() {
|
|
2562
|
-
return JSON.stringify(this.getManifest());
|
|
2563
|
-
}
|
|
2564
|
-
|
|
2565
|
-
/**
|
|
2566
|
-
* @return {{ Scope: string, Descriptors: Record<string, ManifestDescriptor>, HashTranslations: Record<string, string> }} - A copy of the manifest state.
|
|
2567
|
-
*/
|
|
2568
|
-
getManifest() {
|
|
2569
|
-
return {
|
|
2570
|
-
Scope: this.scope,
|
|
2571
|
-
Descriptors: JSON.parse(JSON.stringify(this.elementDescriptors)),
|
|
2572
|
-
HashTranslations: JSON.parse(JSON.stringify(this.hashTranslations.translationTable))
|
|
2573
|
-
};
|
|
2574
|
-
}
|
|
2575
|
-
|
|
2576
|
-
/**
|
|
2577
|
-
* Add a descriptor to the manifest
|
|
2578
|
-
*
|
|
2579
|
-
* @param {string} pAddress - The address of the element to add the descriptor for.
|
|
2580
|
-
* @param {ManifestDescriptor} pDescriptor - The descriptor object to add.
|
|
2581
|
-
*/
|
|
2582
|
-
addDescriptor(pAddress, pDescriptor) {
|
|
2583
|
-
if (typeof pDescriptor === 'object') {
|
|
2584
|
-
// Add the Address into the Descriptor if it doesn't exist:
|
|
2585
|
-
if (!('Address' in pDescriptor)) {
|
|
2586
|
-
pDescriptor.Address = pAddress;
|
|
2587
|
-
}
|
|
2588
|
-
if (!(pAddress in this.elementDescriptors)) {
|
|
2589
|
-
this.elementAddresses.push(pAddress);
|
|
2590
|
-
}
|
|
2591
|
-
|
|
2592
|
-
// Add the element descriptor to the schema
|
|
2593
|
-
this.elementDescriptors[pAddress] = pDescriptor;
|
|
2594
|
-
|
|
2595
|
-
// Always add the address as a hash
|
|
2596
|
-
this.elementHashes[pAddress] = pAddress;
|
|
2597
|
-
if ('Hash' in pDescriptor) {
|
|
2598
|
-
// TODO: Check if this is a good idea or not..
|
|
2599
|
-
// Collisions are bound to happen with both representations of the address/hash in here and developers being able to create their own hashes.
|
|
2600
|
-
this.elementHashes[pDescriptor.Hash] = pAddress;
|
|
2601
|
-
} else {
|
|
2602
|
-
pDescriptor.Hash = pAddress;
|
|
2603
|
-
}
|
|
2604
|
-
return true;
|
|
2605
|
-
} else {
|
|
2606
|
-
this.logError("(".concat(this.scope, ") Error loading object descriptor for address '").concat(pAddress, "' from manifest object. Expecting an object but property was type ").concat(typeof pDescriptor, "."));
|
|
2607
|
-
return false;
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
|
|
2611
|
-
/**
|
|
2612
|
-
* @param {string} pHash - The hash of the address to resolve.
|
|
2613
|
-
*
|
|
2614
|
-
* @return {ManifestDescriptor} The descriptor for the address
|
|
2615
|
-
*/
|
|
2616
|
-
getDescriptorByHash(pHash) {
|
|
2617
|
-
return this.getDescriptor(this.resolveHashAddress(pHash));
|
|
2618
|
-
}
|
|
2619
|
-
|
|
2620
|
-
/**
|
|
2621
|
-
* @param {string} pAddress - The address of the element to get the descriptor for.
|
|
2622
|
-
*
|
|
2623
|
-
* @return {ManifestDescriptor} The descriptor for the address
|
|
2624
|
-
*/
|
|
2625
|
-
getDescriptor(pAddress) {
|
|
2626
|
-
return this.elementDescriptors[pAddress];
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
/**
|
|
2630
|
-
* execute an action function for each descriptor
|
|
2631
|
-
* @param {(d: ManifestDescriptor) => void} fAction - The action function to execute for each descriptor.
|
|
2632
|
-
*/
|
|
2633
|
-
eachDescriptor(fAction) {
|
|
2634
|
-
let tmpDescriptorAddresses = Object.keys(this.elementDescriptors);
|
|
2635
|
-
for (let i = 0; i < tmpDescriptorAddresses.length; i++) {
|
|
2636
|
-
fAction(this.elementDescriptors[tmpDescriptorAddresses[i]]);
|
|
2637
|
-
}
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
/*************************************************************************
|
|
2641
|
-
* Beginning of Object Manipulation (read & write) Functions
|
|
2642
|
-
*/
|
|
2643
|
-
// Check if an element exists by its hash
|
|
2644
|
-
checkAddressExistsByHash(pObject, pHash) {
|
|
2645
|
-
return this.checkAddressExists(pObject, this.resolveHashAddress(pHash));
|
|
2646
|
-
}
|
|
2647
|
-
|
|
2648
|
-
// Check if an element exists at an address
|
|
2649
|
-
checkAddressExists(pObject, pAddress) {
|
|
2650
|
-
return this.objectAddressCheckAddressExists.checkAddressExists(pObject, pAddress);
|
|
2651
|
-
}
|
|
2652
|
-
|
|
2653
|
-
// Turn a hash into an address, factoring in the translation table.
|
|
2654
|
-
resolveHashAddress(pHash) {
|
|
2655
|
-
let tmpAddress = undefined;
|
|
2656
|
-
let tmpInElementHashTable = pHash in this.elementHashes;
|
|
2657
|
-
let tmpInTranslationTable = pHash in this.hashTranslations.translationTable;
|
|
2658
|
-
|
|
2659
|
-
// The most straightforward: the hash exists, no translations.
|
|
2660
|
-
if (tmpInElementHashTable && !tmpInTranslationTable) {
|
|
2661
|
-
tmpAddress = this.elementHashes[pHash];
|
|
2662
|
-
}
|
|
2663
|
-
// There is a translation from one hash to another, and, the elementHashes contains the pointer end
|
|
2664
|
-
else if (tmpInTranslationTable && this.hashTranslations.translate(pHash) in this.elementHashes) {
|
|
2665
|
-
tmpAddress = this.elementHashes[this.hashTranslations.translate(pHash)];
|
|
2666
|
-
}
|
|
2667
|
-
// Use the level of indirection only in the Translation Table
|
|
2668
|
-
else if (tmpInTranslationTable) {
|
|
2669
|
-
tmpAddress = this.hashTranslations.translate(pHash);
|
|
2670
|
-
}
|
|
2671
|
-
// Just treat the hash as an address.
|
|
2672
|
-
// TODO: Discuss this ... it is magic but controversial
|
|
2673
|
-
else {
|
|
2674
|
-
tmpAddress = pHash;
|
|
2675
|
-
}
|
|
2676
|
-
return tmpAddress;
|
|
2677
|
-
}
|
|
2678
|
-
|
|
2679
|
-
// Get the value of an element by its hash
|
|
2680
|
-
getValueByHash(pObject, pHash) {
|
|
2681
|
-
let tmpValue = this.getValueAtAddress(pObject, this.resolveHashAddress(pHash));
|
|
2682
|
-
if (typeof tmpValue == 'undefined') {
|
|
2683
|
-
// Try to get a default if it exists
|
|
2684
|
-
tmpValue = this.getDefaultValue(this.getDescriptorByHash(pHash));
|
|
2685
|
-
}
|
|
2686
|
-
return tmpValue;
|
|
2687
|
-
}
|
|
2688
|
-
lintAddress(pAddress) {
|
|
2689
|
-
let tmpLintedAddress = pAddress.trim();
|
|
2690
|
-
// Check for a single . (but not a ..) at the end of the address and remove it.
|
|
2691
|
-
if (tmpLintedAddress.endsWith('..')) {
|
|
2692
|
-
tmpLintedAddress = tmpLintedAddress.slice(0, -1);
|
|
2693
|
-
} else if (tmpLintedAddress.endsWith('.')) {
|
|
2694
|
-
tmpLintedAddress = tmpLintedAddress.slice(0, -1);
|
|
2695
|
-
}
|
|
2696
|
-
return tmpLintedAddress;
|
|
2697
|
-
}
|
|
2698
|
-
|
|
2699
|
-
// Get the value of an element at an address
|
|
2700
|
-
getValueAtAddress(pObject, pAddress) {
|
|
2701
|
-
let tmpLintedAddress = this.lintAddress(pAddress);
|
|
2702
|
-
if (tmpLintedAddress == '') {
|
|
2703
|
-
this.logError("(".concat(this.scope, ") Error getting value at address; address is an empty string."), pObject);
|
|
2704
|
-
return undefined;
|
|
2705
|
-
}
|
|
2706
|
-
let tmpValue = this.objectAddressGetValue.getValueAtAddress(pObject, tmpLintedAddress);
|
|
2707
|
-
if (typeof tmpValue == 'undefined') {
|
|
2708
|
-
// Try to get a default if it exists
|
|
2709
|
-
tmpValue = this.getDefaultValue(this.getDescriptor(tmpLintedAddress));
|
|
2710
|
-
}
|
|
2711
|
-
return tmpValue;
|
|
2712
|
-
}
|
|
2713
|
-
|
|
2714
|
-
// Set the value of an element by its hash
|
|
2715
|
-
setValueByHash(pObject, pHash, pValue) {
|
|
2716
|
-
return this.setValueAtAddress(pObject, this.resolveHashAddress(pHash), pValue);
|
|
2717
|
-
}
|
|
2718
|
-
|
|
2719
|
-
// Set the value of an element at an address
|
|
2720
|
-
setValueAtAddress(pObject, pAddress, pValue) {
|
|
2721
|
-
let tmpLintedAddress = this.lintAddress(pAddress);
|
|
2722
|
-
return this.objectAddressSetValue.setValueAtAddress(pObject, tmpLintedAddress, pValue);
|
|
2723
|
-
}
|
|
2724
|
-
|
|
2725
|
-
// Delete the value of an element by its hash
|
|
2726
|
-
deleteValueByHash(pObject, pHash, pValue) {
|
|
2727
|
-
return this.deleteValueAtAddress(pObject, this.resolveHashAddress(pHash), pValue);
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
// Delete the value of an element at an address
|
|
2731
|
-
deleteValueAtAddress(pObject, pAddress, pValue) {
|
|
2732
|
-
let tmpLintedAddress = this.lintAddress(pAddress);
|
|
2733
|
-
return this.objectAddressDeleteValue.deleteValueAtAddress(pObject, tmpLintedAddress, pValue);
|
|
2734
|
-
}
|
|
2735
|
-
|
|
2736
|
-
// Validate the consistency of an object against the schema
|
|
2737
|
-
validate(pObject) {
|
|
2738
|
-
let tmpValidationData = {
|
|
2739
|
-
Error: null,
|
|
2740
|
-
Errors: [],
|
|
2741
|
-
MissingElements: []
|
|
2742
|
-
};
|
|
2743
|
-
if (typeof pObject !== 'object') {
|
|
2744
|
-
tmpValidationData.Error = true;
|
|
2745
|
-
tmpValidationData.Errors.push("Expected passed in object to be type object but was passed in ".concat(typeof pObject));
|
|
2746
|
-
}
|
|
2747
|
-
let addValidationError = (pAddress, pErrorMessage) => {
|
|
2748
|
-
tmpValidationData.Error = true;
|
|
2749
|
-
tmpValidationData.Errors.push("Element at address \"".concat(pAddress, "\" ").concat(pErrorMessage, "."));
|
|
2750
|
-
};
|
|
2751
|
-
|
|
2752
|
-
// Now enumerate through the values and check for anomalies based on the schema
|
|
2753
|
-
for (let i = 0; i < this.elementAddresses.length; i++) {
|
|
2754
|
-
let tmpDescriptor = this.getDescriptor(this.elementAddresses[i]);
|
|
2755
|
-
let tmpValueExists = this.checkAddressExists(pObject, tmpDescriptor.Address);
|
|
2756
|
-
let tmpValue = this.getValueAtAddress(pObject, tmpDescriptor.Address);
|
|
2757
|
-
if (typeof tmpValue == 'undefined' || !tmpValueExists) {
|
|
2758
|
-
// This will technically mean that `Object.Some.Value = undefined` will end up showing as "missing"
|
|
2759
|
-
// TODO: Do we want to do a different message based on if the property exists but is undefined?
|
|
2760
|
-
tmpValidationData.MissingElements.push(tmpDescriptor.Address);
|
|
2761
|
-
if (tmpDescriptor.Required || this.options.strict) {
|
|
2762
|
-
addValidationError(tmpDescriptor.Address, 'is flagged REQUIRED but is not set in the object');
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
2765
|
-
|
|
2766
|
-
// Now see if there is a data type specified for this element
|
|
2767
|
-
if (tmpDescriptor.DataType) {
|
|
2768
|
-
let tmpElementType = typeof tmpValue;
|
|
2769
|
-
switch (tmpDescriptor.DataType.toString().trim().toLowerCase()) {
|
|
2770
|
-
case 'string':
|
|
2771
|
-
if (tmpElementType != 'string') {
|
|
2772
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " but is of the type ").concat(tmpElementType));
|
|
2773
|
-
}
|
|
2774
|
-
break;
|
|
2775
|
-
case "precisenumber":
|
|
2776
|
-
if (tmpElementType != 'string') {
|
|
2777
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " but is of the type ").concat(tmpElementType));
|
|
2778
|
-
} else if (!this.numberRegex.test(tmpValue)) {
|
|
2779
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " but is not a valid number"));
|
|
2780
|
-
}
|
|
2781
|
-
break;
|
|
2782
|
-
case 'number':
|
|
2783
|
-
if (tmpElementType != 'number') {
|
|
2784
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " but is of the type ").concat(tmpElementType));
|
|
2785
|
-
}
|
|
2786
|
-
break;
|
|
2787
|
-
case 'integer':
|
|
2788
|
-
if (tmpElementType != 'number') {
|
|
2789
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " but is of the type ").concat(tmpElementType));
|
|
2790
|
-
} else {
|
|
2791
|
-
let tmpValueString = tmpValue.toString();
|
|
2792
|
-
if (tmpValueString.indexOf('.') > -1) {
|
|
2793
|
-
// TODO: Is this an error?
|
|
2794
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " but has a decimal point in the number."));
|
|
2795
|
-
}
|
|
2796
|
-
}
|
|
2797
|
-
break;
|
|
2798
|
-
case 'float':
|
|
2799
|
-
if (tmpElementType != 'number') {
|
|
2800
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " but is of the type ").concat(tmpElementType));
|
|
2801
|
-
}
|
|
2802
|
-
break;
|
|
2803
|
-
case 'datetime':
|
|
2804
|
-
let tmpValueDate = new Date(tmpValue);
|
|
2805
|
-
if (tmpValueDate.toString() == 'Invalid Date') {
|
|
2806
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " but is not parsable as a Date by Javascript"));
|
|
2807
|
-
}
|
|
2808
|
-
default:
|
|
2809
|
-
// Check if this is a string, in the default case
|
|
2810
|
-
// Note this is only when a DataType is specified and it is an unrecognized data type.
|
|
2811
|
-
if (tmpElementType != 'string') {
|
|
2812
|
-
addValidationError(tmpDescriptor.Address, "has a DataType ".concat(tmpDescriptor.DataType, " (which auto-converted to String because it was unrecognized) but is of the type ").concat(tmpElementType));
|
|
2813
|
-
}
|
|
2814
|
-
break;
|
|
2815
|
-
}
|
|
2816
|
-
}
|
|
2817
|
-
}
|
|
2818
|
-
return tmpValidationData;
|
|
2819
|
-
}
|
|
2820
|
-
|
|
2821
|
-
/**
|
|
2822
|
-
* Returns a default value, or, the default value for the data type (which is overridable with configuration)
|
|
2823
|
-
*
|
|
2824
|
-
* @param {ManifestDescriptor} pDescriptor - The descriptor definition.
|
|
2825
|
-
*/
|
|
2826
|
-
getDefaultValue(pDescriptor) {
|
|
2827
|
-
if (typeof pDescriptor != 'object') {
|
|
2828
|
-
return undefined;
|
|
2829
|
-
}
|
|
2830
|
-
if ('Default' in pDescriptor) {
|
|
2831
|
-
return pDescriptor.Default;
|
|
2832
|
-
} else {
|
|
2833
|
-
// Default to a null if it doesn't have a type specified.
|
|
2834
|
-
// This will ensure a placeholder is created but isn't misinterpreted.
|
|
2835
|
-
let tmpDataType = 'DataType' in pDescriptor ? pDescriptor.DataType : 'String';
|
|
2836
|
-
if (tmpDataType in this.options.defaultValues) {
|
|
2837
|
-
return this.options.defaultValues[tmpDataType];
|
|
2838
|
-
} else {
|
|
2839
|
-
// give up and return null
|
|
2840
|
-
return null;
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
|
-
}
|
|
2844
|
-
|
|
2845
|
-
// Enumerate through the schema and populate default values if they don't exist.
|
|
2846
|
-
populateDefaults(pObject, pOverwriteProperties) {
|
|
2847
|
-
return this.populateObject(pObject, pOverwriteProperties,
|
|
2848
|
-
// This just sets up a simple filter to see if there is a default set.
|
|
2849
|
-
pDescriptor => {
|
|
2850
|
-
return 'Default' in pDescriptor;
|
|
2851
|
-
});
|
|
2852
|
-
}
|
|
2853
|
-
|
|
2854
|
-
// Forcefully populate all values even if they don't have defaults.
|
|
2855
|
-
// Based on type, this can do unexpected things.
|
|
2856
|
-
populateObject(pObject, pOverwriteProperties, fFilter) {
|
|
2857
|
-
// Automatically create an object if one isn't passed in.
|
|
2858
|
-
let tmpObject = typeof pObject === 'object' ? pObject : {};
|
|
2859
|
-
// Default to *NOT OVERWRITING* properties
|
|
2860
|
-
let tmpOverwriteProperties = typeof pOverwriteProperties == 'undefined' ? false : pOverwriteProperties;
|
|
2861
|
-
// This is a filter function, which is passed the schema and allows complex filtering of population
|
|
2862
|
-
// The default filter function just returns true, populating everything.
|
|
2863
|
-
let tmpFilterFunction = typeof fFilter == 'function' ? fFilter : pDescriptor => {
|
|
2864
|
-
return true;
|
|
2865
|
-
};
|
|
2866
|
-
this.elementAddresses.forEach(pAddress => {
|
|
2867
|
-
let tmpDescriptor = this.getDescriptor(pAddress);
|
|
2868
|
-
// Check the filter function to see if this is an address we want to set the value for.
|
|
2869
|
-
if (tmpFilterFunction(tmpDescriptor)) {
|
|
2870
|
-
// If we are overwriting properties OR the property does not exist
|
|
2871
|
-
if (tmpOverwriteProperties || !this.checkAddressExists(tmpObject, pAddress)) {
|
|
2872
|
-
this.setValueAtAddress(tmpObject, pAddress, this.getDefaultValue(tmpDescriptor));
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2875
|
-
});
|
|
2876
|
-
return tmpObject;
|
|
2877
|
-
}
|
|
2878
|
-
}
|
|
2879
|
-
;
|
|
2880
|
-
module.exports = Manyfest;
|
|
2881
|
-
}, {
|
|
2882
|
-
"./Manyfest-HashTranslation.js": 4,
|
|
2883
|
-
"./Manyfest-LogToConsole.js": 5,
|
|
2884
|
-
"./Manyfest-ObjectAddress-CheckAddressExists.js": 6,
|
|
2885
|
-
"./Manyfest-ObjectAddress-DeleteValue.js": 7,
|
|
2886
|
-
"./Manyfest-ObjectAddress-GetValue.js": 8,
|
|
2887
|
-
"./Manyfest-ObjectAddress-SetValue.js": 10,
|
|
2888
|
-
"./Manyfest-ObjectAddressGeneration.js": 11,
|
|
2889
|
-
"./Manyfest-SchemaManipulation.js": 13,
|
|
2890
|
-
"fable-serviceproviderbase": 2
|
|
2891
|
-
}]
|
|
2892
|
-
}, {}, [14])(14);
|
|
2893
|
-
});
|
|
2894
|
-
//# sourceMappingURL=manyfest.js.map
|