@pyreon/document-primitives 0.13.1 → 0.15.0
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/lib/index.d.ts +410 -410
- package/package.json +20 -18
- package/src/__tests__/manifest-snapshot.test.ts +45 -0
- package/src/__tests__/primitives-attrs.test.ts +12 -12
- package/src/__tests__/useDocumentExport.test.ts +69 -18
- package/src/manifest.ts +388 -0
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/document-primitives",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Rocketstyle document components — render in browser, export to 18 formats",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"lib",
|
|
13
|
+
"!lib/**/*.map",
|
|
13
14
|
"!lib/analysis",
|
|
14
15
|
"README.md",
|
|
15
16
|
"LICENSE",
|
|
@@ -41,28 +42,29 @@
|
|
|
41
42
|
"typecheck": "tsc --noEmit"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@pyreon/connector-document": "^0.
|
|
45
|
+
"@pyreon/connector-document": "^0.15.0"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@pyreon/core": "^0.
|
|
48
|
-
"@pyreon/elements": "^0.
|
|
49
|
-
"@pyreon/
|
|
50
|
-
"@pyreon/
|
|
51
|
-
"@pyreon/
|
|
52
|
-
"@pyreon/
|
|
53
|
-
"@pyreon/
|
|
54
|
-
"@pyreon/
|
|
55
|
-
"@pyreon/
|
|
48
|
+
"@pyreon/core": "^0.15.0",
|
|
49
|
+
"@pyreon/elements": "^0.15.0",
|
|
50
|
+
"@pyreon/manifest": "0.13.1",
|
|
51
|
+
"@pyreon/reactivity": "^0.15.0",
|
|
52
|
+
"@pyreon/rocketstyle": "^0.15.0",
|
|
53
|
+
"@pyreon/runtime-dom": "^0.15.0",
|
|
54
|
+
"@pyreon/styler": "^0.15.0",
|
|
55
|
+
"@pyreon/test-utils": "^0.13.2",
|
|
56
|
+
"@pyreon/typescript": "^0.15.0",
|
|
57
|
+
"@pyreon/ui-core": "^0.15.0",
|
|
56
58
|
"@vitest/browser-playwright": "^4.1.4",
|
|
57
|
-
"@vitus-labs/tools-rolldown": "^
|
|
59
|
+
"@vitus-labs/tools-rolldown": "^2.3.0"
|
|
58
60
|
},
|
|
59
61
|
"peerDependencies": {
|
|
60
|
-
"@pyreon/core": "^0.
|
|
61
|
-
"@pyreon/document": "^0.
|
|
62
|
-
"@pyreon/elements": "^0.
|
|
63
|
-
"@pyreon/rocketstyle": "^0.
|
|
64
|
-
"@pyreon/styler": "^0.
|
|
65
|
-
"@pyreon/ui-core": "^0.
|
|
62
|
+
"@pyreon/core": "^0.15.0",
|
|
63
|
+
"@pyreon/document": "^0.15.0",
|
|
64
|
+
"@pyreon/elements": "^0.15.0",
|
|
65
|
+
"@pyreon/rocketstyle": "^0.15.0",
|
|
66
|
+
"@pyreon/styler": "^0.15.0",
|
|
67
|
+
"@pyreon/ui-core": "^0.15.0"
|
|
66
68
|
},
|
|
67
69
|
"engines": {
|
|
68
70
|
"node": ">= 22"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
renderApiReferenceEntries,
|
|
3
|
+
renderLlmsFullSection,
|
|
4
|
+
renderLlmsTxtLine,
|
|
5
|
+
} from '@pyreon/manifest'
|
|
6
|
+
import manifest from '../manifest'
|
|
7
|
+
|
|
8
|
+
describe('gen-docs — document-primitives snapshot', () => {
|
|
9
|
+
it('renders a llms.txt bullet starting with the package prefix', () => {
|
|
10
|
+
const line = renderLlmsTxtLine(manifest)
|
|
11
|
+
expect(line.startsWith('- @pyreon/document-primitives —')).toBe(true)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('renders a llms-full.txt section with the right header', () => {
|
|
15
|
+
const section = renderLlmsFullSection(manifest)
|
|
16
|
+
expect(section.startsWith('## @pyreon/document-primitives —')).toBe(true)
|
|
17
|
+
expect(section).toContain('```typescript')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('renders MCP api-reference entries for every api[] item', () => {
|
|
21
|
+
const record = renderApiReferenceEntries(manifest)
|
|
22
|
+
expect(Object.keys(record).sort()).toEqual([
|
|
23
|
+
'document-primitives/DocButton',
|
|
24
|
+
'document-primitives/DocCode',
|
|
25
|
+
'document-primitives/DocColumn',
|
|
26
|
+
'document-primitives/DocDivider',
|
|
27
|
+
'document-primitives/DocDocument',
|
|
28
|
+
'document-primitives/DocHeading',
|
|
29
|
+
'document-primitives/DocImage',
|
|
30
|
+
'document-primitives/DocLink',
|
|
31
|
+
'document-primitives/DocList',
|
|
32
|
+
'document-primitives/DocListItem',
|
|
33
|
+
'document-primitives/DocPage',
|
|
34
|
+
'document-primitives/DocPageBreak',
|
|
35
|
+
'document-primitives/DocQuote',
|
|
36
|
+
'document-primitives/DocRow',
|
|
37
|
+
'document-primitives/DocSection',
|
|
38
|
+
'document-primitives/DocSpacer',
|
|
39
|
+
'document-primitives/DocTable',
|
|
40
|
+
'document-primitives/DocText',
|
|
41
|
+
'document-primitives/createDocumentExport',
|
|
42
|
+
'document-primitives/extractDocNode',
|
|
43
|
+
])
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -14,7 +14,7 @@ describe('DocDocument attrs', () => {
|
|
|
14
14
|
it('sets tag to div', async () => {
|
|
15
15
|
const DocDocument = (await import('../primitives/DocDocument')).default
|
|
16
16
|
const result = renderProps(DocDocument, { children: 'test' })
|
|
17
|
-
expect(result.
|
|
17
|
+
expect(result.as).toBe('div')
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
it('passes title to _documentProps', async () => {
|
|
@@ -158,7 +158,7 @@ describe('DocTable attrs', () => {
|
|
|
158
158
|
it('sets tag to table', async () => {
|
|
159
159
|
const DocTable = (await import('../primitives/DocTable')).default
|
|
160
160
|
const result = renderProps(DocTable, { children: null })
|
|
161
|
-
expect(result.
|
|
161
|
+
expect(result.as).toBe('table')
|
|
162
162
|
})
|
|
163
163
|
|
|
164
164
|
it('defaults columns and rows to empty arrays', async () => {
|
|
@@ -195,13 +195,13 @@ describe('DocList attrs', () => {
|
|
|
195
195
|
it('sets tag to ul by default', async () => {
|
|
196
196
|
const DocList = (await import('../primitives/DocList')).default
|
|
197
197
|
const result = renderProps(DocList, { children: null })
|
|
198
|
-
expect(result.
|
|
198
|
+
expect(result.as).toBe('ul')
|
|
199
199
|
})
|
|
200
200
|
|
|
201
201
|
it('sets tag to ol when ordered is true', async () => {
|
|
202
202
|
const DocList = (await import('../primitives/DocList')).default
|
|
203
203
|
const result = renderProps(DocList, { ordered: true, children: null })
|
|
204
|
-
expect(result.
|
|
204
|
+
expect(result.as).toBe('ol')
|
|
205
205
|
expect(result._documentProps.ordered).toBe(true)
|
|
206
206
|
})
|
|
207
207
|
|
|
@@ -278,7 +278,7 @@ describe('DocPage attrs', () => {
|
|
|
278
278
|
it('sets tag to div', async () => {
|
|
279
279
|
const DocPage = (await import('../primitives/DocPage')).default
|
|
280
280
|
const result = renderProps(DocPage, { children: 'page' })
|
|
281
|
-
expect(result.
|
|
281
|
+
expect(result.as).toBe('div')
|
|
282
282
|
})
|
|
283
283
|
|
|
284
284
|
it('passes size and orientation when provided', async () => {
|
|
@@ -306,7 +306,7 @@ describe('DocPageBreak attrs', () => {
|
|
|
306
306
|
it('sets tag to div with empty _documentProps', async () => {
|
|
307
307
|
const DocPageBreak = (await import('../primitives/DocPageBreak')).default
|
|
308
308
|
const result = renderProps(DocPageBreak, { children: null })
|
|
309
|
-
expect(result.
|
|
309
|
+
expect(result.as).toBe('div')
|
|
310
310
|
expect(result._documentProps).toEqual({})
|
|
311
311
|
})
|
|
312
312
|
})
|
|
@@ -318,7 +318,7 @@ describe('DocQuote attrs', () => {
|
|
|
318
318
|
it('sets tag to blockquote', async () => {
|
|
319
319
|
const DocQuote = (await import('../primitives/DocQuote')).default
|
|
320
320
|
const result = renderProps(DocQuote, { children: 'quote' })
|
|
321
|
-
expect(result.
|
|
321
|
+
expect(result.as).toBe('blockquote')
|
|
322
322
|
})
|
|
323
323
|
|
|
324
324
|
it('passes borderColor when provided', async () => {
|
|
@@ -341,7 +341,7 @@ describe('DocRow attrs', () => {
|
|
|
341
341
|
it('sets tag to div with empty _documentProps', async () => {
|
|
342
342
|
const DocRow = (await import('../primitives/DocRow')).default
|
|
343
343
|
const result = renderProps(DocRow, { children: null })
|
|
344
|
-
expect(result.
|
|
344
|
+
expect(result.as).toBe('div')
|
|
345
345
|
expect(result._documentProps).toEqual({})
|
|
346
346
|
})
|
|
347
347
|
})
|
|
@@ -353,7 +353,7 @@ describe('DocColumn attrs', () => {
|
|
|
353
353
|
it('sets tag to div', async () => {
|
|
354
354
|
const DocColumn = (await import('../primitives/DocColumn')).default
|
|
355
355
|
const result = renderProps(DocColumn, { children: null })
|
|
356
|
-
expect(result.
|
|
356
|
+
expect(result.as).toBe('div')
|
|
357
357
|
})
|
|
358
358
|
|
|
359
359
|
it('passes width to _documentProps when provided', async () => {
|
|
@@ -376,7 +376,7 @@ describe('DocSpacer attrs', () => {
|
|
|
376
376
|
it('sets tag to div', async () => {
|
|
377
377
|
const DocSpacer = (await import('../primitives/DocSpacer')).default
|
|
378
378
|
const result = renderProps(DocSpacer, { children: null })
|
|
379
|
-
expect(result.
|
|
379
|
+
expect(result.as).toBe('div')
|
|
380
380
|
})
|
|
381
381
|
|
|
382
382
|
it('defaults height to 16', async () => {
|
|
@@ -399,7 +399,7 @@ describe('DocSection attrs', () => {
|
|
|
399
399
|
it('sets tag to div', async () => {
|
|
400
400
|
const DocSection = (await import('../primitives/DocSection')).default
|
|
401
401
|
const result = renderProps(DocSection, { children: null })
|
|
402
|
-
expect(result.
|
|
402
|
+
expect(result.as).toBe('div')
|
|
403
403
|
})
|
|
404
404
|
|
|
405
405
|
it('defaults direction to column', async () => {
|
|
@@ -445,7 +445,7 @@ describe('DocumentPreview attrs', () => {
|
|
|
445
445
|
it('sets tag to div', async () => {
|
|
446
446
|
const DocumentPreview = (await import('../DocumentPreview')).default
|
|
447
447
|
const result = renderProps(DocumentPreview, { children: null })
|
|
448
|
-
expect(result.
|
|
448
|
+
expect(result.as).toBe('div')
|
|
449
449
|
})
|
|
450
450
|
|
|
451
451
|
it('defaults size to A4 when not provided', async () => {
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import type { DocumentMarker } from '@pyreon/connector-document'
|
|
2
|
+
import { h } from '@pyreon/core'
|
|
2
3
|
import { describe, expect, it } from 'vitest'
|
|
3
4
|
import { createDocumentExport, extractDocNode } from '../useDocumentExport'
|
|
4
5
|
|
|
5
|
-
//
|
|
6
|
-
|
|
6
|
+
// Helper: build a real VNode via @pyreon/core's h(). The third arg
|
|
7
|
+
// is an array (kept for parity with the prior mock helper) and
|
|
8
|
+
// spreads into h()'s varargs. Tests run real-h() VNodes through
|
|
9
|
+
// the export pipeline — the mock-only path masked PR #197's silent
|
|
10
|
+
// metadata drop bug.
|
|
11
|
+
const node = (
|
|
7
12
|
type: string | ((...args: any[]) => any),
|
|
8
13
|
props: Record<string, any> = {},
|
|
9
14
|
children: unknown[] = [],
|
|
10
|
-
) => (
|
|
15
|
+
) => h(type as any, props, ...(children as any[])) as any
|
|
11
16
|
|
|
12
|
-
//
|
|
17
|
+
// Document-marked component
|
|
13
18
|
const docComponent = (docType: string) => {
|
|
14
|
-
const fn = (props: any) =>
|
|
19
|
+
const fn = (props: any) => node('div', props, props.children ? [props.children] : [])
|
|
15
20
|
;(fn as any)._documentType = docType
|
|
16
21
|
return fn as ((...args: any[]) => any) & DocumentMarker
|
|
17
22
|
}
|
|
@@ -23,8 +28,8 @@ const DocText = docComponent('text')
|
|
|
23
28
|
describe('createDocumentExport', () => {
|
|
24
29
|
it('extracts a document tree from template function', () => {
|
|
25
30
|
const doc = createDocumentExport(() =>
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
node(DocDocument, { _documentProps: { title: 'Test' } }, [
|
|
32
|
+
node(
|
|
28
33
|
DocHeading,
|
|
29
34
|
{
|
|
30
35
|
$rocketstyle: { fontSize: 24, fontWeight: 'bold' },
|
|
@@ -32,7 +37,7 @@ describe('createDocumentExport', () => {
|
|
|
32
37
|
},
|
|
33
38
|
['Hello'],
|
|
34
39
|
),
|
|
35
|
-
|
|
40
|
+
node(
|
|
36
41
|
DocText,
|
|
37
42
|
{
|
|
38
43
|
$rocketstyle: { fontSize: 14, color: '#333' },
|
|
@@ -62,7 +67,7 @@ describe('createDocumentExport', () => {
|
|
|
62
67
|
|
|
63
68
|
it('can be called multiple times', () => {
|
|
64
69
|
const doc = createDocumentExport(() =>
|
|
65
|
-
|
|
70
|
+
node(DocText, { $rocketstyle: { fontSize: 14 } }, ['Static']),
|
|
66
71
|
)
|
|
67
72
|
|
|
68
73
|
const tree1 = doc.getDocNode()
|
|
@@ -74,7 +79,7 @@ describe('createDocumentExport', () => {
|
|
|
74
79
|
|
|
75
80
|
it('respects includeStyles option', () => {
|
|
76
81
|
const doc = createDocumentExport(
|
|
77
|
-
() =>
|
|
82
|
+
() => node(DocHeading, { $rocketstyle: { fontSize: 24 } }, ['Hello']),
|
|
78
83
|
{ includeStyles: false },
|
|
79
84
|
)
|
|
80
85
|
|
|
@@ -99,8 +104,8 @@ describe('extractDocNode (one-step alias)', () => {
|
|
|
99
104
|
|
|
100
105
|
it('extracts a tree from a template function in one call', () => {
|
|
101
106
|
const tree = extractDocNode(() =>
|
|
102
|
-
|
|
103
|
-
|
|
107
|
+
node(DocDocument, { _documentProps: { title: 'Test' } }, [
|
|
108
|
+
node(DocHeading, { _documentProps: { level: 1 } }, ['Hello']),
|
|
104
109
|
]),
|
|
105
110
|
)
|
|
106
111
|
|
|
@@ -114,7 +119,7 @@ describe('extractDocNode (one-step alias)', () => {
|
|
|
114
119
|
|
|
115
120
|
it('respects extraction options (includeStyles: false)', () => {
|
|
116
121
|
const tree = extractDocNode(
|
|
117
|
-
() =>
|
|
122
|
+
() => node(DocHeading, { $rocketstyle: { fontSize: 24 } }, ['Hello']),
|
|
118
123
|
{ includeStyles: false },
|
|
119
124
|
)
|
|
120
125
|
expect(tree.styles).toBeUndefined()
|
|
@@ -125,7 +130,7 @@ describe('extractDocNode (one-step alias)', () => {
|
|
|
125
130
|
// one-step form internally, so they MUST produce identical
|
|
126
131
|
// output for identical input.
|
|
127
132
|
const template = () =>
|
|
128
|
-
|
|
133
|
+
node(DocText, { $rocketstyle: { fontSize: 14 } }, ['Hello'])
|
|
129
134
|
|
|
130
135
|
const oneStep = extractDocNode(template)
|
|
131
136
|
const twoStep = createDocumentExport(template).getDocNode()
|
|
@@ -150,12 +155,12 @@ describe('extractDocNode (one-step alias)', () => {
|
|
|
150
155
|
// catch the regression. The three extractions should be deeply
|
|
151
156
|
// equal under any change to the primitive's setup path.
|
|
152
157
|
const template = () =>
|
|
153
|
-
|
|
158
|
+
node(
|
|
154
159
|
DocDocument,
|
|
155
160
|
{ _documentProps: { title: 'Idempotent', author: 'Test' } },
|
|
156
161
|
[
|
|
157
|
-
|
|
158
|
-
|
|
162
|
+
node(DocHeading, { _documentProps: { level: 1 } }, ['Hello']),
|
|
163
|
+
node(DocText, { $rocketstyle: { fontSize: 14 } }, ['World']),
|
|
159
164
|
],
|
|
160
165
|
)
|
|
161
166
|
|
|
@@ -174,7 +179,7 @@ describe('extractDocNode (one-step alias)', () => {
|
|
|
174
179
|
// value of any accessor in _documentProps.
|
|
175
180
|
let counter = 0
|
|
176
181
|
const template = () =>
|
|
177
|
-
|
|
182
|
+
node(
|
|
178
183
|
DocDocument,
|
|
179
184
|
{
|
|
180
185
|
_documentProps: {
|
|
@@ -312,4 +317,50 @@ describe('DocDocument reactive metadata (D1 integration)', () => {
|
|
|
312
317
|
cleanup()
|
|
313
318
|
}
|
|
314
319
|
})
|
|
320
|
+
|
|
321
|
+
it('extractDocNode does NOT invoke a real DocDocument component (T3.1 Path C)', { timeout: 30_000 }, async () => {
|
|
322
|
+
// The architectural invariant locked in by T3.1 (PR #321):
|
|
323
|
+
// `extractDocumentTree` consumes a real rocketstyle primitive's
|
|
324
|
+
// `__rs_attrs` chain directly, never invoking the wrapped component
|
|
325
|
+
// function. The previous Path B workaround had to call the full
|
|
326
|
+
// styled wrapper per export to read post-attrs `_documentProps`.
|
|
327
|
+
//
|
|
328
|
+
// Spy mechanism: wrap the imported component in a Proxy whose
|
|
329
|
+
// `apply` trap counts function-call invocations. Property reads
|
|
330
|
+
// (IS_ROCKETSTYLE, __rs_attrs, _documentType, .meta, .displayName,
|
|
331
|
+
// etc.) flow through to the original via the default `get` handler,
|
|
332
|
+
// so extractDocumentTree's contract still works — but any code
|
|
333
|
+
// path that CALLS the spied component bumps the counter.
|
|
334
|
+
//
|
|
335
|
+
// The connector-document tests already pin this with a
|
|
336
|
+
// `FakeRocketDoc` fixture; this test pins it for a real
|
|
337
|
+
// rocketstyle primitive end-to-end, closing the abstract /
|
|
338
|
+
// concrete coverage gap.
|
|
339
|
+
const { initTestConfig } = await import('@pyreon/test-utils')
|
|
340
|
+
const { h } = await import('@pyreon/core')
|
|
341
|
+
const cleanup = initTestConfig()
|
|
342
|
+
try {
|
|
343
|
+
const RealDocDocument = (await import('../primitives/DocDocument')).default
|
|
344
|
+
|
|
345
|
+
let callCount = 0
|
|
346
|
+
const SpiedDoc = new Proxy(RealDocDocument, {
|
|
347
|
+
apply(target, thisArg, args) {
|
|
348
|
+
callCount++
|
|
349
|
+
return Reflect.apply(target as (...a: unknown[]) => unknown, thisArg, args as unknown[])
|
|
350
|
+
},
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
const tree = extractDocNode(() =>
|
|
354
|
+
h(SpiedDoc as never, { title: 'Hoisted', author: 'Aisha' } as never),
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
expect(tree.type).toBe('document')
|
|
358
|
+
expect(tree.props.title).toBe('Hoisted')
|
|
359
|
+
expect(tree.props.author).toBe('Aisha')
|
|
360
|
+
// The architectural assertion — the styled wrapper must NOT run.
|
|
361
|
+
expect(callCount).toBe(0)
|
|
362
|
+
} finally {
|
|
363
|
+
cleanup()
|
|
364
|
+
}
|
|
365
|
+
})
|
|
315
366
|
})
|