diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e48740..e8b0491 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,23 +2,19 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: build: runs-on: ubuntu-latest - strategy: - matrix: - deno-version: [v1.x] steps: - name: Git Checkout Deno Module - uses: actions/checkout@v2 - - name: Use Deno Version ${{ matrix.deno-version }} - uses: denoland/setup-deno@v1 + uses: actions/checkout@v4 + - uses: denoland/setup-deno@v2 with: - deno-version: ${{ matrix.deno-version }} + deno-version: vx.x.x - name: Format run: deno fmt --check - name: Lint @@ -26,10 +22,12 @@ jobs: - name: Unit Test run: deno test --coverage=coverage - name: Create coverage report - run: deno coverage ./coverage --lcov > coverage.lcov + run: deno coverage ./coverage --lcov > coverage.lcov - name: Collect coverage - uses: codecov/codecov-action@v1.0.10 + uses: codecov/codecov-action@v5 with: - file: ./coverage.lcov + token: ${{ secrets.CODECOV_TOKEN }} + slug: denostack/intlit + files: ./coverage.lcov - name: Build Module run: deno task build:npm diff --git a/.gitignore b/.gitignore index b5cfec7..918ef5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ .npm -deno.lock diff --git a/README.md b/README.md index 6bc0a80..8fab1f9 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@
This library provides three iterable weak data structures for JavaScript, -IterableWeakSet, IterableWeakMap, and WeakValueMap. These data structures are -designed to work with objects as keys or values, and are useful when you need to -store a collection of objects that may be garbage collected. +IterableWeakSet, IterableWeakMap, and WeakValueMap. They keep only weak +references to their keys or values, so entries disappear automatically once the +referenced objects are garbage collected instead of blocking GC. ## Usage @@ -43,17 +43,23 @@ npm install weakref ``` ```ts -import { IterableWeakMap, IterableWeakSet } from "weakref"; +import { IterableWeakMap, IterableWeakSet, WeakValueMap } from "weakref"; ``` +> [!NOTE] +> Examples below call `globalThis.gc?.()` only to symbolize “a GC cycle just +> finished”. Manual GC is available only when the runtime exposes it (e.g. +> Node.js started with `--expose-gc`); otherwise entries disappear the next time +> the runtime notifies the `FinalizationRegistry`. + ## Features ### IterableWeakSet -IterableWeakSet is a class that extends the WeakSet and Set classes in -JavaScript, allowing you to create a set of objects that can be iterated over. -Objects in the set are stored using weak references, which means that they can -be garbage collected if they are no longer referenced elsewhere in the program. +IterableWeakSet implements the semantics of both WeakSet (weak keys) and Set +(iteration helpers) so you can keep a deduplicated collection of objects without +preventing them from being garbage collected. Once an object is collected, the +entry is removed automatically. **Interface** @@ -76,10 +82,8 @@ const set = new IterableWeakSet(); } // end of scope, user will be garbage collected -// force garbage collection -if (global.gc) { - global.gc(); -} +// ...later, after a GC cycle (optional manual trigger shown here) +globalThis.gc?.(); // Node needs --expose-gc // check the set size console.log(set.size); // output: 0 @@ -87,10 +91,9 @@ console.log(set.size); // output: 0 ### IterableWeakMap -IterableWeakMap is a class that extends the WeakMap and Map classes in -JavaScript, allowing you to create a map of objects that can be iterated over. -Keys in the map are stored using weak references, which means that they can be -garbage collected if they are no longer referenced elsewhere in the program. +IterableWeakMap combines a WeakMap with iterable Map helpers so you can inspect +entries without blocking GC. Keys are weakly referenced and disappear once they +are no longer referenced elsewhere. **Interface** @@ -114,10 +117,8 @@ const map = new IterableWeakMap(); } // end of scope, user will be garbage collected -// force garbage collection -if (global.gc) { - global.gc(); -} +// ...later, after a GC cycle (optional manual trigger shown here) +globalThis.gc?.(); // Node needs --expose-gc // check the map size console.log(map.size); // output: 0 @@ -126,9 +127,9 @@ console.log(map.size); // output: 0 ### WeakValueMap WeakValueMap is a class that allows you to create a map of non-object keys with -weak references to object values. This is useful when you have a collection of -non-object keys that you want to use to look up objects, and those objects may -be garbage collected. +weak references to object values. It is useful when primitive identifiers are +used to look up objects that should be collected when no longer referenced +elsewhere. **Interface** @@ -151,19 +152,22 @@ const map = new WeakValueMap(); } // end of scope, user will be garbage collected -// force garbage collection -if (global.gc) { - global.gc(); -} +// ...later, after a GC cycle (optional manual trigger shown here) +globalThis.gc?.(); // Node needs --expose-gc // check the map size console.log(map.size); // output: 0 ``` +> [!TIP] +> WeakValueMap relies on the host's `FinalizationRegistry`, so `size`/`has` +> shrink as soon as the GC notifies the registry. There can be a short delay +> between the object being collected and the entry disappearing. + ## See Also -- [Python weakref](https://docs.python.org/3/library/weakref.html) my library is - inspired by this library. +- [Python weakref](https://docs.python.org/3/library/weakref.html) inspired this + project. - [MDN - JS WeakMap](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) - [MDN - JS WeakSet](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) - [MDN - JS WeakRef](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef) diff --git a/deno.json b/deno.json index 5574223..62ff762 100644 --- a/deno.json +++ b/deno.json @@ -1,18 +1,18 @@ { "name": "@denostack/weakref", - "version": "0.2.1", + "version": "0.2.2", "tasks": { "test": "deno task test:unit && deno task test:lint && deno task test:format && deno task test:types", "test:format": "deno fmt --check", "test:lint": "deno lint", - "test:unit": "deno test -A", - "test:types": "deno check mod.ts", - "build:npm": "deno run --allow-sys --allow-env --allow-read --allow-write --allow-net --allow-run scripts/build_npm.ts" + "test:unit": "deno test", + "test:types": "find . -name '*.ts' -not -path './.npm/*' | xargs deno check", + "build:npm": "deno run --allow-env --allow-read --allow-write --allow-run scripts/build_npm.ts" }, "imports": { - "@deno/dnt": "jsr:@deno/dnt@^0.41.1", - "@std/assert": "jsr:@std/assert@^0.221.0", - "@std/fmt": "jsr:@std/fmt@^0.221.0" + "@deno/dnt": "jsr:@deno/dnt@^0.42.3", + "@std/assert": "jsr:@std/assert@^1.0.15", + "@std/fmt": "jsr:@std/fmt@^1.0.8" }, "exports": { ".": "./mod.ts", @@ -26,5 +26,12 @@ "fmt": { "exclude": [".npm"] }, - "lock": false + "publish": { + "exclude": [ + ".github", + ".vscode", + "scripts", + "**/*.test.ts" + ] + } } diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..85f5bfb --- /dev/null +++ b/deno.lock @@ -0,0 +1,78 @@ +{ + "version": "5", + "specifiers": { + "jsr:@david/code-block-writer@^13.0.3": "13.0.3", + "jsr:@deno/dnt@~0.42.3": "0.42.3", + "jsr:@std/assert@^1.0.15": "1.0.15", + "jsr:@std/fmt@1": "1.0.8", + "jsr:@std/fmt@^1.0.8": "1.0.8", + "jsr:@std/fs@1": "1.0.19", + "jsr:@std/internal@^1.0.10": "1.0.12", + "jsr:@std/internal@^1.0.12": "1.0.12", + "jsr:@std/internal@^1.0.9": "1.0.12", + "jsr:@std/path@1": "1.1.2", + "jsr:@std/path@^1.1.1": "1.1.2", + "jsr:@ts-morph/bootstrap@0.27": "0.27.0", + "jsr:@ts-morph/common@0.27": "0.27.0" + }, + "jsr": { + "@david/code-block-writer@13.0.3": { + "integrity": "f98c77d320f5957899a61bfb7a9bead7c6d83ad1515daee92dbacc861e13bb7f" + }, + "@deno/dnt@0.42.3": { + "integrity": "62a917a0492f3c8af002dce90605bb0d41f7d29debc06aca40dba72ab65d8ae3", + "dependencies": [ + "jsr:@david/code-block-writer", + "jsr:@std/fmt@1", + "jsr:@std/fs", + "jsr:@std/path@1", + "jsr:@ts-morph/bootstrap" + ] + }, + "@std/assert@1.0.15": { + "integrity": "d64018e951dbdfab9777335ecdb000c0b4e3df036984083be219ce5941e4703b", + "dependencies": [ + "jsr:@std/internal@^1.0.12" + ] + }, + "@std/fmt@1.0.8": { + "integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7" + }, + "@std/fs@1.0.19": { + "integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06", + "dependencies": [ + "jsr:@std/internal@^1.0.9", + "jsr:@std/path@^1.1.1" + ] + }, + "@std/internal@1.0.12": { + "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" + }, + "@std/path@1.1.2": { + "integrity": "c0b13b97dfe06546d5e16bf3966b1cadf92e1cc83e56ba5476ad8b498d9e3038", + "dependencies": [ + "jsr:@std/internal@^1.0.10" + ] + }, + "@ts-morph/bootstrap@0.27.0": { + "integrity": "b8d7bc8f7942ce853dde4161b28f9aa96769cef3d8eebafb379a81800b9e2448", + "dependencies": [ + "jsr:@ts-morph/common" + ] + }, + "@ts-morph/common@0.27.0": { + "integrity": "c7b73592d78ce8479b356fd4f3d6ec3c460d77753a8680ff196effea7a939052", + "dependencies": [ + "jsr:@std/fs", + "jsr:@std/path@1" + ] + } + }, + "workspace": { + "dependencies": [ + "jsr:@deno/dnt@~0.42.3", + "jsr:@std/assert@^1.0.15", + "jsr:@std/fmt@^1.0.8" + ] + } +} diff --git a/iterable_weak_map.ts b/iterable_weak_map.ts index 2d50ad3..ec9dbac 100644 --- a/iterable_weak_map.ts +++ b/iterable_weak_map.ts @@ -60,21 +60,21 @@ export class IterableWeakMap