@gravito/cosmos 3.0.1 → 3.1.0

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/src/index.ts CHANGED
@@ -1,19 +1,32 @@
1
+ /**
2
+ * @file packages/cosmos/src/index.ts
3
+ * @module @gravito/cosmos
4
+ * @description Entry point for the Gravito Cosmos internationalization module.
5
+ */
6
+
1
7
  import type { GravitoMiddleware, GravitoOrbit, PlanetCore } from '@gravito/core'
2
8
  import { type I18nConfig, I18nManager, type I18nService, localeMiddleware } from './I18nService'
3
9
 
4
10
  declare module '@gravito/core' {
5
11
  interface GravitoVariables {
6
- /** I18n service for translations */
12
+ /**
13
+ * The request-scoped I18n service instance.
14
+ * Provides translation and locale management for the current request.
15
+ */
7
16
  i18n: I18nService
8
17
  }
9
18
  }
10
19
 
11
20
  /**
12
- * OrbitCosmos provides internationalization (i18n) support for Gravito.
13
- * It manages translations, locale switching, and provides a middleware for request-scoped locale detection.
21
+ * OrbitCosmos provides internationalization (i18n) support for the Gravito framework.
22
+ *
23
+ * It manages global translation resources, handles locale detection through middleware,
24
+ * and provides request-scoped i18n instances.
14
25
  *
15
26
  * @example
16
27
  * ```typescript
28
+ * import { OrbitCosmos } from '@gravito/cosmos'
29
+ *
17
30
  * const cosmos = new OrbitCosmos({
18
31
  * defaultLocale: 'en',
19
32
  * supportedLocales: ['en', 'zh-TW'],
@@ -25,19 +38,25 @@ declare module '@gravito/core' {
25
38
  * core.addOrbit(cosmos);
26
39
  * ```
27
40
  * @public
41
+ * @since 3.0.0
28
42
  */
29
43
  export class OrbitCosmos implements GravitoOrbit {
30
44
  /**
31
45
  * Create a new OrbitCosmos instance.
32
- * @param config - The i18n configuration options.
46
+ *
47
+ * @param config - The i18n configuration options including locales and translations.
33
48
  */
34
49
  constructor(private config: I18nConfig) {}
35
50
 
36
51
  /**
37
52
  * Install the i18n service into PlanetCore.
38
- * Registers the I18nManager and sets up the locale middleware.
39
53
  *
40
- * @param core - The PlanetCore instance.
54
+ * This method:
55
+ * 1. Initializes the central `I18nManager`.
56
+ * 2. Registers the manager in the IoC container as 'i18n'.
57
+ * 3. Injects the `localeMiddleware` into the adapter for all routes.
58
+ *
59
+ * @param core - The PlanetCore instance to install into.
41
60
  */
42
61
  install(core: PlanetCore): void {
43
62
  const i18nManager = new I18nManager(this.config)
package/src/loader.ts CHANGED
@@ -1,15 +1,28 @@
1
+ /**
2
+ * @file packages/cosmos/src/loader.ts
3
+ * @module @gravito/cosmos/loader
4
+ * @description Utilities for loading translation files from the filesystem.
5
+ */
6
+
1
7
  import { readdir, readFile } from 'node:fs/promises'
2
8
  import { join, parse } from 'node:path'
3
9
 
4
10
  /**
5
- * Load translations from a directory
6
- * Structure:
11
+ * Load translations from a directory.
12
+ *
13
+ * Scans the specified directory for JSON files and loads them as translation bundles.
14
+ * The filename (without extension) is used as the locale key.
15
+ *
16
+ * @example
17
+ * ```
7
18
  * /lang
8
- * /en.json -> { "welcome": "Hello" }
9
- * /zh.json -> { "welcome": "Hello" }
10
- * /en/auth.json -> { "failed": "Login failed" } (Optional deep structure, maybe later)
19
+ * /en.json -> { "welcome": "Hello" }
20
+ * /zh-TW.json -> { "welcome": "你好" }
21
+ * ```
11
22
  *
12
- * For now, we support flat JSON files per locale: en.json, zh.json
23
+ * @param directory - The absolute path to the translations directory.
24
+ * @returns A promise that resolves to a map of locale -> translations.
25
+ * @public
13
26
  */
14
27
  export async function loadTranslations(
15
28
  directory: string
@@ -25,12 +38,9 @@ export async function loadTranslations(
25
38
  }
26
39
 
27
40
  const locale = parse(file).name // 'en' from 'en.json'
28
- const content = await readFile(join(directory, file), 'utf-8')
29
-
30
- try {
31
- translations[locale] = JSON.parse(content)
32
- } catch (e) {
33
- console.error(`[Orbit-I18n] Failed to parse translation file: ${file}`, e)
41
+ const translationsForLocale = await loadLocale(directory, locale)
42
+ if (translationsForLocale) {
43
+ translations[locale] = translationsForLocale
34
44
  }
35
45
  }
36
46
  } catch (_e) {
@@ -41,3 +51,26 @@ export async function loadTranslations(
41
51
 
42
52
  return translations
43
53
  }
54
+
55
+ /**
56
+ * Load translations for a specific locale from a directory.
57
+ *
58
+ * Expects a file named `{locale}.json` in the given directory.
59
+ *
60
+ * @param directory - The directory containing translation files.
61
+ * @param locale - The locale string to load.
62
+ * @returns A promise that resolves to the translations map, or null if the file could not be read.
63
+ * @public
64
+ */
65
+ export async function loadLocale(
66
+ directory: string,
67
+ locale: string
68
+ ): Promise<Record<string, string> | null> {
69
+ const filePath = join(directory, `${locale}.json`)
70
+ try {
71
+ const content = await readFile(filePath, 'utf-8')
72
+ return JSON.parse(content)
73
+ } catch {
74
+ return null
75
+ }
76
+ }
@@ -0,0 +1,41 @@
1
+ import { type I18nConfig, I18nManager, type TranslationMap } from '../../src/I18nService'
2
+
3
+ export function createTestManager(overrides?: Partial<I18nConfig>): I18nManager {
4
+ return new I18nManager({
5
+ defaultLocale: 'en',
6
+ supportedLocales: ['en', 'zh-TW'],
7
+ translations: {
8
+ en: {
9
+ common: {
10
+ welcome: 'Welcome',
11
+ goodbye: 'Goodbye',
12
+ },
13
+ },
14
+ 'zh-TW': {
15
+ common: {
16
+ welcome: '歡迎',
17
+ goodbye: '再見',
18
+ },
19
+ },
20
+ },
21
+ ...overrides,
22
+ })
23
+ }
24
+
25
+ export function generateLargeTranslations(
26
+ locales = 5,
27
+ keys = 1000
28
+ ): Record<string, TranslationMap> {
29
+ const result: Record<string, TranslationMap> = {}
30
+
31
+ for (let l = 0; l < locales; l++) {
32
+ const locale = `locale${l}`
33
+ result[locale] = {}
34
+
35
+ for (let k = 0; k < keys; k++) {
36
+ result[locale][`key${k}`] = `Translation ${k} for ${locale}`
37
+ }
38
+ }
39
+
40
+ return result
41
+ }
@@ -0,0 +1,27 @@
1
+ import { bench, run } from 'mitata'
2
+ import { I18nManager } from '../../src/I18nService'
3
+ import { generateLargeTranslations } from '../helpers/factory'
4
+
5
+ const manager = new I18nManager({
6
+ defaultLocale: 'en',
7
+ supportedLocales: ['en', 'zh-TW'],
8
+ translations: generateLargeTranslations(),
9
+ })
10
+
11
+ // Warmup
12
+ manager.translate('locale0', 'key0')
13
+
14
+ bench('Simple translation (cache hit)', () => {
15
+ manager.translate('locale0', 'key0')
16
+ })
17
+
18
+ bench('Batch translation (100 keys)', () => {
19
+ const keys = Array.from({ length: 100 }, (_, i) => `key${i}`)
20
+ manager.tMany(keys)
21
+ })
22
+
23
+ bench('Fallback chain (missing key)', () => {
24
+ manager.translate('locale0', 'missing.key')
25
+ })
26
+
27
+ await run()
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { I18nManager } from '../../src/I18nService'
3
+
4
+ describe('I18nManager API', () => {
5
+ it('supports multiple translations', () => {
6
+ const manager = new I18nManager({
7
+ defaultLocale: 'en',
8
+ supportedLocales: ['en'],
9
+ translations: {
10
+ en: {
11
+ hello: 'Hello',
12
+ goodbye: 'Goodbye',
13
+ },
14
+ },
15
+ })
16
+
17
+ const translations = manager.tMany(['hello', 'goodbye', 'missing'])
18
+
19
+ expect(translations.hello).toBe('Hello')
20
+ expect(translations.goodbye).toBe('Goodbye')
21
+ expect(translations.missing).toBe('missing')
22
+ })
23
+
24
+ it('reports loaded locales', async () => {
25
+ const manager = new I18nManager({
26
+ defaultLocale: 'en',
27
+ supportedLocales: ['en', 'fr'],
28
+ translations: {
29
+ en: { hello: 'Hello' },
30
+ },
31
+ })
32
+
33
+ expect(manager.getLocales()).toContain('en')
34
+ expect(manager.isLocaleLoaded('en')).toBe(true)
35
+ expect(manager.isLocaleLoaded('fr')).toBe(false)
36
+ })
37
+ })
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it, jest } from 'bun:test'
2
+ import {
3
+ DefaultDetectors,
4
+ type I18nService,
5
+ type LocaleDetector,
6
+ localeMiddleware,
7
+ } from '../../src/I18nService'
8
+
9
+ describe('Locale Detectors', () => {
10
+ const mockContext = (params: any = {}) => ({
11
+ req: {
12
+ param: (key: string) => params.param?.[key],
13
+ query: (key: string) => params.query?.[key],
14
+ header: (key: string) => params.header?.[key],
15
+ },
16
+ set: jest.fn(),
17
+ })
18
+
19
+ it('detects from route param', async () => {
20
+ const ctx = mockContext({ param: { locale: 'fr' } })
21
+ const detector = DefaultDetectors.find((d) => d.name === 'routeParam')!
22
+ expect(await detector.detect(ctx)).toBe('fr')
23
+ })
24
+
25
+ it('detects from query', async () => {
26
+ const ctx = mockContext({ query: { lang: 'de' } })
27
+ const detector = DefaultDetectors.find((d) => d.name === 'query')!
28
+ expect(await detector.detect(ctx)).toBe('de')
29
+ })
30
+
31
+ it('detects from header', async () => {
32
+ const ctx = mockContext({ header: { 'Accept-Language': 'es-ES,es;q=0.9' } })
33
+ const detector = DefaultDetectors.find((d) => d.name === 'header')!
34
+ expect(await detector.detect(ctx)).toBe('es-ES')
35
+ })
36
+ })
37
+
38
+ describe('Locale Middleware', () => {
39
+ it('uses detectors in order', async () => {
40
+ const mockManager = {
41
+ ensureLocale: jest.fn(),
42
+ clone: jest.fn().mockReturnValue({}),
43
+ } as unknown as I18nService
44
+
45
+ const middleware = localeMiddleware(mockManager, DefaultDetectors)
46
+
47
+ const ctx1 = {
48
+ req: {
49
+ param: () => 'fr',
50
+ query: () => 'de',
51
+ header: () => 'es',
52
+ },
53
+ set: jest.fn(),
54
+ }
55
+ await middleware(ctx1 as any, async () => {})
56
+ expect(mockManager.ensureLocale).toHaveBeenCalledWith('fr')
57
+ expect(mockManager.clone).toHaveBeenCalledWith('fr')
58
+
59
+ const ctx2 = {
60
+ req: {
61
+ param: () => undefined,
62
+ query: () => 'de',
63
+ header: () => 'es',
64
+ },
65
+ set: jest.fn(),
66
+ }
67
+ await middleware(ctx2 as any, async () => {})
68
+ expect(mockManager.ensureLocale).toHaveBeenCalledWith('de')
69
+ })
70
+ })
@@ -0,0 +1,100 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { I18nManager } from '../../src/I18nService'
3
+
4
+ describe('I18nManager - Edge Cases', () => {
5
+ describe('Empty Translations', () => {
6
+ it('should handle empty translation object', () => {
7
+ const manager = new I18nManager({
8
+ defaultLocale: 'en',
9
+ supportedLocales: ['en'],
10
+ translations: {},
11
+ })
12
+ expect(manager.translate('en', 'any.key')).toBe('any.key')
13
+ })
14
+
15
+ it('should handle empty string translation', () => {
16
+ const manager = new I18nManager({
17
+ defaultLocale: 'en',
18
+ supportedLocales: ['en'],
19
+ translations: { en: { empty: '' } },
20
+ })
21
+ expect(manager.translate('en', 'empty')).toBe('')
22
+ })
23
+ })
24
+
25
+ describe('Special Characters', () => {
26
+ it('should handle keys with special characters', () => {
27
+ const manager = new I18nManager({
28
+ defaultLocale: 'en',
29
+ supportedLocales: ['en'],
30
+ translations: { en: { 'key.with.dots': 'value' } },
31
+ })
32
+ expect(manager.translate('en', 'key.with.dots')).toBe('key.with.dots')
33
+ })
34
+
35
+ it('should handle unicode keys', () => {
36
+ const manager = new I18nManager({
37
+ defaultLocale: 'zh-TW',
38
+ supportedLocales: ['zh-TW'],
39
+ translations: { 'zh-TW': { 歡迎: '歡迎訊息' } },
40
+ })
41
+ expect(manager.translate('zh-TW', '歡迎')).toBe('歡迎訊息')
42
+ })
43
+
44
+ it('should handle HTML in translations', () => {
45
+ const manager = new I18nManager({
46
+ defaultLocale: 'en',
47
+ supportedLocales: ['en'],
48
+ translations: { en: { html: '<strong>Bold</strong>' } },
49
+ })
50
+ expect(manager.translate('en', 'html')).toBe('<strong>Bold</strong>')
51
+ })
52
+ })
53
+
54
+ describe('Deep Nesting', () => {
55
+ it('should handle deeply nested keys (10 levels)', () => {
56
+ const translations = {
57
+ en: {
58
+ l1: { l2: { l3: { l4: { l5: { l6: { l7: { l8: { l9: { l10: 'deep' } } } } } } } } },
59
+ },
60
+ }
61
+ const manager = new I18nManager({
62
+ defaultLocale: 'en',
63
+ supportedLocales: ['en'],
64
+ translations,
65
+ })
66
+ expect(manager.translate('en', 'l1.l2.l3.l4.l5.l6.l7.l8.l9.l10')).toBe('deep')
67
+ })
68
+ })
69
+
70
+ describe('Parameter Replacement', () => {
71
+ it('should handle missing parameters', () => {
72
+ const manager = new I18nManager({
73
+ defaultLocale: 'en',
74
+ supportedLocales: ['en'],
75
+ translations: { en: { greeting: 'Hello :name!' } },
76
+ })
77
+ expect(manager.translate('en', 'greeting', {})).toBe('Hello :name!')
78
+ })
79
+
80
+ it('should handle extra parameters', () => {
81
+ const manager = new I18nManager({
82
+ defaultLocale: 'en',
83
+ supportedLocales: ['en'],
84
+ translations: { en: { greeting: 'Hello :name!' } },
85
+ })
86
+ expect(manager.translate('en', 'greeting', { name: 'Carl', extra: 'ignored' })).toBe(
87
+ 'Hello Carl!'
88
+ )
89
+ })
90
+
91
+ it('should handle parameter with special regex characters', () => {
92
+ const manager = new I18nManager({
93
+ defaultLocale: 'en',
94
+ supportedLocales: ['en'],
95
+ translations: { en: { msg: 'Value: :value' } },
96
+ })
97
+ expect(manager.translate('en', 'msg', { value: '$100.00' })).toBe('Value: $100.00')
98
+ })
99
+ })
100
+ })
@@ -0,0 +1,66 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { I18nManager } from '../../src/I18nService'
3
+
4
+ describe('Fallback Strategies', () => {
5
+ it('uses default fallback chain (defaultLocale)', () => {
6
+ const manager = new I18nManager({
7
+ defaultLocale: 'en',
8
+ supportedLocales: ['en', 'fr'],
9
+ translations: {
10
+ en: { hello: 'Hello' },
11
+ fr: {},
12
+ },
13
+ })
14
+ expect(manager.translate('fr', 'hello')).toBe('Hello')
15
+ })
16
+
17
+ it('uses configured fallback chain', () => {
18
+ const manager = new I18nManager({
19
+ defaultLocale: 'en',
20
+ supportedLocales: ['en', 'es', 'pt'],
21
+ translations: {
22
+ en: { hello: 'Hello' },
23
+ es: { hello: 'Hola' },
24
+ pt: {},
25
+ },
26
+ fallback: {
27
+ fallbackChain: {
28
+ pt: ['es', 'en'],
29
+ },
30
+ },
31
+ })
32
+ expect(manager.translate('pt', 'hello')).toBe('Hola')
33
+ })
34
+
35
+ it('handles missing key strategies', () => {
36
+ const manager = new I18nManager({
37
+ defaultLocale: 'en',
38
+ supportedLocales: ['en'],
39
+ translations: { en: {} },
40
+ fallback: {
41
+ onMissingKey: 'empty',
42
+ },
43
+ })
44
+ expect(manager.translate('en', 'missing')).toBe('')
45
+
46
+ const managerThrow = new I18nManager({
47
+ defaultLocale: 'en',
48
+ supportedLocales: ['en'],
49
+ translations: { en: {} },
50
+ fallback: {
51
+ onMissingKey: 'throw',
52
+ },
53
+ })
54
+ expect(() => managerThrow.translate('en', 'missing')).toThrow()
55
+
56
+ const managerCustom = new I18nManager({
57
+ defaultLocale: 'en',
58
+ supportedLocales: ['en'],
59
+ translations: { en: {} },
60
+ fallback: {
61
+ onMissingKey: (key, locale) => `Missing ${key} in ${locale}`,
62
+ },
63
+ })
64
+ expect(managerCustom.translate('en', 'missing')).toBe('Missing missing in en')
65
+ })
66
+ })
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { mkdtempSync, writeFileSync } from 'node:fs'
3
+ import { rm } from 'node:fs/promises'
4
+ import { tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+ import { I18nManager } from '../../src/I18nService'
7
+
8
+ describe('Lazy Loading', () => {
9
+ it('loads translations on demand', async () => {
10
+ const tmpDir = mkdtempSync(join(tmpdir(), 'gravito-i18n-lazy-'))
11
+
12
+ try {
13
+ writeFileSync(join(tmpDir, 'fr.json'), JSON.stringify({ greeting: 'Bonjour' }))
14
+
15
+ const manager = new I18nManager({
16
+ defaultLocale: 'en',
17
+ supportedLocales: ['en', 'fr'],
18
+ lazyLoad: {
19
+ baseDir: tmpDir,
20
+ },
21
+ })
22
+
23
+ expect(manager.translate('fr', 'greeting')).toBe('greeting')
24
+
25
+ await manager.ensureLocale('fr')
26
+
27
+ expect(manager.translate('fr', 'greeting')).toBe('Bonjour')
28
+
29
+ await manager.ensureLocale('fr')
30
+ expect(manager.translate('fr', 'greeting')).toBe('Bonjour')
31
+ } finally {
32
+ await rm(tmpDir, { force: true, recursive: true })
33
+ }
34
+ })
35
+ })
@@ -3,7 +3,7 @@ import { mkdtempSync, writeFileSync } from 'node:fs'
3
3
  import { rm } from 'node:fs/promises'
4
4
  import { tmpdir } from 'node:os'
5
5
  import { join } from 'node:path'
6
- import { loadTranslations } from '../src/loader'
6
+ import { loadTranslations } from '../../src/loader'
7
7
 
8
8
  describe('loadTranslations', () => {
9
9
  it('loads json translation files from a directory', async () => {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
- import { I18nManager } from '../src/index'
2
+ import { I18nManager } from '../../src/index'
3
3
 
4
4
  describe('Orbit I18n Manager', () => {
5
5
  const _config = {
@@ -0,0 +1,58 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { I18nManager } from '../../src/I18nService'
3
+
4
+ describe('Pluralization', () => {
5
+ it('handles plural forms correctly', () => {
6
+ const translations = {
7
+ en: {
8
+ items: {
9
+ zero: 'No items',
10
+ one: '1 item',
11
+ other: ':count items',
12
+ },
13
+ apples: {
14
+ one: 'an apple',
15
+ other: ':count apples',
16
+ },
17
+ },
18
+ fr: {
19
+ // French treats 0 and 1 as singular (usually)
20
+ items: {
21
+ one: 'un élément', // used for 0 and 1 in standard FR rule? Wait.
22
+ other: ':count éléments',
23
+ },
24
+ },
25
+ }
26
+
27
+ const manager = new I18nManager({
28
+ defaultLocale: 'en',
29
+ supportedLocales: ['en', 'fr'],
30
+ translations,
31
+ })
32
+
33
+ // English
34
+ expect(manager.translate('en', 'items', { count: 0 })).toBe('No items') // explicit zero
35
+ expect(manager.translate('en', 'items', { count: 1 })).toBe('1 item')
36
+ expect(manager.translate('en', 'items', { count: 2 })).toBe('2 items')
37
+ expect(manager.translate('en', 'items', { count: 10 })).toBe('10 items')
38
+
39
+ // Missing zero key uses 'other' or 'one' based on rules?
40
+ // English rules: one (n=1), other (n!=1). 0 falls to other.
41
+ expect(manager.translate('en', 'apples', { count: 0 })).toBe('0 apples')
42
+ expect(manager.translate('en', 'apples', { count: 1 })).toBe('an apple')
43
+
44
+ // French
45
+ // French rules: one (n>=0 && n<2), other (everything else)
46
+ // So 0 is one. 1 is one. 2 is other.
47
+ // Wait, let's verify node/bun Intl behavior.
48
+
49
+ // In our code, we check 'zero' key FIRST if count === 0.
50
+ // But 'fr' translations above don't have 'zero' key.
51
+ // So it should fall to `pluralMap[form]`.
52
+ // For fr, 0 -> 'one'.
53
+
54
+ expect(manager.translate('fr', 'items', { count: 0 })).toBe('un élément')
55
+ expect(manager.translate('fr', 'items', { count: 1 })).toBe('un élément')
56
+ expect(manager.translate('fr', 'items', { count: 2 })).toBe('2 éléments')
57
+ })
58
+ })
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it, jest } from 'bun:test'
2
- import { I18nInstance, I18nManager, localeMiddleware } from '../src/I18nService'
3
- import { I18nOrbit, OrbitCosmos } from '../src/index'
2
+ import { I18nInstance, I18nManager, localeMiddleware } from '../../src/I18nService'
3
+ import { I18nOrbit, OrbitCosmos } from '../../src/index'
4
4
 
5
5
  describe('I18nManager and I18nInstance', () => {
6
6
  const baseConfig = {
package/tsconfig.json CHANGED
@@ -1,26 +1,14 @@
1
1
  {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "baseUrl": ".",
6
- "paths": {
7
- "@gravito/core": [
8
- "../../packages/core/src/index.ts"
9
- ],
10
- "@gravito/*": [
11
- "../../packages/*/src/index.ts"
12
- ]
13
- },
14
- "types": [
15
- "bun-types"
16
- ]
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "baseUrl": ".",
6
+ "paths": {
7
+ "@gravito/core": ["../../packages/core/src/index.ts"],
8
+ "@gravito/*": ["../../packages/*/src/index.ts"]
17
9
  },
18
- "include": [
19
- "src/**/*"
20
- ],
21
- "exclude": [
22
- "node_modules",
23
- "dist",
24
- "**/*.test.ts"
25
- ]
26
- }
10
+ "types": ["bun-types"]
11
+ },
12
+ "include": ["src/**/*"],
13
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
14
+ }
@@ -1,20 +0,0 @@
1
-
2
- $ bun run build.ts
3
- Building @gravito/cosmos...
4
- ⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠋⠙⠹CLI Building entry: src/index.ts
5
- CLI Using tsconfig: tsconfig.json
6
- CLI tsup v8.5.1
7
- CLI Target: esnext
8
- ESM Build start
9
- CJS Build start
10
- ESM You have emitDecoratorMetadata enabled but @swc/core was not installed, skipping swc plugin
11
- CJS You have emitDecoratorMetadata enabled but @swc/core was not installed, skipping swc plugin
12
- ESM dist/index.js 6.93 KB
13
- ESM ⚡️ Build success in 72ms
14
- CJS dist/index.cjs 8.20 KB
15
- CJS ⚡️ Build success in 72ms
16
- DTS Build start
17
- DTS ⚡️ Build success in 7986ms
18
- DTS dist/index.d.ts 7.87 KB
19
- DTS dist/index.d.cts 7.87 KB
20
- ⠙✅ Build complete!