@awsless/i18n 0.0.9 → 0.0.11

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.MD CHANGED
@@ -1,18 +1,15 @@
1
1
  # AI-generated internationalization made easy
2
2
 
3
- The `@awsless/i18n` package is a Vite plugin that automatically translates your text during build time using AI or any other tool you prefer. The plugin will inline the translations so you don't have to worry about loading the translations at the right time.
3
+ The `@awsless/i18n` package is a Vite plugin that automatically translates your text during build time using AI or any other tool you prefer. The plugin will inline the translations so you don't have to worry about loading the translations at the right time. This means, switching the locale will instantly switch all your translated text on the page.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - Automatic text translation
8
8
  - Inlines translations
9
- - Extremely lightweight (373 bytes uncompressed 🔥)
9
+ - Extremely lightweight (431 bytes uncompressed 🔥)
10
+ - Instant locale switch
10
11
  - Svelte 5 support
11
12
 
12
- ## Todo's
13
-
14
- - Create a CLI command to find the text that needs to be translated & translate it, so that we can remove that part from the vite plugin.
15
-
16
13
  ## Setup
17
14
 
18
15
  Install with (NPM):
@@ -25,6 +22,7 @@ npm i @awsless/i18n
25
22
 
26
23
  ```ts
27
24
  import { i18n, ai } from '@awsless/i18n'
25
+ import { openai } from '@ai-sdk/openai'
28
26
 
29
27
  export defineConfig({
30
28
  plugins: [
@@ -65,7 +63,7 @@ lang.t.get(`${count} count`, {
65
63
  })
66
64
  ```
67
65
 
68
- To change the locale that is being rendered simply set the `lang.locale` property.
66
+ To change the locale that is being rendered simply change the `lang.locale` property.
69
67
 
70
68
  ```ts
71
69
  import { lang } from '@awsless/i18n/svelte'
package/dist/index.cjs CHANGED
@@ -31,7 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ai: () => ai,
34
- i18n: () => createI18nPlugin
34
+ i18n: () => i18n
35
35
  });
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
@@ -55,32 +55,32 @@ var Cache = class {
55
55
  constructor(data = {}) {
56
56
  this.data = data;
57
57
  }
58
- set(original, locale, translation) {
59
- if (!this.data[original]) {
60
- this.data[original] = {};
58
+ set(source, locale, translation) {
59
+ if (!this.data[source]) {
60
+ this.data[source] = {};
61
61
  }
62
- if (!this.data[original][locale]) {
63
- this.data[original][locale] = translation;
62
+ if (typeof this.data[source][locale] === "undefined") {
63
+ this.data[source][locale] = translation;
64
64
  }
65
65
  }
66
- get(original, locale) {
67
- return this.data[original]?.[locale];
66
+ get(source, locale) {
67
+ return this.data[source]?.[locale];
68
68
  }
69
- has(original, locale) {
70
- return typeof this.get(original, locale) === "string";
69
+ has(source, locale) {
70
+ return typeof this.get(source, locale) === "string";
71
71
  }
72
- delete(original, locale) {
73
- if (this.data[original]?.[locale]) {
74
- delete this.data[original][locale];
72
+ delete(source, locale) {
73
+ if (typeof this.data[source]?.[locale] !== "undefined") {
74
+ delete this.data[source][locale];
75
75
  }
76
- if (this.data[original] && Object.keys(this.data[original]).length === 0) {
77
- delete this.data[original];
76
+ if (this.data[source] && Object.keys(this.data[source]).length === 0) {
77
+ delete this.data[source];
78
78
  }
79
79
  }
80
80
  *entries() {
81
- for (const [original, locales] of Object.entries(this.data)) {
81
+ for (const [source, locales] of Object.entries(this.data)) {
82
82
  for (const [locale, translation] of Object.entries(locales)) {
83
- yield { original, locale, translation };
83
+ yield { source, locale, translation };
84
84
  }
85
85
  }
86
86
  }
@@ -90,21 +90,21 @@ var Cache = class {
90
90
  };
91
91
 
92
92
  // src/diff.ts
93
- var findNewTranslations = (cache, originals, locales) => {
93
+ var findNewTranslations = (cache, sources, locales) => {
94
94
  const list = [];
95
- for (const original of originals) {
95
+ for (const source of sources) {
96
96
  for (const locale of locales) {
97
- if (!cache.has(original, locale)) {
98
- list.push({ original, locale });
97
+ if (!cache.has(source, locale)) {
98
+ list.push({ source, locale });
99
99
  }
100
100
  }
101
101
  }
102
102
  return list;
103
103
  };
104
- var removeUnusedTranslations = (cache, originals, locales) => {
104
+ var removeUnusedTranslations = (cache, sources, locales) => {
105
105
  for (const item of cache.entries()) {
106
- if (!locales.includes(item.locale) || !originals.includes(item.original)) {
107
- cache.delete(item.original, item.locale);
106
+ if (!locales.includes(item.locale) || !sources.includes(item.source)) {
107
+ cache.delete(item.source, item.locale);
108
108
  }
109
109
  }
110
110
  };
@@ -125,10 +125,7 @@ var findSvelteTranslatable = (code) => {
125
125
  css: false
126
126
  });
127
127
  const enter = (node) => {
128
- if (
129
- //
130
- node.type === "TaggedTemplateExpression" && node.tag.type === "MemberExpression" && node.tag.object.type === "Identifier" && node.tag.object.name === "lang" && node.tag.property.type === "Identifier" && node.tag.property.name === "t" && node.quasi.type === "TemplateLiteral" && node.quasi.loc
131
- ) {
128
+ if (node.type === "TaggedTemplateExpression" && node.tag.type === "MemberExpression" && node.tag.object.type === "Identifier" && node.tag.object.name === "lang" && node.tag.property.type === "Identifier" && node.tag.property.name === "t" && node.quasi.type === "TemplateLiteral" && node.quasi.loc) {
132
129
  const start = node.quasi.loc.start;
133
130
  const end = node.quasi.loc.end;
134
131
  const content = code.substring(
@@ -188,7 +185,8 @@ var findTranslatable = async (cwd) => {
188
185
  if (file.endsWith(".svelte")) {
189
186
  found.push(...findSvelteTranslatable(code));
190
187
  } else {
191
- found.push(...await findTypescriptTranslatable(code));
188
+ const entries = await findTypescriptTranslatable(code);
189
+ found.push(...entries);
192
190
  }
193
191
  }
194
192
  }
@@ -196,7 +194,7 @@ var findTranslatable = async (cwd) => {
196
194
  };
197
195
 
198
196
  // src/vite.ts
199
- var createI18nPlugin = (props) => {
197
+ var i18n = (props) => {
200
198
  let cache;
201
199
  return {
202
200
  name: "awsless/i18n",
@@ -204,16 +202,16 @@ var createI18nPlugin = (props) => {
204
202
  async buildStart() {
205
203
  const cwd = process.cwd();
206
204
  this.info("Finding all translatable text...");
207
- const originals = await findTranslatable(cwd);
205
+ const sourceTexts = await findTranslatable(cwd);
208
206
  cache = await loadCache(cwd);
209
- removeUnusedTranslations(cache, originals, props.locales);
210
- const newOriginals = findNewTranslations(cache, originals, props.locales);
211
- if (newOriginals.length > 0) {
212
- this.info(`Translating ${newOriginals.length} new texts.`);
213
- const translations = await props.translate(props.default ?? "en", newOriginals);
207
+ removeUnusedTranslations(cache, sourceTexts, props.locales);
208
+ const newSourceTexts = findNewTranslations(cache, sourceTexts, props.locales);
209
+ if (newSourceTexts.length > 0) {
210
+ this.info(`Translating ${newSourceTexts.length} new texts.`);
211
+ const translations = await props.translate(props.default ?? "en", newSourceTexts);
214
212
  this.info(`Translated ${translations.length} texts.`);
215
213
  for (const item of translations) {
216
- cache.set(item.original, item.locale, item.translation);
214
+ cache.set(item.source, item.locale, item.translation);
217
215
  }
218
216
  }
219
217
  await saveCache(cwd, cache);
@@ -223,11 +221,15 @@ var createI18nPlugin = (props) => {
223
221
  let replaced = false;
224
222
  if (code.includes("lang.t`")) {
225
223
  for (const item of cache.entries()) {
226
- code = code.replaceAll(`lang.t\`${item.original}\``, () => {
224
+ code = code.replaceAll(`lang.t\`${item.source}\``, () => {
227
225
  replaced = true;
228
- return `lang.t.get(\`${item.original}\`, {${props.locales.map((locale) => {
229
- return `"${locale}":\`${cache.get(item.original, locale)}\``;
230
- }).join(",")}})`;
226
+ return `lang.t.get(\`${item.source}\`, {${props.locales.map((locale) => {
227
+ const translation = cache.get(item.source, locale);
228
+ if (translation === item.source) {
229
+ return;
230
+ }
231
+ return `"${locale}":\`${translation}\``;
232
+ }).filter((v) => !!v).join(",")}})`;
231
233
  });
232
234
  }
233
235
  }
@@ -255,7 +257,7 @@ var ai = (props) => {
255
257
  maxTokens: props.maxTokens,
256
258
  schema: import_zod.z.object({
257
259
  translations: import_zod.z.object({
258
- original: import_zod.z.string(),
260
+ source: import_zod.z.string(),
259
261
  locale: import_zod.z.string(),
260
262
  translation: import_zod.z.string()
261
263
  }).array()
package/dist/index.d.cts CHANGED
@@ -2,34 +2,38 @@ import { Plugin } from 'vite';
2
2
  import { LanguageModel } from 'ai';
3
3
 
4
4
  type Translator = (defaultLocale: string, list: {
5
- original: string;
5
+ source: string;
6
6
  locale: string;
7
7
  }[]) => TranslationResponse[] | Promise<TranslationResponse[]>;
8
8
  type TranslationResponse = {
9
- original: string;
9
+ source: string;
10
10
  locale: string;
11
11
  translation: string;
12
12
  };
13
13
  type I18nPluginProps = {
14
- /** The default locale that your original text is writen in. */
14
+ /** The original language your source text is written in.
15
+ * @default "en"
16
+ */
15
17
  default?: string;
16
- /** A list of locales that you want your text translated too. */
18
+ /** The list of target locales to translate your text into. */
17
19
  locales: string[];
18
- /** The callback that is responsible for translating the text. */
20
+ /** Function that performs the translation of a given text. */
19
21
  translate: Translator;
20
22
  };
21
- declare const createI18nPlugin: (props: I18nPluginProps) => Plugin;
23
+ declare const i18n: (props: I18nPluginProps) => Plugin;
22
24
 
23
25
  type AiTranslationProps = {
24
- /** The maximum number of tokens that can be generated in the chat completion. */
26
+ /** The maximum number of tokens allowed in the AI's response. */
25
27
  maxTokens: number;
26
- /** ID of the model to use. */
28
+ /** The language model to use for translations (e.g., gpt-4, gpt-3.5-turbo). */
27
29
  model: LanguageModel;
28
- /** */
30
+ /** Number of text entries to translate in a single batch.
31
+ * @default 1000
32
+ */
29
33
  batchSize?: number;
30
- /** The rules that the ai should follow. It will be added to the prompt. */
34
+ /** Custom translation guidelines for the AI. These are injected into the prompt. */
31
35
  rules?: string[];
32
36
  };
33
37
  declare const ai: (props: AiTranslationProps) => Translator;
34
38
 
35
- export { type I18nPluginProps, type Translator, ai, createI18nPlugin as i18n };
39
+ export { type AiTranslationProps, type I18nPluginProps, type Translator, ai, i18n };
package/dist/index.d.ts CHANGED
@@ -2,34 +2,38 @@ import { Plugin } from 'vite';
2
2
  import { LanguageModel } from 'ai';
3
3
 
4
4
  type Translator = (defaultLocale: string, list: {
5
- original: string;
5
+ source: string;
6
6
  locale: string;
7
7
  }[]) => TranslationResponse[] | Promise<TranslationResponse[]>;
8
8
  type TranslationResponse = {
9
- original: string;
9
+ source: string;
10
10
  locale: string;
11
11
  translation: string;
12
12
  };
13
13
  type I18nPluginProps = {
14
- /** The default locale that your original text is writen in. */
14
+ /** The original language your source text is written in.
15
+ * @default "en"
16
+ */
15
17
  default?: string;
16
- /** A list of locales that you want your text translated too. */
18
+ /** The list of target locales to translate your text into. */
17
19
  locales: string[];
18
- /** The callback that is responsible for translating the text. */
20
+ /** Function that performs the translation of a given text. */
19
21
  translate: Translator;
20
22
  };
21
- declare const createI18nPlugin: (props: I18nPluginProps) => Plugin;
23
+ declare const i18n: (props: I18nPluginProps) => Plugin;
22
24
 
23
25
  type AiTranslationProps = {
24
- /** The maximum number of tokens that can be generated in the chat completion. */
26
+ /** The maximum number of tokens allowed in the AI's response. */
25
27
  maxTokens: number;
26
- /** ID of the model to use. */
28
+ /** The language model to use for translations (e.g., gpt-4, gpt-3.5-turbo). */
27
29
  model: LanguageModel;
28
- /** */
30
+ /** Number of text entries to translate in a single batch.
31
+ * @default 1000
32
+ */
29
33
  batchSize?: number;
30
- /** The rules that the ai should follow. It will be added to the prompt. */
34
+ /** Custom translation guidelines for the AI. These are injected into the prompt. */
31
35
  rules?: string[];
32
36
  };
33
37
  declare const ai: (props: AiTranslationProps) => Translator;
34
38
 
35
- export { type I18nPluginProps, type Translator, ai, createI18nPlugin as i18n };
39
+ export { type AiTranslationProps, type I18nPluginProps, type Translator, ai, i18n };
package/dist/index.js CHANGED
@@ -18,32 +18,32 @@ var Cache = class {
18
18
  constructor(data = {}) {
19
19
  this.data = data;
20
20
  }
21
- set(original, locale, translation) {
22
- if (!this.data[original]) {
23
- this.data[original] = {};
21
+ set(source, locale, translation) {
22
+ if (!this.data[source]) {
23
+ this.data[source] = {};
24
24
  }
25
- if (!this.data[original][locale]) {
26
- this.data[original][locale] = translation;
25
+ if (typeof this.data[source][locale] === "undefined") {
26
+ this.data[source][locale] = translation;
27
27
  }
28
28
  }
29
- get(original, locale) {
30
- return this.data[original]?.[locale];
29
+ get(source, locale) {
30
+ return this.data[source]?.[locale];
31
31
  }
32
- has(original, locale) {
33
- return typeof this.get(original, locale) === "string";
32
+ has(source, locale) {
33
+ return typeof this.get(source, locale) === "string";
34
34
  }
35
- delete(original, locale) {
36
- if (this.data[original]?.[locale]) {
37
- delete this.data[original][locale];
35
+ delete(source, locale) {
36
+ if (typeof this.data[source]?.[locale] !== "undefined") {
37
+ delete this.data[source][locale];
38
38
  }
39
- if (this.data[original] && Object.keys(this.data[original]).length === 0) {
40
- delete this.data[original];
39
+ if (this.data[source] && Object.keys(this.data[source]).length === 0) {
40
+ delete this.data[source];
41
41
  }
42
42
  }
43
43
  *entries() {
44
- for (const [original, locales] of Object.entries(this.data)) {
44
+ for (const [source, locales] of Object.entries(this.data)) {
45
45
  for (const [locale, translation] of Object.entries(locales)) {
46
- yield { original, locale, translation };
46
+ yield { source, locale, translation };
47
47
  }
48
48
  }
49
49
  }
@@ -53,21 +53,21 @@ var Cache = class {
53
53
  };
54
54
 
55
55
  // src/diff.ts
56
- var findNewTranslations = (cache, originals, locales) => {
56
+ var findNewTranslations = (cache, sources, locales) => {
57
57
  const list = [];
58
- for (const original of originals) {
58
+ for (const source of sources) {
59
59
  for (const locale of locales) {
60
- if (!cache.has(original, locale)) {
61
- list.push({ original, locale });
60
+ if (!cache.has(source, locale)) {
61
+ list.push({ source, locale });
62
62
  }
63
63
  }
64
64
  }
65
65
  return list;
66
66
  };
67
- var removeUnusedTranslations = (cache, originals, locales) => {
67
+ var removeUnusedTranslations = (cache, sources, locales) => {
68
68
  for (const item of cache.entries()) {
69
- if (!locales.includes(item.locale) || !originals.includes(item.original)) {
70
- cache.delete(item.original, item.locale);
69
+ if (!locales.includes(item.locale) || !sources.includes(item.source)) {
70
+ cache.delete(item.source, item.locale);
71
71
  }
72
72
  }
73
73
  };
@@ -88,10 +88,7 @@ var findSvelteTranslatable = (code) => {
88
88
  css: false
89
89
  });
90
90
  const enter = (node) => {
91
- if (
92
- //
93
- node.type === "TaggedTemplateExpression" && node.tag.type === "MemberExpression" && node.tag.object.type === "Identifier" && node.tag.object.name === "lang" && node.tag.property.type === "Identifier" && node.tag.property.name === "t" && node.quasi.type === "TemplateLiteral" && node.quasi.loc
94
- ) {
91
+ if (node.type === "TaggedTemplateExpression" && node.tag.type === "MemberExpression" && node.tag.object.type === "Identifier" && node.tag.object.name === "lang" && node.tag.property.type === "Identifier" && node.tag.property.name === "t" && node.quasi.type === "TemplateLiteral" && node.quasi.loc) {
95
92
  const start = node.quasi.loc.start;
96
93
  const end = node.quasi.loc.end;
97
94
  const content = code.substring(
@@ -151,7 +148,8 @@ var findTranslatable = async (cwd) => {
151
148
  if (file.endsWith(".svelte")) {
152
149
  found.push(...findSvelteTranslatable(code));
153
150
  } else {
154
- found.push(...await findTypescriptTranslatable(code));
151
+ const entries = await findTypescriptTranslatable(code);
152
+ found.push(...entries);
155
153
  }
156
154
  }
157
155
  }
@@ -159,7 +157,7 @@ var findTranslatable = async (cwd) => {
159
157
  };
160
158
 
161
159
  // src/vite.ts
162
- var createI18nPlugin = (props) => {
160
+ var i18n = (props) => {
163
161
  let cache;
164
162
  return {
165
163
  name: "awsless/i18n",
@@ -167,16 +165,16 @@ var createI18nPlugin = (props) => {
167
165
  async buildStart() {
168
166
  const cwd = process.cwd();
169
167
  this.info("Finding all translatable text...");
170
- const originals = await findTranslatable(cwd);
168
+ const sourceTexts = await findTranslatable(cwd);
171
169
  cache = await loadCache(cwd);
172
- removeUnusedTranslations(cache, originals, props.locales);
173
- const newOriginals = findNewTranslations(cache, originals, props.locales);
174
- if (newOriginals.length > 0) {
175
- this.info(`Translating ${newOriginals.length} new texts.`);
176
- const translations = await props.translate(props.default ?? "en", newOriginals);
170
+ removeUnusedTranslations(cache, sourceTexts, props.locales);
171
+ const newSourceTexts = findNewTranslations(cache, sourceTexts, props.locales);
172
+ if (newSourceTexts.length > 0) {
173
+ this.info(`Translating ${newSourceTexts.length} new texts.`);
174
+ const translations = await props.translate(props.default ?? "en", newSourceTexts);
177
175
  this.info(`Translated ${translations.length} texts.`);
178
176
  for (const item of translations) {
179
- cache.set(item.original, item.locale, item.translation);
177
+ cache.set(item.source, item.locale, item.translation);
180
178
  }
181
179
  }
182
180
  await saveCache(cwd, cache);
@@ -186,11 +184,15 @@ var createI18nPlugin = (props) => {
186
184
  let replaced = false;
187
185
  if (code.includes("lang.t`")) {
188
186
  for (const item of cache.entries()) {
189
- code = code.replaceAll(`lang.t\`${item.original}\``, () => {
187
+ code = code.replaceAll(`lang.t\`${item.source}\``, () => {
190
188
  replaced = true;
191
- return `lang.t.get(\`${item.original}\`, {${props.locales.map((locale) => {
192
- return `"${locale}":\`${cache.get(item.original, locale)}\``;
193
- }).join(",")}})`;
189
+ return `lang.t.get(\`${item.source}\`, {${props.locales.map((locale) => {
190
+ const translation = cache.get(item.source, locale);
191
+ if (translation === item.source) {
192
+ return;
193
+ }
194
+ return `"${locale}":\`${translation}\``;
195
+ }).filter((v) => !!v).join(",")}})`;
194
196
  });
195
197
  }
196
198
  }
@@ -218,7 +220,7 @@ var ai = (props) => {
218
220
  maxTokens: props.maxTokens,
219
221
  schema: z.object({
220
222
  translations: z.object({
221
- original: z.string(),
223
+ source: z.string(),
222
224
  locale: z.string(),
223
225
  translation: z.string()
224
226
  }).array()
@@ -240,5 +242,5 @@ var ai = (props) => {
240
242
  };
241
243
  export {
242
244
  ai,
243
- createI18nPlugin as i18n
245
+ i18n
244
246
  };
@@ -34,12 +34,29 @@ var t = $derived.by(() => {
34
34
  return api;
35
35
  });
36
36
  var lang = {
37
- set locale(v) {
38
- locale = v;
39
- },
37
+ /** Get the current locale.
38
+ *
39
+ * @example
40
+ * console.log(lang.locale)
41
+ */
40
42
  get locale() {
41
43
  return locale;
42
44
  },
45
+ /** To change the locale that is being rendered simply change this property.
46
+ *
47
+ * @example
48
+ * lang.locale = 'jp'
49
+ */
50
+ set locale(v) {
51
+ locale = v;
52
+ },
53
+ /** Translate helper for translating template strings.
54
+ * The i18n Vite plugin will find all instances where you want text
55
+ * to be translated and automatically translate your text during build time.
56
+ *
57
+ * @example
58
+ * lang.t`Hello world!`
59
+ */
43
60
  get t() {
44
61
  return t;
45
62
  }
@@ -1,5 +1,17 @@
1
1
  declare const lang: {
2
+ /** Get the current locale.
3
+ *
4
+ * @example
5
+ * console.log(lang.locale)
6
+ */
2
7
  locale: string;
8
+ /** Translate helper for translating template strings.
9
+ * The i18n Vite plugin will find all instances where you want text
10
+ * to be translated and automatically translate your text during build time.
11
+ *
12
+ * @example
13
+ * lang.t`Hello world!`
14
+ */
3
15
  readonly t: (template: TemplateStringsArray, ...args: Array<string | number | {
4
16
  toString(): string;
5
17
  }>) => string;
@@ -1,5 +1,17 @@
1
1
  declare const lang: {
2
+ /** Get the current locale.
3
+ *
4
+ * @example
5
+ * console.log(lang.locale)
6
+ */
2
7
  locale: string;
8
+ /** Translate helper for translating template strings.
9
+ * The i18n Vite plugin will find all instances where you want text
10
+ * to be translated and automatically translate your text during build time.
11
+ *
12
+ * @example
13
+ * lang.t`Hello world!`
14
+ */
3
15
  readonly t: (template: TemplateStringsArray, ...args: Array<string | number | {
4
16
  toString(): string;
5
17
  }>) => string;
@@ -10,12 +10,29 @@ var t = $derived.by(() => {
10
10
  return api;
11
11
  });
12
12
  var lang = {
13
- set locale(v) {
14
- locale = v;
15
- },
13
+ /** Get the current locale.
14
+ *
15
+ * @example
16
+ * console.log(lang.locale)
17
+ */
16
18
  get locale() {
17
19
  return locale;
18
20
  },
21
+ /** To change the locale that is being rendered simply change this property.
22
+ *
23
+ * @example
24
+ * lang.locale = 'jp'
25
+ */
26
+ set locale(v) {
27
+ locale = v;
28
+ },
29
+ /** Translate helper for translating template strings.
30
+ * The i18n Vite plugin will find all instances where you want text
31
+ * to be translated and automatically translate your text during build time.
32
+ *
33
+ * @example
34
+ * lang.t`Hello world!`
35
+ */
19
36
  get t() {
20
37
  return t;
21
38
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsless/i18n",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "keywords": [