{"id":14155975,"url":"https://github.com/unjs/unctx","last_synced_at":"2025-06-10T22:41:45.198Z","repository":{"id":40069113,"uuid":"352956816","full_name":"unjs/unctx","owner":"unjs","description":"🍦 Composables in vanilla JS","archived":false,"fork":false,"pushed_at":"2024-05-01T19:37:56.000Z","size":564,"stargazers_count":360,"open_issues_count":12,"forks_count":12,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-05-02T01:03:36.388Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-30T10:14:00.000Z","updated_at":"2024-05-03T10:51:05.378Z","dependencies_parsed_at":"2024-03-19T00:32:58.167Z","dependency_job_id":"c5681a6e-0473-42c5-9545-22cd3e8328a9","html_url":"https://github.com/unjs/unctx","commit_stats":{"total_commits":97,"total_committers":7,"mean_commits":"13.857142857142858","dds":0.5670103092783505,"last_synced_commit":"0c6992178f399c905cd7ce6364e2a94e82d8370c"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unjs%2Functx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unjs%2Functx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unjs%2Functx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unjs%2Functx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unjs","download_url":"https://codeload.github.com/unjs/unctx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227679598,"owners_count":17803116,"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":[],"created_at":"2024-08-17T08:05:07.673Z","updated_at":"2025-06-10T22:41:45.182Z","avatar_url":"https://github.com/unjs.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","others"],"sub_categories":[],"readme":"# 🍦 unctx\n\n\u003e Composition-API in Vanilla js\n\n[![npm version][npm-v-src]][npm-v-href]\n[![npm downloads][npm-dm-src]][npm-dm-href]\n[![package phobia][packagephobia-src]][packagephobia-href]\n[![bundle phobia][bundlephobia-src]][bundlephobia-href]\n[![codecov][codecov-src]][codecov-href]\n\n## What is unctx?\n\n[Vue.js](https://vuejs.org) introduced an amazing pattern called [Composition API](https://v3.vuejs.org/guide/composition-api-introduction.html) that allows organizing complex logic by splitting it into reusable functions and grouping in logical order. `unctx` allows easily implementing composition API pattern in your javascript libraries without hassle.\n\n## Usage\n\nIn your **awesome** library:\n\n```bash\nyarn add unctx\n# or\nnpm install unctx\n```\n\n```js\nimport { createContext } from \"unctx\";\n\nconst ctx = createContext();\n\nexport const useAwesome = ctx.use;\n\n// ...\nctx.call({ test: 1 }, () =\u003e {\n  // This is similar to the vue setup function\n  // Any function called here can use `useAwesome` to get { test: 1 }\n});\n```\n\nUser code:\n\n```js\nimport { useAwesome } from \"awesome-lib\";\n\n// ...\nfunction setup() {\n  const ctx = useAwesome();\n}\n```\n\n**Note:** When no context is presented `ctx.use` will throw an error. Use `ctx.tryUse` for tolerant usages (return nullable context).\n\n### Using Namespaces\n\nTo avoid issues with multiple version of the library, `unctx` provides a safe global namespace to access context by key (kept in [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis)). **Important:** Please use a verbose name for the key to avoid conflict with other js libraries. Using the npm package name is recommended. Using symbols has no effect since it still causes multiple context issues.\n\n```js\nimport { useContext, getContext } from \"unctx\";\n\nconst useAwesome = useContext(\"awesome-lib\");\n\n// or\n// const awesomeContext = getContext('awesome-lib')\n```\n\nYou can also create your internal namespace with `createNamespace` utility for more advanced use cases.\n\n## Async Context\n\nUsing context is only possible in non-async usages and only before the first await statement. This is to make sure context is not shared between concurrent calls.\n\n```js\nasync function setup() {\n  console.log(useAwesome()); // Returns context\n  setTimeout(() =\u003e {\n    console.log(useAwesome());\n  }, 1); // Returns null\n  await new Promise((resolve) =\u003e setTimeout(resolve, 1000));\n  console.log(useAwesome()); // Returns null\n}\n```\n\nA simple workaround is caching context into a local variable:\n\n```js\nasync function setup() {\n  const ctx = useAwesome(); // We can directly access cached version of ctx\n  await new Promise((resolve) =\u003e setTimeout(resolve, 1000));\n  console.log(ctx);\n}\n```\n\nThis is not always an elegant and easy way by making a variable and passing it around. After all, this is the purpose of unctx to make sure context is magically available everywhere in composables!\n\n### Native Async Context\n\nUnctx supports Node.js [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) as a native way to preserve and track async contexts. To enable this mode, you need to set `asyncContext: true` option and also provides an implementation for `AsyncLocalStorage` (or provide `globalThis.AsyncLocalStorage` polyfill).\n\nSee [tc39 proposal for async context](https://github.com/tc39/proposal-async-context) and [cloudflare docs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/asynclocalstorage/) for relevant platform specific docs.\n\n```ts\nimport { createContext } from \"unctx\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nconst ctx = createContext({\n  asyncContext: true,\n  AsyncLocalStorage,\n});\n\nctx.call(\"123\", () =\u003e {\n  setTimeout(() =\u003e {\n    // Prints 123\n    console.log(ctx.use());\n  }, 100);\n});\n```\n\n### Async Transform\n\nSince native async context is not supported in all platforms yet, unctx provides a build-time solution that transforms async syntax to automatically restore context after each async/await statement. This requires using a bundler such as Rollup, Vite, or Webpack.\n\nImport and register transform plugin:\n\n```js\nimport { unctxPlugin } from \"unctx/plugin\";\n\n// Rollup\n// TODO: Add to rollup configuration\nunctxPlugin.rollup();\n\n// Vite\n// TODO: Add to vite configuration\nunctxPlugin.vite();\n\n// Webpack\n// TODO: Add to webpack configuration\nunctxPlugin.webpack();\n```\n\nUse `ctx.callAsync` instead of `ctx.call`:\n\n```js\nawait ctx.callAsync(\"test\", setup);\n```\n\n**_NOTE:_** `callAsync` is not transformed by default. You need to add it to the plugin's `asyncFunctions: []` option to transform it.\n\nAny async function that requires context, should be wrapped with `withAsyncContext`:\n\n```js\nimport { withAsyncContext } from \"unctx\";\n\nconst setup = withAsyncContext(async () =\u003e {\n  console.log(useAwesome()); // Returns context\n  await new Promise((resolve) =\u003e setTimeout(resolve, 1000));\n  console.log(useAwesome()); // Still returns context with dark magic!\n});\n```\n\n## Singleton Pattern\n\nIf you are sure it is safe to use a shared instance (not depending to request), you can also use `ctx.set` and `ctx.unset` for a [singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern).\n\n**Note:** You cannot combine `set` with `call`. Always use `unset` before replacing the instance otherwise you will get `Context conflict` error.\n\n```js\nimport { createContext } from \"unctx\";\n\nconst ctx = createContext();\nctx.set(new Awesome());\n\n// Replacing instance without unset\n// ctx.set(new Awesome(), true)\n\nexport const useAwesome = ctx.use;\n```\n\n## Typed Context\n\nA generic type exists on all utilities to be set for instance/context type for typescript support.\n\n```ts\n// Return type of useAwesome is Awesome | null\nconst { use: useAwesome } = createContext\u003cAwesome\u003e();\n```\n\n## Under the hood\n\nThe composition of functions is possible using temporary context injection. When calling `ctx.call(instance, cb)`, `instance` argument will be stored in a temporary variable then `cb` is called. Any function inside `cb`, can then implicitly access the instance by using `ctx.use` (or `useAwesome`)\n\n## Pitfalls\n\n**context can be only used before first await**:\n\nPlease check [Async Context](#async-context) section.\n\n**`Context conflict` error**:\n\nIn your library, you should only keep one `call()` running at a time (unless calling with the same reference for the first argument)\n\nFor instance, this makes an error:\n\n```js\nctx.call({ test: 1 }, () =\u003e {\n  ctx.call({ test: 2 }, () =\u003e {\n    // Throws error!\n  });\n});\n```\n\n## License\n\nMIT. Made with 💖\n\n\u003c!-- Refs --\u003e\n\n[npm-v-src]: https://flat.badgen.net/npm/v/unctx/latest\n[npm-v-href]: https://npmjs.com/package/unctx\n[npm-dm-src]: https://flat.badgen.net/npm/dm/unctx\n[npm-dm-href]: https://npmjs.com/package/unctx\n[packagephobia-src]: https://flat.badgen.net/packagephobia/install/unctx\n[packagephobia-href]: https://packagephobia.now.sh/result?p=unctx\n[bundlephobia-src]: https://flat.badgen.net/bundlephobia/min/unctx\n[bundlephobia-href]: https://bundlephobia.com/result?p=unctx\n[codecov-src]: https://flat.badgen.net/codecov/c/github/unjs/unctx/master\n[codecov-href]: https://codecov.io/gh/unjs/unctx\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funjs%2Functx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funjs%2Functx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funjs%2Functx/lists"}