@iwonz/ywtc 3.4.1 → 3.5.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/dist/migrator.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import { pathToFileURL } from 'node:url';
4
5
  import PQueue from 'p-queue';
5
6
  import { createProviderAdapter } from './adapters/factory.js';
7
+ import { secretValues } from './config.js';
6
8
  import { appendGridSnapshots, assertDestinationContent, emptyRendererFallbackReason, isLegacyTransferPlaceholder, normalizePageContent, normalizedPreviewHtml, rawContentFallback, renderedPreviewHtml, renderConfluenceStorage, renderYfm, setSourceResourceBinding, sourceResourceBindings, verifyDestinationContent, } from './content-model.js';
7
9
  import { commentAttachmentUses, createCommentArchiveZip, renderCommentArchive, renderCommentArchiveIndex, resolveCommentAttachmentReferences, verifyCommentArchive, } from './comments.js';
8
10
  import { ConfirmedDestinationMissingError, verifyConfirmedDestinationPage } from './consistency.js';
@@ -186,6 +188,19 @@ function destinationFromState(state, adapter) {
186
188
  };
187
189
  }
188
190
  function reportPage(input) {
191
+ const inventory = input.commentInventory;
192
+ const commentResourceIds = new Set();
193
+ for (const thread of inventory?.threads ?? []) {
194
+ const stack = [thread.root];
195
+ while (stack.length > 0) {
196
+ const entry = stack.pop();
197
+ for (const attachment of entry.attachments) {
198
+ if (attachment.resourceId)
199
+ commentResourceIds.add(attachment.resourceId);
200
+ }
201
+ stack.push(...entry.replies);
202
+ }
203
+ }
189
204
  const transferable = input.resources.filter((resource) => resource.kind !== 'grid');
190
205
  const resourceItems = transferable.map((resource) => {
191
206
  const state = input.resourceStates?.[resource.id];
@@ -194,12 +209,16 @@ function reportPage(input) {
194
209
  return {
195
210
  sourceId: resource.id,
196
211
  sourceName: resource.name,
197
- kind: resource.kind,
212
+ ...(resource.downloadUrl ? { sourceUrl: resource.downloadUrl } : {}),
213
+ ...(resource.referenceAliases?.[0] ? { sourceReference: resource.referenceAliases[0] } : {}),
214
+ kind: commentResourceIds.has(resource.id) ? 'comment-attachment' : resource.kind,
198
215
  status,
199
216
  ...(sourceBytes !== undefined ? { sourceBytes } : {}),
200
217
  ...(status === 'complete' && sourceBytes !== undefined ? { transferredBytes: sourceBytes } : {}),
201
218
  ...(state?.destinationId ? { destinationId: state.destinationId } : {}),
202
219
  ...(state?.destinationName ? { destinationName: state.destinationName } : {}),
220
+ ...(state?.destinationUrl ? { destinationUrl: state.destinationUrl } : {}),
221
+ ...(state?.sha256 ? { sha256: state.sha256 } : {}),
203
222
  ...(state?.lastError ? { error: state.lastError } : {}),
204
223
  ...(status === 'blocked' && input.blockedBy ? input.blockedBy : {}),
205
224
  };
@@ -207,24 +226,57 @@ function reportPage(input) {
207
226
  const knownBytes = resourceItems.reduce((total, resource) => total + (resource.sourceBytes ?? 0), 0);
208
227
  const transferredBytes = resourceItems.reduce((total, resource) => total + (resource.transferredBytes ?? 0), 0);
209
228
  const commentsEnabled = input.commentsEnabled ?? true;
210
- const inventory = input.commentInventory;
211
229
  const commentState = input.commentState;
212
230
  const commentStatus = !commentsEnabled
213
231
  ? 'skipped'
214
232
  : commentState?.status
215
233
  ?? (inventory?.status === 'failed' ? 'failed' : inventory?.status === 'empty' ? 'complete' : 'planned');
216
234
  const commentError = commentState?.lastError ?? inventory?.error;
235
+ const commentItems = inventory?.threads.flatMap((thread) => {
236
+ const result = [];
237
+ const visit = (entry) => {
238
+ const author = entry.author.displayName
239
+ ?? entry.author.username
240
+ ?? entry.author.publicName
241
+ ?? entry.author.providerId;
242
+ result.push({
243
+ threadId: thread.id,
244
+ commentId: entry.id,
245
+ ...(entry.parentId ? { parentId: entry.parentId } : {}),
246
+ author,
247
+ ...(entry.createdAt ? { createdAt: entry.createdAt } : {}),
248
+ ...(entry.status ? { sourceStatus: entry.status } : {}),
249
+ outcome: commentStatus,
250
+ sourceUrl: `${input.page.url}#comment-${encodeURIComponent(entry.id)}`,
251
+ ...(commentState?.destinationUrl ? { destinationUrl: commentState.destinationUrl } : {}),
252
+ ...(commentError ? { error: commentError } : {}),
253
+ });
254
+ for (const reply of entry.replies)
255
+ visit(reply);
256
+ };
257
+ visit(thread.root);
258
+ return result;
259
+ });
217
260
  return {
218
261
  sourceCanonicalId: input.page.canonicalId,
219
262
  sourceId: input.page.id,
220
263
  sourceTitle: input.page.title,
221
264
  sourceUrl: input.page.url,
265
+ ...(input.page.parentId ? { sourceParentId: input.page.parentId } : {}),
266
+ ...(input.page.ancestorIds?.length ? { sourceAncestorIds: input.page.ancestorIds } : {}),
222
267
  ...(input.destination ? {
223
268
  destinationId: input.destination.id,
224
269
  destinationTitle: input.destination.title,
225
270
  destinationUrl: input.destination.url,
271
+ ...(input.destination.parentId ? { destinationParentId: input.destination.parentId } : {}),
226
272
  } : {}),
227
- ...(input.page.projectedParentId ? { parentId: input.page.projectedParentId } : {}),
273
+ ...(input.destinationFile ? {
274
+ destinationId: input.destinationFile.id,
275
+ destinationTitle: input.page.title,
276
+ destinationUrl: input.destinationFile.url,
277
+ ...(input.destinationFile.parentId ? { destinationParentId: input.destinationFile.parentId } : {}),
278
+ } : {}),
279
+ ...(input.page.projectedParentId ? { parentId: input.page.projectedParentId, projectedParentId: input.page.projectedParentId } : {}),
228
280
  depth: input.page.depth,
229
281
  status: input.status,
230
282
  inventoryStatus: input.inventoryStatus ?? 'complete',
@@ -252,11 +304,14 @@ function reportPage(input) {
252
304
  attachments: commentState?.attachments ?? inventory?.attachments ?? 0,
253
305
  bytes: commentState?.bytes ?? inventory?.knownBytes ?? 0,
254
306
  ...(commentState?.archiveName ? { archiveName: commentState.archiveName } : {}),
307
+ ...(commentState?.archiveUrl ? { archiveUrl: commentState.archiveUrl } : {}),
255
308
  ...(commentState?.destinationId ? { destinationId: commentState.destinationId } : {}),
309
+ ...(commentState?.destinationUrl ? { destinationUrl: commentState.destinationUrl } : {}),
256
310
  ...(commentState?.previousDestinationIds?.length ? { revision: true } : {}),
257
311
  ...(commentState?.status === 'stale' ? { stale: true } : {}),
258
312
  ...(commentError ? { error: commentError } : {}),
259
313
  ...(commentState?.preview ? { preview: commentState.preview } : {}),
314
+ ...(commentItems?.length ? { items: commentItems } : {}),
260
315
  ...(commentStatus === 'blocked' && input.blockedBy ? input.blockedBy : {}),
261
316
  },
262
317
  };
@@ -380,6 +435,7 @@ export class Migrator {
380
435
  await store.save();
381
436
  const run = await store.beginRun(options.scope, options.dryRun, sourceStatus, commentsEnabled);
382
437
  const report = new TransferReport({
438
+ secrets: secretValues(this.config),
383
439
  dryRun: options.dryRun,
384
440
  commentsEnabled,
385
441
  scope: options.scope,
@@ -590,6 +646,8 @@ export class Migrator {
590
646
  const result = await this.exportFilesystem(source, graph, options.destination, options, stateLocation.id, store, report, sourceFingerprints, resourcesByPage, commentsByPage);
591
647
  output = result.directory;
592
648
  archive = result.archive;
649
+ const exportEntry = path.join(result.directory, options.destination.format === 'html' ? 'index.html' : 'index.md');
650
+ report.setDestinationUrl(pathToFileURL(exportEntry).toString());
593
651
  report.output('directory', result.directory);
594
652
  if (result.archive)
595
653
  report.output('archive', result.archive);
@@ -684,6 +742,7 @@ export class Migrator {
684
742
  sourceName: sourceResource?.name ?? resource.name,
685
743
  destinationId: resource.path,
686
744
  destinationName: resource.name,
745
+ destinationUrl: pathToFileURL(path.join(destination.path, resource.path)).toString(),
687
746
  ...(resource.sha256 ? { sha256: resource.sha256 } : {}),
688
747
  ...(resource.size !== undefined ? { size: resource.size } : {}),
689
748
  status: options.dryRun ? 'planned' : 'complete',
@@ -722,8 +781,16 @@ export class Migrator {
722
781
  } } : {}),
723
782
  updatedAt: new Date().toISOString(),
724
783
  });
784
+ const parentManifestPage = manifestPage?.parentId
785
+ ? result.manifest.pages.find((item) => item.sourceCanonicalId === manifestPage.parentId)
786
+ : undefined;
725
787
  report.addPage(reportPage({
726
788
  page,
789
+ ...(manifestPage ? { destinationFile: {
790
+ id: manifestPage.path,
791
+ url: pathToFileURL(path.join(destination.path, manifestPage.path)).toString(),
792
+ ...(parentManifestPage ? { parentId: parentManifestPage.path } : {}),
793
+ } } : {}),
727
794
  status: pageStatus,
728
795
  resources: sourceResources,
729
796
  document,
@@ -928,6 +995,7 @@ export class Migrator {
928
995
  const archiveState = zip ? {
929
996
  ...(archiveResource ? { archiveResourceId: archiveResource.id } : {}),
930
997
  archiveName: archiveResource?.name ?? zip.name,
998
+ ...(archiveResource?.url ? { archiveUrl: archiveResource.url } : {}),
931
999
  archivePath: zip.path,
932
1000
  archiveSha256: zip.sha256,
933
1001
  } : {};
@@ -1047,6 +1115,7 @@ export class Migrator {
1047
1115
  fingerprint,
1048
1116
  destinationId: verified.id,
1049
1117
  destinationFingerprint: verified.fingerprint,
1118
+ ...(verified.url ? { destinationUrl: verified.url } : {}),
1050
1119
  ...(previousIds?.length ? { previousDestinationIds: previousIds } : {}),
1051
1120
  ...archiveState,
1052
1121
  ...fullArchiveState,
@@ -1535,6 +1604,7 @@ export class Migrator {
1535
1604
  sourceName: resource.name,
1536
1605
  destinationId: mappedRemote.id,
1537
1606
  destinationName: mappedRemote.name,
1607
+ ...(mappedRemote.url ? { destinationUrl: mappedRemote.url } : {}),
1538
1608
  sha256: downloaded.sha256,
1539
1609
  size: downloaded.size,
1540
1610
  cachePath: downloaded.path,
@@ -1558,6 +1628,7 @@ export class Migrator {
1558
1628
  sourceName: resource.name,
1559
1629
  destinationId: reconciled.id,
1560
1630
  destinationName: reconciled.name,
1631
+ ...(reconciled.url ? { destinationUrl: reconciled.url } : {}),
1561
1632
  sha256: downloaded.sha256,
1562
1633
  size: downloaded.size,
1563
1634
  cachePath: downloaded.path,
@@ -1621,6 +1692,7 @@ export class Migrator {
1621
1692
  sourceName: resource.name,
1622
1693
  destinationId: result.id,
1623
1694
  destinationName: result.name,
1695
+ ...(result.url ? { destinationUrl: result.url } : {}),
1624
1696
  sha256: downloaded.sha256,
1625
1697
  size: downloaded.size,
1626
1698
  cachePath: downloaded.path,