@knpkv/confluence-to-markdown 0.2.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +62 -0
  3. package/dist/Brand.d.ts +77 -0
  4. package/dist/Brand.d.ts.map +1 -0
  5. package/dist/Brand.js +44 -0
  6. package/dist/Brand.js.map +1 -0
  7. package/dist/ConfluenceClient.d.ts +140 -0
  8. package/dist/ConfluenceClient.d.ts.map +1 -0
  9. package/dist/ConfluenceClient.js +195 -0
  10. package/dist/ConfluenceClient.js.map +1 -0
  11. package/dist/ConfluenceConfig.d.ts +83 -0
  12. package/dist/ConfluenceConfig.d.ts.map +1 -0
  13. package/dist/ConfluenceConfig.js +122 -0
  14. package/dist/ConfluenceConfig.js.map +1 -0
  15. package/dist/ConfluenceError.d.ts +178 -0
  16. package/dist/ConfluenceError.d.ts.map +1 -0
  17. package/dist/ConfluenceError.js +131 -0
  18. package/dist/ConfluenceError.js.map +1 -0
  19. package/dist/LocalFileSystem.d.ts +85 -0
  20. package/dist/LocalFileSystem.d.ts.map +1 -0
  21. package/dist/LocalFileSystem.js +101 -0
  22. package/dist/LocalFileSystem.js.map +1 -0
  23. package/dist/MarkdownConverter.d.ts +50 -0
  24. package/dist/MarkdownConverter.d.ts.map +1 -0
  25. package/dist/MarkdownConverter.js +151 -0
  26. package/dist/MarkdownConverter.js.map +1 -0
  27. package/dist/Schemas.d.ts +225 -0
  28. package/dist/Schemas.d.ts.map +1 -0
  29. package/dist/Schemas.js +164 -0
  30. package/dist/Schemas.js.map +1 -0
  31. package/dist/SyncEngine.d.ts +132 -0
  32. package/dist/SyncEngine.d.ts.map +1 -0
  33. package/dist/SyncEngine.js +267 -0
  34. package/dist/SyncEngine.js.map +1 -0
  35. package/dist/bin.d.ts +3 -0
  36. package/dist/bin.d.ts.map +1 -0
  37. package/dist/bin.js +163 -0
  38. package/dist/bin.js.map +1 -0
  39. package/dist/index.d.ts +16 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +16 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/internal/frontmatter.d.ts +38 -0
  44. package/dist/internal/frontmatter.d.ts.map +1 -0
  45. package/dist/internal/frontmatter.js +69 -0
  46. package/dist/internal/frontmatter.js.map +1 -0
  47. package/dist/internal/hashUtils.d.ts +11 -0
  48. package/dist/internal/hashUtils.d.ts.map +1 -0
  49. package/dist/internal/hashUtils.js +17 -0
  50. package/dist/internal/hashUtils.js.map +1 -0
  51. package/dist/internal/pathUtils.d.ts +41 -0
  52. package/dist/internal/pathUtils.d.ts.map +1 -0
  53. package/dist/internal/pathUtils.js +69 -0
  54. package/dist/internal/pathUtils.js.map +1 -0
  55. package/package.json +113 -0
  56. package/src/Brand.ts +104 -0
  57. package/src/ConfluenceClient.ts +387 -0
  58. package/src/ConfluenceConfig.ts +184 -0
  59. package/src/ConfluenceError.ts +193 -0
  60. package/src/LocalFileSystem.ts +225 -0
  61. package/src/MarkdownConverter.ts +187 -0
  62. package/src/Schemas.ts +235 -0
  63. package/src/SyncEngine.ts +429 -0
  64. package/src/bin.ts +269 -0
  65. package/src/index.ts +35 -0
  66. package/src/internal/frontmatter.ts +98 -0
  67. package/src/internal/hashUtils.ts +19 -0
  68. package/src/internal/pathUtils.ts +77 -0
  69. package/test/Brand.test.ts +72 -0
  70. package/test/MarkdownConverter.test.ts +108 -0
  71. package/test/Schemas.test.ts +99 -0
  72. package/tsconfig.json +32 -0
  73. package/vitest.config.integration.ts +12 -0
  74. package/vitest.config.ts +13 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Front-matter parsing and serialization utilities.
3
+ *
4
+ * @module
5
+ * @internal
6
+ */
7
+ import * as Effect from "effect/Effect";
8
+ import { FrontMatterError } from "../ConfluenceError.js";
9
+ import type { NewPageFrontMatter, PageFrontMatter } from "../Schemas.js";
10
+ /**
11
+ * Parsed markdown file with front-matter.
12
+ */
13
+ export interface ParsedMarkdown {
14
+ readonly frontMatter: PageFrontMatter | NewPageFrontMatter | null;
15
+ readonly content: string;
16
+ readonly isNew: boolean;
17
+ }
18
+ /**
19
+ * Parse a markdown file with YAML front-matter.
20
+ *
21
+ * @param filePath - Path to the file (for error messages)
22
+ * @param content - The file content
23
+ * @returns Parsed markdown with front-matter and content
24
+ *
25
+ * @internal
26
+ */
27
+ export declare const parseMarkdown: (filePath: string, content: string) => Effect.Effect<ParsedMarkdown, FrontMatterError>;
28
+ /**
29
+ * Serialize markdown with YAML front-matter.
30
+ *
31
+ * @param frontMatter - The front-matter data
32
+ * @param content - The markdown content
33
+ * @returns The serialized markdown file content
34
+ *
35
+ * @internal
36
+ */
37
+ export declare const serializeMarkdown: (frontMatter: PageFrontMatter, content: string) => string;
38
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/internal/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGxE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,EAAE,eAAe,GAAG,kBAAkB,GAAG,IAAI,CAAA;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,GACxB,UAAU,MAAM,EAChB,SAAS,MAAM,KACd,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,gBAAgB,CAqC7C,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,GAC5B,aAAa,eAAe,EAC5B,SAAS,MAAM,KACd,MAYF,CAAA"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Front-matter parsing and serialization utilities.
3
+ *
4
+ * @module
5
+ * @internal
6
+ */
7
+ import * as Effect from "effect/Effect";
8
+ import * as Schema from "effect/Schema";
9
+ import matter from "gray-matter";
10
+ import { FrontMatterError } from "../ConfluenceError.js";
11
+ import { NewPageFrontMatterSchema, PageFrontMatterSchema } from "../Schemas.js";
12
+ /**
13
+ * Parse a markdown file with YAML front-matter.
14
+ *
15
+ * @param filePath - Path to the file (for error messages)
16
+ * @param content - The file content
17
+ * @returns Parsed markdown with front-matter and content
18
+ *
19
+ * @internal
20
+ */
21
+ export const parseMarkdown = (filePath, content) => Effect.gen(function* () {
22
+ const parsed = yield* Effect.try({
23
+ try: () => matter(content),
24
+ catch: (cause) => new FrontMatterError({ path: filePath, cause })
25
+ });
26
+ // If no front-matter or empty, treat as new page
27
+ if (!parsed.data || Object.keys(parsed.data).length === 0) {
28
+ return {
29
+ frontMatter: null,
30
+ content: parsed.content.trim(),
31
+ isNew: true
32
+ };
33
+ }
34
+ // Try to parse as existing page front-matter
35
+ const existingResult = yield* Schema.decodeUnknown(PageFrontMatterSchema)(parsed.data).pipe(Effect.map((fm) => ({
36
+ frontMatter: fm,
37
+ content: parsed.content.trim(),
38
+ isNew: false
39
+ })), Effect.catchAll(() =>
40
+ // Try to parse as new page front-matter
41
+ Schema.decodeUnknown(NewPageFrontMatterSchema)(parsed.data).pipe(Effect.map((fm) => ({
42
+ frontMatter: fm,
43
+ content: parsed.content.trim(),
44
+ isNew: true
45
+ })), Effect.catchAll((cause) => Effect.fail(new FrontMatterError({ path: filePath, cause }))))));
46
+ return existingResult;
47
+ });
48
+ /**
49
+ * Serialize markdown with YAML front-matter.
50
+ *
51
+ * @param frontMatter - The front-matter data
52
+ * @param content - The markdown content
53
+ * @returns The serialized markdown file content
54
+ *
55
+ * @internal
56
+ */
57
+ export const serializeMarkdown = (frontMatter, content) => {
58
+ const fm = {
59
+ pageId: frontMatter.pageId,
60
+ version: frontMatter.version,
61
+ title: frontMatter.title,
62
+ updated: frontMatter.updated.toISOString(),
63
+ ...(frontMatter.parentId !== undefined ? { parentId: frontMatter.parentId } : {}),
64
+ ...(frontMatter.position !== undefined ? { position: frontMatter.position } : {}),
65
+ contentHash: frontMatter.contentHash
66
+ };
67
+ return matter.stringify(content, fm);
68
+ };
69
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/internal/frontmatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ContentHash } from "../Brand.js";
2
+ /**
3
+ * Compute SHA256 hash of content.
4
+ *
5
+ * @param content - The content to hash
6
+ * @returns The hex-encoded SHA256 hash
7
+ *
8
+ * @internal
9
+ */
10
+ export declare const computeHash: (content: string) => ContentHash;
11
+ //# sourceMappingURL=hashUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashUtils.d.ts","sourceRoot":"","sources":["../../src/internal/hashUtils.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM,KAAG,WACoC,CAAA"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Content hashing utilities for change detection.
3
+ *
4
+ * @module
5
+ * @internal
6
+ */
7
+ import * as crypto from "node:crypto";
8
+ /**
9
+ * Compute SHA256 hash of content.
10
+ *
11
+ * @param content - The content to hash
12
+ * @returns The hex-encoded SHA256 hash
13
+ *
14
+ * @internal
15
+ */
16
+ export const computeHash = (content) => crypto.createHash("sha256").update(content, "utf8").digest("hex");
17
+ //# sourceMappingURL=hashUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashUtils.js","sourceRoot":"","sources":["../../src/internal/hashUtils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAGrC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAe,EAAe,EAAE,CAC1D,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAgB,CAAA"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Convert a page title to a URL-safe slug.
3
+ * Prevents path traversal by only allowing alphanumeric characters and hyphens.
4
+ *
5
+ * @param title - The page title
6
+ * @returns A slugified version of the title
7
+ *
8
+ * @internal
9
+ */
10
+ export declare const slugify: (title: string) => string;
11
+ /**
12
+ * Convert a page to a local file path.
13
+ *
14
+ * @param title - The page title
15
+ * @param hasChildren - Whether the page has child pages
16
+ * @param parentPath - The parent directory path
17
+ * @returns The local file path for the page
18
+ *
19
+ * @internal
20
+ */
21
+ export declare const pageToPath: (title: string, hasChildren: boolean, parentPath: string) => string;
22
+ /**
23
+ * Get the directory path for a page (used when creating children).
24
+ *
25
+ * @param title - The page title
26
+ * @param parentPath - The parent directory path
27
+ * @returns The directory path for the page's children
28
+ *
29
+ * @internal
30
+ */
31
+ export declare const pageToDir: (title: string, parentPath: string) => string;
32
+ /**
33
+ * Extract page slug from a file path.
34
+ *
35
+ * @param filePath - The file path
36
+ * @returns The page slug
37
+ *
38
+ * @internal
39
+ */
40
+ export declare const pathToSlug: (filePath: string) => string;
41
+ //# sourceMappingURL=pathUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pathUtils.d.ts","sourceRoot":"","sources":["../../src/internal/pathUtils.ts"],"names":[],"mappings":"AAQA;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,MAWvC,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,MAAM,EACb,aAAa,OAAO,EACpB,YAAY,MAAM,KACjB,MAKF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,EAAE,YAAY,MAAM,KAAG,MAG7D,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,KAAG,MAG7C,CAAA"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Path utilities for converting page titles to file paths.
3
+ *
4
+ * @module
5
+ * @internal
6
+ */
7
+ import * as path from "node:path";
8
+ /**
9
+ * Convert a page title to a URL-safe slug.
10
+ * Prevents path traversal by only allowing alphanumeric characters and hyphens.
11
+ *
12
+ * @param title - The page title
13
+ * @returns A slugified version of the title
14
+ *
15
+ * @internal
16
+ */
17
+ export const slugify = (title) => {
18
+ const slug = title
19
+ .toLowerCase()
20
+ .normalize("NFD")
21
+ .replace(/[\u0300-\u036f]/g, "") // Remove diacritics
22
+ .replace(/[^a-z0-9]+/g, "-") // Replace non-alphanumeric with hyphens (prevents ../ path traversal)
23
+ .replace(/^-+|-+$/g, "") // Trim leading/trailing hyphens
24
+ .substring(0, 100); // Limit length
25
+ // Ensure we have a valid slug (not empty after sanitization)
26
+ return slug || "untitled";
27
+ };
28
+ /**
29
+ * Convert a page to a local file path.
30
+ *
31
+ * @param title - The page title
32
+ * @param hasChildren - Whether the page has child pages
33
+ * @param parentPath - The parent directory path
34
+ * @returns The local file path for the page
35
+ *
36
+ * @internal
37
+ */
38
+ export const pageToPath = (title, hasChildren, parentPath) => {
39
+ const slug = slugify(title);
40
+ return hasChildren
41
+ ? path.join(parentPath, slug, "index.md")
42
+ : path.join(parentPath, `${slug}.md`);
43
+ };
44
+ /**
45
+ * Get the directory path for a page (used when creating children).
46
+ *
47
+ * @param title - The page title
48
+ * @param parentPath - The parent directory path
49
+ * @returns The directory path for the page's children
50
+ *
51
+ * @internal
52
+ */
53
+ export const pageToDir = (title, parentPath) => {
54
+ const slug = slugify(title);
55
+ return path.join(parentPath, slug);
56
+ };
57
+ /**
58
+ * Extract page slug from a file path.
59
+ *
60
+ * @param filePath - The file path
61
+ * @returns The page slug
62
+ *
63
+ * @internal
64
+ */
65
+ export const pathToSlug = (filePath) => {
66
+ const basename = path.basename(filePath, ".md");
67
+ return basename === "index" ? path.basename(path.dirname(filePath)) : basename;
68
+ };
69
+ //# sourceMappingURL=pathUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pathUtils.js","sourceRoot":"","sources":["../../src/internal/pathUtils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAa,EAAU,EAAE;IAC/C,MAAM,IAAI,GAAG,KAAK;SACf,WAAW,EAAE;SACb,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,oBAAoB;SACpD,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,sEAAsE;SAClG,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,gCAAgC;SACxD,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA,CAAC,eAAe;IAEpC,6DAA6D;IAC7D,OAAO,IAAI,IAAI,UAAU,CAAA;AAC3B,CAAC,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,KAAa,EACb,WAAoB,EACpB,UAAkB,EACV,EAAE;IACV,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IAC3B,OAAO,WAAW;QAChB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC;QACzC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,IAAI,KAAK,CAAC,CAAA;AACzC,CAAC,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,UAAkB,EAAU,EAAE;IACrE,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;AACpC,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAAgB,EAAU,EAAE;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC/C,OAAO,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;AAChF,CAAC,CAAA"}
package/package.json ADDED
@@ -0,0 +1,113 @@
1
+ {
2
+ "name": "@knpkv/confluence-to-markdown",
3
+ "version": "0.2.0",
4
+ "description": "Sync Confluence Cloud pages to local GitHub Flavored Markdown files",
5
+ "license": "MIT",
6
+ "author": "knpkv",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/knpkv/npm.git",
10
+ "directory": "packages/confluence-to-markdown"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/knpkv/npm/issues"
14
+ },
15
+ "homepage": "https://github.com/knpkv/npm/tree/main/packages/confluence-to-markdown#readme",
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "bin": {
21
+ "confluence": "./dist/bin.js"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "import": "./dist/index.js",
26
+ "types": "./dist/index.d.ts"
27
+ },
28
+ "./Brand": {
29
+ "import": "./dist/Brand.js",
30
+ "types": "./dist/Brand.d.ts"
31
+ },
32
+ "./Schemas": {
33
+ "import": "./dist/Schemas.js",
34
+ "types": "./dist/Schemas.d.ts"
35
+ },
36
+ "./ConfluenceError": {
37
+ "import": "./dist/ConfluenceError.js",
38
+ "types": "./dist/ConfluenceError.d.ts"
39
+ },
40
+ "./ConfluenceConfig": {
41
+ "import": "./dist/ConfluenceConfig.js",
42
+ "types": "./dist/ConfluenceConfig.d.ts"
43
+ },
44
+ "./ConfluenceAuth": {
45
+ "import": "./dist/ConfluenceAuth.js",
46
+ "types": "./dist/ConfluenceAuth.d.ts"
47
+ },
48
+ "./ConfluenceClient": {
49
+ "import": "./dist/ConfluenceClient.js",
50
+ "types": "./dist/ConfluenceClient.d.ts"
51
+ },
52
+ "./MarkdownConverter": {
53
+ "import": "./dist/MarkdownConverter.js",
54
+ "types": "./dist/MarkdownConverter.d.ts"
55
+ },
56
+ "./LocalFileSystem": {
57
+ "import": "./dist/LocalFileSystem.js",
58
+ "types": "./dist/LocalFileSystem.d.ts"
59
+ },
60
+ "./SyncEngine": {
61
+ "import": "./dist/SyncEngine.js",
62
+ "types": "./dist/SyncEngine.d.ts"
63
+ }
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "scripts": {
69
+ "build": "tsc",
70
+ "clean": "rimraf dist dist-test .tsbuildinfo",
71
+ "test": "vitest run",
72
+ "test:watch": "vitest",
73
+ "test:integration": "vitest run --config vitest.config.integration.ts",
74
+ "lint": "eslint src",
75
+ "lint:fix": "eslint src --fix",
76
+ "check": "tsc --noEmit",
77
+ "link": "npm link"
78
+ },
79
+ "peerDependencies": {
80
+ "effect": "^3.19.3"
81
+ },
82
+ "dependencies": {
83
+ "@effect/cli": "^0.61.0",
84
+ "@effect/platform": "^0.93.0",
85
+ "@effect/platform-node": "^0.87.0",
86
+ "gray-matter": "^4.0.3",
87
+ "rehype-parse": "^9.0.1",
88
+ "rehype-remark": "^10.0.1",
89
+ "rehype-stringify": "^10.0.1",
90
+ "remark-gfm": "^4.0.1",
91
+ "remark-parse": "^11.0.0",
92
+ "remark-rehype": "^11.1.1",
93
+ "remark-stringify": "^11.0.0",
94
+ "unified": "^11.0.5"
95
+ },
96
+ "devDependencies": {
97
+ "@effect/vitest": "^0.27.0",
98
+ "@types/node": "^22.15.21",
99
+ "effect": "^3.19.3",
100
+ "typescript": "~5.9.0",
101
+ "vitest": "^4.0.13"
102
+ },
103
+ "keywords": [
104
+ "effect",
105
+ "effect-ts",
106
+ "confluence",
107
+ "markdown",
108
+ "sync",
109
+ "atlassian",
110
+ "documentation",
111
+ "cli"
112
+ ]
113
+ }
package/src/Brand.ts ADDED
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Branded types for type-safe Confluence identifiers.
3
+ *
4
+ * @module
5
+ */
6
+ import * as Brand from "effect/Brand"
7
+ import * as Schema from "effect/Schema"
8
+
9
+ /**
10
+ * Branded type for Confluence page IDs.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { PageId } from "@knpkv/confluence-to-markdown/Brand"
15
+ *
16
+ * const id = PageId("12345") // Valid
17
+ * const invalid = PageId("") // Throws BrandErrors
18
+ * ```
19
+ *
20
+ * @category Brand
21
+ */
22
+ export type PageId = string & Brand.Brand<"PageId">
23
+
24
+ /**
25
+ * Refined brand constructor for PageId.
26
+ *
27
+ * @category Brand
28
+ */
29
+ export const PageId = Brand.refined<PageId>(
30
+ (s): s is PageId => typeof s === "string" && s.length > 0,
31
+ (s) => Brand.error(`Invalid page ID: "${s}" (must be non-empty string)`)
32
+ )
33
+
34
+ /**
35
+ * Schema for PageId validation and parsing.
36
+ *
37
+ * @category Schema
38
+ */
39
+ export const PageIdSchema = Schema.String.pipe(
40
+ Schema.nonEmptyString(),
41
+ Schema.brand("PageId")
42
+ )
43
+
44
+ /**
45
+ * Branded type for Confluence space keys.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * import { SpaceKey } from "@knpkv/confluence-to-markdown/Brand"
50
+ *
51
+ * const key = SpaceKey("DOCS") // Valid
52
+ * ```
53
+ *
54
+ * @category Brand
55
+ */
56
+ export type SpaceKey = string & Brand.Brand<"SpaceKey">
57
+
58
+ /**
59
+ * Refined brand constructor for SpaceKey.
60
+ *
61
+ * @category Brand
62
+ */
63
+ export const SpaceKey = Brand.refined<SpaceKey>(
64
+ (s): s is SpaceKey => typeof s === "string" && s.length > 0 && /^[A-Z0-9]+$/.test(s),
65
+ (s) => Brand.error(`Invalid space key: "${s}" (must be uppercase alphanumeric)`)
66
+ )
67
+
68
+ /**
69
+ * Schema for SpaceKey validation.
70
+ *
71
+ * @category Schema
72
+ */
73
+ export const SpaceKeySchema = Schema.String.pipe(
74
+ Schema.nonEmptyString(),
75
+ Schema.pattern(/^[A-Z0-9]+$/),
76
+ Schema.brand("SpaceKey")
77
+ )
78
+
79
+ /**
80
+ * Branded type for content hash (SHA256).
81
+ *
82
+ * @category Brand
83
+ */
84
+ export type ContentHash = string & Brand.Brand<"ContentHash">
85
+
86
+ /**
87
+ * Refined brand constructor for ContentHash.
88
+ *
89
+ * @category Brand
90
+ */
91
+ export const ContentHash = Brand.refined<ContentHash>(
92
+ (s): s is ContentHash => typeof s === "string" && /^[a-f0-9]{64}$/.test(s),
93
+ (s) => Brand.error(`Invalid content hash: "${s}" (must be 64-char hex string)`)
94
+ )
95
+
96
+ /**
97
+ * Schema for ContentHash validation.
98
+ *
99
+ * @category Schema
100
+ */
101
+ export const ContentHashSchema = Schema.String.pipe(
102
+ Schema.pattern(/^[a-f0-9]{64}$/),
103
+ Schema.brand("ContentHash")
104
+ )