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.
Files changed (86) hide show
  1. package/dist/tutuca-cli.js +74 -39
  2. package/package.json +6 -7
  3. package/skill/immutable-js/SKILL.md +79 -0
  4. package/skill/immutable-js/references/collection.md +346 -0
  5. package/skill/immutable-js/references/conversions.md +99 -0
  6. package/skill/immutable-js/references/deep-updates.md +172 -0
  7. package/skill/immutable-js/references/equality.md +95 -0
  8. package/skill/immutable-js/references/list.md +266 -0
  9. package/skill/immutable-js/references/map.md +300 -0
  10. package/skill/immutable-js/references/predicates.md +93 -0
  11. package/skill/immutable-js/references/range-repeat.md +55 -0
  12. package/skill/immutable-js/references/record.md +196 -0
  13. package/skill/immutable-js/references/seq.md +248 -0
  14. package/skill/immutable-js/references/set.md +270 -0
  15. package/skill/immutable-js/references/shallow-functional.md +99 -0
  16. package/skill/immutable-js/references/stack.md +210 -0
  17. package/skill/margaui/SKILL.md +101 -0
  18. package/skill/margaui/components/accordion.md +127 -0
  19. package/skill/margaui/components/alert.md +174 -0
  20. package/skill/margaui/components/avatar.md +220 -0
  21. package/skill/margaui/components/badge.md +193 -0
  22. package/skill/margaui/components/breadcrumbs.md +103 -0
  23. package/skill/margaui/components/button.md +322 -0
  24. package/skill/margaui/components/calendar.md +67 -0
  25. package/skill/margaui/components/card.md +373 -0
  26. package/skill/margaui/components/carousel.md +387 -0
  27. package/skill/margaui/components/chat.md +171 -0
  28. package/skill/margaui/components/checkbox.md +101 -0
  29. package/skill/margaui/components/collapse.md +172 -0
  30. package/skill/margaui/components/countdown.md +165 -0
  31. package/skill/margaui/components/diff.md +53 -0
  32. package/skill/margaui/components/divider.md +107 -0
  33. package/skill/margaui/components/dock.md +173 -0
  34. package/skill/margaui/components/drawer.md +184 -0
  35. package/skill/margaui/components/dropdown.md +388 -0
  36. package/skill/margaui/components/fab.md +346 -0
  37. package/skill/margaui/components/fieldset.md +88 -0
  38. package/skill/margaui/components/file-input.md +84 -0
  39. package/skill/margaui/components/filter.md +52 -0
  40. package/skill/margaui/components/footer.md +583 -0
  41. package/skill/margaui/components/hero.md +135 -0
  42. package/skill/margaui/components/hover-3d.md +129 -0
  43. package/skill/margaui/components/hover-gallery.md +49 -0
  44. package/skill/margaui/components/indicator.md +265 -0
  45. package/skill/margaui/components/input.md +389 -0
  46. package/skill/margaui/components/join.md +100 -0
  47. package/skill/margaui/components/kbd.md +127 -0
  48. package/skill/margaui/components/label.md +102 -0
  49. package/skill/margaui/components/link.md +96 -0
  50. package/skill/margaui/components/list.md +182 -0
  51. package/skill/margaui/components/loading.md +105 -0
  52. package/skill/margaui/components/mask.md +168 -0
  53. package/skill/margaui/components/menu.md +856 -0
  54. package/skill/margaui/components/mockup-browser.md +39 -0
  55. package/skill/margaui/components/mockup-code.md +81 -0
  56. package/skill/margaui/components/mockup-phone.md +39 -0
  57. package/skill/margaui/components/mockup-window.md +33 -0
  58. package/skill/margaui/components/modal.md +178 -0
  59. package/skill/margaui/components/navbar.md +282 -0
  60. package/skill/margaui/components/pagination.md +122 -0
  61. package/skill/margaui/components/progress.md +135 -0
  62. package/skill/margaui/components/radial-progress.md +67 -0
  63. package/skill/margaui/components/radio.md +133 -0
  64. package/skill/margaui/components/range.md +134 -0
  65. package/skill/margaui/components/rating.md +170 -0
  66. package/skill/margaui/components/select.md +225 -0
  67. package/skill/margaui/components/skeleton.md +64 -0
  68. package/skill/margaui/components/stack.md +142 -0
  69. package/skill/margaui/components/stat.md +254 -0
  70. package/skill/margaui/components/status.md +73 -0
  71. package/skill/margaui/components/steps.md +138 -0
  72. package/skill/margaui/components/swap.md +152 -0
  73. package/skill/margaui/components/tab.md +248 -0
  74. package/skill/margaui/components/table.md +1018 -0
  75. package/skill/margaui/components/text-rotate.md +91 -0
  76. package/skill/margaui/components/textarea.md +85 -0
  77. package/skill/margaui/components/theme-controller.md +266 -0
  78. package/skill/margaui/components/timeline.md +1356 -0
  79. package/skill/margaui/components/toast.md +165 -0
  80. package/skill/margaui/components/toggle.md +135 -0
  81. package/skill/margaui/components/tooltip.md +181 -0
  82. package/skill/margaui/components/validator.md +163 -0
  83. package/skill/{advanced.md → tutuca/advanced.md} +5 -0
  84. package/skill/{cli.md → tutuca/cli.md} +17 -0
  85. package/skill/{core.md → tutuca/core.md} +5 -0
  86. /package/skill/{SKILL.md → tutuca/SKILL.md} +0 -0
@@ -10378,24 +10378,53 @@ __export(exports_install_skill, {
10378
10378
  run: () => run,
10379
10379
  describe: () => describe
10380
10380
  });
10381
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
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
- function findSkillDir() {
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
- if (scope === "user")
10397
- return resolve(homedir(), ".claude/skills/tutuca");
10398
- return resolve(process.cwd(), ".claude/skills/tutuca");
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 target = targetDir(scope);
10428
- const src = findSkillDir();
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
- if (existsSync(target) && !parsed.values.force) {
10435
- const existing = readdirSync(target).filter((n) => SKILL_FILES.includes(n));
10436
- if (existing.length > 0) {
10437
- process.stderr.write(`tutuca: ${target} already contains skill files. Re-run with --force to overwrite.
10438
- `);
10439
- process.exit(1);
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
- mkdirSync(target, { recursive: true });
10443
- for (const name of SKILL_FILES) {
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 the tutuca Claude Code skill into .claude/skills/tutuca/.", SKILL_FILES;
10490
+ var describe = "Install Claude Code skills (tutuca, margaui, immutable-js) into .claude/skills/.", SKILLS;
10460
10491
  var init_install_skill = __esm(() => {
10461
- SKILL_FILES = ["SKILL.md", "core.md", "cli.md", "advanced.md"];
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 as writeFileSync2 } from "node:fs";
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
- writeFileSync2(output, text);
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.40",
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/SKILL.md",
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.