@knpkv/confluence-to-markdown 0.4.2 → 0.6.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/CHANGELOG.md +35 -0
- package/README.md +45 -10
- package/dist/ConfluenceAuth.d.ts.map +1 -1
- package/dist/ConfluenceAuth.js +12 -22
- package/dist/ConfluenceAuth.js.map +1 -1
- package/dist/ConfluenceClient.d.ts +13 -3
- package/dist/ConfluenceClient.d.ts.map +1 -1
- package/dist/ConfluenceClient.js +34 -70
- package/dist/ConfluenceClient.js.map +1 -1
- package/dist/ConfluenceError.d.ts +12 -12
- package/dist/GitError.d.ts +5 -5
- package/dist/GitService.d.ts.map +1 -1
- package/dist/GitService.js +0 -3
- package/dist/GitService.js.map +1 -1
- package/dist/SchemaConverterError.d.ts +3 -3
- package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts +23 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts.map +1 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.js +323 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.js.map +1 -0
- package/dist/parsers/preprocessing/index.d.ts +7 -0
- package/dist/parsers/preprocessing/index.d.ts.map +1 -0
- package/dist/parsers/preprocessing/index.js +7 -0
- package/dist/parsers/preprocessing/index.js.map +1 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +29 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +1 -1
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js +3 -5
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +1 -1
- package/package.json +35 -26
- package/src/AdfPlaceholders.ts +266 -0
- package/src/AdfSchemaValidator.ts +67 -0
- package/src/AdfWalker.ts +511 -0
- package/src/AtlaskitTransformers.ts +72 -0
- package/src/ConfluenceClient.ts +4 -4
- package/src/ConfluenceError.ts +65 -3
- package/src/MarkdownConverter.ts +106 -139
- package/src/Schemas.ts +4 -4
- package/src/SyncEngine.ts +130 -83
- package/src/atlaskit-adf-schema.d.ts +3 -0
- package/src/commands/clone.ts +8 -1
- package/src/commands/layers.ts +11 -4
- package/src/index.ts +3 -18
- package/test/AdfPlaceholders.test.ts +295 -0
- package/test/AdfSchemaValidator.test.ts +34 -0
- package/test/AdfWalker.test.ts +530 -0
- package/test/AtlaskitTransformers.test.ts +25 -0
- package/test/MarkdownConverter.test.ts +120 -105
- package/test/RoundTrip.test.ts +266 -0
- package/LICENSE +0 -21
- package/src/SchemaConverterError.ts +0 -108
- package/src/ast/BlockNode.ts +0 -425
- package/src/ast/Document.ts +0 -90
- package/src/ast/InlineNode.ts +0 -323
- package/src/ast/MacroNode.ts +0 -245
- package/src/ast/index.ts +0 -83
- package/src/parsers/ConfluenceParser.ts +0 -950
- package/src/parsers/MarkdownParser.ts +0 -1198
- package/src/parsers/index.ts +0 -8
- package/src/schemas/ConfluenceSchema.ts +0 -56
- package/src/schemas/ConversionSchema.ts +0 -318
- package/src/schemas/MarkdownSchema.ts +0 -56
- package/src/schemas/hast/HastFromHtml.ts +0 -153
- package/src/schemas/hast/HastSchema.ts +0 -274
- package/src/schemas/hast/index.ts +0 -35
- package/src/schemas/index.ts +0 -20
- package/src/schemas/mdast/MdastFromMarkdown.ts +0 -118
- package/src/schemas/mdast/MdastSchema.ts +0 -566
- package/src/schemas/mdast/index.ts +0 -59
- package/src/schemas/mdast/mdastToString.ts +0 -102
- package/src/schemas/nodes/block/BlockSchema.ts +0 -773
- package/src/schemas/nodes/block/index.ts +0 -13
- package/src/schemas/nodes/index.ts +0 -20
- package/src/schemas/nodes/inline/InlineSchema.ts +0 -523
- package/src/schemas/nodes/inline/index.ts +0 -14
- package/src/schemas/nodes/macro/MacroSchema.ts +0 -226
- package/src/schemas/nodes/macro/index.ts +0 -6
- package/src/schemas/preprocessing/ConfluencePreprocessor.ts +0 -446
- package/src/schemas/preprocessing/index.ts +0 -8
- package/src/serializers/ConfluenceSerializer.ts +0 -717
- package/src/serializers/MarkdownSerializer.ts +0 -493
- package/src/serializers/index.ts +0 -8
- package/test/ast/BlockNode.test.ts +0 -265
- package/test/ast/Document.test.ts +0 -126
- package/test/ast/InlineNode.test.ts +0 -161
- package/test/fixtures/integration-test.html.fixture +0 -103
- package/test/fixtures/integration-test.md.expected +0 -257
- package/test/parsers/ConfluenceParser.test.ts +0 -283
- package/test/schemas/ConfluencePreprocessor.test.ts +0 -180
- package/test/schemas/ConversionSchema.test.ts +0 -159
- package/test/schemas/HastSchema.test.ts +0 -138
- package/test/schemas/MdastSchema.test.ts +0 -145
- package/test/schemas/nodes/block/BlockSchema.test.ts +0 -173
- package/test/schemas/nodes/inline/InlineSchema.test.ts +0 -198
- package/test/schemas/nodes/macro/MacroSchema.test.ts +0 -142
package/src/ConfluenceError.ts
CHANGED
|
@@ -124,16 +124,23 @@ export class RateLimitError extends Data.TaggedError("RateLimitError")<{
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
|
-
*
|
|
127
|
+
* Direction of an ADF/Markdown conversion.
|
|
128
|
+
*
|
|
129
|
+
* @category Errors
|
|
130
|
+
*/
|
|
131
|
+
export type ConversionDirection = "adfToMarkdown" | "markdownToAdf"
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Error thrown when ADF/Markdown conversion fails.
|
|
128
135
|
*
|
|
129
136
|
* @category Errors
|
|
130
137
|
*/
|
|
131
138
|
export class ConversionError extends Data.TaggedError("ConversionError")<{
|
|
132
|
-
readonly direction:
|
|
139
|
+
readonly direction: ConversionDirection
|
|
133
140
|
readonly cause: unknown
|
|
134
141
|
readonly message: string
|
|
135
142
|
}> {
|
|
136
|
-
constructor(params: { direction:
|
|
143
|
+
constructor(params: { direction: ConversionDirection; cause: unknown }) {
|
|
137
144
|
super({
|
|
138
145
|
direction: params.direction,
|
|
139
146
|
cause: params.cause,
|
|
@@ -142,6 +149,57 @@ export class ConversionError extends Data.TaggedError("ConversionError")<{
|
|
|
142
149
|
}
|
|
143
150
|
}
|
|
144
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Issue produced by ADF JSON Schema validation.
|
|
154
|
+
*
|
|
155
|
+
* @category Errors
|
|
156
|
+
*/
|
|
157
|
+
export interface AdfSchemaIssue {
|
|
158
|
+
readonly instancePath?: string
|
|
159
|
+
readonly schemaPath?: string
|
|
160
|
+
readonly keyword?: string
|
|
161
|
+
readonly message?: string
|
|
162
|
+
readonly params?: Record<string, unknown>
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Error thrown when an ADF document fails JSON Schema validation.
|
|
167
|
+
*
|
|
168
|
+
* @category Errors
|
|
169
|
+
*/
|
|
170
|
+
export class AdfSchemaError extends Data.TaggedError("AdfSchemaError")<{
|
|
171
|
+
readonly direction: "incoming" | "outgoing"
|
|
172
|
+
readonly issues: ReadonlyArray<AdfSchemaIssue>
|
|
173
|
+
readonly message: string
|
|
174
|
+
}> {
|
|
175
|
+
constructor(params: { direction: "incoming" | "outgoing"; issues: ReadonlyArray<AdfSchemaIssue> }) {
|
|
176
|
+
super({
|
|
177
|
+
direction: params.direction,
|
|
178
|
+
issues: params.issues,
|
|
179
|
+
message: `ADF schema validation failed (${params.direction}): ${params.issues.length} issue(s)`
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Error thrown when the wrapped @atlaskit transformer libraries throw.
|
|
186
|
+
*
|
|
187
|
+
* @category Errors
|
|
188
|
+
*/
|
|
189
|
+
export class AtlaskitTransformersError extends Data.TaggedError("AtlaskitTransformersError")<{
|
|
190
|
+
readonly cause: unknown
|
|
191
|
+
readonly message: string
|
|
192
|
+
}> {
|
|
193
|
+
constructor(params: { cause: unknown }) {
|
|
194
|
+
super({
|
|
195
|
+
cause: params.cause,
|
|
196
|
+
message: `Atlaskit transformer failed: ${
|
|
197
|
+
params.cause instanceof Error ? params.cause.message : String(params.cause)
|
|
198
|
+
}`
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
145
203
|
/**
|
|
146
204
|
* Error thrown when sync conflict is detected.
|
|
147
205
|
*
|
|
@@ -255,6 +313,8 @@ export type ConfluenceError =
|
|
|
255
313
|
| ApiError
|
|
256
314
|
| RateLimitError
|
|
257
315
|
| ConversionError
|
|
316
|
+
| AdfSchemaError
|
|
317
|
+
| AtlaskitTransformersError
|
|
258
318
|
| ConflictError
|
|
259
319
|
| FileSystemError
|
|
260
320
|
| OAuthError
|
|
@@ -281,6 +341,8 @@ export const isConfluenceError = (error: unknown): error is ConfluenceError =>
|
|
|
281
341
|
"ApiError",
|
|
282
342
|
"RateLimitError",
|
|
283
343
|
"ConversionError",
|
|
344
|
+
"AdfSchemaError",
|
|
345
|
+
"AtlaskitTransformersError",
|
|
284
346
|
"ConflictError",
|
|
285
347
|
"FileSystemError",
|
|
286
348
|
"OAuthError",
|
package/src/MarkdownConverter.ts
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ADF ↔ Markdown conversion facade.
|
|
3
|
+
*
|
|
4
|
+
* Push (markdown → ADF) routes through the official `@atlaskit` markdown
|
|
5
|
+
* + JSON transformers and is strictly validated by `AdfSchemaValidator` —
|
|
6
|
+
* we author that side, so schema failures are bugs. Pull (ADF → markdown)
|
|
7
|
+
* routes through the in-package `AdfWalker`; incoming validation is advisory
|
|
8
|
+
* (logged, not thrown) because Confluence Cloud routinely emits documents the
|
|
9
|
+
* canonical schema lags behind.
|
|
3
10
|
*
|
|
4
11
|
* @module
|
|
5
12
|
*/
|
|
13
|
+
import type { DocNode } from "@atlaskit/adf-schema"
|
|
6
14
|
import * as Context from "effect/Context"
|
|
7
15
|
import * as Effect from "effect/Effect"
|
|
8
16
|
import * as Layer from "effect/Layer"
|
|
9
17
|
import * as Schema from "effect/Schema"
|
|
10
|
-
import
|
|
18
|
+
import { revertPlaceholders } from "./AdfPlaceholders.js"
|
|
19
|
+
import { AdfSchemaValidator } from "./AdfSchemaValidator.js"
|
|
20
|
+
import { walk, type WalkerWarning } from "./AdfWalker.js"
|
|
21
|
+
import { AtlaskitTransformers } from "./AtlaskitTransformers.js"
|
|
22
|
+
import type { AdfSchemaError, AtlaskitTransformersError } from "./ConfluenceError.js"
|
|
11
23
|
import { ConversionError } from "./ConfluenceError.js"
|
|
12
|
-
import { parseConfluenceHtml } from "./parsers/ConfluenceParser.js"
|
|
13
|
-
import { parseMarkdown } from "./parsers/MarkdownParser.js"
|
|
14
|
-
import { ParseError, type SerializeError } from "./SchemaConverterError.js"
|
|
15
|
-
import { ConfluenceToMarkdown, DocumentFromHast, DocumentFromMdast } from "./schemas/ConversionSchema.js"
|
|
16
|
-
import { HastFromHtml } from "./schemas/hast/index.js"
|
|
17
|
-
import { MdastFromMarkdown } from "./schemas/mdast/index.js"
|
|
18
|
-
import { PreprocessedHtmlFromConfluence } from "./schemas/preprocessing/index.js"
|
|
19
|
-
import { serializeToConfluence } from "./serializers/ConfluenceSerializer.js"
|
|
20
|
-
import { type SerializeOptions, serializeToMarkdown } from "./serializers/MarkdownSerializer.js"
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
|
-
* Markdown conversion service
|
|
26
|
+
* Markdown conversion service. Public surface is two delegating methods —
|
|
27
|
+
* `adfToMarkdown` (pull) and `markdownToAdf` (push).
|
|
24
28
|
*
|
|
25
29
|
* @example
|
|
26
30
|
* ```typescript
|
|
@@ -29,13 +33,8 @@ import { type SerializeOptions, serializeToMarkdown } from "./serializers/Markdo
|
|
|
29
33
|
*
|
|
30
34
|
* const program = Effect.gen(function* () {
|
|
31
35
|
* const converter = yield* MarkdownConverter
|
|
32
|
-
* const md = yield* converter.
|
|
33
|
-
* console.log(md) // # Hello\n\nWorld
|
|
36
|
+
* const md = yield* converter.adfToMarkdown(adfJson)
|
|
34
37
|
* })
|
|
35
|
-
*
|
|
36
|
-
* Effect.runPromise(
|
|
37
|
-
* program.pipe(Effect.provide(MarkdownConverter.layer))
|
|
38
|
-
* )
|
|
39
38
|
* ```
|
|
40
39
|
*
|
|
41
40
|
* @category Conversion
|
|
@@ -46,146 +45,114 @@ export class MarkdownConverter extends Context.Tag(
|
|
|
46
45
|
MarkdownConverter,
|
|
47
46
|
{
|
|
48
47
|
/**
|
|
49
|
-
* Convert
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
options?: SerializeOptions
|
|
54
|
-
) => Effect.Effect<string, ConversionError>
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Convert GitHub Flavored Markdown to HTML (Confluence storage format).
|
|
58
|
-
*/
|
|
59
|
-
readonly markdownToHtml: (markdown: string) => Effect.Effect<string, ConversionError>
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Parse Confluence HTML to Document AST.
|
|
63
|
-
*/
|
|
64
|
-
readonly htmlToAst: (html: string) => Effect.Effect<Document, ParseError>
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Parse Markdown to Document AST.
|
|
68
|
-
*/
|
|
69
|
-
readonly markdownToAst: (markdown: string) => Effect.Effect<Document, ParseError>
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Serialize Document AST to Confluence HTML.
|
|
48
|
+
* Convert an ADF JSON document (as wire-format string) to GitHub Flavored
|
|
49
|
+
* Markdown. Checks against the canonical @atlaskit/adf-schema before
|
|
50
|
+
* walking; violations are logged as warnings (remote drift), and only a
|
|
51
|
+
* document too malformed to walk at all fails with ConversionError.
|
|
73
52
|
*/
|
|
74
|
-
readonly
|
|
53
|
+
readonly adfToMarkdown: (adfJson: string) => Effect.Effect<string, ConversionError>
|
|
75
54
|
|
|
76
55
|
/**
|
|
77
|
-
*
|
|
56
|
+
* Convert GitHub Flavored Markdown to a JSON-stringified ADF document.
|
|
57
|
+
* Routes through the official @atlaskit transformers; validates the
|
|
58
|
+
* produced ADF against the canonical schema before stringification.
|
|
78
59
|
*/
|
|
79
|
-
readonly
|
|
60
|
+
readonly markdownToAdf: (markdown: string) => Effect.Effect<string, ConversionError>
|
|
80
61
|
}
|
|
81
62
|
>() {}
|
|
82
63
|
|
|
64
|
+
const warningSummary = (w: WalkerWarning): string => {
|
|
65
|
+
switch (w._tag) {
|
|
66
|
+
case "UnsupportedNode":
|
|
67
|
+
return `${w._tag} ${w.nodeType}`
|
|
68
|
+
case "LossyMark":
|
|
69
|
+
return `${w._tag} ${w.mark}`
|
|
70
|
+
case "MediaWithoutUrl":
|
|
71
|
+
return `${w._tag} ${w.mediaId}`
|
|
72
|
+
case "UnsupportedExtension":
|
|
73
|
+
return `${w._tag} ${w.nodeType} ${w.extensionKey || "?"}`
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const toConversionError = (
|
|
78
|
+
direction: "adfToMarkdown" | "markdownToAdf"
|
|
79
|
+
) =>
|
|
80
|
+
(cause: AdfSchemaError | AtlaskitTransformersError | { readonly cause: unknown }): ConversionError =>
|
|
81
|
+
new ConversionError({ direction, cause })
|
|
82
|
+
|
|
83
|
+
// The least structure the walker needs: a doc node with a content array.
|
|
84
|
+
// Anything failing this isn't "schema drift", it's not an ADF document —
|
|
85
|
+
// advisory validation must not let it through (walking `null` is a defect;
|
|
86
|
+
// walking `{}` silently produces an empty page).
|
|
87
|
+
const isWalkableDoc = Schema.is(Schema.Struct({
|
|
88
|
+
type: Schema.Literal("doc"),
|
|
89
|
+
content: Schema.Array(Schema.Unknown)
|
|
90
|
+
}))
|
|
91
|
+
|
|
83
92
|
/**
|
|
84
93
|
* Layer that provides the MarkdownConverter service.
|
|
85
94
|
*
|
|
86
95
|
* @category Layers
|
|
87
96
|
*/
|
|
88
|
-
export const layer: Layer.Layer<
|
|
97
|
+
export const layer: Layer.Layer<
|
|
89
98
|
MarkdownConverter,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return yield* serializeToMarkdown(doc, options).pipe(
|
|
98
|
-
Effect.mapError((e) => new ConversionError({ direction: "htmlToMarkdown", cause: e.message }))
|
|
99
|
-
)
|
|
100
|
-
}),
|
|
99
|
+
never,
|
|
100
|
+
AtlaskitTransformers | AdfSchemaValidator
|
|
101
|
+
> = Layer.effect(
|
|
102
|
+
MarkdownConverter,
|
|
103
|
+
Effect.gen(function*() {
|
|
104
|
+
const transformers = yield* AtlaskitTransformers
|
|
105
|
+
const validator = yield* AdfSchemaValidator
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
const adfToMarkdown = (adfJson: string): Effect.Effect<string, ConversionError> =>
|
|
103
108
|
Effect.gen(function*() {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
const raw = yield* Effect.try({
|
|
110
|
+
try: () => JSON.parse(adfJson),
|
|
111
|
+
catch: (cause) => new ConversionError({ direction: "adfToMarkdown", cause })
|
|
112
|
+
})
|
|
113
|
+
// Incoming validation is advisory: the canonical @atlaskit schema is
|
|
114
|
+
// routinely stricter than what Confluence Cloud actually emits (old
|
|
115
|
+
// revisions, experimental nodes), so a failure here usually means
|
|
116
|
+
// "schema drift", not "broken document". Log + continue. Outgoing
|
|
117
|
+
// validation stays strict because we control that side.
|
|
118
|
+
const doc = yield* validator.check(raw, "incoming").pipe(
|
|
119
|
+
Effect.catchTag("AdfSchemaError", (err) =>
|
|
120
|
+
isWalkableDoc(raw)
|
|
121
|
+
? Effect.gen(function*() {
|
|
122
|
+
yield* Effect.logWarning(
|
|
123
|
+
`adf schema (incoming): ${err.issues.length} issue(s); first: ${
|
|
124
|
+
err.issues.slice(0, 3).map((i) =>
|
|
125
|
+
`${i.instancePath ?? "?"} ${i.keyword ?? "?"} ${i.message ?? ""}`.trim()
|
|
126
|
+
).join(" | ")
|
|
127
|
+
}`
|
|
128
|
+
)
|
|
129
|
+
return raw as DocNode
|
|
130
|
+
})
|
|
131
|
+
: Effect.fail(toConversionError("adfToMarkdown")(err)))
|
|
107
132
|
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}),
|
|
112
|
-
|
|
113
|
-
htmlToAst: (html) => parseConfluenceHtml(html),
|
|
114
|
-
|
|
115
|
-
markdownToAst: (markdown) => parseMarkdown(markdown),
|
|
116
|
-
|
|
117
|
-
astToHtml: (doc) => serializeToConfluence(doc),
|
|
118
|
-
|
|
119
|
-
astToMarkdown: (doc) => serializeToMarkdown(doc)
|
|
120
|
-
})
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Schema-based layer for MarkdownConverter using Effect Schema transforms.
|
|
125
|
-
*
|
|
126
|
-
* This is an alternative implementation that uses the new Schema-based
|
|
127
|
-
* conversion pipeline. It provides the same API as the default layer.
|
|
128
|
-
*
|
|
129
|
-
* Note: For full fidelity, continue to use the default layer. This schema-based
|
|
130
|
-
* layer is useful for simpler use cases or when you want to leverage Schema
|
|
131
|
-
* composition.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```typescript
|
|
135
|
-
* import { MarkdownConverter, schemaBasedLayer } from "@knpkv/confluence-to-markdown/MarkdownConverter"
|
|
136
|
-
* import { Effect } from "effect"
|
|
137
|
-
*
|
|
138
|
-
* const program = Effect.gen(function* () {
|
|
139
|
-
* const converter = yield* MarkdownConverter
|
|
140
|
-
* const md = yield* converter.htmlToMarkdown("<h1>Hello</h1>")
|
|
141
|
-
* })
|
|
142
|
-
*
|
|
143
|
-
* Effect.runPromise(
|
|
144
|
-
* program.pipe(Effect.provide(schemaBasedLayer))
|
|
145
|
-
* )
|
|
146
|
-
* ```
|
|
147
|
-
*
|
|
148
|
-
* @category Layers
|
|
149
|
-
*/
|
|
150
|
-
export const schemaBasedLayer: Layer.Layer<MarkdownConverter> = Layer.succeed(
|
|
151
|
-
MarkdownConverter,
|
|
152
|
-
MarkdownConverter.of({
|
|
153
|
-
// Note: Schema-based layer doesn't support includeRawSource option yet
|
|
154
|
-
htmlToMarkdown: (html, options) =>
|
|
155
|
-
Effect.gen(function*() {
|
|
156
|
-
if (options?.includeRawSource !== undefined) {
|
|
157
|
-
yield* Effect.logWarning("schemaBasedLayer: includeRawSource option is not supported, use default layer")
|
|
133
|
+
const { markdown, warnings } = walk(doc)
|
|
134
|
+
for (const w of warnings) {
|
|
135
|
+
yield* Effect.logWarning(`adf walker: ${warningSummary(w)}`, w)
|
|
158
136
|
}
|
|
159
|
-
return
|
|
160
|
-
})
|
|
161
|
-
Effect.mapError((e) => new ConversionError({ direction: "htmlToMarkdown", cause: e.message }))
|
|
162
|
-
),
|
|
137
|
+
return markdown
|
|
138
|
+
})
|
|
163
139
|
|
|
164
|
-
|
|
165
|
-
Schema.encode(ConfluenceToMarkdown)(markdown).pipe(
|
|
166
|
-
Effect.mapError((e) => new ConversionError({ direction: "markdownToHtml", cause: e.message }))
|
|
167
|
-
),
|
|
168
|
-
|
|
169
|
-
htmlToAst: (html) =>
|
|
170
|
-
Effect.gen(function*() {
|
|
171
|
-
const preprocessed = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
|
|
172
|
-
const hast = yield* Schema.decode(HastFromHtml)(preprocessed)
|
|
173
|
-
return yield* Schema.decode(DocumentFromHast)(hast)
|
|
174
|
-
}).pipe(
|
|
175
|
-
Effect.mapError((e) => new ParseError({ source: "confluence", message: e.message }))
|
|
176
|
-
),
|
|
177
|
-
|
|
178
|
-
markdownToAst: (markdown) =>
|
|
140
|
+
const markdownToAdf = (markdown: string): Effect.Effect<string, ConversionError> =>
|
|
179
141
|
Effect.gen(function*() {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
142
|
+
const adf = yield* transformers.use(({ json, md }) => json.encode(md.parse(markdown))).pipe(
|
|
143
|
+
Effect.mapError(toConversionError("markdownToAdf"))
|
|
144
|
+
)
|
|
145
|
+
// The walker emits HTML/comment placeholders for Confluence-only nodes
|
|
146
|
+
// (status, extension). @atlaskit's markdown transformer doesn't know
|
|
147
|
+
// these, so they come through as plain text. Rewrite them back into
|
|
148
|
+
// the structured nodes Confluence expects before validation.
|
|
149
|
+
const reverted = revertPlaceholders(adf)
|
|
150
|
+
const validated = yield* validator.check(reverted, "outgoing").pipe(
|
|
151
|
+
Effect.mapError(toConversionError("markdownToAdf"))
|
|
152
|
+
)
|
|
153
|
+
return JSON.stringify(validated)
|
|
154
|
+
})
|
|
188
155
|
|
|
189
|
-
|
|
156
|
+
return MarkdownConverter.of({ adfToMarkdown, markdownToAdf })
|
|
190
157
|
})
|
|
191
158
|
)
|
package/src/Schemas.ts
CHANGED
|
@@ -163,7 +163,7 @@ export const PageResponseSchema = Schema.Struct({
|
|
|
163
163
|
}),
|
|
164
164
|
body: Schema.optional(
|
|
165
165
|
Schema.Struct({
|
|
166
|
-
|
|
166
|
+
atlas_doc_format: Schema.optional(
|
|
167
167
|
Schema.Struct({
|
|
168
168
|
value: Schema.String,
|
|
169
169
|
representation: Schema.optional(Schema.String)
|
|
@@ -204,7 +204,7 @@ export const PageListItemSchema = Schema.Struct({
|
|
|
204
204
|
})),
|
|
205
205
|
body: Schema.optional(
|
|
206
206
|
Schema.Struct({
|
|
207
|
-
|
|
207
|
+
atlas_doc_format: Schema.optional(
|
|
208
208
|
Schema.Struct({
|
|
209
209
|
value: Schema.String,
|
|
210
210
|
representation: Schema.optional(Schema.String)
|
|
@@ -377,7 +377,7 @@ export const PageVersionSchema = Schema.Struct({
|
|
|
377
377
|
title: Schema.optional(Schema.String),
|
|
378
378
|
body: Schema.optional(
|
|
379
379
|
Schema.Struct({
|
|
380
|
-
|
|
380
|
+
atlas_doc_format: Schema.optional(
|
|
381
381
|
Schema.Struct({
|
|
382
382
|
value: Schema.String,
|
|
383
383
|
representation: Schema.optional(Schema.String)
|
|
@@ -413,7 +413,7 @@ export const PageVersionContentSchema = Schema.Struct({
|
|
|
413
413
|
/** Page content */
|
|
414
414
|
body: Schema.optional(
|
|
415
415
|
Schema.Struct({
|
|
416
|
-
|
|
416
|
+
atlas_doc_format: Schema.optional(
|
|
417
417
|
Schema.Struct({
|
|
418
418
|
value: Schema.String,
|
|
419
419
|
representation: Schema.optional(Schema.String)
|