digital-products 2.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 (110) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +9 -0
  3. package/README.md +535 -0
  4. package/dist/api.d.ts +99 -0
  5. package/dist/api.d.ts.map +1 -0
  6. package/dist/api.js +129 -0
  7. package/dist/api.js.map +1 -0
  8. package/dist/app.d.ts +79 -0
  9. package/dist/app.d.ts.map +1 -0
  10. package/dist/app.js +107 -0
  11. package/dist/app.js.map +1 -0
  12. package/dist/content.d.ts +58 -0
  13. package/dist/content.d.ts.map +1 -0
  14. package/dist/content.js +78 -0
  15. package/dist/content.js.map +1 -0
  16. package/dist/data.d.ts +67 -0
  17. package/dist/data.d.ts.map +1 -0
  18. package/dist/data.js +107 -0
  19. package/dist/data.js.map +1 -0
  20. package/dist/dataset.d.ts +32 -0
  21. package/dist/dataset.d.ts.map +1 -0
  22. package/dist/dataset.js +50 -0
  23. package/dist/dataset.js.map +1 -0
  24. package/dist/entities/ai.d.ts +53 -0
  25. package/dist/entities/ai.d.ts.map +1 -0
  26. package/dist/entities/ai.js +859 -0
  27. package/dist/entities/ai.js.map +1 -0
  28. package/dist/entities/content.d.ts +52 -0
  29. package/dist/entities/content.d.ts.map +1 -0
  30. package/dist/entities/content.js +784 -0
  31. package/dist/entities/content.js.map +1 -0
  32. package/dist/entities/index.d.ts +112 -0
  33. package/dist/entities/index.d.ts.map +1 -0
  34. package/dist/entities/index.js +89 -0
  35. package/dist/entities/index.js.map +1 -0
  36. package/dist/entities/interfaces.d.ts +67 -0
  37. package/dist/entities/interfaces.d.ts.map +1 -0
  38. package/dist/entities/interfaces.js +930 -0
  39. package/dist/entities/interfaces.js.map +1 -0
  40. package/dist/entities/lifecycle.d.ts +51 -0
  41. package/dist/entities/lifecycle.d.ts.map +1 -0
  42. package/dist/entities/lifecycle.js +804 -0
  43. package/dist/entities/lifecycle.js.map +1 -0
  44. package/dist/entities/products.d.ts +53 -0
  45. package/dist/entities/products.d.ts.map +1 -0
  46. package/dist/entities/products.js +798 -0
  47. package/dist/entities/products.js.map +1 -0
  48. package/dist/entities/web.d.ts +44 -0
  49. package/dist/entities/web.d.ts.map +1 -0
  50. package/dist/entities/web.js +658 -0
  51. package/dist/entities/web.js.map +1 -0
  52. package/dist/index.d.ts +29 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +36 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/mcp.d.ts +101 -0
  57. package/dist/mcp.d.ts.map +1 -0
  58. package/dist/mcp.js +140 -0
  59. package/dist/mcp.js.map +1 -0
  60. package/dist/product.d.ts +37 -0
  61. package/dist/product.d.ts.map +1 -0
  62. package/dist/product.js +54 -0
  63. package/dist/product.js.map +1 -0
  64. package/dist/registry.d.ts +9 -0
  65. package/dist/registry.d.ts.map +1 -0
  66. package/dist/registry.js +32 -0
  67. package/dist/registry.js.map +1 -0
  68. package/dist/sdk.d.ts +99 -0
  69. package/dist/sdk.d.ts.map +1 -0
  70. package/dist/sdk.js +128 -0
  71. package/dist/sdk.js.map +1 -0
  72. package/dist/site.d.ts +85 -0
  73. package/dist/site.d.ts.map +1 -0
  74. package/dist/site.js +113 -0
  75. package/dist/site.js.map +1 -0
  76. package/dist/types.d.ts +528 -0
  77. package/dist/types.d.ts.map +1 -0
  78. package/dist/types.js +5 -0
  79. package/dist/types.js.map +1 -0
  80. package/example.ts +236 -0
  81. package/package.json +35 -0
  82. package/src/api.ts +140 -0
  83. package/src/app.ts +117 -0
  84. package/src/content.ts +82 -0
  85. package/src/data.ts +129 -0
  86. package/src/dataset.ts +53 -0
  87. package/src/entities/ai.ts +932 -0
  88. package/src/entities/content.ts +851 -0
  89. package/src/entities/index.ts +156 -0
  90. package/src/entities/interfaces.ts +1017 -0
  91. package/src/entities/lifecycle.ts +872 -0
  92. package/src/entities/products.ts +867 -0
  93. package/src/entities/web.ts +719 -0
  94. package/src/index.ts +55 -0
  95. package/src/mcp.ts +163 -0
  96. package/src/product.ts +59 -0
  97. package/src/registry.ts +41 -0
  98. package/src/sdk.ts +148 -0
  99. package/src/site.ts +127 -0
  100. package/src/types.ts +558 -0
  101. package/test/api.test.ts +247 -0
  102. package/test/app.test.ts +220 -0
  103. package/test/content.test.ts +171 -0
  104. package/test/data.test.ts +201 -0
  105. package/test/dataset.test.ts +181 -0
  106. package/test/mcp.test.ts +230 -0
  107. package/test/product.test.ts +200 -0
  108. package/test/sdk.test.ts +236 -0
  109. package/test/site.test.ts +245 -0
  110. package/tsconfig.json +9 -0
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Tests for Product functionality
3
+ *
4
+ * Covers product creation and registration.
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach } from 'vitest'
8
+ import { Product, createProduct, registerProduct, registry } from '../src/index.js'
9
+
10
+ describe('Product', () => {
11
+ beforeEach(() => {
12
+ registry.clear()
13
+ })
14
+
15
+ describe('Product creation', () => {
16
+ it('creates a product with basic config', () => {
17
+ const product = Product({
18
+ id: 'my-product',
19
+ name: 'My Product',
20
+ description: 'A digital product',
21
+ version: '1.0.0',
22
+ })
23
+
24
+ expect(product.id).toBe('my-product')
25
+ expect(product.name).toBe('My Product')
26
+ expect(product.description).toBe('A digital product')
27
+ expect(product.version).toBe('1.0.0')
28
+ })
29
+
30
+ it('defaults status to active', () => {
31
+ const product = Product({
32
+ id: 'test',
33
+ name: 'Test',
34
+ description: 'Test product',
35
+ version: '1.0.0',
36
+ })
37
+
38
+ expect(product.status).toBe('active')
39
+ })
40
+
41
+ it('allows custom status', () => {
42
+ const product = Product({
43
+ id: 'test',
44
+ name: 'Test',
45
+ description: 'Test product',
46
+ version: '1.0.0',
47
+ status: 'deprecated',
48
+ })
49
+
50
+ expect(product.status).toBe('deprecated')
51
+ })
52
+
53
+ it('supports metadata', () => {
54
+ const product = Product({
55
+ id: 'test',
56
+ name: 'Test',
57
+ description: 'Test product',
58
+ version: '1.0.0',
59
+ metadata: { author: 'John', license: 'MIT' },
60
+ })
61
+
62
+ expect(product.metadata).toEqual({ author: 'John', license: 'MIT' })
63
+ })
64
+
65
+ it('supports tags', () => {
66
+ const product = Product({
67
+ id: 'test',
68
+ name: 'Test',
69
+ description: 'Test product',
70
+ version: '1.0.0',
71
+ tags: ['api', 'production'],
72
+ })
73
+
74
+ expect(product.tags).toEqual(['api', 'production'])
75
+ })
76
+ })
77
+
78
+ describe('createProduct', () => {
79
+ it('creates a product', () => {
80
+ const product = createProduct({
81
+ id: 'created-product',
82
+ name: 'Created Product',
83
+ description: 'A created product',
84
+ version: '1.0.0',
85
+ })
86
+
87
+ expect(product.id).toBe('created-product')
88
+ expect(product.name).toBe('Created Product')
89
+ })
90
+ })
91
+
92
+ describe('registerProduct', () => {
93
+ it('registers a product in the registry', () => {
94
+ const product = Product({
95
+ id: 'registered-product',
96
+ name: 'Registered',
97
+ description: 'Registered product',
98
+ version: '1.0.0',
99
+ })
100
+
101
+ registerProduct(product)
102
+
103
+ expect(registry.get('registered-product')).toBeDefined()
104
+ })
105
+
106
+ it('returns the registered product', () => {
107
+ const product = Product({
108
+ id: 'returned-product',
109
+ name: 'Returned',
110
+ description: 'Returned product',
111
+ version: '1.0.0',
112
+ })
113
+
114
+ const result = registerProduct(product)
115
+
116
+ expect(result).toBe(product)
117
+ })
118
+ })
119
+ })
120
+
121
+ describe('Registry', () => {
122
+ beforeEach(() => {
123
+ registry.clear()
124
+ })
125
+
126
+ describe('register', () => {
127
+ it('adds a product to the registry', () => {
128
+ const product = Product({
129
+ id: 'test-product',
130
+ name: 'Test',
131
+ description: 'Test',
132
+ version: '1.0.0',
133
+ })
134
+
135
+ registerProduct(product)
136
+
137
+ expect(registry.get('test-product')).toEqual(product)
138
+ })
139
+ })
140
+
141
+ describe('get', () => {
142
+ it('returns product by id', () => {
143
+ const product = Product({
144
+ id: 'get-test',
145
+ name: 'Get Test',
146
+ description: 'Test',
147
+ version: '1.0.0',
148
+ })
149
+
150
+ registerProduct(product)
151
+
152
+ expect(registry.get('get-test')?.name).toBe('Get Test')
153
+ })
154
+
155
+ it('returns undefined for non-existent id', () => {
156
+ expect(registry.get('non-existent')).toBeUndefined()
157
+ })
158
+ })
159
+
160
+ describe('list', () => {
161
+ it('returns all products', () => {
162
+ registerProduct(Product({ id: 'p1', name: 'P1', description: 'D1', version: '1.0.0' }))
163
+ registerProduct(Product({ id: 'p2', name: 'P2', description: 'D2', version: '1.0.0' }))
164
+
165
+ const products = registry.list()
166
+
167
+ expect(products).toHaveLength(2)
168
+ })
169
+
170
+ it('returns empty array when no products', () => {
171
+ expect(registry.list()).toHaveLength(0)
172
+ })
173
+ })
174
+
175
+ describe('remove', () => {
176
+ it('removes a product from the registry', () => {
177
+ registerProduct(Product({ id: 'to-remove', name: 'Remove', description: 'D', version: '1.0.0' }))
178
+
179
+ const result = registry.remove('to-remove')
180
+
181
+ expect(result).toBe(true)
182
+ expect(registry.get('to-remove')).toBeUndefined()
183
+ })
184
+
185
+ it('returns false for non-existent product', () => {
186
+ expect(registry.remove('non-existent')).toBe(false)
187
+ })
188
+ })
189
+
190
+ describe('clear', () => {
191
+ it('removes all products', () => {
192
+ registerProduct(Product({ id: 'p1', name: 'P1', description: 'D1', version: '1.0.0' }))
193
+ registerProduct(Product({ id: 'p2', name: 'P2', description: 'D2', version: '1.0.0' }))
194
+
195
+ registry.clear()
196
+
197
+ expect(registry.list()).toHaveLength(0)
198
+ })
199
+ })
200
+ })
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Tests for SDK functionality
3
+ *
4
+ * Covers SDK creation and helper functions.
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach } from 'vitest'
8
+ import { SDK, Export, Example, registry } from '../src/index.js'
9
+
10
+ describe('SDK', () => {
11
+ beforeEach(() => {
12
+ registry.clear()
13
+ })
14
+
15
+ describe('SDK creation', () => {
16
+ it('creates an SDK with basic config', () => {
17
+ const sdk = SDK({
18
+ id: 'my-sdk',
19
+ name: 'My SDK',
20
+ description: 'JavaScript SDK for My API',
21
+ version: '1.0.0',
22
+ language: 'typescript',
23
+ })
24
+
25
+ expect(sdk.id).toBe('my-sdk')
26
+ expect(sdk.name).toBe('My SDK')
27
+ expect(sdk.type).toBe('sdk')
28
+ expect(sdk.language).toBe('typescript')
29
+ })
30
+
31
+ it('creates an SDK with API reference', () => {
32
+ const sdk = SDK({
33
+ id: 'api-sdk',
34
+ name: 'API SDK',
35
+ description: 'SDK for API',
36
+ version: '1.0.0',
37
+ language: 'typescript',
38
+ api: 'my-api',
39
+ })
40
+
41
+ expect(sdk.api).toBe('my-api')
42
+ })
43
+
44
+ it('creates an SDK with exports', () => {
45
+ const sdk = SDK({
46
+ id: 'export-sdk',
47
+ name: 'Export SDK',
48
+ description: 'SDK with exports',
49
+ version: '1.0.0',
50
+ language: 'typescript',
51
+ exports: [
52
+ Export('function', 'createClient', 'Create an API client'),
53
+ Export('class', 'APIClient', 'Main API client'),
54
+ ],
55
+ })
56
+
57
+ expect(sdk.exports).toHaveLength(2)
58
+ expect(sdk.exports?.[0]?.name).toBe('createClient')
59
+ })
60
+
61
+ it('creates an SDK with install command', () => {
62
+ const sdk = SDK({
63
+ id: 'install-sdk',
64
+ name: 'Install SDK',
65
+ description: 'SDK with install command',
66
+ version: '1.0.0',
67
+ language: 'typescript',
68
+ install: 'npm install my-sdk',
69
+ })
70
+
71
+ expect(sdk.install).toBe('npm install my-sdk')
72
+ })
73
+
74
+ it('creates an SDK with docs URL', () => {
75
+ const sdk = SDK({
76
+ id: 'docs-sdk',
77
+ name: 'Docs SDK',
78
+ description: 'SDK with docs',
79
+ version: '1.0.0',
80
+ language: 'typescript',
81
+ docs: 'https://docs.example.com/sdk',
82
+ })
83
+
84
+ expect(sdk.docs).toBe('https://docs.example.com/sdk')
85
+ })
86
+
87
+ it('creates an SDK with examples', () => {
88
+ const sdk = SDK({
89
+ id: 'example-sdk',
90
+ name: 'Example SDK',
91
+ description: 'SDK with examples',
92
+ version: '1.0.0',
93
+ language: 'typescript',
94
+ examples: [
95
+ Example(
96
+ 'Basic Usage',
97
+ 'Create a client and make a request',
98
+ `import { createClient } from 'my-sdk'\nconst client = createClient({ apiKey: 'key' })`
99
+ ),
100
+ ],
101
+ })
102
+
103
+ expect(sdk.examples).toHaveLength(1)
104
+ expect(sdk.examples?.[0]?.title).toBe('Basic Usage')
105
+ })
106
+
107
+ it('registers SDK automatically', () => {
108
+ SDK({
109
+ id: 'auto-registered',
110
+ name: 'Auto Registered',
111
+ description: 'Automatically registered',
112
+ version: '1.0.0',
113
+ language: 'typescript',
114
+ })
115
+
116
+ expect(registry.get('auto-registered')).toBeDefined()
117
+ })
118
+
119
+ it('supports Python language', () => {
120
+ const sdk = SDK({
121
+ id: 'python-sdk',
122
+ name: 'Python SDK',
123
+ description: 'Python SDK',
124
+ version: '1.0.0',
125
+ language: 'python',
126
+ install: 'pip install my-sdk',
127
+ })
128
+
129
+ expect(sdk.language).toBe('python')
130
+ })
131
+ })
132
+
133
+ describe('Export helper', () => {
134
+ it('creates a function export', () => {
135
+ const exp = Export('function', 'createClient', 'Create an API client', {
136
+ parameters: {
137
+ apiKey: 'API key for authentication',
138
+ baseUrl: 'Optional base URL',
139
+ },
140
+ returns: 'API client instance',
141
+ })
142
+
143
+ expect(exp.type).toBe('function')
144
+ expect(exp.name).toBe('createClient')
145
+ expect(exp.description).toBe('Create an API client')
146
+ expect(exp.parameters?.apiKey).toBe('API key for authentication')
147
+ expect(exp.returns).toBe('API client instance')
148
+ })
149
+
150
+ it('creates a class export', () => {
151
+ const exp = Export('class', 'APIClient', 'Main API client', {
152
+ methods: [
153
+ Export('function', 'get', 'GET request', {
154
+ parameters: { path: 'Request path' },
155
+ returns: 'Response data',
156
+ }),
157
+ Export('function', 'post', 'POST request', {
158
+ parameters: { path: 'Request path', data: 'Request body' },
159
+ returns: 'Response data',
160
+ }),
161
+ ],
162
+ })
163
+
164
+ expect(exp.type).toBe('class')
165
+ expect(exp.methods).toHaveLength(2)
166
+ expect(exp.methods?.[0]?.name).toBe('get')
167
+ })
168
+
169
+ it('creates a type export', () => {
170
+ const exp = Export('type', 'ClientOptions', 'Client configuration options', {
171
+ parameters: {
172
+ apiKey: 'API key',
173
+ timeout: 'Request timeout (number)',
174
+ retries: 'Number of retries (number)',
175
+ },
176
+ })
177
+
178
+ expect(exp.type).toBe('type')
179
+ })
180
+
181
+ it('creates a constant export', () => {
182
+ const exp = Export('constant', 'VERSION', 'SDK version string')
183
+
184
+ expect(exp.type).toBe('constant')
185
+ expect(exp.name).toBe('VERSION')
186
+ })
187
+ })
188
+
189
+ describe('Example helper', () => {
190
+ it('creates a basic example', () => {
191
+ const example = Example(
192
+ 'Getting Started',
193
+ 'How to get started with the SDK',
194
+ `import { SDK } from 'my-sdk'\nconst sdk = new SDK()`
195
+ )
196
+
197
+ expect(example.title).toBe('Getting Started')
198
+ expect(example.description).toBe('How to get started with the SDK')
199
+ expect(example.code).toContain('import')
200
+ })
201
+
202
+ it('creates an example with output', () => {
203
+ const example = Example(
204
+ 'Authentication',
205
+ 'How to authenticate',
206
+ `const client = createClient({ apiKey: 'key' })`,
207
+ '{ authenticated: true }'
208
+ )
209
+
210
+ expect(example.output).toBe('{ authenticated: true }')
211
+ })
212
+
213
+ it('creates a multi-line example', () => {
214
+ const code = `
215
+ import { createClient } from 'my-sdk'
216
+
217
+ const client = createClient({
218
+ apiKey: process.env.API_KEY,
219
+ baseUrl: 'https://api.example.com',
220
+ })
221
+
222
+ const users = await client.get('/users')
223
+ console.log(users)
224
+ `.trim()
225
+
226
+ const example = Example(
227
+ 'Complete Example',
228
+ 'Full usage example',
229
+ code
230
+ )
231
+
232
+ expect(example.code).toContain('createClient')
233
+ expect(example.code).toContain('console.log')
234
+ })
235
+ })
236
+ })
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Tests for Site functionality
3
+ *
4
+ * Covers site creation and helper functions.
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach } from 'vitest'
8
+ import { Site, Nav, SEO, Analytics, registry } from '../src/index.js'
9
+
10
+ describe('Site', () => {
11
+ beforeEach(() => {
12
+ registry.clear()
13
+ })
14
+
15
+ describe('Site creation', () => {
16
+ it('creates a site with basic config', () => {
17
+ const site = Site({
18
+ id: 'docs',
19
+ name: 'Documentation Site',
20
+ description: 'Product documentation',
21
+ version: '1.0.0',
22
+ })
23
+
24
+ expect(site.id).toBe('docs')
25
+ expect(site.name).toBe('Documentation Site')
26
+ expect(site.type).toBe('site')
27
+ })
28
+
29
+ it('defaults generator to next', () => {
30
+ const site = Site({
31
+ id: 'default-gen',
32
+ name: 'Default Generator',
33
+ description: 'Uses default generator',
34
+ version: '1.0.0',
35
+ })
36
+
37
+ expect(site.generator).toBe('next')
38
+ })
39
+
40
+ it('supports fumadocs generator', () => {
41
+ const site = Site({
42
+ id: 'fumadocs-site',
43
+ name: 'Fumadocs Site',
44
+ description: 'Uses Fumadocs',
45
+ version: '1.0.0',
46
+ generator: 'fumadocs',
47
+ })
48
+
49
+ expect(site.generator).toBe('fumadocs')
50
+ })
51
+
52
+ it('creates a site with structure', () => {
53
+ const site = Site({
54
+ id: 'structured-site',
55
+ name: 'Structured Site',
56
+ description: 'Site with structure',
57
+ version: '1.0.0',
58
+ structure: {
59
+ home: '/docs/index.mdx',
60
+ docs: [
61
+ '/docs/getting-started.mdx',
62
+ '/docs/api-reference.mdx',
63
+ ],
64
+ },
65
+ })
66
+
67
+ expect(site.structure?.home).toBe('/docs/index.mdx')
68
+ expect(site.structure?.docs).toHaveLength(2)
69
+ })
70
+
71
+ it('creates a site with navigation', () => {
72
+ const site = Site({
73
+ id: 'navigated-site',
74
+ name: 'Navigated Site',
75
+ description: 'Site with navigation',
76
+ version: '1.0.0',
77
+ navigation: [
78
+ Nav('Home', '/'),
79
+ Nav('Docs', '/docs'),
80
+ ],
81
+ })
82
+
83
+ expect(site.navigation).toHaveLength(2)
84
+ expect(site.navigation?.[0]?.label).toBe('Home')
85
+ })
86
+
87
+ it('creates a site with SEO', () => {
88
+ const site = Site({
89
+ id: 'seo-site',
90
+ name: 'SEO Site',
91
+ description: 'Site with SEO',
92
+ version: '1.0.0',
93
+ seo: {
94
+ titleTemplate: '%s | My Product',
95
+ description: 'Official documentation',
96
+ keywords: ['docs', 'api'],
97
+ },
98
+ })
99
+
100
+ expect(site.seo?.titleTemplate).toBe('%s | My Product')
101
+ expect(site.seo?.keywords).toContain('docs')
102
+ })
103
+
104
+ it('creates a site with analytics', () => {
105
+ const site = Site({
106
+ id: 'analytics-site',
107
+ name: 'Analytics Site',
108
+ description: 'Site with analytics',
109
+ version: '1.0.0',
110
+ analytics: {
111
+ provider: 'plausible',
112
+ id: 'docs.example.com',
113
+ },
114
+ })
115
+
116
+ expect(site.analytics?.provider).toBe('plausible')
117
+ expect(site.analytics?.id).toBe('docs.example.com')
118
+ })
119
+
120
+ it('creates a site with deployment config', () => {
121
+ const site = Site({
122
+ id: 'deployed-site',
123
+ name: 'Deployed Site',
124
+ description: 'Site with deployment',
125
+ version: '1.0.0',
126
+ deployment: {
127
+ provider: 'vercel',
128
+ url: 'https://docs.example.com',
129
+ },
130
+ })
131
+
132
+ expect(site.deployment?.provider).toBe('vercel')
133
+ })
134
+
135
+ it('registers site automatically', () => {
136
+ Site({
137
+ id: 'auto-registered',
138
+ name: 'Auto Registered',
139
+ description: 'Automatically registered',
140
+ version: '1.0.0',
141
+ })
142
+
143
+ expect(registry.get('auto-registered')).toBeDefined()
144
+ })
145
+ })
146
+
147
+ describe('Nav helper', () => {
148
+ it('creates a basic nav item', () => {
149
+ const nav = Nav('Home', '/')
150
+
151
+ expect(nav.label).toBe('Home')
152
+ expect(nav.href).toBe('/')
153
+ })
154
+
155
+ it('creates a nav item with icon', () => {
156
+ const nav = Nav('Documentation', '/docs', {
157
+ icon: 'book',
158
+ })
159
+
160
+ expect(nav.icon).toBe('book')
161
+ })
162
+
163
+ it('creates a nav item with children', () => {
164
+ const nav = Nav('Docs', '/docs', {
165
+ children: [
166
+ Nav('Getting Started', '/docs/getting-started'),
167
+ Nav('API Reference', '/docs/api'),
168
+ ],
169
+ })
170
+
171
+ expect(nav.children).toHaveLength(2)
172
+ expect(nav.children?.[0]?.label).toBe('Getting Started')
173
+ })
174
+
175
+ it('creates nested navigation', () => {
176
+ const nav = Nav('Products', '/products', {
177
+ children: [
178
+ Nav('Apps', '/products/apps', {
179
+ children: [
180
+ Nav('Web', '/products/apps/web'),
181
+ Nav('Mobile', '/products/apps/mobile'),
182
+ ],
183
+ }),
184
+ Nav('APIs', '/products/apis'),
185
+ ],
186
+ })
187
+
188
+ expect(nav.children?.[0]?.children).toHaveLength(2)
189
+ })
190
+ })
191
+
192
+ describe('SEO helper', () => {
193
+ it('creates SEO config', () => {
194
+ const seo = SEO({
195
+ titleTemplate: '%s | My Site',
196
+ description: 'My awesome site',
197
+ })
198
+
199
+ expect(seo.titleTemplate).toBe('%s | My Site')
200
+ expect(seo.description).toBe('My awesome site')
201
+ })
202
+
203
+ it('creates SEO config with keywords', () => {
204
+ const seo = SEO({
205
+ keywords: ['keyword1', 'keyword2'],
206
+ })
207
+
208
+ expect(seo.keywords).toHaveLength(2)
209
+ })
210
+
211
+ it('creates SEO config with ogImage', () => {
212
+ const seo = SEO({
213
+ ogImage: '/og-image.png',
214
+ twitterCard: 'summary_large_image',
215
+ })
216
+
217
+ expect(seo.ogImage).toBe('/og-image.png')
218
+ expect(seo.twitterCard).toBe('summary_large_image')
219
+ })
220
+ })
221
+
222
+ describe('Analytics helper', () => {
223
+ it('creates Google analytics config', () => {
224
+ const analytics = Analytics('google', 'G-XXXXXXXXXX')
225
+
226
+ expect(analytics.provider).toBe('google')
227
+ expect(analytics.id).toBe('G-XXXXXXXXXX')
228
+ })
229
+
230
+ it('creates Plausible analytics config', () => {
231
+ const analytics = Analytics('plausible', 'example.com')
232
+
233
+ expect(analytics.provider).toBe('plausible')
234
+ expect(analytics.id).toBe('example.com')
235
+ })
236
+
237
+ it('creates analytics config with custom options', () => {
238
+ const analytics = Analytics('google', 'G-XXXXXXXXXX', {
239
+ anonymizeIp: true,
240
+ })
241
+
242
+ expect(analytics.config?.anonymizeIp).toBe(true)
243
+ })
244
+ })
245
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
9
+ }