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.
@@ -0,0 +1,244 @@
1
+ # Address Notation
2
+
3
+ Addresses are the core of manyfest. They describe locations inside nested JavaScript objects using a compact string syntax. Every read, write, existence check and validation operation resolves through the address system.
4
+
5
+ ## Basic Dot Notation
6
+
7
+ Dots separate levels of nesting:
8
+
9
+ ```javascript
10
+ const manifest = new libManyfest();
11
+
12
+ const data = {
13
+ user: {
14
+ profile: {
15
+ name: 'Alice'
16
+ }
17
+ }
18
+ };
19
+
20
+ manifest.getValueAtAddress(data, 'user'); // { profile: { name: 'Alice' } }
21
+ manifest.getValueAtAddress(data, 'user.profile'); // { name: 'Alice' }
22
+ manifest.getValueAtAddress(data, 'user.profile.name'); // 'Alice'
23
+ ```
24
+
25
+ Paths are case-sensitive. `user.Profile` and `user.profile` resolve to different locations.
26
+
27
+ ## Array Access
28
+
29
+ Square brackets with a numeric index access array elements:
30
+
31
+ ```javascript
32
+ const data = {
33
+ items: ['first', 'second', 'third'],
34
+ users: [
35
+ { name: 'Alice', scores: [95, 88] },
36
+ { name: 'Bob', scores: [72, 91] }
37
+ ]
38
+ };
39
+
40
+ manifest.getValueAtAddress(data, 'items[0]'); // 'first'
41
+ manifest.getValueAtAddress(data, 'items[2]'); // 'third'
42
+ manifest.getValueAtAddress(data, 'users[0].name'); // 'Alice'
43
+ manifest.getValueAtAddress(data, 'users[1].scores[0]'); // 72
44
+ ```
45
+
46
+ Indices are zero-based. Accessing an out-of-bounds index returns `undefined`.
47
+
48
+ ### Set Access
49
+
50
+ Empty brackets access the full array:
51
+
52
+ ```javascript
53
+ manifest.getValueAtAddress(data, 'users[]');
54
+ // Returns all user objects
55
+ ```
56
+
57
+ When combined with further path resolution, this produces an object keyed by each element's full address:
58
+
59
+ ```javascript
60
+ manifest.getValueAtAddress(data, 'users[].name');
61
+ // { 'users[0].name': 'Alice', 'users[1].name': 'Bob' }
62
+ ```
63
+
64
+ ## Boxed Properties
65
+
66
+ Properties with special characters in their keys (dots, spaces, dashes) cannot use dot notation. Wrap them in brackets with quotes instead:
67
+
68
+ ```javascript
69
+ const data = {
70
+ 'my-key': 'value1',
71
+ 'another key': 'value2',
72
+ nested: {
73
+ 'some.dotted.key': 'value3'
74
+ }
75
+ };
76
+
77
+ manifest.getValueAtAddress(data, '["my-key"]'); // 'value1'
78
+ manifest.getValueAtAddress(data, "['another key']"); // 'value2'
79
+ manifest.getValueAtAddress(data, 'nested["some.dotted.key"]'); // 'value3'
80
+ ```
81
+
82
+ Single quotes, double quotes and backticks all work as the wrapping character.
83
+
84
+ ## Object Set Access
85
+
86
+ The `{}` marker enumerates all keys of an object, resolving a sub-path on each:
87
+
88
+ ```javascript
89
+ const data = {
90
+ departments: {
91
+ engineering: { budget: 50000, headcount: 12 },
92
+ marketing: { budget: 30000, headcount: 8 },
93
+ sales: { budget: 40000, headcount: 15 }
94
+ }
95
+ };
96
+
97
+ manifest.getValueAtAddress(data, 'departments{}.budget');
98
+ // {
99
+ // 'departments.engineering.budget': 50000,
100
+ // 'departments.marketing.budget': 30000,
101
+ // 'departments.sales.budget': 40000
102
+ // }
103
+ ```
104
+
105
+ This is useful for extracting a single field across all entries of an object-based collection.
106
+
107
+ ## Back-Navigation
108
+
109
+ Double dots (`..`) navigate upward through the object hierarchy. Each dot beyond the first separator moves one level up from the current position:
110
+
111
+ ```javascript
112
+ const data = {
113
+ Bundle: {
114
+ Contract: {
115
+ IDContract: 500
116
+ },
117
+ Project: {
118
+ IDProject: 42
119
+ }
120
+ }
121
+ };
122
+
123
+ // From IDContract, navigate back up to Bundle, then down into Project
124
+ manifest.getValueAtAddress(data, 'Bundle.Contract.IDContract...Project.IDProject');
125
+ // 42
126
+ ```
127
+
128
+ The address resolves as:
129
+ 1. Navigate to `Bundle.Contract.IDContract`
130
+ 2. `..` means go up -- one dot goes back to `Contract`, the second goes back to `Bundle`
131
+ 3. Continue forward into `Project.IDProject`
132
+
133
+ Back-navigation always resolves relative to the root object.
134
+
135
+ ## Function Calls
136
+
137
+ If a property in the path is a function, it can be called using parentheses:
138
+
139
+ ```javascript
140
+ const data = {
141
+ items: [10, 20, 30],
142
+ getTotal: function() {
143
+ return this.items.reduce((a, b) => a + b, 0);
144
+ }
145
+ };
146
+
147
+ manifest.getValueAtAddress(data, 'getTotal()'); // 60
148
+ ```
149
+
150
+ ### With Arguments
151
+
152
+ Arguments inside the parentheses are resolved as addresses on the root object:
153
+
154
+ ```javascript
155
+ const data = {
156
+ basePrice: 100,
157
+ taxRate: 0.08,
158
+ calculate: function(price, rate) {
159
+ return price + (price * rate);
160
+ }
161
+ };
162
+
163
+ manifest.getValueAtAddress(data, 'calculate(basePrice,taxRate)'); // 108
164
+ ```
165
+
166
+ String literals can be passed by wrapping them in quotes:
167
+
168
+ ```javascript
169
+ const data = {
170
+ greet: function(name) { return 'Hello, ' + name; }
171
+ };
172
+
173
+ manifest.getValueAtAddress(data, 'greet("World")'); // 'Hello, World'
174
+ ```
175
+
176
+ Functions are called with `apply`, bound to their containing object so `this` works as expected.
177
+
178
+ ### Chained Function Calls
179
+
180
+ A function's return value can be navigated further:
181
+
182
+ ```javascript
183
+ const data = {
184
+ getUser: function() {
185
+ return { name: 'Alice', role: 'admin' };
186
+ }
187
+ };
188
+
189
+ manifest.getValueAtAddress(data, 'getUser().name'); // 'Alice'
190
+ ```
191
+
192
+ ## Address Linting
193
+
194
+ Manyfest automatically cleans up common address issues before resolution:
195
+
196
+ - Leading and trailing whitespace is trimmed
197
+ - A trailing single `.` is removed (but `..` for back-navigation is preserved)
198
+
199
+ ```javascript
200
+ manifest.getValueAtAddress(data, ' user.name '); // Works
201
+ manifest.getValueAtAddress(data, 'user.name.'); // Trailing dot removed
202
+ ```
203
+
204
+ ## Writing with Addresses
205
+
206
+ All of the notation above works for write operations too. Manyfest creates intermediate objects as needed:
207
+
208
+ ```javascript
209
+ const data = {};
210
+
211
+ manifest.setValueAtAddress(data, 'user.profile.name', 'Alice');
212
+ // { user: { profile: { name: 'Alice' } } }
213
+
214
+ manifest.setValueAtAddress(data, 'items[0]', 'first');
215
+ // Also creates the items array if needed
216
+
217
+ manifest.setValueAtAddress(data, '["special-key"]', 'value');
218
+ // { 'special-key': 'value', user: { ... }, items: [...] }
219
+ ```
220
+
221
+ ## Address Syntax Summary
222
+
223
+ | Syntax | Example | Description |
224
+ |--------|---------|-------------|
225
+ | `property` | `Name` | Direct property access |
226
+ | `a.b.c` | `user.profile.name` | Nested property access |
227
+ | `a[0]` | `items[0]` | Array element by index |
228
+ | `a[].b` | `users[].name` | Property across all array elements |
229
+ | `a["b"]` | `data["my-key"]` | Boxed property (special characters) |
230
+ | `a{}.b` | `depts{}.budget` | Property across all object keys |
231
+ | `a..b` | `x.y..z` | Back-navigation (up one level) |
232
+ | `a()` | `getTotal()` | Function call |
233
+ | `a(b)` | `calc(price)` | Function call with address argument |
234
+ | `a("b")` | `greet("World")` | Function call with string literal |
235
+
236
+ ## Notes
237
+
238
+ - Addresses are case-sensitive
239
+ - Non-existent paths return `undefined` on read (no exceptions)
240
+ - Non-existent intermediate objects are created on write
241
+ - Array indices are zero-based
242
+ - Function calls require the function to exist on the object at that path
243
+ - Back-navigation resolves from the root object
244
+ - The address system is shared by all operations: get, set, check, delete and validate
package/docs/cover.md ADDED
@@ -0,0 +1,11 @@
1
+ # Manyfest <small>1</small>
2
+
3
+ > JSON Object Manifest for Data Description and Parsing
4
+
5
+ - Safe read and write access to deeply nested objects
6
+ - Schema-driven validation with data type checking
7
+ - Hash-based lookups for friendly access to complex paths
8
+ - Works in both Node.js and browser environments
9
+
10
+ [GitHub](https://github.com/stevenvelozo/manyfest)
11
+ [Get Started](#manyfest)
@@ -0,0 +1,202 @@
1
+ # Hash Translation
2
+
3
+ Hash translation tables let you reuse the same schema structure while resolving hashes to different addresses. This is useful when the same logical data model maps to objects with different property names -- for instance, when two APIs return the same information in different shapes.
4
+
5
+ ## Core Concept
6
+
7
+ Normally, a descriptor's `Hash` property maps to its `Address`:
8
+
9
+ ```javascript
10
+ // Schema: Hash "Title" maps to address "metadata.title"
11
+ manifest.getValueByHash(data, 'Title'); // resolves to data.metadata.title
12
+ ```
13
+
14
+ A translation table adds a layer of indirection. It intercepts hash lookups and redirects them before the normal hash-to-address resolution runs:
15
+
16
+ ```
17
+ Hash Lookup → Translation Table → Descriptor Hash Table → Address → Value
18
+ ```
19
+
20
+ ## Access
21
+
22
+ Translation tables live on the `hashTranslations` property of a manifest instance:
23
+
24
+ ```javascript
25
+ const manifest = new libManyfest({
26
+ Scope: 'Media',
27
+ Descriptors: {
28
+ 'metadata.title': { Hash: 'Title', DataType: 'String' },
29
+ 'metadata.creator': { Hash: 'Creator', DataType: 'String' }
30
+ }
31
+ });
32
+
33
+ // Add translations
34
+ manifest.hashTranslations.addTranslation({
35
+ 'Title': 'info.name',
36
+ 'Creator': 'info.author'
37
+ });
38
+ ```
39
+
40
+ After adding translations, `getValueByHash(data, 'Title')` resolves to `data.info.name` instead of `data.metadata.title`.
41
+
42
+ ## Adding Translations
43
+
44
+ ### addTranslation
45
+
46
+ Pass an object where each key is the source hash and each value is the destination hash or address:
47
+
48
+ ```javascript
49
+ manifest.hashTranslations.addTranslation({
50
+ 'SourceHash': 'DestinationHash',
51
+ 'AnotherSource': 'AnotherDestination'
52
+ });
53
+ ```
54
+
55
+ Multiple calls are additive. Later calls can overwrite earlier translations for the same source hash.
56
+
57
+ ## Removing Translations
58
+
59
+ ### removeTranslation
60
+
61
+ Remove by passing a string (single hash) or an object (multiple hashes):
62
+
63
+ ```javascript
64
+ // Remove a single translation
65
+ manifest.hashTranslations.removeTranslation('Title');
66
+
67
+ // Remove multiple at once
68
+ manifest.hashTranslations.removeTranslation({
69
+ 'Title': 'info.name',
70
+ 'Creator': 'info.author'
71
+ });
72
+ ```
73
+
74
+ When an object is passed, only the keys matter. The values are ignored.
75
+
76
+ ### clearTranslations
77
+
78
+ Remove all translations at once:
79
+
80
+ ```javascript
81
+ manifest.hashTranslations.clearTranslations();
82
+ ```
83
+
84
+ ## Checking the Table
85
+
86
+ ### translationCount
87
+
88
+ ```javascript
89
+ manifest.hashTranslations.translationCount(); // Number of active translations
90
+ ```
91
+
92
+ ### translate
93
+
94
+ Translate a single hash. Returns the original hash if no translation exists:
95
+
96
+ ```javascript
97
+ manifest.hashTranslations.translate('Title'); // 'info.name' (if translated)
98
+ manifest.hashTranslations.translate('Unknown'); // 'Unknown' (passthrough)
99
+ ```
100
+
101
+ ## Resolution Order
102
+
103
+ When `getValueByHash` (or any hash-based method) resolves a hash, the following order is used:
104
+
105
+ 1. **Element hash table only** -- if the hash is in the descriptor table and not in the translation table, resolve normally
106
+ 2. **Translation table then element hash table** -- if the hash is in the translation table and the translated result is in the element hash table, resolve through both
107
+ 3. **Translation table only** -- if the hash is in the translation table but the translated result is not in the element hash table, use the translated value as a direct address
108
+ 4. **Passthrough** -- if the hash is in neither table, treat it as a direct address
109
+
110
+ This means translations can override built-in descriptor hashes.
111
+
112
+ ## Use Cases
113
+
114
+ ### Multiple API Formats
115
+
116
+ When two APIs return the same data in different shapes, one schema handles both:
117
+
118
+ ```javascript
119
+ const mediaSchema = new libManyfest({
120
+ Scope: 'Media',
121
+ Descriptors: {
122
+ 'title': { Hash: 'Title', DataType: 'String' },
123
+ 'author': { Hash: 'Author', DataType: 'String' },
124
+ 'year': { Hash: 'Year', DataType: 'Integer' }
125
+ }
126
+ });
127
+
128
+ // API A returns: { title: '...', author: '...', year: 2024 }
129
+ // Works out of the box:
130
+ mediaSchema.getValueByHash(apiAResponse, 'Title');
131
+
132
+ // API B returns: { metadata: { name: '...', creator: '...' }, info: { published: 2024 } }
133
+ // Add translations for API B's shape:
134
+ mediaSchema.hashTranslations.addTranslation({
135
+ 'Title': 'metadata.name',
136
+ 'Author': 'metadata.creator',
137
+ 'Year': 'info.published'
138
+ });
139
+
140
+ mediaSchema.getValueByHash(apiBResponse, 'Title'); // reads metadata.name
141
+ mediaSchema.getValueByHash(apiBResponse, 'Author'); // reads metadata.creator
142
+ ```
143
+
144
+ ### Locale-Specific Field Names
145
+
146
+ ```javascript
147
+ const formSchema = new libManyfest({
148
+ Scope: 'Form',
149
+ Descriptors: {
150
+ 'firstName': { Hash: 'FirstName', DataType: 'String' },
151
+ 'lastName': { Hash: 'LastName', DataType: 'String' }
152
+ }
153
+ });
154
+
155
+ // Japanese form data uses different field names
156
+ formSchema.hashTranslations.addTranslation({
157
+ 'FirstName': 'mei',
158
+ 'LastName': 'sei'
159
+ });
160
+
161
+ const jpData = { mei: 'Taro', sei: 'Yamada' };
162
+ formSchema.getValueByHash(jpData, 'FirstName'); // 'Taro'
163
+ ```
164
+
165
+ ### Temporary Overrides
166
+
167
+ Apply translations for a specific operation, then clear them:
168
+
169
+ ```javascript
170
+ manifest.hashTranslations.addTranslation({ 'Name': 'display_name' });
171
+
172
+ const displayName = manifest.getValueByHash(record, 'Name');
173
+
174
+ manifest.hashTranslations.clearTranslations();
175
+ // Back to normal resolution
176
+ ```
177
+
178
+ ## Serialization
179
+
180
+ Hash translations are included when you serialize a manifest:
181
+
182
+ ```javascript
183
+ const state = manifest.getManifest();
184
+ // { Scope: '...', Descriptors: {...}, HashTranslations: {...} }
185
+
186
+ const json = manifest.serialize();
187
+ // HashTranslations included in the JSON string
188
+ ```
189
+
190
+ And restored when you clone:
191
+
192
+ ```javascript
193
+ const copy = manifest.clone();
194
+ // copy.hashTranslations contains the same translations
195
+ ```
196
+
197
+ ## Notes
198
+
199
+ - Translations are checked before the built-in hash table, so they can override descriptor hashes
200
+ - If a translation points to a hash that also has a translation, only one level of indirection is resolved
201
+ - Translations work with all hash-based methods: `getValueByHash`, `setValueByHash`, `deleteValueByHash`, `checkAddressExistsByHash`, `getDescriptorByHash`
202
+ - The translation table is a simple `{ source: destination }` object -- no nesting or chaining
@@ -0,0 +1,51 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Manyfest Documentation</title>
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7
+ <meta name="description" content="Documentation for Manyfest - JSON Object Manifest for Data Description and Parsing" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
9
+ <meta name="keywords" content="manyfest, manifest, json, object, schema, data, description, parsing, javascript" />
10
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css" />
11
+ <style>
12
+ :root {
13
+ --theme-color: #42b983;
14
+ }
15
+ </style>
16
+ </head>
17
+ <body>
18
+ <div id="app"></div>
19
+ <script>
20
+ window.$docsify = {
21
+ name: 'Manyfest',
22
+ repo: 'https://github.com/stevenvelozo/manyfest',
23
+ loadSidebar: '_sidebar.md',
24
+ subMaxLevel: 3,
25
+ auto2top: true,
26
+ coverpage: 'cover.md',
27
+ onlyCover: false,
28
+ search: {
29
+ placeholder: 'Search',
30
+ noData: 'No results found',
31
+ depth: 3
32
+ },
33
+ copyCode: {
34
+ buttonText: 'Copy',
35
+ successText: 'Copied'
36
+ }
37
+ };
38
+ </script>
39
+ <!-- Docsify v4 -->
40
+ <script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
41
+ <!-- Syntax highlighting -->
42
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-javascript.min.js"></script>
43
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.min.js"></script>
44
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
45
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-markdown.min.js"></script>
46
+ <!-- Search plugin -->
47
+ <script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js"></script>
48
+ <!-- Copy code plugin -->
49
+ <script src="//cdn.jsdelivr.net/npm/docsify-copy-code@2"></script>
50
+ </body>
51
+ </html>