manyfest 1.0.43 → 1.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -260
- 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/hash-translation.md +202 -0
- package/docs/index.html +51 -0
- package/docs/quickstart.md +203 -0
- package/docs/reading.md +339 -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 +1 -1
- 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/README.md
CHANGED
|
@@ -17,11 +17,56 @@ Manyfest's purpose is to solve this problem by creating a simple pattern to desc
|
|
|
17
17
|
|
|
18
18
|
_Man-E-Faces approves of Manyfest!_
|
|
19
19
|
|
|
20
|
+
## Quick Example
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
const libManyfest = require('manyfest');
|
|
24
|
+
|
|
25
|
+
// Define a schema
|
|
26
|
+
const manifest = new libManyfest({
|
|
27
|
+
Scope: 'User',
|
|
28
|
+
Descriptors: {
|
|
29
|
+
'Name': { DataType: 'String', Required: true },
|
|
30
|
+
'Email': { DataType: 'String', Required: true },
|
|
31
|
+
'Profile.Age': { DataType: 'Integer' },
|
|
32
|
+
'Profile.Bio': { DataType: 'String', Default: '' }
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Read safely from nested objects (no exceptions on missing paths)
|
|
37
|
+
const user = { Name: 'Alice', Profile: { Age: 30 } };
|
|
38
|
+
manifest.getValueAtAddress(user, 'Profile.Age'); // 30
|
|
39
|
+
manifest.getValueAtAddress(user, 'Profile.Phone'); // undefined
|
|
40
|
+
|
|
41
|
+
// Write to any depth (intermediate objects are created automatically)
|
|
42
|
+
const newUser = {};
|
|
43
|
+
manifest.setValueAtAddress(newUser, 'Name', 'Bob');
|
|
44
|
+
manifest.setValueAtAddress(newUser, 'Profile.Age', 25);
|
|
45
|
+
// newUser is now: { Name: 'Bob', Profile: { Age: 25 } }
|
|
46
|
+
|
|
47
|
+
// Validate against the schema
|
|
48
|
+
const result = manifest.validate({ Name: 'Carol' });
|
|
49
|
+
// result.Errors: ['Element at address "Email" is flagged REQUIRED but is not set in the object.']
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Documentation
|
|
53
|
+
|
|
54
|
+
| Guide | Description |
|
|
55
|
+
| ----- | ----------- |
|
|
56
|
+
| [Quickstart](docs/quickstart.md) | Installation, first schema, and basic operations in under five minutes. |
|
|
57
|
+
| [Reading Values](docs/reading.md) | `getValueAtAddress`, `getValueByHash`, array access, boxed properties, back-navigation, function resolution and defaults. |
|
|
58
|
+
| [Writing Values](docs/writing.md) | `setValueAtAddress`, `setValueByHash`, auto-creation of nested structures, populating defaults and deleting values. |
|
|
59
|
+
| [Validating Objects](docs/validating.md) | Required fields, strict mode, data type checking and the validation result structure. |
|
|
60
|
+
| [Schema Definition](docs/schema.md) | Descriptors, data types, scope, serialization, cloning and programmatic schema building. |
|
|
61
|
+
| [Address Notation](docs/address-notation.md) | The complete address syntax: dot notation, arrays, boxed properties, object sets, back-navigation and function calls. |
|
|
62
|
+
| [Hash Translation](docs/hash-translation.md) | Reusing schemas with different hash mappings for multiple API shapes. |
|
|
63
|
+
| [Schema Manipulation](docs/schema-manipulation.md) | Remapping addresses, merging schemas and auto-generating schemas from sample data. |
|
|
64
|
+
|
|
20
65
|
## Where Does this Run
|
|
21
66
|
|
|
22
|
-
Either
|
|
67
|
+
Either as a stand-alone library in node.js software, or, a dependency-free browser library.
|
|
23
68
|
|
|
24
|
-
###
|
|
69
|
+
### Compatibility
|
|
25
70
|
|
|
26
71
|
This library is tested for node.js versions 8 through 16, and will likely work in future versions. It is implemented as a simple set of es6 classes.
|
|
27
72
|
|
|
@@ -32,13 +77,13 @@ The browser version has been packaged into a minified as well as an expanded deb
|
|
|
32
77
|
For use within node.js, run the following in the folder with your `package.json` file:
|
|
33
78
|
|
|
34
79
|
```
|
|
35
|
-
npm install --save
|
|
80
|
+
npm install --save manyfest
|
|
36
81
|
```
|
|
37
82
|
|
|
38
83
|
Then, you can include it in your source code as follows:
|
|
39
84
|
|
|
40
85
|
```javascript
|
|
41
|
-
const libManyfest = require('
|
|
86
|
+
const libManyfest = require('manyfest');
|
|
42
87
|
|
|
43
88
|
// Construct a Manyfest with a few defined columns
|
|
44
89
|
let animalManyfest = new libManyfest(
|
|
@@ -71,16 +116,16 @@ Below is a lexicon of terms used throughout this documentation. If you see anyt
|
|
|
71
116
|
|
|
72
117
|
| Term | Description |
|
|
73
118
|
| ---- | ----------- |
|
|
74
|
-
Scope | The scope of this representation; generally the clustered or parent record name (e.g. Animal, User, Transaction, etc.) -- does not have functional purpose; only for information and logging.
|
|
75
|
-
Schema | The stateful representation of an object's structural definition.
|
|
76
|
-
Element | A defined element of data in an object.
|
|
77
|
-
Address | The address where that data lies in the object.
|
|
78
|
-
Hash | A unique within this scope string-based key for this element. Used for easy access of data.
|
|
79
|
-
Descriptor | A description of an element including data such as Name, NameShort, Hash, Description, and other important properties.
|
|
80
|
-
Name | The name of the element. Meant to be the most succinct human readable name possible.
|
|
81
|
-
NameShort | A shorter name for the element. Meant to be useful enough to identify the property in log lines, tabular views, graphs and anywhere where we don't always want to see the full name.
|
|
82
|
-
Description | A description for the element. Very useful when consuming other APIs with their own terse naming standards (or no naming standards)!
|
|
83
|
-
Required | Set to true if this element is required.
|
|
119
|
+
| Scope | The scope of this representation; generally the clustered or parent record name (e.g. Animal, User, Transaction, etc.) -- does not have functional purpose; only for information and logging. |
|
|
120
|
+
| Schema | The stateful representation of an object's structural definition. |
|
|
121
|
+
| Element | A defined element of data in an object. |
|
|
122
|
+
| Address | The address where that data lies in the object. |
|
|
123
|
+
| Hash | A unique within this scope string-based key for this element. Used for easy access of data. |
|
|
124
|
+
| Descriptor | A description of an element including data such as Name, NameShort, Hash, Description, and other important properties. |
|
|
125
|
+
| Name | The name of the element. Meant to be the most succinct human readable name possible. |
|
|
126
|
+
| NameShort | A shorter name for the element. Meant to be useful enough to identify the property in log lines, tabular views, graphs and anywhere where we don't always want to see the full name. |
|
|
127
|
+
| Description | A description for the element. Very useful when consuming other APIs with their own terse naming standards (or no naming standards)! |
|
|
128
|
+
| Required | Set to true if this element is required. |
|
|
84
129
|
|
|
85
130
|
Okay so these are a lot of crazy words. The important two are *Address* and *Hash*. Every element in a schema must have an address. Having a hash just multiplies the usefulness of these addresses.
|
|
86
131
|
|
|
@@ -100,7 +145,7 @@ let animalManyfest = new libManyfest(
|
|
|
100
145
|
"IDAnimal": { "Name":"Database ID", "Description":"The unique integer-based database identifier for an Animal record.", "DataType":"Integer" },
|
|
101
146
|
"Name": { "Description":"The animal's colloquial species name (e.g. Rabbit, Dog, Bear, Mongoose)." },
|
|
102
147
|
"Type": { "Description":"Whether or not the animal is wild, domesticated, agricultural, in a research lab or a part of a zoo.." },
|
|
103
|
-
"MedicalStats":
|
|
148
|
+
"MedicalStats":
|
|
104
149
|
{
|
|
105
150
|
"Name":"Medical Statistics", "Description":"Basic medical statistics for this animal"
|
|
106
151
|
},
|
|
@@ -126,19 +171,19 @@ To aid in this discovery, reference and such, we've given it a NameShort (for us
|
|
|
126
171
|
|
|
127
172
|
| Type | Description |
|
|
128
173
|
| ---- | ----------- |
|
|
129
|
-
String | A pretty basic string
|
|
130
|
-
Integer | An integer number
|
|
131
|
-
Float | A floating point number; does not require a decimal point
|
|
132
|
-
Number | A number of any type
|
|
133
|
-
PreciseNumber | An arbitrary precision number, stored in a string
|
|
134
|
-
Boolean | A boolean value represented by the JSON true or false
|
|
135
|
-
Binary | A boolean value represented as 1 or 0
|
|
136
|
-
YesNo | A boolean value represented as Y or N
|
|
137
|
-
DateTime | A javascript date
|
|
138
|
-
Key | A two-part Key with an Identifier and Globally Unique Identifier (ID and GUID)
|
|
139
|
-
Array | A plain old javascript array
|
|
140
|
-
Object | A plain old javascript object
|
|
141
|
-
Null | A null value
|
|
174
|
+
| String | A pretty basic string |
|
|
175
|
+
| Integer | An integer number |
|
|
176
|
+
| Float | A floating point number; does not require a decimal point |
|
|
177
|
+
| Number | A number of any type |
|
|
178
|
+
| PreciseNumber | An arbitrary precision number, stored in a string |
|
|
179
|
+
| Boolean | A boolean value represented by the JSON true or false |
|
|
180
|
+
| Binary | A boolean value represented as 1 or 0 |
|
|
181
|
+
| YesNo | A boolean value represented as Y or N |
|
|
182
|
+
| DateTime | A javascript date |
|
|
183
|
+
| Key | A two-part Key with an Identifier and Globally Unique Identifier (ID and GUID) |
|
|
184
|
+
| Array | A plain old javascript array |
|
|
185
|
+
| Object | A plain old javascript object |
|
|
186
|
+
| Null | A null value |
|
|
142
187
|
|
|
143
188
|
#### Keys
|
|
144
189
|
|
|
@@ -215,14 +260,14 @@ For any elements that haven't defined a Hash, the Address is used. This allows
|
|
|
215
260
|
|
|
216
261
|
## Hash Translation Tables
|
|
217
262
|
|
|
218
|
-
Sometimes we want to reuse the structure of a schema, but look up values by hash using translations.
|
|
263
|
+
Sometimes we want to reuse the structure of a schema, but look up values by hash using translations. See the [Hash Translation](docs/hash-translation.md) documentation for the full guide.
|
|
219
264
|
|
|
220
265
|
## Programmatically Defining a Schema
|
|
221
266
|
|
|
222
267
|
Sometimes we don't have schemas, and want to define object element structure on the fly. This can be done programmatically. As a refresher, here is how we loaded our simplest schema manifest above:
|
|
223
268
|
|
|
224
269
|
```javascript
|
|
225
|
-
const libManyfest = require('
|
|
270
|
+
const libManyfest = require('manyfest');
|
|
226
271
|
|
|
227
272
|
// Construct a Manyfest with a few defined columns
|
|
228
273
|
let animalManyfest = new libManyfest(
|
|
@@ -240,13 +285,13 @@ let animalManyfest = new libManyfest(
|
|
|
240
285
|
The programmatic equivalent is the following code:
|
|
241
286
|
|
|
242
287
|
```javascript
|
|
243
|
-
const libManyfest = require('
|
|
288
|
+
const libManyfest = require('manyfest');
|
|
244
289
|
|
|
245
290
|
// Construct a Manyfest with a few defined columns
|
|
246
291
|
let animalManyfest = new libManyfest();
|
|
247
292
|
|
|
248
293
|
// Set the Scope
|
|
249
|
-
animalManyfest.
|
|
294
|
+
animalManyfest.scope = "Animal";
|
|
250
295
|
|
|
251
296
|
// Add descriptors
|
|
252
297
|
animalManyfest.addDescriptor("IDAnimal", { "Name":"Database ID", "Description":"The unique integer-based database identifier for an Animal record.", "DataType":"Integer" });
|
|
@@ -268,10 +313,13 @@ With manyfest, you can easily create a description for each API and code against
|
|
|
268
313
|
|
|
269
314
|
Modern APIs are super complicated. And worse, they change. Let's take a real example. [Archive.org](https://archive.org/) is a wonderful resource for downloading awesome content gathered from the web and such.
|
|
270
315
|
|
|
271
|
-
There is an API to access their data. It's ... really messy, the data you get back.
|
|
316
|
+
There is an API to access their data. It's ... really messy, the data you get back. Below is the JSON for a single video (a rad Count Chocula commercial from 1971) -- it takes quite a bit of scroll to get down to the bottom.
|
|
272
317
|
|
|
273
318
|

|
|
274
319
|
|
|
320
|
+
<details>
|
|
321
|
+
<summary>Archive.org JSON response (click to expand)</summary>
|
|
322
|
+
|
|
275
323
|
```JSON
|
|
276
324
|
{
|
|
277
325
|
"created": 1664830085,
|
|
@@ -290,131 +338,6 @@ There is an API to access their data. It's ... really messy, the data you get b
|
|
|
290
338
|
"crc32": "165c668b",
|
|
291
339
|
"sha1": "383303d9546c381267569ad4e33aff691f0bb8c7"
|
|
292
340
|
},
|
|
293
|
-
{
|
|
294
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971.thumbs/frankerberry_countchockula_1971.0001_000004.jpg",
|
|
295
|
-
"source": "derivative",
|
|
296
|
-
"format": "Thumbnail",
|
|
297
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
298
|
-
"mtime": "1296336957",
|
|
299
|
-
"size": "6843",
|
|
300
|
-
"md5": "c93fa52000ab4665e69b25c403e11aff",
|
|
301
|
-
"crc32": "9444e6f6",
|
|
302
|
-
"sha1": "716b4f9950b8147f51d3265f9c62ff86451308d5"
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971.thumbs/frankerberry_countchockula_1971.0001_000009.jpg",
|
|
306
|
-
"source": "derivative",
|
|
307
|
-
"format": "Thumbnail",
|
|
308
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
309
|
-
"mtime": "1296336957",
|
|
310
|
-
"size": "8388",
|
|
311
|
-
"md5": "30eb3eb4cbbdfa08d531a0a74da7c000",
|
|
312
|
-
"crc32": "be874a9e",
|
|
313
|
-
"sha1": "0c392d777609e967b6022be27edad678c5ae74e2"
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971.thumbs/frankerberry_countchockula_1971.0001_000014.jpg",
|
|
317
|
-
"source": "derivative",
|
|
318
|
-
"format": "Thumbnail",
|
|
319
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
320
|
-
"mtime": "1296336958",
|
|
321
|
-
"size": "5993",
|
|
322
|
-
"md5": "4e9ebc3d076bec8cf7dfe76795f8c769",
|
|
323
|
-
"crc32": "912ec98c",
|
|
324
|
-
"sha1": "01dc49c852e1bbb421199450dd902935c62b06de"
|
|
325
|
-
},
|
|
326
|
-
{
|
|
327
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971.thumbs/frankerberry_countchockula_1971.0001_000019.jpg",
|
|
328
|
-
"source": "derivative",
|
|
329
|
-
"format": "Thumbnail",
|
|
330
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
331
|
-
"mtime": "1296336958",
|
|
332
|
-
"size": "4951",
|
|
333
|
-
"md5": "59f190f0c5b0a048415b26412860b6dd",
|
|
334
|
-
"crc32": "a70a30b1",
|
|
335
|
-
"sha1": "a284af9757cb24d28f96ec88ec1b1c23a8cea9fe"
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971.thumbs/frankerberry_countchockula_1971.0001_000024.jpg",
|
|
339
|
-
"source": "derivative",
|
|
340
|
-
"format": "Thumbnail",
|
|
341
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
342
|
-
"mtime": "1296336959",
|
|
343
|
-
"size": "3383",
|
|
344
|
-
"md5": "be2a908acd563b896e7758b598295148",
|
|
345
|
-
"crc32": "ed467831",
|
|
346
|
-
"sha1": "94c001e72ebc86d837a78c61a004db9ab9d597bd"
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971.thumbs/frankerberry_countchockula_1971.0001_000029.jpg",
|
|
350
|
-
"source": "derivative",
|
|
351
|
-
"format": "Thumbnail",
|
|
352
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
353
|
-
"mtime": "1296336960",
|
|
354
|
-
"size": "3503",
|
|
355
|
-
"md5": "c82199d09be07633000fd07b363dd8a3",
|
|
356
|
-
"crc32": "a1fd79cb",
|
|
357
|
-
"sha1": "2bc8e761edb24a441fa5906dda1c424e1f98a47a"
|
|
358
|
-
},
|
|
359
|
-
{
|
|
360
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971_archive.torrent",
|
|
361
|
-
"source": "metadata",
|
|
362
|
-
"btih": "de6b371e7cc3c83db1cc08150500753eae533409",
|
|
363
|
-
"mtime": "1542761794",
|
|
364
|
-
"size": "4093",
|
|
365
|
-
"md5": "a275d3b4028cccb5bea8b47a88c838af",
|
|
366
|
-
"crc32": "5ffa7334",
|
|
367
|
-
"sha1": "af8222637b574cba1360d0ea77e231640ffd43c4",
|
|
368
|
-
"format": "Archive BitTorrent"
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971_files.xml",
|
|
372
|
-
"source": "metadata",
|
|
373
|
-
"format": "Metadata",
|
|
374
|
-
"md5": "3a7e87b08bed1e203a5858b31352c110"
|
|
375
|
-
},
|
|
376
|
-
{
|
|
377
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971_meta.xml",
|
|
378
|
-
"source": "metadata",
|
|
379
|
-
"format": "Metadata",
|
|
380
|
-
"mtime": "1542761793",
|
|
381
|
-
"size": "1371",
|
|
382
|
-
"md5": "0b9c9bf21b9a26aea43a2f735b404624",
|
|
383
|
-
"crc32": "41077288",
|
|
384
|
-
"sha1": "22e6f2c73bf63072f671d846355da2785db51dbd"
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
"name": "FrankenberryCountChoculaTevevisionCommercial1971_reviews.xml",
|
|
388
|
-
"source": "original",
|
|
389
|
-
"mtime": "1466898697",
|
|
390
|
-
"size": "620",
|
|
391
|
-
"md5": "260bfba5d696772445dcc7ff6e6d5bdb",
|
|
392
|
-
"crc32": "25ea3229",
|
|
393
|
-
"sha1": "7d541f18fcd5ad9c6e593afe5a80f18771f23b32",
|
|
394
|
-
"format": "Metadata"
|
|
395
|
-
},
|
|
396
|
-
{
|
|
397
|
-
"name": "__ia_thumb.jpg",
|
|
398
|
-
"source": "original",
|
|
399
|
-
"mtime": "1539115881",
|
|
400
|
-
"size": "7481",
|
|
401
|
-
"md5": "8cec324fa0016fd77cc04e6a4b2ebb00",
|
|
402
|
-
"crc32": "d9e1b316",
|
|
403
|
-
"sha1": "4dab42952fe0405a3b7f80146636b33d7b1bd01e",
|
|
404
|
-
"format": "Item Tile",
|
|
405
|
-
"rotation": "0"
|
|
406
|
-
},
|
|
407
|
-
{
|
|
408
|
-
"name": "frankerberry_countchockula_1971.0001.gif",
|
|
409
|
-
"source": "derivative",
|
|
410
|
-
"format": "Animated GIF",
|
|
411
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
412
|
-
"mtime": "1296336965",
|
|
413
|
-
"size": "101114",
|
|
414
|
-
"md5": "b78a13094030f104900eb996bafe2b7d",
|
|
415
|
-
"crc32": "6650cd8",
|
|
416
|
-
"sha1": "669798c037205cac14f70592deef6f7831b3d4a1"
|
|
417
|
-
},
|
|
418
341
|
{
|
|
419
342
|
"name": "frankerberry_countchockula_1971.0001.mpg",
|
|
420
343
|
"source": "original",
|
|
@@ -427,45 +350,6 @@ There is an API to access their data. It's ... really messy, the data you get b
|
|
|
427
350
|
"length": "31.14",
|
|
428
351
|
"height": "480",
|
|
429
352
|
"width": "640"
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
"name": "frankerberry_countchockula_1971.0001.mpg.idx",
|
|
433
|
-
"source": "derivative",
|
|
434
|
-
"format": "Video Index",
|
|
435
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
436
|
-
"mtime": "1296336956",
|
|
437
|
-
"size": "31141",
|
|
438
|
-
"md5": "49423e072726e4ea3cdd8ebdd26c7dfc",
|
|
439
|
-
"crc32": "ae969a68",
|
|
440
|
-
"sha1": "805782cd2d0f9002555816daadf3b8607e621f79"
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
"name": "frankerberry_countchockula_1971.0001.ogv",
|
|
444
|
-
"source": "derivative",
|
|
445
|
-
"format": "Ogg Video",
|
|
446
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
447
|
-
"mtime": "1296336994",
|
|
448
|
-
"size": "2248166",
|
|
449
|
-
"md5": "f1b933e97ce63594fb28a0a019ff3436",
|
|
450
|
-
"crc32": "a2a0e5e9",
|
|
451
|
-
"sha1": "a6bf0aec9f006baeca37c03f586686ebe685d59b",
|
|
452
|
-
"length": "31.15",
|
|
453
|
-
"height": "300",
|
|
454
|
-
"width": "400"
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
"name": "frankerberry_countchockula_1971.0001_512kb.mp4",
|
|
458
|
-
"source": "derivative",
|
|
459
|
-
"format": "512Kb MPEG4",
|
|
460
|
-
"original": "frankerberry_countchockula_1971.0001.mpg",
|
|
461
|
-
"mtime": "1296336977",
|
|
462
|
-
"size": "2378677",
|
|
463
|
-
"md5": "a7750839519c61ba3bb99fc66b32011d",
|
|
464
|
-
"crc32": "4dbd37c8",
|
|
465
|
-
"sha1": "3929314c192dec006fac2739bcb4730788e8c068",
|
|
466
|
-
"length": "31.13",
|
|
467
|
-
"height": "240",
|
|
468
|
-
"width": "320"
|
|
469
353
|
}
|
|
470
354
|
],
|
|
471
355
|
"files_count": 17,
|
|
@@ -476,31 +360,8 @@ There is an API to access their data. It's ... really messy, the data you get b
|
|
|
476
360
|
"title": "Franken Berry / Count Chocula : Tevevision Commercial 1971",
|
|
477
361
|
"creator": "General Mills",
|
|
478
362
|
"mediatype": "movies",
|
|
479
|
-
"collection": [
|
|
480
|
-
|
|
481
|
-
"television"
|
|
482
|
-
],
|
|
483
|
-
"description": "Count Chocula and Franken Berry were both introduced in 1971. Boo Berry Cereal appeared in 1973 followed by Fruit Brute in 1974. Yummy Mummy appeared more than a decade later in 1988 - completing the the group known as the General Mills Monster Cereals.",
|
|
484
|
-
"subject": "Third Eye Cinema; Classic Television Commercials; animation; cartoons;General Mills",
|
|
485
|
-
"licenseurl": "http://creativecommons.org/publicdomain/mark/1.0/",
|
|
486
|
-
"publicdate": "2011-01-29 21:36:42",
|
|
487
|
-
"addeddate": "2011-01-29 21:35:38",
|
|
488
|
-
"uploader": "bolexman@msn.com",
|
|
489
|
-
"updater": [
|
|
490
|
-
"Bolexman",
|
|
491
|
-
"Bolexman",
|
|
492
|
-
"Jeff Kaplan"
|
|
493
|
-
],
|
|
494
|
-
"updatedate": [
|
|
495
|
-
"2011-01-29 21:45:38",
|
|
496
|
-
"2011-01-29 21:55:46",
|
|
497
|
-
"2011-01-29 23:04:55"
|
|
498
|
-
],
|
|
499
|
-
"sound": "sound",
|
|
500
|
-
"color": "color",
|
|
501
|
-
"runtime": "0:31",
|
|
502
|
-
"backup_location": "ia903608_22",
|
|
503
|
-
"ia_orig__runtime": "31 seconds"
|
|
363
|
+
"collection": ["classic_tv_commercials", "television"],
|
|
364
|
+
"description": "Count Chocula and Franken Berry were both introduced in 1971. Boo Berry Cereal appeared in 1973 followed by Fruit Brute in 1974. Yummy Mummy appeared more than a decade later in 1988 - completing the the group known as the General Mills Monster Cereals."
|
|
504
365
|
},
|
|
505
366
|
"reviews": [
|
|
506
367
|
{
|
|
@@ -508,19 +369,15 @@ There is an API to access their data. It's ... really messy, the data you get b
|
|
|
508
369
|
"reviewtitle": "pre booberry",
|
|
509
370
|
"reviewer": "outofthebox",
|
|
510
371
|
"reviewdate": "2016-06-25 23:51:36",
|
|
511
|
-
"createdate": "2016-06-25 23:51:36",
|
|
512
372
|
"stars": "4"
|
|
513
373
|
}
|
|
514
374
|
],
|
|
515
|
-
"server": "ia800202.us.archive.org"
|
|
516
|
-
"uniq": 1957612749,
|
|
517
|
-
"workable_servers": [
|
|
518
|
-
"ia800202.us.archive.org",
|
|
519
|
-
"ia600202.us.archive.org"
|
|
520
|
-
]
|
|
375
|
+
"server": "ia800202.us.archive.org"
|
|
521
376
|
}
|
|
522
377
|
```
|
|
523
378
|
|
|
379
|
+
</details>
|
|
380
|
+
|
|
524
381
|
### A Manyfest Schema for Count Chocula to Thrive In
|
|
525
382
|
|
|
526
383
|
With just a small number of element descriptors, we can make this huge blob of JSON very usable for a developer.
|
|
@@ -577,7 +434,7 @@ With just a small number of element descriptors, we can make this huge blob of J
|
|
|
577
434
|
|
|
578
435
|
These two JSON files are in the [examples/chocula](https://github.com/stevenvelozo/manyfest/tree/main/examples/chocula) folder along with a javascript file `Chocula.js` you can try out yourself and hack on.
|
|
579
436
|
|
|
580
|
-
```
|
|
437
|
+
```javascript
|
|
581
438
|
let libManyfest = require('../../source/Manyfest.js');
|
|
582
439
|
|
|
583
440
|
let dataArchiveOrg = require(`./Data-Archive-org-Frankenberry.json`);
|
|
@@ -588,7 +445,7 @@ let _Schema = new libManyfest(schemaArchiveOrg);
|
|
|
588
445
|
console.log(`The URL for "${_Schema.getValueByHash(dataArchiveOrg,'Title')}" is: ${_Schema.getValueByHash(dataArchiveOrg,'Server')}${_Schema.getValueByHash(dataArchiveOrg,'Path')}`);
|
|
589
446
|
```
|
|
590
447
|
|
|
591
|
-
### A Manyfest Schema for Book
|
|
448
|
+
### A Manyfest Schema for Book Records
|
|
592
449
|
|
|
593
450
|
This shows a book record with an Author Key as well.
|
|
594
451
|
|
|
@@ -624,12 +481,12 @@ This shows a book record with an Author Key as well.
|
|
|
624
481
|
"KeyRepresentation": "ID",
|
|
625
482
|
"GUIDAddress": "GUIDAuthor",
|
|
626
483
|
"IDAddress": "IDAuthor"
|
|
627
|
-
}
|
|
484
|
+
}
|
|
628
485
|
}
|
|
629
486
|
}
|
|
630
487
|
```
|
|
631
488
|
|
|
632
|
-
Because we expect only one scope to be the controlling scope for a particular key pair, we can use the key pair presence as a
|
|
489
|
+
Because we expect only one scope to be the controlling scope for a particular key pair, we can use the key pair presence as a mechanism for resolution of IDs to GUIDs when they aren't natural.
|
|
633
490
|
|
|
634
491
|
What does this mean in practice?
|
|
635
492
|
|
|
@@ -653,36 +510,10 @@ And the following Author record:
|
|
|
653
510
|
}
|
|
654
511
|
```
|
|
655
512
|
|
|
656
|
-
|
|
657
513
|
#### This gives rise to the need for a "Key" data type which is a tuple
|
|
658
514
|
|
|
659
515
|
The tuple will be a GUID or ID, but will represent both. As long as only one entity in the controlling scope (the Book table for instance) to have *both* the ID and the GUID, this can be used to make that the source of record for the Key. This allows lookups back and forth between GUID and ID.
|
|
660
516
|
|
|
661
|
-
1. Author is the
|
|
517
|
+
1. Author is the canonical source for finding the GUIDAuthor->IDAuthor connection, and vice versa. Because it is the only record shape in the model space that contains both.
|
|
662
518
|
2. GUIDAuthor is not in the Descriptors as a secondary address, but IDAuthor is. GUIDAuthor is, though, resolvable from the IDAuthor GUIDAddress property.
|
|
663
519
|
3. This means we want to treat the "GUIDAuthor"/"IDAuthor" pairing as a single entry in a manyfest, which is a departure from how the rest of them operate.
|
|
664
|
-
|
|
665
|
-
## BELOW WILL BE IN Pict
|
|
666
|
-
|
|
667
|
-
## Record "Sieve"s
|
|
668
|
-
|
|
669
|
-
Sometimes we want to take a record from a source shape, and translate it into a destination shape. This can be done with an implicit sieve or an explicit sieve.
|
|
670
|
-
|
|
671
|
-
### Handy Assertions for Working with a Sieve
|
|
672
|
-
|
|
673
|
-
* A record is a single object without loops.
|
|
674
|
-
* A record is part of a RecordSet.
|
|
675
|
-
* A record has an RecordSet key (e.g. "Users", "Projects", "Books", etc.).
|
|
676
|
-
* A record may have an Entity key (e.g. "Users", "Projects", "Books", etc.).
|
|
677
|
-
* The difference between RecordSet and Entity is that an Entity is a Record associated with a Key is expected to be stored in an Entity Storage System of some kind whereas a RecordSet Record might be abstract.
|
|
678
|
-
* A Record has a Key which represents a GUID and Identifier.
|
|
679
|
-
* GUIDs are RecordSet scoped strings that are unique to a Record.
|
|
680
|
-
* Identifiers are RecordSet Storage scoped strings or numbers that are unique to a Record.
|
|
681
|
-
* There is only one cannonical provider of each Record Entity
|
|
682
|
-
* When creating a sieve instance, we are going to migrate sets of records from one schema to another.
|
|
683
|
-
|
|
684
|
-
### Implicit Sieve
|
|
685
|
-
|
|
686
|
-
An implicit sieve embeds the configuration for Projection(s) within the current recordset. There is a Projection array, each of which has an entry for basic record projections. This should be good enough for simple transformation operations.
|
|
687
|
-
|
|
688
|
-
Each of these Projections come as a simple record shape with templated expressions.
|
package/docs/.nojekyll
ADDED
|
File without changes
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Manyfest
|
|
2
|
+
|
|
3
|
+
> JSON Object Manifest for Data Description and Parsing
|
|
4
|
+
|
|
5
|
+
Manyfest provides a simple pattern to describe, validate, manipulate and interpret data using address-based object navigation. It solves the problem of maintaining context as data crosses boundaries between persistence engines, APIs, and user interfaces.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Safe Object Navigation** - Read and write deeply nested properties without exceptions
|
|
10
|
+
- **Schema Definitions** - Describe data elements with types, defaults, names and descriptions
|
|
11
|
+
- **Hash-Based Lookups** - Access complex paths through short, friendly hash identifiers
|
|
12
|
+
- **Validation** - Check objects against schemas for required fields and type conformance
|
|
13
|
+
- **Default Population** - Fill in missing values based on schema definitions
|
|
14
|
+
- **Auto-Structure Creation** - Writing to a nested path automatically creates intermediate objects
|
|
15
|
+
- **Browser & Node.js** - Works seamlessly in both environments
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
const libManyfest = require('manyfest');
|
|
21
|
+
|
|
22
|
+
// Define a schema
|
|
23
|
+
const manifest = new libManyfest({
|
|
24
|
+
Scope: 'User',
|
|
25
|
+
Descriptors: {
|
|
26
|
+
'Name': { DataType: 'String', Required: true },
|
|
27
|
+
'Email': { DataType: 'String', Required: true },
|
|
28
|
+
'Profile.Age': { DataType: 'Integer' },
|
|
29
|
+
'Profile.Bio': { DataType: 'String', Default: '' }
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Read safely from nested objects
|
|
34
|
+
const user = { Name: 'Alice', Profile: { Age: 30 } };
|
|
35
|
+
manifest.getValueAtAddress(user, 'Profile.Age'); // 30
|
|
36
|
+
manifest.getValueAtAddress(user, 'Profile.Phone'); // undefined (no error)
|
|
37
|
+
|
|
38
|
+
// Write to any depth -- intermediate objects are created for you
|
|
39
|
+
const newUser = {};
|
|
40
|
+
manifest.setValueAtAddress(newUser, 'Name', 'Bob');
|
|
41
|
+
manifest.setValueAtAddress(newUser, 'Profile.Age', 25);
|
|
42
|
+
// newUser is now: { Name: 'Bob', Profile: { Age: 25 } }
|
|
43
|
+
|
|
44
|
+
// Validate against the schema
|
|
45
|
+
const result = manifest.validate({ Name: 'Carol' });
|
|
46
|
+
// result.Error: true
|
|
47
|
+
// result.Errors: ['Element at address "Email" is flagged REQUIRED but is not set in the object.']
|
|
48
|
+
// result.MissingElements: ['Email', 'Profile.Age', 'Profile.Bio']
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install manyfest
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## How It Works
|
|
58
|
+
|
|
59
|
+
Manyfest uses **addresses** -- dot-notation paths -- to navigate objects:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
manifest.getValueAtAddress(data, 'user.profile.name'); // Nested properties
|
|
63
|
+
manifest.getValueAtAddress(data, 'items[0].title'); // Array elements
|
|
64
|
+
manifest.getValueAtAddress(data, '["special-key"]'); // Boxed properties
|
|
65
|
+
manifest.getValueAtAddress(data, 'departments{}.budget'); // Object sets
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A **schema** maps these addresses to human-readable metadata:
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
const manifest = new libManyfest({
|
|
72
|
+
Scope: 'Archive.org',
|
|
73
|
+
Descriptors: {
|
|
74
|
+
'd1': { Hash: 'Server', Name: 'Server', DataType: 'String' },
|
|
75
|
+
'metadata.title': { Hash: 'Title', Name: 'Title', DataType: 'String' },
|
|
76
|
+
'metadata.creator': { Hash: 'Creator', Name: 'Creator', DataType: 'String' }
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Now use friendly hashes instead of raw paths
|
|
81
|
+
manifest.getValueByHash(archiveData, 'Title'); // reads metadata.title
|
|
82
|
+
manifest.getValueByHash(archiveData, 'Creator'); // reads metadata.creator
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
No schema required for basic usage -- manyfest works as a standalone safe-access utility:
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
const manifest = new libManyfest();
|
|
89
|
+
manifest.getValueAtAddress(anyObject, 'any.nested.path'); // Safe, returns undefined if missing
|
|
90
|
+
manifest.setValueAtAddress(anyObject, 'any.new.path', 42); // Creates structure automatically
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
- [Quickstart](quickstart.md) - Up and running in five minutes
|
|
96
|
+
- [Reading Values](reading.md) - Full guide to address-based reading
|
|
97
|
+
- [Writing Values](writing.md) - Setting, populating and deleting values
|
|
98
|
+
- [Validating Objects](validating.md) - Schema validation and type checking
|
|
99
|
+
- [Schema Definition](schema.md) - Descriptors, data types and serialization
|
|
100
|
+
- [Address Notation](address-notation.md) - The complete address syntax reference
|
|
101
|
+
- [Hash Translation](hash-translation.md) - Reusing schemas with different mappings
|
|
102
|
+
- [Schema Manipulation](schema-manipulation.md) - Remapping, merging and generating schemas
|
|
103
|
+
|
|
104
|
+
## Related Packages
|
|
105
|
+
|
|
106
|
+
- [fable](https://github.com/stevenvelozo/fable) - Service dependency injection framework
|
|
107
|
+
- [pict](https://github.com/stevenvelozo/pict) - UI application framework
|
|
108
|
+
- [pict-view](https://github.com/stevenvelozo/pict-view) - View base class for Pict
|
package/docs/_sidebar.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
- Getting Started
|
|
2
|
+
|
|
3
|
+
- [Introduction](/)
|
|
4
|
+
- [Quickstart](quickstart.md)
|
|
5
|
+
|
|
6
|
+
- Working with Objects
|
|
7
|
+
|
|
8
|
+
- [Reading Values](reading.md)
|
|
9
|
+
- [Writing Values](writing.md)
|
|
10
|
+
- [Validating Objects](validating.md)
|
|
11
|
+
|
|
12
|
+
- Reference
|
|
13
|
+
|
|
14
|
+
- [Schema Definition](schema.md)
|
|
15
|
+
- [Address Notation](address-notation.md)
|
|
16
|
+
- [Hash Translation](hash-translation.md)
|
|
17
|
+
- [Schema Manipulation](schema-manipulation.md)
|