contentbase 0.0.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.
Files changed (89) hide show
  1. package/README.md +460 -0
  2. package/bun.lock +473 -0
  3. package/examples/sdlc-queries.ts +161 -0
  4. package/package.json +41 -0
  5. package/showcases/national-parks/models.ts +74 -0
  6. package/showcases/national-parks/parks/acadia.mdx +40 -0
  7. package/showcases/national-parks/parks/yosemite.mdx +44 -0
  8. package/showcases/national-parks/parks/zion.mdx +44 -0
  9. package/showcases/national-parks/queries.ts +103 -0
  10. package/showcases/national-parks/trails/angels-landing.mdx +19 -0
  11. package/showcases/national-parks/trails/cathedral-lakes.mdx +19 -0
  12. package/showcases/national-parks/trails/half-dome.mdx +19 -0
  13. package/showcases/national-parks/trails/jordan-pond-path.mdx +19 -0
  14. package/showcases/national-parks/trails/mist-trail.mdx +19 -0
  15. package/showcases/national-parks/trails/observation-point.mdx +19 -0
  16. package/showcases/national-parks/trails/precipice-trail.mdx +19 -0
  17. package/showcases/national-parks/trails/the-narrows.mdx +19 -0
  18. package/showcases/recipes/cuisines/chinese.mdx +28 -0
  19. package/showcases/recipes/cuisines/italian.mdx +32 -0
  20. package/showcases/recipes/cuisines/mexican.mdx +28 -0
  21. package/showcases/recipes/models.ts +77 -0
  22. package/showcases/recipes/queries.ts +89 -0
  23. package/showcases/recipes/recipes/chinese/egg-fried-rice.mdx +43 -0
  24. package/showcases/recipes/recipes/chinese/mapo-tofu.mdx +47 -0
  25. package/showcases/recipes/recipes/italian/bruschetta.mdx +38 -0
  26. package/showcases/recipes/recipes/italian/cacio-e-pepe.mdx +39 -0
  27. package/showcases/recipes/recipes/italian/tiramisu.mdx +43 -0
  28. package/showcases/recipes/recipes/mexican/chicken-tinga.mdx +44 -0
  29. package/showcases/recipes/recipes/mexican/guacamole.mdx +39 -0
  30. package/showcases/vinyl-collection/albums/bitches-brew.mdx +36 -0
  31. package/showcases/vinyl-collection/albums/i-put-a-spell-on-you.mdx +35 -0
  32. package/showcases/vinyl-collection/albums/in-rainbows.mdx +35 -0
  33. package/showcases/vinyl-collection/albums/kind-of-blue.mdx +32 -0
  34. package/showcases/vinyl-collection/albums/ok-computer.mdx +37 -0
  35. package/showcases/vinyl-collection/albums/wild-is-the-wind.mdx +35 -0
  36. package/showcases/vinyl-collection/artists/miles-davis.mdx +27 -0
  37. package/showcases/vinyl-collection/artists/nina-simone.mdx +26 -0
  38. package/showcases/vinyl-collection/artists/radiohead.mdx +27 -0
  39. package/showcases/vinyl-collection/models.ts +73 -0
  40. package/showcases/vinyl-collection/queries.ts +87 -0
  41. package/src/ast-query.ts +132 -0
  42. package/src/cli/commands/action.ts +44 -0
  43. package/src/cli/commands/create.ts +59 -0
  44. package/src/cli/commands/export.ts +24 -0
  45. package/src/cli/commands/init.ts +75 -0
  46. package/src/cli/commands/inspect.ts +46 -0
  47. package/src/cli/commands/validate.ts +75 -0
  48. package/src/cli/index.ts +20 -0
  49. package/src/cli/load-collection.ts +53 -0
  50. package/src/collection.ts +399 -0
  51. package/src/define-model.ts +80 -0
  52. package/src/document.ts +468 -0
  53. package/src/index.ts +47 -0
  54. package/src/model-instance.ts +227 -0
  55. package/src/node-shortcuts.ts +87 -0
  56. package/src/parse.ts +123 -0
  57. package/src/query/collection-query.ts +149 -0
  58. package/src/query/index.ts +5 -0
  59. package/src/query/operators.ts +37 -0
  60. package/src/query/query-builder.ts +109 -0
  61. package/src/relationships/belongs-to.ts +50 -0
  62. package/src/relationships/has-many.ts +136 -0
  63. package/src/relationships/index.ts +57 -0
  64. package/src/relationships/types.ts +7 -0
  65. package/src/section.ts +29 -0
  66. package/src/types.ts +221 -0
  67. package/src/utils/index.ts +11 -0
  68. package/src/utils/inflect.ts +82 -0
  69. package/src/utils/normalize-headings.ts +31 -0
  70. package/src/utils/parse-table.ts +30 -0
  71. package/src/utils/read-directory.ts +35 -0
  72. package/src/utils/stringify-ast.ts +9 -0
  73. package/src/validator.ts +52 -0
  74. package/test/ast-query.test.ts +128 -0
  75. package/test/collection.test.ts +99 -0
  76. package/test/define-model.test.ts +78 -0
  77. package/test/document.test.ts +225 -0
  78. package/test/fixtures/sdlc/epics/authentication.mdx +42 -0
  79. package/test/fixtures/sdlc/epics/searching-and-browsing.mdx +21 -0
  80. package/test/fixtures/sdlc/models.ts +89 -0
  81. package/test/fixtures/sdlc/stories/authentication/a-user-should-be-able-to-register.mdx +20 -0
  82. package/test/helpers.ts +21 -0
  83. package/test/model-instance.test.ts +197 -0
  84. package/test/query.test.ts +167 -0
  85. package/test/relationships.test.ts +84 -0
  86. package/test/section.test.ts +99 -0
  87. package/test/validator.test.ts +62 -0
  88. package/tsconfig.json +18 -0
  89. package/vitest.config.ts +11 -0
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { section } from "../src/section";
3
+ import { Collection } from "../src/collection";
4
+ import { createModelInstance } from "../src/model-instance";
5
+ import { createTestCollection } from "./helpers";
6
+ import { Story } from "./fixtures/sdlc/models";
7
+ import { z } from "zod";
8
+ import { toString } from "mdast-util-to-string";
9
+
10
+ describe("section helper", () => {
11
+ it("creates a SectionDefinition with heading and extract", () => {
12
+ const sd = section("My Section", {
13
+ extract: (q) => q.selectAll("listItem"),
14
+ });
15
+ expect(sd.heading).toBe("My Section");
16
+ expect(sd.extract).toBeDefined();
17
+ });
18
+
19
+ it("stores optional schema", () => {
20
+ const schema = z.array(z.string());
21
+ const sd = section("Items", {
22
+ extract: (q) =>
23
+ q.selectAll("listItem").map((n) => toString(n)),
24
+ schema,
25
+ });
26
+ expect(sd.schema).toBe(schema);
27
+ });
28
+
29
+ it("extract receives AstQuery scoped to section content only", async () => {
30
+ const collection = await createTestCollection();
31
+ const doc = collection.document(
32
+ "stories/authentication/a-user-should-be-able-to-register"
33
+ );
34
+ const instance = createModelInstance(doc, Story, collection);
35
+
36
+ // acceptanceCriteria should only contain items from that section
37
+ const criteria = instance.sections.acceptanceCriteria;
38
+ expect(criteria.length).toBe(4);
39
+ expect(criteria[0]).toContain("signup form");
40
+ });
41
+
42
+ it("section data is lazily computed", async () => {
43
+ const collection = await createTestCollection();
44
+ const doc = collection.document(
45
+ "stories/authentication/a-user-should-be-able-to-register"
46
+ );
47
+
48
+ let extractCalled = 0;
49
+ const TestModel = {
50
+ name: "Test",
51
+ prefix: "test",
52
+ meta: z.object({}).passthrough(),
53
+ schema: z.object({}).passthrough(),
54
+ sections: {
55
+ items: section("Acceptance Criteria", {
56
+ extract: (q) => {
57
+ extractCalled++;
58
+ return q.selectAll("listItem").map((n) => toString(n));
59
+ },
60
+ }),
61
+ },
62
+ relationships: {},
63
+ computed: {},
64
+ } as any;
65
+
66
+ const instance = createModelInstance(doc, TestModel, collection);
67
+ expect(extractCalled).toBe(0); // Not yet accessed
68
+ const _items = instance.sections.items;
69
+ expect(extractCalled).toBe(1); // Now extracted
70
+ const _items2 = instance.sections.items;
71
+ expect(extractCalled).toBe(1); // Cached
72
+ });
73
+
74
+ it("missing section returns empty data", async () => {
75
+ const collection = await createTestCollection();
76
+ const doc = collection.createDocument({
77
+ id: "test/no-section",
78
+ content: "# Just a title\n\nSome content.\n",
79
+ });
80
+
81
+ const TestModel = {
82
+ name: "Test",
83
+ prefix: "test",
84
+ meta: z.object({}).passthrough(),
85
+ schema: z.object({}).passthrough(),
86
+ sections: {
87
+ missing: section("Nonexistent Section", {
88
+ extract: (q) => q.selectAll("listItem").map((n) => toString(n)),
89
+ }),
90
+ },
91
+ relationships: {},
92
+ computed: {},
93
+ } as any;
94
+
95
+ const instance = createModelInstance(doc, TestModel, collection);
96
+ // Should not crash, just return empty
97
+ expect(instance.sections.missing).toEqual([]);
98
+ });
99
+ });
@@ -0,0 +1,62 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { validateDocument } from "../src/validator";
3
+ import { Collection } from "../src/collection";
4
+ import { createTestCollection } from "./helpers";
5
+ import { Epic, Story } from "./fixtures/sdlc/models";
6
+
7
+ describe("validateDocument", () => {
8
+ let collection: Collection;
9
+
10
+ beforeEach(async () => {
11
+ collection = await createTestCollection();
12
+ });
13
+
14
+ it("returns valid for good meta", () => {
15
+ const doc = collection.document("epics/authentication");
16
+ const result = validateDocument(doc, Epic);
17
+ expect(result.valid).toBe(true);
18
+ expect(result.errors.length).toBe(0);
19
+ });
20
+
21
+ it("returns errors for invalid meta", () => {
22
+ const doc = collection.createDocument({
23
+ id: "test/bad",
24
+ content: "# Bad\n",
25
+ meta: { status: "BOGUS" },
26
+ });
27
+ const result = validateDocument(doc, Epic);
28
+ expect(result.valid).toBe(false);
29
+ expect(result.errors.length).toBeGreaterThan(0);
30
+ });
31
+
32
+ it("applies defaults before validation", () => {
33
+ const doc = collection.createDocument({
34
+ id: "test/defaults",
35
+ content: "# Defaults\n",
36
+ meta: {},
37
+ });
38
+ const result = validateDocument(doc, Epic);
39
+ expect(result.valid).toBe(true);
40
+ });
41
+
42
+ it("validates sections when schema is present", () => {
43
+ const doc = collection.document(
44
+ "stories/authentication/a-user-should-be-able-to-register"
45
+ );
46
+ const result = validateDocument(doc, Story);
47
+ expect(result.valid).toBe(true);
48
+ });
49
+
50
+ it("section validation errors include section key in path", () => {
51
+ const doc = collection.createDocument({
52
+ id: "test/bad-section",
53
+ content: "# Bad\n\n## Acceptance Criteria\n\nNo list items here.\n",
54
+ meta: { status: "created" },
55
+ });
56
+ const result = validateDocument(doc, Story);
57
+ // acceptanceCriteria extracts listItems; paragraph text won't produce any
58
+ // The schema is z.array(z.string()) which allows empty arrays
59
+ // so this should actually pass - the extract returns []
60
+ expect(result.valid).toBe(true);
61
+ });
62
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "sourceMap": true,
11
+ "outDir": "./dist",
12
+ "rootDir": "./src",
13
+ "types": ["bun-types"],
14
+ "skipLibCheck": true
15
+ },
16
+ "include": ["src/**/*.ts"],
17
+ "exclude": ["node_modules", "dist", "test"]
18
+ }
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ resolve: {
5
+ extensions: [".ts", ".js", ".mjs"],
6
+ },
7
+ test: {
8
+ globals: true,
9
+ include: ["test/**/*.test.ts"],
10
+ },
11
+ });