@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 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 rootDir - The root directory of the application.
53
+ * @param driver - The content driver to use.
53
54
  */
54
- constructor(rootDir) {
55
- this.rootDir = rootDir;
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
- if (this.cache.has(cacheKey)) {
103
- return this.cache.get(cacheKey);
130
+ const cachedItem = this.cache.get(cacheKey);
131
+ if (cachedItem) {
132
+ return cachedItem;
104
133
  }
105
- const filePath = (0, import_node_path.join)(this.rootDir, config.path, safeLocale, `${safeSlug}.md`);
134
+ const filePath = (0, import_node_path.join)(config.path, safeLocale, `${safeSlug}.md`);
106
135
  try {
107
- const exists = await (0, import_promises.stat)(filePath).then(() => true).catch(() => false);
136
+ const exists = await this.driver.exists(filePath);
108
137
  if (!exists) {
109
138
  return null;
110
139
  }
111
- const fileContent = await (0, import_promises.readFile)(filePath, "utf-8");
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)(this.rootDir, config.path, safeLocale);
176
+ const dirPath = (0, import_node_path.join)(config.path, safeLocale);
147
177
  try {
148
- const files = await (0, import_promises.readdir)(dirPath);
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
- const manager = new ContentManager(root);
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 rootDir;
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 rootDir - The root directory of the application.
57
+ * @param driver - The content driver to use.
38
58
  */
39
- constructor(rootDir: string);
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 rootDir;
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 rootDir - The root directory of the application.
57
+ * @param driver - The content driver to use.
38
58
  */
39
- constructor(rootDir: string);
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 };