aberdeen 1.11.1 → 1.12.0

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.
Files changed (78) hide show
  1. package/README.md +27 -26
  2. package/dist/src/aberdeen.d.ts +100 -86
  3. package/dist/src/aberdeen.js +5 -2
  4. package/dist/src/aberdeen.js.map +3 -3
  5. package/dist/src/aberdeen.min.js +4 -4
  6. package/dist/src/aberdeen.min.js.map +3 -3
  7. package/dist-docs/Tutorial/index.html +34 -29
  8. package/dist-docs/aberdeen/A/index.html +6 -6
  9. package/dist-docs/aberdeen/NO_COPY/index.html +2 -2
  10. package/dist-docs/aberdeen/PromiseProxy/index.html +5 -5
  11. package/dist-docs/aberdeen/clean/index.html +3 -3
  12. package/dist-docs/aberdeen/clone/index.html +2 -2
  13. package/dist-docs/aberdeen/copy/index.html +4 -4
  14. package/dist-docs/aberdeen/count/index.html +3 -3
  15. package/dist-docs/aberdeen/cssVars/index.html +2 -2
  16. package/dist-docs/aberdeen/darkMode/index.html +2 -2
  17. package/dist-docs/aberdeen/default/index.html +9 -8
  18. package/dist-docs/aberdeen/derive/index.html +4 -4
  19. package/dist-docs/aberdeen/disableCreateDestroy/index.html +2 -2
  20. package/dist-docs/aberdeen/dump/index.html +3 -3
  21. package/dist-docs/aberdeen/index.html +2 -2
  22. package/dist-docs/aberdeen/insertCss/index.html +2 -2
  23. package/dist-docs/aberdeen/insertGlobalCss/index.html +8 -2
  24. package/dist-docs/aberdeen/invertString/index.html +3 -3
  25. package/dist-docs/aberdeen/isEmpty/index.html +3 -3
  26. package/dist-docs/aberdeen/map/index.html +6 -6
  27. package/dist-docs/aberdeen/merge/index.html +5 -5
  28. package/dist-docs/aberdeen/mount/index.html +3 -3
  29. package/dist-docs/aberdeen/multiMap/index.html +5 -5
  30. package/dist-docs/aberdeen/onEach/index.html +6 -6
  31. package/dist-docs/aberdeen/partition/index.html +6 -6
  32. package/dist-docs/aberdeen/peek/index.html +9 -9
  33. package/dist-docs/aberdeen/proxy/index.html +9 -8
  34. package/dist-docs/aberdeen/ref/index.html +3 -3
  35. package/dist-docs/aberdeen/runQueue/index.html +3 -3
  36. package/dist-docs/aberdeen/setErrorHandler/index.html +2 -2
  37. package/dist-docs/aberdeen/setSpacingCssVars/index.html +2 -2
  38. package/dist-docs/aberdeen/unmountAll/index.html +2 -2
  39. package/dist-docs/aberdeen/unproxy/index.html +3 -3
  40. package/dist-docs/assets/aberdeen/aberdeen.d.ts +100 -86
  41. package/dist-docs/assets/aberdeen/aberdeen.js +5 -2
  42. package/dist-docs/assets/aberdeen/aberdeen.js.map +3 -3
  43. package/dist-docs/assets/aberdeen/aberdeen.min.js +4 -4
  44. package/dist-docs/assets/aberdeen/aberdeen.min.js.map +3 -3
  45. package/dist-docs/assets/search.js +1 -1
  46. package/dist-docs/dispatcher/Dispatcher/index.html +4 -4
  47. package/dist-docs/dispatcher/MATCH_FAILED/index.html +2 -2
  48. package/dist-docs/dispatcher/MATCH_REST/index.html +2 -2
  49. package/dist-docs/dispatcher/index.html +2 -2
  50. package/dist-docs/hierarchy.html +1 -1
  51. package/dist-docs/index.html +4 -4
  52. package/dist-docs/media/CHANGELOG.md +12 -0
  53. package/dist-docs/modules.html +1 -1
  54. package/dist-docs/prediction/applyCanon/index.html +2 -2
  55. package/dist-docs/prediction/applyPrediction/index.html +2 -2
  56. package/dist-docs/prediction/index.html +2 -2
  57. package/dist-docs/route/Route/index.html +9 -9
  58. package/dist-docs/route/back/index.html +2 -2
  59. package/dist-docs/route/current/index.html +2 -2
  60. package/dist-docs/route/go/index.html +2 -2
  61. package/dist-docs/route/index.html +2 -2
  62. package/dist-docs/route/interceptLinks/index.html +2 -2
  63. package/dist-docs/route/persistScroll/index.html +2 -2
  64. package/dist-docs/route/push/index.html +2 -2
  65. package/dist-docs/route/setLog/index.html +2 -2
  66. package/dist-docs/route/up/index.html +2 -2
  67. package/dist-docs/sitemap.xml +56 -56
  68. package/dist-docs/transitions/grow/index.html +2 -2
  69. package/dist-docs/transitions/index.html +2 -2
  70. package/dist-docs/transitions/shrink/index.html +2 -2
  71. package/package.json +1 -1
  72. package/skill/SKILL.md +162 -137
  73. package/skill/aberdeen.md +339 -311
  74. package/skill/dispatcher.md +6 -6
  75. package/skill/prediction.md +3 -3
  76. package/skill/route.md +17 -17
  77. package/skill/transitions.md +3 -3
  78. package/src/aberdeen.ts +163 -146
package/README.md CHANGED
@@ -33,21 +33,22 @@ First, let's start with the obligatory reactive counter example. If you're readi
33
33
  import A from 'aberdeen';
34
34
 
35
35
  // Define some state as a proxied (observable) object
36
- const state = A.proxy({question: "How many roads must a man walk down?", answer: 42});
36
+ // The '$' prefix is just a convention to make reactive objects stand out
37
+ const $state = A.proxy({question: "How many roads must a man walk down?", answer: 42});
37
38
 
38
39
  A('h3', () => {
39
40
  // This function reruns whenever the question or the answer changes
40
- A('text=', `${state.question} ↪ ${state.answer || 'Blowing in the wind'}`)
41
+ A('text=', `${$state.question} ↪ ${$state.answer || 'Blowing in the wind'}`)
41
42
  });
42
43
 
43
- // Two-way bind state.question to an <input>
44
- A('input placeholder=Question bind=', A.ref(state, 'question'))
44
+ // Two-way bind $state.question to an <input>
45
+ A('input placeholder=Question bind=', A.ref($state, 'question'))
45
46
 
46
- // Allow state.answer to be modified using both an <input> and buttons
47
+ // Allow $state.answer to be modified using both an <input> and buttons
47
48
  A('div.row margin-top:1em', () => {
48
- A('button text=- click=', () => state.answer--);
49
- A('input type=number bind=', A.ref(state, 'answer'))
50
- A('button text=+ click=', () => state.answer++);
49
+ A('button text=- click=', () => $state.answer--);
50
+ A('input type=number bind=', A.ref($state, 'answer'))
51
+ A('button text=+ click=', () => $state.answer++);
51
52
  });
52
53
  ```
53
54
 
@@ -75,64 +76,64 @@ class TodoItem {
75
76
  // The top-level user interface.
76
77
  function drawMain() {
77
78
  // Add some initial items. We'll wrap a A.proxy() around it!
78
- let items: TodoItem[] = A.proxy([
79
+ let $items: TodoItem[] = A.proxy([
79
80
  new TodoItem('Make todo-list demo', true),
80
81
  new TodoItem('Learn Aberdeen', false),
81
82
  ]);
82
83
 
83
84
  // Draw the list, ordered by label.
84
- A.onEach(items, drawItem, item => item.label);
85
+ A.onEach($items, drawItem, $item => $item.label);
85
86
 
86
87
  // Add item and delete checked buttons.
87
88
  A('div.row', () => {
88
- A('button text=+ click=', () => items.push(new TodoItem("")));
89
+ A('button text=+ click=', () => $items.push(new TodoItem("")));
89
90
  A('button.outline text="Delete checked" click=', () => {
90
- for(let idx in items) {
91
- if (items[idx].done) delete items[idx];
91
+ for(let idx in $items) {
92
+ if ($items[idx].done) delete $items[idx];
92
93
  }
93
94
  });
94
95
  });
95
96
  };
96
97
 
97
98
  // Called for each todo list item.
98
- function drawItem(item) {
99
+ function drawItem($item) {
99
100
  // Items without a label open in editing state.
100
101
  // Note that we're creating this A.proxy outside the `div.row` scope
101
102
  // create below, so that it will persist when that state reruns.
102
- let editing: {value: boolean} = A.proxy(item.label == '');
103
+ let $editing: {value: boolean} = A.proxy($item.label == '');
103
104
 
104
105
  A('div.row', todoItemStyle, 'create=', grow, 'destroy=', shrink, () => {
105
106
  // Conditionally add a class to `div.row`, based on item.done
106
- A({".done": A.ref(item,'done')});
107
+ A({".done": A.ref($item,'done')});
107
108
 
108
109
  // The checkmark is hidden using CSS
109
110
  A('div.checkmark text=✅');
110
111
 
111
- if (editing.value) {
112
+ if ($editing.value) {
112
113
  // Proxied string to hold label while being edited.
113
- const labelCopy = A.proxy(item.label);
114
+ const $labelCopy = A.proxy($item.label);
114
115
  function save() {
115
- editing.value = false;
116
- item.label = labelCopy.value;
116
+ $editing.value = false;
117
+ $item.label = $labelCopy.value;
117
118
  }
118
119
  // Label <input>. Save using enter or button.
119
- A('input placeholder=Label bind=', labelCopy, 'keydown=', e => e.key==='Enter' && save());
120
- A('button.outline text=Cancel click=', () => editing.value = false);
120
+ A('input placeholder=Label bind=', $labelCopy, 'keydown=', e => e.key==='Enter' && save());
121
+ A('button.outline text=Cancel click=', () => $editing.value = false);
121
122
  A('button text=Save click=', save);
122
123
  } else {
123
124
  // Label as text.
124
- A('p text=', item.label);
125
+ A('p text=', $item.label);
125
126
 
126
127
  // Edit icon, if not done.
127
- if (!item.done) {
128
+ if (!$item.done) {
128
129
  A('a text=Edit click=', e => {
129
- editing.value = true;
130
+ $editing.value = true;
130
131
  e.stopPropagation(); // We don't want to toggle as well.
131
132
  });
132
133
  }
133
134
 
134
135
  // Clicking a row toggles done.
135
- A('cursor:pointer click=', () => item.done = !item.done);
136
+ A('cursor:pointer click=', () => $item.done = !$item.done);
136
137
  }
137
138
  });
138
139
  }
@@ -14,13 +14,13 @@
14
14
  *
15
15
  * @example
16
16
  * ```typescript
17
- * const data = A.proxy("before");
17
+ * const $data = A.proxy("before");
18
18
  *
19
- * A('#', data);
19
+ * A('#', $data);
20
20
  * console.log(1, document.body.innerHTML); // before
21
21
  *
22
22
  * // Make an update that should cause the DOM to change.
23
- * data.value = "after";
23
+ * $data.value = "after";
24
24
  *
25
25
  * // Normally, the DOM update would happen after a timeout.
26
26
  * // But this causes an immediate update:
@@ -42,15 +42,15 @@ export declare function runQueue(): void;
42
42
  *
43
43
  * @example
44
44
  * ```typescript
45
- * const users = A.proxy([
45
+ * const $users = A.proxy([
46
46
  * { id: 1, name: 'Charlie', score: 95 },
47
47
  * { id: 2, name: 'Alice', score: 100 },
48
48
  * { id: 3, name: 'Bob', score: 90 },
49
49
  * ]);
50
50
  *
51
- * A.onEach(users, (user) => {
52
- * A(`p#${user.name}: ${user.score}`);
53
- * }, (user) => A.invertString(user.name)); // Reverse alphabetic order
51
+ * A.onEach($users, ($user) => {
52
+ * A(`p#${$user.name}: ${$user.score}`);
53
+ * }, ($user) => A.invertString($user.name)); // Reverse alphabetic order
54
54
  * ```
55
55
  *
56
56
  * @param input The string whose sort order needs to be inverted.
@@ -78,21 +78,21 @@ export declare const EMPTY: unique symbol;
78
78
  *
79
79
  * @example
80
80
  * ```typescript
81
- * const items = A.proxy([]);
81
+ * const $items = A.proxy([]);
82
82
  *
83
83
  * // Reactively display a message if the items array is empty
84
84
  * A('div', () => {
85
- * if (A.isEmpty(items)) {
85
+ * if (A.isEmpty($items)) {
86
86
  * A('p i#No items yet!');
87
87
  * } else {
88
- * A.onEach(items, item => A('p#'+item));
88
+ * A.onEach($items, item => A('p#'+item));
89
89
  * }
90
90
  * });
91
91
  *
92
92
  * // Adding an item will automatically remove the "No items yet!" message
93
93
  * setInterval(() => {
94
- * if (!items.length || Math.random()>0.5) items.push('Item');
95
- * else items.length = 0;
94
+ * if (!$items.length || Math.random()>0.5) $items.push('Item');
95
+ * else $items.length = 0;
96
96
  * }, 1000)
97
97
  * ```
98
98
  */
@@ -109,19 +109,19 @@ export interface ValueRef<T> {
109
109
  *
110
110
  * @example
111
111
  * ```typescript
112
- * const items = A.proxy({x: 3, y: 7} as any);
113
- * const cnt = A.count(items);
112
+ * const $items = A.proxy({x: 3, y: 7} as any);
113
+ * const $count = A.count($items);
114
114
  *
115
115
  * // Create a DOM text node for the count:
116
- * A('div text=', cnt);
116
+ * A('div text=', $count);
117
117
  * // <div>2</div>
118
118
 
119
119
  * // Or we can use it in an {@link derive} function:
120
- * A(() => console.log("The count is now", cnt.value));
120
+ * A(() => console.log("The count is now", $count.value));
121
121
  * // The count is now 2
122
122
  *
123
123
  * // Adding/removing items will update the count
124
- * items.z = 12;
124
+ * $items.z = 12;
125
125
  * // Asynchronously, after 0ms:
126
126
  * // <div>3</div>
127
127
  * // The count is now 3
@@ -162,23 +162,23 @@ export declare function proxy<T extends any>(target: T): ValueRef<T extends numb
162
162
  *
163
163
  * @example
164
164
  * ```typescript
165
- * const userProxy = A.proxy({ name: 'Frank' });
166
- * const rawUser = A.unproxy(userProxy);
165
+ * const $user = A.proxy({ name: 'Frank' });
166
+ * const rawUser = A.unproxy($user);
167
167
  *
168
168
  * // Log reactively
169
- * A(() => console.log('proxied', userProxy.name));
169
+ * A(() => console.log('proxied', $user.name));
170
170
  * // The following will only ever log once, as we're not subscribing to any observable
171
171
  * A(() => console.log('unproxied', rawUser.name));
172
172
  *
173
173
  * // This cause the first log to run again:
174
- * setTimeout(() => userProxy.name += '!', 1000);
174
+ * setTimeout(() => $user.name += '!', 1000);
175
175
  *
176
176
  * // This doesn't cause any new logs:
177
177
  * setTimeout(() => rawUser.name += '?', 2000);
178
178
  *
179
- * // Both userProxy and rawUser end up as `{name: 'Frank!?'}`
179
+ * // Both $user and rawUser end up as `{name: 'Frank!?'}`
180
180
  * setTimeout(() => {
181
- * console.log('final proxied', userProxy)
181
+ * console.log('final proxied', $user)
182
182
  * console.log('final unproxied', rawUser)
183
183
  * }, 3000);
184
184
  * ```
@@ -203,12 +203,12 @@ export declare function unproxy<T>(target: T): T;
203
203
  *
204
204
  * @example Basic Copy
205
205
  * ```typescript
206
- * const source = A.proxy({ a: 1, b: { c: 2 } });
207
- * const dest = A.proxy({ b: { d: 3 } });
208
- * A.copy(dest, source);
209
- * console.log(dest); // proxy({ a: 1, b: { c: 2 } })
210
- * A.copy(dest, 'b', { e: 4 });
211
- * console.log(dest); // proxy({ a: 1, b: { e: 4 } })
206
+ * const $source = A.proxy({ a: 1, b: { c: 2 } });
207
+ * const $dest = A.proxy({ b: { d: 3 } });
208
+ * A.copy($dest, $source);
209
+ * console.log($dest); // proxy({ a: 1, b: { c: 2 } })
210
+ * A.copy($dest, 'b', { e: 4 });
211
+ * console.log($dest); // proxy({ a: 1, b: { e: 4 } })
212
212
  * ```
213
213
  */
214
214
  export declare function copy<T extends object>(dst: T, src: T): boolean;
@@ -226,11 +226,11 @@ export declare function copy<T extends object>(dst: T, dstKey: keyof T, src: T[t
226
226
  * @example Basic merge
227
227
  * ```typescript
228
228
  * const source = { b: { c: 99 }, d: undefined }; // d: undefined will delete
229
- * const dest = A.proxy({ a: 1, b: { x: 5 }, d: 4 });
230
- * A.merge(dest, source);
231
- * A.merge(dest, 'b', { y: 6 }); // merge into dest.b
232
- * A.merge(dest, 'c', { z: 7 }); // merge.c doesn't exist yet, so it will just be assigned
233
- * console.log(dest); // proxy({ a: 1, b: { c: 99, x: 5, y: 6 }, c: { z: 7 } })
229
+ * const $dest = A.proxy({ a: 1, b: { x: 5 }, d: 4 });
230
+ * A.merge($dest, source);
231
+ * A.merge($dest, 'b', { y: 6 }); // merge into $dest.b
232
+ * A.merge($dest, 'c', { z: 7 }); // $dest.c doesn't exist yet, so it will just be assigned
233
+ * console.log($dest); // proxy({ a: 1, b: { c: 99, x: 5, y: 6 }, c: { z: 7 } })
234
234
  * ```
235
235
  *
236
236
  */
@@ -349,16 +349,16 @@ export declare function clone<T extends object>(src: T): T;
349
349
  *
350
350
  * @example
351
351
  * ```javascript
352
- * const formData = A.proxy({ color: 'orange', velocity: 42 });
352
+ * const $formData = A.proxy({ color: 'orange', velocity: 42 });
353
353
  *
354
354
  * // Usage with `bind`
355
- * A('input type=text bind=', A.ref(formData, 'color'));
355
+ * A('input type=text bind=', A.ref($formData, 'color'));
356
356
  *
357
357
  * // Usage as a dynamic property, causes a TextNode with just the name to be created and live-updated
358
- * A('p text="Selected color: " text=', A.ref(formData, 'color'), 'color:', A.ref(formData, 'color'));
358
+ * A('p text="Selected color: " text=', A.ref($formData, 'color'), 'color:', A.ref($formData, 'color'));
359
359
  *
360
- * // Changes are actually stored in formData - this causes logs like `{color: "Blue", velocity 42}`
361
- * A(() => console.log(formData))
360
+ * // Changes are actually stored in $formData - this causes logs like `{color: "Blue", velocity 42}`
361
+ * A(() => console.log($formData))
362
362
  * ```
363
363
  */
364
364
  export declare function ref<T extends TargetType, K extends keyof T>(target: T, index: K): ValueRef<T[K]>;
@@ -455,29 +455,29 @@ export declare function disableCreateDestroy(): void;
455
455
  *
456
456
  * @example Content Functions & Reactive Scope
457
457
  * ```typescript
458
- * const state = A.proxy({ count: 0 });
458
+ * const $state = A.proxy({ count: 0 });
459
459
  * A('div', () => { // Outer element
460
- * // This scope re-renders when state.count changes
461
- * A(`p#Count is ${state.count}`);
462
- * A('button text=Increment click=', () => state.count++);
460
+ * // This scope re-renders when $state.count changes
461
+ * A(`p#Count is ${$state.count}`);
462
+ * A('button text=Increment click=', () => $state.count++);
463
463
  * });
464
464
  * ```
465
465
  *
466
466
  * @example Two-way Binding
467
467
  * ```typescript
468
- * const user = A.proxy({ name: '' });
469
- * A('input placeholder=Name bind=', A.ref(user, 'name'));
468
+ * const $user = A.proxy({ name: '' });
469
+ * A('input placeholder=Name bind=', A.ref($user, 'name'));
470
470
  * A('h3', () => { // Reactive scope
471
- * A(`#Hello ${user.name || 'stranger'}`);
471
+ * A(`#Hello ${$user.name || 'stranger'}`);
472
472
  * });
473
473
  * ```
474
474
  *
475
475
  * @example Conditional Rendering
476
476
  * ```typescript
477
- * const show = A.proxy(false);
478
- * A('button click=', () => show.value = !show.value, () => A(show.value ? '#Hide' : '#Show'));
477
+ * const $show = A.proxy(false);
478
+ * A('button click=', () => $show.value = !$show.value, () => A($show.value ? '#Hide' : '#Show'));
479
479
  * A(() => { // Reactive scope
480
- * if (show.value) {
480
+ * if ($show.value) {
481
481
  * A('p#Details are visible!');
482
482
  * }
483
483
  * });
@@ -485,8 +485,8 @@ export declare function disableCreateDestroy(): void;
485
485
  *
486
486
  * @example Proxied objects as values
487
487
  * ```typescript
488
- * const myColor = A.proxy('red');
489
- * A('p text="The color is " text=', myColor, 'click=', () => myColor.value = 'yellow')
488
+ * const $myColor = A.proxy('red');
489
+ * A('p text="The color is " text=', $myColor, 'click=', () => $myColor.value = 'yellow')
490
490
  * // Clicking the text will cause it to change color without recreating the <p> itself
491
491
  * ```
492
492
  * This is often used together with {@link ref}, in order to use properties other than `.value`.
@@ -604,6 +604,20 @@ export declare function insertCss(style: string | object): string;
604
604
  * }
605
605
  * });
606
606
  * ```
607
+ * At-rules such as `@media` and `@keyframes` should use nested objects. For keyframes,
608
+ * the step selectors (`0%`, `50%`, `100%`, etc.) become the nested keys and each value
609
+ * should be a concise CSS declaration string.
610
+ *
611
+ * @example Animation Keyframes
612
+ * ```typescript
613
+ * A.insertGlobalCss({
614
+ * "@keyframes connection-pulse": {
615
+ * "0%": "box-shadow: inset 0 0 0 0 rgba(255, 70, 70, 0.4);",
616
+ * "50%": "box-shadow: inset 0 0 0 6px rgba(255, 70, 70, 0.08);",
617
+ * "100%": "box-shadow: inset 0 0 0 0 rgba(255, 70, 70, 0.4);"
618
+ * }
619
+ * });
620
+ * ```
607
621
  */
608
622
  export declare function insertGlobalCss(style: object): void;
609
623
  /**
@@ -663,27 +677,27 @@ export declare function setErrorHandler(handler?: (error: Error) => boolean | un
663
677
  *
664
678
  * @example Maintaing a sum for a changing array
665
679
  * ```typescript
666
- * const myArray = A.proxy([3, 5, 10]);
667
- * let sum = A.proxy(0);
680
+ * const $numbers = A.proxy([3, 5, 10]);
681
+ * let $sum = A.proxy(0);
668
682
  *
669
683
  * // Show the array items and maintain the sum
670
- * A.onEach(myArray, (item, index) => {
684
+ * A.onEach($numbers, (item, index) => {
671
685
  * A(`code#${index}→${item}`);
672
- * // We'll update sum.value using peek, as += first does a read, but
686
+ * // We'll update $sum.value using peek, as += first does a read, but
673
687
  * // we don't want to subscribe.
674
- * A.peek(() => sum.value += item);
688
+ * A.peek(() => $sum.value += item);
675
689
  * // Clean gets called before each rerun for a certain item index
676
690
  * // No need for peek here, as the clean code doesn't run in an
677
691
  * // observer scope.
678
- * A.clean(() => sum.value -= item);
692
+ * A.clean(() => $sum.value -= item);
679
693
  * })
680
694
  *
681
695
  * // Show the sum
682
- * A('h1 text=', sum);
696
+ * A('h1 text=', $sum);
683
697
  *
684
698
  * // Make random changes to the array
685
699
  * const rnd = () => 0|(Math.random()*20);
686
- * setInterval(() => myArray[rnd()] = rnd(), 1000);
700
+ * setInterval(() => $numbers[rnd()] = rnd(), 1000);
687
701
  * ```
688
702
  */
689
703
  export declare function clean(cleaner: () => void): void;
@@ -701,20 +715,20 @@ export declare function clean(cleaner: () => void): void;
701
715
  * observable returned by the `derive()` function.
702
716
  * @returns An observable object, with its `value` property containing whatever the last run of `func` returned.
703
717
  *
704
- * @example Observation creating a UI components
718
+ * @example Observation creating UI components
705
719
  * ```typescript
706
- * const data = A.proxy({ user: 'Frank', notifications: 42 });
720
+ * const $data = A.proxy({ user: 'Frank', notifications: 42 });
707
721
  *
708
722
  * A('main', () => {
709
723
  * console.log('Welcome');
710
- * A('h3#Welcome, ' + data.user); // Reactive text
724
+ * A('h3#Welcome, ' + $data.user); // Reactive text
711
725
  *
712
726
  * A.derive(() => {
713
- * // When data.notifications changes, only this inner scope reruns,
727
+ * // When $data.notifications changes, only this inner scope reruns,
714
728
  * // leaving the `<p>Welcome, ..</p>` untouched.
715
729
  * console.log('Notifications');
716
- * A('code.notification-badge text=', data.notifications);
717
- * A('a text=Notify! click=', () => data.notifications++);
730
+ * A('code.notification-badge text=', $data.notifications);
731
+ * A('a text=Notify! click=', () => $data.notifications++);
718
732
  * });
719
733
  * });
720
734
  * ```
@@ -723,12 +737,12 @@ export declare function clean(cleaner: () => void): void;
723
737
  *
724
738
  * @example Observation with return value
725
739
  * ```typescript
726
- * const counter = A.proxy(0);
727
- * setInterval(() => counter.value++, 1000);
728
- * const double = A.derive(() => counter.value * 2);
740
+ * const $counter = A.proxy(0);
741
+ * setInterval(() => $counter.value++, 1000);
742
+ * const $double = A.derive(() => $counter.value * 2);
729
743
  *
730
744
  * A('h3', () => {
731
- * A(`#counter=${counter.value} double=${double.value}`);
745
+ * A(`#counter=${$counter.value} double=${$double.value}`);
732
746
  * })
733
747
  * ```
734
748
  *
@@ -761,16 +775,16 @@ export declare function derive<T>(func: () => T): ValueRef<T>;
761
775
  *
762
776
  * import A from 'aberdeen';
763
777
  *
764
- * const runTime = A.proxy(0);
765
- * setInterval(() => runTime.value++, 1000);
778
+ * const $runTime = A.proxy(0);
779
+ * setInterval(() => $runTime.value++, 1000);
766
780
  *
767
781
  * A.mount(document.getElementById('app-root'), () => {
768
782
  * A('h4#Aberdeen App');
769
- * A(`p#Run time: ${runTime.value}s`);
783
+ * A(`p#Run time: ${$runTime.value}s`);
770
784
  * // Conditionally render some content somewhere else in the static page
771
- * if (runTime.value&1) {
785
+ * if ($runTime.value&1) {
772
786
  * A.mount(document.getElementById('title-extra'), () =>
773
- * A(`i#(${runTime.value}s)`)
787
+ * A(`i#(${$runTime.value}s)`)
774
788
  * );
775
789
  * }
776
790
  * });
@@ -801,14 +815,14 @@ export declare function unmountAll(): void;
801
815
  *
802
816
  * @example Peeking within observer
803
817
  * ```typescript
804
- * const data = A.proxy({ a: 1, b: 2 });
818
+ * const $data = A.proxy({ a: 1, b: 2 });
805
819
  * A(() => {
806
- * // re-executes only when data.a changes, because data.b is peeked.
807
- * const b = A.peek(() => data.b);
808
- * console.log(`A is ${data.a}, B was ${b} when A changed.`);
820
+ * // re-executes only when $data.a changes, because $data.b is peeked.
821
+ * const b = A.peek(() => $data.b);
822
+ * console.log(`A is ${$data.a}, B was ${b} when A changed.`);
809
823
  * });
810
- * data.b = 3; // Does not trigger console.log
811
- * data.a = 2; // Triggers console.log (logs "A is 2, B was 3 when A changed.")
824
+ * $data.b = 3; // Does not trigger console.log
825
+ * $data.a = 2; // Triggers console.log (logs "A is 2, B was 3 when A changed.")
812
826
  * ```
813
827
  *
814
828
  */
@@ -855,16 +869,16 @@ export declare function partition<IN_K extends string | number | symbol, OUT_K e
855
869
  * ```typescript
856
870
  * import A from 'aberdeen';
857
871
  *
858
- * const state = A.proxy({
872
+ * const $state = A.proxy({
859
873
  * user: { name: 'Frank', kids: 1 },
860
874
  * items: ['a', 'b']
861
875
  * });
862
876
  *
863
877
  * A('h2#Live State Dump');
864
- * A.dump(state);
878
+ * A.dump($state);
865
879
  *
866
880
  * // Change state later, the dump in the DOM will update
867
- * setTimeout(() => { state.user.kids++; state.items.push('c'); }, 2000);
881
+ * setTimeout(() => { $state.user.kids++; $state.items.push('c'); }, 2000);
868
882
  * ```
869
883
  */
870
884
  export declare function dump<T>(data: T): T;
@@ -877,10 +891,10 @@ export declare function dump<T>(data: T): T;
877
891
  * ```typescript
878
892
  * import A from 'aberdeen';
879
893
  *
880
- * const state = A.proxy({ count: 0 });
894
+ * const $state = A.proxy({ count: 0 });
881
895
  * A('div', () => {
882
- * A(`p#Count: ${state.count}`);
883
- * A('button text=+ click=', () => state.count++);
896
+ * A(`p#Count: ${$state.count}`);
897
+ * A('button text=+ click=', () => $state.count++);
884
898
  * });
885
899
  * ```
886
900
  */
@@ -1533,6 +1533,7 @@ var KEBAB_SEGMENT = /-([a-z])/g;
1533
1533
  function toCamel(p) {
1534
1534
  return p.replace(KEBAB_SEGMENT, (_, l) => l.toUpperCase());
1535
1535
  }
1536
+ var VALID_CSS_KEY = /^[a-zA-Z-]+$/;
1536
1537
  function styleStringToCss(styleStr, selector) {
1537
1538
  let props = "";
1538
1539
  for (let pos = 0, len = styleStr.length;pos < len; ) {
@@ -1542,8 +1543,10 @@ function styleStringToCss(styleStr, selector) {
1542
1543
  break;
1543
1544
  const colon = styleStr.indexOf(":", pos);
1544
1545
  if (colon === -1)
1545
- break;
1546
+ throw new Error(`Trailing data in style string: "${styleStr.substring(pos)}"`);
1546
1547
  const key = styleStr.substring(pos, colon);
1548
+ if (!VALID_CSS_KEY.test(key))
1549
+ throw new Error(`Invalid CSS key: "${key}" in style string: "${styleStr}"`);
1547
1550
  pos = colon + 1;
1548
1551
  let val;
1549
1552
  if (styleStr[pos] === " ") {
@@ -1868,5 +1871,5 @@ export {
1868
1871
  A
1869
1872
  };
1870
1873
 
1871
- //# debugId=CEB17464CFEBA2F464756E2164756E21
1874
+ //# debugId=C25FF24CC098C9BC64756E2164756E21
1872
1875
  //# sourceMappingURL=aberdeen.js.map