{"id":23684204,"url":"https://github.com/mrjacobbloom/echo","last_synced_at":"2026-01-06T07:30:19.206Z","repository":{"id":44211856,"uuid":"196922258","full_name":"mrjacobbloom/echo","owner":"mrjacobbloom","description":"Fun with proxies. Repeats after you.","archived":false,"fork":false,"pushed_at":"2023-07-18T21:06:16.000Z","size":1120,"stargazers_count":2,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-30T00:08:32.817Z","etag":null,"topics":["console-log","metaprogramming","proxy","syntax-highlighting"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mrjacobbloom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-07-15T04:11:26.000Z","updated_at":"2022-12-29T18:54:43.000Z","dependencies_parsed_at":"2024-01-13T22:24:18.790Z","dependency_job_id":"4e427df0-0e7f-486e-9901-1a66f6754534","html_url":"https://github.com/mrjacobbloom/echo","commit_stats":{"total_commits":67,"total_committers":2,"mean_commits":33.5,"dds":"0.17910447761194026","last_synced_commit":"6ffb2b5fb96ae59c2d4dcefcc1bebeaf3ec915c9"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrjacobbloom%2Fecho","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrjacobbloom%2Fecho/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrjacobbloom%2Fecho/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrjacobbloom%2Fecho/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrjacobbloom","download_url":"https://codeload.github.com/mrjacobbloom/echo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239736080,"owners_count":19688385,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["console-log","metaprogramming","proxy","syntax-highlighting"],"created_at":"2024-12-29T20:35:53.210Z","updated_at":"2026-01-06T07:30:19.169Z","avatar_url":"https://github.com/mrjacobbloom.png","language":"JavaScript","funding_links":[],"categories":["Proxy Resources"],"sub_categories":["Modules"],"readme":"# Echo [![license](https://img.shields.io/npm/l/echo-proxy)](https://github.com/mrjacobbloom/echo/blob/master/LICENSE) [![npm](https://img.shields.io/npm/v/echo-proxy)](https://www.npmjs.com/package/echo-proxy) [![Try echo-proxy on RunKit](https://badge.runkitcdn.com/echo-proxy.svg)](https://npm.runkit.com/echo-proxy)\n\nFun with proxies. Do any combination of calls, constructs, and dot-walks\nstarting with `Echo`, and an approximation of what you typed is logged to the\nconsole (with syntax highlighting!) Play with it in your browser on\n[RunKit](https://npm.runkit.com/echo-proxy)!\n\n![Demo of Echo in action](https://raw.githubusercontent.com/mrjacobbloom/echo/master/demo.gif)\n\n```javascript\n/* \u003e */ Echo\n/* \u003c */ Echo\n\n/* \u003e */ new Echo.foo\n/* \u003c */ new Echo.foo\n\n/* \u003e */ new ((new Echo).bar.baz(1, Echo.qux, new Echo(7)))\n/* \u003c */ new ((new Echo).bar.baz(1, Echo.qux, new Echo(7)))\n\n/* \u003e */ Echo.gfdgfd[4][' fff '](undefined, true, -Infinity)\n/* \u003c */ Echo.gfdgfd[4][' fff '](undefined, true, -Infinity)\n\n/* \u003e */ Echo`foo${[1, 2, 3, 4]}bar`.baz\n/* \u003c */ Echo`foo${[1, 2, 3, 4]}bar`.baz\n```\n\n## Usage\n\n_Requirements: Browsers released after 2018. Node 11 or newer._\n\n### RunKit\n\nThe easiest way to play with Echo is in your browser on [RunKit](https://npm.runkit.com/echo-proxy)!\n\n### Browser\n\nAnother way to use Echo is to paste [`dist/echo.min.js`](https://raw.githubusercontent.com/mrjacobbloom/echo/master/dist/echo.min.js)\ninto your console (note: Echo is harmless, but you should always\n[be careful about pasting code into your console!](https://en.wikipedia.org/wiki/Self-XSS)).\n\n### Node\n\n```shell\nnpm install echo-proxy\nnode --require echo-proxy\n```\n\nYou can also import it into your code. Note that Echo attaches itself to the global,\nso you don't actually want the import to be named Echo.\n\n```javascript\n// in ES module (package.json type=module)\nimport {} from 'echo-proxy';\n\n// or in script (non-module)\nrequire('echo-proxy');\n```\n\n## API\n\nThe public API gets special treatment, and is not echoed. This also goes for\nJS-internal things like `__proto__` that lead to weird behavior when messed\nwith. The following builtin functions are overridden to return the stringified\nEcho:\n\n- `Echo.toString`\n- `Echo[Symbol.toPrimitive]`\n- `Echo[Symbol.for('nodejs.util.inspect.custom')]`\n\n### `Echo.render()`\n\n_Example: `const { tokens } = Echo.foo().render()`_\n\nReturns an object with the following shape:\n\n```\n{\n  tokens: {value: string, type: string}[] -- The output broken up into typed \"tokens,\" which is what Echo uses to do syntax highlighting.\n  plaintext: string -- The tokens strung together without any syntax highlighting.\n  formatted: string[] -- The tokens with syntax highlighting, formatted as arguments to console.log.\n  theme: { [tokenType]: [colorString] } -- The \"theme\" object that maps \"token types\" to either ANSI colors or hsl colors (or null if options.colorMode='off').\n}\n```\n\n### `Echo.print()`\n\n_Example: `const value = Echo.foo(); value.print();`_\n\nLogs the value to the console.\n\n### `Echo.then(...)`\n\n_Example: `const { tokens } = await Echo.foo()`_\n\nResolves to the value of `Echo.render()`.\n\n### `Echo.options`\n\n_Example: `Echo.options.theme = 'firefox'`_\n\nCan be used to configure Echo. This includes options to make it reflect how you\nwould write code (for example, to always use parentheses with a constructor),\nand options that affect its behavior in other ways.\n\n| Property | Type/Options | Default | Explanation |\n| -------- | ------- | ------- | ----------- |\n| `bracketNotationOptional` | Boolean | `true` | When true, use heuristics to decide when to use dot notation. Otherwise, always use square-bracket notation. |\n| `parensOptional` | Boolean | `true` | When true, use heuristics to decide whether to wrap sub-expressions in parentheses. When false, always wrap them. |\n| `constructorParensOptional` | Boolean | `true` | When true, drop parentheses for constructors with no arguments. |\n| `stringDelimiter` | String | `\"'\"` | What character is used to delimit (bookend) strings, and is escaped from strings. If the string contains a newline, this is ignored and the string is rendered as a template string. |\n| `colorMode` | `\"browser\"`, `\"ansi\"`, `\"off\"` | [auto detected] | Enable or disable syntax highlighting and the mode used to set colors. |\n| `theme` | `\"chrome\"`, `\"firefox\"`, Object | [auto detected] | Which DevTools' syntax highlighting theme to use. Defaults to Chrome unless you're using Firefox. Can also be a custom theme object whose keys are TokenTypes and whose values are ANSI color codes or CSS styles for the given token type (depending on colorMode). |\n| `autoLog` | Boolean | `true` | When true, automatically console.log the output. See below. |\n| `autoLogMinLength` | Number | `1` | The minimum number of tokens required to log to console. You can use this to avoid printing in some autocomplete situations. See below. |\n\n#### The Auto-Logging Problem\n\nWhen `Echo.options.autoLog=true`, the output is automatically logged to the\nconsole. If you're in an environment with autocomplete (like Chrome console),\nthis mode may cause Echo to be logged several times as you type.\n\nA little explanation of the way Echo's auto-logging works: it never knows if the\ncode has finished evaluating and if it's time to print the output. Here\nare a couple examples to illustrate the problem:\n\n- Consider the expression `Echo.foo.bar`. The sub-expression `Echo.foo` is completely evaluated before it looks for `.bar`. While this sub-expression is evaluating, all Echo knows is that the user tried to access `Echo.foo`, it can never tell if that getter is the last one in the chain or if there are more to come.\n- Consider the expression `Echo(Echo.foo)`. We don't want to print the inner Echo on its own line, we want them both to appear in one line.\n\nEcho circumvents this by using setTimeout to wait 1 tick. All the various Echoes\nin your code calculate their outputs individually, and then the longest output\nis printed.\n\nThis timeout process is skipped when you manually call `Echo.render()` or\n`Echo.print()`. Instead of waiting a tick and logging, everything is handled\nimmediately.\n\nSo that's how auto-logging works. So why does your browser sometimes cause it to\nprint while you're typing? This happens when your browser tries to inspect the\nEcho object to populate the console's autocomplete/intellisense system. From\nEcho's perspective, this snooping looks the same as accessing values on it, so\nit tries to render the output.\n\nTo mitigate the extra rendering, we have `Echo.options.autoLogMinLength`. This\nsays that any Echo output less than minLength items long shouldn't be printed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrjacobbloom%2Fecho","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrjacobbloom%2Fecho","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrjacobbloom%2Fecho/lists"}