@gravito/monolith 3.0.1 → 3.2.1
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/CHANGELOG.md +22 -0
- package/dist/index.cjs +295 -12
- package/dist/index.d.cts +62 -4
- package/dist/index.d.ts +62 -4
- package/dist/index.js +293 -12
- package/ion/src/index.js +2775 -2559
- package/package.json +13 -10
- package/scripts/check-coverage.ts +64 -0
- package/src/ContentManager.ts +103 -13
- package/src/ContentWatcher.ts +123 -0
- package/src/driver/ContentDriver.ts +5 -0
- package/src/driver/GitHubDriver.ts +80 -0
- package/src/driver/LocalDriver.ts +30 -0
- package/src/index.ts +29 -1
- package/tests/content-cache.test.ts +36 -0
- package/tests/content-search.test.ts +79 -0
- package/tests/content-watcher.test.ts +56 -0
- package/tests/content.test.ts +2 -1
- package/tests/extra.test.ts +2 -1
- package/tests/hot-reload.test.ts +74 -0
- package/tsconfig.json +13 -19
- package/dist/src/index.js +0 -5624
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { ContentManager } from '../src/ContentManager'
|
|
6
|
+
import { LocalDriver } from '../src/driver/LocalDriver'
|
|
7
|
+
|
|
8
|
+
const TMP_DIR = join(import.meta.dir, 'tmp_search')
|
|
9
|
+
|
|
10
|
+
describe('ContentManager Search', () => {
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
if (existsSync(TMP_DIR)) {
|
|
13
|
+
await rm(TMP_DIR, { recursive: true, force: true })
|
|
14
|
+
}
|
|
15
|
+
await mkdir(join(TMP_DIR, 'docs', 'en'), { recursive: true })
|
|
16
|
+
await mkdir(join(TMP_DIR, 'blog', 'en'), { recursive: true })
|
|
17
|
+
|
|
18
|
+
await writeFile(
|
|
19
|
+
join(TMP_DIR, 'docs', 'en', 'install.md'),
|
|
20
|
+
'---\ntitle: Installation Guide\n---\nRun bun install to get started.'
|
|
21
|
+
)
|
|
22
|
+
await writeFile(
|
|
23
|
+
join(TMP_DIR, 'blog', 'en', 'welcome.md'),
|
|
24
|
+
'---\ntitle: Welcome to Gravito\n---\nThis is a framework for everyone.'
|
|
25
|
+
)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
if (existsSync(TMP_DIR)) {
|
|
30
|
+
await rm(TMP_DIR, { recursive: true, force: true })
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should find items by keyword', async () => {
|
|
35
|
+
const manager = new ContentManager(new LocalDriver(TMP_DIR))
|
|
36
|
+
manager.defineCollection('docs', { path: 'docs' })
|
|
37
|
+
manager.defineCollection('blog', { path: 'blog' })
|
|
38
|
+
|
|
39
|
+
// Index items
|
|
40
|
+
await manager.find('docs', 'install')
|
|
41
|
+
await manager.find('blog', 'welcome')
|
|
42
|
+
|
|
43
|
+
const results = manager.search('install')
|
|
44
|
+
expect(results.length).toBe(1)
|
|
45
|
+
expect(results[0].slug).toBe('install')
|
|
46
|
+
|
|
47
|
+
const results2 = manager.search('framework')
|
|
48
|
+
expect(results2.length).toBe(1)
|
|
49
|
+
expect(results2[0].slug).toBe('welcome')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should support multiple terms (OR logic)', async () => {
|
|
53
|
+
const manager = new ContentManager(new LocalDriver(TMP_DIR))
|
|
54
|
+
manager.defineCollection('docs', { path: 'docs' })
|
|
55
|
+
manager.defineCollection('blog', { path: 'blog' })
|
|
56
|
+
|
|
57
|
+
await manager.find('docs', 'install')
|
|
58
|
+
await manager.find('blog', 'welcome')
|
|
59
|
+
|
|
60
|
+
const results = manager.search('install framework')
|
|
61
|
+
expect(results.length).toBe(2)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should filter by collection', async () => {
|
|
65
|
+
const manager = new ContentManager(new LocalDriver(TMP_DIR))
|
|
66
|
+
manager.defineCollection('docs', { path: 'docs' })
|
|
67
|
+
manager.defineCollection('blog', { path: 'blog' })
|
|
68
|
+
|
|
69
|
+
await manager.find('docs', 'install')
|
|
70
|
+
await manager.find('blog', 'welcome')
|
|
71
|
+
|
|
72
|
+
// 'everyone' is in blog
|
|
73
|
+
const results = manager.search('everyone', { collection: 'docs' })
|
|
74
|
+
expect(results.length).toBe(0)
|
|
75
|
+
|
|
76
|
+
const results2 = manager.search('everyone', { collection: 'blog' })
|
|
77
|
+
expect(results2.length).toBe(1)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { ContentManager } from '../src/ContentManager'
|
|
6
|
+
import { ContentWatcher } from '../src/ContentWatcher'
|
|
7
|
+
import { LocalDriver } from '../src/driver/LocalDriver'
|
|
8
|
+
|
|
9
|
+
const TMP_DIR = join(import.meta.dir, 'tmp_watch')
|
|
10
|
+
|
|
11
|
+
describe('ContentWatcher', () => {
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
if (existsSync(TMP_DIR)) {
|
|
14
|
+
await rm(TMP_DIR, { recursive: true, force: true })
|
|
15
|
+
}
|
|
16
|
+
await mkdir(join(TMP_DIR, 'docs', 'en'), { recursive: true })
|
|
17
|
+
await writeFile(join(TMP_DIR, 'docs', 'en', 'test.md'), '# Original')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
if (existsSync(TMP_DIR)) {
|
|
22
|
+
await rm(TMP_DIR, { recursive: true, force: true })
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should invalidate cache on file change', async () => {
|
|
27
|
+
const manager = new ContentManager(new LocalDriver(TMP_DIR))
|
|
28
|
+
manager.defineCollection('docs', { path: 'docs' })
|
|
29
|
+
|
|
30
|
+
// Prime cache
|
|
31
|
+
await manager.find('docs', 'test')
|
|
32
|
+
// @ts-expect-error
|
|
33
|
+
expect(manager.cache.size).toBe(1)
|
|
34
|
+
|
|
35
|
+
const watcher = new ContentWatcher(manager, TMP_DIR, { debounceMs: 100 })
|
|
36
|
+
watcher.watch('docs')
|
|
37
|
+
|
|
38
|
+
// Simulate change
|
|
39
|
+
await writeFile(join(TMP_DIR, 'docs', 'en', 'test.md'), '# Changed')
|
|
40
|
+
|
|
41
|
+
// Wait for cache invalidation (with retry loop for reliability)
|
|
42
|
+
let cleared = false
|
|
43
|
+
for (let i = 0; i < 40; i++) {
|
|
44
|
+
// @ts-expect-error
|
|
45
|
+
if (manager.cache.size === 0) {
|
|
46
|
+
cleared = true
|
|
47
|
+
break
|
|
48
|
+
}
|
|
49
|
+
await new Promise((r) => setTimeout(r, 100))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
expect(cleared).toBe(true)
|
|
53
|
+
|
|
54
|
+
watcher.close()
|
|
55
|
+
})
|
|
56
|
+
})
|
package/tests/content.test.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { ContentManager } from '../src/ContentManager'
|
|
4
|
+
import { LocalDriver } from '../src/driver/LocalDriver'
|
|
4
5
|
|
|
5
6
|
describe('Orbit Content Manager', () => {
|
|
6
7
|
const rootDir = join(import.meta.dir, 'fixtures')
|
|
7
|
-
const manager = new ContentManager(rootDir)
|
|
8
|
+
const manager = new ContentManager(new LocalDriver(rootDir))
|
|
8
9
|
|
|
9
10
|
manager.defineCollection('docs', {
|
|
10
11
|
path: 'docs',
|
package/tests/extra.test.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { tmpdir } from 'node:os'
|
|
|
5
5
|
import { join } from 'node:path'
|
|
6
6
|
import { ContentManager } from '../src/ContentManager'
|
|
7
7
|
import { Controller } from '../src/Controller'
|
|
8
|
+
import { LocalDriver } from '../src/driver/LocalDriver'
|
|
8
9
|
import { OrbitMonolith } from '../src/index'
|
|
9
10
|
import { Sanitizer } from '../src/Sanitizer'
|
|
10
11
|
|
|
@@ -82,7 +83,7 @@ describe('ContentManager security helpers', () => {
|
|
|
82
83
|
const markdown = `---\ntitle: Safe\n---\n\n<link>\n\n[bad](javascript:alert(1))\n\n[good](https://example.com "Title")`
|
|
83
84
|
writeFileSync(join(base, 'safe.md'), markdown)
|
|
84
85
|
|
|
85
|
-
const manager = new ContentManager(root)
|
|
86
|
+
const manager = new ContentManager(new LocalDriver(root))
|
|
86
87
|
manager.defineCollection('docs', { path: 'content/docs' })
|
|
87
88
|
|
|
88
89
|
expect(await manager.find('docs', '../hack', 'en')).toBeNull()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test'
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { PlanetCore } from '@gravito/core'
|
|
6
|
+
import { OrbitMonolith } from '../src/index'
|
|
7
|
+
|
|
8
|
+
const TMP_DIR = join(import.meta.dir, 'tmp_hot_reload')
|
|
9
|
+
|
|
10
|
+
describe('Orbit Monolith Hot Reload', () => {
|
|
11
|
+
const originalEnv = process.env.NODE_ENV
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
process.env.NODE_ENV = 'development'
|
|
15
|
+
if (existsSync(TMP_DIR)) {
|
|
16
|
+
await rm(TMP_DIR, { recursive: true, force: true })
|
|
17
|
+
}
|
|
18
|
+
await mkdir(join(TMP_DIR, 'docs', 'en'), { recursive: true })
|
|
19
|
+
await writeFile(join(TMP_DIR, 'docs', 'en', 'test.md'), '# Original')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
afterEach(async () => {
|
|
23
|
+
process.env.NODE_ENV = originalEnv
|
|
24
|
+
if (existsSync(TMP_DIR)) {
|
|
25
|
+
await rm(TMP_DIR, { recursive: true, force: true })
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should enable watcher in development mode', async () => {
|
|
30
|
+
const core = new PlanetCore()
|
|
31
|
+
const orbit = new OrbitMonolith({
|
|
32
|
+
root: TMP_DIR,
|
|
33
|
+
collections: { docs: { path: 'docs' } },
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// We need to capture the ContentManager instance to verify cache
|
|
37
|
+
let capturedManager: any
|
|
38
|
+
let registeredHandler: any
|
|
39
|
+
|
|
40
|
+
// @ts-expect-error
|
|
41
|
+
core.adapter.use = mock((_path, handler) => {
|
|
42
|
+
registeredHandler = handler
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
await orbit.install(core)
|
|
46
|
+
|
|
47
|
+
// Execute captured handler
|
|
48
|
+
const ctx: any = {
|
|
49
|
+
set: (k: string, v: any) => {
|
|
50
|
+
if (k === 'content') {
|
|
51
|
+
capturedManager = v
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
await registeredHandler(ctx, async () => {})
|
|
56
|
+
|
|
57
|
+
expect(capturedManager).toBeDefined()
|
|
58
|
+
|
|
59
|
+
// Prime cache
|
|
60
|
+
await capturedManager.find('docs', 'test')
|
|
61
|
+
// @ts-expect-error
|
|
62
|
+
expect(capturedManager.cache.size).toBe(1)
|
|
63
|
+
|
|
64
|
+
// Modify file
|
|
65
|
+
await writeFile(join(TMP_DIR, 'docs', 'en', 'test.md'), '# Changed')
|
|
66
|
+
|
|
67
|
+
// Wait for watcher
|
|
68
|
+
await new Promise((r) => setTimeout(r, 500))
|
|
69
|
+
|
|
70
|
+
// Cache should be cleared
|
|
71
|
+
// @ts-expect-error
|
|
72
|
+
expect(capturedManager.cache.size).toBe(0)
|
|
73
|
+
})
|
|
74
|
+
})
|
package/tsconfig.json
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
],
|
|
16
|
-
"exclude": [
|
|
17
|
-
"node_modules",
|
|
18
|
-
"dist",
|
|
19
|
-
"**/*.test.ts"
|
|
20
|
-
]
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"baseUrl": ".",
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"types": ["bun-types"],
|
|
8
|
+
"paths": {
|
|
9
|
+
"@gravito/core": ["../../packages/core/src/index.ts"],
|
|
10
|
+
"@gravito/*": ["../../packages/*/src/index.ts"]
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
21
15
|
}
|