@jwilleke/ngdpbase-types 5.25.0 → 5.27.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/README.md +1 -1
- package/lib/packages/ngdpbase-types/index.d.ts +9 -2
- package/lib/packages/ngdpbase-types/testing.d.ts +7 -3
- package/lib/packages/ngdpbase-types/testing.js +7 -3
- package/lib/src/converters/DocxConverter.d.ts +30 -0
- package/lib/src/converters/MarkdownConverter.d.ts +31 -0
- package/lib/src/export/ChromeServicePdfProvider.d.ts +52 -0
- package/lib/src/export/ChromeServicePdfProvider.js +153 -0
- package/lib/src/export/LocalChromePdfProvider.d.ts +25 -0
- package/lib/src/export/LocalChromePdfProvider.js +33 -0
- package/lib/src/export/PdfProvider.d.ts +51 -0
- package/lib/src/export/PdfProvider.js +37 -0
- package/lib/src/export/pdfEngine.d.ts +113 -0
- package/lib/src/export/pdfEngine.js +195 -0
- package/lib/src/http/egressPolicy.js +61 -0
- package/lib/src/http/guardedElasticsearch.d.ts +27 -0
- package/lib/src/http/guardedElasticsearch.js +99 -0
- package/lib/src/ice/converterShim.d.ts +5 -0
- package/lib/src/ice/fileHandling.d.ts +30 -0
- package/lib/src/ice/ledger.d.ts +37 -0
- package/lib/src/ice/runImport.d.ts +51 -0
- package/lib/src/ice/types.d.ts +262 -0
- package/lib/src/mail/MailProvider.d.ts +15 -0
- package/lib/src/mail/NodemailerMailProvider.d.ts +1 -1
- package/lib/src/managers/AddonsManager.d.ts +55 -3
- package/lib/src/managers/AttachmentManager.d.ts +4 -5
- package/lib/src/managers/AuthManager.d.ts +150 -32
- package/lib/src/managers/BackgroundJobManager.d.ts +22 -12
- package/lib/src/managers/BackupManager.d.ts +779 -0
- package/lib/src/managers/BackupManager.js +2042 -0
- package/lib/src/managers/BaseManager.d.ts +156 -6
- package/lib/src/managers/BaseManager.js +64 -2
- package/lib/src/managers/CommentManager.d.ts +6 -6
- package/lib/src/managers/ConsentManager.d.ts +130 -0
- package/lib/src/managers/DatabaseManager.d.ts +6 -0
- package/lib/src/managers/DatabaseManager.js +25 -15
- package/lib/src/managers/EmailManager.d.ts +32 -5
- package/lib/src/managers/ExportManager.d.ts +62 -0
- package/lib/src/managers/ExportManager.js +147 -2
- package/lib/src/managers/FootnoteManager.d.ts +6 -6
- package/lib/src/managers/ImportManager.d.ts +590 -0
- package/lib/src/managers/OrganizationManager.d.ts +8 -6
- package/lib/src/managers/PageManager.d.ts +61 -1
- package/lib/src/managers/PersonManager.d.ts +8 -6
- package/lib/src/managers/RoleManager.d.ts +6 -8
- package/lib/src/providers/BaseAttachmentProvider.d.ts +4 -4
- package/lib/src/providers/BaseBackupProvider.d.ts +77 -0
- package/lib/src/providers/BaseBackupProvider.js +52 -0
- package/lib/src/providers/BasicAttachmentProvider.d.ts +4 -4
- package/lib/src/providers/FileBackupProvider.d.ts +34 -0
- package/lib/src/providers/FileBackupProvider.js +116 -0
- package/lib/src/providers/FileConsentProvider.d.ts +18 -0
- package/lib/src/providers/SqliteDatabaseProvider.d.ts +2 -2
- package/lib/src/providers/SqliteDatabaseProvider.js +12 -11
- package/lib/src/providers/attachmentFilesBackup.d.ts +5 -4
- package/lib/src/providers/databaseMigrations.js +45 -0
- package/lib/src/security/PolicyDecisionPoint.d.ts +14 -0
- package/lib/src/security/PolicyInformationPoint.d.ts +25 -2
- package/lib/src/types/Config.d.ts +2 -0
- package/lib/src/types/Consent.d.ts +90 -0
- package/lib/src/types/express.d.ts +6 -0
- package/lib/src/utils/addonHost.d.ts +45 -2
- package/lib/src/utils/addonHost.js +39 -3
- package/lib/src/utils/auditEventNames.generated.d.ts +20 -0
- package/lib/src/utils/auditEventNames.generated.js +20 -0
- package/lib/src/utils/backupCrypto.js +275 -0
- package/lib/src/utils/backupDocument.d.ts +36 -0
- package/lib/src/utils/backupDocument.js +99 -0
- package/lib/src/utils/browserDescription.d.ts +11 -0
- package/lib/src/utils/directoryBackup.d.ts +48 -48
- package/lib/src/utils/directoryBackup.js +291 -0
- package/lib/src/utils/frontmatterAccess.d.ts +54 -11
- package/lib/src/utils/importRunSummary.d.ts +64 -0
- package/lib/src/utils/instanceEnvBackup.d.ts +46 -0
- package/lib/src/utils/instanceEnvBackup.js +120 -0
- package/lib/src/utils/instanceEnvSecret.js +79 -0
- package/lib/src/utils/ipLocation.d.ts +11 -0
- package/lib/src/utils/ncmNotify.d.ts +30 -0
- package/lib/src/utils/privateStoreImport.d.ts +58 -0
- package/lib/src/utils/schedule.js +171 -0
- package/lib/src/utils/secretFileMode.js +78 -0
- package/lib/src/utils/siteRestore.d.ts +13 -16
- package/lib/src/utils/siteRestore.js +33 -6
- package/lib/src/utils/treeArchive.d.ts +2 -0
- package/lib/src/utils/treeArchive.js +242 -0
- package/lib/src/utils/version.js +408 -0
- package/lib/src/utils/zipArchive.d.ts +71 -0
- package/package.json +6 -1
- package/lib/src/utils/administrators.d.ts +0 -21
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the import of content from external wiki formats into ngdpbase.
|
|
5
|
+
* Uses an extensible converter registry pattern to support multiple formats
|
|
6
|
+
* (JSPWiki, MediaWiki, Confluence, etc.).
|
|
7
|
+
*
|
|
8
|
+
* @module ImportManager
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const importManager = engine.getManager('ImportManager');
|
|
12
|
+
*
|
|
13
|
+
* // Register additional converters
|
|
14
|
+
* importManager.registerConverter(new MediaWikiConverter());
|
|
15
|
+
*
|
|
16
|
+
* // Preview import
|
|
17
|
+
* const preview = await importManager.previewImport({
|
|
18
|
+
* sourceDir: '/path/to/wiki',
|
|
19
|
+
* format: 'auto'
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Execute import
|
|
23
|
+
* const result = await importManager.importPages({
|
|
24
|
+
* sourceDir: '/path/to/wiki',
|
|
25
|
+
* format: 'jspwiki',
|
|
26
|
+
* dryRun: false
|
|
27
|
+
* });
|
|
28
|
+
*/
|
|
29
|
+
import { type ActorContext } from '../context/ActorContext.js';
|
|
30
|
+
import BaseManager, { BackupData } from './BaseManager.js';
|
|
31
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
32
|
+
import { IContentConverter } from '../converters/IContentConverter.js';
|
|
33
|
+
import { type ZipReadLimits } from '../utils/zipArchive.js';
|
|
34
|
+
import { type JobContext } from '../context/JobContext.js';
|
|
35
|
+
import type { IceLedger, IceReceiver, IceRunReport, IceRunRequest, IceShim, RegisteredShim } from '../ice/types.js';
|
|
36
|
+
/**
|
|
37
|
+
* Options for import operations
|
|
38
|
+
*/
|
|
39
|
+
export interface ImportOptions {
|
|
40
|
+
/** Source directory containing files to import */
|
|
41
|
+
sourceDir: string;
|
|
42
|
+
/**
|
|
43
|
+
* Target directory for converted files (default: the live pages dir).
|
|
44
|
+
* When this resolves to the live pages directory, imports go through
|
|
45
|
+
* PageManager save semantics (#880 — indexed, versioned, in Recent
|
|
46
|
+
* Changes). Any other directory means convert-to-files/export mode:
|
|
47
|
+
* raw markdown files are written there instead.
|
|
48
|
+
*/
|
|
49
|
+
targetDir?: string;
|
|
50
|
+
/** Format ID or 'auto' for auto-detection */
|
|
51
|
+
format?: string;
|
|
52
|
+
/** Keep original files after conversion (default: true) */
|
|
53
|
+
preserveOriginals?: boolean;
|
|
54
|
+
/** Preview only, don't write files (default: false) */
|
|
55
|
+
dryRun?: boolean;
|
|
56
|
+
/** Generate UUIDs for pages that don't have them (default: true) */
|
|
57
|
+
generateUUIDs?: boolean;
|
|
58
|
+
/** File extensions to process (default: determined by format) */
|
|
59
|
+
fileExtensions?: string[];
|
|
60
|
+
/** Maximum files to process (for large imports) */
|
|
61
|
+
limit?: number;
|
|
62
|
+
/** Skip first N files (for resuming imports) */
|
|
63
|
+
offset?: number;
|
|
64
|
+
/** Progress callback for streaming updates */
|
|
65
|
+
onProgress?: (event: ImportProgressEvent) => void;
|
|
66
|
+
/**
|
|
67
|
+
* Categorises this run for the per-run summary (#738). Examples:
|
|
68
|
+
* `'paste'`, `'url'`, `'file'`, `'jspwiki'`, `'ingest:<sourceId>'`.
|
|
69
|
+
* Defaults to `'import'` when caller doesn't specify.
|
|
70
|
+
*/
|
|
71
|
+
importType?: string;
|
|
72
|
+
/**
|
|
73
|
+
* The identity that initiated this run, forwarded rather than flattened
|
|
74
|
+
* (#1164, #1179, #1236).
|
|
75
|
+
*
|
|
76
|
+
* Until #1236 this sat beside `actor?: string` and `actorIsSystem?`, two
|
|
77
|
+
* more carriers of the same fact that the run summary and the page
|
|
78
|
+
* frontmatter read instead. One identity now: the run's actor, the page's
|
|
79
|
+
* `editor` and its `author` fallback, and the summary's `isSystem` are all
|
|
80
|
+
* derived from this context. A username string cannot carry a
|
|
81
|
+
* delegation. `importPageAttachments` needed a context to authorise an
|
|
82
|
+
* upload, could not get one from a string, and __fabricated__ one:
|
|
83
|
+
*
|
|
84
|
+
* context: { username: author, name: author, isAuthenticated: true,
|
|
85
|
+
* roles: ['admin'] }
|
|
86
|
+
*
|
|
87
|
+
* where `author` came from the imported file's own `attachment.properties`.
|
|
88
|
+
* The PDP (then `UserManager.hasPermission`) trusts supplied roles verbatim — it does not
|
|
89
|
+
* re-resolve them — so that object authorised as an admin, attributed the
|
|
90
|
+
* write to a name taken from imported content, and carried no `viaToken`,
|
|
91
|
+
* so an admin's agent token doing an import escaped its scope ceiling.
|
|
92
|
+
*
|
|
93
|
+
* That is P1 twice over: a parameter that could not carry provenance, and a
|
|
94
|
+
* synthetic admin principal invented to fill the gap it left.
|
|
95
|
+
*
|
|
96
|
+
* Mandatory: the upload door records it, the summary names it, the page
|
|
97
|
+
* carries it. A non-request caller passes a JobContext with its reason.
|
|
98
|
+
*/
|
|
99
|
+
actorContext: ActorContext;
|
|
100
|
+
/**
|
|
101
|
+
* What to do when a file's title matches an existing page (#874).
|
|
102
|
+
* `'skip'` (default) — leave the existing page untouched, report duplicate.
|
|
103
|
+
* `'overwrite'` — update the existing page in place through PageManager save
|
|
104
|
+
* semantics: UUID, author, created, and slug preserved; version bumped by
|
|
105
|
+
* the provider; `editor` = `actor`; search index and link graph updated
|
|
106
|
+
* in-band (same contract as an edit-form save / the ingest API upsert).
|
|
107
|
+
* Folder import only — URL import keeps hard-skip.
|
|
108
|
+
*/
|
|
109
|
+
conflictPolicy?: 'skip' | 'overwrite';
|
|
110
|
+
/**
|
|
111
|
+
* Explicit private-store destination (#1389). Markdown still goes through
|
|
112
|
+
* PageManager. JSON/XML become files in the store via AttachmentManager.
|
|
113
|
+
* Never inferred from a page. Store id from ConfigurationManager when omitted.
|
|
114
|
+
*/
|
|
115
|
+
private?: boolean;
|
|
116
|
+
store?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Progress event for streaming import updates
|
|
120
|
+
*/
|
|
121
|
+
export interface ImportProgressEvent {
|
|
122
|
+
/** Event type */
|
|
123
|
+
type: 'start' | 'progress' | 'complete' | 'error';
|
|
124
|
+
/** Source file path */
|
|
125
|
+
file?: string;
|
|
126
|
+
/** Current file index (0-based) */
|
|
127
|
+
index?: number;
|
|
128
|
+
/** Total files to process */
|
|
129
|
+
total?: number;
|
|
130
|
+
/** Status of the file import */
|
|
131
|
+
status?: 'success' | 'skipped' | 'failed';
|
|
132
|
+
/** Error message if failed */
|
|
133
|
+
error?: string;
|
|
134
|
+
/** Imported page title (success only) — lets the UI link to /view/<title> */
|
|
135
|
+
pageTitle?: string;
|
|
136
|
+
/** Final result (for complete event) */
|
|
137
|
+
result?: unknown;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Error information for failed imports
|
|
141
|
+
*/
|
|
142
|
+
export interface ImportError {
|
|
143
|
+
/** Source file path */
|
|
144
|
+
file: string;
|
|
145
|
+
/** Error message */
|
|
146
|
+
message: string;
|
|
147
|
+
/** Error stack trace (optional) */
|
|
148
|
+
stack?: string;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Information about an imported file
|
|
152
|
+
*/
|
|
153
|
+
export interface ImportedFile {
|
|
154
|
+
/** Original source file path */
|
|
155
|
+
sourcePath: string;
|
|
156
|
+
/** Target file path */
|
|
157
|
+
targetPath: string;
|
|
158
|
+
/** Format detected/used */
|
|
159
|
+
format: string;
|
|
160
|
+
/** File size in bytes */
|
|
161
|
+
size: number;
|
|
162
|
+
/** Extracted metadata */
|
|
163
|
+
metadata: Record<string, unknown>;
|
|
164
|
+
/** Conversion warnings */
|
|
165
|
+
warnings: string[];
|
|
166
|
+
/** Whether file was actually written (false for dry runs or duplicates) */
|
|
167
|
+
written: boolean;
|
|
168
|
+
/** Reason the file was skipped (e.g. 'duplicate') */
|
|
169
|
+
skippedReason?: string;
|
|
170
|
+
/** UUID of existing page if duplicate */
|
|
171
|
+
existingPageUuid?: string;
|
|
172
|
+
/** True when an existing page was (or on dry run: would be) updated in place (#874) */
|
|
173
|
+
overwritten?: boolean;
|
|
174
|
+
/** Attachment import stats (JSPWiki imports only) */
|
|
175
|
+
attachments?: {
|
|
176
|
+
imported: number;
|
|
177
|
+
skipped: number;
|
|
178
|
+
errors: string[];
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Result of an import operation
|
|
183
|
+
*/
|
|
184
|
+
export interface ImportResult {
|
|
185
|
+
/** Overall success status */
|
|
186
|
+
success: boolean;
|
|
187
|
+
/** Number of files converted */
|
|
188
|
+
converted: number;
|
|
189
|
+
/** Number of files skipped */
|
|
190
|
+
skipped: number;
|
|
191
|
+
/** Number of files with errors */
|
|
192
|
+
failed: number;
|
|
193
|
+
/** Total files processed */
|
|
194
|
+
total: number;
|
|
195
|
+
/** Error details */
|
|
196
|
+
errors: ImportError[];
|
|
197
|
+
/** Information about imported files */
|
|
198
|
+
files: ImportedFile[];
|
|
199
|
+
/** Import duration in milliseconds */
|
|
200
|
+
durationMs: number;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Why a takeout import was refused before anything was written (#1472).
|
|
204
|
+
* A refusal is never a partial import.
|
|
205
|
+
*/
|
|
206
|
+
export declare class TakeoutImportRefused extends Error {
|
|
207
|
+
readonly reason: 'not-owner' | 'no-such-store' | 'locked' | 'unreadable';
|
|
208
|
+
constructor(reason: 'not-owner' | 'no-such-store' | 'locked' | 'unreadable', message: string);
|
|
209
|
+
}
|
|
210
|
+
/** One page's outcome in a takeout import (#1472). */
|
|
211
|
+
export type TakeoutPageOutcome = {
|
|
212
|
+
title: string;
|
|
213
|
+
outcome: 'imported';
|
|
214
|
+
importedAs: string;
|
|
215
|
+
} | {
|
|
216
|
+
title: string;
|
|
217
|
+
outcome: 'unchanged';
|
|
218
|
+
} | {
|
|
219
|
+
title: string;
|
|
220
|
+
outcome: 'changed-since-takeout';
|
|
221
|
+
}
|
|
222
|
+
/** `where` is named only when the requester may view that page. */
|
|
223
|
+
| {
|
|
224
|
+
title: string;
|
|
225
|
+
outcome: 'uuid-elsewhere';
|
|
226
|
+
where?: string;
|
|
227
|
+
} | {
|
|
228
|
+
title: string;
|
|
229
|
+
outcome: 'failed';
|
|
230
|
+
message: string;
|
|
231
|
+
};
|
|
232
|
+
export type TakeoutImportReport = {
|
|
233
|
+
store: string;
|
|
234
|
+
pages: TakeoutPageOutcome[];
|
|
235
|
+
/** Files stored, or matched to one the store already held. */
|
|
236
|
+
files: number;
|
|
237
|
+
/** Files that could not be stored, by name. */
|
|
238
|
+
fileErrors: Array<{
|
|
239
|
+
name: string;
|
|
240
|
+
message: string;
|
|
241
|
+
}>;
|
|
242
|
+
/**
|
|
243
|
+
* Files stored that no page now in the store links to — typically because
|
|
244
|
+
* the pages that used them were skipped (operator, 2026-09-25: they come
|
|
245
|
+
* in, and are reported). Named here, for the owner; counted in the logs.
|
|
246
|
+
*/
|
|
247
|
+
unlinkedFiles: string[];
|
|
248
|
+
/** Archive members that are neither a page nor a file. */
|
|
249
|
+
ignored: string[];
|
|
250
|
+
/**
|
|
251
|
+
* The private page this import's report was saved as, in the target store
|
|
252
|
+
* (operator, 2026-09-25). Absent only when that save failed.
|
|
253
|
+
*/
|
|
254
|
+
reportPage?: string;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Import Manager class
|
|
258
|
+
*
|
|
259
|
+
* Manages content import with extensible format support via converter registry.
|
|
260
|
+
*/
|
|
261
|
+
declare class ImportManager extends BaseManager {
|
|
262
|
+
/** Registry of format converters */
|
|
263
|
+
private converterRegistry;
|
|
264
|
+
/** ICE (#1979): shims by id (`<owner>.<name>`), in registration order. */
|
|
265
|
+
private readonly shimRegistry;
|
|
266
|
+
/** ICE: receivers by id. */
|
|
267
|
+
private readonly receiverRegistry;
|
|
268
|
+
/** ICE: runs this process knows about, oldest first. */
|
|
269
|
+
private readonly iceRuns;
|
|
270
|
+
/** ICE: runs waiting for the background job, each with its own context. */
|
|
271
|
+
private readonly iceQueue;
|
|
272
|
+
/** ICE: true while a background job is draining the queue. */
|
|
273
|
+
private iceDraining;
|
|
274
|
+
/** ICE: the last background run id the queue was handed to. */
|
|
275
|
+
private iceLastJobRunId;
|
|
276
|
+
private iceLedger;
|
|
277
|
+
constructor(engine: WikiEngine);
|
|
278
|
+
/**
|
|
279
|
+
* Initialize the manager
|
|
280
|
+
*/
|
|
281
|
+
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
282
|
+
/**
|
|
283
|
+
* Register a content converter. Since #1979 (D8) it is also the ICE shim
|
|
284
|
+
* `core.<formatId>`, producing NCM for the pages receiver, so the folder
|
|
285
|
+
* import and the engine run the same conversion.
|
|
286
|
+
*
|
|
287
|
+
* @param converter - Converter instance implementing IContentConverter
|
|
288
|
+
*/
|
|
289
|
+
registerConverter(converter: IContentConverter): void;
|
|
290
|
+
/**
|
|
291
|
+
* Register a shim (D6, D7). Core registers its own as owner `core`; an
|
|
292
|
+
* add-on registers through `host.registerShim`, which names the add-on as
|
|
293
|
+
* the owner and refuses a shim its manifest does not declare.
|
|
294
|
+
*
|
|
295
|
+
* @returns The shim's id, `<owner>.<name>`
|
|
296
|
+
* @throws Error when the shim is malformed or its id is taken
|
|
297
|
+
*/
|
|
298
|
+
registerShim(owner: string, shim: IceShim): string;
|
|
299
|
+
/**
|
|
300
|
+
* Register a receiver (D4): a door that already exists. Core's are
|
|
301
|
+
* `core.pages` and `core.files`; an add-on registers its own manager's door
|
|
302
|
+
* through `host.registerReceiver`.
|
|
303
|
+
*
|
|
304
|
+
* @returns The receiver's id, `<owner>.<name>`
|
|
305
|
+
*/
|
|
306
|
+
registerReceiver(owner: string, receiver: IceReceiver): string;
|
|
307
|
+
/** Is this shim switched on (D6)? `ngdpbase.import.shims.<id>.enabled`, default on. */
|
|
308
|
+
isShimEnabled(id: string): boolean;
|
|
309
|
+
/** Every registered shim, and whether it is on. */
|
|
310
|
+
listShims(): Array<Pick<RegisteredShim, 'id' | 'owner' | 'label' | 'direction' | 'produces' | 'receivers'> & {
|
|
311
|
+
enabled: boolean;
|
|
312
|
+
}>;
|
|
313
|
+
/** The receivers this context may use (D5): each answers with its own existing permission. */
|
|
314
|
+
receiversFor(ctx: ActorContext): Promise<Array<{
|
|
315
|
+
id: string;
|
|
316
|
+
label?: string;
|
|
317
|
+
}>>;
|
|
318
|
+
/**
|
|
319
|
+
* Run one import now and wait for it. For code already off the request
|
|
320
|
+
* path: an add-on's own background job, a test. A route uses
|
|
321
|
+
* {@link enqueueImport}, so the work runs off the request path.
|
|
322
|
+
*
|
|
323
|
+
* @param ctx - Who it runs as: the person's context, or a job's (P1)
|
|
324
|
+
*/
|
|
325
|
+
runImport(request: IceRunRequest, ctx: ActorContext): Promise<IceRunReport>;
|
|
326
|
+
/**
|
|
327
|
+
* Queue an import as background work and return its run id at once. The
|
|
328
|
+
* run carries `ctx` — the person who asked (`jobContextFromRequest`), or a
|
|
329
|
+
* job's context with its reason — and each receiver checks its own
|
|
330
|
+
* permission with it when the run reaches it (D5).
|
|
331
|
+
*/
|
|
332
|
+
enqueueImport(request: IceRunRequest, ctx: JobContext): Promise<string>;
|
|
333
|
+
/** A run's report, to the person it runs as; null to anyone else. */
|
|
334
|
+
getImportRun(runId: string, ctx: ActorContext): IceRunReport | null;
|
|
335
|
+
/** Use this ledger instead of the file one: a test's, or a database table's. */
|
|
336
|
+
setImportLedger(ledger: IceLedger): void;
|
|
337
|
+
private newRunReport;
|
|
338
|
+
private executeRun;
|
|
339
|
+
private enabledShims;
|
|
340
|
+
/**
|
|
341
|
+
* Hand the queue to the background job. One job run drains every queued
|
|
342
|
+
* import, each under its own context. A job run that has finished draining
|
|
343
|
+
* but is not yet marked done answers `enqueue` with its old run id; then
|
|
344
|
+
* try again shortly, so nothing queued is stranded.
|
|
345
|
+
*/
|
|
346
|
+
private kickIceQueue;
|
|
347
|
+
private setting;
|
|
348
|
+
private numberSetting;
|
|
349
|
+
/** `ngdpbase.import.engine.*`: every limit the engine applies is configuration. */
|
|
350
|
+
private engineSettings;
|
|
351
|
+
/** The engine's ledger (fingerprints, per-file status, item keys), opened on first use. */
|
|
352
|
+
private ledger;
|
|
353
|
+
/** What a shim is handed: the engine's guarded fetch (design rule) and its own options (D6). */
|
|
354
|
+
private shimTools;
|
|
355
|
+
/** A receiver's existing permission, asked of the PDP with the run's context (D5, P2). */
|
|
356
|
+
private permits;
|
|
357
|
+
/**
|
|
358
|
+
* `core.pages` (D4): NCM into pages, through PageManager's save door, as the
|
|
359
|
+
* folder import saves. It checks `page-create` itself (D5). A title that
|
|
360
|
+
* already exists is a duplicate, as it is for the folder import by default.
|
|
361
|
+
*/
|
|
362
|
+
private pagesReceiver;
|
|
363
|
+
/**
|
|
364
|
+
* `core.files` (D4): a file through AttachmentManager's upload door, which
|
|
365
|
+
* checks `asset-upload` itself. Also where a kept original goes (D11) when
|
|
366
|
+
* the chosen receiver does not keep its own. It becomes FileManager's write
|
|
367
|
+
* door once the files layer has one (#1851).
|
|
368
|
+
*/
|
|
369
|
+
private filesReceiver;
|
|
370
|
+
/**
|
|
371
|
+
* Get available format IDs
|
|
372
|
+
*
|
|
373
|
+
* @returns Array of registered format identifiers
|
|
374
|
+
*/
|
|
375
|
+
getAvailableFormats(): string[];
|
|
376
|
+
/**
|
|
377
|
+
* Get converter by format ID
|
|
378
|
+
*
|
|
379
|
+
* @param formatId - Format identifier
|
|
380
|
+
* @returns Converter instance or undefined
|
|
381
|
+
*/
|
|
382
|
+
getConverter(formatId: string): IContentConverter | undefined;
|
|
383
|
+
/**
|
|
384
|
+
* Get all registered converters with their metadata
|
|
385
|
+
*
|
|
386
|
+
* @returns Array of converter info objects
|
|
387
|
+
*/
|
|
388
|
+
getConverterInfo(): Array<{
|
|
389
|
+
formatId: string;
|
|
390
|
+
formatName: string;
|
|
391
|
+
fileExtensions: string[];
|
|
392
|
+
}>;
|
|
393
|
+
/**
|
|
394
|
+
* Auto-detect format from file content
|
|
395
|
+
*
|
|
396
|
+
* @param content - File content
|
|
397
|
+
* @param filename - Filename (for extension matching)
|
|
398
|
+
* @returns Format ID or null if no match
|
|
399
|
+
*/
|
|
400
|
+
detectFormat(content: string, filename: string): string | null;
|
|
401
|
+
/**
|
|
402
|
+
* Preview import without writing files
|
|
403
|
+
*
|
|
404
|
+
* @param options - Import options (dryRun is forced to true)
|
|
405
|
+
* @returns Import result with preview data
|
|
406
|
+
*/
|
|
407
|
+
previewImport(options: ImportOptions): Promise<ImportResult>;
|
|
408
|
+
/**
|
|
409
|
+
* Import pages with streaming progress updates
|
|
410
|
+
* Alias for importPages that makes the progress callback more explicit
|
|
411
|
+
*
|
|
412
|
+
* @param options - Import options with onProgress callback
|
|
413
|
+
* @returns Import result with statistics and file details
|
|
414
|
+
*/
|
|
415
|
+
importPagesWithProgress(options: ImportOptions): Promise<ImportResult>;
|
|
416
|
+
/**
|
|
417
|
+
* Import pages from source directory
|
|
418
|
+
*
|
|
419
|
+
* @param options - Import options
|
|
420
|
+
* @returns Import result with statistics and file details
|
|
421
|
+
*/
|
|
422
|
+
importPages(options: ImportOptions): Promise<ImportResult>;
|
|
423
|
+
/**
|
|
424
|
+
* Import a single page
|
|
425
|
+
*
|
|
426
|
+
* @param filePath - Source file path
|
|
427
|
+
* @param options - Import options
|
|
428
|
+
* @returns Imported file info or null if skipped
|
|
429
|
+
*/
|
|
430
|
+
importSinglePage(filePath: string, options: ImportOptions): Promise<ImportedFile | null>;
|
|
431
|
+
/**
|
|
432
|
+
* Update an existing page in place from an import (#874 conflict policy
|
|
433
|
+
* 'overwrite'). Mirrors the edit-form / ingest-API save contract: UUID,
|
|
434
|
+
* author, created, and slug are preserved from the existing page; the
|
|
435
|
+
* provider bumps the version; `editor` is the import actor; the search
|
|
436
|
+
* index, link graph, and render cache are updated in-band so the result
|
|
437
|
+
* is immediately searchable and link-resolvable.
|
|
438
|
+
*/
|
|
439
|
+
private overwriteExistingPage;
|
|
440
|
+
/**
|
|
441
|
+
* A title the save door had to rewrite (#1455) is named in this file's
|
|
442
|
+
* warnings, so the import report says what the source called the page and
|
|
443
|
+
* what it is called here. Returns the title the page actually has.
|
|
444
|
+
*/
|
|
445
|
+
private reportNormalisedTitle;
|
|
446
|
+
/**
|
|
447
|
+
* Create a new page from an import through PageManager save semantics
|
|
448
|
+
* (#880): the provider assigns versioning and the page comes out indexed,
|
|
449
|
+
* link-resolvable, and visible in Recent Changes — instead of the raw file
|
|
450
|
+
* write that left pages invisible to search until a manual reindex.
|
|
451
|
+
* `author` and `editor` are the import actor (unless the source frontmatter
|
|
452
|
+
* carries an author).
|
|
453
|
+
*/
|
|
454
|
+
private createPageThroughPipeline;
|
|
455
|
+
/**
|
|
456
|
+
* Import a page from a URL
|
|
457
|
+
*
|
|
458
|
+
* Fetches the URL and imports it like a file import of the same HTML
|
|
459
|
+
* (#1337): the NCM normalizer (links, tables, `ncmVersion`), the #1332 fix
|
|
460
|
+
* steps, then a save through PageManager — validated, audited as the
|
|
461
|
+
* importer, versioned and indexed. The fetched page's schema.org metadata
|
|
462
|
+
* and the source URL are kept in frontmatter.
|
|
463
|
+
*
|
|
464
|
+
* @param url - URL to fetch and import
|
|
465
|
+
* @param options - The importer's context, and optional title and dryRun
|
|
466
|
+
* @returns Imported file info
|
|
467
|
+
*/
|
|
468
|
+
importFromUrl(url: string, options: {
|
|
469
|
+
actorContext: ActorContext;
|
|
470
|
+
title?: string;
|
|
471
|
+
dryRun?: boolean;
|
|
472
|
+
}): Promise<ImportedFile>;
|
|
473
|
+
/**
|
|
474
|
+
* Run every Markdown fix step over an imported body (#1332), through
|
|
475
|
+
* PageManager — the steps Convert to NCM runs. Each step that changed
|
|
476
|
+
* something is added to `warnings` as a `converter-note`, which the import
|
|
477
|
+
* preview and the run notification already show. Without a PageManager the
|
|
478
|
+
* body is returned unchanged.
|
|
479
|
+
*
|
|
480
|
+
* @param body - Page body, without frontmatter
|
|
481
|
+
* @param warnings - The conversion's warnings, appended to
|
|
482
|
+
* @returns The fixed body
|
|
483
|
+
*/
|
|
484
|
+
private applyFixSteps;
|
|
485
|
+
/** Style classes for up-converted tables (NCM §2.1), operator-configurable. */
|
|
486
|
+
private ncmTableClasses;
|
|
487
|
+
/**
|
|
488
|
+
* JSON/XML into a private store are files, not pages (#1389).
|
|
489
|
+
*/
|
|
490
|
+
private importPrivateStoreFile;
|
|
491
|
+
/**
|
|
492
|
+
* Import attachments from a JSPWiki `-att/` directory alongside a page file.
|
|
493
|
+
*
|
|
494
|
+
* @param sourceFilePath - Path to the source `.txt` page file
|
|
495
|
+
* @param pageName - Decoded page name (used to link attachments)
|
|
496
|
+
* @param options - Import options (dryRun support)
|
|
497
|
+
* @returns Stats about imported attachments
|
|
498
|
+
*/
|
|
499
|
+
private importPageAttachments;
|
|
500
|
+
/**
|
|
501
|
+
* Get MIME type from file extension
|
|
502
|
+
*/
|
|
503
|
+
private getMimeType;
|
|
504
|
+
/**
|
|
505
|
+
* Get file extensions to process based on options
|
|
506
|
+
*/
|
|
507
|
+
private getFileExtensions;
|
|
508
|
+
/**
|
|
509
|
+
* Find all files with matching extensions in directory
|
|
510
|
+
*/
|
|
511
|
+
private findFiles;
|
|
512
|
+
/**
|
|
513
|
+
* Build YAML frontmatter from conversion result
|
|
514
|
+
*/
|
|
515
|
+
private buildImportMetadata;
|
|
516
|
+
private buildFrontmatter;
|
|
517
|
+
/**
|
|
518
|
+
* Register new user-keywords to the custom configuration
|
|
519
|
+
*
|
|
520
|
+
* When importing JSPWiki pages with %%category%% blocks, this method
|
|
521
|
+
* adds extracted categories to the config so they appear in the
|
|
522
|
+
* user-keywords picker when editing pages.
|
|
523
|
+
*
|
|
524
|
+
* @param keywords - Array of keyword names to register
|
|
525
|
+
* @returns Number of new keywords added
|
|
526
|
+
*/
|
|
527
|
+
private registerUserKeywordsToConfig;
|
|
528
|
+
/**
|
|
529
|
+
* Import a takeout into one of the requester's OWN private stores (#1472).
|
|
530
|
+
*
|
|
531
|
+
* The mirror of `PageManager.buildOwnStoreTakeout`, and not `importPages`:
|
|
532
|
+
* that reads a directory on the server, so a decrypted takeout would have to
|
|
533
|
+
* be staged on disk, and it converts every page, which would rewrite bodies
|
|
534
|
+
* that are already this system's own markdown. Here the archive is read in
|
|
535
|
+
* memory and each page and file goes through the same doors an edit and an
|
|
536
|
+
* upload use, so a sealed store encrypts on write with no new crypto.
|
|
537
|
+
*
|
|
538
|
+
* Refused before anything is written — never a partial import — when the
|
|
539
|
+
* requester is not the owner, the store is not theirs, or it is encrypted
|
|
540
|
+
* and locked in this session.
|
|
541
|
+
*
|
|
542
|
+
* Idempotent (operator, 2026-09-25): importing the same takeout twice leaves
|
|
543
|
+
* the store as the first import did.
|
|
544
|
+
*
|
|
545
|
+
* - Files first, so page links can be pointed at them. The store's upload
|
|
546
|
+
* finds a file whose bytes it already holds and returns that one, so a
|
|
547
|
+
* second import stores nothing new.
|
|
548
|
+
* - A page whose uuid the target store already holds is skipped:
|
|
549
|
+
* `unchanged` when its body matches, `changed-since-takeout` when not —
|
|
550
|
+
* the live page wins.
|
|
551
|
+
* - A page whose uuid is used elsewhere on the site is skipped as
|
|
552
|
+
* `uuid-elsewhere`, naming the page only when the requester may view it.
|
|
553
|
+
* - A title held by a DIFFERENT page lands beside it as `Title (imported)`.
|
|
554
|
+
*
|
|
555
|
+
* This is not a restore: a takeout carries no history and no trash.
|
|
556
|
+
*
|
|
557
|
+
* @param ctx - Who is importing; only into their own container
|
|
558
|
+
*/
|
|
559
|
+
importOwnStoreTakeout(ctx: ActorContext, options: {
|
|
560
|
+
store: string;
|
|
561
|
+
archive: Buffer;
|
|
562
|
+
limits: ZipReadLimits;
|
|
563
|
+
/** The uploaded file's name, for the report page. */
|
|
564
|
+
sourceName?: string;
|
|
565
|
+
}): Promise<TakeoutImportReport>;
|
|
566
|
+
/** The import report page's markdown. Private links are `[Title|store/Title]` (#1457). */
|
|
567
|
+
private takeoutReportMarkdown;
|
|
568
|
+
/**
|
|
569
|
+
* Where a uuid is already in use outside the target store, or null.
|
|
570
|
+
*
|
|
571
|
+
* The owner's other stores this session can read, then the public pages.
|
|
572
|
+
* `where` is set only for a page the requester may view: a uuid held by a
|
|
573
|
+
* page they cannot see is reported without naming it, so an import cannot
|
|
574
|
+
* be used to probe for pages.
|
|
575
|
+
*/
|
|
576
|
+
private whereUuidLives;
|
|
577
|
+
/**
|
|
578
|
+
* Backup manager data (no persistent data to backup)
|
|
579
|
+
*/
|
|
580
|
+
backup(): Promise<BackupData>;
|
|
581
|
+
/**
|
|
582
|
+
* Restore manager data (no persistent data to restore)
|
|
583
|
+
*/
|
|
584
|
+
restore(backupData: BackupData): Promise<void>;
|
|
585
|
+
/**
|
|
586
|
+
* Shutdown the manager
|
|
587
|
+
*/
|
|
588
|
+
shutdown(): Promise<void>;
|
|
589
|
+
}
|
|
590
|
+
export default ImportManager;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import BaseManager, { type
|
|
1
|
+
import BaseManager, { type BackupFolder, type ManagerStats } from './BaseManager.js';
|
|
2
2
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
3
3
|
import type { Organization, OrganizationInput, OrganizationUpdate } from '../types/Organization.js';
|
|
4
4
|
import type { OrganizationProvider } from '../types/OrganizationProvider.js';
|
|
@@ -45,11 +45,13 @@ declare class OrganizationManager extends BaseManager {
|
|
|
45
45
|
constructor(engine: WikiEngine);
|
|
46
46
|
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
47
47
|
/** Provider accessor, mainly for tests. */
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
/** `ngdpbase.application.organization.storagedir`, resolved: where the records are. */
|
|
49
|
+
private configuredStorageDir;
|
|
50
|
+
/**
|
|
51
|
+
* Organization records are in the backup (#1876), as `<stem>.organizations.tar` beside the document. BackupManager backs it up, stages it in a site restore and puts it
|
|
52
|
+
* in place at the next start, before the records are read (#1985).
|
|
53
|
+
*/
|
|
54
|
+
backupFolders(): BackupFolder[];
|
|
53
55
|
getProvider(): OrganizationProvider | null;
|
|
54
56
|
/**
|
|
55
57
|
* Create the anchor organization from the instance's own identity (#1027).
|