Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/publish_jsr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ jobs:
id-token: write # The OIDC ID token is used for authentication with JSR.
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 26
- run: npx jsr publish
12 changes: 8 additions & 4 deletions .github/workflows/publish_npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ jobs:
run: deno -A scripts/build_npm.ts

- name: Setup Node.js and npm
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 26
registry-url: https://registry.npmjs.org/

- name: Update npm
run: npm install -g npm@latest
# Avoid npm@latest provenance regression that fails with MODULE_NOT_FOUND for sigstore.
- name: Pin npm CLI
run: npm install -g npm@11.4.2

- name: Verify npm CLI version
run: npm --version

- name: Publish to NPM
run: cd npm && npm publish --access public
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bytebury/toolkit",
"version": "2.2.0",
"version": "2.3.0",
"exports": {
".": "./mod.ts",
"./core": "./src/core.ts",
Expand Down
33 changes: 15 additions & 18 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -757,29 +757,26 @@ export function move<T>(list: T[], from: number, to: number): T[] {
}

/**
* Checks whether a collection contains **exactly** the specified values.
* Checks whether a list contains only the specified values.
*
* The order of values does not matter, but the set of values must match
* exactly. Duplicate values are ignored, since both the collection and the
* expected values are compared as sets.
* The order of values does not matter, but the list must contain every
* specified value and must not contain any additional values. Duplicate values
* are ignored, since both the list and expected values are compared as sets.
*
* @template T The type of values in the collection.
* @param collection The collection to compare.
* @param values The expected values.
* @returns `true` if the collection contains exactly the specified values;
* otherwise, `false`.
* @template T The type of values in the list.
* @param list The list to compare.
* @param values The values the list must contain.
* @returns `true` if the list contains only the specified values; otherwise,
* `false`.
*
* @example
* includesOnly([1], 1); // true
* includesOnly([1], 1, 2); // false
* includesOnly([2, 1], 1, 2); // true
* includesOnly(new Set(["a", "b"]), "b", "a"); // true
* hasOnly([1, 2, 3], 1, 2, 3); // true
* hasOnly([1], 1, 2); // false
* hasOnly([2, 1], 1, 2); // true
* hasOnly([1, 1], 1); // true
*/
export function includesOnly<T>(
collection: Iterable<T>,
...values: T[]
): boolean {
const actual = new Set(collection);
export function hasOnly<T>(list: T[], ...values: T[]): boolean {
const actual = new Set(list);
const expected = new Set(values);

if (actual.size !== expected.size) {
Expand Down
14 changes: 14 additions & 0 deletions src/core_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
falsy,
first,
groupBy,
hasOnly,
includes,
includesAny,
intersection,
Expand Down Expand Up @@ -440,6 +441,19 @@ Deno.test("includesAny list in list", () => {
assertFalse(includesAny(["a", "b", "c"], []));
});

Deno.test("hasOnly returns true when a list contains only the specified values", () => {
assert(hasOnly([1, 2, 3], 1, 2, 3));
assert(hasOnly([2, 1], 1, 2));
assert(hasOnly([1, 1], 1));
assert(hasOnly([]));
});

Deno.test("hasOnly returns false when values are missing or additional", () => {
assertFalse(hasOnly([1], 1, 2));
assertFalse(hasOnly([1, 2, 3], 1, 2));
assertFalse(hasOnly([1, 2]));
});

Deno.test("includes returns false for null or undefined inputs", () => {
assertFalse(includes(null, "hello"));
assertFalse(includes(undefined, "hello"));
Expand Down
Loading