{"id":42133874,"url":"https://github.com/pureartisan/user-intents","last_synced_at":"2026-01-26T16:03:41.023Z","repository":{"id":39415328,"uuid":"264423838","full_name":"pureartisan/user-intents","owner":"pureartisan","description":"A light-weight library for User Intents on Web Applications","archived":false,"fork":false,"pushed_at":"2023-01-06T06:12:22.000Z","size":134,"stargazers_count":1,"open_issues_count":12,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-21T11:54:40.740Z","etag":null,"topics":["monitoring","npm-package","typescript","user-intent"],"latest_commit_sha":null,"homepage":null,"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/pureartisan.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}},"created_at":"2020-05-16T11:40:22.000Z","updated_at":"2025-02-12T13:39:34.000Z","dependencies_parsed_at":"2023-02-05T12:16:53.231Z","dependency_job_id":null,"html_url":"https://github.com/pureartisan/user-intents","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/pureartisan/user-intents","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pureartisan%2Fuser-intents","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pureartisan%2Fuser-intents/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pureartisan%2Fuser-intents/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pureartisan%2Fuser-intents/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pureartisan","download_url":"https://codeload.github.com/pureartisan/user-intents/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pureartisan%2Fuser-intents/sbom","scorecard":{"id":749871,"data":{"date":"2025-08-11","repo":{"name":"github.com/pureartisan/user-intents","commit":"c5ba4cf80e81e9a0fd715f310544d84b6d41e1cf"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/nodejs.yml:1","Warn: no topLevel permission defined: .github/workflows/npmpublish.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":6,"reason":"dependency not pinned by hash detected -- score normalized to 6","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/pureartisan/user-intents/nodejs.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/pureartisan/user-intents/nodejs.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/nodejs.yml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/pureartisan/user-intents/nodejs.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npmpublish.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/pureartisan/user-intents/npmpublish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npmpublish.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/pureartisan/user-intents/npmpublish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npmpublish.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/pureartisan/user-intents/npmpublish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npmpublish.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/pureartisan/user-intents/npmpublish.yml/master?enable=pin","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   3 out of   3 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"32 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-44c6-4v22-4mhx","Warn: Project is vulnerable to: GHSA-4x5v-gmq8-25ch","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T19:58:43.916Z","repository_id":39415328,"created_at":"2025-08-22T19:58:43.916Z","updated_at":"2025-08-22T19:58:43.916Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28782096,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T13:55:28.044Z","status":"ssl_error","status_checked_at":"2026-01-26T13:55:26.068Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["monitoring","npm-package","typescript","user-intent"],"created_at":"2026-01-26T16:03:40.949Z","updated_at":"2026-01-26T16:03:41.015Z","avatar_url":"https://github.com/pureartisan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# User Intents\n\n[![npm version](https://badge.fury.io/js/user-intents.svg)](https://badge.fury.io/js/user-intents) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=pureartisan_user-intents\u0026metric=coverage)](https://sonarcloud.io/dashboard?id=pureartisan_user-intents) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=pureartisan_user-intents\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=pureartisan_user-intents) [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)\n\nA light-weight javascript library for User Intents on Web Applications. Supports ES6 and TypeScript.\n\n## What is an Intent?\n\nA user's intention to perform an action is considered an \"Intent\". For example, a user clicking a the \"save\" button on the settings page would be the \"save-settings\" intent.\n\nIn the above example, we would expect the settings to be saved in less than 30 seconds, or the user experience is interrupted. If everything goes well, we will get a `completed` event (which can be listened to). However, if the action to save settings takes more than 30 seconds, we would get a `timedout` event when the timeout is reached.\n\nTherefore, we will consider the \"user intent\" to be `save-settings` with a timeout of `30000 miliseconds` (or 30 seconds).\n\nWhen such events occur, we can decide what we want to do with them. For exmaple, log an error, or perhaps even let the user know the action is taking a bit longer than usual, etc.\n\n## Getting started\n\n```\nnpm install --save user-intents\n```\n\n## Usage\n\n```\nimport {\n    addEventListener,\n    startIntent, completeIntent, cancelIntent, failIntent\n} from 'user-intents';\n\n// or using `require`\n// const userIntents = require('user-intents');\n\naddEventListener('failed', (intent, timeout, data) =\u003e {\n    console.error(`${intent} failed`, data);\n});\n\n...\n\nconst SAVE_SETTINGS_INTENT_NAME = 'save-settings';\n\n...\n\nfunction onUserClicksSaveButton() {\n    // save settings via API call or any asynchronous action\n    callAsyncActionToSave();\n\n    // ---- start user intent\n    const timeuot = 30000;\n    // arbitarary data passed to events, can be anything you want\n    const data = { profile: true };\n    startIntent(SAVE_SETTINGS_INTENT_NAME, timeuot, data);\n}\n\nfunction onUserClicksCancelButtonWhileSavingInProgress() {\n    // intent is cancelled\n    // it will trigger the `cancelled` event\n    cancelIntent(SAVE_SETTINGS_INTENT_NAME);\n}\n\nfunction onSaveSuccessful() {\n    // intent was successful\n    // it will trigger the `completed` event\n    completeIntent(SAVE_SETTINGS_INTENT_NAME);\n}\n\nfunction onSaveError() {\n    // user intented to save, but it failed\n    // so mark it as failed, it will trigger the `failed` event\n    failIntent(SAVE_SETTINGS_INTENT_NAME);\n}\n\n```\n\n## Intents\n\n### `startIntent(name, timeout, data?)`\n\nStarts an intent with a given name. Stating an intent with the same name, while the previous intent is still pending will trigger an `incompleted` event for the previously pending intent.\n\n| Parameter | Type | Description |\n|-----------|--------|----------------------------------------------------------------------------------------------------------------------|\n| `name` | string | Name of the intent, and this is case sensitive. |\n| `timeout` | number | The timeout (max duration) this intent should be completed within. Otherwise the `timedout` event will be triggered. |\n| `data` | any | [Optional] Arbitaray data that will be passed to events, when an event is triggered for this intent |\n\n### `completeIntent(name)`\n\nCompletes a pending intent. If no intent by that name is pending, a warning will be logged in console. Use `disableWarnings()` to suppress the warnings. Completing an intent will trigger the `completed` event for that intent.\n\n| Parameter | Type | Description |\n|-----------|--------|----------------------------------------------------------------------------------------------------------------------|\n| `name` | string | Name of the intent, and this is case sensitive. |\n\n### `cancelIntent(name)`\n\nCancels a pending intent. If no intent by that name is pending, a warning will be logged in console. Use `disableWarnings()` to suppress the warnings. Cancelling an intent will trigger the `cancelled` event for that intent.\n\n| Parameter | Type | Description |\n|-----------|--------|----------------------------------------------------------------------------------------------------------------------|\n| `name` | string | Name of the intent, and this is case sensitive. |\n\n### `failIntent(name)`\n\nFails a pending intent. If no intent by that name is pending, a warning will be logged in console. Use `disableWarnings()` to suppress the warnings. Failing an intent will trigger the `failed` event for that intent.\n\n| Parameter | Type | Description |\n|-----------|--------|----------------------------------------------------------------------------------------------------------------------|\n| `name` | string | Name of the intent, and this is case sensitive. |\n\n\n## Events\n\nYou can listen to any of the following events using `addEventListener` or remove an existing event listener using `removeEventListener`.\n\n| Event         | Description |\n|---------------|-------------|\n| `completed`   | Triggered when the `completeIntent` method is called on a pending intent |\n| `failed`      | Triggered when the `failIntent` method is called on a pending intent |\n| `timedout`    | Triggered when a pending intent reaches the timeout |\n| `cancelled`   | Triggered when the `cancelIntent` method is called on a pending intent |\n| `incompleted` | Triggered when the `startIntent` method is called for the same intent name while the previous intent is still pending |\n\n### `addEventListener(event, listener)`\n\nAdds an event listener for a given event.\n\n| Parameter | Type | Description |\n|------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `event` | string | Event name and has to be one of the following: [`completed`, `incompleted`, `cancelled`, `failed`, `timedout`] |\n| `listener` | function | The callback function. The following will be passed as arguments: [ `name`, `timeout`, `data` ], same as what was passed in when `startIntent()` was called. |\n\n\n### `removeEventListener(event, listener)`\n\n| Parameter | Type | Description |\n|------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `event` | string | Event name and has to be one of the following: [`completed`, `incompleted`, `cancelled`, `failed`, `timedout`] |\n| `listener` | function | The callback function. Should the the same reference to the previously added callback function, otherwise this action is ignored. |\n\n## Miscellaneous\n\n### `disableWarnings()`\n\nWarnings are enabled by default. Calling this method will disable logging warnings to console by this library. See `Intents` section above for more descriptions of what kind of warnings are logged to console. To re-enable warnings, simply call `enableWarnings()`\n\n### `enableWarnings()`\n\nWarnings are enabled by default. This method can be used to re-enable warnings if it was disabled at some point during runtime. See above for more details.\n\n### `ignoreFirstWarning()`\n\nThis method will ignore the first warning from being logged, and is useful in certain sitautions. In those cases, this method must be called at the start of the application, as early as possible. This method is unnecessary if warnings are disable completely using `disableWarnings()`.\n\nLet's look at an example:\nWe are tracking intents of the user going between pages/screens in the application. This is usually handled by `startIntent()` when the user clicks on a navigation link on the application to take the user to a certain page. When the page transitions/changes and the user is on the new page, we will call the `completeIntent()` method. However, if we come to this page the very first time and there was no `startIntent()` called at page load, there will be a warning. Calling `ignoreFirstWarning()` allows you to ignore logging this very first warning.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpureartisan%2Fuser-intents","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpureartisan%2Fuser-intents","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpureartisan%2Fuser-intents/lists"}