opengrammar-server 2.0.615350
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.
- package/README.npm.md +95 -0
- package/bin/opengrammar-server.js +111 -0
- package/dist/server.js +48639 -0
- package/package.json +80 -0
- package/server-node.ts +159 -0
- package/server.ts +15 -0
- package/src/analyzer.ts +542 -0
- package/src/dictionary.ts +1973 -0
- package/src/index.ts +978 -0
- package/src/nlp/nlp-engine.ts +17 -0
- package/src/nlp/tone-analyzer.ts +269 -0
- package/src/rephraser.ts +146 -0
- package/src/rules/categories/academic-writing.ts +182 -0
- package/src/rules/categories/adjectives-adverbs.ts +152 -0
- package/src/rules/categories/articles.ts +160 -0
- package/src/rules/categories/business-writing.ts +250 -0
- package/src/rules/categories/capitalization.ts +79 -0
- package/src/rules/categories/clarity.ts +117 -0
- package/src/rules/categories/common-errors.ts +601 -0
- package/src/rules/categories/confused-words.ts +219 -0
- package/src/rules/categories/conjunctions.ts +176 -0
- package/src/rules/categories/dangling-modifiers.ts +123 -0
- package/src/rules/categories/formality.ts +274 -0
- package/src/rules/categories/formatting-idioms.ts +323 -0
- package/src/rules/categories/gerund-infinitive.ts +274 -0
- package/src/rules/categories/grammar-advanced.ts +294 -0
- package/src/rules/categories/grammar.ts +286 -0
- package/src/rules/categories/inclusive-language.ts +280 -0
- package/src/rules/categories/nouns-pronouns.ts +233 -0
- package/src/rules/categories/prepositions-extended.ts +217 -0
- package/src/rules/categories/prepositions.ts +159 -0
- package/src/rules/categories/punctuation.ts +347 -0
- package/src/rules/categories/quantity-agreement.ts +200 -0
- package/src/rules/categories/readability.ts +293 -0
- package/src/rules/categories/sentence-structure.ts +100 -0
- package/src/rules/categories/spelling-advanced.ts +164 -0
- package/src/rules/categories/spelling.ts +119 -0
- package/src/rules/categories/style-tone.ts +511 -0
- package/src/rules/categories/style.ts +78 -0
- package/src/rules/categories/subject-verb-agreement.ts +201 -0
- package/src/rules/categories/tone-rules.ts +206 -0
- package/src/rules/categories/verb-tense.ts +582 -0
- package/src/rules/context-filter.ts +446 -0
- package/src/rules/index.ts +96 -0
- package/src/rules/ruleset-part1-cj-pu-sp.json +657 -0
- package/src/rules/ruleset-part1-np-ad-aa-pr.json +831 -0
- package/src/rules/ruleset-part1-ss-vt.json +907 -0
- package/src/rules/ruleset-part2-cw-st-nf.json +318 -0
- package/src/rules/ruleset-part3-aw-bw-il-rd.json +161 -0
- package/src/rules/types.ts +79 -0
- package/src/shared-types.ts +152 -0
- package/src/spellchecker.ts +418 -0
- package/tsconfig.json +25 -0
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opengrammar-server",
|
|
3
|
+
"version": "2.0.615350",
|
|
4
|
+
"description": "Privacy-first, open-source grammar intelligence server ā run it on any device",
|
|
5
|
+
"homepage": "https://opengrammer.eu.cc",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/swadhinbiswas/opengrammar.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/swadhinbiswas/opengrammar/issues"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Swadhin Biswas",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"grammar",
|
|
17
|
+
"grammar-checker",
|
|
18
|
+
"nlp",
|
|
19
|
+
"spell-check",
|
|
20
|
+
"writing",
|
|
21
|
+
"hono",
|
|
22
|
+
"edge",
|
|
23
|
+
"api",
|
|
24
|
+
"server",
|
|
25
|
+
"opengrammar"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "dist/server.js",
|
|
29
|
+
"bin": {
|
|
30
|
+
"opengrammar-server": "bin/opengrammar-server.js",
|
|
31
|
+
"opengrammar": "bin/opengrammar-server.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"bin/",
|
|
35
|
+
"dist/",
|
|
36
|
+
"src/",
|
|
37
|
+
"server.ts",
|
|
38
|
+
"server-node.ts",
|
|
39
|
+
"tsconfig.json",
|
|
40
|
+
"README.npm.md"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"dev": "bun --hot server-bun.ts",
|
|
44
|
+
"start": "bun server-bun.ts",
|
|
45
|
+
"dev:node": "tsx watch server-node.ts",
|
|
46
|
+
"start:node": "tsx server-node.ts",
|
|
47
|
+
"build": "bun build server.ts --outdir dist --target node --format esm",
|
|
48
|
+
"build:check": "tsc --noEmit",
|
|
49
|
+
"prepublishOnly": "npm run build:check && npm run build",
|
|
50
|
+
"deploy": "wrangler deploy --env production",
|
|
51
|
+
"deploy:vercel": "vercel --prod",
|
|
52
|
+
"deploy:render": "git push render main",
|
|
53
|
+
"deploy:railway": "railway up",
|
|
54
|
+
"test": "bun test"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@hono/node-server": "^1.19.9",
|
|
58
|
+
"@hono/zod-validator": "^0.7.6",
|
|
59
|
+
"compromise": "^14.15.0",
|
|
60
|
+
"groq-sdk": "^0.5.0",
|
|
61
|
+
"hono": "^4.11.10",
|
|
62
|
+
"openai": "^4.22.0",
|
|
63
|
+
"zod": "^3.25.76"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@cloudflare/workers-types": "^4.20260219.0",
|
|
67
|
+
"@netlify/functions": "^5.1.5",
|
|
68
|
+
"@types/bun": "^1.3.10",
|
|
69
|
+
"@types/node": "^25.3.0",
|
|
70
|
+
"tsx": "^4.21.0",
|
|
71
|
+
"typescript": "^5.9.3",
|
|
72
|
+
"wrangler": "^4.66.0"
|
|
73
|
+
},
|
|
74
|
+
"engines": {
|
|
75
|
+
"node": ">=18.0.0"
|
|
76
|
+
},
|
|
77
|
+
"publishConfig": {
|
|
78
|
+
"access": "public"
|
|
79
|
+
}
|
|
80
|
+
}
|
package/server-node.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { serve } from '@hono/node-server';
|
|
2
|
+
import { Hono } from 'hono';
|
|
3
|
+
import { cors } from 'hono/cors';
|
|
4
|
+
import { logger } from 'hono/logger';
|
|
5
|
+
import { RuleBasedAnalyzer } from './src/analyzer.js';
|
|
6
|
+
import { PROVIDERS } from './src/shared-types.js';
|
|
7
|
+
|
|
8
|
+
const app = new Hono();
|
|
9
|
+
|
|
10
|
+
// Middleware
|
|
11
|
+
app.use('/*', logger());
|
|
12
|
+
app.use('/*', cors());
|
|
13
|
+
|
|
14
|
+
// Health check endpoint
|
|
15
|
+
app.get('/health', (c) => {
|
|
16
|
+
return c.json({
|
|
17
|
+
status: 'healthy',
|
|
18
|
+
timestamp: new Date().toISOString(),
|
|
19
|
+
environment: 'nodejs',
|
|
20
|
+
version: '2.0.0',
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// List available providers
|
|
25
|
+
app.get('/providers', (c) => {
|
|
26
|
+
return c.json({
|
|
27
|
+
providers: PROVIDERS,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Get models for a provider
|
|
32
|
+
app.post('/models', async (c) => {
|
|
33
|
+
try {
|
|
34
|
+
const { provider } = await c.req.json().catch(() => ({}));
|
|
35
|
+
const providerConfig = PROVIDERS.find((p) => p.id === provider);
|
|
36
|
+
return c.json({
|
|
37
|
+
provider,
|
|
38
|
+
models: providerConfig?.models || [],
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Failed to fetch models:', error);
|
|
42
|
+
return c.json({ models: [] });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Main analysis endpoint - rule-based only (fast and reliable)
|
|
47
|
+
app.post('/analyze', async (c) => {
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const body = await c.req.json();
|
|
52
|
+
const { text, dictionary } = body;
|
|
53
|
+
|
|
54
|
+
// Validate input
|
|
55
|
+
if (!text || typeof text !== 'string') {
|
|
56
|
+
return c.json({ error: 'Invalid request: text is required' }, 400);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (text.length > 50000) {
|
|
60
|
+
return c.json({ error: 'Text exceeds maximum length of 50,000 characters' }, 413);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(`[Analyze] Text length: ${text.length}`);
|
|
64
|
+
|
|
65
|
+
// Run rule-based analysis
|
|
66
|
+
const issues = RuleBasedAnalyzer.analyze(text, {
|
|
67
|
+
dictionary: dictionary || [],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const duration = Date.now() - startTime;
|
|
71
|
+
|
|
72
|
+
console.log(`[Analyze] Found ${issues.length} issues in ${duration}ms`);
|
|
73
|
+
|
|
74
|
+
return c.json({
|
|
75
|
+
issues,
|
|
76
|
+
metadata: {
|
|
77
|
+
textLength: text.length,
|
|
78
|
+
issuesCount: issues.length,
|
|
79
|
+
processingTimeMs: duration,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('Analysis error:', error);
|
|
84
|
+
return c.json(
|
|
85
|
+
{
|
|
86
|
+
error: 'Failed to analyze text',
|
|
87
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
88
|
+
issues: [],
|
|
89
|
+
},
|
|
90
|
+
500,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Tone rewriting endpoint (requires API key)
|
|
96
|
+
app.post('/rewrite', async (c) => {
|
|
97
|
+
try {
|
|
98
|
+
const { text, tone, apiKey } = await c.req.json();
|
|
99
|
+
|
|
100
|
+
if (!text || !tone) {
|
|
101
|
+
return c.json({ error: 'Text and tone are required' }, 400);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!apiKey) {
|
|
105
|
+
return c.json({
|
|
106
|
+
error: 'API key required for rewriting',
|
|
107
|
+
original: text,
|
|
108
|
+
rewritten: text,
|
|
109
|
+
tone,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// LLM rewriting would go here
|
|
114
|
+
return c.json({
|
|
115
|
+
original: text,
|
|
116
|
+
rewritten: text,
|
|
117
|
+
tone,
|
|
118
|
+
});
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('Rewrite error:', error);
|
|
121
|
+
return c.json(
|
|
122
|
+
{
|
|
123
|
+
error: 'Failed to rewrite text',
|
|
124
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
125
|
+
},
|
|
126
|
+
500,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Error handler
|
|
132
|
+
app.onError((err, c) => {
|
|
133
|
+
console.error('Unhandled error:', err);
|
|
134
|
+
return c.json(
|
|
135
|
+
{
|
|
136
|
+
error: 'Internal server error',
|
|
137
|
+
message: err.message,
|
|
138
|
+
},
|
|
139
|
+
500,
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// 404 handler
|
|
144
|
+
app.notFound((c) => {
|
|
145
|
+
return c.json({ error: 'Not found' }, 404);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const port = 8787;
|
|
149
|
+
console.log(`\nšŖ¶ OpenGrammar Backend starting on http://localhost:${port}\n`);
|
|
150
|
+
|
|
151
|
+
serve(
|
|
152
|
+
{
|
|
153
|
+
fetch: app.fetch,
|
|
154
|
+
port,
|
|
155
|
+
},
|
|
156
|
+
(info) => {
|
|
157
|
+
console.log(`ā
Server ready on http://localhost:${info.port}`);
|
|
158
|
+
},
|
|
159
|
+
);
|
package/server.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { serve } from '@hono/node-server';
|
|
2
|
+
import app from './src/index.js';
|
|
3
|
+
|
|
4
|
+
const port = parseInt(process.env.PORT || '8787', 10);
|
|
5
|
+
const host = process.env.HOST || '0.0.0.0';
|
|
6
|
+
|
|
7
|
+
serve(
|
|
8
|
+
{ fetch: app.fetch, port, hostname: host },
|
|
9
|
+
(info) => {
|
|
10
|
+
console.log(` ā
OpenGrammar ready ā http://${info.address}:${info.port}`);
|
|
11
|
+
console.log(` š Dashboard ā http://${info.address}:${info.port}/`);
|
|
12
|
+
console.log(` ā¤ļø Health ā http://${info.address}:${info.port}/health`);
|
|
13
|
+
console.log(` š Analyze ā POST http://${info.address}:${info.port}/analyze\n`);
|
|
14
|
+
},
|
|
15
|
+
);
|