@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.
@@ -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
+ })
@@ -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',
@@ -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
- "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": [
14
- "src/**/*"
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
  }