npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@statedelta-apex/list-state

v3.0.0

Published

ApexStore List primitive - ordered list state with dual-end access (queue/stack)

Readme

@statedelta-apex/list-state

List primitive — lista ordenada tipada sem semântica de identidade, acesso dual-end, capacidade opcional, transações aninhadas e imutabilidade por convenção.

Filosofia

O CollectionState gerencia entidades com identidade — cada item tem um ID único, operações são ID-based, duplicatas são proibidas. Perfeito pra users, products, records.

O ListState gerencia valores posicionais — qualquer tipo T, sem restrição de identidade, duplicatas válidas. A ordem é o que importa, não quem é o item. Perfeito pra filas, pilhas, buffers, logs, histórico.

import { createList } from "@statedelta-apex/list-state";

// Strings, números, objetos sem ID, duplicatas — tudo válido
const tasks = createList<string>("tasks", ["process", "validate", "process"]);
const scores = createList<number>("scores", [100, 85, 92]);
const events = createList<{ type: string; data: unknown }>("events", []);

O ListState é agnóstico de padrão de acesso. Expõe ambas as pontas (append/prepend, removeFirst/removeLast) sem impor FIFO ou LIFO. O consumer decide como usar — ou compõe um derived type (Queue, Stack) pra semântica explícita.

Princípios

  • Determinístico — mesmas actions no mesmo estado produzem sempre o mesmo resultado.
  • Imutável por convenção — toda mutação gera uma nova referência via shallow clone. Referências anteriores permanecem intactas.
  • Autossuficiente — funciona standalone ou gerenciado por um Store. Transaction, subscribe, middleware, snapshot — tudo built-in.
  • Zero branches no hot path — o pipeline de mutação usa strategy swap. Quem não usa subscribe/middleware/hook não paga nada.
  • Sem identidade — items são valores posicionais. Duplicatas válidas, primitivos aceitos, sem overhead de ID tracking.

Instalação

pnpm add @statedelta-apex/list-state

Requer @statedelta-apex/store como dependência (instalada automaticamente).

Quick Start

import { createList } from "@statedelta-apex/list-state";

// Criar
const queue = createList<string>("tasks", ["task-A", "task-B"]);

// Mutar
queue.append("task-C");
queue.prepend("urgent-task");
const next = queue.removeFirst(); // "urgent-task"

// Ler
queue.peekFirst(); // "task-A"
queue.peekLast(); // "task-C"
queue.size; // 3

// Reagir
const unsub = queue.subscribe((items, prevItems) => {
  console.log(`Queue: ${prevItems.length} → ${items.length}`);
});

// Transacionar
queue.transaction(() => {
  queue.append("step-1");
  queue.append("step-2");
  // throw aqui → rollback ambos
});

// Simular (what-if)
const wouldOverflow = queue.simulate(() => {
  queue.append("big-payload");
  return queue.size > MAX_QUEUE;
});
// queue intacto

Criação

createList(id, initialItems, config?)

function createList<T>(
  id: string,
  initialItems: T[],
  config?: ListConfig,
): ListState<T>;

| Parâmetro | Tipo | Descrição | | -------------- | ------------ | -------------------------------------- | | id | string | Identificador único do state | | initialItems | T[] | Items iniciais (clonados internamente) | | config | ListConfig | Configuração opcional |

// Default — sem limite de tamanho
const log = createList<Event>("log", []);

// Com capacidade máxima
const buffer = createList<Message>("buffer", [], { maxSize: 100 });

// Strict mode
const safe = createList<string>("safe", [], { strict: true });

Também disponível via new:

import { ListState } from "@statedelta-apex/list-state";

const list = new ListState<string>("tasks", ["a", "b"]);

Actions (mutações)

Toda action produz uma nova referência do array e dispara o pipeline de mutação (hook → middleware → state → listeners). Uma action = um clone = um notify.

append(item)

Adiciona um item no final.

append(item: T): void
queue.append("task-C");
// ["task-A", "task-B"] → ["task-A", "task-B", "task-C"]

appendMany(items)

Batch append — N items numa única mutação. Um clone, um notify. Noop se array vazio.

appendMany(items: T[]): void
queue.appendMany(["task-D", "task-E"]);

prepend(item)

Adiciona um item no início.

prepend(item: T): void
queue.prepend("urgent");
// ["task-A", "task-B"] → ["urgent", "task-A", "task-B"]

prependMany(items)

Batch prepend — N items no início numa única mutação. Noop se array vazio.

prependMany(items: T[]): void

removeFirst()

Remove e retorna o primeiro item. Retorna undefined se vazio (sem mutação).

removeFirst(): T | undefined
const next = queue.removeFirst(); // "task-A"
// ["task-A", "task-B", "task-C"] → ["task-B", "task-C"]

removeLast()

Remove e retorna o último item. Retorna undefined se vazio (sem mutação).

removeLast(): T | undefined
const last = queue.removeLast(); // "task-C"

clear()

Remove todos os items. Noop se já está vazio.

clear(): void

replace(items)

Substitui todos os items. Input é clonado internamente. Valida contra maxSize.

replace(items: T[]): void
queue.replace(["new-A", "new-B"]);

reset()

Restaura os items iniciais (passados na criação). Cada reset produz um clone independente.

reset(): void

Getters (leitura)

Getters são leituras puras. Sem side effects, sem mutações.

peekFirst() / peekLast()

Retorna o primeiro/último item sem remover. undefined se vazio.

peekFirst(): T | undefined
peekLast(): T | undefined
queue.peekFirst(); // olha o início sem tocar
queue.peekLast(); // olha o final sem tocar

at(index)

Acesso posicional. Suporta índices negativos (-1 = último).

at(index: number): T | undefined
queue.at(0); // primeiro
queue.at(-1); // último
queue.at(999); // undefined

find(predicate)

Primeiro item que satisfaz o predicado.

find(predicate: (item: T, index: number) => boolean): T | undefined
const urgent = queue.find((task) => task.startsWith("urgent"));

findIndex(predicate)

Índice do primeiro item que satisfaz o predicado. -1 se não encontrado.

findIndex(predicate: (item: T, index: number) => boolean): number

includes(item)

Verifica se o item existe na lista (por referência/valor).

includes(item: T): boolean

indexOf(item)

Índice do item na lista. -1 se não encontrado.

indexOf(item: T): number

every(predicate)

true se todos os items satisfazem o predicado.

every(predicate: (item: T, index: number) => boolean): boolean

some(predicate)

true se pelo menos um item satisfaz o predicado.

some(predicate: (item: T, index: number) => boolean): boolean

toArray()

Retorna cópia do array (spread). Seguro pra mutar.

toArray(): T[]
const copy = queue.toArray();
copy.push("extra"); // não afeta o state

Properties

| Property | Tipo | Descrição | | --------- | --------- | -------------------------------------------------- | | items | T[] | Referência direta ao array (alias de getState()) | | size | number | Número de items | | isEmpty | boolean | true se zero items | | isFull | boolean | true se size >= maxSize | | maxSize | number | Capacidade máxima (Infinity = sem limite) | | id | string | ID passado na criação | | type | string | "list" |


Estado

getState()

Retorna o array completo. A referência é imutável por convenção — não modifique diretamente.

getState(): T[]
const items = queue.getState(); // ["task-A", "task-B"]

queue.append("task-C");
const newItems = queue.getState();
items !== newItems; // true — referências diferentes
items.length; // 2 — original intacto

Capacidade (maxSize)

Limite opcional de items. Default: Infinity (sem limite).

const buffer = createList<string>("buffer", [], { maxSize: 5 });

buffer.append("a");
buffer.append("b");
buffer.append("c");
buffer.append("d");
buffer.append("e");
buffer.isFull; // true

buffer.append("f"); // ← throw RangeError: would exceed maxSize

O maxSize é validado em todas as operações que aumentam o tamanho (append, appendMany, prepend, prependMany, replace, restore). Operações que reduzem ou mantêm o tamanho nunca falham por capacidade.


Transactions

A List suporta transações aninhadas via stack de snapshots. Cada beginTransaction() empilha o estado atual. rollback() restaura, commitTransaction() descarta o snapshot.

transaction(fn)

Commit automático no sucesso, rollback automático no throw.

transaction<R>(fn: () => R): R
queue.transaction(() => {
  queue.append("step-1");
  queue.append("step-2");
  // throw → rollback ambos
});

simulate(fn)

Sempre rollback. Para cenários "what if?" sem mutar estado.

simulate<R>(fn: () => R): R
const wouldOverflow = buffer.simulate(() => {
  buffer.appendMany(newBatch);
  return buffer.size > threshold;
});
// buffer intacto

Nesting

Transações suportam nesting arbitrário.

queue.transaction(() => {
  queue.append("a");

  const safe = queue.simulate(() => {
    queue.append("danger");
    return queue.every((t) => isValid(t));
  });

  if (!safe) throw new Error("invalid");
});

inTransaction / transactionDepth

readonly inTransaction: boolean   // true se algum nível está ativo
readonly transactionDepth: number // 0 = sem transaction

Subscribe (reatividade)

Registra um listener chamado a cada mutação. Recebe (items, prevItems).

subscribe(listener: (items: T[], prevItems: T[]) => void): () => void

Retorna uma função de unsubscribe.

const unsub = queue.subscribe((items, prev) => {
  console.log(`Queue: ${prev.length} → ${items.length}`);
});

queue.append("task-X");
// log: "Queue: 2 → 3"

unsub();
queue.append("task-Y");
// sem log

Zero overhead sem subscribers

Quando nenhum listener está registrado, o pipeline de mutação usa a strategy sem listeners. Sem iteração, sem checagem.


Middleware

Interceptors de mutação. Podem observar, validar ou bloquear mutações.

use(middleware: (current: T[], next: T[], proceed: (override?: T[]) => void) => void): () => void

Retorna uma função de remoção.

Logging

const remove = queue.use((current, next, proceed) => {
  console.log(`${current.length} → ${next.length} items`);
  proceed();
});

Validação (bloquear mutação)

Não chamar proceed() bloqueia a mutação. O estado permanece inalterado e listeners não são notificados.

buffer.use((current, next, proceed) => {
  if (next.some((item) => !isValid(item))) return; // bloqueia
  proceed();
});

Snapshot / Restore

Serialização e restauração de estado para persistência.

snapshot()

snapshot(): StateSnapshot
// { type: 'list', id: string, data: T[] }
const snap = queue.snapshot();
localStorage.setItem("queue", JSON.stringify(snap));

restore(snapshot)

Restaura o estado. Valida contra maxSize — rejeita se o snapshot excede a capacidade.

restore(snapshot: StateSnapshot): void
const saved = JSON.parse(localStorage.getItem("queue")!);
queue.restore(saved);

Store Integration

A List funciona standalone por padrão. Quando registrada em um Store, o Store seta hooks internos para coordenar transações entre múltiplos states.

import { createStore } from "@statedelta-apex/store";

// Standalone
const queue = createList<string>("tasks", []);
queue.append("task-A");

// Com Store — coordenação entre states
const store = createStore();
store.register(queue);

store.transaction(() => {
  queue.append("task-B");
  hp.set("value", 50);
  // throw → rollback AMBOS
});

Extensão via Composição

O ListState é o mecanismo — dual-end, capacidade, transactions, reactivity. Derived types adicionam semântica sem se preocupar com mecânica interna.

Queue (FIFO)

class Queue<T> extends DerivedState<T[], ListState<T>> {
  constructor(id: string, items: T[] = [], maxSize?: number) {
    super(new ListState(id, items, { maxSize }));
  }

  enqueue(item: T): void {
    this._primitive.append(item);
  }
  dequeue(): T | undefined {
    return this._primitive.removeFirst();
  }
  peek(): T | undefined {
    return this._primitive.peekFirst();
  }

  get size(): number {
    return this._primitive.size;
  }
  get isEmpty(): boolean {
    return this._primitive.isEmpty;
  }
}

Stack (LIFO)

class Stack<T> extends DerivedState<T[], ListState<T>> {
  constructor(id: string, items: T[] = [], maxSize?: number) {
    super(new ListState(id, items, { maxSize }));
  }

  push(item: T): void {
    this._primitive.append(item);
  }
  pop(): T | undefined {
    return this._primitive.removeLast();
  }
  peek(): T | undefined {
    return this._primitive.peekLast();
  }

  get size(): number {
    return this._primitive.size;
  }
  get isEmpty(): boolean {
    return this._primitive.isEmpty;
  }
}

O Queue/Stack não sabem como transaction funciona, como snapshot serializa, como subscribe notifica. O ListState cuida de tudo.


Configuração

interface ListConfig {
  /** Capacidade máxima. Default: Infinity (sem limite) */
  maxSize?: number;

  /** Validação de serializabilidade a cada mutação. Default: false */
  strict?: boolean;
}

| Config | Default | Descrição | | --------- | ---------- | ---------------------------------------------- | | maxSize | Infinity | Limite de items (throw RangeError se excedido) | | strict | false | Validação de serializabilidade a cada mutação |


Tipos Exportados

import { createList, ListState } from "@statedelta-apex/list-state";
import type { ListConfig, IListState } from "@statedelta-apex/list-state";

| Export | Tipo | Descrição | | --------------- | --------- | ----------------------------------------------------- | | createList | function | Factory principal | | ListState<T> | class | Classe do primitivo (usável com new ou via factory) | | ListConfig | type | Configuração (extends StateConfig) | | IListState<T> | interface | Interface pública completa |