@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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @gravito/monolith
|
|
2
2
|
|
|
3
|
+
## 3.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Convert all workspace:\* dependencies to version numbers for npm publishing
|
|
8
|
+
|
|
9
|
+
- Fixed 144 workspace:\* dependencies across 58 packages
|
|
10
|
+
- Ensures all packages work properly when installed from npm
|
|
11
|
+
- Resolves issues with bunx and npm installation of CLI tools
|
|
12
|
+
- All internal dependencies now use explicit version constraints
|
|
13
|
+
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
- @gravito/core@1.6.1
|
|
16
|
+
- @gravito/mass@3.0.2
|
|
17
|
+
|
|
18
|
+
## 3.1.0
|
|
19
|
+
|
|
20
|
+
### Minor Changes
|
|
21
|
+
|
|
22
|
+
- feat: implement ContentWatcher for hot reload during development
|
|
23
|
+
- feat: implement in-memory full-text search index in ContentManager
|
|
24
|
+
|
|
3
25
|
## 3.0.1
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,8 @@ __export(index_exports, {
|
|
|
34
34
|
ContentManager: () => ContentManager,
|
|
35
35
|
Controller: () => Controller,
|
|
36
36
|
FormRequest: () => FormRequest,
|
|
37
|
+
GitHubDriver: () => GitHubDriver,
|
|
38
|
+
LocalDriver: () => LocalDriver,
|
|
37
39
|
OrbitMonolith: () => OrbitMonolith,
|
|
38
40
|
Route: () => RouterHelper,
|
|
39
41
|
Schema: () => import_mass2.Schema
|
|
@@ -41,7 +43,6 @@ __export(index_exports, {
|
|
|
41
43
|
module.exports = __toCommonJS(index_exports);
|
|
42
44
|
|
|
43
45
|
// src/ContentManager.ts
|
|
44
|
-
var import_promises = require("fs/promises");
|
|
45
46
|
var import_node_path = require("path");
|
|
46
47
|
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
47
48
|
var import_marked = require("marked");
|
|
@@ -49,14 +50,16 @@ var ContentManager = class {
|
|
|
49
50
|
/**
|
|
50
51
|
* Create a new ContentManager instance.
|
|
51
52
|
*
|
|
52
|
-
* @param
|
|
53
|
+
* @param driver - The content driver to use.
|
|
53
54
|
*/
|
|
54
|
-
constructor(
|
|
55
|
-
this.
|
|
55
|
+
constructor(driver) {
|
|
56
|
+
this.driver = driver;
|
|
56
57
|
}
|
|
57
58
|
collections = /* @__PURE__ */ new Map();
|
|
58
59
|
// Simple memory cache: collection:locale:slug -> ContentItem
|
|
59
60
|
cache = /* @__PURE__ */ new Map();
|
|
61
|
+
// In-memory search index: term -> Set<cacheKey>
|
|
62
|
+
searchIndex = /* @__PURE__ */ new Map();
|
|
60
63
|
renderer = (() => {
|
|
61
64
|
const renderer = new import_marked.marked.Renderer();
|
|
62
65
|
renderer.html = (html) => this.escapeHtml(html);
|
|
@@ -70,6 +73,31 @@ var ContentManager = class {
|
|
|
70
73
|
};
|
|
71
74
|
return renderer;
|
|
72
75
|
})();
|
|
76
|
+
/**
|
|
77
|
+
* Clear all cached content.
|
|
78
|
+
* Useful for hot reload during development.
|
|
79
|
+
*/
|
|
80
|
+
clearCache() {
|
|
81
|
+
this.cache.clear();
|
|
82
|
+
this.searchIndex.clear();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Invalidate a specific content item.
|
|
86
|
+
* @param collection - The collection name.
|
|
87
|
+
* @param slug - The file slug.
|
|
88
|
+
* @param locale - The locale. Defaults to 'en'.
|
|
89
|
+
*/
|
|
90
|
+
invalidate(collection, slug, locale = "en") {
|
|
91
|
+
const safeSlug = this.sanitizeSegment(slug);
|
|
92
|
+
const safeLocale = this.sanitizeSegment(locale);
|
|
93
|
+
if (safeSlug && safeLocale) {
|
|
94
|
+
const cacheKey = `${collection}:${safeLocale}:${safeSlug}`;
|
|
95
|
+
this.cache.delete(cacheKey);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
getCollectionConfig(name) {
|
|
99
|
+
return this.collections.get(name);
|
|
100
|
+
}
|
|
73
101
|
/**
|
|
74
102
|
* Register a new content collection.
|
|
75
103
|
*
|
|
@@ -99,16 +127,17 @@ var ContentManager = class {
|
|
|
99
127
|
return null;
|
|
100
128
|
}
|
|
101
129
|
const cacheKey = `${collectionName}:${locale}:${slug}`;
|
|
102
|
-
|
|
103
|
-
|
|
130
|
+
const cachedItem = this.cache.get(cacheKey);
|
|
131
|
+
if (cachedItem) {
|
|
132
|
+
return cachedItem;
|
|
104
133
|
}
|
|
105
|
-
const filePath = (0, import_node_path.join)(
|
|
134
|
+
const filePath = (0, import_node_path.join)(config.path, safeLocale, `${safeSlug}.md`);
|
|
106
135
|
try {
|
|
107
|
-
const exists = await
|
|
136
|
+
const exists = await this.driver.exists(filePath);
|
|
108
137
|
if (!exists) {
|
|
109
138
|
return null;
|
|
110
139
|
}
|
|
111
|
-
const fileContent = await
|
|
140
|
+
const fileContent = await this.driver.read(filePath);
|
|
112
141
|
const { data, content, excerpt } = (0, import_gray_matter.default)(fileContent);
|
|
113
142
|
const html = await import_marked.marked.parse(content, { renderer: this.renderer });
|
|
114
143
|
const item = {
|
|
@@ -119,6 +148,7 @@ var ContentManager = class {
|
|
|
119
148
|
excerpt
|
|
120
149
|
};
|
|
121
150
|
this.cache.set(cacheKey, item);
|
|
151
|
+
this.buildSearchIndex(cacheKey, item);
|
|
122
152
|
return item;
|
|
123
153
|
} catch (e) {
|
|
124
154
|
console.error(`[Orbit-Content] Error reading file: ${filePath}`, e);
|
|
@@ -143,9 +173,9 @@ var ContentManager = class {
|
|
|
143
173
|
if (!safeLocale) {
|
|
144
174
|
return [];
|
|
145
175
|
}
|
|
146
|
-
const dirPath = (0, import_node_path.join)(
|
|
176
|
+
const dirPath = (0, import_node_path.join)(config.path, safeLocale);
|
|
147
177
|
try {
|
|
148
|
-
const files = await
|
|
178
|
+
const files = await this.driver.list(dirPath);
|
|
149
179
|
const items = [];
|
|
150
180
|
for (const file of files) {
|
|
151
181
|
if (!file.endsWith(".md")) {
|
|
@@ -162,6 +192,54 @@ var ContentManager = class {
|
|
|
162
192
|
return [];
|
|
163
193
|
}
|
|
164
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Search for content items across collections and locales.
|
|
197
|
+
*
|
|
198
|
+
* @param query - The search query.
|
|
199
|
+
* @param options - Optional filters for collection and locale.
|
|
200
|
+
* @returns An array of matching ContentItems.
|
|
201
|
+
*/
|
|
202
|
+
search(query, options = {}) {
|
|
203
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
204
|
+
if (terms.length === 0) {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
const matches = /* @__PURE__ */ new Set();
|
|
208
|
+
for (const term of terms) {
|
|
209
|
+
const keys = this.searchIndex.get(term);
|
|
210
|
+
if (keys) {
|
|
211
|
+
for (const key of keys) {
|
|
212
|
+
matches.add(key);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const results = [];
|
|
217
|
+
for (const key of matches) {
|
|
218
|
+
const item = this.cache.get(key);
|
|
219
|
+
if (!item) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const [collection, locale] = key.split(":");
|
|
223
|
+
if (options.collection && options.collection !== collection) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (options.locale && options.locale !== locale) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
results.push(item);
|
|
230
|
+
}
|
|
231
|
+
return results;
|
|
232
|
+
}
|
|
233
|
+
buildSearchIndex(cacheKey, item) {
|
|
234
|
+
const text = `${item.slug} ${item.meta.title || ""} ${item.raw} ${item.excerpt || ""}`.toLowerCase();
|
|
235
|
+
const terms = text.split(/[^\w\d]+/).filter((t) => t.length > 2);
|
|
236
|
+
for (const term of terms) {
|
|
237
|
+
if (!this.searchIndex.has(term)) {
|
|
238
|
+
this.searchIndex.set(term, /* @__PURE__ */ new Set());
|
|
239
|
+
}
|
|
240
|
+
this.searchIndex.get(term)?.add(cacheKey);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
165
243
|
sanitizeSegment(value) {
|
|
166
244
|
if (!value) {
|
|
167
245
|
return null;
|
|
@@ -198,6 +276,128 @@ var ContentManager = class {
|
|
|
198
276
|
}
|
|
199
277
|
};
|
|
200
278
|
|
|
279
|
+
// src/ContentWatcher.ts
|
|
280
|
+
var import_node_fs = require("fs");
|
|
281
|
+
var import_node_path2 = require("path");
|
|
282
|
+
var ContentWatcher = class {
|
|
283
|
+
constructor(contentManager, rootDir, options = {}) {
|
|
284
|
+
this.contentManager = contentManager;
|
|
285
|
+
this.rootDir = rootDir;
|
|
286
|
+
this.options = options;
|
|
287
|
+
}
|
|
288
|
+
watchers = [];
|
|
289
|
+
debounceTimers = /* @__PURE__ */ new Map();
|
|
290
|
+
watch(collectionName) {
|
|
291
|
+
const config = this.contentManager.getCollectionConfig(collectionName);
|
|
292
|
+
if (!config) {
|
|
293
|
+
console.warn(`[ContentWatcher] Collection '${collectionName}' not found`);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const watchPath = (0, import_node_path2.join)(this.rootDir, config.path);
|
|
297
|
+
this.addWatcher(watchPath, collectionName);
|
|
298
|
+
try {
|
|
299
|
+
const entries = (0, import_node_fs.readdirSync)(watchPath, { withFileTypes: true });
|
|
300
|
+
for (const entry of entries) {
|
|
301
|
+
if (entry.isDirectory()) {
|
|
302
|
+
this.addWatcher((0, import_node_path2.join)(watchPath, entry.name), collectionName, entry.name);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (_e) {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
addWatcher(watchPath, collection, localePrefix) {
|
|
309
|
+
try {
|
|
310
|
+
const watcher = (0, import_node_fs.watch)(watchPath, { recursive: true }, (_eventType, filename) => {
|
|
311
|
+
if (!filename) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const key = `${collection}:${localePrefix || ""}:${filename}`;
|
|
315
|
+
if (this.debounceTimers.has(key)) {
|
|
316
|
+
clearTimeout(this.debounceTimers.get(key));
|
|
317
|
+
}
|
|
318
|
+
this.debounceTimers.set(
|
|
319
|
+
key,
|
|
320
|
+
setTimeout(() => {
|
|
321
|
+
this.handleFileChange(collection, filename.toString(), localePrefix);
|
|
322
|
+
this.debounceTimers.delete(key);
|
|
323
|
+
}, this.options.debounceMs ?? 100)
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
this.watchers.push(watcher);
|
|
327
|
+
} catch (_e) {
|
|
328
|
+
try {
|
|
329
|
+
const watcher = (0, import_node_fs.watch)(watchPath, { recursive: false }, (_eventType, filename) => {
|
|
330
|
+
if (!filename) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
this.handleFileChange(collection, filename.toString(), localePrefix);
|
|
334
|
+
});
|
|
335
|
+
this.watchers.push(watcher);
|
|
336
|
+
} catch (err) {
|
|
337
|
+
console.error(`[ContentWatcher] Failed to watch ${watchPath}:`, err);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
handleFileChange(collection, filename, localePrefix) {
|
|
342
|
+
const parts = filename.split(/[/\\]/);
|
|
343
|
+
let locale;
|
|
344
|
+
let file;
|
|
345
|
+
if (parts.length >= 2) {
|
|
346
|
+
locale = parts[parts.length - 2];
|
|
347
|
+
file = parts[parts.length - 1];
|
|
348
|
+
} else if (localePrefix) {
|
|
349
|
+
locale = localePrefix;
|
|
350
|
+
file = parts[0];
|
|
351
|
+
} else {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (!file.endsWith(".md")) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const slug = file.replace(/\.md$/, "");
|
|
358
|
+
this.contentManager.invalidate(collection, slug, locale);
|
|
359
|
+
}
|
|
360
|
+
close() {
|
|
361
|
+
for (const watcher of this.watchers) {
|
|
362
|
+
watcher.close();
|
|
363
|
+
}
|
|
364
|
+
this.watchers = [];
|
|
365
|
+
for (const timer of this.debounceTimers.values()) {
|
|
366
|
+
clearTimeout(timer);
|
|
367
|
+
}
|
|
368
|
+
this.debounceTimers.clear();
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// src/driver/LocalDriver.ts
|
|
373
|
+
var import_promises = require("fs/promises");
|
|
374
|
+
var import_node_path3 = require("path");
|
|
375
|
+
var LocalDriver = class {
|
|
376
|
+
constructor(rootDir) {
|
|
377
|
+
this.rootDir = rootDir;
|
|
378
|
+
}
|
|
379
|
+
async read(path) {
|
|
380
|
+
const fullPath = (0, import_node_path3.join)(this.rootDir, path);
|
|
381
|
+
return (0, import_promises.readFile)(fullPath, "utf-8");
|
|
382
|
+
}
|
|
383
|
+
async exists(path) {
|
|
384
|
+
try {
|
|
385
|
+
await (0, import_promises.stat)((0, import_node_path3.join)(this.rootDir, path));
|
|
386
|
+
return true;
|
|
387
|
+
} catch {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
async list(dir) {
|
|
392
|
+
try {
|
|
393
|
+
const fullPath = (0, import_node_path3.join)(this.rootDir, dir);
|
|
394
|
+
return await (0, import_promises.readdir)(fullPath);
|
|
395
|
+
} catch {
|
|
396
|
+
return [];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
201
401
|
// src/index.ts
|
|
202
402
|
var import_mass2 = require("@gravito/mass");
|
|
203
403
|
|
|
@@ -297,6 +497,69 @@ var Controller = class {
|
|
|
297
497
|
}
|
|
298
498
|
};
|
|
299
499
|
|
|
500
|
+
// src/driver/GitHubDriver.ts
|
|
501
|
+
var import_rest = require("@octokit/rest");
|
|
502
|
+
var GitHubDriver = class {
|
|
503
|
+
octokit;
|
|
504
|
+
owner;
|
|
505
|
+
repo;
|
|
506
|
+
ref;
|
|
507
|
+
constructor(options) {
|
|
508
|
+
this.octokit = new import_rest.Octokit({ auth: options.auth });
|
|
509
|
+
this.owner = options.owner;
|
|
510
|
+
this.repo = options.repo;
|
|
511
|
+
this.ref = options.ref;
|
|
512
|
+
}
|
|
513
|
+
async read(path) {
|
|
514
|
+
try {
|
|
515
|
+
const { data } = await this.octokit.rest.repos.getContent({
|
|
516
|
+
owner: this.owner,
|
|
517
|
+
repo: this.repo,
|
|
518
|
+
path,
|
|
519
|
+
ref: this.ref
|
|
520
|
+
});
|
|
521
|
+
if (Array.isArray(data) || !("content" in data)) {
|
|
522
|
+
throw new Error(`Path is not a file: ${path}`);
|
|
523
|
+
}
|
|
524
|
+
return Buffer.from(data.content, "base64").toString("utf-8");
|
|
525
|
+
} catch (e) {
|
|
526
|
+
if (e.status === 404) {
|
|
527
|
+
throw new Error(`File not found: ${path}`);
|
|
528
|
+
}
|
|
529
|
+
throw e;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
async exists(path) {
|
|
533
|
+
try {
|
|
534
|
+
await this.octokit.rest.repos.getContent({
|
|
535
|
+
owner: this.owner,
|
|
536
|
+
repo: this.repo,
|
|
537
|
+
path,
|
|
538
|
+
ref: this.ref
|
|
539
|
+
});
|
|
540
|
+
return true;
|
|
541
|
+
} catch {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
async list(dir) {
|
|
546
|
+
try {
|
|
547
|
+
const { data } = await this.octokit.rest.repos.getContent({
|
|
548
|
+
owner: this.owner,
|
|
549
|
+
repo: this.repo,
|
|
550
|
+
path: dir,
|
|
551
|
+
ref: this.ref
|
|
552
|
+
});
|
|
553
|
+
if (!Array.isArray(data)) {
|
|
554
|
+
return [];
|
|
555
|
+
}
|
|
556
|
+
return data.map((item) => item.name);
|
|
557
|
+
} catch {
|
|
558
|
+
return [];
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
|
|
300
563
|
// src/FormRequest.ts
|
|
301
564
|
var import_mass = require("@gravito/mass");
|
|
302
565
|
var FormRequest = class {
|
|
@@ -404,7 +667,16 @@ var OrbitMonolith = class {
|
|
|
404
667
|
}
|
|
405
668
|
install(core) {
|
|
406
669
|
const root = this.config.root || process.cwd();
|
|
407
|
-
|
|
670
|
+
let driver;
|
|
671
|
+
let isLocal = false;
|
|
672
|
+
if (this.config.driver) {
|
|
673
|
+
driver = this.config.driver;
|
|
674
|
+
isLocal = driver instanceof LocalDriver;
|
|
675
|
+
} else {
|
|
676
|
+
driver = new LocalDriver(root);
|
|
677
|
+
isLocal = true;
|
|
678
|
+
}
|
|
679
|
+
const manager = new ContentManager(driver);
|
|
408
680
|
if (this.config.collections) {
|
|
409
681
|
for (const [name, config] of Object.entries(this.config.collections)) {
|
|
410
682
|
manager.defineCollection(name, config);
|
|
@@ -414,6 +686,15 @@ var OrbitMonolith = class {
|
|
|
414
686
|
c.set("content", manager);
|
|
415
687
|
return await next();
|
|
416
688
|
});
|
|
689
|
+
if (process.env.NODE_ENV === "development" && isLocal) {
|
|
690
|
+
const watcher = new ContentWatcher(manager, root);
|
|
691
|
+
if (this.config.collections) {
|
|
692
|
+
for (const name of Object.keys(this.config.collections)) {
|
|
693
|
+
watcher.watch(name);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
core.logger.info("Orbit Monolith Hot Reload Active \u{1F525}");
|
|
697
|
+
}
|
|
417
698
|
core.logger.info("Orbit Monolith installed \u2B1B\uFE0F");
|
|
418
699
|
}
|
|
419
700
|
};
|
|
@@ -423,6 +704,8 @@ var OrbitMonolith = class {
|
|
|
423
704
|
ContentManager,
|
|
424
705
|
Controller,
|
|
425
706
|
FormRequest,
|
|
707
|
+
GitHubDriver,
|
|
708
|
+
LocalDriver,
|
|
426
709
|
OrbitMonolith,
|
|
427
710
|
Route,
|
|
428
711
|
Schema
|
package/dist/index.d.cts
CHANGED
|
@@ -4,6 +4,12 @@ import { TSchema } from '@gravito/mass';
|
|
|
4
4
|
export { Schema } from '@gravito/mass';
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
6
|
|
|
7
|
+
interface ContentDriver {
|
|
8
|
+
read(path: string): Promise<string>;
|
|
9
|
+
exists(path: string): Promise<boolean>;
|
|
10
|
+
list(dir: string): Promise<string[]>;
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* Represents a single content item (file).
|
|
9
15
|
* @public
|
|
@@ -27,16 +33,30 @@ interface CollectionConfig {
|
|
|
27
33
|
* @public
|
|
28
34
|
*/
|
|
29
35
|
declare class ContentManager {
|
|
30
|
-
private
|
|
36
|
+
private readonly driver;
|
|
31
37
|
private collections;
|
|
32
38
|
private cache;
|
|
39
|
+
private searchIndex;
|
|
33
40
|
private renderer;
|
|
41
|
+
/**
|
|
42
|
+
* Clear all cached content.
|
|
43
|
+
* Useful for hot reload during development.
|
|
44
|
+
*/
|
|
45
|
+
clearCache(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Invalidate a specific content item.
|
|
48
|
+
* @param collection - The collection name.
|
|
49
|
+
* @param slug - The file slug.
|
|
50
|
+
* @param locale - The locale. Defaults to 'en'.
|
|
51
|
+
*/
|
|
52
|
+
invalidate(collection: string, slug: string, locale?: string): void;
|
|
53
|
+
getCollectionConfig(name: string): CollectionConfig | undefined;
|
|
34
54
|
/**
|
|
35
55
|
* Create a new ContentManager instance.
|
|
36
56
|
*
|
|
37
|
-
* @param
|
|
57
|
+
* @param driver - The content driver to use.
|
|
38
58
|
*/
|
|
39
|
-
constructor(
|
|
59
|
+
constructor(driver: ContentDriver);
|
|
40
60
|
/**
|
|
41
61
|
* Register a new content collection.
|
|
42
62
|
*
|
|
@@ -64,6 +84,18 @@ declare class ContentManager {
|
|
|
64
84
|
* @throws {Error} If the collection is not defined.
|
|
65
85
|
*/
|
|
66
86
|
list(collectionName: string, locale?: string): Promise<ContentItem[]>;
|
|
87
|
+
/**
|
|
88
|
+
* Search for content items across collections and locales.
|
|
89
|
+
*
|
|
90
|
+
* @param query - The search query.
|
|
91
|
+
* @param options - Optional filters for collection and locale.
|
|
92
|
+
* @returns An array of matching ContentItems.
|
|
93
|
+
*/
|
|
94
|
+
search(query: string, options?: {
|
|
95
|
+
collection?: string;
|
|
96
|
+
locale?: string;
|
|
97
|
+
}): ContentItem[];
|
|
98
|
+
private buildSearchIndex;
|
|
67
99
|
private sanitizeSegment;
|
|
68
100
|
private escapeHtml;
|
|
69
101
|
private isSafeUrl;
|
|
@@ -136,6 +168,31 @@ declare abstract class Controller {
|
|
|
136
168
|
static call(method: string): any;
|
|
137
169
|
}
|
|
138
170
|
|
|
171
|
+
interface GitHubDriverOptions {
|
|
172
|
+
owner: string;
|
|
173
|
+
repo: string;
|
|
174
|
+
ref?: string;
|
|
175
|
+
auth?: string;
|
|
176
|
+
}
|
|
177
|
+
declare class GitHubDriver implements ContentDriver {
|
|
178
|
+
private octokit;
|
|
179
|
+
private owner;
|
|
180
|
+
private repo;
|
|
181
|
+
private ref?;
|
|
182
|
+
constructor(options: GitHubDriverOptions);
|
|
183
|
+
read(path: string): Promise<string>;
|
|
184
|
+
exists(path: string): Promise<boolean>;
|
|
185
|
+
list(dir: string): Promise<string[]>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
declare class LocalDriver implements ContentDriver {
|
|
189
|
+
private rootDir;
|
|
190
|
+
constructor(rootDir: string);
|
|
191
|
+
read(path: string): Promise<string>;
|
|
192
|
+
exists(path: string): Promise<boolean>;
|
|
193
|
+
list(dir: string): Promise<string[]>;
|
|
194
|
+
}
|
|
195
|
+
|
|
139
196
|
/**
|
|
140
197
|
* Base class for Monolith Form Requests.
|
|
141
198
|
*
|
|
@@ -203,6 +260,7 @@ declare module '@gravito/core' {
|
|
|
203
260
|
*/
|
|
204
261
|
interface ContentConfig {
|
|
205
262
|
root?: string;
|
|
263
|
+
driver?: ContentDriver;
|
|
206
264
|
collections?: Record<string, CollectionConfig>;
|
|
207
265
|
}
|
|
208
266
|
/**
|
|
@@ -216,4 +274,4 @@ declare class OrbitMonolith implements GravitoOrbit {
|
|
|
216
274
|
install(core: PlanetCore): void;
|
|
217
275
|
}
|
|
218
276
|
|
|
219
|
-
export { BaseController, type CollectionConfig, type ContentConfig, type ContentItem, ContentManager, Controller, FormRequest, OrbitMonolith, RouterHelper as Route };
|
|
277
|
+
export { BaseController, type CollectionConfig, type ContentConfig, type ContentDriver, type ContentItem, ContentManager, Controller, FormRequest, GitHubDriver, type GitHubDriverOptions, LocalDriver, OrbitMonolith, RouterHelper as Route };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,12 @@ import { TSchema } from '@gravito/mass';
|
|
|
4
4
|
export { Schema } from '@gravito/mass';
|
|
5
5
|
import { Hono } from 'hono';
|
|
6
6
|
|
|
7
|
+
interface ContentDriver {
|
|
8
|
+
read(path: string): Promise<string>;
|
|
9
|
+
exists(path: string): Promise<boolean>;
|
|
10
|
+
list(dir: string): Promise<string[]>;
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* Represents a single content item (file).
|
|
9
15
|
* @public
|
|
@@ -27,16 +33,30 @@ interface CollectionConfig {
|
|
|
27
33
|
* @public
|
|
28
34
|
*/
|
|
29
35
|
declare class ContentManager {
|
|
30
|
-
private
|
|
36
|
+
private readonly driver;
|
|
31
37
|
private collections;
|
|
32
38
|
private cache;
|
|
39
|
+
private searchIndex;
|
|
33
40
|
private renderer;
|
|
41
|
+
/**
|
|
42
|
+
* Clear all cached content.
|
|
43
|
+
* Useful for hot reload during development.
|
|
44
|
+
*/
|
|
45
|
+
clearCache(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Invalidate a specific content item.
|
|
48
|
+
* @param collection - The collection name.
|
|
49
|
+
* @param slug - The file slug.
|
|
50
|
+
* @param locale - The locale. Defaults to 'en'.
|
|
51
|
+
*/
|
|
52
|
+
invalidate(collection: string, slug: string, locale?: string): void;
|
|
53
|
+
getCollectionConfig(name: string): CollectionConfig | undefined;
|
|
34
54
|
/**
|
|
35
55
|
* Create a new ContentManager instance.
|
|
36
56
|
*
|
|
37
|
-
* @param
|
|
57
|
+
* @param driver - The content driver to use.
|
|
38
58
|
*/
|
|
39
|
-
constructor(
|
|
59
|
+
constructor(driver: ContentDriver);
|
|
40
60
|
/**
|
|
41
61
|
* Register a new content collection.
|
|
42
62
|
*
|
|
@@ -64,6 +84,18 @@ declare class ContentManager {
|
|
|
64
84
|
* @throws {Error} If the collection is not defined.
|
|
65
85
|
*/
|
|
66
86
|
list(collectionName: string, locale?: string): Promise<ContentItem[]>;
|
|
87
|
+
/**
|
|
88
|
+
* Search for content items across collections and locales.
|
|
89
|
+
*
|
|
90
|
+
* @param query - The search query.
|
|
91
|
+
* @param options - Optional filters for collection and locale.
|
|
92
|
+
* @returns An array of matching ContentItems.
|
|
93
|
+
*/
|
|
94
|
+
search(query: string, options?: {
|
|
95
|
+
collection?: string;
|
|
96
|
+
locale?: string;
|
|
97
|
+
}): ContentItem[];
|
|
98
|
+
private buildSearchIndex;
|
|
67
99
|
private sanitizeSegment;
|
|
68
100
|
private escapeHtml;
|
|
69
101
|
private isSafeUrl;
|
|
@@ -136,6 +168,31 @@ declare abstract class Controller {
|
|
|
136
168
|
static call(method: string): any;
|
|
137
169
|
}
|
|
138
170
|
|
|
171
|
+
interface GitHubDriverOptions {
|
|
172
|
+
owner: string;
|
|
173
|
+
repo: string;
|
|
174
|
+
ref?: string;
|
|
175
|
+
auth?: string;
|
|
176
|
+
}
|
|
177
|
+
declare class GitHubDriver implements ContentDriver {
|
|
178
|
+
private octokit;
|
|
179
|
+
private owner;
|
|
180
|
+
private repo;
|
|
181
|
+
private ref?;
|
|
182
|
+
constructor(options: GitHubDriverOptions);
|
|
183
|
+
read(path: string): Promise<string>;
|
|
184
|
+
exists(path: string): Promise<boolean>;
|
|
185
|
+
list(dir: string): Promise<string[]>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
declare class LocalDriver implements ContentDriver {
|
|
189
|
+
private rootDir;
|
|
190
|
+
constructor(rootDir: string);
|
|
191
|
+
read(path: string): Promise<string>;
|
|
192
|
+
exists(path: string): Promise<boolean>;
|
|
193
|
+
list(dir: string): Promise<string[]>;
|
|
194
|
+
}
|
|
195
|
+
|
|
139
196
|
/**
|
|
140
197
|
* Base class for Monolith Form Requests.
|
|
141
198
|
*
|
|
@@ -203,6 +260,7 @@ declare module '@gravito/core' {
|
|
|
203
260
|
*/
|
|
204
261
|
interface ContentConfig {
|
|
205
262
|
root?: string;
|
|
263
|
+
driver?: ContentDriver;
|
|
206
264
|
collections?: Record<string, CollectionConfig>;
|
|
207
265
|
}
|
|
208
266
|
/**
|
|
@@ -216,4 +274,4 @@ declare class OrbitMonolith implements GravitoOrbit {
|
|
|
216
274
|
install(core: PlanetCore): void;
|
|
217
275
|
}
|
|
218
276
|
|
|
219
|
-
export { BaseController, type CollectionConfig, type ContentConfig, type ContentItem, ContentManager, Controller, FormRequest, OrbitMonolith, RouterHelper as Route };
|
|
277
|
+
export { BaseController, type CollectionConfig, type ContentConfig, type ContentDriver, type ContentItem, ContentManager, Controller, FormRequest, GitHubDriver, type GitHubDriverOptions, LocalDriver, OrbitMonolith, RouterHelper as Route };
|