@gringow/gringow 0.1.2 → 0.1.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.
Files changed (58) hide show
  1. package/README.md +313 -35
  2. package/dist/browser.d.mts +2 -1
  3. package/dist/browser.mjs +61 -4
  4. package/dist/config.d.mts +1 -0
  5. package/dist/config.mjs +5 -3
  6. package/dist/config.test.mjs +52 -67
  7. package/dist/constants/default-config.mjs +1 -0
  8. package/dist/{data/create-cache-id.d.mts → create-cache-id.d.mts} +2 -2
  9. package/dist/create-cache-id.mjs +54 -0
  10. package/dist/create-cache-id.test.d.mts +2 -0
  11. package/dist/create-cache-id.test.mjs +18464 -0
  12. package/dist/data/check-cache.d.mts +1 -1
  13. package/dist/data/check-cache.mjs +14 -7
  14. package/dist/data/clear-cache.mjs +14 -7
  15. package/dist/data/create-cache-dir.mjs +5 -3
  16. package/dist/data/delete-cache-item.d.mts +1 -1
  17. package/dist/data/delete-cache-item.mjs +14 -7
  18. package/dist/data/get-cache.mjs +5 -3
  19. package/dist/data/get-or-create-cache.d.mts +1 -1
  20. package/dist/data/get-or-create-cache.mjs +14 -7
  21. package/dist/data/md5.d.mts +13 -0
  22. package/dist/data/md5.mjs +47 -0
  23. package/dist/data/md5.test.d.mts +2 -0
  24. package/dist/data/md5.test.mjs +18457 -0
  25. package/dist/data/save-cache.d.mts +2 -2
  26. package/dist/data/save-cache.mjs +14 -7
  27. package/dist/data/write-cache-to-disk.d.mts +1 -1
  28. package/dist/data/write-cache-to-disk.mjs +14 -7
  29. package/dist/flat-template-string.test.mjs +10 -10
  30. package/dist/gringow.d.mts +1 -1
  31. package/dist/gringow.mjs +100 -35
  32. package/dist/index.d.mts +1 -1
  33. package/dist/index.mjs +100 -35
  34. package/dist/interpolate-template.d.mts +3 -0
  35. package/dist/interpolate-template.mjs +14 -0
  36. package/dist/io/anthropic/ask.mjs +5 -3
  37. package/dist/io/anthropic/commit-response.d.mts +1 -1
  38. package/dist/io/anthropic/index.d.mts +1 -1
  39. package/dist/io/anthropic/index.mjs +5 -3
  40. package/dist/io/anthropic/parse.d.mts +1 -1
  41. package/dist/io/ask-llm.d.mts +1 -1
  42. package/dist/io/ask-llm.mjs +38 -25
  43. package/dist/io/gemini/ask.mjs +38 -25
  44. package/dist/io/gemini/index.d.mts +1 -1
  45. package/dist/io/gemini/index.mjs +38 -25
  46. package/dist/io/gemini/parse.d.mts +1 -1
  47. package/dist/io/huggingface/ask.d.mts +1 -1
  48. package/dist/io/huggingface/ask.mjs +5 -3
  49. package/dist/io/huggingface/commit-response.d.mts +1 -1
  50. package/dist/io/huggingface/index.d.mts +1 -1
  51. package/dist/io/huggingface/index.mjs +5 -3
  52. package/dist/io/huggingface/parse.d.mts +1 -1
  53. package/dist/logger.test.mjs +10 -10
  54. package/dist/schemas/config.shcema.d.mts +5 -0
  55. package/dist/schemas/config.shcema.mjs +1 -0
  56. package/dist/types/index.d.mts +1 -1
  57. package/package.json +10 -10
  58. package/dist/data/create-cache-id.mjs +0 -10
package/README.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # @gringow/gringow
2
2
 
3
- Core library for Gringow to translate strings and template strings using different LLM providers.
3
+ [![npm version](https://img.shields.io/npm/v/@gringow/gringow)](https://www.npmjs.com/package/@gringow/gringow)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ AI-powered translation library for template strings using multiple LLM providers (Hugging Face, Anthropic, Gemini). Gringow provides smart caching, deterministic content hashing, and seamless integration for modern JavaScript/TypeScript projects.
7
+
8
+ ## Features
9
+
10
+ - 🌍 **Multi-language translation** using LLM providers
11
+ - 🏷️ **Tagged template literal** syntax for inline translations
12
+ - 💾 **Smart caching** with content-based hashing
13
+ - 🔌 **Three LLM providers**: Hugging Face (local), Anthropic, Gemini
14
+ - 🚀 **Zero configuration** - works out of the box with Hugging Face
15
+ - 📦 **Optimized builds** via Vite/Next.js plugins
4
16
 
5
17
  ## Installation
6
18
 
@@ -12,76 +24,342 @@ pnpm add @gringow/gringow
12
24
  yarn add @gringow/gringow
13
25
  ```
14
26
 
15
- ## Usage
27
+ ## Quick Start
16
28
 
17
29
  ```typescript
18
30
  import Gringow from '@gringow/gringow'
19
31
 
20
32
  // Simple string translation
21
- const result = await Gringow`Hello world`
22
- console.log(result.translation) // { "pt-BR": "Olá mundo", "fr-CA": "Bonjour le monde" }
33
+ const { cacheId, translation } = await Gringow`Hello world`
34
+ console.log(translation.translation)
35
+ // => { "pt-BR": "Olá mundo", "fr-CA": "Bonjour le monde" }
23
36
 
24
- // Template string translation
25
- const name = "John"
26
- const result2 = await Gringow`Hello ${name}`
27
- console.log(result2.translation) // { "pt-BR": "Olá João", "fr-CA": "Bonjour John" }
37
+ // Template string with dynamic values
38
+ const name = 'John'
39
+ const { translation: result } = await Gringow`Welcome, ${name}!`
40
+ console.log(result.translation)
41
+ // => { "pt-BR": "Bem-vindo, John!", "fr-CA": "Bienvenue, John!" }
42
+ ```
43
+
44
+ ## Usage Examples
45
+
46
+ ### Basic Translation
47
+
48
+ ```typescript
49
+ import Gringow from '@gringow/gringow'
50
+
51
+ // Translate a static string
52
+ const { translation } = await Gringow`Hello world`
53
+ console.log(translation.translation['pt-BR']) // "Olá mundo"
54
+ ```
55
+
56
+ ### Dynamic Content
57
+
58
+ ```typescript
59
+ // Template literals are flattened before hashing - dynamic values preserved
60
+ const user = { name: 'Alice', role: 'admin' }
61
+ const { translation } = await Gringow`User ${user.name} has ${user.role} privileges`
62
+
63
+ console.log(translation.translation['pt-BR'])
64
+ // => "Usuário Alice tem privilégios de admin"
65
+ ```
66
+
67
+ ### Cache Management
68
+
69
+ ```typescript
70
+ import Gringow, { rebuild, checkCache } from '@gringow/gringow'
71
+
72
+ const { cacheId } = await Gringow`Hello world`
73
+
74
+ // Check if translation exists in cache
75
+ const cached = await checkCache(cacheId)
76
+ console.log(cached) // GringowCacheItem or undefined
77
+
78
+ // Force re-translation by rebuilding cache entry
79
+ const refreshed = await rebuild(cacheId)
80
+ console.log(refreshed.translation.translation)
81
+ ```
82
+
83
+ ### Browser Usage
84
+
85
+ For client-side interpolation without Node.js dependencies:
86
+
87
+ ```typescript
88
+ import { interpolateTemplate } from '@gringow/gringow/browser'
89
+
90
+ const template = 'Hello {name}, you have {count} messages'
91
+ const result = interpolateTemplate(template, { name: 'Alice', count: 5 })
92
+ console.log(result) // "Hello Alice, you have 5 messages"
28
93
  ```
29
94
 
30
95
  ## Configuration
31
96
 
32
- Create a `gringow.config.js` file in your project root:
97
+ Gringow uses [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig) to load configuration from your project root. Supported formats: `gringow.config.js`, `gringow.config.mjs`, `gringow.config.json`, or a `gringow` field in `package.json`.
98
+
99
+ ### Default Configuration
100
+
101
+ ```js
102
+ // gringow.config.js
103
+ export default {
104
+ debug: false,
105
+ log: { level: 'info' },
106
+ cache: {
107
+ dir: './gringow',
108
+ file: 'gringow.json',
109
+ prefix: 'grw',
110
+ write: true,
111
+ },
112
+ keys: {
113
+ anthropicApiKey: process.env.ANTHROPIC_API_KEY,
114
+ geminiApiKey: process.env.GEMINI_API_KEY,
115
+ },
116
+ sourceLanguage: 'en-US',
117
+ languages: ['pt-BR', 'fr-CA'],
118
+ llm: {
119
+ choice: 'huggingface', // 'huggingface' | 'anthropic' | 'gemini'
120
+ model: 'Xenova/nllb-200-distilled-600M',
121
+ temperature: 1.0,
122
+ sleep: 0,
123
+ },
124
+ globby: './src/**/*.{js,jsx,ts,tsx}',
125
+ }
126
+ ```
127
+
128
+ ### Example: Using Gemini
33
129
 
34
- ```javascript
130
+ ```js
131
+ // gringow.config.js
35
132
  export default {
36
- sourceLanguage: "en-US",
37
- languages: ["pt-BR", "fr-CA"],
133
+ keys: {
134
+ geminiApiKey: process.env.GEMINI_API_KEY,
135
+ },
38
136
  llm: {
39
- choice: "huggingface", // or "anthropic", "gemini"
40
- model: "Xenova/nllb-200-distilled-600M"
137
+ choice: 'gemini',
138
+ model: 'gemini-2.0-flash',
139
+ prePromptAppend: 'Please respond in a friendly tone.',
41
140
  },
141
+ languages: ['es-ES', 'de-DE', 'ja-JP'],
142
+ }
143
+ ```
144
+
145
+ ### Example: Using Anthropic
146
+
147
+ ```js
148
+ // gringow.config.js
149
+ export default {
42
150
  keys: {
43
- // anthropicApiKey: "your-key-here", // if using Anthropic
44
- // geminiApiKey: "your-key-here" // if using Gemini
45
- }
151
+ anthropicApiKey: process.env.ANTHROPIC_API_KEY,
152
+ },
153
+ llm: {
154
+ choice: 'anthropic',
155
+ model: 'claude-3-5-sonnet-20241022',
156
+ temperature: 0.7,
157
+ },
158
+ }
159
+ ```
160
+
161
+ ### Prompt Customization
162
+
163
+ Fine-tune LLM behavior with prompt options:
164
+
165
+ ```js
166
+ export default {
167
+ llm: {
168
+ choice: 'huggingface',
169
+ prePrompt: 'Translate the following text:', // Default prompt prefix
170
+ postPrompt: 'Ensure translations are natural.', // Additional context
171
+ prePromptAppend: 'Use formal language.', // Appended to pre-prompt
172
+ postPromptPrepend: 'Maintain original formatting.', // Prepended to post-prompt
173
+ prePromptReplace: 'Custom instruction here', // Replaces entire pre-prompt
174
+ },
46
175
  }
47
176
  ```
48
177
 
49
178
  ## LLM Providers
50
179
 
51
- - **Hugging Face** (default): Local models, no API key required
52
- - **Anthropic**: Requires `anthropicApiKey`
53
- - **Gemini**: Requires `geminiApiKey`
180
+ ### Hugging Face (Default)
181
+
182
+ Runs local models via `@huggingface/transformers`. **No API key required.**
183
+
184
+ ```js
185
+ // gringow.config.js
186
+ export default {
187
+ llm: {
188
+ choice: 'huggingface',
189
+ model: 'Xenova/nllb-200-distilled-600M', // Multilingual translation model
190
+ },
191
+ }
192
+ ```
193
+
194
+ **Pros**: Free, private, offline-capable
195
+ **Cons**: Slower first run (model download), higher memory usage
54
196
 
55
- ## API
197
+ ### Anthropic
56
198
 
57
- ### Gringow(strings, ...values)
199
+ Uses Claude models via Anthropic API. **Requires API key.**
58
200
 
59
- Main function for template string translation.
201
+ ```js
202
+ // gringow.config.js
203
+ export default {
204
+ keys: {
205
+ anthropicApiKey: process.env.ANTHROPIC_API_KEY,
206
+ },
207
+ llm: {
208
+ choice: 'anthropic',
209
+ model: 'claude-3-5-sonnet-20241022',
210
+ },
211
+ }
212
+ ```
60
213
 
61
- **Parameters:**
62
- - `strings`: TemplateStringsArray or string
63
- - `values`: Values for interpolation
214
+ **Pros**: High-quality translations, fast
215
+ **Cons**: API costs, requires internet
64
216
 
65
- **Returns:** Promise<GringowResponse>
217
+ ### Gemini
66
218
 
67
- ### GringowResponse
219
+ Uses Google's Gemini models. **Requires API key.**
68
220
 
69
- ```typescript
70
- type GringowResponse = {
71
- cacheId: string
72
- translation: GringowCacheItem
221
+ ```js
222
+ // gringow.config.js
223
+ export default {
224
+ keys: {
225
+ geminiApiKey: process.env.GEMINI_API_KEY,
226
+ },
227
+ llm: {
228
+ choice: 'gemini',
229
+ model: 'gemini-2.0-flash',
230
+ },
73
231
  }
232
+ ```
233
+
234
+ **Pros**: Fast, cost-effective
235
+ **Cons**: API costs, requires internet
236
+
237
+ ## API Reference
238
+
239
+ ### Core Functions
240
+
241
+ #### `Gringow(strings, ...values): Promise<GringowResponse>`
242
+
243
+ Main translation function. Accepts tagged template literals or plain strings.
244
+
245
+ ```typescript
246
+ const result = await Gringow`Hello ${name}`
247
+ ```
248
+
249
+ **Returns**: `Promise<GringowResponse>` with `{ cacheId, translation }`
250
+
251
+ #### `rebuild(cacheId: string): Promise<GringowResponse>`
252
+
253
+ Re-translates a cached entry using the current LLM configuration.
254
+
255
+ ```typescript
256
+ const refreshed = await rebuild('grw_abc123')
257
+ ```
258
+
259
+ ### Cache Helpers
260
+
261
+ ```typescript
262
+ import {
263
+ checkCache, // Check if cacheId exists
264
+ getCache, // Get entire cache object
265
+ getOrCreateCache,// Get cache or create empty one
266
+ writeCacheToDisk,// Manually write cache entry
267
+ deleteCacheItem, // Remove cache entry
268
+ clearCache, // Clear all cache entries
269
+ } from '@gringow/gringow'
270
+ ```
271
+
272
+ ### Utilities
273
+
274
+ ```typescript
275
+ import {
276
+ createCacheId, // Generate cache ID from string
277
+ flatTemplateString, // Flatten template literal to string
278
+ interpolateTemplate, // Browser-safe interpolation (from /browser export)
279
+ awaitedConfig, // Access resolved configuration
280
+ isLocalConfigFound, // Check if local config exists
281
+ getConfigSource, // Get config file path
282
+ } from '@gringow/gringow'
283
+ ```
284
+
285
+ ### Type Definitions
286
+
287
+ ```typescript
288
+ type GringowTranslation = Record<string, string>
74
289
 
75
290
  type GringowCacheItem = {
76
291
  original: string
77
292
  translation: GringowTranslation
293
+ append?: string
294
+ prepend?: string
295
+ rebuild?: boolean
78
296
  }
79
297
 
80
- type GringowTranslation = {
81
- [key: string]: string
298
+ type GringowResponse = {
299
+ cacheId: string
300
+ translation: GringowCacheItem
82
301
  }
83
302
  ```
84
303
 
304
+ ## How It Works
305
+
306
+ 1. **Input Processing**: Template strings are flattened to plain text (preserving dynamic values)
307
+ 2. **Cache ID Generation**: Content-based hash created using `createCacheId()`
308
+ 3. **Cache Lookup**: Check `./gringow/gringow.json` for existing translation
309
+ 4. **LLM Query**: If not cached, request translation from configured provider
310
+ 5. **Response Parsing**: Parse provider response to `GringowCacheItem` format
311
+ 6. **Cache Storage**: Write to disk when `cache.write` is `true`
312
+
313
+ **Example cache structure:**
314
+
315
+ ```json
316
+ {
317
+ "grw_a1b2c3": {
318
+ "original": "Hello world",
319
+ "translation": {
320
+ "pt-BR": "Olá mundo",
321
+ "fr-CA": "Bonjour le monde"
322
+ }
323
+ }
324
+ }
325
+ ```
326
+
327
+ ## Development
328
+
329
+ ```bash
330
+ # Install dependencies
331
+ pnpm install
332
+
333
+ # Build the package
334
+ pnpm run build
335
+
336
+ # Run tests
337
+ pnpm run test
338
+
339
+ # Watch mode
340
+ pnpm run test:watch
341
+ ```
342
+
343
+ ## Integrations
344
+
345
+ Gringow has official integrations for modern web frameworks:
346
+
347
+ - **[@gringow/cli](https://www.npmjs.com/package/@gringow/gringow-cli)** - Command-line interface for cache management and translations
348
+ - **[@gringow/gringow-vite](https://www.npmjs.com/package/@gringow/gringow-vite)** - Vite plugin for build-time translation extraction
349
+ - **[@gringow/gringow-react](https://www.npmjs.com/package/@gringow/gringow-react)** - React hooks and components
350
+ - **[@gringow/gringow-shadow](https://www.npmjs.com/package/@gringow/gringow-shadow)** - Web Component layer (vanilla JS)
351
+ - **[@gringow/gringow-nextjs](https://www.npmjs.com/package/@gringow/gringow-nextjs)** - Next.js plugin (experimental)
352
+
353
+ ## Related Resources
354
+
355
+ - [Documentation](https://gringow.dev)
356
+ - [GitHub Repository](https://github.com/rntgspr/gringow)
357
+ - [Examples](https://github.com/rntgspr/gringow/tree/main/example-gringow-vite)
358
+
85
359
  ## License
86
360
 
87
- MIT
361
+ MIT © [Renato Gaspar](https://github.com/rntgspr)
362
+
363
+ ---
364
+
365
+ **Questions or Issues?** Open an issue on [GitHub](https://github.com/rntgspr/gringow/issues)
@@ -1,5 +1,6 @@
1
- export { createCacheId } from './data/create-cache-id.mjs';
1
+ export { createCacheId } from './create-cache-id.mjs';
2
2
  export { flatTemplateString } from './flat-template-string.mjs';
3
+ export { interpolateTemplate } from './interpolate-template.mjs';
3
4
 
4
5
  declare const GRINGOW_IO_URL = "https://gringow.io";
5
6
 
package/dist/browser.mjs CHANGED
@@ -1,8 +1,52 @@
1
- import CryptoJS from 'crypto-js';
1
+ // src/data/md5.ts
2
+ var INITIAL_STATE = [1831565813, 461845907, 2246822507, 3266489909];
3
+ var MULTIPLIERS = [668265261, 374761393, 2654435769, 2496678331];
4
+ var AVA_MULT1 = 2146121005;
5
+ var AVA_MULT2 = 2221713035;
6
+ function seedState(input) {
7
+ const state = [...INITIAL_STATE];
8
+ for (const char of input) {
9
+ const codePoint = char.codePointAt(0) ?? 0;
10
+ for (let i = 0; i < state.length; i++) {
11
+ state[i] = Math.imul(state[i] ^ codePoint, MULTIPLIERS[i]);
12
+ state[i] = rotateLeft(state[i], (i + 1) * 3);
13
+ }
14
+ state[0] ^= state[3] >>> 11;
15
+ state[1] ^= state[0] << 7;
16
+ state[2] ^= state[1] >>> 13;
17
+ state[3] ^= state[2] << 5;
18
+ }
19
+ return state;
20
+ }
21
+ function rotateLeft(value, shift) {
22
+ return value << shift | value >>> 32 - shift;
23
+ }
24
+ function avalanche(value) {
25
+ value ^= value >>> 16;
26
+ value = Math.imul(value, AVA_MULT1);
27
+ value ^= value >>> 15;
28
+ value = Math.imul(value, AVA_MULT2);
29
+ value ^= value >>> 16;
30
+ return value >>> 0;
31
+ }
32
+ function toHex(value) {
33
+ return (value >>> 0).toString(16).padStart(8, "0");
34
+ }
35
+ function mixState([h1, h2, h3, h4]) {
36
+ h1 = avalanche(h1 ^ h3);
37
+ h2 = avalanche(h2 ^ h4);
38
+ h3 = avalanche(h3 ^ h1);
39
+ h4 = avalanche(h4 ^ h2);
40
+ return [h1, h2, h3, h4];
41
+ }
42
+ function md5(input) {
43
+ const state = mixState(seedState(input));
44
+ return state.map(toHex).join("");
45
+ }
2
46
 
3
- // src/data/create-cache-id.ts
47
+ // src/create-cache-id.ts
4
48
  function createCacheId(text) {
5
- const hexHash = CryptoJS.MD5(text).toString();
49
+ const hexHash = md5(text);
6
50
  const cacheId = `gid_${hexHash}`;
7
51
  return cacheId;
8
52
  }
@@ -22,7 +66,20 @@ function flatTemplateString(givenStrings, givenValues) {
22
66
  return flatted;
23
67
  }
24
68
 
69
+ // src/interpolate-template.ts
70
+ function interpolateTemplate(template, values) {
71
+ if (!values || values.length === 0) return template;
72
+ let valueIndex = 0;
73
+ return template.replace(/\$\{[^}]*\}/g, () => {
74
+ if (valueIndex < values.length) {
75
+ const value = values[valueIndex++];
76
+ return value === null || value === void 0 ? "" : String(value);
77
+ }
78
+ return "";
79
+ });
80
+ }
81
+
25
82
  // src/browser.ts
26
83
  var GRINGOW_IO_URL = "https://gringow.io";
27
84
 
28
- export { GRINGOW_IO_URL, createCacheId, flatTemplateString };
85
+ export { GRINGOW_IO_URL, createCacheId, flatTemplateString, interpolateTemplate };
package/dist/config.d.mts CHANGED
@@ -40,6 +40,7 @@ declare const awaitedConfig: Promise<{
40
40
  write?: boolean | undefined;
41
41
  };
42
42
  sourceLanguage: string;
43
+ defaultLanguage: string;
43
44
  languages: string[];
44
45
  llm: {
45
46
  choice: "gemini" | "anthropic" | "huggingface";
package/dist/config.mjs CHANGED
@@ -18,6 +18,7 @@ var defaultConfig = {
18
18
  },
19
19
  keys: {},
20
20
  sourceLanguage: "en-US",
21
+ defaultLanguage: "pt-BR",
21
22
  languages: ["pt-BR", "fr-CA"],
22
23
  llm: {
23
24
  choice: "huggingface",
@@ -45,6 +46,7 @@ var configSchema = z.object({
45
46
  geminiApiKey: z.string().optional()
46
47
  }),
47
48
  sourceLanguage: languageSchema,
49
+ defaultLanguage: languageSchema,
48
50
  languages: z.array(languageSchema),
49
51
  llm: z.object({
50
52
  choice: llmOptionsSchema,
@@ -107,9 +109,9 @@ async function loadUserConfig() {
107
109
  var awaitedConfig = loadUserConfig();
108
110
  function defaultPrePrompt(config, extraInstructions) {
109
111
  return `
110
- You are a translation generator.
112
+ You are a ultimate translation generator server api.
111
113
 
112
- Your task is to output **only one valid JSON object**, with **no text, explanations, or formatting outside the JSON**.
114
+ Your task is to translate a given text and output **only one valid JSON object**, with **no text, explanations, or formatting than the JSON**.
113
115
 
114
116
  Follow these rules strictly:
115
117
 
@@ -119,7 +121,7 @@ Follow these rules strictly:
119
121
  - Apply these creatively but **without altering the meaning or punctuation** of the original text.
120
122
  3. Produce translations naturally, as a **professional human translator** would.
121
123
  4. Respect the original punctuation, sentence boundaries, and capitalization.
122
- 5. Consider only the following target languages: ${(config?.languages ? config.languages : []).join(", ")}.
124
+ 5. Consider only the following target languages: ${(config?.languages ? Array.from(/* @__PURE__ */ new Set(["es-ES", ...config.languages, config.sourceLanguage])) : []).join(", ")}.
123
125
  6. The final output must be a **valid JSON** following this exact schema:
124
126
 
125
127
  {