@iwonz/ywtc 0.0.0-stage → 2.0.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/.env.example +4 -0
- package/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +259 -2
- package/dist/adapters/confluence.d.ts +46 -0
- package/dist/adapters/confluence.js +187 -0
- package/dist/adapters/confluence.js.map +1 -0
- package/dist/adapters/factory.d.ts +3 -0
- package/dist/adapters/factory.js +11 -0
- package/dist/adapters/factory.js.map +1 -0
- package/dist/adapters/yandex.d.ts +45 -0
- package/dist/adapters/yandex.js +233 -0
- package/dist/adapters/yandex.js.map +1 -0
- package/dist/cli-arguments.d.ts +41 -0
- package/dist/cli-arguments.js +149 -0
- package/dist/cli-arguments.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +180 -0
- package/dist/cli.js.map +1 -0
- package/dist/clients/confluence-cloud.d.ts +45 -0
- package/dist/clients/confluence-cloud.js +261 -0
- package/dist/clients/confluence-cloud.js.map +1 -0
- package/dist/clients/confluence-factory.d.ts +3 -0
- package/dist/clients/confluence-factory.js +8 -0
- package/dist/clients/confluence-factory.js.map +1 -0
- package/dist/clients/confluence.d.ts +42 -0
- package/dist/clients/confluence.js +286 -0
- package/dist/clients/confluence.js.map +1 -0
- package/dist/clients/yandex-wiki.d.ts +35 -0
- package/dist/clients/yandex-wiki.js +255 -0
- package/dist/clients/yandex-wiki.js.map +1 -0
- package/dist/config.d.ts +52 -0
- package/dist/config.js +177 -0
- package/dist/config.js.map +1 -0
- package/dist/content-model.d.ts +12 -0
- package/dist/content-model.js +416 -0
- package/dist/content-model.js.map +1 -0
- package/dist/conversion.d.ts +43 -0
- package/dist/conversion.js +450 -0
- package/dist/conversion.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.js +45 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain.d.ts +202 -0
- package/dist/domain.js +18 -0
- package/dist/domain.js.map +1 -0
- package/dist/filesystem-export.d.ts +53 -0
- package/dist/filesystem-export.js +243 -0
- package/dist/filesystem-export.js.map +1 -0
- package/dist/graph.d.ts +13 -0
- package/dist/graph.js +121 -0
- package/dist/graph.js.map +1 -0
- package/dist/http.d.ts +26 -0
- package/dist/http.js +100 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +4 -0
- package/dist/input.js +56 -0
- package/dist/input.js.map +1 -0
- package/dist/locators.d.ts +24 -0
- package/dist/locators.js +84 -0
- package/dist/locators.js.map +1 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.js +28 -0
- package/dist/logger.js.map +1 -0
- package/dist/migrator.d.ts +53 -0
- package/dist/migrator.js +669 -0
- package/dist/migrator.js.map +1 -0
- package/dist/rate-limiter.d.ts +12 -0
- package/dist/rate-limiter.js +31 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/report.d.ts +101 -0
- package/dist/report.js +106 -0
- package/dist/report.js.map +1 -0
- package/dist/state.d.ts +119 -0
- package/dist/state.js +201 -0
- package/dist/state.js.map +1 -0
- package/dist/types.d.ts +197 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/updater.d.ts +24 -0
- package/dist/updater.js +92 -0
- package/dist/updater.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/dist/workspace.d.ts +29 -0
- package/dist/workspace.js +157 -0
- package/dist/workspace.js.map +1 -0
- package/docs/assets/readme-header.png +0 -0
- package/package.json +110 -4
- package/ywtc.config.example.yaml +52 -0
package/dist/graph.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
function byCanonicalPage(left, right) {
|
|
2
|
+
return left.canonicalId.localeCompare(right.canonicalId);
|
|
3
|
+
}
|
|
4
|
+
function edgeStatusForReference(reference) {
|
|
5
|
+
if (reference.kind === 'external' || reference.kind === 'attachment' || reference.kind === 'anchor')
|
|
6
|
+
return 'external';
|
|
7
|
+
return reference.kind === 'unsupported' ? 'unsupported' : 'broken';
|
|
8
|
+
}
|
|
9
|
+
export async function discoverPageGraph(input) {
|
|
10
|
+
const { adapter, root, scope, signal, onProgress } = input;
|
|
11
|
+
const selected = new Map([[root.canonicalId, root]]);
|
|
12
|
+
const edges = [];
|
|
13
|
+
const warnings = [];
|
|
14
|
+
if (scope === 'subtree') {
|
|
15
|
+
const descendants = (await adapter.getDescendants(root)).sort(byCanonicalPage);
|
|
16
|
+
for (const page of descendants) {
|
|
17
|
+
if (signal?.aborted)
|
|
18
|
+
throw signal.reason ?? new Error('Discovery cancelled');
|
|
19
|
+
selected.set(page.canonicalId, page);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else if (scope === 'linked') {
|
|
23
|
+
const queue = [root];
|
|
24
|
+
let queueIndex = 0;
|
|
25
|
+
const expanded = new Set();
|
|
26
|
+
while (queueIndex < queue.length) {
|
|
27
|
+
if (signal?.aborted)
|
|
28
|
+
throw signal.reason ?? new Error('Discovery cancelled');
|
|
29
|
+
const page = queue[queueIndex++];
|
|
30
|
+
if (expanded.has(page.canonicalId))
|
|
31
|
+
continue;
|
|
32
|
+
expanded.add(page.canonicalId);
|
|
33
|
+
const references = [...page.references].sort((left, right) => left.raw.localeCompare(right.raw));
|
|
34
|
+
for (const reference of references) {
|
|
35
|
+
if (reference.kind !== 'page') {
|
|
36
|
+
edges.push({ from: page.canonicalId, raw: reference.raw, status: edgeStatusForReference(reference) });
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (reference.href && /^https?:\/\//i.test(reference.href)) {
|
|
40
|
+
const target = new URL(reference.href);
|
|
41
|
+
const origin = new URL(page.url).origin;
|
|
42
|
+
if (target.origin !== origin) {
|
|
43
|
+
edges.push({ from: page.canonicalId, raw: reference.raw, status: 'external' });
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const resolved = await adapter.resolveReference(page, reference);
|
|
49
|
+
if (!resolved) {
|
|
50
|
+
edges.push({ from: page.canonicalId, raw: reference.raw, status: 'broken' });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const alreadySelected = selected.has(resolved.canonicalId);
|
|
54
|
+
edges.push({
|
|
55
|
+
from: page.canonicalId,
|
|
56
|
+
to: resolved.canonicalId,
|
|
57
|
+
raw: reference.raw,
|
|
58
|
+
status: alreadySelected && expanded.has(resolved.canonicalId) ? 'cycle' : 'resolved',
|
|
59
|
+
});
|
|
60
|
+
if (!alreadySelected) {
|
|
61
|
+
selected.set(resolved.canonicalId, resolved);
|
|
62
|
+
queue.push(resolved);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
+
const inaccessible = /\b(?:401|403|forbidden|permission|access)\b/i.test(message);
|
|
68
|
+
edges.push({ from: page.canonicalId, raw: reference.raw, status: inaccessible ? 'inaccessible' : 'broken', message });
|
|
69
|
+
warnings.push(`${page.title}: cannot follow ${reference.raw}: ${message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
onProgress?.({ pages: selected.size, edges: edges.length, active: page.title });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const selectedByProviderId = new Map([...selected.values()].map((page) => [page.id, page]));
|
|
76
|
+
const projected = new Map();
|
|
77
|
+
projected.set(root.canonicalId, undefined);
|
|
78
|
+
for (const page of selected.values()) {
|
|
79
|
+
if (page.canonicalId === root.canonicalId)
|
|
80
|
+
continue;
|
|
81
|
+
const sourceParent = page.parentId ? selectedByProviderId.get(page.parentId) : undefined;
|
|
82
|
+
projected.set(page.canonicalId, sourceParent?.canonicalId ?? root.canonicalId);
|
|
83
|
+
}
|
|
84
|
+
// A malformed vendor hierarchy must never make creation order impossible.
|
|
85
|
+
// Break the first cycle encountered from each node by attaching that node to
|
|
86
|
+
// the selected root; link-graph cycles remain recorded independently above.
|
|
87
|
+
for (const page of selected.values()) {
|
|
88
|
+
if (page.canonicalId === root.canonicalId)
|
|
89
|
+
continue;
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
let current = page.canonicalId;
|
|
92
|
+
while (current && current !== root.canonicalId) {
|
|
93
|
+
if (seen.has(current)) {
|
|
94
|
+
projected.set(page.canonicalId, root.canonicalId);
|
|
95
|
+
warnings.push(`Hierarchy cycle detected at ${page.title}; attached below the root.`);
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
seen.add(current);
|
|
99
|
+
current = projected.get(current);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const depths = new Map([[root.canonicalId, 0]]);
|
|
103
|
+
const depthOf = (id) => {
|
|
104
|
+
if (id === root.canonicalId)
|
|
105
|
+
return 0;
|
|
106
|
+
const cached = depths.get(id);
|
|
107
|
+
if (cached !== undefined)
|
|
108
|
+
return cached;
|
|
109
|
+
const parent = projected.get(id);
|
|
110
|
+
const depth = parent ? depthOf(parent) + 1 : 0;
|
|
111
|
+
depths.set(id, depth);
|
|
112
|
+
return depth;
|
|
113
|
+
};
|
|
114
|
+
const pages = [...selected.values()].map((page) => {
|
|
115
|
+
const projectedParentId = projected.get(page.canonicalId);
|
|
116
|
+
return { ...page, depth: depthOf(page.canonicalId), ...(projectedParentId ? { projectedParentId } : {}) };
|
|
117
|
+
}).sort((left, right) => left.depth - right.depth || left.canonicalId.localeCompare(right.canonicalId));
|
|
118
|
+
onProgress?.({ pages: pages.length, edges: edges.length });
|
|
119
|
+
return { rootId: root.canonicalId, pages, edges, warnings };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type AxiosRequestConfig, type AxiosResponse } from 'axios';
|
|
2
|
+
import type { RequestRateLimiter } from './rate-limiter.js';
|
|
3
|
+
export interface RetryingHttpClientOptions {
|
|
4
|
+
baseURL?: string;
|
|
5
|
+
timeoutMs: number;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
maxRetries?: number;
|
|
8
|
+
rateLimiter?: RequestRateLimiter;
|
|
9
|
+
}
|
|
10
|
+
export type RetryingRequestConfig = AxiosRequestConfig & {
|
|
11
|
+
/** Unsafe methods are not retried by default because their outcome can be ambiguous. */
|
|
12
|
+
retryMode?: 'safe' | 'always' | 'never';
|
|
13
|
+
};
|
|
14
|
+
export declare class HttpRequestError extends Error {
|
|
15
|
+
readonly status?: number | undefined;
|
|
16
|
+
readonly method?: string | undefined;
|
|
17
|
+
readonly url?: string | undefined;
|
|
18
|
+
constructor(message: string, status?: number | undefined, method?: string | undefined, url?: string | undefined);
|
|
19
|
+
}
|
|
20
|
+
export declare class RetryingHttpClient {
|
|
21
|
+
private readonly client;
|
|
22
|
+
private readonly maxRetries;
|
|
23
|
+
private readonly rateLimiter;
|
|
24
|
+
constructor(options: RetryingHttpClientOptions);
|
|
25
|
+
request<T>(config: RetryingRequestConfig): Promise<AxiosResponse<T>>;
|
|
26
|
+
}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export class HttpRequestError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
method;
|
|
5
|
+
url;
|
|
6
|
+
constructor(message, status, method, url) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.method = method;
|
|
10
|
+
this.url = url;
|
|
11
|
+
this.name = 'HttpRequestError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function retryAfterMs(response) {
|
|
15
|
+
const raw = response?.headers['retry-after'];
|
|
16
|
+
if (raw === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
const seconds = Number(raw);
|
|
19
|
+
if (Number.isFinite(seconds))
|
|
20
|
+
return Math.max(0, seconds * 1_000);
|
|
21
|
+
const date = Date.parse(String(raw));
|
|
22
|
+
return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());
|
|
23
|
+
}
|
|
24
|
+
function isRetriable(error) {
|
|
25
|
+
if (!error.response)
|
|
26
|
+
return error.code !== 'ERR_CANCELED';
|
|
27
|
+
return error.response.status === 429 || error.response.status >= 500;
|
|
28
|
+
}
|
|
29
|
+
function safeErrorMessage(error) {
|
|
30
|
+
const status = error.response?.status;
|
|
31
|
+
const payload = error.response?.data;
|
|
32
|
+
let detail = '';
|
|
33
|
+
if (typeof payload === 'string')
|
|
34
|
+
detail = payload.slice(0, 500);
|
|
35
|
+
else if (payload && typeof payload === 'object') {
|
|
36
|
+
const body = payload;
|
|
37
|
+
detail = String(body.debug_message ?? body.message ?? body.error_code ?? '').slice(0, 500);
|
|
38
|
+
}
|
|
39
|
+
const guidance = status === 401
|
|
40
|
+
? 'authentication rejected; check token and auth type'
|
|
41
|
+
: status === 403
|
|
42
|
+
? 'permission denied; check source visibility and destination page/attachment permissions'
|
|
43
|
+
: status === 404
|
|
44
|
+
? 'resource or API route not found; check URL, page ID, deployment, and API compatibility'
|
|
45
|
+
: status === 409
|
|
46
|
+
? 'destination conflict; retry with the same state after checking the current page version/title'
|
|
47
|
+
: status === 413
|
|
48
|
+
? 'payload too large for the destination; check Confluence attachment limits'
|
|
49
|
+
: status === 429
|
|
50
|
+
? 'rate limited after bounded retries; lower configured RPS/concurrency or retry later'
|
|
51
|
+
: undefined;
|
|
52
|
+
return [`HTTP request failed`, status ? `status=${status}` : undefined, guidance, detail || error.message]
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.join(': ');
|
|
55
|
+
}
|
|
56
|
+
export class RetryingHttpClient {
|
|
57
|
+
client;
|
|
58
|
+
maxRetries;
|
|
59
|
+
rateLimiter;
|
|
60
|
+
constructor(options) {
|
|
61
|
+
this.client = axios.create({
|
|
62
|
+
...(options.baseURL ? { baseURL: options.baseURL } : {}),
|
|
63
|
+
timeout: options.timeoutMs,
|
|
64
|
+
...(options.headers ? { headers: options.headers } : {}),
|
|
65
|
+
maxRedirects: 5,
|
|
66
|
+
validateStatus: (status) => status >= 200 && status < 300,
|
|
67
|
+
});
|
|
68
|
+
this.maxRetries = options.maxRetries ?? 4;
|
|
69
|
+
this.rateLimiter = options.rateLimiter;
|
|
70
|
+
}
|
|
71
|
+
async request(config) {
|
|
72
|
+
const { retryMode = 'safe', ...axiosConfig } = config;
|
|
73
|
+
const method = String(axiosConfig.method ?? 'GET').toUpperCase();
|
|
74
|
+
const canRetry = retryMode === 'always'
|
|
75
|
+
|| (retryMode === 'safe' && ['GET', 'HEAD', 'OPTIONS'].includes(method));
|
|
76
|
+
for (let attempt = 0;; attempt += 1) {
|
|
77
|
+
try {
|
|
78
|
+
await this.rateLimiter?.acquire();
|
|
79
|
+
return await this.client.request(axiosConfig);
|
|
80
|
+
}
|
|
81
|
+
catch (caught) {
|
|
82
|
+
const error = caught;
|
|
83
|
+
if (!axios.isAxiosError(error))
|
|
84
|
+
throw caught;
|
|
85
|
+
const backoff = Math.min(30_000, 500 * 2 ** attempt + Math.floor(Math.random() * 250));
|
|
86
|
+
const headerDelay = retryAfterMs(error.response);
|
|
87
|
+
const delay = error.response?.status === 429
|
|
88
|
+
? Math.max(1_000, headerDelay ?? backoff)
|
|
89
|
+
: Math.min(60_000, headerDelay ?? backoff);
|
|
90
|
+
if (error.response?.status === 429)
|
|
91
|
+
this.rateLimiter?.pause(delay);
|
|
92
|
+
if (!canRetry || attempt >= this.maxRetries || !isRetriable(error)) {
|
|
93
|
+
throw new HttpRequestError(safeErrorMessage(error), error.response?.status, method, String(axiosConfig.url ?? ''));
|
|
94
|
+
}
|
|
95
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export * from './config.js';
|
|
2
|
+
export * from './cli-arguments.js';
|
|
3
|
+
export * from './adapters/confluence.js';
|
|
4
|
+
export * from './adapters/factory.js';
|
|
5
|
+
export * from './adapters/yandex.js';
|
|
6
|
+
export * from './clients/confluence-cloud.js';
|
|
7
|
+
export * from './clients/confluence-factory.js';
|
|
8
|
+
export * from './clients/confluence.js';
|
|
9
|
+
export * from './clients/yandex-wiki.js';
|
|
10
|
+
export * from './conversion.js';
|
|
11
|
+
export * from './content-model.js';
|
|
12
|
+
export * from './domain.js';
|
|
13
|
+
export * from './filesystem-export.js';
|
|
14
|
+
export * from './graph.js';
|
|
15
|
+
export * from './input.js';
|
|
16
|
+
export * from './locators.js';
|
|
17
|
+
export * from './migrator.js';
|
|
18
|
+
export * from './rate-limiter.js';
|
|
19
|
+
export * from './report.js';
|
|
20
|
+
export * from './state.js';
|
|
21
|
+
export * from './types.js';
|
|
22
|
+
export * from './updater.js';
|
|
23
|
+
export * from './version.js';
|
|
24
|
+
export * from './workspace.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export * from './config.js';
|
|
2
|
+
export * from './cli-arguments.js';
|
|
3
|
+
export * from './adapters/confluence.js';
|
|
4
|
+
export * from './adapters/factory.js';
|
|
5
|
+
export * from './adapters/yandex.js';
|
|
6
|
+
export * from './clients/confluence-cloud.js';
|
|
7
|
+
export * from './clients/confluence-factory.js';
|
|
8
|
+
export * from './clients/confluence.js';
|
|
9
|
+
export * from './clients/yandex-wiki.js';
|
|
10
|
+
export * from './conversion.js';
|
|
11
|
+
export * from './content-model.js';
|
|
12
|
+
export * from './domain.js';
|
|
13
|
+
export * from './filesystem-export.js';
|
|
14
|
+
export * from './graph.js';
|
|
15
|
+
export * from './input.js';
|
|
16
|
+
export * from './locators.js';
|
|
17
|
+
export * from './migrator.js';
|
|
18
|
+
export * from './rate-limiter.js';
|
|
19
|
+
export * from './report.js';
|
|
20
|
+
export * from './state.js';
|
|
21
|
+
export * from './types.js';
|
|
22
|
+
export * from './updater.js';
|
|
23
|
+
export * from './version.js';
|
|
24
|
+
export * from './workspace.js';
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
|
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function normalizeWikiSlug(value: string): string;
|
|
2
|
+
export declare function parseWikiSource(input: string, wikiWebUrl: string): string;
|
|
3
|
+
export declare function resolveRelativeWikiSlug(currentSlug: string, hrefPath: string): string;
|
|
4
|
+
export declare function safeFilename(value: string): string;
|
package/dist/input.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
function decodePath(value) {
|
|
3
|
+
try {
|
|
4
|
+
return decodeURIComponent(value);
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function normalizeWikiSlug(value) {
|
|
11
|
+
return decodePath(value)
|
|
12
|
+
.replace(/^\/+|\/+$/g, '')
|
|
13
|
+
.replace(/\/{2,}/g, '/')
|
|
14
|
+
.trim();
|
|
15
|
+
}
|
|
16
|
+
export function parseWikiSource(input, wikiWebUrl) {
|
|
17
|
+
const trimmed = input.trim();
|
|
18
|
+
if (!trimmed)
|
|
19
|
+
throw new Error('Source Wiki slug or URL is required');
|
|
20
|
+
try {
|
|
21
|
+
const source = new URL(trimmed);
|
|
22
|
+
const configured = new URL(wikiWebUrl);
|
|
23
|
+
if (source.host !== configured.host) {
|
|
24
|
+
throw new Error(`Source URL host must match ${configured.host}`);
|
|
25
|
+
}
|
|
26
|
+
const basePath = configured.pathname.replace(/\/+$/, '');
|
|
27
|
+
const pathname = basePath && source.pathname.startsWith(basePath)
|
|
28
|
+
? source.pathname.slice(basePath.length)
|
|
29
|
+
: source.pathname;
|
|
30
|
+
const slug = normalizeWikiSlug(pathname);
|
|
31
|
+
if (!slug)
|
|
32
|
+
throw new Error('Source URL does not contain a Wiki page slug');
|
|
33
|
+
return slug;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (/^[a-z][a-z\d+.-]*:\/\//i.test(trimmed))
|
|
37
|
+
throw error;
|
|
38
|
+
const slug = normalizeWikiSlug(trimmed);
|
|
39
|
+
if (!slug)
|
|
40
|
+
throw new Error('Source Wiki slug is empty');
|
|
41
|
+
return slug;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function resolveRelativeWikiSlug(currentSlug, hrefPath) {
|
|
45
|
+
const decoded = decodePath(hrefPath).replace(/\\/g, '/');
|
|
46
|
+
if (decoded.startsWith('/'))
|
|
47
|
+
return normalizeWikiSlug(decoded);
|
|
48
|
+
const directory = path.posix.dirname(normalizeWikiSlug(currentSlug));
|
|
49
|
+
return normalizeWikiSlug(path.posix.normalize(path.posix.join(directory === '.' ? '' : directory, decoded)));
|
|
50
|
+
}
|
|
51
|
+
export function safeFilename(value) {
|
|
52
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: filesystem control characters must be removed.
|
|
53
|
+
const normalized = path.basename(value).replace(/[\u0000-\u001f<>:"/\\|?*]/g, '_').trim();
|
|
54
|
+
return normalized || 'unnamed-file';
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AppConfig, ConnectionProfile } from './config.js';
|
|
2
|
+
import type { ExportFormat } from './domain.js';
|
|
3
|
+
export interface RemoteLocator {
|
|
4
|
+
type: 'remote';
|
|
5
|
+
value: string;
|
|
6
|
+
profile: ConnectionProfile;
|
|
7
|
+
}
|
|
8
|
+
export interface DirectoryLocator {
|
|
9
|
+
type: 'directory';
|
|
10
|
+
path: string;
|
|
11
|
+
format: ExportFormat;
|
|
12
|
+
}
|
|
13
|
+
export type ResolvedLocator = RemoteLocator | DirectoryLocator;
|
|
14
|
+
export declare function selectRemoteProfile(config: AppConfig, locator: string, requestedProfile: string | undefined, role: 'source' | 'destination'): ConnectionProfile;
|
|
15
|
+
export declare function resolveSourceLocator(config: AppConfig, value: string, profile?: string): RemoteLocator;
|
|
16
|
+
export declare function resolveDestinationLocator(input: {
|
|
17
|
+
config: AppConfig;
|
|
18
|
+
value: string;
|
|
19
|
+
profile?: string;
|
|
20
|
+
format?: ExportFormat;
|
|
21
|
+
cwd?: string;
|
|
22
|
+
}): ResolvedLocator;
|
|
23
|
+
export declare function profileBaseUrl(profile: ConnectionProfile): string;
|
|
24
|
+
export declare function sameRemoteInstance(left: ConnectionProfile, right: ConnectionProfile): boolean;
|
package/dist/locators.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
function normalizedBase(profile) {
|
|
3
|
+
return new URL(profile.type === 'yandex-wiki' ? profile.webUrl : profile.baseUrl);
|
|
4
|
+
}
|
|
5
|
+
function normalizedProfileUrl(profile) {
|
|
6
|
+
const url = normalizedBase(profile);
|
|
7
|
+
url.hash = '';
|
|
8
|
+
url.search = '';
|
|
9
|
+
url.pathname = url.pathname.replace(/\/+$/, '') || '/';
|
|
10
|
+
if (profile.type === 'confluence' && profile.deployment === 'cloud') {
|
|
11
|
+
url.pathname = url.pathname.replace(/\/wiki$/, '') || '/';
|
|
12
|
+
}
|
|
13
|
+
return url;
|
|
14
|
+
}
|
|
15
|
+
function isAbsoluteUrl(value) {
|
|
16
|
+
try {
|
|
17
|
+
const parsed = new URL(value);
|
|
18
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function urlMatchesProfile(value, profile) {
|
|
25
|
+
const candidate = new URL(value);
|
|
26
|
+
const base = normalizedBase(profile);
|
|
27
|
+
if (candidate.origin !== base.origin)
|
|
28
|
+
return false;
|
|
29
|
+
const basePath = base.pathname.replace(/\/+$/, '');
|
|
30
|
+
return !basePath || candidate.pathname === basePath || candidate.pathname.startsWith(`${basePath}/`);
|
|
31
|
+
}
|
|
32
|
+
export function selectRemoteProfile(config, locator, requestedProfile, role) {
|
|
33
|
+
if (requestedProfile) {
|
|
34
|
+
const profile = config.profiles[requestedProfile];
|
|
35
|
+
if (!profile)
|
|
36
|
+
throw new Error(`Unknown --${role}-profile "${requestedProfile}". Available profiles: ${Object.keys(config.profiles).sort().join(', ') || 'none'}.`);
|
|
37
|
+
if (isAbsoluteUrl(locator) && !urlMatchesProfile(locator, profile)) {
|
|
38
|
+
throw new Error(`${role} URL does not belong to profile "${requestedProfile}" (${normalizedBase(profile).origin}).`);
|
|
39
|
+
}
|
|
40
|
+
return profile;
|
|
41
|
+
}
|
|
42
|
+
const profiles = Object.values(config.profiles);
|
|
43
|
+
if (isAbsoluteUrl(locator)) {
|
|
44
|
+
const matches = profiles
|
|
45
|
+
.filter((profile) => urlMatchesProfile(locator, profile))
|
|
46
|
+
.sort((left, right) => normalizedBase(right).pathname.length - normalizedBase(left).pathname.length);
|
|
47
|
+
if (matches.length === 0)
|
|
48
|
+
throw new Error(`No configured profile matches ${role} URL. Add its base URL to ${config.configFile} or pass a configured endpoint.`);
|
|
49
|
+
if (matches.length > 1 && normalizedBase(matches[0]).pathname.length === normalizedBase(matches[1]).pathname.length) {
|
|
50
|
+
throw new Error(`Multiple profiles match ${role} URL: ${matches.map((profile) => profile.name).join(', ')}. Pass --${role}-profile.`);
|
|
51
|
+
}
|
|
52
|
+
return matches[0];
|
|
53
|
+
}
|
|
54
|
+
const suitable = /^\d+$/.test(locator)
|
|
55
|
+
? profiles
|
|
56
|
+
: profiles.filter((profile) => profile.type === 'yandex-wiki');
|
|
57
|
+
if (suitable.length === 1)
|
|
58
|
+
return suitable[0];
|
|
59
|
+
if (suitable.length === 0) {
|
|
60
|
+
throw new Error(`No configured profile accepts the ${role} shorthand "${locator}". Use an absolute URL or add a Yandex Wiki profile.`);
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`The ${role} locator "${locator}" is shorthand and ${suitable.length} suitable profiles are configured. Pass --${role}-profile (${suitable.map((profile) => profile.name).sort().join(', ')}).`);
|
|
63
|
+
}
|
|
64
|
+
export function resolveSourceLocator(config, value, profile) {
|
|
65
|
+
return { type: 'remote', value: value.trim(), profile: selectRemoteProfile(config, value.trim(), profile, 'source') };
|
|
66
|
+
}
|
|
67
|
+
export function resolveDestinationLocator(input) {
|
|
68
|
+
const value = input.value.trim();
|
|
69
|
+
if (input.format) {
|
|
70
|
+
if (input.profile)
|
|
71
|
+
throw new Error('--destination-profile cannot be combined with a filesystem --format.');
|
|
72
|
+
if (isAbsoluteUrl(value))
|
|
73
|
+
throw new Error('--format cannot be used with an HTTP(S) destination. Omit --format for a remote page.');
|
|
74
|
+
return { type: 'directory', path: path.resolve(input.cwd ?? process.cwd(), value), format: input.format };
|
|
75
|
+
}
|
|
76
|
+
return { type: 'remote', value, profile: selectRemoteProfile(input.config, value, input.profile, 'destination') };
|
|
77
|
+
}
|
|
78
|
+
export function profileBaseUrl(profile) {
|
|
79
|
+
return normalizedProfileUrl(profile).toString().replace(/\/$/, '');
|
|
80
|
+
}
|
|
81
|
+
export function sameRemoteInstance(left, right) {
|
|
82
|
+
return left.type === right.type && profileBaseUrl(left) === profileBaseUrl(right);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=locators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locators.js","sourceRoot":"","sources":["../src/locators.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface Logger {
|
|
2
|
+
info(message: string): void;
|
|
3
|
+
warn(message: string): void;
|
|
4
|
+
error(message: string): void;
|
|
5
|
+
}
|
|
6
|
+
export declare function redact(value: unknown, secrets: string[]): string;
|
|
7
|
+
export declare function createLogger(secrets?: string[]): Logger;
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function redact(value, secrets) {
|
|
2
|
+
let message = value instanceof Error ? value.message : String(value);
|
|
3
|
+
for (const secret of secrets) {
|
|
4
|
+
if (secret) {
|
|
5
|
+
message = message.split(secret).join('[REDACTED]');
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return message;
|
|
9
|
+
}
|
|
10
|
+
export function createLogger(secrets = []) {
|
|
11
|
+
const write = (level, message) => {
|
|
12
|
+
const safe = redact(message, secrets);
|
|
13
|
+
const target = level === 'error' ? console.error : level === 'warn' ? console.warn : console.log;
|
|
14
|
+
const time = new Date().toISOString().slice(11, 19);
|
|
15
|
+
const symbol = level === 'error' ? '✗' : level === 'warn' ? '⚠' : '◆';
|
|
16
|
+
const color = process.stderr.isTTY && !process.env.NO_COLOR
|
|
17
|
+
? level === 'error' ? 31 : level === 'warn' ? 33 : 36
|
|
18
|
+
: undefined;
|
|
19
|
+
const marker = color ? `\u001B[${color}m${symbol}\u001B[0m` : symbol;
|
|
20
|
+
target(` ${time} ${marker} ${safe}`);
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
info: (message) => write('info', message),
|
|
24
|
+
warn: (message) => write('warn', message),
|
|
25
|
+
error: (message) => write('error', message),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,OAAiB;IACtD,IAAI,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,UAAoB,EAAE;IACjD,MAAM,KAAK,GAAG,CAAC,KAAe,EAAE,OAAe,EAAQ,EAAE;QACvD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;QACjG,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACtE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ;YACzD,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;YACrD,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;QACrE,MAAM,CAAC,KAAK,IAAI,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC;QACzC,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC;QACzC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC;KAC5C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createProviderAdapter } from './adapters/factory.js';
|
|
2
|
+
import type { AppConfig } from './config.js';
|
|
3
|
+
import type { TransferScope } from './domain.js';
|
|
4
|
+
import type { Logger } from './logger.js';
|
|
5
|
+
import type { RemoteLocator, ResolvedLocator } from './locators.js';
|
|
6
|
+
import { type MigrationReportPaths } from './report.js';
|
|
7
|
+
import { type RunStatus } from './state.js';
|
|
8
|
+
export interface TransferOptions {
|
|
9
|
+
source: RemoteLocator;
|
|
10
|
+
destination: ResolvedLocator;
|
|
11
|
+
scope: TransferScope;
|
|
12
|
+
dryRun: boolean;
|
|
13
|
+
archive: boolean;
|
|
14
|
+
fresh?: boolean;
|
|
15
|
+
concurrency: number;
|
|
16
|
+
stateFilename?: string;
|
|
17
|
+
runtimeDirectory: string;
|
|
18
|
+
cacheDirectory: string;
|
|
19
|
+
reportsDirectory: string;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
onStatus?: (message: string) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface TransferResult {
|
|
24
|
+
status: RunStatus;
|
|
25
|
+
hasErrors: boolean;
|
|
26
|
+
reportPaths: MigrationReportPaths;
|
|
27
|
+
stateId: string;
|
|
28
|
+
stateFilename: string;
|
|
29
|
+
stateBackupFilename?: string;
|
|
30
|
+
output?: string;
|
|
31
|
+
archive?: string;
|
|
32
|
+
summary: {
|
|
33
|
+
pages: number;
|
|
34
|
+
complete: number;
|
|
35
|
+
partial: number;
|
|
36
|
+
failed: number;
|
|
37
|
+
resources: number;
|
|
38
|
+
bytes: number;
|
|
39
|
+
links: number;
|
|
40
|
+
cycles: number;
|
|
41
|
+
warnings: number;
|
|
42
|
+
errors: number;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export declare class Migrator {
|
|
46
|
+
private readonly config;
|
|
47
|
+
private readonly logger;
|
|
48
|
+
private readonly adapterFactory;
|
|
49
|
+
constructor(config: AppConfig, logger: Logger, adapterFactory?: typeof createProviderAdapter);
|
|
50
|
+
run(options: TransferOptions): Promise<TransferResult>;
|
|
51
|
+
private exportFilesystem;
|
|
52
|
+
private transferRemote;
|
|
53
|
+
}
|