@krtclcdy/tersio 2.25.5 → 2.25.7

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 (231) hide show
  1. package/README.md +42 -38
  2. package/dashboard/dist/index.html +15 -15
  3. package/dist/bench/code-out/pfull-0/debounce.js +13 -0
  4. package/dist/bench/code-out/pfull-0/deepClone.js +18 -0
  5. package/dist/bench/code-out/pfull-0/groupBy.js +15 -0
  6. package/dist/bench/code-out/pfull-1/debounce.js +13 -0
  7. package/dist/bench/code-out/pfull-1/deepClone.js +17 -0
  8. package/dist/bench/code-out/pfull-1/groupBy.js +14 -0
  9. package/dist/bench/code-out/pfull-2/debounce.js +13 -0
  10. package/dist/bench/code-out/pfull-2/deepClone.js +18 -0
  11. package/dist/bench/code-out/pfull-2/groupBy.js +15 -0
  12. package/dist/bench/code-out/pfull-3/debounce.js +12 -0
  13. package/dist/bench/code-out/pfull-3/deepClone.js +17 -0
  14. package/dist/bench/code-out/pfull-3/groupBy.js +7 -0
  15. package/dist/bench/code-out/pfull-4/debounce.js +10 -0
  16. package/dist/bench/code-out/pfull-4/deepClone.js +17 -0
  17. package/dist/bench/code-out/pfull-4/groupBy.js +14 -0
  18. package/dist/bench/code-out/plite-0/debounce.js +13 -0
  19. package/dist/bench/code-out/plite-0/deepClone.js +18 -0
  20. package/dist/bench/code-out/plite-0/groupBy.js +15 -0
  21. package/dist/bench/code-out/plite-1/debounce.js +13 -0
  22. package/dist/bench/code-out/plite-1/deepClone.js +17 -0
  23. package/dist/bench/code-out/plite-1/groupBy.js +11 -0
  24. package/dist/bench/code-out/plite-2/debounce.js +12 -0
  25. package/dist/bench/code-out/plite-2/deepClone.js +17 -0
  26. package/dist/bench/code-out/plite-2/groupBy.js +14 -0
  27. package/dist/bench/code-out/plite-3/debounce.js +12 -0
  28. package/dist/bench/code-out/plite-3/deepClone.js +17 -0
  29. package/dist/bench/code-out/plite-3/groupBy.js +14 -0
  30. package/dist/bench/code-out/plite-4/debounce.js +12 -0
  31. package/dist/bench/code-out/plite-4/deepClone.js +17 -0
  32. package/dist/bench/code-out/plite-4/groupBy.js +14 -0
  33. package/dist/bench/code-out/pultra-0/debounce.js +12 -0
  34. package/dist/bench/code-out/pultra-0/deepClone.js +20 -0
  35. package/dist/bench/code-out/pultra-0/groupBy.js +14 -0
  36. package/dist/bench/code-out/pultra-1/debounce.js +10 -0
  37. package/dist/bench/code-out/pultra-1/deepClone.js +17 -0
  38. package/dist/bench/code-out/pultra-1/groupBy.js +7 -0
  39. package/dist/bench/code-out/pultra-2/debounce.js +13 -0
  40. package/dist/bench/code-out/pultra-2/deepClone.js +18 -0
  41. package/dist/bench/code-out/pultra-2/groupBy.js +15 -0
  42. package/dist/bench/code-out/pultra-3/debounce.js +13 -0
  43. package/dist/bench/code-out/pultra-3/deepClone.js +18 -0
  44. package/dist/bench/code-out/pultra-3/groupBy.js +15 -0
  45. package/dist/bench/code-out/pultra-4/debounce.js +13 -0
  46. package/dist/bench/code-out/pultra-4/deepClone.js +18 -0
  47. package/dist/bench/code-out/pultra-4/groupBy.js +15 -0
  48. package/dist/bench/code-out/run-0/debounce.js +12 -0
  49. package/dist/bench/code-out/run-0/deepClone.js +17 -0
  50. package/dist/bench/code-out/run-0/groupBy.js +14 -0
  51. package/dist/bench/code-out/run-1/debounce.js +13 -0
  52. package/dist/bench/code-out/run-1/deepClone.js +15 -0
  53. package/dist/bench/code-out/run-1/groupBy.js +13 -0
  54. package/dist/bench/code-out/run-2/debounce.js +12 -0
  55. package/dist/bench/code-out/run-2/deepClone.js +18 -0
  56. package/dist/bench/code-out/run-2/groupBy.js +13 -0
  57. package/dist/bench/code-out/run-3/debounce.js +12 -0
  58. package/dist/bench/code-out/run-3/deepClone.js +18 -0
  59. package/dist/bench/code-out/run-3/groupBy.js +14 -0
  60. package/dist/bench/code-out/run-4/debounce.js +12 -0
  61. package/dist/bench/code-out/run-4/deepClone.js +16 -0
  62. package/dist/bench/code-out/run-4/groupBy.js +13 -0
  63. package/dist/cli/backups.js +7 -14
  64. package/dist/cli/banner.js +3 -3
  65. package/dist/cli/common.js +29 -13
  66. package/dist/cli/currency.js +1 -1
  67. package/dist/cli/dashboard.js +66 -35
  68. package/dist/cli/doctor-fix.js +204 -19
  69. package/dist/cli/doctor.js +263 -40
  70. package/dist/cli/hosts.js +25 -6
  71. package/dist/cli/install.js +150 -49
  72. package/dist/cli/interactive.js +3 -3
  73. package/dist/cli/manifest.js +109 -9
  74. package/dist/cli/profile.js +13 -8
  75. package/dist/cli/rtk-wiring.js +1 -1
  76. package/dist/cli/settings.js +20 -5
  77. package/dist/cli/uninstall.js +4 -4
  78. package/dist/cli/update.js +6 -7
  79. package/dist/cli/usage.js +6 -9
  80. package/dist/extensions/caveman/index.js +10 -4
  81. package/dist/extensions/caveman/rule-lite.md +77 -0
  82. package/dist/extensions/caveman/rule-megacave-lite.md +59 -0
  83. package/dist/extensions/caveman/rule-megacave-ultra.md +59 -0
  84. package/dist/extensions/caveman/rule-megacave.md +5 -3
  85. package/dist/extensions/caveman/rule-ultra.md +4 -4
  86. package/dist/extensions/caveman/rule.md +55 -45
  87. package/dist/extensions/combo/index.js +85 -25
  88. package/dist/extensions/commands/index.js +1 -1
  89. package/dist/extensions/pstack/index.js +121 -0
  90. package/dist/extensions/pstack/rule.md +141 -0
  91. package/dist/extensions/rtk/filter.js +0 -1
  92. package/dist/extensions/rtk/session.js +20 -12
  93. package/dist/extensions/server.js +90 -6
  94. package/dist/extensions/shared/agent-stats.js +214 -278
  95. package/dist/extensions/shared/brand-marks.js +33 -34
  96. package/dist/extensions/shared/carbon.js +1 -1
  97. package/dist/extensions/shared/plugin-settings.js +11 -6
  98. package/dist/extensions/shared/pricing.js +8 -16
  99. package/dist/extensions/shared/rtk-eligible.js +84 -0
  100. package/dist/extensions/shared/rtk-gain.js +5 -10
  101. package/dist/extensions/shared/session-state.js +15 -10
  102. package/dist/extensions/shared/sqlite.js +162 -0
  103. package/dist/extensions/shared/status.js +8 -9
  104. package/dist/extensions/shared/types.js +1 -1
  105. package/dist/extensions/shared/usage-ledger.js +155 -61
  106. package/dist/extensions/shared/usage-store.js +111 -52
  107. package/dist/extensions/shared/utils.js +5 -5
  108. package/dist/extensions/timing/index.js +31 -0
  109. package/dist/extensions/updater/index.js +3 -6
  110. package/dist/tersio.js +8 -3
  111. package/extensions/caveman/index.ts +10 -4
  112. package/extensions/caveman/rule-lite.md +77 -0
  113. package/extensions/caveman/rule-megacave-lite.md +59 -0
  114. package/extensions/caveman/rule-megacave-ultra.md +59 -0
  115. package/extensions/caveman/rule-megacave.md +5 -3
  116. package/extensions/caveman/rule-ultra.md +4 -4
  117. package/extensions/caveman/rule.md +55 -45
  118. package/extensions/combo/index.ts +86 -23
  119. package/extensions/commands/index.ts +1 -1
  120. package/extensions/pstack/index.ts +130 -0
  121. package/extensions/pstack/playbooks/authoring-a-skill.md +12 -0
  122. package/extensions/pstack/playbooks/autonomous-run.md +13 -0
  123. package/extensions/pstack/playbooks/autopilot-full.md +13 -0
  124. package/extensions/pstack/playbooks/autopilot-stack.md +16 -0
  125. package/extensions/pstack/playbooks/babysit.md +23 -0
  126. package/extensions/pstack/playbooks/bug-fix.md +15 -0
  127. package/extensions/pstack/playbooks/eval.md +25 -0
  128. package/extensions/pstack/playbooks/feature.md +21 -0
  129. package/extensions/pstack/playbooks/hillclimb.md +21 -0
  130. package/extensions/pstack/playbooks/investigation.md +14 -0
  131. package/extensions/pstack/playbooks/multi-phase-plan.md +155 -0
  132. package/extensions/pstack/playbooks/opening-a-pr.md +34 -0
  133. package/extensions/pstack/playbooks/orchestrate.md +111 -0
  134. package/extensions/pstack/playbooks/pause-safely.md +10 -0
  135. package/extensions/pstack/playbooks/perf-issue.md +25 -0
  136. package/extensions/pstack/playbooks/prototype.md +14 -0
  137. package/extensions/pstack/playbooks/refactoring.md +16 -0
  138. package/extensions/pstack/playbooks/runtime-forensics.md +11 -0
  139. package/extensions/pstack/playbooks/session-pickup.md +11 -0
  140. package/extensions/pstack/playbooks/shipping.md +17 -0
  141. package/extensions/pstack/playbooks/trace-forensics.md +14 -0
  142. package/extensions/pstack/playbooks/visual-parity.md +11 -0
  143. package/extensions/pstack/playbooks/worktree-cleanup.md +12 -0
  144. package/extensions/pstack/principles/attack-the-premise.md +22 -0
  145. package/extensions/pstack/principles/boundary-discipline.md +33 -0
  146. package/extensions/pstack/principles/build-the-lever.md +22 -0
  147. package/extensions/pstack/principles/encode-lessons-in-structure.md +10 -0
  148. package/extensions/pstack/principles/exhaust-the-design-space.md +8 -0
  149. package/extensions/pstack/principles/experience-first.md +18 -0
  150. package/extensions/pstack/principles/explain-the-number.md +16 -0
  151. package/extensions/pstack/principles/fix-root-causes.md +23 -0
  152. package/extensions/pstack/principles/foundational-thinking.md +20 -0
  153. package/extensions/pstack/principles/guard-the-context-window.md +11 -0
  154. package/extensions/pstack/principles/laziness-protocol.md +9 -0
  155. package/extensions/pstack/principles/make-operations-idempotent.md +23 -0
  156. package/extensions/pstack/principles/migrate-callers-then-delete-legacy-apis.md +21 -0
  157. package/extensions/pstack/principles/minimize-reader-load.md +17 -0
  158. package/extensions/pstack/principles/model-the-domain.md +23 -0
  159. package/extensions/pstack/principles/never-block-on-the-human.md +14 -0
  160. package/extensions/pstack/principles/outcome-oriented-execution.md +20 -0
  161. package/extensions/pstack/principles/prove-it-works.md +21 -0
  162. package/extensions/pstack/principles/redesign-from-first-principles.md +15 -0
  163. package/extensions/pstack/principles/separate-before-serializing-shared-state.md +15 -0
  164. package/extensions/pstack/principles/sequence-verifiable-units.md +16 -0
  165. package/extensions/pstack/principles/subtract-before-you-add.md +17 -0
  166. package/extensions/pstack/principles/test-behavior-not-implementation.md +20 -0
  167. package/extensions/pstack/principles/type-system-discipline.md +17 -0
  168. package/extensions/pstack/references/bugbot-triage.md +142 -0
  169. package/extensions/pstack/rule.md +141 -0
  170. package/extensions/pstack/skills/architect/SKILL.md +78 -0
  171. package/extensions/pstack/skills/architect/references/design-red-flags.md +57 -0
  172. package/extensions/pstack/skills/architect/references/rationale-template.md +35 -0
  173. package/extensions/pstack/skills/architect/references/runner-prompt.md +20 -0
  174. package/extensions/pstack/skills/arena/SKILL.md +47 -0
  175. package/extensions/pstack/skills/benchmark-checklist/SKILL.md +38 -0
  176. package/extensions/pstack/skills/blast-radius/SKILL.md +49 -0
  177. package/extensions/pstack/skills/bro/SKILL.md +6 -0
  178. package/extensions/pstack/skills/correct/SKILL.md +31 -0
  179. package/extensions/pstack/skills/figure-it-out/SKILL.md +51 -0
  180. package/extensions/pstack/skills/how/SKILL.md +35 -0
  181. package/extensions/pstack/skills/how/references/explainer-prompt.md +55 -0
  182. package/extensions/pstack/skills/how/references/explorer-prompt.md +35 -0
  183. package/extensions/pstack/skills/interrogate/SKILL.md +73 -0
  184. package/extensions/pstack/skills/interrogate/references/code-quality-review.md +47 -0
  185. package/extensions/pstack/skills/interrogate/references/lead-judgment.md +58 -0
  186. package/extensions/pstack/skills/interrogate/references/reviewer-prompt.md +62 -0
  187. package/extensions/pstack/skills/interrogate/references/rubric.md +77 -0
  188. package/extensions/pstack/skills/no-comments/SKILL.md +23 -0
  189. package/extensions/pstack/skills/reflect/SKILL.md +66 -0
  190. package/extensions/pstack/skills/reflect/references/divergent-reviewer.md +38 -0
  191. package/extensions/pstack/skills/reflect/references/judgment-reviewer.md +37 -0
  192. package/extensions/pstack/skills/reflect/references/synthesizer.md +56 -0
  193. package/extensions/pstack/skills/reflect/references/tooling-reviewer.md +48 -0
  194. package/extensions/pstack/skills/show-me-your-work/SKILL.md +81 -0
  195. package/extensions/pstack/skills/swarm/SKILL.md +45 -0
  196. package/extensions/pstack/skills/tdd/SKILL.md +41 -0
  197. package/extensions/pstack/skills/technical-writing/SKILL.md +105 -0
  198. package/extensions/pstack/skills/unslop/SKILL.md +61 -0
  199. package/extensions/pstack/skills/why/SKILL.md +97 -0
  200. package/extensions/pstack/skills/why/references/epistemics.md +71 -0
  201. package/extensions/pstack/skills/why/references/investigator-prompt.md +84 -0
  202. package/extensions/pstack/skills/why/references/source-playbook.md +17 -0
  203. package/extensions/pstack/skills/why/references/sources/code-archaeology.md +51 -0
  204. package/extensions/pstack/skills/why/references/sources/databricks.md +70 -0
  205. package/extensions/pstack/skills/why/references/sources/datadog.md +97 -0
  206. package/extensions/pstack/skills/why/references/sources/incident-postmortem.md +5 -0
  207. package/extensions/pstack/skills/why/references/sources/linear.md +48 -0
  208. package/extensions/pstack/skills/why/references/sources/notion.md +55 -0
  209. package/extensions/pstack/skills/why/references/sources/sentry.md +66 -0
  210. package/extensions/pstack/skills/why/references/sources/slack.md +17 -0
  211. package/extensions/pstack/skills/why/references/synthesizer-prompt.md +129 -0
  212. package/extensions/rtk/filter.ts +0 -1
  213. package/extensions/rtk/session.ts +19 -11
  214. package/extensions/server.ts +72 -7
  215. package/extensions/shared/agent-stats.ts +226 -278
  216. package/extensions/shared/brand-marks.ts +34 -34
  217. package/extensions/shared/carbon.ts +1 -1
  218. package/extensions/shared/plugin-settings.ts +12 -6
  219. package/extensions/shared/pricing.ts +10 -26
  220. package/extensions/shared/rtk-eligible.ts +83 -0
  221. package/extensions/shared/rtk-gain.ts +5 -10
  222. package/extensions/shared/session-state.ts +18 -13
  223. package/extensions/shared/sqlite.ts +162 -0
  224. package/extensions/shared/status.ts +9 -9
  225. package/extensions/shared/types.ts +2 -1
  226. package/extensions/shared/usage-ledger.ts +157 -66
  227. package/extensions/shared/usage-store.ts +120 -52
  228. package/extensions/shared/utils.ts +5 -5
  229. package/extensions/timing/index.ts +44 -0
  230. package/extensions/updater/index.ts +3 -8
  231. package/package.json +21 -9
@@ -0,0 +1,13 @@
1
+ // Debounce: fires fn once, ms after the last call, using that last call's arguments. A call during the wait restarts it.
2
+ export function debounce(fn, ms) {
3
+ let timer;
4
+ return (...args) => {
5
+ if (timer !== undefined) {
6
+ clearTimeout(timer);
7
+ }
8
+ timer = setTimeout(() => {
9
+ timer = undefined;
10
+ fn(...args);
11
+ }, ms);
12
+ };
13
+ }
@@ -0,0 +1,18 @@
1
+ // deepClone: recursive clone of plain objects, arrays, Date, and primitives. Shares no mutable structure with the input; no structuredClone or JSON round-trip.
2
+ export function deepClone(value) {
3
+ if (value === null || typeof value !== 'object') {
4
+ return value;
5
+ }
6
+ if (value instanceof Date) {
7
+ return new Date(value.getTime());
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value.map((item) => deepClone(item));
11
+ }
12
+ const source = value;
13
+ const clone = {};
14
+ for (const key of Object.keys(source)) {
15
+ clone[key] = deepClone(source[key]);
16
+ }
17
+ return clone;
18
+ }
@@ -0,0 +1,15 @@
1
+ // groupBy: buckets items by keyFn. Keys land in first-seen order and each bucket keeps the input order.
2
+ export function groupBy(items, keyFn) {
3
+ const groups = {};
4
+ for (const item of items) {
5
+ const key = keyFn(item);
6
+ const bucket = groups[key];
7
+ if (bucket === undefined) {
8
+ groups[key] = [item];
9
+ }
10
+ else {
11
+ bucket.push(item);
12
+ }
13
+ }
14
+ return groups;
15
+ }
@@ -0,0 +1,13 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ if (timer !== undefined) {
5
+ clearTimeout(timer);
6
+ }
7
+ // The closure rebinds per call, so only the last call's arguments fire.
8
+ timer = setTimeout(() => {
9
+ timer = undefined;
10
+ fn(...args);
11
+ }, ms);
12
+ };
13
+ }
@@ -0,0 +1,17 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return value;
4
+ }
5
+ if (value instanceof Date) {
6
+ return new Date(value.getTime());
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return value.map((item) => deepClone(item));
10
+ }
11
+ const source = value;
12
+ const clone = {};
13
+ for (const key of Object.keys(source)) {
14
+ clone[key] = deepClone(source[key]);
15
+ }
16
+ return clone;
17
+ }
@@ -0,0 +1,14 @@
1
+ export function groupBy(items, keyFn) {
2
+ const grouped = {};
3
+ for (const item of items) {
4
+ const key = keyFn(item);
5
+ const bucket = grouped[key];
6
+ if (bucket === undefined) {
7
+ grouped[key] = [item];
8
+ }
9
+ else {
10
+ bucket.push(item);
11
+ }
12
+ }
13
+ return grouped;
14
+ }
@@ -0,0 +1,13 @@
1
+ // debounce: fires fn once, ms after the last call, using that last call's arguments. A call during the wait restarts it.
2
+ export function debounce(fn, ms) {
3
+ let timer;
4
+ return (...args) => {
5
+ if (timer !== undefined) {
6
+ clearTimeout(timer);
7
+ }
8
+ timer = setTimeout(() => {
9
+ timer = undefined;
10
+ fn(...args);
11
+ }, ms);
12
+ };
13
+ }
@@ -0,0 +1,18 @@
1
+ // deepClone: recursive clone of plain objects, arrays, Date, and primitives. Shares no mutable structure with the input; no structuredClone or JSON round-trip.
2
+ export function deepClone(value) {
3
+ if (value === null || typeof value !== 'object') {
4
+ return value;
5
+ }
6
+ if (value instanceof Date) {
7
+ return new Date(value.getTime());
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value.map((item) => deepClone(item));
11
+ }
12
+ const source = value;
13
+ const clone = {};
14
+ for (const key of Object.keys(source)) {
15
+ clone[key] = deepClone(source[key]);
16
+ }
17
+ return clone;
18
+ }
@@ -0,0 +1,15 @@
1
+ // groupBy: buckets items by keyFn. Keys land in first-seen order and each bucket keeps the input order.
2
+ export function groupBy(items, keyFn) {
3
+ const groups = {};
4
+ for (const item of items) {
5
+ const key = keyFn(item);
6
+ const bucket = groups[key];
7
+ if (bucket === undefined) {
8
+ groups[key] = [item];
9
+ }
10
+ else {
11
+ bucket.push(item);
12
+ }
13
+ }
14
+ return groups;
15
+ }
@@ -0,0 +1,12 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ if (timer !== undefined) {
5
+ clearTimeout(timer);
6
+ }
7
+ timer = setTimeout(() => {
8
+ timer = undefined;
9
+ fn(...args);
10
+ }, ms);
11
+ };
12
+ }
@@ -0,0 +1,17 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return value;
4
+ }
5
+ if (value instanceof Date) {
6
+ return new Date(value.getTime());
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return value.map((item) => deepClone(item));
10
+ }
11
+ const source = value;
12
+ const clone = {};
13
+ for (const key of Object.keys(source)) {
14
+ clone[key] = deepClone(source[key]);
15
+ }
16
+ return clone;
17
+ }
@@ -0,0 +1,7 @@
1
+ export function groupBy(items, keyFn) {
2
+ const groups = {};
3
+ for (const item of items) {
4
+ (groups[keyFn(item)] ??= []).push(item);
5
+ }
6
+ return groups;
7
+ }
@@ -0,0 +1,10 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ clearTimeout(timer);
5
+ timer = setTimeout(() => {
6
+ timer = undefined;
7
+ fn(...args);
8
+ }, ms);
9
+ };
10
+ }
@@ -0,0 +1,17 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return value;
4
+ }
5
+ if (value instanceof Date) {
6
+ return new Date(value.getTime());
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return value.map((item) => deepClone(item));
10
+ }
11
+ const source = value;
12
+ const clone = {};
13
+ for (const key of Object.keys(source)) {
14
+ clone[key] = deepClone(source[key]);
15
+ }
16
+ return clone;
17
+ }
@@ -0,0 +1,14 @@
1
+ export function groupBy(items, keyFn) {
2
+ const groups = {};
3
+ for (const item of items) {
4
+ const key = keyFn(item);
5
+ const bucket = groups[key];
6
+ if (bucket === undefined) {
7
+ groups[key] = [item];
8
+ }
9
+ else {
10
+ bucket.push(item);
11
+ }
12
+ }
13
+ return groups;
14
+ }
@@ -0,0 +1,13 @@
1
+ // Debounce: fires fn once, ms after the last call, using that last call's arguments. A call during the wait restarts it.
2
+ export function debounce(fn, ms) {
3
+ let timer;
4
+ return (...args) => {
5
+ if (timer !== undefined) {
6
+ clearTimeout(timer);
7
+ }
8
+ timer = setTimeout(() => {
9
+ timer = undefined;
10
+ fn(...args);
11
+ }, ms);
12
+ };
13
+ }
@@ -0,0 +1,18 @@
1
+ // deepClone: recursive clone of plain objects, arrays, Date, and primitives. Shares no mutable structure with the input; no structuredClone or JSON round-trip.
2
+ export function deepClone(value) {
3
+ if (value === null || typeof value !== 'object') {
4
+ return value;
5
+ }
6
+ if (value instanceof Date) {
7
+ return new Date(value.getTime());
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value.map((item) => deepClone(item));
11
+ }
12
+ const source = value;
13
+ const clone = {};
14
+ for (const key of Object.keys(source)) {
15
+ clone[key] = deepClone(source[key]);
16
+ }
17
+ return clone;
18
+ }
@@ -0,0 +1,15 @@
1
+ // groupBy: buckets items by keyFn. Keys land in first-seen order and each bucket keeps the input order.
2
+ export function groupBy(items, keyFn) {
3
+ const groups = {};
4
+ for (const item of items) {
5
+ const key = keyFn(item);
6
+ const bucket = groups[key];
7
+ if (bucket === undefined) {
8
+ groups[key] = [item];
9
+ }
10
+ else {
11
+ bucket.push(item);
12
+ }
13
+ }
14
+ return groups;
15
+ }
@@ -0,0 +1,13 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ if (timer !== undefined) {
5
+ clearTimeout(timer);
6
+ }
7
+ // Close over the latest arguments so the trailing call forwards them.
8
+ timer = setTimeout(() => {
9
+ timer = undefined;
10
+ fn(...args);
11
+ }, ms);
12
+ };
13
+ }
@@ -0,0 +1,17 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return value;
4
+ }
5
+ if (value instanceof Date) {
6
+ return new Date(value.getTime());
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return value.map((item) => deepClone(item));
10
+ }
11
+ const source = value;
12
+ const result = {};
13
+ for (const key of Object.keys(source)) {
14
+ result[key] = deepClone(source[key]);
15
+ }
16
+ return result;
17
+ }
@@ -0,0 +1,11 @@
1
+ export function groupBy(items, keyFn) {
2
+ const result = {};
3
+ for (const item of items) {
4
+ const key = keyFn(item);
5
+ if (result[key] === undefined) {
6
+ result[key] = [];
7
+ }
8
+ result[key].push(item);
9
+ }
10
+ return result;
11
+ }
@@ -0,0 +1,12 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ if (timer !== undefined) {
5
+ clearTimeout(timer);
6
+ }
7
+ timer = setTimeout(() => {
8
+ timer = undefined;
9
+ fn(...args);
10
+ }, ms);
11
+ };
12
+ }
@@ -0,0 +1,17 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return value;
4
+ }
5
+ if (value instanceof Date) {
6
+ return new Date(value.getTime());
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return value.map((item) => deepClone(item));
10
+ }
11
+ const source = value;
12
+ const clone = {};
13
+ for (const key of Object.keys(source)) {
14
+ clone[key] = deepClone(source[key]);
15
+ }
16
+ return clone;
17
+ }
@@ -0,0 +1,14 @@
1
+ export function groupBy(items, keyFn) {
2
+ const groups = {};
3
+ for (const item of items) {
4
+ const key = keyFn(item);
5
+ const existing = groups[key];
6
+ if (existing === undefined) {
7
+ groups[key] = [item];
8
+ }
9
+ else {
10
+ existing.push(item);
11
+ }
12
+ }
13
+ return groups;
14
+ }
@@ -0,0 +1,12 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ if (timer !== undefined) {
5
+ clearTimeout(timer);
6
+ }
7
+ timer = setTimeout(() => {
8
+ timer = undefined;
9
+ fn(...args);
10
+ }, ms);
11
+ };
12
+ }
@@ -0,0 +1,17 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return value;
4
+ }
5
+ if (value instanceof Date) {
6
+ return new Date(value.getTime());
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return value.map((item) => deepClone(item));
10
+ }
11
+ const source = value;
12
+ const clone = {};
13
+ for (const key of Object.keys(source)) {
14
+ clone[key] = deepClone(source[key]);
15
+ }
16
+ return clone;
17
+ }
@@ -0,0 +1,14 @@
1
+ export function groupBy(items, keyFn) {
2
+ const groups = {};
3
+ for (const item of items) {
4
+ const key = keyFn(item);
5
+ const existing = groups[key];
6
+ if (existing === undefined) {
7
+ groups[key] = [item];
8
+ }
9
+ else {
10
+ existing.push(item);
11
+ }
12
+ }
13
+ return groups;
14
+ }
@@ -0,0 +1,12 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ if (timer !== undefined) {
5
+ clearTimeout(timer);
6
+ }
7
+ timer = setTimeout(() => {
8
+ timer = undefined;
9
+ fn(...args);
10
+ }, ms);
11
+ };
12
+ }
@@ -0,0 +1,17 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return value;
4
+ }
5
+ if (value instanceof Date) {
6
+ return new Date(value.getTime());
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return value.map((item) => deepClone(item));
10
+ }
11
+ const source = value;
12
+ const clone = {};
13
+ for (const key of Object.keys(source)) {
14
+ clone[key] = deepClone(source[key]);
15
+ }
16
+ return clone;
17
+ }
@@ -0,0 +1,14 @@
1
+ export function groupBy(items, keyFn) {
2
+ const groups = {};
3
+ for (const item of items) {
4
+ const key = keyFn(item);
5
+ const existing = groups[key];
6
+ if (existing === undefined) {
7
+ groups[key] = [item];
8
+ }
9
+ else {
10
+ existing.push(item);
11
+ }
12
+ }
13
+ return groups;
14
+ }
@@ -0,0 +1,12 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ if (timer !== undefined) {
5
+ clearTimeout(timer);
6
+ }
7
+ timer = setTimeout(() => {
8
+ timer = undefined;
9
+ fn(...args);
10
+ }, ms);
11
+ };
12
+ }
@@ -0,0 +1,20 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== "object") {
3
+ return value;
4
+ }
5
+ if (Array.isArray(value)) {
6
+ // SAFETY: every element is cloned recursively, so the result has the same shape as the input array.
7
+ return value.map((item) => deepClone(item));
8
+ }
9
+ if (value instanceof Date) {
10
+ // SAFETY: getTime() is the Date's full state, so a new Date is equal but unshared.
11
+ return new Date(value.getTime());
12
+ }
13
+ const source = value;
14
+ const clone = {};
15
+ for (const key of Object.keys(source)) {
16
+ clone[key] = deepClone(source[key]);
17
+ }
18
+ // SAFETY: only the input's own keys are rebuilt, so the clone is structurally the same T.
19
+ return clone;
20
+ }
@@ -0,0 +1,14 @@
1
+ export function groupBy(items, keyFn) {
2
+ const groups = {};
3
+ for (const item of items) {
4
+ const key = keyFn(item);
5
+ const bucket = groups[key];
6
+ if (bucket === undefined) {
7
+ groups[key] = [item];
8
+ }
9
+ else {
10
+ bucket.push(item);
11
+ }
12
+ }
13
+ return groups;
14
+ }
@@ -0,0 +1,10 @@
1
+ export function debounce(fn, ms) {
2
+ let timer;
3
+ return (...args) => {
4
+ clearTimeout(timer);
5
+ timer = setTimeout(() => {
6
+ timer = undefined;
7
+ fn(...args);
8
+ }, ms);
9
+ };
10
+ }
@@ -0,0 +1,17 @@
1
+ export function deepClone(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return value;
4
+ }
5
+ if (value instanceof Date) {
6
+ return new Date(value.getTime());
7
+ }
8
+ if (Array.isArray(value)) {
9
+ return value.map((item) => deepClone(item));
10
+ }
11
+ const source = value;
12
+ const clone = {};
13
+ for (const key of Object.keys(source)) {
14
+ clone[key] = deepClone(source[key]);
15
+ }
16
+ return clone;
17
+ }
@@ -0,0 +1,7 @@
1
+ export function groupBy(items, keyFn) {
2
+ const groups = {};
3
+ for (const item of items) {
4
+ (groups[keyFn(item)] ??= []).push(item);
5
+ }
6
+ return groups;
7
+ }
@@ -0,0 +1,13 @@
1
+ // debounce: fires fn once, ms after the last call, using that last call's arguments. A call during the wait restarts it.
2
+ export function debounce(fn, ms) {
3
+ let timer;
4
+ return (...args) => {
5
+ if (timer !== undefined) {
6
+ clearTimeout(timer);
7
+ }
8
+ timer = setTimeout(() => {
9
+ timer = undefined;
10
+ fn(...args);
11
+ }, ms);
12
+ };
13
+ }
@@ -0,0 +1,18 @@
1
+ // deepClone: recursive clone of plain objects, arrays, Date, and primitives. Shares no mutable structure with the input; no structuredClone or JSON round-trip.
2
+ export function deepClone(value) {
3
+ if (value === null || typeof value !== 'object') {
4
+ return value;
5
+ }
6
+ if (value instanceof Date) {
7
+ return new Date(value.getTime());
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value.map((item) => deepClone(item));
11
+ }
12
+ const source = value;
13
+ const clone = {};
14
+ for (const key of Object.keys(source)) {
15
+ clone[key] = deepClone(source[key]);
16
+ }
17
+ return clone;
18
+ }
@@ -0,0 +1,15 @@
1
+ // groupBy: buckets items by keyFn. Keys land in first-seen order and each bucket keeps the input order.
2
+ export function groupBy(items, keyFn) {
3
+ const groups = {};
4
+ for (const item of items) {
5
+ const key = keyFn(item);
6
+ const bucket = groups[key];
7
+ if (bucket === undefined) {
8
+ groups[key] = [item];
9
+ }
10
+ else {
11
+ bucket.push(item);
12
+ }
13
+ }
14
+ return groups;
15
+ }
@@ -0,0 +1,13 @@
1
+ // Debounce: fires fn ms after the last call with that call's arguments; a call during the wait restarts it.
2
+ export function debounce(fn, ms) {
3
+ let timer;
4
+ return (...args) => {
5
+ if (timer !== undefined) {
6
+ clearTimeout(timer);
7
+ }
8
+ timer = setTimeout(() => {
9
+ timer = undefined;
10
+ fn(...args);
11
+ }, ms);
12
+ };
13
+ }
@@ -0,0 +1,18 @@
1
+ // deepClone: recursive clone of plain objects, arrays, Date, and primitives; shares no mutable structure and never round-trips through structuredClone or JSON.
2
+ export function deepClone(value) {
3
+ if (value === null || typeof value !== 'object') {
4
+ return value;
5
+ }
6
+ if (value instanceof Date) {
7
+ return new Date(value.getTime());
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value.map((item) => deepClone(item));
11
+ }
12
+ const source = value;
13
+ const clone = {};
14
+ for (const key of Object.keys(source)) {
15
+ clone[key] = deepClone(source[key]);
16
+ }
17
+ return clone;
18
+ }
@@ -0,0 +1,15 @@
1
+ // groupBy: buckets items by keyFn; keys land in first-seen order and each bucket preserves input order.
2
+ export function groupBy(items, keyFn) {
3
+ const groups = {};
4
+ for (const item of items) {
5
+ const key = keyFn(item);
6
+ const bucket = groups[key];
7
+ if (bucket === undefined) {
8
+ groups[key] = [item];
9
+ }
10
+ else {
11
+ bucket.push(item);
12
+ }
13
+ }
14
+ return groups;
15
+ }