aberdeen 1.0.13 → 1.2.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.
package/README.md CHANGED
@@ -11,8 +11,8 @@ Aberdeen's approach is refreshingly simple:
11
11
  - 🎩 **Simple:** Express UIs naturally in JavaScript/TypeScript, without build steps or JSX, and with a minimal amount of concepts you need to learn.
12
12
  - ⏩ **Fast:** No virtual DOM. Aberdeen intelligently updates only the minimal, necessary parts of your UI when proxied data changes.
13
13
  - 👥 **Awesome lists**: It's very easy and performant to reactively display data sorted by whatever you like.
14
- - 🔬 **Tiny:** Around 6KB (minimized and gzipped) and with zero runtime dependencies.
15
- - 🔋 **Batteries included**: Comes with client-side routing, revertible patches for optimistic user-interface updates, component-local CSS, SVG support, helper functions for transforming reactive data (mapping, partitioning, filtering, etc) and hide/unhide transition effects. No bikeshedding required!
14
+ - 🔬 **Tiny:** Around 6KB (minimized and gzipped) for the core system. Zero runtime dependencies.
15
+ - 🔋 **Batteries included**: Comes with browser history management, routing, revertible patches for optimistic user-interface updates, component-local CSS, SVG support, helper functions for transforming reactive data (mapping, partitioning, filtering, etc) and hide/unhide transition effects. No bikeshedding required!
16
16
 
17
17
  ## Why *not* use Aberdeen?
18
18
 
@@ -31,14 +31,14 @@ const state = proxy({question: "How many roads must a man walk down?", answer: 4
31
31
 
32
32
  $('h3', () => {
33
33
  // This function reruns whenever the question or the answer changes
34
- $(`:${state.question} ↪ ${state.answer || 'Blowing in the wind'}`)
34
+ $('text=', `${state.question} ↪ ${state.answer || 'Blowing in the wind'}`)
35
35
  });
36
36
 
37
37
  // Two-way bind state.question to an <input>
38
- $('input', {placeholder: 'Question', bind: ref(state, 'question')})
38
+ $('input placeholder=Question bind=', ref(state, 'question'))
39
39
 
40
40
  // Allow state.answer to be modified using both an <input> and buttons
41
- $('div.row', {$marginTop: '1em'}, () => {
41
+ $('div.row $marginTop=1em', () => {
42
42
  $('button:-', {click: () => state.answer--});
43
43
  $('input', {type: 'number', bind: ref(state, 'answer')})
44
44
  $('button:+', {click: () => state.answer++});
@@ -57,7 +57,7 @@ Okay, next up is a somewhat more complex app - a todo-list with the following be
57
57
  Pfew.. now let's look at the code:
58
58
 
59
59
  ```typescript
60
- import {$, proxy, onEach, insertCss, peek, observe, unproxy, ref} from "aberdeen";
60
+ import {$, proxy, onEach, insertCss, peek, unproxy, ref} from "aberdeen";
61
61
  import {grow, shrink} from "aberdeen/transitions";
62
62
 
63
63
  // We'll use a simple class to store our data.
@@ -172,6 +172,49 @@ Some further examples:
172
172
 
173
173
  And you may want to study the examples above, of course!
174
174
 
175
- ## News
175
+ ## Changelog
176
176
 
177
- - **2025-05-07**: After five years of working on this library on and off, I'm finally happy with its API and the developer experience it offers. I'm calling it 1.0! To celebrate, I've created some pretty fancy (if I may say so myself) interactive documentation and a tutorial.
177
+ ### 1.2.0 (2025-09-27)
178
+
179
+ **Enhancements:**
180
+ - The `$` function now supports a more concise syntax for setting attributes and properties. Instead of writing `$('p', 'button', {$color: 'red', click: () => ...})`, you can now write `$('p button $color=red click=', () => ...)`.
181
+ - The `proxy()` function can now accept `Promise`s, which will return an observable object with properties for `busy` status, `error` (if any), and the resolved `value`. This makes it easier to call async functions from within UI code.
182
+
183
+ **Breaking changes:**
184
+ - When a UI render function returns a `Promise`, that will now be reported as an error. Async render functions are fundamentally incompatible with Aberdeen's reactive model, so it's helpful to point that out. Use the new `proxy()` async support instead.
185
+
186
+ ### 1.1.0 (2025-09-12)
187
+
188
+ This major release aims to reduce surprises in our API, aligning more closely with regular JavaScript semantics (for better or worse).
189
+
190
+ **Breaking changes:**
191
+
192
+ - Functions that iterate objects (like `onEach` and `map`) will now only work on *own* properties of the object, ignoring those in the prototype chain. The new behavior should be more consistent and faster.
193
+ - These iteration function now properly distinguish between `undefined` and *empty*. Previously, object/array/map items with `undefined` values were considered non-existent. The new behavior (though arguably confusing) is more consistent with regular JavaScript semantics.
194
+ - The `copy` function no longer ..
195
+ - Supports `SHALLOW` and `MERGE` flags. The latter has been replaced by a dedicated `merge` function. The former turned out not to be particularly useful.
196
+ - Has weird special cases that would allow copying objects into maps and merging objects into arrays.
197
+ - Copies properties from the prototype chain of objects. Only *own* properties are copied now. As the prototype link itself *is* copied over, this should actually result in copies being *more* similar to the original.
198
+ - The `observe` function has been renamed to `derive` to better reflect its purpose and match terminology used in other reactive programming libraries.
199
+ - The `$({element: myElement})` syntax for inserting existing DOM elements has been removed. Use `$(myElement)` instead.
200
+ - The `route` API brings some significant changes. Modifying the `route` observable (which should now be accessed as `route.current`) will now always result in changing the current browser history item (URL and state, using `replaceState`), instead of using a heuristic to figure out what you probably want. Dedicated functions have been added for navigating to a new URL (`go`), back to a previous URL (`back`), and for going up in the route hierarchy (`up`).
201
+ - The concept of immediate observers (through the `immediateObserve` function) no longer exists. It caused unexpected behavior (for instance due to the fact that an array `pop()` in JavaScript is implemented as a delete followed by a length change, so happens in two steps that would each call immediate observers). The reason it existed was mostly to enable a pre-1.0 version of the `route` API. It turned out to be a mistake.
202
+
203
+ **Enhancements:**
204
+
205
+ - The `peek` function can no also accept an object and a key as argument (e.g. `peek(obj, 'myKey')`). It does the same as `peek(() => obj.myKey)`, but more concise and faster.
206
+ - The `copy` and `merge` functions now ..
207
+ - Accept an optional `dstKey` argument, allowing you to assign to a specific key with `copy` semantics, and without subscribing to the key.
208
+ - Return a boolean indicating whether any changes were made.
209
+ - Are faster.
210
+ - A new `dispatcher` module has been added. It provides a simple and type-safe way to match URL paths to handler functions, and extract parameters from the path. You can still use your own routing solution if you prefer, of course.
211
+ - The `route` module now also has tests, making the whole project now fully covered by tests.
212
+
213
+ **Fixes:**
214
+
215
+ - Browser-back behavior in the `route` module had some reliability issues after page reloads.
216
+ - The `copy` and `clone` function created Maps and Arrays with the wrong internal type. So `instanceof Array` would say yes, while `Array.isArray` would say no. JavaScript is weird.
217
+
218
+ ### 1.0.0 (2025-05-07)
219
+
220
+ After five years of working on this library on and off, I'm finally happy with its API and the developer experience it offers. I'm calling it 1.0! To celebrate, I've created some pretty fancy (if I may say so myself) interactive documentation and a tutorial.
@@ -70,7 +70,7 @@ export declare function onEach<K extends string | number | symbol, T>(target: Re
70
70
  * is deleted from an object), the scope that called `isEmpty` will be automatically
71
71
  * scheduled for re-evaluation.
72
72
  *
73
- * @param proxied The observable array or object (obtained via `observe()`) to check.
73
+ * @param proxied The observable array or object to check.
74
74
  * @returns `true` if the array has length 0 or the object has no own enumerable properties, `false` otherwise.
75
75
  *
76
76
  * @example
@@ -113,8 +113,8 @@ export interface ValueRef<T> {
113
113
  * $('div', {text: cnt});
114
114
  * // <div>2</div>
115
115
 
116
- * // Or we can use it in an {@link observe} function:
117
- * observe(() => console.log("The count is now", cnt.value));
116
+ * // Or we can use it in an {@link derive} function:
117
+ * $(() => console.log("The count is now", cnt.value));
118
118
  * // The count is now 2
119
119
  *
120
120
  * // Adding/removing items will update the count
@@ -125,6 +125,12 @@ export interface ValueRef<T> {
125
125
  * ```
126
126
  */
127
127
  export declare function count(proxied: TargetType): ValueRef<number>;
128
+ interface PromiseProxy<T> {
129
+ busy: boolean;
130
+ error?: any;
131
+ value?: T;
132
+ }
133
+ export declare function proxy<T extends any>(target: Promise<T>): PromiseProxy<T>;
128
134
  export declare function proxy<T extends any>(target: Array<T>): Array<T extends number ? number : T extends string ? string : T extends boolean ? boolean : T>;
129
135
  export declare function proxy<T extends object>(target: T): T;
130
136
  export declare function proxy<T extends any>(target: T): ValueRef<T extends number ? number : T extends string ? string : T extends boolean ? boolean : T>;
@@ -178,12 +184,9 @@ export declare function unproxy<T>(target: T): T;
178
184
  *
179
185
  * @param dst - The destination object/array/Map (proxied or unproxied).
180
186
  * @param src - The source object/array/Map (proxied or unproxied). It won't be modified.
181
- * @param flags - Bitmask controlling copy behavior:
182
- * - {@link MERGE}: Performs a partial update. Properties in `dst` not present in `src` are kept.
183
- * `null`/`undefined` in `src` delete properties in `dst`. Handles partial array updates via object keys.
184
- * - {@link SHALLOW}: Performs a shallow copy; when an array/object of the right type doesn't exist in `dst` yet, a reference to the array/object in `src` will be made, instead of creating a copy. If the array/object already exists, it won't be replaced (by a reference), but all items will be individually checked and copied like normal, keeping changes (and therefore UI updates) to a minimum.
185
- * @template T - The type of the destination object.
186
- * @throws Error if attempting to copy an array into a non-array or vice versa (unless {@link MERGE} is set, allowing for sparse array updates).
187
+ * @template T - The type of the objects being copied.
188
+ * @returns `true` if any changes were made to `dst`, or `false` if not.
189
+ * @throws Error if attempting to copy an array into a non-array or vice versa.
187
190
  *
188
191
  * @example Basic Copy
189
192
  * ```typescript
@@ -191,67 +194,53 @@ export declare function unproxy<T>(target: T): T;
191
194
  * const dest = proxy({ b: { d: 3 } });
192
195
  * copy(dest, source);
193
196
  * console.log(dest); // proxy({ a: 1, b: { c: 2 } })
197
+ * copy(dest, 'b', { e: 4 });
198
+ * console.log(dest); // proxy({ a: 1, b: { e: 4 } })
194
199
  * ```
200
+ */
201
+ export declare function copy<T extends object>(dst: T, src: T): boolean;
202
+ /**
203
+ * Like above, but copies `src` into `dst[dstKey]`. This is useful if you're unsure if dst[dstKey]
204
+ * already exists (as the right type of object) or if you don't want to subscribe to dst[dstKey].
195
205
  *
196
- * @example Map to Object
197
- * ```typescript
198
- * const source = new Map([['x', 3], ['y', 4]]);
199
- * const dest = proxy({});
200
- * copy(dest, source);
201
- * console.log(dest); // proxy({ x: 3, y: 4 })
202
- * ```
206
+ * @param dstKey - Optional key in `dst` to copy into.
207
+ */
208
+ export declare function copy<T extends object>(dst: T, dstKey: keyof T, src: T[typeof dstKey]): boolean;
209
+ /**
210
+ * Like {@link copy}, but uses merge semantics. Properties in `dst` not present in `src` are kept.
211
+ * `null`/`undefined` in `src` delete properties in `dst`.
203
212
  *
204
- * @example Object to Map
205
- * ```typescript
206
- * const source = { x: 3, y: 4 };
207
- * const dest = proxy(new Map());
208
- * copy(dest, source);
209
- * console.log(dest); // proxy(Map([['x', 3], ['y', 4]]))
210
- * ```
213
+ * When the destination is an object and the source is an array, its keys are used as (sparse) array indices.
211
214
  *
212
- * @example MERGE
215
+ * @example Basic merge
213
216
  * ```typescript
214
217
  * const source = { b: { c: 99 }, d: undefined }; // d: undefined will delete
215
218
  * const dest = proxy({ a: 1, b: { x: 5 }, d: 4 });
216
- * copy(dest, source, MERGE);
217
- * console.log(dest); // proxy({ a: 1, b: { c: 99, x: 5 } })
219
+ * merge(dest, source);
220
+ * merge(dest, 'b', { y: 6 }); // merge into dest.b
221
+ * merge(dest, 'c', { z: 7 }); // merge.c doesn't exist yet, so it will just be assigned
222
+ * console.log(dest); // proxy({ a: 1, b: { c: 99, x: 5, y: 6 }, c: { z: 7 } })
218
223
  * ```
219
224
  *
220
- * @example Partial Array Update with MERGE
225
+ * @example Partial Array Merge
221
226
  * ```typescript
222
227
  * const messages = proxy(['msg1', 'msg2', 'msg3']);
223
228
  * const update = { 1: 'updated msg2' }; // Update using object key as index
224
- * copy(messages, update, MERGE);
229
+ * merge(messages, update);
225
230
  * console.log(messages); // proxy(['msg1', 'updated msg2', 'msg3'])
226
231
  * ```
227
232
  *
228
- * @example SHALLOW
229
- * ```typescript
230
- * const source = { nested: [1, 2] };
231
- * const dest = {};
232
- * copy(dest, source, SHALLOW);
233
- * dest.nested.push(3);
234
- * console.log(source.nested); // [1, 2, 3] (source was modified)
235
- * ```
236
233
  */
237
- export declare function copy<K, V>(dst: Map<K, V>, src: Record<K extends string | number | symbol ? K : never, V> | Partial<Record<K extends string | number | symbol ? K : never, V>>, flags?: number): void;
238
- export declare function copy<K, V>(dst: Map<K, V>, src: Map<K, V> | Partial<Map<K, V>>, flags?: number): void;
239
- export declare function copy<T extends Record<string | number | symbol, any>>(dst: T, src: Map<keyof T, T[keyof T]>, flags?: number): void;
240
- export declare function copy<T extends object>(dst: T, src: Partial<T>, flags?: number): void;
241
- /** Flag to {@link copy} causing it to use merge semantics. See {@link copy} for details. */
242
- export declare const MERGE = 1;
243
- /** Flag to {@link copy} and {@link clone} causing them to create a shallow copy (instead of the deep copy done by default).*/
244
- export declare const SHALLOW = 2;
234
+ export declare function merge<T extends object>(dst: T, value: Partial<T>): boolean;
235
+ export declare function merge<T extends object>(dst: T, dstKey: keyof T, value: Partial<T[typeof dstKey]>): boolean;
245
236
  /**
246
237
  * Clone an (optionally proxied) object or array.
247
238
  *
248
239
  * @param src The object or array to clone. If it is proxied, `clone` will subscribe to any changes to the (nested) data structure.
249
- * @param flags
250
- * - {@link SHALLOW}: Performs a shallow clone, meaning that only the top-level array or object will be copied, while object/array values will just be references to the original data in `src`.
251
240
  * @template T - The type of the objects being copied.
252
- * @returns A new unproxied array or object (of the same type as `src`), containing a deep (by default) copy of `src`.
241
+ * @returns A new unproxied array or object (of the same type as `src`), containing a deep copy of `src`.
253
242
  */
254
- export declare function clone<T extends object>(src: T, flags?: number): T;
243
+ export declare function clone<T extends object>(src: T): T;
255
244
  /**
256
245
  * Creates a reactive reference (`{ value: T }`-like object) to a specific value
257
246
  * within a proxied object or array.
@@ -296,12 +285,14 @@ export declare function ref<T extends TargetType, K extends keyof T>(target: T,
296
285
  * @param {...(string | function | object | false | undefined | null)} args - Any number of arguments can be given. How they're interpreted depends on their types:
297
286
  *
298
287
  * - `string`: Strings can be used to create and insert new elements, set classnames for the *current* element, and add text to the current element.
299
- * The format of a string is: **tag**? (`.` **class**)* (':' **text**)?
300
- * meaning it consists of...
301
- * - An optional HTML **tag**, something like `h1`. If present, a DOM element of that tag is created, and that element will be the *current* element for the rest of this `$` function execution.
302
- * - Any number of CSS classes prefixed by `.` characters. These classes will be added to the *current* element.
303
- * - Optional content **text** prefixed by a `:` character, ranging til the end of the string. This will be added as a TextNode to the *current* element.
304
- * - `function`: When a function (without argument nor a return value) is passed in, it will be reactively executed in its own observe scope, preserving the *current element*. So any `$()` invocations within this function will create DOM elements with our *current* element as parent. If the function reads observable data, and that data is changed later on, the function we re-execute (after side effects, such as DOM modifications through `$`, have been cleaned - see also {@link clean}).
288
+ * The format of a string is: (**tag** | `.` **class** | **key**=**val** | **key**="**long val**")* (':' **text** | **key**=)?
289
+ * So there can be:
290
+ * - Any number of **tag** element, like `h1` or `div`. These elements are created, added to the *current* element, and become the new *current* element for the rest of this `$` function execution.
291
+ * - Any number of CSS classes prefixed by `.` characters. These classes will be added to the *current* element. Optionally, CSS classes can be appended to a **tag** without a space. So both `div.myclass` and `div .myclass` are valid and do the same thing.
292
+ * - Any number of key/value pairs with string values, like `placeholder="Your name"` or `data-id=123`. These will be handled according to the rules specified for `object`, below, but with the caveat that values can only be strings. The quotes around string values are optional, unless the value contains spaces. It's not possible to escape quotes within the value. If you want to do that, or if you have user-provided values, use the `object` syntax (see below) or end your string with `key=` followed by the data as a separate argument (see below).
293
+ * - The string may end in a ':' followed by text, which will be added as a TextNode to the *current* element. The text ranges til the end of the string, and may contain any characters, including spaces and quotes.
294
+ * - Alternatively, the string may end in a key followed by an '=' character, in which case the value is expected as a separate argument. The key/value pair is set according to the rules specified for `object` below. This is useful when the value is not a string or contains spaces or user data. Example: `$('button text="Click me" click=', () => alert('Clicked!'))` or `$('input.value=', someUserData, "placeholder=", "Type your stuff")`.
295
+ * - `function`: When a function (without argument nor a return value) is passed in, it will be reactively executed in its own observer scope, preserving the *current element*. So any `$()` invocations within this function will create DOM elements with our *current* element as parent. If the function reads observable data, and that data is changed later on, the function we re-execute (after side effects, such as DOM modifications through `$`, have been cleaned - see also {@link clean}).
305
296
  * - `object`: When an object is passed in, its key-value pairs are used to modify the *current* element in the following ways...
306
297
  * - `{<attrName>: any}`: The common case is setting the value as an HTML attribute named key. So `{placeholder: "Your name"}` would add `placeholder="Your name"` to the current HTML element.
307
298
  * - `{<propName>: boolean}` or `{value: any}` or `{selectedIndex: number}`: If the value is a boolean, or if the key is `value` or `selectedIndex`, it is set on the `current` element as a DOM property instead of an HTML attribute. For example `{checked: true}` would do `el.checked = true` for the *current* element.
@@ -312,7 +303,7 @@ export declare function ref<T extends TargetType, K extends keyof T>(target: T,
312
303
  * - `{destroy: string}`: When the *current* element is a top-level element to be removed (due to reactivity cleanup), actual removal from the DOM is delayed by 2 seconds, and in the mean time the value string is added as a CSS class to the element, allowing for a deletion transition. The string may also contain multiple dot-separated CSS classes, such as `.fade.shrink`.
313
304
  * - `{create: function}` and `{destroy: function}`: The function is invoked when the *current* element is the top-level element being created/destroyed. It can be used for more involved creation/deletion animations. In case of `destroy`, the function is responsible for actually removing the element from the DOM (eventually). See `transitions.ts` in the Aberdeen source code for some examples.
314
305
  * - `{bind: <obsValue>}`: Create a two-way binding element between the `value` property of the given observable (proxy) variable, and the *current* input element (`<input>`, `<select>` or `<textarea>`). This is often used together with {@link ref}, in order to use properties other than `.value`.
315
- * - `{<any>: <obsvalue>}`: Create a new observe scope and read the `value` property of the given observable (proxy) variable from within it, and apply the contained value using any of the other rules in this list. Example:
306
+ * - `{<any>: <obsvalue>}`: Create a new observer scope and read the `value` property of the given observable (proxy) variable from within it, and apply the contained value using any of the other rules in this list. Example:
316
307
  * ```typescript
317
308
  * const myColor = proxy('red');
318
309
  * $('p:Test', {$color: myColor, click: () => myColor.value = 'yellow'})
@@ -335,6 +326,14 @@ export declare function ref<T extends TargetType, K extends keyof T>(target: T,
335
326
  * });
336
327
  * ```
337
328
  *
329
+ * Which can also be written as:
330
+ * ```typescript
331
+ * $('button.secondary.outline text=Submit $color=red disabled=', false, 'click=', () => console.log('Clicked!'));
332
+ * ```
333
+ *
334
+ * We want to set `disabled` as a property instead of an attribute, so we must use the `key=` syntax in order to provide
335
+ * `false` as a boolean instead of a string.
336
+ *
338
337
  * @example Create Nested Elements
339
338
  * ```typescript
340
339
  * let inputElement: Element = $('label:Click me', 'input', {type: 'checkbox'});
@@ -435,7 +434,7 @@ export declare function insertCss(style: object, global?: boolean): string;
435
434
  /**
436
435
  * Sets a custom error handler function for errors that occur asynchronously
437
436
  * within reactive scopes (e.g., during updates triggered by proxy changes in
438
- * {@link observe} or {@link $} render functions).
437
+ * {@link derive} or {@link $} render functions).
439
438
  *
440
439
  * The default handler logs the error to `console.error` and adds a simple
441
440
  * 'Error' message div to the DOM at the location where the error occurred (if possible).
@@ -515,7 +514,7 @@ export declare function getParentElement(): Element;
515
514
  * This is useful for releasing resources, removing manual event listeners, or cleaning up
516
515
  * side effects associated with the scope. Cleaners are run in reverse order of registration.
517
516
  *
518
- * Scopes are created by functions like {@link observe}, {@link mount}, {@link $} (when given a render function),
517
+ * Scopes are created by functions like {@link derive}, {@link mount}, {@link $} (when given a render function),
519
518
  * and internally by constructs like {@link onEach}.
520
519
  *
521
520
  * @param cleaner - The function to execute during cleanup.
@@ -533,7 +532,7 @@ export declare function getParentElement(): Element;
533
532
  * peek(() => sum.value += item);
534
533
  * // Clean gets called before each rerun for a certain item index
535
534
  * // No need for peek here, as the clean code doesn't run in an
536
- * // observe scope.
535
+ * // observer scope.
537
536
  * clean(() => sum.value -= item);
538
537
  * })
539
538
  *
@@ -557,7 +556,7 @@ export declare function clean(cleaner: () => void): void;
557
556
  *
558
557
  * @param func - The function to execute reactively. Any DOM manipulations should typically
559
558
  * be done using {@link $} within this function. Its return value will be made available as an
560
- * observable returned by the `observe()` function.
559
+ * observable returned by the `derive()` function.
561
560
  * @returns An observable object, with its `value` property containing whatever the last run of `func` returned.
562
561
  *
563
562
  * @example Observation creating a UI components
@@ -568,7 +567,7 @@ export declare function clean(cleaner: () => void): void;
568
567
  * console.log('Welcome');
569
568
  * $('h3:Welcome, ' + data.user); // Reactive text
570
569
  *
571
- * observe(() => {
570
+ * derive(() => {
572
571
  * // When data.notifications changes, only this inner scope reruns,
573
572
  * // leaving the `<p>Welcome, ..</p>` untouched.
574
573
  * console.log('Notifications');
@@ -578,13 +577,13 @@ export declare function clean(cleaner: () => void): void;
578
577
  * });
579
578
  * ```
580
579
  *
581
- * ***Note*** that the above could just as easily be done using `$(func)` instead of `observe(func)`.
580
+ * ***Note*** that the above could just as easily be done using `$(func)` instead of `derive(func)`.
582
581
  *
583
582
  * @example Observation with return value
584
583
  * ```typescript
585
584
  * const counter = proxy(0);
586
585
  * setInterval(() => counter.value++, 1000);
587
- * const double = observe(() => counter.value * 2);
586
+ * const double = derive(() => counter.value * 2);
588
587
  *
589
588
  * $('h3', () => {
590
589
  * $(`:counter=${counter.value} double=${double.value}`);
@@ -594,36 +593,7 @@ export declare function clean(cleaner: () => void): void;
594
593
  * @overload
595
594
  * @param func Func without a return value.
596
595
  */
597
- export declare function observe<T>(func: () => T): ValueRef<T>;
598
- /**
599
- * Similar to {@link observe}, creates a reactive scope that re-executes the function
600
- * when its proxied dependencies change.
601
- *
602
- * **Difference:** Updates run **synchronously and immediately** after the proxy modification
603
- * that triggered the update occurs.
604
- *
605
- * **Caution:** Use sparingly. Immediate execution bypasses Aberdeen's usual batching and
606
- * ordering optimizations, which can lead to performance issues or observing inconsistent
607
- * intermediate states if multiple related updates are applied sequentially.
608
- * Prefer {@link observe} or {@link $} for most use cases.
609
- *
610
- * @param func - The function to execute reactively and synchronously.
611
- *
612
- * @example
613
- * ```javascript
614
- * const state = proxy({ single: 'A' });
615
- *
616
- * immediateObserve(() => {
617
- * state.double = state.single + state.single
618
- * });
619
- * console.log(state.double); // 'AA'
620
- *
621
- * state.single = 'B';
622
- * // Synchronously:
623
- * console.log(state.double); // 'BB'
624
- * ```
625
- */
626
- export declare function immediateObserve(func: () => void): void;
596
+ export declare function derive<T>(func: () => T): ValueRef<T>;
627
597
  /**
628
598
  * Attaches a reactive Aberdeen UI fragment to an existing DOM element. Without the use of
629
599
  * this function, {@link $} will assume `document.body` as its root.
@@ -633,11 +603,11 @@ export declare function immediateObserve(func: () => void): void;
633
603
  * will cause it to re-execute when the data changes, updating the DOM elements created within it.
634
604
  *
635
605
  * Calls to {@link $} inside `func` will append nodes to `parentElement`.
636
- * You can nest {@link observe} or other {@link $} scopes within `func`.
606
+ * You can nest {@link derive} or other {@link $} scopes within `func`.
637
607
  * Use {@link unmountAll} to clean up all mounted scopes and their DOM nodes.
638
608
  *
639
609
  * Mounting scopes happens reactively, meaning that if this function is called from within another
640
- * ({@link observe} or {@link $} or {@link mount}) scope that gets cleaned up, so will the mount.
610
+ * ({@link derive} or {@link $} or {@link mount}) scope that gets cleaned up, so will the mount.
641
611
  *
642
612
  * @param parentElement - The native DOM `Element` to which the UI fragment will be appended.
643
613
  * @param func - The function that defines the UI fragment, typically containing calls to {@link $}.
@@ -669,26 +639,27 @@ export declare function immediateObserve(func: () => void): void;
669
639
  export declare function mount(parentElement: Element, func: () => void): void;
670
640
  /**
671
641
  * Removes all Aberdeen-managed DOM nodes and stops all active reactive scopes
672
- * (created by {@link mount}, {@link observe}, {@link $} with functions, etc.).
642
+ * (created by {@link mount}, {@link derive}, {@link $} with functions, etc.).
673
643
  *
674
644
  * This effectively cleans up the entire Aberdeen application state.
675
645
  */
676
646
  export declare function unmountAll(): void;
677
647
  /**
678
- * Executes a function *without* creating subscriptions in the current reactive scope, and returns its result.
648
+ * Executes a function or retrieves a value *without* creating subscriptions in the current reactive scope, and returns its result.
679
649
  *
680
- * This is useful when you need to access reactive data inside a reactive scope (like {@link observe})
650
+ * This is useful when you need to access reactive data inside a reactive scope (like {@link $})
681
651
  * but do not want changes to that specific data to trigger a re-execute of the scope.
682
652
  *
683
- * @template T The type of the return value of your function.
653
+ * Note: You may also use {@link unproxy} to get to the raw underlying data structure, which can be used to similar effect.
684
654
  *
685
- * @param func - The function to execute without creating subscriptions.
686
- * @returns Whatever `func` returns.
655
+ * @param target - Either a function to execute, or an object (which may also be an Array or a Map) to index.
656
+ * @param key - Optional key/index to use when `target` is an object.
657
+ * @returns The result of the function call, or the value at `target[key]` when `target` is an object or `target.get(key)` when it's a Map.
687
658
  *
688
- * @example Peeking within observe
659
+ * @example Peeking within observer
689
660
  * ```typescript
690
661
  * const data = proxy({ a: 1, b: 2 });
691
- * observe(() => {
662
+ * $(() => {
692
663
  * // re-executes only when data.a changes, because data.b is peeked.
693
664
  * const b = peek(() => data.b);
694
665
  * console.log(`A is ${data.a}, B was ${b} when A changed.`);
@@ -698,13 +669,16 @@ export declare function unmountAll(): void;
698
669
  * ```
699
670
  *
700
671
  */
701
- export declare function peek<T>(func: () => T): T;
672
+ export declare function peek<T extends object>(target: T, key: keyof T): T[typeof key];
673
+ export declare function peek<K, V>(target: Map<K, V>, key: K): V | undefined;
674
+ export declare function peek<T>(target: T[], key: number): T | undefined;
675
+ export declare function peek<T>(target: () => T): T;
702
676
  /** When using a Map as `source`. */
703
677
  export declare function map<K, IN, OUT>(source: Map<K, IN>, func: (value: IN, key: K) => undefined | OUT): Map<K, OUT>;
704
- /** When using an object as `source`. */
705
- export declare function map<IN, const IN_KEY extends string | number | symbol, OUT>(source: Record<IN_KEY, IN>, func: (value: IN, index: KeyToString<IN_KEY>) => undefined | OUT): Record<string | symbol, OUT>;
706
678
  /** When using an array as `source`. */
707
679
  export declare function map<IN, OUT>(source: Array<IN>, func: (value: IN, index: number) => undefined | OUT): Array<OUT>;
680
+ /** When using an object as `source`. */
681
+ export declare function map<IN, const IN_KEY extends string | number | symbol, OUT>(source: Record<IN_KEY, IN>, func: (value: IN, index: KeyToString<IN_KEY>) => undefined | OUT): Record<string | symbol, OUT>;
708
682
  /** When using an array as `source`. */
709
683
  export declare function multiMap<IN, OUT extends {
710
684
  [key: string | symbol]: any;
@@ -751,3 +725,4 @@ export declare function partition<IN_K extends string | number | symbol, OUT_K e
751
725
  * ```
752
726
  */
753
727
  export declare function dump<T>(data: T): T;
728
+ export {};