Skip to content

Commit

Permalink
Add eslint
Browse files Browse the repository at this point in the history
Add prettier
Add github action
Add README
Fix lint errors
  • Loading branch information
ChromeQ committed Oct 6, 2023
1 parent dd3156d commit 5e61857
Show file tree
Hide file tree
Showing 15 changed files with 1,998 additions and 92 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
51 changes: 51 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"root": true,
"env": {
"browser": true,
"es6": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"extends": [
"eslint:recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:promise/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"settings": {
"import/extensions": [".js", ".jsx", ".ts", ".tsx"],
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"]
}
},
"rules": {
"no-debugger": "warn",
"no-implicit-coercion": ["error", { "allow": ["!!"] }],
"no-unused-vars": "off",
"promise/catch-or-return": [
"error",
{ "allowFinally": true, "terminationMethod": ["catch", "finally"] }
],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
},
"overrides": [
{
"files": ["./src/DataStore.ts"],
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
},
{
"files": ["./src/index.ts"],
"rules": {
"import/export": "off"
}
}
]
}
35 changes: 35 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Publish to NPM

on:
push:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 💾 Checkout
uses: actions/checkout@v3

- name: ⚙️ Setup Node
uses: actions/setup-node@v2
with:
node-version: '18.18.0'
registry-url: 'https://registry.npmjs.org'

- name: 🧰 Install
uses: borales/actions-yarn@v4
with:
cmd: install

- name: 👊 Bump
uses: anothrNick/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WITH_V: true

- name: 📦 Publish
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
18.18.0
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"printWidth": 100,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# **AGE** -- Abstract Gamification Engine.

This is a rewrite of (rodw/age)[https://github.com/rodw/age] from coffeescript to TypeScript. Mostly it is the same but with some notable changes:

1. Renamed all methods from snake_case to camelCase
2. Achievement Rules are evaluated on event received rather than on `getPlayerAchievments`
3. Achieved Achievements are also added to the `player.history`
4. Transient rules are not yet implemented
5. History entries are objects with a timestamp Date, and Achievements have an `achieved` Date
6. Methods return values rather than replying on Node style callbacks with errors

## Installation

```bash
npm install @chromeq/age
```

## Examples

For a detailed example of how to use the AGE framework, visit the base project [example docs](https://github.com/rodw/age/blob/master/docs/stack-exchange-example.litcoffee).

## Usage

Clients must:

1. Initialise the ***GameEngine*** and register ***players***. [Further Docs](https://github.com/rodw/age/blob/master/docs/stack-exchange-example.litcoffee#implementing-the-game)
```ts
import { GameEngine } from '@chromeq/age';

// Create a new instance of the gamification engine
const engine = new GameEngine();

// Register the "player" -- any object that contains at least an `id`
const player = { id: 123 };
engine.addPlayer(player);
```

2. Define ***achievement rules*** which define conditions that ***players*** must meet in order to earn the corresponding ***achievement***. [Further Docs](https://github.com/rodw/age/blob/master/docs/stack-exchange-example.litcoffee#age-achievement-rules)
```ts
const rule = new AchievementRule({
key: 'SessionCount',
multiplicity: 1,
transient: false,
predicate: (player, engine) => {
return sessionCount > 3; // Any logic required to evaluate to `boolean`
},
});

// Add the achievemt rule to the engine -- Can also be defined first and provided to the GameEngine constructor
engine.addAchievementRule(rule);
```

3. Publish ***events*** that represent actions or events that add to a player's ***event history***.
```ts
// Add an event that happens -- Also has alias `trigger`, `triggerEvent`, `dispatch` or `dispatchEvent`
engine.addEvent(player, 'SessionCount'); // TODO: Add extra data to the event
```

4. Subscribe to ***events*** or ***achievements***
```ts
// Subscribe to `'achievement-achieved'` or `'event-occurred'` -- Also has alias `listen`, `addListner` or `addEventListner`

engine.on('event-occurred', async (player, event) => {
// Useful to "chain" events and you can call `engine.addEvent` again here
console.log('EVENT OCCURRED', player, event);
});

engine.on('achievement-achieved', async (player, achievement) => {
// Congrats 🎉 - Show the appropriate UI or save state
console.log('ACHIEVEMENT ACHIEVED', player, achievement);
});
```

36 changes: 31 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,52 @@
"version": "1.0.0",
"description": "Abstract Gamification Engine",
"main": "dist/index.js",
"repository": "https://github.com/ChromeQ/rn-age",
"author": "ChromeQ",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/ChromeQ/rn-age.git"
},
"bugs": {
"url": "https://github.com/ChromeQ/rn-age/issues"
},
"homepage": "https://github.com/ChromeQ/rn-age#readme",
"keywords": [
"gamification",
"game engine",
"achievements",
"awards",
"rewards",
"badges"
],
"files": [
"dist"
],
"types": "./dist/index.d.ts",
"devDependencies": {
"@types/events": "^3.0.1",
"@types/lodash.clonedeep": "^4.5.7",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"eslint": "^8.50.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-promise": "^6.1.1",
"prettier": "^3.0.3",
"rimraf": "^5.0.5",
"typescript": "^5.2.2"
},
"dependencies": {
"events": "^3.3.0",
"lodash.clonedeep": "^4.5.0"
},
"files": [
"dist"
],
"scripts": {
"prebuild": "npm run clean",
"build": "tsc",
"clean": "rimraf dist",
"prepare": "npm run build"
"lint": "eslint '**/*.{js,ts,tsx}'",
"lint:fix": "eslint '**/*.{js,ts,tsx}' --fix",
"prepublishOnly": "npm run build"
}
}
4 changes: 2 additions & 2 deletions src/@types/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export type { GameEngine } from "../GameEngine";
export type { GameEngine } from '../GameEngine';

export type Player = Record<string, unknown> & { id: string | number };

export interface GamePlayer<T extends Player = Player> {
id: Player["id"];
id: Player['id'];
achievements: Achievement[];
history: HistoryItem[];
data: T;
Expand Down
14 changes: 7 additions & 7 deletions src/AchievementRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* This base class simply provides a handy place to document these assumptions
* and to provide default properties that are sufficient for most use cases.
*/
import type { GameEngine, GamePlayer } from "./@types";
import type { GameEngine, GamePlayer } from './@types';

/**
* The `AchievementRule` constructor accepts an optional map of properties.
Expand Down Expand Up @@ -71,13 +71,13 @@ interface AchievementRuleProps {
}

export class AchievementRule {
multiplicity: AchievementRuleProps["multiplicity"] = 0;
transient: AchievementRuleProps["transient"] = false;
key: AchievementRuleProps["key"] = () => {
throw new Error("Method not implemented.");
multiplicity: AchievementRuleProps['multiplicity'] = 0;
transient: AchievementRuleProps['transient'] = false;
key: AchievementRuleProps['key'] = () => {
throw new Error('Method not implemented.');
};
predicate: AchievementRuleProps["predicate"] = () => {
throw new Error("Method not implemented.");
predicate: AchievementRuleProps['predicate'] = () => {
throw new Error('Method not implemented.');
};

/**
Expand Down
16 changes: 5 additions & 11 deletions src/DataStore.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
/**
* DataStore defines an abstract API by which the `GameEngine` can save, persist and retrieve information about players and game states.
*/
import type {
Player,
Event,
Achievement,
HistoryItem,
GamePlayer,
} from "./@types";
import type { Player, Event, Achievement, HistoryItem, GamePlayer } from './@types';

export class DataStore {
/**
Expand All @@ -30,7 +24,7 @@ export class DataStore {
* @param player Player
*/
recordPlayer(player: Player): void {
throw new Error("Method not implemented.");
throw new Error('Method not implemented.');
}

/**
Expand All @@ -40,7 +34,7 @@ export class DataStore {
* @param event Event
*/
recordEvent(player: Player, event: Event): void {
throw new Error("Method not implemented.");
throw new Error('Method not implemented.');
}

/**
Expand All @@ -50,7 +44,7 @@ export class DataStore {
* @param achievement Achievement
*/
recordAchievement(player: Player, achievement: Achievement): void {
throw new Error("Method not implemented.");
throw new Error('Method not implemented.');
}

/**
Expand All @@ -60,7 +54,7 @@ export class DataStore {
* @returns GamePlayer
*/
getPlayer(player: Player): GamePlayer | undefined {
throw new Error("Method not implemented.");
throw new Error('Method not implemented.');
}

/**
Expand Down
Loading

0 comments on commit 5e61857

Please sign in to comment.