Skip to content

new: queue package upgrade for Solid 2.0#918

Open
davedbase wants to merge 2 commits into
solidjs-community:nextfrom
davedbase:v2/queue
Open

new: queue package upgrade for Solid 2.0#918
davedbase wants to merge 2 commits into
solidjs-community:nextfrom
davedbase:v2/queue

Conversation

@davedbase
Copy link
Copy Markdown
Member

@davedbase davedbase commented May 23, 2026

Adds a new @solid-primitives/queue package with six primitives covering plain FIFO queues, priority queues, and async task queues — all built for Solid 2.0 (beta.14).

Primitives

makeQueue<T> / createQueue<T> — FIFO queue in non-reactive and reactive forms. createQueue exposes queue, first, last, size, isEmpty as reactive accessors. Both support add (append to back) and push(comparator, ...items) (sorted insert) so priority behavior can be applied without a separate type.

makePriorityQueue(q, comparator) — modifier that turns any queue (reactive or plain) into a priority queue by binding comparator to add. Designed to compose with makeQueue / createQueue rather than duplicate their logic.

createPriorityQueue<T>(comparator, initialValues?) — ergonomic reactive factory: pre-sorts initial values and delegates to makePriorityQueue(createQueue(...), comparator).

createTaskQueue<T>() — runs async tasks one at a time in FIFO order. enqueue(task) returns a Promise<T>. Exposes reactive size (pending count) and active (boolean). Uses a plain async drain loop rather than Solid's action() so size/active update live during execution.

createConcurrentTaskQueue<T>(concurrency) — like createTaskQueue but runs up to concurrency tasks in parallel. active is Accessor<number> (count of running tasks).

Implementation notes

  • Signal writes in task queues use INTERNAL_OPTIONS (ownedWrite: true) so the queues work outside a reactive owner.
  • action() was considered and rejected for the drain loop — Solid 2.0's transition system holds all scheduler writes globally while a transition is open, making live size/active state unobservable mid-execution.
  • Source is split by concern: queue.ts, priority-queue.ts, task-queue.ts, barrel index.ts.

Summary by CodeRabbit

  • New Features

    • Released @solid-primitives/queue package with FIFO and priority queue primitives
    • Added reactive and non-reactive queue variants
    • Added async task queues with sequential and bounded concurrent execution support
    • Support for tracking active tasks and limiting parallelism
  • Documentation

    • Added comprehensive README with API examples and usage guidance
    • Added CHANGELOG and package metadata

Review Change Stack

@davedbase davedbase added this to the Solid 2.0 Migration milestone May 23, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 23, 2026

🦋 Changeset detected

Latest commit: ba0cf24

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@solid-primitives/queue Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@davedbase davedbase marked this pull request as ready for review May 23, 2026 14:03
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 842d821f-84ef-4b79-b4b2-b5e5f639e78f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/queue-initial.md:
- Line 11: The changeset incorrectly documents makePriorityQueue with the old
constructor signature; update the changeset text to reflect the actual API used
in the PR by changing the documented form from makePriorityQueue<T>(comparator,
initialValues?) to the modifier form makePriorityQueue(q, comparator) (or the
exact PR contract signature), and ensure the description mentions it modifies an
existing queue rather than returning a non-reactive standalone queue; reference
makePriorityQueue and the queue modifier form in your edit so release notes
match the implemented API.

In `@packages/queue/CHANGELOG.md`:
- Around line 8-10: Update the CHANGELOG entries to reflect the actual exported
signatures: change the createQueue line to document
createQueue<T>(initialValues?) — reactive FIFO queue backed by Solid signals;
exposes queue, first, last, size, isEmpty, add, remove, clear (include the
`queue` accessor), update makePriorityQueue to describe the modifier form
makePriorityQueue<T>(q, comparator) (not a factory), and keep
createPriorityQueue<T>(comparator, initialValues?) as-is; adjust wording so the
listed symbols (`createQueue`, `makePriorityQueue`, `createPriorityQueue`) match
their real function signatures and exposed members.

In `@packages/queue/README.md`:
- Around line 55-63: The README type definitions for Queue and ReactiveQueue are
missing the public push method; update the type shapes (the Queue<T> and
ReactiveQueue<T> entries) to include a push signature that matches the runtime
(e.g., push: (...items: T[]) => void), keeping it consistent with the existing
add method and other members (first, last, size, isEmpty, add, remove, clear).
Ensure both occurrences of the type doc (the block around Queue<T> and the
similar block later referenced) are updated so the documented contract matches
the runtime API.
- Around line 121-125: The README demonstrates makePriorityQueue with the wrong
argument order; the implementation expects an existing queue first and the
comparator second. Update all examples and descriptive text that show
makePriorityQueue to pass the initial values/queue as the first argument and the
comparator function as the second (fix both example blocks that currently show
the reversed signature), and adjust any prose that describes the function
signature to reflect (q, comparator) instead of (comparator, initialValues).

In `@packages/queue/src/task-queue.ts`:
- Around line 149-157: The function createConcurrentTaskQueue accepts invalid
concurrency values (0, negative, NaN, non-integers) which can leave pending
tasks unresolved; fix by validating and normalizing the concurrency parameter at
the start of createConcurrentTaskQueue (before runNext/pending are used): ensure
concurrency is a finite integer >= 1 (e.g., Number.isFinite and Number.isInteger
checks) and throw a clear RangeError if not, so runNext and enqueue() can rely
on a valid positive concurrency value; reference createConcurrentTaskQueue,
runNext, and pending when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 41062067-ef1c-4638-8855-330fba9554ba

📥 Commits

Reviewing files that changed from the base of the PR and between 76d6f2e and ba0cf24.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • .changeset/queue-initial.md
  • packages/queue/CHANGELOG.md
  • packages/queue/LICENSE
  • packages/queue/README.md
  • packages/queue/dev/index.tsx
  • packages/queue/package.json
  • packages/queue/src/index.ts
  • packages/queue/src/priority-queue.ts
  • packages/queue/src/queue.ts
  • packages/queue/src/task-queue.ts
  • packages/queue/test/index.test.ts
  • packages/queue/test/server.test.ts
  • packages/queue/tsconfig.json


- **`makeQueue<T>(initialValues?)`** — non-reactive FIFO queue backed by a plain array.
- **`createQueue<T>(initialValues?)`** — reactive FIFO queue backed by Solid signals. Exposes reactive accessors (`queue`, `first`, `last`, `size`, `isEmpty`) and imperative methods (`add`, `remove`, `clear`).
- **`makePriorityQueue<T>(comparator, initialValues?)`** — non-reactive priority queue; items are dequeued in comparator order rather than insertion order.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update makePriorityQueue signature in changeset text.

Line 11 documents makePriorityQueue<T>(comparator, initialValues?), but the PR contract describes a queue modifier form (makePriorityQueue(q, comparator)). This should be corrected before release notes are generated.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/queue-initial.md at line 11, The changeset incorrectly documents
makePriorityQueue with the old constructor signature; update the changeset text
to reflect the actual API used in the PR by changing the documented form from
makePriorityQueue<T>(comparator, initialValues?) to the modifier form
makePriorityQueue(q, comparator) (or the exact PR contract signature), and
ensure the description mentions it modifies an existing queue rather than
returning a non-reactive standalone queue; reference makePriorityQueue and the
queue modifier form in your edit so release notes match the implemented API.

Comment on lines +8 to +10
- `createQueue<T>(initialValues?)` — reactive FIFO queue backed by Solid signals; exposes `first`, `last`, `size`, `isEmpty`, `add`, `remove`, `clear`
- `makePriorityQueue<T>(comparator, initialValues?)` — non-reactive priority queue; items dequeued in comparator order
- `createPriorityQueue<T>(comparator, initialValues?)` — reactive priority queue; same interface as `createQueue`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Correct API docs to match released signatures.

Line 8 and Line 9 appear out of sync with the PR’s described API: createQueue should include queue in exposed accessors, and makePriorityQueue is described as a modifier form (makePriorityQueue(q, comparator)), not a (comparator, initialValues?) factory signature. Please align the changelog so users don’t implement against the wrong contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/queue/CHANGELOG.md` around lines 8 - 10, Update the CHANGELOG
entries to reflect the actual exported signatures: change the createQueue line
to document createQueue<T>(initialValues?) — reactive FIFO queue backed by Solid
signals; exposes queue, first, last, size, isEmpty, add, remove, clear (include
the `queue` accessor), update makePriorityQueue to describe the modifier form
makePriorityQueue<T>(q, comparator) (not a factory), and keep
createPriorityQueue<T>(comparator, initialValues?) as-is; adjust wording so the
listed symbols (`createQueue`, `makePriorityQueue`, `createPriorityQueue`) match
their real function signatures and exposed members.

Comment thread packages/queue/README.md
Comment on lines +55 to +63
type Queue<T> = {
readonly first: T | undefined;
readonly last: T | undefined;
readonly size: number;
readonly isEmpty: boolean;
add: (...items: T[]) => void;
remove: () => T | undefined;
clear: () => void;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Queue/ReactiveQueue type docs omit push, which is part of the public API.

Both runtime implementations expose push, but the documented type shapes do not. This makes the API contract inconsistent for users.

Suggested README fix
 type Queue<T> = {
   readonly first: T | undefined;
   readonly last: T | undefined;
   readonly size: number;
   readonly isEmpty: boolean;
   add: (...items: T[]) => void;
+  push: (comparator: (a: T, b: T) => number, ...items: T[]) => void;
   remove: () => T | undefined;
   clear: () => void;
 };
 type ReactiveQueue<T> = {
   readonly queue: Accessor<T[]>;
   readonly first: Accessor<T | undefined>;
   readonly last: Accessor<T | undefined>;
   readonly size: Accessor<number>;
   readonly isEmpty: Accessor<boolean>;
   add: (...items: T[]) => void;
+  push: (comparator: (a: T, b: T) => number, ...items: T[]) => void;
   remove: () => T | undefined;
   clear: () => void;
 };

Also applies to: 97-106

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/queue/README.md` around lines 55 - 63, The README type definitions
for Queue and ReactiveQueue are missing the public push method; update the type
shapes (the Queue<T> and ReactiveQueue<T> entries) to include a push signature
that matches the runtime (e.g., push: (...items: T[]) => void), keeping it
consistent with the existing add method and other members (first, last, size,
isEmpty, add, remove, clear). Ensure both occurrences of the type doc (the block
around Queue<T> and the similar block later referenced) are updated so the
documented contract matches the runtime API.

Comment thread packages/queue/README.md
Comment on lines +121 to +125
```ts
import { makePriorityQueue } from "@solid-primitives/queue";

const q = makePriorityQueue((a, b) => a - b, [3, 1, 2]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

makePriorityQueue docs use the wrong function signature.

The README currently documents and demonstrates makePriorityQueue as if it creates a queue from (comparator, initialValues), but the implementation takes an existing queue first: (q, comparator). This will lead users to call a non-existent API.

Suggested README fix
-import { makePriorityQueue } from "`@solid-primitives/queue`";
+import { makePriorityQueue, makeQueue } from "`@solid-primitives/queue`";

-const q = makePriorityQueue((a, b) => a - b, [3, 1, 2]);
+const asc = (a, b) => a - b;
+const q = makePriorityQueue(makeQueue([3, 1, 2].sort(asc)), asc);
-function makePriorityQueue<T>(
-  comparator: (a: T, b: T) => number,
-  initialValues?: T[],
-): Queue<T>;
+function makePriorityQueue<T, Q extends { add: (...items: T[]) => void; push: (comparator: (a: T, b: T) => number, ...items: T[]) => void }>(
+  q: Q,
+  comparator: (a: T, b: T) => number,
+): Q;

Also applies to: 137-142

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/queue/README.md` around lines 121 - 125, The README demonstrates
makePriorityQueue with the wrong argument order; the implementation expects an
existing queue first and the comparator second. Update all examples and
descriptive text that show makePriorityQueue to pass the initial values/queue as
the first argument and the comparator function as the second (fix both example
blocks that currently show the reversed signature), and adjust any prose that
describes the function signature to reflect (q, comparator) instead of
(comparator, initialValues).

Comment on lines +149 to +157
export function createConcurrentTaskQueue<T>(concurrency: number): ReactiveConcurrentTaskQueue<T> {
const pending: TaskEntry<T>[] = [];
const [size, setSize] = createSignal(0, INTERNAL_OPTIONS);
const [activeCount, setActiveCount] = createSignal(0, INTERNAL_OPTIONS);
let running = 0;

const runNext = () => {
while (running < concurrency && pending.length > 0) {
const entry = pending.shift()!;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate concurrency to prevent permanently pending tasks.

Line 149 currently accepts invalid values (0, negatives, NaN, non-integers). In those cases, runNext may never schedule work (or schedule an unintended count), leaving enqueue() promises unresolved.

Suggested fix
 export function createConcurrentTaskQueue<T>(concurrency: number): ReactiveConcurrentTaskQueue<T> {
+  if (!Number.isInteger(concurrency) || concurrency < 1) {
+    throw new RangeError("concurrency must be a positive integer");
+  }
+
   const pending: TaskEntry<T>[] = [];
   const [size, setSize] = createSignal(0, INTERNAL_OPTIONS);
   const [activeCount, setActiveCount] = createSignal(0, INTERNAL_OPTIONS);
   let running = 0;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/queue/src/task-queue.ts` around lines 149 - 157, The function
createConcurrentTaskQueue accepts invalid concurrency values (0, negative, NaN,
non-integers) which can leave pending tasks unresolved; fix by validating and
normalizing the concurrency parameter at the start of createConcurrentTaskQueue
(before runNext/pending are used): ensure concurrency is a finite integer >= 1
(e.g., Number.isFinite and Number.isInteger checks) and throw a clear RangeError
if not, so runNext and enqueue() can rely on a valid positive concurrency value;
reference createConcurrentTaskQueue, runNext, and pending when making the
change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant