UNPKG

modsure

Version:

Check user content against your site rules using Jev.

202 lines (148 loc) • 8.21 kB
# modsure Moderate posts, comments, and other user-written text against your site's rules using Jev. **Someone clicks Post → modsure checks your rules → your site publishes the post or explains what needs to change.** Zero runtime or development dependencies. Native `fetch`, ESM JavaScript, TypeScript declarations, Node.js 22+. Runs on your server with your own TypeSafe API key. ## 1. Install Install modsure in your application: ```sh npm install modsure ``` These examples use ES modules. Set `"type": "module"` in your application's `package.json`, or use `.mjs` file extensions and update the imports accordingly. ## 2. Add your API key Get your own TypeSafe API key from [console.typesafe.ai](https://console.typesafe.ai). Create a `.env` file in your application's root: ```dotenv TYPESAFE_API_KEY=your_api_key ``` Add `.env` to `.gitignore`. On your hosting platform, set `TYPESAFE_API_KEY` in its environment settings. Keep the key and moderation code on your server, not in browser code. ## 3. Write your rules in one config file Create `modsure.config.js` in your application's root. This file keeps the API-key environment variable, settings, and rules together: ```js export default { apiKey: process.env.TYPESAFE_API_KEY, threshold: 0.7, rules: [ { id: 'no-spam', description: 'No unsolicited advertising or promotional links.', message: 'Please remove advertising or promotional links before posting.', }, { id: 'be-respectful', description: 'No personal attacks, harassment, or threats against other people.', message: 'Please remove personal attacks, harassment, or threats.', }, { id: 'respect-privacy', description: 'Do not share another person’s private contact details without permission.', message: 'Please remove other people’s private information.', }, ], }; ``` - `id`: a unique name for the rule, returned when it is flagged. - `description`: the rule Jev checks, written in plain language. - `message`: what your site can show the author if the rule is flagged. Defaults to `description`. - `threshold`: the violation probability that triggers rejection. Here, `0.7` means 70% or higher. A rule can set its own `threshold` to override this value. The library's default threshold is `0.5` if you omit it. Try your rules against representative posts and adjust the threshold for your community. ## 4. Check text before publishing Create `moderation.js` next to your config: ```js import { createModerator } from 'modsure'; import config from './modsure.config.js'; // Create once and reuse for incoming posts. export const moderator = createModerator(config); ``` In your server's post handler: ```js import { ModerationError } from 'modsure'; import { moderator } from './moderation.js'; // `text` is the submitted post from your request handler. try { const result = await moderator.moderate(text); if (result.allowed) { // Your application saves/publishes this exact text here. } else { // Leave the post unpublished and return these reasons to the author. const reasons = result.violations.map(({ id, message }) => ({ rule: id, message, })); console.log(reasons); } } catch (error) { if (!(error instanceof ModerationError)) throw error; // No decision was made. Keep the draft and let the author retry. // Your application should return an unavailable response, not publish. console.error(error.code); } ``` Load `.env` when starting your server, replacing `server.js` with your application's entry point: ```sh node --env-file=.env server.js ``` If your framework already loads `.env`, use its normal start command. Modsure does not automatically load `.env` or discover `modsure.config.js`: your application loads the environment and imports the config explicitly. **Modsure returns decisions; your application handles publishing and displaying feedback.** Any flagged rule makes `allowed` false. Errors never count as an approved post. Invalid configuration or empty text raises `TypeError`. Keep the rules under your site's control. Post text and rule descriptions are sent to TypeSafe for evaluation; modsure does not store or log them. ## Try a complete example With the config and `moderation.js` above, create `check-post.js`: ```js import { moderator } from './moderation.js'; const text = process.argv[2] ?? 'Thanks for writing this helpful article!'; const result = await moderator.moderate(text); console.log(JSON.stringify(result, null, 2)); ``` ```sh node --env-file=.env check-post.js 'Thanks for writing this helpful article!' ``` This makes a real API call using your key. For a full social-feed example, use the repository's `demo/` directory. ## Rules and decisions Each rule needs a unique `id` and a plain-language `description` of the site's policy. Optional `message` is the explanation shown to the author; it defaults to the description. Write one clear requirement per rule. The library sends one Noul question per rule in a single request to [TypeSafe's Jev API](https://docs.typesafe.ai/introduction/quickstart). A [Noul answer](https://docs.typesafe.ai/primitives/noul) is the probability that the content violates that rule. A rule is flagged when its probability is **greater than or equal to** its threshold. If any rule is flagged, `allowed` is false. Jev returns judgments, not generated explanations. Rejection messages come from your configured rules, so authors see your wording. These are model judgments and can be wrong; test rules and thresholds against representative posts before relying on them. ```js // Example result; probabilities depend on the model's response. { allowed: false, model: 'jev-1.13.0', violations: [{ id: 'no-spam', description: 'No unsolicited advertising or promotional links.', message: 'Please remove advertising or promotional links before posting.', threshold: 0.5, probability: 0.94, violated: true }], checks: [/* every evaluated rule, using the same fields */] } ``` ## Configuration | Option | Default | Meaning | | --- | --- | --- | | `apiKey` | Required | Your TypeSafe API key | | `rules` | Required | Nonempty array of site rules | | `threshold` | `0.5` | Default violation threshold, between 0 and 1 | | `rules[].threshold` | Global threshold | Override for a particular rule | | `model` | `jev-latest` | Jev model identifier; pin a version if desired | | `timeoutMs` | `10000` | Deadline for request and response body | | `fetch` | Native `fetch` | Optional compatible transport, useful for tests | Raising a threshold requires a higher violation probability to reject. A threshold of 0 flags every answer; 1 flags only an answer of exactly 1. The default is a starting point, not a calibrated policy for every community. Rules are copied when the moderator is created; create a new moderator when policy changes. `moderator.moderate(text, { signal })` accepts an optional `AbortSignal`. Empty or whitespace-only text and invalid configuration raise `TypeError`. Service failures throw `ModerationError`; they never return an accept/reject result: | `code` | Meaning | | --- | --- | | `HTTP_ERROR` | Provider returned an error; `status` contains the HTTP status | | `NETWORK_ERROR` | Network/transport failure | | `TIMEOUT` | Request exceeded its deadline | | `ABORTED` | Caller cancelled the request | | `INVALID_RESPONSE` | Invalid JSON, malformed data, or a missing/invalid rule answer | There are no automatic retries or hidden additional calls. Your site controls retries and how an unavailable moderation service affects posting. Limits and billing are controlled by your TypeSafe account. ## Development A working social-feed demo and browser recording instructions are in [demo/README.md](demo/README.md). Run `npm run demo` after adding your API key to `.env` to try it locally. ```sh npm test npm pack --dry-run TYPESAFE_API_KEY=your-key node examples/moderate.js 'Text to check' ``` Tests use mocked HTTP responses and require no API key. The example makes a real, billable API request. The package has no build step. ## License MIT. See [LICENSE](LICENSE).