tutuca 0.9.40 → 0.9.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tutuca-cli.js +74 -39
- package/package.json +6 -7
- package/skill/immutable-js/SKILL.md +79 -0
- package/skill/immutable-js/references/collection.md +346 -0
- package/skill/immutable-js/references/conversions.md +99 -0
- package/skill/immutable-js/references/deep-updates.md +172 -0
- package/skill/immutable-js/references/equality.md +95 -0
- package/skill/immutable-js/references/list.md +266 -0
- package/skill/immutable-js/references/map.md +300 -0
- package/skill/immutable-js/references/predicates.md +93 -0
- package/skill/immutable-js/references/range-repeat.md +55 -0
- package/skill/immutable-js/references/record.md +196 -0
- package/skill/immutable-js/references/seq.md +248 -0
- package/skill/immutable-js/references/set.md +270 -0
- package/skill/immutable-js/references/shallow-functional.md +99 -0
- package/skill/immutable-js/references/stack.md +210 -0
- package/skill/margaui/SKILL.md +101 -0
- package/skill/margaui/components/accordion.md +127 -0
- package/skill/margaui/components/alert.md +174 -0
- package/skill/margaui/components/avatar.md +220 -0
- package/skill/margaui/components/badge.md +193 -0
- package/skill/margaui/components/breadcrumbs.md +103 -0
- package/skill/margaui/components/button.md +322 -0
- package/skill/margaui/components/calendar.md +67 -0
- package/skill/margaui/components/card.md +373 -0
- package/skill/margaui/components/carousel.md +387 -0
- package/skill/margaui/components/chat.md +171 -0
- package/skill/margaui/components/checkbox.md +101 -0
- package/skill/margaui/components/collapse.md +172 -0
- package/skill/margaui/components/countdown.md +165 -0
- package/skill/margaui/components/diff.md +53 -0
- package/skill/margaui/components/divider.md +107 -0
- package/skill/margaui/components/dock.md +173 -0
- package/skill/margaui/components/drawer.md +184 -0
- package/skill/margaui/components/dropdown.md +388 -0
- package/skill/margaui/components/fab.md +346 -0
- package/skill/margaui/components/fieldset.md +88 -0
- package/skill/margaui/components/file-input.md +84 -0
- package/skill/margaui/components/filter.md +52 -0
- package/skill/margaui/components/footer.md +583 -0
- package/skill/margaui/components/hero.md +135 -0
- package/skill/margaui/components/hover-3d.md +129 -0
- package/skill/margaui/components/hover-gallery.md +49 -0
- package/skill/margaui/components/indicator.md +265 -0
- package/skill/margaui/components/input.md +389 -0
- package/skill/margaui/components/join.md +100 -0
- package/skill/margaui/components/kbd.md +127 -0
- package/skill/margaui/components/label.md +102 -0
- package/skill/margaui/components/link.md +96 -0
- package/skill/margaui/components/list.md +182 -0
- package/skill/margaui/components/loading.md +105 -0
- package/skill/margaui/components/mask.md +168 -0
- package/skill/margaui/components/menu.md +856 -0
- package/skill/margaui/components/mockup-browser.md +39 -0
- package/skill/margaui/components/mockup-code.md +81 -0
- package/skill/margaui/components/mockup-phone.md +39 -0
- package/skill/margaui/components/mockup-window.md +33 -0
- package/skill/margaui/components/modal.md +178 -0
- package/skill/margaui/components/navbar.md +282 -0
- package/skill/margaui/components/pagination.md +122 -0
- package/skill/margaui/components/progress.md +135 -0
- package/skill/margaui/components/radial-progress.md +67 -0
- package/skill/margaui/components/radio.md +133 -0
- package/skill/margaui/components/range.md +134 -0
- package/skill/margaui/components/rating.md +170 -0
- package/skill/margaui/components/select.md +225 -0
- package/skill/margaui/components/skeleton.md +64 -0
- package/skill/margaui/components/stack.md +142 -0
- package/skill/margaui/components/stat.md +254 -0
- package/skill/margaui/components/status.md +73 -0
- package/skill/margaui/components/steps.md +138 -0
- package/skill/margaui/components/swap.md +152 -0
- package/skill/margaui/components/tab.md +248 -0
- package/skill/margaui/components/table.md +1018 -0
- package/skill/margaui/components/text-rotate.md +91 -0
- package/skill/margaui/components/textarea.md +85 -0
- package/skill/margaui/components/theme-controller.md +266 -0
- package/skill/margaui/components/timeline.md +1356 -0
- package/skill/margaui/components/toast.md +165 -0
- package/skill/margaui/components/toggle.md +135 -0
- package/skill/margaui/components/tooltip.md +181 -0
- package/skill/margaui/components/validator.md +163 -0
- package/skill/{advanced.md → tutuca/advanced.md} +5 -0
- package/skill/{cli.md → tutuca/cli.md} +17 -0
- package/skill/{core.md → tutuca/core.md} +5 -0
- /package/skill/{SKILL.md → tutuca/SKILL.md} +0 -0
package/dist/tutuca-cli.js
CHANGED
|
@@ -10378,24 +10378,53 @@ __export(exports_install_skill, {
|
|
|
10378
10378
|
run: () => run,
|
|
10379
10379
|
describe: () => describe
|
|
10380
10380
|
});
|
|
10381
|
-
import { existsSync, mkdirSync, readdirSync
|
|
10381
|
+
import { cpSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
10382
10382
|
import { homedir } from "node:os";
|
|
10383
10383
|
import { dirname, resolve } from "node:path";
|
|
10384
|
-
import { parseArgs } from "node:util";
|
|
10385
10384
|
import { fileURLToPath } from "node:url";
|
|
10386
|
-
|
|
10385
|
+
import { parseArgs } from "node:util";
|
|
10386
|
+
function findSkillsRoot() {
|
|
10387
10387
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
10388
10388
|
const candidates = [resolve(here, "..", "..", "..", "skill"), resolve(here, "..", "skill")];
|
|
10389
10389
|
for (const c of candidates) {
|
|
10390
|
-
if (existsSync(resolve(c, "SKILL.md")))
|
|
10390
|
+
if (existsSync(resolve(c, "tutuca", "SKILL.md")))
|
|
10391
10391
|
return c;
|
|
10392
10392
|
}
|
|
10393
10393
|
return null;
|
|
10394
10394
|
}
|
|
10395
|
-
function targetDir(scope) {
|
|
10396
|
-
|
|
10397
|
-
|
|
10398
|
-
|
|
10395
|
+
function targetDir(scope, name) {
|
|
10396
|
+
const base = scope === "user" ? homedir() : process.cwd();
|
|
10397
|
+
return resolve(base, ".claude/skills", name);
|
|
10398
|
+
}
|
|
10399
|
+
function targetHasSkillFiles(dir) {
|
|
10400
|
+
if (!existsSync(dir))
|
|
10401
|
+
return false;
|
|
10402
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
10403
|
+
if (entry.isFile() && entry.name.endsWith(".md"))
|
|
10404
|
+
return true;
|
|
10405
|
+
if (entry.isDirectory() && targetHasSkillFiles(resolve(dir, entry.name)))
|
|
10406
|
+
return true;
|
|
10407
|
+
}
|
|
10408
|
+
return false;
|
|
10409
|
+
}
|
|
10410
|
+
function installSkill(skill, root, scope, force) {
|
|
10411
|
+
const src = resolve(root, skill.srcSubdir);
|
|
10412
|
+
if (!existsSync(resolve(src, "SKILL.md"))) {
|
|
10413
|
+
process.stderr.write(`tutuca: missing skill assets for ${skill.name} at ${src}
|
|
10414
|
+
`);
|
|
10415
|
+
process.exit(1);
|
|
10416
|
+
}
|
|
10417
|
+
const target = targetDir(scope, skill.name);
|
|
10418
|
+
if (targetHasSkillFiles(target) && !force) {
|
|
10419
|
+
process.stderr.write(`tutuca: ${target} already contains skill files. Re-run with --force to overwrite.
|
|
10420
|
+
`);
|
|
10421
|
+
process.exit(1);
|
|
10422
|
+
}
|
|
10423
|
+
mkdirSync(target, { recursive: true });
|
|
10424
|
+
cpSync(src, target, { recursive: true });
|
|
10425
|
+
const rel = scope === "project" ? `.claude/skills/${skill.name}` : target;
|
|
10426
|
+
process.stdout.write(`installed ${skill.name} skill → ${rel}
|
|
10427
|
+
`);
|
|
10399
10428
|
}
|
|
10400
10429
|
async function run(argv) {
|
|
10401
10430
|
const parsed = parseArgs({
|
|
@@ -10403,62 +10432,68 @@ async function run(argv) {
|
|
|
10403
10432
|
options: {
|
|
10404
10433
|
user: { type: "boolean", default: false },
|
|
10405
10434
|
project: { type: "boolean", default: false },
|
|
10435
|
+
"margaui-skill": { type: "boolean", default: false },
|
|
10436
|
+
"immutable-skill": { type: "boolean", default: false },
|
|
10437
|
+
all: { type: "boolean", default: false },
|
|
10406
10438
|
force: { type: "boolean", short: "f", default: false },
|
|
10407
10439
|
help: { type: "boolean", short: "h", default: false }
|
|
10408
10440
|
},
|
|
10409
10441
|
allowPositionals: false
|
|
10410
10442
|
});
|
|
10411
10443
|
if (parsed.values.help) {
|
|
10412
|
-
process.stdout.write(`tutuca install-skill [--user | --project] [--force]
|
|
10444
|
+
process.stdout.write(`tutuca install-skill [--user | --project] [--margaui-skill | --immutable-skill | --all] [--force]
|
|
10445
|
+
` + `
|
|
10446
|
+
` + ` Installs Claude Code skill assets into .claude/skills/<name>/.
|
|
10447
|
+
` + ` Defaults to --project (cwd); --user installs at ~/.claude/skills/.
|
|
10448
|
+
` + `
|
|
10449
|
+
` + ` Selection:
|
|
10450
|
+
` + ` (default) install the tutuca skill
|
|
10451
|
+
` + ` --margaui-skill install the margaui skill instead
|
|
10452
|
+
` + ` --immutable-skill install the immutable-js skill instead
|
|
10453
|
+
` + ` --all install every bundled skill (tutuca + margaui + immutable-js)
|
|
10413
10454
|
` + `
|
|
10414
|
-
` + ` Copies SKILL.md + core.md + cli.md + advanced.md into
|
|
10415
|
-
` + ` .claude/skills/tutuca/. Defaults to --project (cwd).
|
|
10416
|
-
` + ` --user installs at ~/.claude/skills/tutuca/.
|
|
10417
10455
|
` + ` --force overwrites existing files.
|
|
10418
10456
|
`);
|
|
10419
10457
|
return;
|
|
10420
10458
|
}
|
|
10421
10459
|
if (parsed.values.user && parsed.values.project) {
|
|
10422
10460
|
process.stderr.write(`tutuca: --user and --project are mutually exclusive
|
|
10461
|
+
`);
|
|
10462
|
+
process.exit(1);
|
|
10463
|
+
}
|
|
10464
|
+
const selectionFlags = ["margaui-skill", "immutable-skill", "all"].filter((k) => parsed.values[k]);
|
|
10465
|
+
if (selectionFlags.length > 1) {
|
|
10466
|
+
process.stderr.write(`tutuca: ${selectionFlags.map((f) => `--${f}`).join(", ")} are mutually exclusive
|
|
10423
10467
|
`);
|
|
10424
10468
|
process.exit(1);
|
|
10425
10469
|
}
|
|
10426
10470
|
const scope = parsed.values.user ? "user" : "project";
|
|
10427
|
-
const
|
|
10428
|
-
|
|
10429
|
-
if (!src) {
|
|
10471
|
+
const root = findSkillsRoot();
|
|
10472
|
+
if (!root) {
|
|
10430
10473
|
process.stderr.write(`tutuca: skill assets not found alongside this CLI.
|
|
10431
10474
|
` + "If you're running from a checkout, run `bun scripts/build-skill.js` first.\n");
|
|
10432
10475
|
process.exit(1);
|
|
10433
10476
|
}
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
}
|
|
10477
|
+
let selected;
|
|
10478
|
+
if (parsed.values.all) {
|
|
10479
|
+
selected = SKILLS;
|
|
10480
|
+
} else {
|
|
10481
|
+
const selFlag = SKILLS.find((s) => s.flag && parsed.values[s.flag]);
|
|
10482
|
+
selected = selFlag ? [selFlag] : SKILLS.filter((s) => s.name === "tutuca");
|
|
10441
10483
|
}
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
const from = resolve(src, name);
|
|
10445
|
-
if (!existsSync(from)) {
|
|
10446
|
-
process.stderr.write(`tutuca: missing skill asset: ${from}
|
|
10447
|
-
`);
|
|
10448
|
-
process.exit(1);
|
|
10449
|
-
}
|
|
10450
|
-
const buf = readFileSync(from);
|
|
10451
|
-
writeFileSync(resolve(target, name), buf);
|
|
10484
|
+
for (const skill of selected) {
|
|
10485
|
+
installSkill(skill, root, scope, parsed.values.force);
|
|
10452
10486
|
}
|
|
10453
|
-
const rel = scope === "project" ? ".claude/skills/tutuca" : target;
|
|
10454
|
-
process.stdout.write(`installed tutuca skill → ${rel}
|
|
10455
|
-
`);
|
|
10456
10487
|
process.stdout.write(`Open a Claude Code session in this directory to use it.
|
|
10457
10488
|
`);
|
|
10458
10489
|
}
|
|
10459
|
-
var describe = "Install
|
|
10490
|
+
var describe = "Install Claude Code skills (tutuca, margaui, immutable-js) into .claude/skills/.", SKILLS;
|
|
10460
10491
|
var init_install_skill = __esm(() => {
|
|
10461
|
-
|
|
10492
|
+
SKILLS = [
|
|
10493
|
+
{ name: "tutuca", srcSubdir: "tutuca", flag: null },
|
|
10494
|
+
{ name: "margaui", srcSubdir: "margaui", flag: "margaui-skill" },
|
|
10495
|
+
{ name: "immutable-js", srcSubdir: "immutable-js", flag: "immutable-skill" }
|
|
10496
|
+
];
|
|
10462
10497
|
});
|
|
10463
10498
|
|
|
10464
10499
|
// tools/tutuca.js
|
|
@@ -10670,7 +10705,7 @@ async function loadAndNormalize(modulePath) {
|
|
|
10670
10705
|
}
|
|
10671
10706
|
|
|
10672
10707
|
// tools/cli/output.js
|
|
10673
|
-
import { writeFileSync
|
|
10708
|
+
import { writeFileSync } from "node:fs";
|
|
10674
10709
|
|
|
10675
10710
|
// tools/format/cli.js
|
|
10676
10711
|
var exports_cli = {};
|
|
@@ -11191,7 +11226,7 @@ async function formatResult(formatName, result, options = {}) {
|
|
|
11191
11226
|
async function emit(result, { format: format5, pretty, output }) {
|
|
11192
11227
|
const text = await formatResult(format5, result, { pretty });
|
|
11193
11228
|
if (output) {
|
|
11194
|
-
|
|
11229
|
+
writeFileSync(output, text);
|
|
11195
11230
|
} else {
|
|
11196
11231
|
process.stdout.write(text);
|
|
11197
11232
|
if (!text.endsWith(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tutuca",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.41",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Zero-dependency SPA framework with immutable state and virtual DOM",
|
|
6
6
|
"main": "./dist/tutuca.js",
|
|
@@ -20,9 +20,11 @@
|
|
|
20
20
|
"dist": "bun scripts/dist.js",
|
|
21
21
|
"dist-immutable": "bun scripts/dist-immutable.js",
|
|
22
22
|
"dist-all": "bun run dist-immutable && bun run dist",
|
|
23
|
-
"release": "bun run dist-all && bun run build-skill && npm publish --access public",
|
|
24
|
-
"release-dry": "bun run dist-all && bun run build-skill && npm publish --dry-run",
|
|
23
|
+
"release": "bun run dist-all && bun run build-skill && bun run build-margaui-skill && bun run build-immutable-skill && npm publish --access public",
|
|
24
|
+
"release-dry": "bun run dist-all && bun run build-skill && bun run build-margaui-skill && bun run build-immutable-skill && npm publish --dry-run",
|
|
25
25
|
"build-skill": "bun scripts/build-skill.js",
|
|
26
|
+
"build-margaui-skill": "bun scripts/build-margaui-skill.js",
|
|
27
|
+
"build-immutable-skill": "bun scripts/build-immutable-skill.js",
|
|
26
28
|
"test": "bun test test/*.test.js",
|
|
27
29
|
"test-watch": "bun test --watch test/*.test.js",
|
|
28
30
|
"format": "bunx @biomejs/biome format --write src test/*.js docs/src/*.js docs/examples/*.js tools/*.js tools/*/*.js",
|
|
@@ -42,10 +44,7 @@
|
|
|
42
44
|
"dist/tutuca-extra.js",
|
|
43
45
|
"dist/tutuca-extra.min.js",
|
|
44
46
|
"dist/tutuca-cli.js",
|
|
45
|
-
"skill
|
|
46
|
-
"skill/core.md",
|
|
47
|
-
"skill/cli.md",
|
|
48
|
-
"skill/advanced.md"
|
|
47
|
+
"skill"
|
|
49
48
|
],
|
|
50
49
|
"dependencies": {
|
|
51
50
|
"jsdom": "^28.0.0 || ^29.0.0"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: immutable-js
|
|
3
|
+
description: Reference for the Immutable.js API organized by datatype (List, Map, Set, OrderedMap, OrderedSet, Stack, Record, Seq, Collection, Range, Repeat) and by topic (deep updates, equality, type predicates, JS conversion). Use when the user asks how to use a specific Immutable.js datatype or method, when writing or reviewing code in this repository, or when explaining Immutable.js semantics like persistent updates, value equality, or lazy evaluation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Immutable.js
|
|
7
|
+
|
|
8
|
+
Persistent immutable data structures for JavaScript. All operations return a
|
|
9
|
+
new collection rather than mutating the original; structural sharing keeps
|
|
10
|
+
this efficient. Treat collections as **values**, not objects — compare with
|
|
11
|
+
`is(a, b)` or `a.equals(b)`, never `===`.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { Map } from 'immutable';
|
|
15
|
+
const m1 = Map({ a: 1, b: 2 });
|
|
16
|
+
const m2 = m1.set('b', 50);
|
|
17
|
+
m1.get('b'); // 2 — m1 is unchanged
|
|
18
|
+
m2.get('b'); // 50
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Inheritance cheatsheet
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Collection ─┬─ Collection.Keyed ─┬─ Map ── OrderedMap
|
|
25
|
+
│ └─ Record (object-like, fixed keys)
|
|
26
|
+
│
|
|
27
|
+
├─ Collection.Indexed ─┬─ List
|
|
28
|
+
│ └─ Stack
|
|
29
|
+
│
|
|
30
|
+
└─ Collection.Set ─┬─ Set ── OrderedSet
|
|
31
|
+
└─ (OrderedCollection marker)
|
|
32
|
+
|
|
33
|
+
Seq mirrors the hierarchy lazily:
|
|
34
|
+
Seq ─┬─ Seq.Keyed ─ keyed lazy sequence
|
|
35
|
+
├─ Seq.Indexed ─ indexed lazy sequence
|
|
36
|
+
└─ Seq.Set ─ set-like lazy sequence
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Datatypes
|
|
40
|
+
|
|
41
|
+
Read the reference for a specific datatype when answering questions about
|
|
42
|
+
its constructors, methods, or semantics.
|
|
43
|
+
|
|
44
|
+
- [List](references/list.md) — ordered indexed collection (push/pop/get/set/etc.)
|
|
45
|
+
- [Map](references/map.md) — keyed collection with value-equality keys; covers OrderedMap
|
|
46
|
+
- [Set](references/set.md) — unique values with value equality; covers OrderedSet
|
|
47
|
+
- [Stack](references/stack.md) — LIFO; fast push/pop/peek at the front
|
|
48
|
+
- [Record](references/record.md) — fixed-shape, named, typed record; class-like
|
|
49
|
+
- [Seq](references/seq.md) — lazy sequence (Keyed / Indexed / Set variants)
|
|
50
|
+
- [Collection](references/collection.md) — abstract base; methods shared by all collections
|
|
51
|
+
- [Range, Repeat](references/range-repeat.md) — lazy generator factories
|
|
52
|
+
|
|
53
|
+
## Operations and topics
|
|
54
|
+
|
|
55
|
+
Cross-cutting topics. Load these alongside (or instead of) a datatype reference
|
|
56
|
+
when the question is about an operation rather than a single type.
|
|
57
|
+
|
|
58
|
+
- [Deep updates](references/deep-updates.md) — `getIn`, `setIn`, `updateIn`, `removeIn`, `hasIn`, `merge`, `mergeWith`, `mergeDeep`, `mergeDeepWith`
|
|
59
|
+
- [Shallow functional helpers](references/shallow-functional.md) — top-level `get`, `set`, `has`, `update`, `remove` that work on any collection or plain JS object
|
|
60
|
+
- [JS conversions](references/conversions.md) — `fromJS`, `toJS`, plain-JS interop, reviver patterns
|
|
61
|
+
- [Equality and hashing](references/equality.md) — `is`, `hash`, `ValueObject`, `isValueObject`, value vs reference semantics
|
|
62
|
+
- [Type predicates](references/predicates.md) — `isList`, `isMap`, `isSet`, `isOrderedMap`, `isOrderedSet`, `isStack`, `isRecord`, `isSeq`, `isCollection`, `isKeyed`, `isIndexed`, `isAssociative`, `isOrdered`, `isImmutable`
|
|
63
|
+
|
|
64
|
+
## Cross-cutting semantics worth knowing up front
|
|
65
|
+
|
|
66
|
+
- **Value equality**: keys in `Map`/`Set` and elements in `Set` compare by `is()`, not `===`. Two distinct `Map({a:1})` instances are equal as keys.
|
|
67
|
+
- **Identity preservation**: an operation that produces an equal collection returns the original (`===`-identical), so memoization with `===` is sound.
|
|
68
|
+
- **Mutation batching**: `withMutations(c => { c.set(...).push(...) })` builds a transient copy; cheaper than chained calls when doing many updates.
|
|
69
|
+
- **Lazy `Seq`**: chained `map`/`filter`/etc. on a `Seq` doesn't run until something pulls values (e.g. `toList()`, `forEach`, iteration). See [seq.md](references/seq.md).
|
|
70
|
+
- **Path-based updates**: `setIn`/`updateIn`/`mergeDeep` create missing intermediate `Map`s by default. See [deep-updates.md](references/deep-updates.md).
|
|
71
|
+
- **`fromJS` is shallow-recursive**: arrays become `List`, plain objects become `Map`. Customize with the `reviver`. See [conversions.md](references/conversions.md).
|
|
72
|
+
|
|
73
|
+
## Authoritative sources in this repo
|
|
74
|
+
|
|
75
|
+
When a reference here is ambiguous or you need an exact signature, fall back to:
|
|
76
|
+
|
|
77
|
+
- `type-definitions/immutable.d.ts` — canonical TypeScript signatures for the entire public API
|
|
78
|
+
- `website/docs/<Name>.mdx` — the original long-form docs each reference here distills
|
|
79
|
+
- `README.md` — top-level rationale and worked examples
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
`Collection<K, V>` is the abstract base interface for every immutable iterable in immutable-js. You never construct a `Collection` directly — you construct one of the concrete types (`List`, `Map`, `Set`, `Stack`, `Seq`, `Record`) and use the methods documented here, all of which they inherit. Three sub-interfaces tailor the iteration shape: `Collection.Keyed<K, V>` yields `[K, V]` tuples, `Collection.Indexed<T>` yields values in numeric-index order, and `Collection.Set<T>` yields values (value-unique on concrete `Set`/`OrderedSet`). The `Collection(...)` factory is also a conversion function: returns an existing `Collection` unchanged, wraps an array-like as `Collection.Indexed`, wraps a plain object as `Collection.Keyed<string, V>`.
|
|
2
|
+
|
|
3
|
+
## Class hierarchy summary
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Collection
|
|
7
|
+
├─ Collection.Keyed → Map, OrderedMap, Record (and Seq.Keyed)
|
|
8
|
+
├─ Collection.Indexed → List, Stack (and Seq.Indexed)
|
|
9
|
+
└─ Collection.Set → Set, OrderedSet (and Seq.Set)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`OrderedCollection<T>` is a marker interface (no own behaviour) mixed into every type with stable iteration order: `List`, `Stack`, `OrderedMap`, `OrderedSet`, every `Collection.Indexed`/`Seq.Indexed`, and any `Seq.Keyed` built from an ordered source. Detect at runtime with `isOrdered()`. There is no `OrderedCollection` value to construct.
|
|
13
|
+
|
|
14
|
+
## Reading
|
|
15
|
+
|
|
16
|
+
### size
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
readonly size: number | undefined;
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Element count. Always defined on concrete types; may be `undefined` on a lazy `Seq` whose source has not been evaluated. Use `count()` for a guaranteed number.
|
|
23
|
+
|
|
24
|
+
### get
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
get(key: K): V | undefined;
|
|
28
|
+
get<NSV>(key: K, notSetValue: NSV): V | NSV;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Returns the value for `key`, or `notSetValue` (or `undefined`) if absent. On `Collection.Indexed`, `key` is a numeric index and negative values count from the end (`get(-1)` is last). Because a stored value may itself be `undefined`, prefer the two-arg form to disambiguate "missing" from "set to undefined".
|
|
32
|
+
|
|
33
|
+
### has / includes / contains
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
has(key: K): boolean;
|
|
37
|
+
includes(value: V): boolean;
|
|
38
|
+
contains(value: V): boolean; // alias of includes
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`has` checks key membership; `includes`/`contains` check value membership. Both use `Immutable.is` (deep value equality).
|
|
42
|
+
|
|
43
|
+
### first / last
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
first(): V | undefined;
|
|
47
|
+
first<NSV>(notSetValue: NSV): V | NSV;
|
|
48
|
+
last(): V | undefined;
|
|
49
|
+
last<NSV>(notSetValue: NSV): V | NSV;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
First/last value in iteration order, or the optional default if empty.
|
|
53
|
+
|
|
54
|
+
### getIn / hasIn
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
getIn(searchKeyPath: Iterable<unknown>, notSetValue?: unknown): unknown;
|
|
58
|
+
hasIn(searchKeyPath: Iterable<unknown>): boolean;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Walk a path of keys/indices through nested Immutable collections (and plain JS objects/arrays). See `nested-updates.md`.
|
|
62
|
+
|
|
63
|
+
## Sequence algorithms (return same kind of collection)
|
|
64
|
+
|
|
65
|
+
Most return a Collection of the same variant (`this`-typed where possible). For `Map`/`Set`, sorting/reversing returns the ordered counterpart (`OrderedMap`/`OrderedSet`).
|
|
66
|
+
|
|
67
|
+
### map / flatMap
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
map<M>(mapper: (value: V, key: K, iter: this) => M, context?: unknown): Collection<K, M>;
|
|
71
|
+
flatMap<M>(mapper: (value: V, key: K, iter: this) => Iterable<M>, context?: unknown): Collection<K, M>;
|
|
72
|
+
flatMap<KM, VM>(mapper: (value: V, key: K, iter: this) => Iterable<[KM, VM]>, context?: unknown): Collection<KM, VM>;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Always returns a new instance. `flatMap` is `map(...).flatten(true)`. On `Collection.Set` callbacks, `key === value`.
|
|
76
|
+
|
|
77
|
+
### filter / filterNot
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
filter(predicate: (value: V, key: K, iter: this) => unknown, context?: unknown): this;
|
|
81
|
+
filter<F extends V>(predicate: (value: V, key: K, iter: this) => value is F, context?: unknown): Collection<K, F>;
|
|
82
|
+
filterNot(predicate: (value: V, key: K, iter: this) => boolean, context?: unknown): this;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`filter` keeps truthy entries; `filterNot` keeps falsy ones. Type guards narrow the result.
|
|
86
|
+
|
|
87
|
+
### reverse / sort / sortBy
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
reverse(): this;
|
|
91
|
+
sort(comparator?: Comparator<V>): this;
|
|
92
|
+
sortBy<C>(mapper: (value: V, key: K, iter: this) => C, comparator?: Comparator<C>): this;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`comparator(a, b)` returns negative/zero/positive (or a `PairSorting` enum value) and must be pure. Sort is stable. On unordered collections (`Map`, `Set`), the result is the ordered counterpart. Always eager.
|
|
96
|
+
|
|
97
|
+
### slice / rest / butLast
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
slice(begin?: number, end?: number): this;
|
|
101
|
+
rest(): this; // all but first
|
|
102
|
+
butLast(): this; // all but last
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Negative slice indices count from the end. Returns the same instance if the slice is the whole collection.
|
|
106
|
+
|
|
107
|
+
### skip / skipLast / skipWhile / skipUntil / take / takeLast / takeWhile / takeUntil
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
skip(amount: number): this;
|
|
111
|
+
skipLast(amount: number): this;
|
|
112
|
+
skipWhile(predicate: (value: V, key: K, iter: this) => boolean, context?: unknown): this;
|
|
113
|
+
skipUntil(predicate: (value: V, key: K, iter: this) => boolean, context?: unknown): this;
|
|
114
|
+
take(amount: number): this;
|
|
115
|
+
takeLast(amount: number): this;
|
|
116
|
+
takeWhile(predicate: (value: V, key: K, iter: this) => boolean, context?: unknown): this;
|
|
117
|
+
takeUntil(predicate: (value: V, key: K, iter: this) => boolean, context?: unknown): this;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`*While` keeps taking/skipping while predicate is true; `*Until` until it becomes true.
|
|
121
|
+
|
|
122
|
+
### concat / flatten
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
concat(...valuesOrCollections: Array<unknown>): Collection<unknown, unknown>;
|
|
126
|
+
flatten(depth?: number): Collection<unknown, unknown>;
|
|
127
|
+
flatten(shallow?: boolean): Collection<unknown, unknown>;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Concrete variants narrow `concat`'s return type — see the per-variant sections below. `flatten()` (no args) flattens deeply; `flatten(true)` flattens one level; `flatten(0)` flattens deeply. Only flattens nested Immutable Collections, not arrays/objects.
|
|
131
|
+
|
|
132
|
+
### groupBy / partition
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
groupBy<G>(grouper: (value: V, key: K, iter: this) => G, context?: unknown): Map<G, this>;
|
|
136
|
+
partition(predicate, context?): [this, this]; // [falsy, truthy]
|
|
137
|
+
partition<F extends V, C>(predicate: (...) => value is F, context?: C): [Collection<K, V>, Collection<K, F>];
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`groupBy` is eager and returns a `Map` of group key → Collection of the same variant. `partition` returns `[notMatching, matching]` in one pass.
|
|
141
|
+
|
|
142
|
+
## Side effects and reductions
|
|
143
|
+
|
|
144
|
+
### forEach
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
forEach(sideEffect: (value: V, key: K, iter: this) => unknown, context?: unknown): number;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Returns the number of iterations executed. Unlike `Array#forEach`, returning `false` from `sideEffect` aborts iteration.
|
|
151
|
+
|
|
152
|
+
### reduce / reduceRight
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
reduce<R>(reducer: (acc: R, value: V, key: K, iter: this) => R, initialReduction: R, context?: unknown): R;
|
|
156
|
+
reduce<R>(reducer): R; // first item is the initial reduction
|
|
157
|
+
reduceRight<R>(reducer, initialReduction: R, context?: unknown): R;
|
|
158
|
+
reduceRight<R>(reducer): R;
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`reduceRight` is `reverse().reduce(...)`.
|
|
162
|
+
|
|
163
|
+
### every / some / find* / keyOf*
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
every(predicate, context?: unknown): boolean;
|
|
167
|
+
some(predicate, context?: unknown): boolean;
|
|
168
|
+
|
|
169
|
+
find(predicate, context?, notSetValue?: V): V | undefined;
|
|
170
|
+
findLast(predicate, context?, notSetValue?: V): V | undefined;
|
|
171
|
+
findEntry(predicate, context?, notSetValue?: V): [K, V] | undefined;
|
|
172
|
+
findLastEntry(predicate, context?, notSetValue?: V): [K, V] | undefined;
|
|
173
|
+
findKey(predicate, context?): K | undefined;
|
|
174
|
+
findLastKey(predicate, context?): K | undefined;
|
|
175
|
+
|
|
176
|
+
keyOf(searchValue: V): K | undefined;
|
|
177
|
+
lastKeyOf(searchValue: V): K | undefined;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`*Last` variants iterate in reverse. `keyOf`/`lastKeyOf` look up by `Immutable.is` value equality; on `Collection.Indexed` prefer `indexOf`/`lastIndexOf` for clarity.
|
|
181
|
+
|
|
182
|
+
### max / maxBy / min / minBy
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
max(comparator?: Comparator<V>): V | undefined;
|
|
186
|
+
maxBy<C>(mapper: (value, key, iter) => C, comparator?: Comparator<C>): V | undefined;
|
|
187
|
+
min(comparator?: Comparator<V>): V | undefined;
|
|
188
|
+
minBy<C>(mapper: (value, key, iter) => C, comparator?: Comparator<C>): V | undefined;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Default comparator is `>` / `<`. On ties, the first encountered wins.
|
|
192
|
+
|
|
193
|
+
### count / countBy / isEmpty / join
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
count(): number;
|
|
197
|
+
count(predicate, context?): number;
|
|
198
|
+
countBy<G>(grouper, context?): Map<G, number>;
|
|
199
|
+
isEmpty(): boolean;
|
|
200
|
+
join(separator?: string): string; // default ","
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`count()` always returns the actual size, even for a lazy `Seq` (forcing evaluation). `isEmpty` may iterate at most once on a lazy `Seq`.
|
|
204
|
+
|
|
205
|
+
### isSubset / isSuperset
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
isSubset(iter: Iterable<V>): boolean; // every value in this is in iter
|
|
209
|
+
isSuperset(iter: Iterable<V>): boolean; // every value in iter is in this
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Conversion
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
toArray(): Array<V> | Array<[K, V]>;
|
|
216
|
+
toObject(): { [key: string]: V };
|
|
217
|
+
toJS(): Array<DeepCopy<V>> | { [key: PropertyKey]: DeepCopy<V> };
|
|
218
|
+
toJSON(): Array<V> | { [key: PropertyKey]: V };
|
|
219
|
+
|
|
220
|
+
toList(): List<V>;
|
|
221
|
+
toMap(): Map<K, V>;
|
|
222
|
+
toOrderedMap(): OrderedMap<K, V>;
|
|
223
|
+
toSet(): Set<V>;
|
|
224
|
+
toOrderedSet(): OrderedSet<V>;
|
|
225
|
+
toStack(): Stack<V>;
|
|
226
|
+
|
|
227
|
+
toSeq(): Seq<K, V>;
|
|
228
|
+
toKeyedSeq(): Seq.Keyed<K, V>;
|
|
229
|
+
toIndexedSeq(): Seq.Indexed<V>;
|
|
230
|
+
toSetSeq(): Seq.Set<V>;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`toJS` is deep (recursively converts nested Immutable structures); `toJSON`/`toArray`/`toObject` are shallow. `Collection.Keyed#toArray` returns `Array<[K, V]>`; `Collection.Indexed`/`Collection.Set#toArray` return `Array<V>`. `to{List,Set,OrderedSet,Stack}` discard keys; `toMap`/`toOrderedMap` throw if keys are not hashable. See `conversions.md`.
|
|
234
|
+
|
|
235
|
+
## Comparison
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
equals(other: unknown): boolean;
|
|
239
|
+
hashCode(): number;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`equals` is `Immutable.is(this, other)` — deep value equality. `hashCode` is a stable Uint32; equal collections must produce equal hashes. See `equality.md`.
|
|
243
|
+
|
|
244
|
+
## Iteration
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
keys(): IterableIterator<K>;
|
|
248
|
+
values(): IterableIterator<V>;
|
|
249
|
+
entries(): IterableIterator<[K, V]>;
|
|
250
|
+
[Symbol.iterator](): IterableIterator<unknown>;
|
|
251
|
+
|
|
252
|
+
keySeq(): Seq.Indexed<K>;
|
|
253
|
+
valueSeq(): Seq.Indexed<V>;
|
|
254
|
+
entrySeq(): Seq.Indexed<[K, V]>;
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Default iteration shape per variant: `Keyed` yields `[K, V]` tuples; `Indexed` yields `V` in index order; `Set` yields `V` (value-unique on concrete `Set`). The `keys()`/`values()`/`entries()` methods return ES iterators (no chainable Immutable methods); `keySeq()`/`valueSeq()`/`entrySeq()` return `Seq.Indexed`s that you can chain.
|
|
258
|
+
|
|
259
|
+
### update
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
update<R>(updater: (value: this) => R): R;
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Pipe the collection through a function — useful for chaining (`coll.update(fn).map(...)`). RxJS calls this `let`, lodash calls it `thru`.
|
|
266
|
+
|
|
267
|
+
## Variant-specific methods
|
|
268
|
+
|
|
269
|
+
### Collection.Keyed
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
flip(): Collection.Keyed<V, K>;
|
|
273
|
+
mapKeys<M>(mapper: (key: K, value: V, iter: this) => M, context?: unknown): Collection.Keyed<M, V>;
|
|
274
|
+
mapEntries<KM, VM>(
|
|
275
|
+
mapper: (entry: [K, V], index: number, iter: this) => [KM, VM] | undefined,
|
|
276
|
+
context?: unknown,
|
|
277
|
+
): Collection.Keyed<KM, VM>;
|
|
278
|
+
|
|
279
|
+
concat<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): Collection.Keyed<K | KC, V | VC>;
|
|
280
|
+
concat<C>(...collections: Array<{ [key: string]: C }>): Collection.Keyed<K | string, V | C>;
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
`flip` swaps keys and values. `mapEntries` may return `undefined` to drop that entry. Inherited `keyOf` returns the key associated with a value (not an index). When iterated, yields `[K, V]`.
|
|
284
|
+
|
|
285
|
+
### Collection.Indexed
|
|
286
|
+
|
|
287
|
+
Indices are 0-based and dense ("unset" and `undefined` are indistinguishable; iteration visits every index `0..size`). Negative indices count from the end.
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
indexOf(searchValue: T): number; // -1 if absent
|
|
291
|
+
lastIndexOf(searchValue: T): number;
|
|
292
|
+
findIndex(predicate, context?): number;
|
|
293
|
+
findLastIndex(predicate, context?): number;
|
|
294
|
+
|
|
295
|
+
interpose(separator: T): this; // T, sep, T, sep, T
|
|
296
|
+
interleave(...collections: Array<Collection<unknown, T>>): this; // round-robin; stops at shortest
|
|
297
|
+
splice(index: number, removeNum: number, ...values: Array<T>): this;
|
|
298
|
+
|
|
299
|
+
zip<U>(other: Collection<unknown, U>): Collection.Indexed<[T, U]>;
|
|
300
|
+
zip(...collections): Collection.Indexed<unknown>;
|
|
301
|
+
zipAll<U>(other: Collection<unknown, U>): Collection.Indexed<[T, U]>; // pads shorter with undefined
|
|
302
|
+
zipWith<U, Z>(zipper: (value: T, otherValue: U) => Z, other: Collection<unknown, U>): Collection.Indexed<Z>;
|
|
303
|
+
|
|
304
|
+
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Collection.Indexed<T | C>;
|
|
305
|
+
fromEntrySeq(): Seq.Keyed<unknown, unknown>; // when T is [K, V], reinterpret as keyed
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
`splice`, `interleave`, and other reindexing operations are `O(N)` and cannot be used inside `withMutations`. All `Collection.Indexed` methods return re-indexed Collections — to preserve original indices as keys, call `toKeyedSeq()`.
|
|
309
|
+
|
|
310
|
+
### Collection.Set
|
|
311
|
+
|
|
312
|
+
Iterates values; on the concrete `Set`/`OrderedSet`, values are unique (`Immutable.is`). On lazy `Seq.Set`, duplicates may be present. In callbacks, `key === value`.
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
concat<U>(...collections: Array<Iterable<U>>): Collection.Set<T | U>;
|
|
316
|
+
map<M>(mapper: (value: T, key: T, iter: this) => M, context?: unknown): Collection.Set<M>;
|
|
317
|
+
flatMap<M>(mapper: (value: T, key: T, iter: this) => Iterable<M>, context?: unknown): Collection.Set<M>;
|
|
318
|
+
filter<F extends T>(predicate: (value: T, key: T, iter: this) => value is F, context?: unknown): Collection.Set<F>;
|
|
319
|
+
filter(predicate, context?): this;
|
|
320
|
+
partition(predicate, context?): [this, this];
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
`union`, `intersect`, and `subtract` are NOT on `Collection.Set` — they live on the concrete `Set`/`OrderedSet`. See `set.md`.
|
|
324
|
+
|
|
325
|
+
## OrderedCollection
|
|
326
|
+
|
|
327
|
+
`OrderedCollection<T>` is a marker interface — it adds nothing beyond `toArray()` and `[Symbol.iterator]()` that every collection already has. Its purpose is purely type-level: it identifies collections whose iteration order is deterministic and stable across operations.
|
|
328
|
+
|
|
329
|
+
Implementers: `List`, `Stack`, `OrderedMap`, `OrderedSet`, every `Collection.Indexed` (so every `Seq.Indexed`), and any `Seq.Keyed` constructed from an ordered source. `Map` and `Set` are NOT ordered. Detect at runtime:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
function isOrdered<T>(maybeOrdered: Collection<unknown, T>): maybeOrdered is OrderedCollection<T>;
|
|
333
|
+
function isOrdered(maybeOrdered: unknown): maybeOrdered is OrderedCollection<unknown>;
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
`KeyPath<K>` (used by `getIn`/`setIn`/etc.) is typed as `OrderedCollection<K> | ArrayLike<K>` — so any ordered Collection is a valid key path.
|
|
337
|
+
|
|
338
|
+
## See also
|
|
339
|
+
|
|
340
|
+
- `list.md`, `stack.md` — concrete `Collection.Indexed` types
|
|
341
|
+
- `map.md`, `record.md` — concrete `Collection.Keyed` types
|
|
342
|
+
- `set.md` — concrete `Collection.Set` types (`union`/`intersect`/`subtract` live here)
|
|
343
|
+
- `seq.md` — lazy `Seq.Keyed`/`Seq.Indexed`/`Seq.Set`
|
|
344
|
+
- `equality.md` — `equals`, `hashCode`, `Immutable.is`
|
|
345
|
+
- `conversions.md` — `toJS`/`fromJS`, `toArray`, cross-type `to*`
|
|
346
|
+
- `predicates.md` — `isCollection`, `isOrdered`, `isKeyed`, etc.
|