k9guard 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,54 +0,0 @@
1
- import { randomBytes } from './crypto';
2
-
3
- export class ReverseGenerator {
4
- private static readonly easyWords = [
5
- 'cat', 'dog', 'sun', 'moon', 'star', 'fish', 'bird', 'tree',
6
- 'ball', 'book', 'door', 'lamp', 'rock', 'leaf', 'wind', 'fire',
7
- 'ice', 'sky', 'sea', 'fog', 'rain', 'snow', 'bear', 'wolf',
8
- 'coin', 'key', 'cup', 'pen', 'box', 'hat', 'map', 'web'
9
- ];
10
-
11
- private static readonly mediumWords = [
12
- 'apple', 'house', 'water', 'bread', 'ocean', 'river', 'tiger', 'eagle',
13
- 'storm', 'cloud', 'flame', 'frost', 'stone', 'metal', 'glass', 'paper',
14
- 'forest', 'desert', 'valley', 'castle', 'bridge', 'garden', 'market', 'temple',
15
- 'dragon', 'wizard', 'knight', 'shield', 'sword', 'crown', 'jewel', 'crystal',
16
- 'planet', 'galaxy', 'comet', 'nebula', 'cipher', 'enigma', 'puzzle', 'riddle',
17
- 'shadow', 'mirror', 'portal', 'beacon', 'anchor', 'compass', 'lantern', 'prism'
18
- ];
19
-
20
- private static readonly hardWords = [
21
- 'typescript', 'javascript', 'encryption', 'cryptography', 'algorithm', 'infrastructure',
22
- 'architecture', 'authentication', 'authorization', 'vulnerability', 'cybersecurity',
23
- 'blockchain', 'metamorphosis', 'constellation', 'synchronization', 'transformation',
24
- 'illumination', 'orchestration', 'denomination', 'comprehension', 'manifestation',
25
- 'extraordinary', 'revolutionary', 'sophisticated', 'kaleidoscope', 'optimization',
26
- 'crystallization', 'configuration', 'implementation', 'parallelization', 'serialization',
27
- 'decentralization', 'internationalization', 'containerization', 'virtualization',
28
- 'obfuscation', 'triangulation', 'interpolation', 'extrapolation', 'approximation',
29
- 'segmentation', 'fragmentation', 'concatenation', 'regeneration', 'degeneration'
30
- ];
31
-
32
- static generate(difficulty: 'easy' | 'medium' | 'hard'): { question: string; answer: string } {
33
- let wordPool: string[];
34
-
35
- if (difficulty === 'easy') {
36
- wordPool = this.easyWords;
37
- }
38
-
39
- if (difficulty === 'medium') {
40
- wordPool = this.mediumWords;
41
- }
42
-
43
- if (difficulty === 'hard') {
44
- wordPool = this.hardWords;
45
- }
46
-
47
- const buf = randomBytes(4);
48
- const rand = buf.readUInt32LE(0) / 0xFFFFFFFF;
49
- const text = wordPool![Math.floor(rand * wordPool!.length)]!;
50
- const reversed = text.split('').reverse().join('');
51
-
52
- return { question: reversed, answer: text };
53
- }
54
- }
@@ -1,49 +0,0 @@
1
- import { randomBytes } from './crypto';
2
-
3
- export class ScrambleGenerator {
4
- private static readonly easyWords: string[] = [
5
- 'apple', 'cat', 'dog', 'house', 'sun', 'moon', 'car', 'tree', 'book', 'water'
6
- ];
7
-
8
- private static readonly mediumWords: string[] = [
9
- 'apple', 'cat', 'dog', 'house', 'sun', 'moon', 'car', 'tree', 'book', 'water',
10
- 'bread', 'milk', 'fish', 'bird', 'flower', 'star', 'hand', 'eye', 'nose', 'mouth'
11
- ];
12
-
13
- // hard pool: longer words with uncommon letter patterns to resist guessing
14
- private static readonly hardWords: string[] = [
15
- 'typescript', 'javascript', 'algorithm', 'encryption', 'blockchain',
16
- 'metamorphosis', 'cryptography', 'synchronize', 'parallelism', 'obfuscation',
17
- 'infrastructure', 'authentication', 'authorization', 'vulnerability', 'orchestration'
18
- ];
19
-
20
- static generate(difficulty: 'easy' | 'medium' | 'hard'): { question: string; answer: string } {
21
- let pool: string[];
22
- if (difficulty === 'easy') {
23
- pool = this.easyWords;
24
- } else if (difficulty === 'medium') {
25
- pool = this.mediumWords;
26
- } else {
27
- pool = this.hardWords;
28
- }
29
-
30
- const buffer = randomBytes(4);
31
- const rand = buffer.readUInt32LE(0) / 0xFFFFFFFF;
32
- const word = pool[Math.floor(rand * pool.length)]!;
33
-
34
- const scrambled = this.scramble(word);
35
- return { question: scrambled, answer: word };
36
- }
37
-
38
- private static scramble(word: string): string {
39
- const arr = word.split('');
40
- const n = arr.length;
41
- // batch all uint32 values for Fisher-Yates in one crypto call (n-1 swaps × 4 bytes)
42
- const batchBuf = randomBytes(n * 4);
43
- for (let i = n - 1; i > 0; i--) {
44
- const j = batchBuf.readUInt32LE((n - 1 - i) * 4) % (i + 1);
45
- [arr[i]!, arr[j]!] = [arr[j]!, arr[i]!];
46
- }
47
- return arr.join('');
48
- }
49
- }
@@ -1,34 +0,0 @@
1
- import { randomBytes } from './crypto';
2
-
3
- export class SequenceGenerator {
4
- static generate(difficulty: 'easy' | 'medium' | 'hard'): { question: string; answer: number | string } {
5
- if (difficulty === 'easy') {
6
- const buffer = randomBytes(8);
7
- // use separate byte ranges for start and step to remove correlation between them
8
- const start = (buffer.readUInt32LE(0) % 5) + 1;
9
- const step = (buffer.readUInt32LE(4) % 3) + 1;
10
- const sequence = [start, start + step, start + 2 * step];
11
- const answer = start + 3 * step;
12
- return { question: `${sequence.join(', ')}, ?`, answer };
13
- }
14
- if (difficulty === 'medium') {
15
- const letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
16
- const step = 2;
17
- // max start ensures all 4 elements (start, +2, +4, +6) fit within the 10-letter array
18
- const maxStart = letters.length - step * 3 - 1;
19
- const buffer = randomBytes(4);
20
- const start = buffer.readUInt32LE(0) % (maxStart + 1);
21
- const sequence = [letters[start], letters[start + step], letters[start + 2 * step]];
22
- const answer = letters[start + 3 * step]!;
23
- return { question: `${sequence.join(', ')}, ?`, answer };
24
- }
25
- // hard: pick a random starting offset in the Fibonacci sequence so the answer is not always 5
26
- const fibs = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144];
27
- const buffer = randomBytes(4);
28
- const maxStart = fibs.length - 5; // ensure 4 shown + 1 answer all fit
29
- const start = buffer.readUInt32LE(0) % (maxStart + 1);
30
- const sequence = fibs.slice(start, start + 4);
31
- const answer = fibs[start + 4]!;
32
- return { question: `${sequence.join(', ')}, ?`, answer };
33
- }
34
- }
@@ -1,88 +0,0 @@
1
- import type { CustomQuestion } from '../types';
2
-
3
- export class CustomQuestionValidator {
4
- private static readonly MAX_QUESTIONS = 100;
5
- private static readonly MAX_QUESTION_LENGTH = 500;
6
- private static readonly MAX_ANSWER_LENGTH = 200;
7
- private static readonly MIN_QUESTION_LENGTH = 5;
8
- private static readonly MIN_ANSWER_LENGTH = 1;
9
- private static readonly VALID_DIFFICULTY = ['easy', 'medium', 'hard'];
10
-
11
- // validates the entire questions array structure and content
12
- static validate(questions: unknown): { valid: boolean; error?: string } {
13
- if (!Array.isArray(questions)) {
14
- return { valid: false, error: 'Questions must be an array' };
15
- }
16
-
17
- if (questions.length === 0) {
18
- return { valid: false, error: 'At least one question is required' };
19
- }
20
-
21
- if (questions.length > this.MAX_QUESTIONS) {
22
- return { valid: false, error: `Maximum ${this.MAX_QUESTIONS} questions allowed` };
23
- }
24
-
25
- // NOTE: validate each question individually and return detailed error if any fail
26
- for (let i = 0; i < questions.length; i++) {
27
- const questionItem = questions[i];
28
- const singleValidation = this.validateSingle(questionItem);
29
-
30
- if (!singleValidation.valid) {
31
- return { valid: false, error: `Question ${i + 1}: ${singleValidation.error}` };
32
- }
33
- }
34
-
35
- return { valid: true };
36
- }
37
-
38
- private static validateSingle(question: unknown): { valid: boolean; error?: string } {
39
- if (typeof question !== 'object' || question === null) {
40
- return { valid: false, error: 'Each question must be an object' };
41
- }
42
-
43
- const q = question as Record<string, unknown>;
44
-
45
- if (typeof q.question !== 'string') {
46
- return { valid: false, error: 'Question text must be a string' };
47
- }
48
-
49
- if (typeof q.answer !== 'string') {
50
- return { valid: false, error: 'Answer must be a string' };
51
- }
52
-
53
- if (typeof q.difficulty !== 'string') {
54
- return { valid: false, error: 'Difficulty must be specified' };
55
- }
56
-
57
- if (!this.VALID_DIFFICULTY.includes(q.difficulty)) {
58
- return { valid: false, error: `Difficulty must be one of: ${this.VALID_DIFFICULTY.join(', ')}` };
59
- }
60
-
61
- if (q.question.length < this.MIN_QUESTION_LENGTH) {
62
- return { valid: false, error: `Question must be at least ${this.MIN_QUESTION_LENGTH} characters` };
63
- }
64
-
65
- if (q.question.length > this.MAX_QUESTION_LENGTH) {
66
- return { valid: false, error: `Question must not exceed ${this.MAX_QUESTION_LENGTH} characters` };
67
- }
68
-
69
- if (q.answer.length < this.MIN_ANSWER_LENGTH) {
70
- return { valid: false, error: 'Answer cannot be empty' };
71
- }
72
-
73
- if (q.answer.length > this.MAX_ANSWER_LENGTH) {
74
- return { valid: false, error: `Answer must not exceed ${this.MAX_ANSWER_LENGTH} characters` };
75
- }
76
-
77
- return { valid: true };
78
- }
79
-
80
- // clean up whitespace from questions and answers
81
- static sanitize(questions: CustomQuestion[]): CustomQuestion[] {
82
- return questions.map(q => ({
83
- question: q.question.trim(),
84
- answer: q.answer.trim(),
85
- difficulty: q.difficulty
86
- }));
87
- }
88
- }
package/tsconfig.json DELETED
@@ -1,29 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Environment setup & latest features
4
- "lib": ["ESNext"],
5
- "target": "ESNext",
6
- "module": "Preserve",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
10
-
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "verbatimModuleSyntax": true,
15
- "noEmit": true,
16
-
17
- // Best practices
18
- "strict": true,
19
- "skipLibCheck": true,
20
- "noFallthroughCasesInSwitch": true,
21
- "noUncheckedIndexedAccess": true,
22
- "noImplicitOverride": true,
23
-
24
- // Some stricter flags (disabled by default)
25
- "noUnusedLocals": false,
26
- "noUnusedParameters": false,
27
- "noPropertyAccessFromIndexSignature": false
28
- }
29
- }