{"id":24186393,"url":"https://github.com/kanocomputing/challenge-engine","last_synced_at":"2026-02-03T10:31:32.154Z","repository":{"id":73541898,"uuid":"108010025","full_name":"KanoComputing/challenge-engine","owner":"KanoComputing","description":"JS Challenge Manager","archived":false,"fork":false,"pushed_at":"2018-07-31T11:06:58.000Z","size":63,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":22,"default_branch":"master","last_synced_at":"2025-06-22T18:52:50.859Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KanoComputing.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2017-10-23T16:40:45.000Z","updated_at":"2018-07-31T11:06:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"3e0b844b-feeb-4f78-a063-0e0706fd70ea","html_url":"https://github.com/KanoComputing/challenge-engine","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/KanoComputing/challenge-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KanoComputing%2Fchallenge-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KanoComputing%2Fchallenge-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KanoComputing%2Fchallenge-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KanoComputing%2Fchallenge-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KanoComputing","download_url":"https://codeload.github.com/KanoComputing/challenge-engine/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KanoComputing%2Fchallenge-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29041302,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-03T10:09:22.136Z","status":"ssl_error","status_checked_at":"2026-02-03T10:09:16.814Z","response_time":96,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":[],"created_at":"2025-01-13T12:35:21.974Z","updated_at":"2026-02-03T10:31:32.131Z","avatar_url":"https://github.com/KanoComputing.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Challenge Engine\n\nThis provides a challenge definition system that allows developers to define challenges as a series of steps and validations for their own product.\n\n## Challenges\n\nA challenge is a series of steps containing a validation definition that will, upon reception of an event move to the next step or not depending on the validation and the details of that event.\n\nExample:\n\n```json\n{\n    \"validation\": {\n        \"button-click\": true\n    }\n}\n```\n\nIf the event `button-click` is triggered, the challenge will move on to the next step\n\nEach step can also contain custom data with a key and a value. The key can be used to define a behavior specific to this step\n\n## API\n\n### Kano.Challenge.Definition\n\nA challenge needs to have its steps behaviors and shorthands defined before the steps are processed, this is why, the steps are only expanded when the challenge starts\n\n#### `Definition#start`\n\nExpand the steps, start the challenge with the first step. Before starting, the challenge is in an idle state.\n\n#### `Definition#nextStep`\n\nMoves to the next step.\n\n#### `Definition#defineBehavior`\n\nDefines a callback that will run every time a custom property in a step changes. This can be used to display UI hints to the user.\n\n```js\ndef.defineBehavior('banner', data =\u003e {\n    // data will be the contents of the `banner` property\n    // You can use this to customise your UI\n    myBannerEl.textContent = data.text;\n});\n```\n\n#### `Definition#defineShorthand`\n\nSome parts of the challenges will be very similar, you can define shorthands for groups of steps in your challenges that will be expanded by the engine before running the challenge.\n\n```json\n{\n    \"type\": \"button-and-dialog\",\n    \"buttonCopy\": \"Click on the button\",\n    \"dialogCopy\": \"open the dialog\"\n}\n```\n\n```js\n// This would define a shortcut with static validations but flexible copies\ndef.defineShorthand('button-and-dialog', data =\u003e {\n    //data: { type: 'button-and-dialog', buttonCopy: 'Click on the button', dialogCopy: 'open the dialog' }\n    return [{\n        banner: buttonCopy,\n        validation: {/* ... */}\n    }, {\n        banner: dialogCopy,\n        validation: {/* ... */}\n    }]\n});\n```\n\n#### `Definition#triggerEvent`\n\nNotifies the engine of an event. This will make the engine checks for the current validation\n\n```js\ndef.triggerEvent('button-tapped', { rightClick: true });\n```\n\n#### `Definition#addValidation`\n\nAdds a validation for a specific event. When this event is triggered, the engine will run the function\nto know if the event matches the validation.\n\n```js\ndef.addValidation('button-tapped', (validation, event) =\u003e {\n    // validation is the validation object defined in the JSON challenge\n    // event is the details of the event matching the type\n\n    // The function returns a boolean to indicate if the event matches completely the validation\n    return event.aProperty === 'aValue';\n});\n\n```\n\n#### `Definition#addMatchFallback`\n\nIf the event was triggered, but didn't pass the validation, the match fallback will run.\nThis allows you to define UI actions to help the user get back on track if needed\n\n```js\ndef.addMatchFallback('button-tapped', (validation, event) =\u003e {\n    if (validation.requiredValue === 'red' \u0026\u0026 event.value === 'blue') {\n        displayUIHelp('Try a different colour');\n    }\n});\n\n```\n\n#### `Definition#addOppositeAction`\n\nWhen waiting for an event, but a different one is triggered some actions can be performed using `addOppositeAction`\n\n```js\ndef.addOppositeAction('button-tapped', 'dialog-opened', (validation, event) =\u003e {\n    // We expected the user to tap the button, but instead they opened the dialog\n    // we can use this to display some indications\n    displayUIHelp(`Do not open the dialog right now, we'll need that later, but for now, it's all about tapping that button`);\n});\n```\n\n#### `Definition#addToStore`\n\nAdds data to a store, use this to create handles to runtime data of your projects that needs to be accessed later on.\n\n```js\ndef.addToStore('blockIds', 'block_0', block.id);\n```\n\n#### `Definition#getFromStore`\n\nRetrieves the data stored earlier.\n\n```js\ndef.getFromStore('blockIds', 'block_0');\n```\n\n### Kano.Challenge.ElementsRegistry\n\n#### `ElementsRegistry#add`\n\nAdds an element to the regitry with an id.\n\n```js\n// This would make the element available globally from the id `next-button`\nreg.add('next-button', document.getElementById('nxt-btn'));\n```\n\n#### `ElementsRegistry#get`\n\nGet an element from the regitry.\n\n```js\n// In a step behavior for example\ndef.defineBehavior('bouncing-arrow', data =\u003e {\n    // Get the element defined in the step\n    const target = reg.get(data.target);\n    // Create a bouncing arrow to show the user where to click\n    const arrow = new BouncingArrow();\n    // Set the target of the arrow to the element\n    arrow.setTarget(target);\n    // Bounce\n    arrow.bounce();\n});\n```\n\n### API style\n\n```js\n\n// Can extend class for challenge abstraction and resusability\n// Here we only define behaviors and validations spcific to blockly challenges\nclass BlocklyChallenge extends Challenge {\n    constructor (elementsRegistry) {\n        super();\n        this.reg = elementsRegistry\n        this.defineBehavior('phantom_block', data =\u003e {\n            this.displayPhantomBlock(data);\n        });\n    }\n    // ...\n}\n\n// A Kano Code challenge would be an extension of a blockly challenge as it also contains its own UI\nclass KanoCodeChallenge extends BlocklyChallenge {\n    constructor (elementsRegistry) {\n        super(elementsRegistry);\n        this.beacon = document.createElement('kano-beacon');\n        this.defineBehavior('beacon', data =\u003e {\n            this.beacon.target = this.elementsRegistry.get(data.target);\n        });\n    }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkanocomputing%2Fchallenge-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkanocomputing%2Fchallenge-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkanocomputing%2Fchallenge-engine/lists"}