contentbase 0.1.5 → 0.1.6

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/docs/models.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
 
10
10
  export const Idea = defineModel("Idea", {
11
11
  prefix: "ideas",
12
+ description: 'Ideas are used to capture thoughts for new features, apis, etc',
12
13
  meta: z.object({
13
14
  goal: z.string().optional().describe("Slug of the goal this idea is aligned to"),
14
15
  tags: z.array(z.string()).default([]).describe("Arbitrary tags for categorizing the idea"),
@@ -18,8 +19,10 @@ export const Idea = defineModel("Idea", {
18
19
 
19
20
  export const Tutorial = defineModel("Tutorial", {
20
21
  prefix: "tutorials",
22
+ pattern: ['tutorials/:slug', 'tutorials/:category/:slug'],
21
23
  meta: z.object({
22
24
  tags: z.array(z.string()).default([]).describe("Arbitrary tags for categorizing the tutorial"),
25
+ category: z.string().default('contentbase')
23
26
  }),
24
27
  })
25
28
 
@@ -35,4 +38,4 @@ export const Example = defineModel("Example", {
35
38
  meta: z.object({
36
39
  tags: z.array(z.string()).default([]).describe("Arbitrary tags for categorizing the example"),
37
40
  }),
38
- })
41
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contentbase",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "repository": "https://github.com/soederpop/contentbase",
5
5
  "website": "https://contentbase.soederpop.com",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "release": "./scripts/release.sh"
21
21
  },
22
22
  "dependencies": {
23
- "@soederpop/luca": "latest",
23
+ "@soederpop/luca": ">=0.0.16",
24
24
  "gray-matter": "^4.0.3",
25
25
  "js-yaml": "^4.1.0",
26
26
  "mdast-util-mdxjs-esm": "^2.0.1",
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Static imports of all built-in endpoint modules.
3
+ * This allows endpoints to be bundled into a compiled bun binary
4
+ * instead of relying on runtime filesystem scanning.
5
+ */
6
+ import * as actions from './actions.js'
7
+ import * as doc from './doc.js'
8
+ import * as docsIndex from './docs-index.js'
9
+ import * as document from './document.js'
10
+ import * as documents from './documents.js'
11
+ import * as inspect from './inspect.js'
12
+ import * as models from './models.js'
13
+ import * as query from './query.js'
14
+ import * as root from './root.js'
15
+ import * as searchReindex from './search-reindex.js'
16
+ import * as searchStatus from './search-status.js'
17
+ import * as search from './search.js'
18
+ import * as semanticSearch from './semantic-search.js'
19
+ import * as textSearch from './text-search.js'
20
+ import * as validate from './validate.js'
21
+
22
+ export const builtinEndpoints = [
23
+ actions,
24
+ doc,
25
+ docsIndex,
26
+ document,
27
+ documents,
28
+ inspect,
29
+ models,
30
+ query,
31
+ root,
32
+ searchReindex,
33
+ searchStatus,
34
+ search,
35
+ semanticSearch,
36
+ textSearch,
37
+ validate,
38
+ ]
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { resolveModelDef } from '../helpers.js'
3
- import { queryDSLSchema, executeQueryDSL } from '../../query/query-dsl.js'
3
+ import { queryDSLSchema, executeQueryDSL } from '../../../../query/query-dsl.js'
4
4
 
5
5
  export const path = '/api/query'
6
6
  export const description = 'Query model instances with filtering'
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod'
2
- import { validateDocument } from '../../index.js'
2
+ import { validateDocument } from '../../../../index.js'
3
3
  import { resolveModelDef } from '../helpers.js'
4
4
 
5
5
  export const path = '/api/validate'
@@ -3,8 +3,8 @@ import remarkParse from 'remark-parse'
3
3
  import remarkGfm from 'remark-gfm'
4
4
  import remarkRehype from 'remark-rehype'
5
5
  import rehypeStringify from 'rehype-stringify'
6
- import type { Collection } from '../collection.js'
7
- import { introspectMetaSchema } from '../collection.js'
6
+ import type { Collection } from '../../../collection.js'
7
+ import { introspectMetaSchema } from '../../../collection.js'
8
8
 
9
9
  export async function renderMarkdownToHtml(markdown: string): Promise<string> {
10
10
  const result = await unified()
@@ -6,7 +6,7 @@ import matter from 'gray-matter'
6
6
  import { commands } from '../registry.js'
7
7
  import { loadCollection } from '../load-collection.js'
8
8
  import { introspectMetaSchema, validateDocument } from '../../index.js'
9
- import { resolveModelDef, buildSchemaJSON } from '../../api/helpers.js'
9
+ import { resolveModelDef, buildSchemaJSON } from './api/helpers.js'
10
10
  import { queryDSLSchema, executeQueryDSL } from '../../query/query-dsl.js'
11
11
  // MCPServer type comes from container.server('mcp', ...) at runtime
12
12
 
@@ -3,7 +3,8 @@ import path from 'node:path'
3
3
  import fs from 'node:fs'
4
4
  import { commands } from '../registry.js'
5
5
  import { loadCollection } from '../load-collection.js'
6
- import { buildSchemaJSON } from '../../api/helpers.js'
6
+ import { buildSchemaJSON } from './api/helpers.js'
7
+ import { builtinEndpoints } from './api/endpoints/index.js'
7
8
 
8
9
  const argsSchema = z.object({
9
10
  port: z.number().default(8000),
@@ -118,9 +119,8 @@ async function handler(options: z.infer<typeof argsSchema>, context: { container
118
119
  historyFallback: false,
119
120
  }) as any
120
121
 
121
- // Load built-in contentbase endpoints
122
- const builtinEndpointsDir = path.resolve(import.meta.dir, '../../api/endpoints')
123
- await expressServer.useEndpoints(builtinEndpointsDir)
122
+ // Load built-in contentbase endpoints (statically imported so they survive bun compile)
123
+ await expressServer.useEndpointModules(builtinEndpoints)
124
124
 
125
125
  // Load user endpoints if present
126
126
  if (userEndpointsDir) {
@@ -6,6 +6,7 @@ import { loadCollection } from '../load-collection.js'
6
6
 
7
7
  const argsSchema = z.object({
8
8
  contentFolder: z.string().optional(),
9
+ includeIds: z.boolean().optional().default(false),
9
10
  })
10
11
 
11
12
  async function handler(options: z.infer<typeof argsSchema>) {
@@ -13,7 +14,7 @@ async function handler(options: z.infer<typeof argsSchema>) {
13
14
  contentFolder: options.contentFolder,
14
15
  })
15
16
 
16
- await collection.saveModelSummary()
17
+ await collection.saveModelSummary({ includeIds: options.includeIds })
17
18
  console.log(`README.md written to ${collection.rootPath}/README.md`)
18
19
 
19
20
  const toc = collection.tableOfContents({ title: 'Table of Contents' })
@@ -39,6 +40,7 @@ cnotes summary [options]
39
40
  | Option | Description |
40
41
  |--------|-------------|
41
42
  | \`--contentFolder\` | Path to content folder |
43
+ | \`--include-ids\` | Include document IDs in the summary (default: false) |
42
44
 
43
45
  ## Generated Files
44
46
 
package/src/collection.ts CHANGED
@@ -715,7 +715,7 @@ export class Collection {
715
715
  * Generate a plain-text summary of the collection and its models.
716
716
  * Returns the same output as `cnotes inspect`.
717
717
  */
718
- generateModelSummary(): string {
718
+ generateModelSummary(options: { includeIds?: boolean } = {}): string {
719
719
  if (!this.#loaded) {
720
720
  throw new Error("Collection has not been loaded. Call load() first.");
721
721
  }
@@ -728,11 +728,17 @@ export class Collection {
728
728
  lines.push("");
729
729
 
730
730
  for (const def of this.modelDefinitions) {
731
- const matchingItems = this.available.filter(
732
- (id) => this.findModelDefinition(id)?.name === def.name
733
- );
734
731
  lines.push(` Model: ${def.name}`);
735
- lines.push(` Prefix: ${def.prefix}`);
732
+ if (def.description) {
733
+ lines.push(` Description: ${def.description}`);
734
+ }
735
+ if (def.pattern) {
736
+ const patterns = Array.isArray(def.pattern) ? def.pattern : [def.pattern];
737
+ lines.push(` Path prefix: ${patterns.join(", ")}`);
738
+ } else {
739
+ const rel = path.relative(process.cwd(), path.join(this.rootPath, def.prefix));
740
+ lines.push(` Path prefix: ${rel}/*.md`);
741
+ }
736
742
  const fields = introspectMetaSchema(def.meta);
737
743
  lines.push(
738
744
  ` Meta: ${fields.length > 0 ? fields.map((f) => `${f.name}(${f.type})`).join(", ") : "(none)"}`
@@ -743,9 +749,13 @@ export class Collection {
743
749
  lines.push(
744
750
  ` Relationships: ${Object.keys(def.relationships).join(", ") || "(none)"}`
745
751
  );
746
- lines.push(` Documents: ${matchingItems.length}`);
747
- if (matchingItems.length > 0) {
748
- lines.push(` IDs: ${matchingItems.join(", ")}`);
752
+ if (options.includeIds) {
753
+ const matchingItems = this.available.filter(
754
+ (id) => this.findModelDefinition(id)?.name === def.name
755
+ );
756
+ if (matchingItems.length > 0) {
757
+ lines.push(` IDs: ${[...matchingItems].sort().join(", ")}`);
758
+ }
749
759
  }
750
760
  lines.push("");
751
761
  }
@@ -762,8 +772,8 @@ export class Collection {
762
772
  * Preserves the `## Overview` section if it already exists.
763
773
  * The generated summary is placed in the `## Summary` section.
764
774
  */
765
- async saveModelSummary(): Promise<string> {
766
- const summary = this.generateModelSummary();
775
+ async saveModelSummary(options: { includeIds?: boolean } = {}): Promise<string> {
776
+ const summary = this.generateModelSummary(options);
767
777
  const modelsPath = path.join(this.rootPath, "README.md");
768
778
 
769
779
  // Preserve existing Overview section content
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import type { Collection } from "../collection";
3
3
  import type { Condition } from "./query-builder";
4
4
  import type { Operator } from "./operators";
5
- import { resolveModelDef } from "../api/helpers.js";
5
+ import { resolveModelDef } from "../cli/commands/api/helpers.js";
6
6
 
7
7
  // ---------------------------------------------------------------------------
8
8
  // Operator mapping: $dsl → internal
File without changes
File without changes