@foxy.io/sdk 1.9.1 → 1.9.2

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.
@@ -1,8 +1,3 @@
1
- var Entity;
2
- (function (Entity) {
3
- Entity[Entity["List"] = 0] = "List";
4
- Entity[Entity["Set"] = 1] = "Set";
5
- })(Entity || (Entity = {}));
6
1
  /**
7
2
  * Boolean selector is an HTML boolean attribute value format that allows
8
3
  * developers to write configurations for elements deep in a shadow DOM. Here's
@@ -54,7 +49,6 @@ export class BooleanSelector {
54
49
  * @param value boolean selector value, e.g. `foo:bar baz:not=qux`
55
50
  */
56
51
  constructor(value) {
57
- this.__value = value;
58
52
  this.__tree = BooleanSelector.__parse(value);
59
53
  }
60
54
  /**
@@ -123,24 +117,38 @@ export class BooleanSelector {
123
117
  return isFullMatch ? selector === 'not=*' : selector !== '';
124
118
  }
125
119
  /**
126
- * Zooms on the given top-level identifier.
120
+ * Zooms on the given top-level identifier or follows a path.
127
121
  *
128
122
  * @example
123
+ * new BooleanSelector('foo:bar:baz').zoom('foo:bar').toString() // => "baz"
129
124
  * new BooleanSelector('foo:bar:baz').zoom('foo').toString() // => "bar:baz"
130
125
  * new BooleanSelector('not=foo').zoom('bar').toString() // => "not=*"
131
126
  * new BooleanSelector('not=foo').zoom('foo').toString() // => ""
132
127
  *
133
- * @param id identifier to look for
134
- * @returns `true` is current selector includes rules for the given identifier
128
+ * @param path path to look for
129
+ * @returns zoomed BooleanSelector
135
130
  */
136
- zoom(id) {
137
- const [firstPart, ...rest] = id.split(':');
138
- const { only, not } = this.__tree;
139
- if (only === null || only === void 0 ? void 0 : only[firstPart]) {
140
- const selector = new BooleanSelector(BooleanSelector.__stringify(only[firstPart]));
141
- return rest.length === 0 ? selector : selector.zoom(rest.join(':'));
142
- }
143
- return !not || not.includes(firstPart) ? BooleanSelector.False : BooleanSelector.True;
131
+ zoom(path) {
132
+ const zoomedSelector = new BooleanSelector('');
133
+ zoomedSelector.__tree = path.split(':').reduce((currentTree, id) => {
134
+ let zoomedTree;
135
+ if ('include' in currentTree) {
136
+ zoomedTree = currentTree.include[id];
137
+ if (zoomedTree === undefined)
138
+ return { include: {} };
139
+ if (zoomedTree === true)
140
+ return { exclude: { '*': true } };
141
+ }
142
+ else {
143
+ zoomedTree = currentTree.exclude[id];
144
+ if (zoomedTree === undefined)
145
+ return { exclude: { '*': true } };
146
+ if (zoomedTree === true)
147
+ return { include: {} };
148
+ }
149
+ return zoomedTree;
150
+ }, this.__tree);
151
+ return zoomedSelector;
144
152
  }
145
153
  /**
146
154
  * Converts this selector to string.
@@ -151,7 +159,7 @@ export class BooleanSelector {
151
159
  * @returns serialized representation of this selector
152
160
  */
153
161
  toString() {
154
- return this.__value;
162
+ return BooleanSelector.__stringifyTree(this.__tree);
155
163
  }
156
164
  /**
157
165
  * Converts this selector to an attribute value.
@@ -166,103 +174,226 @@ export class BooleanSelector {
166
174
  * @returns attribute value representing this selector.
167
175
  */
168
176
  toAttribute(truthyValue = '') {
169
- var _a;
170
- if (((_a = this.__tree.not) === null || _a === void 0 ? void 0 : _a[0]) === '*')
177
+ const serializedSelector = this.toString();
178
+ if (serializedSelector === 'not=*')
171
179
  return truthyValue;
172
- return this.__value.trim().length === 0 ? null : this.toString();
180
+ return serializedSelector.length === 0 ? null : serializedSelector;
181
+ }
182
+ static __parsePath(path, tree) {
183
+ const firstSeparatorIndex = path.indexOf(':');
184
+ const topLevelId = path.substring(0, firstSeparatorIndex);
185
+ const nestedPath = path.substring(firstSeparatorIndex + 1);
186
+ if ('exclude' in tree) {
187
+ const subTree = tree.exclude[topLevelId];
188
+ if (subTree)
189
+ tree.exclude[topLevelId] = this.__parseListItem(nestedPath, subTree === true ? void 0 : subTree);
190
+ }
191
+ else {
192
+ const subTree = tree.include[topLevelId];
193
+ if (subTree !== true)
194
+ tree.include[topLevelId] = this.__parseListItem(nestedPath, subTree);
195
+ }
196
+ return tree;
197
+ }
198
+ static __parseSet(set, tree) {
199
+ const setItems = set.split(',');
200
+ if ('include' in tree) {
201
+ tree = { exclude: tree.include };
202
+ for (const id in tree.exclude)
203
+ if (!setItems.includes(id))
204
+ delete tree.exclude[id];
205
+ for (const item of setItems) {
206
+ if (item in tree.exclude) {
207
+ delete tree.exclude[item];
208
+ }
209
+ else {
210
+ tree.exclude[item] = true;
211
+ }
212
+ }
213
+ }
214
+ else {
215
+ for (const id in tree.exclude)
216
+ if (!setItems.includes(id))
217
+ delete tree.exclude[id];
218
+ }
219
+ return tree;
173
220
  }
174
- static __stringify(tree, path = '') {
175
- if (tree.only) {
176
- return Object.entries(tree.only).reduce((output, [key, subtree]) => {
177
- const result = BooleanSelector.__stringify(subtree, path.length === 0 ? key : `${path}:${key}`);
178
- return output.length === 0 ? result : `${output} ${result}`;
179
- }, '');
221
+ static __parseListItem(listItem, tree = { include: {} }) {
222
+ if (listItem.includes(':'))
223
+ return this.__parsePath(listItem, tree);
224
+ if (listItem.startsWith('not='))
225
+ return this.__parseSet(listItem.substring(4), tree);
226
+ if ('include' in tree) {
227
+ tree.include[listItem] = true;
180
228
  }
181
- if (tree.not)
182
- return `${path.length === 0 ? '' : `${path}:`}not=${tree.not.join(',')}`;
183
- return path;
229
+ else {
230
+ for (const id in tree.exclude)
231
+ if (id === listItem)
232
+ delete tree.exclude[id];
233
+ }
234
+ return tree;
184
235
  }
185
- static __parse(value) {
186
- const tree = {};
187
- const output = { branch: tree, buffer: '', entity: Entity.List, tree };
188
- Array.from(`${value} `).forEach((character, position) => {
236
+ static __parseList(list, tree = { include: {} }) {
237
+ return list.split(' ').reduce((newTree, listItem) => this.__parseListItem(listItem, newTree), tree);
238
+ }
239
+ static __lintList(list) {
240
+ var _a;
241
+ let position = 'list';
242
+ let result = '';
243
+ for (let i = 0; i < list.length; ++i) {
244
+ const character = list.charAt(i);
189
245
  try {
190
- BooleanSelector.__processors[output.entity](output, character);
246
+ if (position === 'list') {
247
+ if (/^\s$/.test(character)) {
248
+ if (!/^\s$/.test((_a = list[i - 1]) !== null && _a !== void 0 ? _a : ' '))
249
+ result += ' ';
250
+ continue;
251
+ }
252
+ if (/^[a-z]$/.test(character)) {
253
+ result += character;
254
+ position = 'path';
255
+ continue;
256
+ }
257
+ throw new SyntaxError(`Expected [a-z] or a whitespace, but got "${character}" instead.`);
258
+ }
259
+ if (position === 'path') {
260
+ if (/^[a-z]$/.test(character)) {
261
+ result += character;
262
+ continue;
263
+ }
264
+ if (character === '-') {
265
+ if (list[i - 1] === '-' || list[i - 1] === ':') {
266
+ throw new SyntaxError(`Expected [a-z], but got "${character}" instead.`);
267
+ }
268
+ else {
269
+ result += character;
270
+ continue;
271
+ }
272
+ }
273
+ if (character === ':') {
274
+ if (list[i - 1] === ':' || list[i - 1] === '-') {
275
+ throw new SyntaxError(`Expected [a-z], but got "${character}" instead.`);
276
+ }
277
+ else {
278
+ result += character;
279
+ continue;
280
+ }
281
+ }
282
+ if (character === '=') {
283
+ if (list[i - 1] === '=' || list[i - 1] === ':' || list[i - 1] === '-') {
284
+ throw new SyntaxError(`Expected [a-z], but got "${character}" instead.`);
285
+ }
286
+ if (result.endsWith('not') && (result.length === 3 || !/[a-z]|-/.test(result[i - 4]))) {
287
+ result += character;
288
+ position = 'set';
289
+ continue;
290
+ }
291
+ else {
292
+ throw new SyntaxError(`Expected [a-z] or ":", but got "${character}" instead.`);
293
+ }
294
+ }
295
+ if (/^\s$/.test(character)) {
296
+ result += ' ';
297
+ position = 'list';
298
+ continue;
299
+ }
300
+ throw new SyntaxError(`Expected [a-z], ",", ":", ":" or a whitespace, but got "${character}" instead.`);
301
+ }
302
+ if (position === 'set') {
303
+ if (/^\s$/.test(character))
304
+ continue;
305
+ if (/^[a-z]|\*$/.test(character)) {
306
+ position = 'set-item';
307
+ result += character;
308
+ continue;
309
+ }
310
+ throw new SyntaxError(`Expected [a-z] or a whitespace, but got "${character}" instead.`);
311
+ }
312
+ if (position === 'set-item') {
313
+ if (list[i - 1] === '*') {
314
+ if (character === ',') {
315
+ result += character;
316
+ position = 'set';
317
+ continue;
318
+ }
319
+ if (/^\s$/.test(character)) {
320
+ if (i !== list.length - 1)
321
+ result += ' ';
322
+ position = 'list';
323
+ continue;
324
+ }
325
+ throw new SyntaxError(`Expected "," or a whitespace, but got "${character}" instead.`);
326
+ }
327
+ else {
328
+ if (/^[a-z]$/.test(character)) {
329
+ result += character;
330
+ continue;
331
+ }
332
+ if (character === '-') {
333
+ if (list[i - 1] === '-' || list[i - 1] === ':' || list[i - 1] === '=') {
334
+ throw new SyntaxError(`Expected [a-z], but got "${character}" instead.`);
335
+ }
336
+ else {
337
+ result += character;
338
+ continue;
339
+ }
340
+ }
341
+ if (character === ',') {
342
+ result += character;
343
+ position = 'set';
344
+ continue;
345
+ }
346
+ if (/^\s$/.test(character)) {
347
+ if (i !== list.length - 1)
348
+ result += ' ';
349
+ position = 'list';
350
+ continue;
351
+ }
352
+ throw new SyntaxError(`Expected [a-z], "," or a whitespace, but got "${character}" instead.`);
353
+ }
354
+ }
191
355
  }
192
356
  catch (err) {
193
357
  const hint = 'This error occured at: ';
194
- const trim = (v) => v.substring(Math.max(0, position - 30), position + 30);
195
- const preview = trim(value);
196
- const pointer = ' '.repeat(hint.length) + trim('^'.padStart(position + 1, ' '));
358
+ const trim = (v) => v.substring(Math.max(0, i - 30), i + 30);
359
+ const preview = trim(list);
360
+ const pointer = ' '.repeat(hint.length) + trim('^'.padStart(i + 1, ' '));
197
361
  throw new SyntaxError([err.message, `${hint}${preview}`, pointer].join('\n'));
198
362
  }
199
- });
200
- return tree;
201
- }
202
- }
203
- BooleanSelector.__processors = {
204
- [Entity.List](output, character) {
205
- var _a, _b, _c, _d;
206
- /* istanbul ignore next */
207
- if (Array.isArray(output.branch))
208
- throw new SyntaxError('Paths are not allowed in sets.');
209
- if (character === '=') {
210
- if (output.buffer === 'not') {
211
- const newBranch = (_a = output.branch.not) !== null && _a !== void 0 ? _a : [];
212
- delete output.branch.only;
213
- output.branch.not = newBranch;
214
- output.entity = Entity.Set;
215
- output.branch = newBranch;
216
- output.buffer = '';
217
- return;
218
- }
219
- else {
220
- throw new SyntaxError(`Unknown modifier "${output.buffer}".`);
221
- }
222
363
  }
223
- if (/^\s$/.test(character) || character === ':') {
224
- if (output.buffer.length > 0) {
225
- const selector = output.buffer;
226
- const newBranch = (_c = (_b = output.branch.only) === null || _b === void 0 ? void 0 : _b[selector]) !== null && _c !== void 0 ? _c : {};
227
- if (character !== ':')
228
- newBranch.not = ['*'];
229
- if (((_d = output.branch.not) === null || _d === void 0 ? void 0 : _d.includes('*')) !== true) {
230
- output.branch.only = Object.assign(Object.assign({}, output.branch.only), { [selector]: newBranch });
231
- delete output.branch.not;
364
+ return result.trimEnd();
365
+ }
366
+ static __parse(list) {
367
+ return this.__parseList(this.__lintList(list));
368
+ }
369
+ static __stringifyTree(tree, path) {
370
+ const parts = [];
371
+ if ('include' in tree) {
372
+ for (const id in tree.include) {
373
+ const nestedTree = tree.include[id];
374
+ const newPath = path ? [path, id].join(':') : id;
375
+ if (nestedTree === true) {
376
+ parts.push(newPath);
377
+ }
378
+ else {
379
+ parts.push(this.__stringifyTree(nestedTree, newPath));
232
380
  }
233
- output.branch = character === ':' ? newBranch : output.tree;
234
- output.buffer = '';
235
381
  }
236
- return;
237
382
  }
238
- if (/^[a-z]|-$/.test(character)) {
239
- output.buffer += character;
240
- return;
241
- }
242
- throw new SyntaxError(`Expected [a-z], "-", ":" or a whitespace, but got "${character}" instead.`);
243
- },
244
- [Entity.Set](output, character) {
245
- /* istanbul ignore next */
246
- if (!Array.isArray(output.branch))
247
- throw new SyntaxError('Unexpected set item.');
248
- if (output.buffer.length === 0 && /^\s$/.test(character))
249
- return;
250
- if (character === ',' || character === '*' || /^\s$/.test(character)) {
251
- const newItem = character === '*' ? '*' : output.buffer;
252
- const updatedSet = new Set([...output.branch, newItem]);
253
- const normalizedSet = updatedSet.has('*') ? new Set(['*']) : updatedSet;
254
- output.branch.splice(0, output.branch.length, ...normalizedSet);
255
- output.entity = character === ',' ? Entity.Set : Entity.List;
256
- output.branch = character === ',' ? output.branch : output.tree;
257
- output.buffer = '';
258
- return;
259
- }
260
- if (/^[a-z]|-$/.test(character)) {
261
- output.buffer += character;
262
- return;
383
+ else {
384
+ const ids = [];
385
+ const partsToPush = [];
386
+ for (const id in tree.exclude) {
387
+ const nestedTree = tree.exclude[id];
388
+ const newPath = path ? [path, id].join(':') : id;
389
+ ids.push(id);
390
+ if (nestedTree !== true)
391
+ partsToPush.push(this.__stringifyTree(nestedTree, newPath));
392
+ }
393
+ parts.push(`${path ? `${path}:` : ''}not=${ids.join(',')}`, ...partsToPush);
263
394
  }
264
- throw new SyntaxError(`Expected [a-z], "-", "," or a whitespace, but got "${character}" instead.`);
265
- },
266
- };
395
+ return parts.join(' ');
396
+ }
397
+ }
267
398
  const falseBooleanSelectorSingleton = new BooleanSelector('');
268
399
  const trueBooleanSelectorSingleton = new BooleanSelector('not=*');
@@ -78,8 +78,6 @@ export declare class BooleanSelector {
78
78
  * @returns `BooleanSelector` instance constructed from the given attribite value
79
79
  */
80
80
  static fromAttribute(value: string | null, truthyValue?: string): BooleanSelector;
81
- private static __processors;
82
- private __value;
83
81
  private __tree;
84
82
  /**
85
83
  * Parses the boolean selector value and creates an instance
@@ -105,17 +103,18 @@ export declare class BooleanSelector {
105
103
  */
106
104
  matches(id: string, isFullMatch?: boolean): boolean;
107
105
  /**
108
- * Zooms on the given top-level identifier.
106
+ * Zooms on the given top-level identifier or follows a path.
109
107
  *
110
108
  * @example
109
+ * new BooleanSelector('foo:bar:baz').zoom('foo:bar').toString() // => "baz"
111
110
  * new BooleanSelector('foo:bar:baz').zoom('foo').toString() // => "bar:baz"
112
111
  * new BooleanSelector('not=foo').zoom('bar').toString() // => "not=*"
113
112
  * new BooleanSelector('not=foo').zoom('foo').toString() // => ""
114
113
  *
115
- * @param id identifier to look for
116
- * @returns `true` is current selector includes rules for the given identifier
114
+ * @param path path to look for
115
+ * @returns zoomed BooleanSelector
117
116
  */
118
- zoom(id: string): BooleanSelector;
117
+ zoom(path: string): BooleanSelector;
119
118
  /**
120
119
  * Converts this selector to string.
121
120
  *
@@ -138,6 +137,11 @@ export declare class BooleanSelector {
138
137
  * @returns attribute value representing this selector.
139
138
  */
140
139
  toAttribute(truthyValue?: string): string | null;
141
- private static __stringify;
140
+ private static __parsePath;
141
+ private static __parseSet;
142
+ private static __parseListItem;
143
+ private static __parseList;
144
+ private static __lintList;
142
145
  private static __parse;
146
+ private static __stringifyTree;
143
147
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@foxy.io/sdk",
3
3
  "type": "commonjs",
4
- "version": "1.9.1",
4
+ "version": "1.9.2",
5
5
  "description": "Universal SDK for a full server-side and a limited in-browser access to Foxy hAPI.",
6
6
  "main": "dist/cjs/index.js",
7
7
  "module": "dist/esm/index.js",