@blaze-ng/observe-sequence
Efficient reactive list observation and diffing. Tracks changes to arrays and database cursors, emitting granular added/removed/moved/changed callbacks.
Installation
npm install @blaze-ng/observe-sequenceObserveSequence
observe()
Watch a reactive data source and receive granular change callbacks.
ObserveSequence.observe(
sequenceFunc: () => unknown,
callbacks: SequenceCallbacks
): ObserveHandle;Parameters:
sequenceFunc— Reactive function returning an array, cursor, or single valuecallbacks— Object with change notification handlers
Returns: Handle with a stop() method
import { ObserveSequence } from '@blaze-ng/observe-sequence';
const handle = ObserveSequence.observe(() => Items.find({}, { sort: { position: 1 } }), {
addedAt(id, item, index, beforeId) {
// Insert DOM node at index
},
removedAt(id, item, index) {
// Remove DOM node at index
},
movedTo(id, item, fromIndex, toIndex, beforeId) {
// Move DOM node
},
changedAt(id, newItem, oldItem, index) {
// Update DOM node
},
});
// Stop observing
handle.stop();Callback Types
interface SequenceCallbacks {
/** An item was added at the given index */
addedAt(id: string, item: unknown, index: number, beforeId?: string): void;
/** An item was removed from the given index */
removedAt(id: string, item: unknown, index: number): void;
/** An item was moved from one index to another */
movedTo(id: string, item: unknown, fromIndex: number, toIndex: number, beforeId?: string): void;
/** An item at the given index was changed */
changedAt(id: string, newItem: unknown, oldItem: unknown, index: number): void;
}Diff Algorithm
diffQueryOrderedChanges()
Compute the minimal set of operations to transform one ordered sequence into another.
function diffQueryOrderedChanges(
oldResults: Map<string, unknown>,
newResults: Map<string, unknown>,
callbacks: DiffCallbacks,
): void;Uses an O(n+m) algorithm based on identity tracking:
- Build a map of items by
_id - Detect new items (insertions)
- Detect removed items (deletions)
- Detect position changes (moves)
- Detect content changes
DiffCallbacks
interface DiffCallbacks {
addedBefore?(id: string, item: unknown, beforeId?: string): void;
removed?(id: string, item: unknown): void;
movedBefore?(id: string, beforeId?: string): void;
changed?(id: string, newFields: object): void;
}ID Utilities
idStringify()
Convert an ID value to a string for map keys.
function idStringify(id: unknown): string;Handles strings, numbers, ObjectID instances, and null:
idStringify('abc'); // => '"abc"'
idStringify(123); // => '123'
idStringify(null); // => 'null'idParse()
Parse a stringified ID back to its original type.
function idParse(idString: string): unknown;idParse('"abc"'); // => 'abc'
idParse('123'); // => 123
idParse('null'); // => nullSupported Data Sources
ObserveSequence works with:
- Arrays — plain JavaScript arrays with optional
_idfields - Cursors — objects with
observe()andfetch()methods (Mongo cursors) - Single values — wrapped as a single-element array
- Null/undefined — treated as empty array
Array Items
When observing arrays, items are tracked by _id if present, or by index:
// Tracked by _id (efficient)
const items = [
{ _id: '1', name: 'First' },
{ _id: '2', name: 'Second' },
];
// Tracked by index (less efficient for reordering)
const items = ['apple', 'banana', 'cherry'];Cursor Support
Objects that implement the cursor interface:
interface StoreCursor {
observe(callbacks: CursorCallbacks): { stop(): void };
fetch(): unknown[];
}This is compatible with Meteor's Mongo.Cursor.
How Blaze Uses ObserveSequence
The each block helper uses ObserveSequence internally:
↓ Blaze internally calls:
ObserveSequence.observe(
() => view.lookup('items'), // reactive data source
{
addedAt(id, item, index) {
// Create a new View for this item
// Render it at the correct position
},
removedAt(id, item, index) {
// Destroy the View for this item
// Remove its DOM nodes
},
movedTo(id, item, fromIndex, toIndex) {
// Move the View's DOM nodes
},
changedAt(id, newItem, oldItem, index) {
// Update the View's data context
// Reactive helpers re-run automatically
},
}
);This is why Blaze can efficiently update lists of thousands of items.