sf-plugin-cms 0.2.0 → 0.3.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 +116 -13
- package/lib/{commands/cms/command-base.d.ts → command-base.d.ts} +2 -1
- package/lib/{commands/cms/command-base.js → command-base.js} +3 -0
- package/lib/command-base.js.map +1 -0
- package/lib/commands/cms/export/workspace.d.ts +12 -5
- package/lib/commands/cms/export/workspace.js +102 -11
- package/lib/commands/cms/export/workspace.js.map +1 -1
- package/lib/commands/cms/get/channel.d.ts +1 -1
- package/lib/commands/cms/get/channel.js +1 -1
- package/lib/commands/cms/get/channel.js.map +1 -1
- package/lib/commands/cms/get/content.d.ts +1 -1
- package/lib/commands/cms/get/content.js +1 -1
- package/lib/commands/cms/get/content.js.map +1 -1
- package/lib/commands/cms/get/variant.d.ts +1 -1
- package/lib/commands/cms/get/variant.js +1 -1
- package/lib/commands/cms/get/variant.js.map +1 -1
- package/lib/commands/cms/get/workspace.d.ts +1 -1
- package/lib/commands/cms/get/workspace.js +1 -1
- package/lib/commands/cms/get/workspace.js.map +1 -1
- package/lib/commands/cms/import/workspace.d.ts +7 -4
- package/lib/commands/cms/import/workspace.js +121 -35
- package/lib/commands/cms/import/workspace.js.map +1 -1
- package/lib/commands/cms/info.d.ts +11 -10
- package/lib/commands/cms/info.js +123 -8
- package/lib/commands/cms/info.js.map +1 -1
- package/lib/commands/cms/list/channel.d.ts +1 -1
- package/lib/commands/cms/list/channel.js +1 -1
- package/lib/commands/cms/list/channel.js.map +1 -1
- package/lib/commands/cms/list/workspace.d.ts +1 -1
- package/lib/commands/cms/list/workspace.js +1 -1
- package/lib/commands/cms/list/workspace.js.map +1 -1
- package/lib/contracts/info.d.ts +43 -0
- package/lib/contracts/info.js +69 -0
- package/lib/contracts/info.js.map +1 -0
- package/lib/contracts/shared.d.ts +81 -0
- package/lib/contracts/shared.js +218 -0
- package/lib/contracts/shared.js.map +1 -0
- package/lib/contracts/workspace-export.d.ts +91 -0
- package/lib/contracts/workspace-export.js +324 -0
- package/lib/contracts/workspace-export.js.map +1 -0
- package/lib/contracts/workspace-import.d.ts +46 -0
- package/lib/contracts/workspace-import.js +108 -0
- package/lib/contracts/workspace-import.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/services/bulk-export-workspaces.d.ts +25 -0
- package/lib/services/bulk-export-workspaces.js +272 -0
- package/lib/services/bulk-export-workspaces.js.map +1 -0
- package/lib/services/export-references.d.ts +21 -0
- package/lib/services/export-references.js +150 -0
- package/lib/services/export-references.js.map +1 -0
- package/lib/services/export-workspace.d.ts +10 -22
- package/lib/services/export-workspace.js +68 -2
- package/lib/services/export-workspace.js.map +1 -1
- package/lib/services/import-workspace.d.ts +7 -3
- package/lib/services/import-workspace.js +224 -96
- package/lib/services/import-workspace.js.map +1 -1
- package/lib/services/resolve-workspace.d.ts +5 -0
- package/lib/services/resolve-workspace.js +56 -47
- package/lib/services/resolve-workspace.js.map +1 -1
- package/messages/cms.export.workspace.md +22 -4
- package/messages/cms.import.workspace.md +8 -2
- package/messages/cms.info.md +21 -0
- package/package.json +3 -2
- package/lib/commands/cms/command-base.js.map +0 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, stat } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { assertCmsEnvelope, MAX_DIAGNOSTICS_PER_KIND, sanitizeDiagnostics, } from '../contracts/shared.js';
|
|
5
|
+
import { assertWorkspaceExportSetResult, WORKSPACE_EXPORT_MANIFEST_CONTRACT, WORKSPACE_EXPORT_SET_CONTRACT, } from '../contracts/workspace-export.js';
|
|
6
|
+
import { redactSecrets } from '../transport/redact-secrets.js';
|
|
7
|
+
import { exportWorkspace, safeWorkspaceDirectoryName } from './export-workspace.js';
|
|
8
|
+
import { getWorkspace } from './read.js';
|
|
9
|
+
import { enumerateWorkspaces } from './resolve-workspace.js';
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function nonemptyString(value) {
|
|
14
|
+
return typeof value === 'string' && value.length > 0;
|
|
15
|
+
}
|
|
16
|
+
function folded(value) {
|
|
17
|
+
return value.normalize('NFKC').toLocaleLowerCase('en-US');
|
|
18
|
+
}
|
|
19
|
+
function actualWorkspaceType(workspace) {
|
|
20
|
+
const spaceType = workspace.spaceType;
|
|
21
|
+
if (typeof spaceType === 'string')
|
|
22
|
+
return normalizeWorkspaceType(spaceType);
|
|
23
|
+
if (isRecord(spaceType) && typeof spaceType.apiName === 'string') {
|
|
24
|
+
return normalizeWorkspaceType(spaceType.apiName);
|
|
25
|
+
}
|
|
26
|
+
return normalizeWorkspaceType();
|
|
27
|
+
}
|
|
28
|
+
export function normalizeWorkspaceType(value) {
|
|
29
|
+
if (typeof value === 'string') {
|
|
30
|
+
const normalized = value.toLocaleLowerCase('en-US');
|
|
31
|
+
if (normalized === 'marketing')
|
|
32
|
+
return 'Marketing';
|
|
33
|
+
if (normalized === 'content')
|
|
34
|
+
return 'Content';
|
|
35
|
+
}
|
|
36
|
+
throw new Error('Workspace type must be Marketing or Content.');
|
|
37
|
+
}
|
|
38
|
+
async function exists(candidate) {
|
|
39
|
+
try {
|
|
40
|
+
await stat(candidate);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (isRecord(error) && error.code === 'ENOENT')
|
|
45
|
+
return false;
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function assertParentCompatible(parent) {
|
|
50
|
+
let cursor = path.resolve(parent);
|
|
51
|
+
for (;;) {
|
|
52
|
+
try {
|
|
53
|
+
const information = await stat(cursor);
|
|
54
|
+
if (!information.isDirectory()) {
|
|
55
|
+
throw new Error(`Bulk output parent is not a directory: ${cursor}`);
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (!isRecord(error) || error.code !== 'ENOENT')
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
const next = path.dirname(cursor);
|
|
64
|
+
if (next === cursor)
|
|
65
|
+
throw new Error(`Bulk output parent is not usable: ${parent}`);
|
|
66
|
+
cursor = next;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function normalizedError(error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
return (redactSecrets(message)
|
|
72
|
+
.replaceAll(/[\r\n]+/gu, ' ')
|
|
73
|
+
.trim() || 'Workspace export failed.');
|
|
74
|
+
}
|
|
75
|
+
function portableOutputPath(value) {
|
|
76
|
+
const portable = value.split(path.sep).join('/');
|
|
77
|
+
if (/^[A-Za-z]:/u.test(portable) || portable.startsWith('/'))
|
|
78
|
+
return path.basename(value);
|
|
79
|
+
return portable.startsWith('./') ? portable : `./${portable}`;
|
|
80
|
+
}
|
|
81
|
+
function relativeArtifactPath(outputDirectory, destination) {
|
|
82
|
+
return path.relative(outputDirectory, destination).split(path.sep).join('/');
|
|
83
|
+
}
|
|
84
|
+
function hashExportSet(result) {
|
|
85
|
+
return `export-set:${createHash('sha256').update(JSON.stringify(result)).digest('hex')}`;
|
|
86
|
+
}
|
|
87
|
+
export async function preflightBulkWorkspaceExport(connection, outputDirectory, workspaceType, options = {}) {
|
|
88
|
+
const listed = await enumerateWorkspaces(connection, options);
|
|
89
|
+
const canonical = [];
|
|
90
|
+
const canonicalNames = new Map();
|
|
91
|
+
for (const summary of listed) {
|
|
92
|
+
const workspace = await getWorkspace(connection, summary.id, options);
|
|
93
|
+
if (workspace.id !== summary.id || !nonemptyString(workspace.name)) {
|
|
94
|
+
throw new Error(`Canonical workspace ${summary.id} returned a malformed or mismatched ID/name.`);
|
|
95
|
+
}
|
|
96
|
+
const nameKey = folded(workspace.name);
|
|
97
|
+
const existingNameId = canonicalNames.get(nameKey);
|
|
98
|
+
if (existingNameId !== undefined && existingNameId !== summary.id) {
|
|
99
|
+
throw new Error(`Workspace names for IDs ${existingNameId} and ${summary.id} differ only by case or Unicode normalization and are ambiguous.`);
|
|
100
|
+
}
|
|
101
|
+
canonicalNames.set(nameKey, summary.id);
|
|
102
|
+
const type = actualWorkspaceType(workspace);
|
|
103
|
+
if (workspaceType !== undefined && type !== workspaceType)
|
|
104
|
+
continue;
|
|
105
|
+
canonical.push({
|
|
106
|
+
id: summary.id,
|
|
107
|
+
name: workspace.name,
|
|
108
|
+
type,
|
|
109
|
+
destination: path.join(outputDirectory, safeWorkspaceDirectoryName(workspace.name)),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
canonical.sort((left, right) => left.id.localeCompare(right.id, 'en-US'));
|
|
113
|
+
await assertParentCompatible(outputDirectory);
|
|
114
|
+
const destinations = new Map();
|
|
115
|
+
for (const workspace of canonical) {
|
|
116
|
+
if (await exists(workspace.destination)) {
|
|
117
|
+
throw new Error(`Export destination already exists: ${workspace.destination}`);
|
|
118
|
+
}
|
|
119
|
+
const destinationKey = folded(path.resolve(workspace.destination));
|
|
120
|
+
const existing = destinations.get(destinationKey);
|
|
121
|
+
if (existing !== undefined) {
|
|
122
|
+
throw new Error(`Workspace destinations collide after sanitization: ${existing.id} (${existing.name}) and ${workspace.id} (${workspace.name}).`);
|
|
123
|
+
}
|
|
124
|
+
destinations.set(destinationKey, workspace);
|
|
125
|
+
}
|
|
126
|
+
return { discoveredCount: listed.length, selected: canonical };
|
|
127
|
+
}
|
|
128
|
+
export async function exportAllWorkspaces(connection, outputDirectory, workspaceType, options = {}) {
|
|
129
|
+
const generatedAt = options.generatedAt ?? new Date().toISOString();
|
|
130
|
+
const pluginVersion = options.pluginVersion ?? '0.3.0';
|
|
131
|
+
const sourceOrgId = options.sourceOrgId ?? 'unknown-org';
|
|
132
|
+
const preflight = await preflightBulkWorkspaceExport(connection, outputDirectory, workspaceType, options);
|
|
133
|
+
if (preflight.selected.length > 0)
|
|
134
|
+
await mkdir(outputDirectory, { recursive: true });
|
|
135
|
+
const workspaces = [];
|
|
136
|
+
const externalReferenceCorrelations = [];
|
|
137
|
+
for (const workspace of preflight.selected) {
|
|
138
|
+
const artifactPath = relativeArtifactPath(outputDirectory, workspace.destination);
|
|
139
|
+
try {
|
|
140
|
+
const exported = await exportWorkspace(connection, workspace.id, workspace.destination, {
|
|
141
|
+
...options,
|
|
142
|
+
generatedAt,
|
|
143
|
+
pluginVersion,
|
|
144
|
+
sourceOrgId,
|
|
145
|
+
});
|
|
146
|
+
const partial = exported.manifest.completeness === 'partial';
|
|
147
|
+
for (const reference of exported.manifest.externalReferences) {
|
|
148
|
+
if (reference.resolution !== 'included' || reference.kind !== 'cms.content')
|
|
149
|
+
continue;
|
|
150
|
+
externalReferenceCorrelations.push({
|
|
151
|
+
sourceWorkspaceId: workspace.id,
|
|
152
|
+
sourceReference: reference.portableKey.value,
|
|
153
|
+
referenceKind: reference.kind,
|
|
154
|
+
referenceId: reference.referenceId,
|
|
155
|
+
packageManifestSha256: exported.manifestSha256,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const warnings = partial
|
|
159
|
+
? exported.manifest.warnings.map(({ code, message }) => ({ code, message }))
|
|
160
|
+
: [];
|
|
161
|
+
workspaces.push({
|
|
162
|
+
source: {
|
|
163
|
+
kind: 'cms.workspace',
|
|
164
|
+
sourceId: workspace.id,
|
|
165
|
+
name: workspace.name,
|
|
166
|
+
workspaceType: workspace.type,
|
|
167
|
+
},
|
|
168
|
+
status: partial ? 'partial' : 'success',
|
|
169
|
+
artifact: {
|
|
170
|
+
path: artifactPath,
|
|
171
|
+
manifestPath: `${artifactPath}/manifest.json`,
|
|
172
|
+
manifestContract: WORKSPACE_EXPORT_MANIFEST_CONTRACT,
|
|
173
|
+
manifestContractVersion: '1.0.0',
|
|
174
|
+
manifestSha256: exported.manifestSha256,
|
|
175
|
+
},
|
|
176
|
+
diagnostics: { warnings, errors: [] },
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
workspaces.push({
|
|
181
|
+
source: {
|
|
182
|
+
kind: 'cms.workspace',
|
|
183
|
+
sourceId: workspace.id,
|
|
184
|
+
name: workspace.name,
|
|
185
|
+
workspaceType: workspace.type,
|
|
186
|
+
},
|
|
187
|
+
status: 'failed',
|
|
188
|
+
artifact: null,
|
|
189
|
+
diagnostics: {
|
|
190
|
+
warnings: [],
|
|
191
|
+
errors: [{ code: 'WORKSPACE_EXPORT_FAILED', message: normalizedError(error) }],
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
externalReferenceCorrelations.sort((left, right) => [
|
|
197
|
+
left.sourceWorkspaceId,
|
|
198
|
+
left.sourceReference,
|
|
199
|
+
left.referenceKind,
|
|
200
|
+
left.referenceId,
|
|
201
|
+
left.packageManifestSha256,
|
|
202
|
+
]
|
|
203
|
+
.join('\u0000')
|
|
204
|
+
.localeCompare([
|
|
205
|
+
right.sourceWorkspaceId,
|
|
206
|
+
right.sourceReference,
|
|
207
|
+
right.referenceKind,
|
|
208
|
+
right.referenceId,
|
|
209
|
+
right.packageManifestSha256,
|
|
210
|
+
].join('\u0000')));
|
|
211
|
+
const result = {
|
|
212
|
+
workspaceType,
|
|
213
|
+
outputDirectory: portableOutputPath(outputDirectory),
|
|
214
|
+
selection: {
|
|
215
|
+
mode: 'all',
|
|
216
|
+
discoveredCount: preflight.discoveredCount,
|
|
217
|
+
selectedCount: workspaces.length,
|
|
218
|
+
},
|
|
219
|
+
summary: {
|
|
220
|
+
succeededCount: workspaces.filter(({ status }) => status === 'success').length,
|
|
221
|
+
partialCount: workspaces.filter(({ status }) => status === 'partial').length,
|
|
222
|
+
failedCount: workspaces.filter(({ status }) => status === 'failed').length,
|
|
223
|
+
},
|
|
224
|
+
externalReferenceCorrelations,
|
|
225
|
+
workspaces,
|
|
226
|
+
};
|
|
227
|
+
let status = 'success';
|
|
228
|
+
if (result.summary.failedCount === workspaces.length && workspaces.length > 0)
|
|
229
|
+
status = 'failed';
|
|
230
|
+
else if (result.summary.failedCount > 0 || result.summary.partialCount > 0)
|
|
231
|
+
status = 'partial';
|
|
232
|
+
const errors = aggregateDiagnostics(workspaces, 'errors');
|
|
233
|
+
const warnings = aggregateDiagnostics(workspaces, 'warnings');
|
|
234
|
+
const envelope = {
|
|
235
|
+
contract: WORKSPACE_EXPORT_SET_CONTRACT,
|
|
236
|
+
contractVersion: '1.0.0',
|
|
237
|
+
status,
|
|
238
|
+
metadata: {
|
|
239
|
+
operation: 'workspace.export.bulk',
|
|
240
|
+
plugin: { name: 'sf-plugin-cms', version: pluginVersion },
|
|
241
|
+
apiVersion: options.apiVersion ?? '67.0',
|
|
242
|
+
},
|
|
243
|
+
diagnostics: { warnings, errors },
|
|
244
|
+
provenance: {
|
|
245
|
+
producer: 'sf-plugin-cms',
|
|
246
|
+
sourceOrgId,
|
|
247
|
+
pluginVersion,
|
|
248
|
+
exportSetId: hashExportSet(result),
|
|
249
|
+
command: 'sf cms export workspace',
|
|
250
|
+
generatedAt,
|
|
251
|
+
},
|
|
252
|
+
result,
|
|
253
|
+
};
|
|
254
|
+
assertCmsEnvelope(envelope, assertWorkspaceExportSetResult);
|
|
255
|
+
return envelope;
|
|
256
|
+
}
|
|
257
|
+
function aggregateDiagnostics(workspaces, kind) {
|
|
258
|
+
const diagnostics = workspaces.flatMap(({ source, diagnostics: workspaceDiagnostics }) => workspaceDiagnostics[kind].map((diagnostic) => ({ ...diagnostic, scope: source.sourceId })));
|
|
259
|
+
if (diagnostics.length <= MAX_DIAGNOSTICS_PER_KIND) {
|
|
260
|
+
return sanitizeDiagnostics(diagnostics, `diagnostics.${kind}`);
|
|
261
|
+
}
|
|
262
|
+
const omitted = diagnostics.length - MAX_DIAGNOSTICS_PER_KIND + 1;
|
|
263
|
+
return sanitizeDiagnostics([
|
|
264
|
+
...diagnostics.slice(0, MAX_DIAGNOSTICS_PER_KIND - 1),
|
|
265
|
+
{
|
|
266
|
+
code: 'DIAGNOSTICS_TRUNCATED',
|
|
267
|
+
message: `${omitted} additional ${kind} omitted from aggregate diagnostics; per-workspace diagnostics remain complete.`,
|
|
268
|
+
retryable: false,
|
|
269
|
+
},
|
|
270
|
+
], `diagnostics.${kind}`);
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=bulk-export-workspaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk-export-workspaces.js","sourceRoot":"","sources":["../../src/services/bulk-export-workspaces.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { OpaqueCmsReference } from '../contracts/shared.js';
|
|
2
|
+
import type { CmsRecord } from './read.js';
|
|
3
|
+
export type ExportReferenceWarning = {
|
|
4
|
+
code: 'REFERENCE_UNRESOLVED' | 'REFERENCE_UNSUPPORTED';
|
|
5
|
+
message: string;
|
|
6
|
+
variantIds: string[];
|
|
7
|
+
};
|
|
8
|
+
export type ExportReferenceInventory = {
|
|
9
|
+
dependencies: string[];
|
|
10
|
+
externalReferences: OpaqueCmsReference[];
|
|
11
|
+
itemReferenceIds: ReadonlyMap<string, string>;
|
|
12
|
+
warnings: ExportReferenceWarning[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Inventories only the CMS content identity fields retained in exported variant payloads.
|
|
16
|
+
* Unknown payload fields are deliberately not interpreted as relationships.
|
|
17
|
+
* @param {string} workspaceId - Canonical source CMS workspace identifier.
|
|
18
|
+
* @param {ReadonlyMap<string, CmsRecord>} details - Exported variants keyed by variant ID.
|
|
19
|
+
* @returns {ExportReferenceInventory} Deterministic included and unresolved reference evidence.
|
|
20
|
+
*/
|
|
21
|
+
export declare function inventoryExportReferences(workspaceId: string, details: ReadonlyMap<string, CmsRecord>): ExportReferenceInventory;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
function nonemptyString(value) {
|
|
3
|
+
return typeof value === 'string' && value.length > 0;
|
|
4
|
+
}
|
|
5
|
+
function referenceId(workspaceId, kind, sourceId) {
|
|
6
|
+
const identity = JSON.stringify({ kind, sourceId, workspaceId });
|
|
7
|
+
return `ref:${createHash('sha256').update(identity).digest('hex')}`;
|
|
8
|
+
}
|
|
9
|
+
function containsCmsContentBodyReference(value) {
|
|
10
|
+
if (Array.isArray(value))
|
|
11
|
+
return value.some((item_) => containsCmsContentBodyReference(item_));
|
|
12
|
+
if (typeof value !== 'object' || value === null)
|
|
13
|
+
return false;
|
|
14
|
+
const record = value;
|
|
15
|
+
if (typeof record.ref === 'object' &&
|
|
16
|
+
record.ref !== null &&
|
|
17
|
+
nonemptyString(record.ref.contentKey)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return Object.values(record).some((item_) => containsCmsContentBodyReference(item_));
|
|
21
|
+
}
|
|
22
|
+
function compareReferences(left, right) {
|
|
23
|
+
return [left.kind, left.referenceId, left.portableKey.value]
|
|
24
|
+
.join('\u0000')
|
|
25
|
+
.localeCompare([right.kind, right.referenceId, right.portableKey.value].join('\u0000'));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Inventories only the CMS content identity fields retained in exported variant payloads.
|
|
29
|
+
* Unknown payload fields are deliberately not interpreted as relationships.
|
|
30
|
+
* @param {string} workspaceId - Canonical source CMS workspace identifier.
|
|
31
|
+
* @param {ReadonlyMap<string, CmsRecord>} details - Exported variants keyed by variant ID.
|
|
32
|
+
* @returns {ExportReferenceInventory} Deterministic included and unresolved reference evidence.
|
|
33
|
+
*/
|
|
34
|
+
export function inventoryExportReferences(workspaceId, details) {
|
|
35
|
+
const candidates = [];
|
|
36
|
+
const externalReferences = [];
|
|
37
|
+
const itemReferenceIds = new Map();
|
|
38
|
+
const warnings = [];
|
|
39
|
+
const includedById = new Map();
|
|
40
|
+
for (const [variantId, detail] of details) {
|
|
41
|
+
if ('references' in detail ||
|
|
42
|
+
'referencesList' in detail ||
|
|
43
|
+
containsCmsContentBodyReference(detail.contentBody)) {
|
|
44
|
+
const id = referenceId(workspaceId, 'cms.relationship', variantId);
|
|
45
|
+
externalReferences.push({
|
|
46
|
+
referenceId: id,
|
|
47
|
+
owner: 'cms',
|
|
48
|
+
kind: 'cms.relationship',
|
|
49
|
+
source: { workspaceId, sourceId: variantId },
|
|
50
|
+
portableKey: { scheme: 'cms-opaque-v1', value: variantId },
|
|
51
|
+
required: true,
|
|
52
|
+
resolution: 'unsupported',
|
|
53
|
+
});
|
|
54
|
+
warnings.push({
|
|
55
|
+
code: 'REFERENCE_UNSUPPORTED',
|
|
56
|
+
message: 'The exported CMS payload contains a relationship class without an evidenced portable resolver.',
|
|
57
|
+
variantIds: [variantId],
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (!nonemptyString(detail.contentKey))
|
|
61
|
+
continue;
|
|
62
|
+
candidates.push({
|
|
63
|
+
contentId: nonemptyString(detail.contentId) ? detail.contentId : undefined,
|
|
64
|
+
contentKey: detail.contentKey,
|
|
65
|
+
variantId,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const idsByKey = new Map();
|
|
69
|
+
const keysById = new Map();
|
|
70
|
+
const incompleteKeys = new Set();
|
|
71
|
+
for (const candidate of candidates) {
|
|
72
|
+
if (candidate.contentId === undefined) {
|
|
73
|
+
incompleteKeys.add(candidate.contentKey);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const ids = idsByKey.get(candidate.contentKey) ?? new Set();
|
|
77
|
+
ids.add(candidate.contentId);
|
|
78
|
+
idsByKey.set(candidate.contentKey, ids);
|
|
79
|
+
const keys = keysById.get(candidate.contentId) ?? new Set();
|
|
80
|
+
keys.add(candidate.contentKey);
|
|
81
|
+
keysById.set(candidate.contentId, keys);
|
|
82
|
+
}
|
|
83
|
+
for (const candidate of candidates.toSorted((left, right) => left.variantId.localeCompare(right.variantId))) {
|
|
84
|
+
const conflicting = candidate.contentId !== undefined &&
|
|
85
|
+
((idsByKey.get(candidate.contentKey)?.size ?? 0) > 1 ||
|
|
86
|
+
incompleteKeys.has(candidate.contentKey) ||
|
|
87
|
+
(keysById.get(candidate.contentId)?.size ?? 0) > 1);
|
|
88
|
+
if (conflicting) {
|
|
89
|
+
const id = referenceId(workspaceId, 'cms.unknown', candidate.variantId);
|
|
90
|
+
externalReferences.push({
|
|
91
|
+
referenceId: id,
|
|
92
|
+
owner: 'cms',
|
|
93
|
+
kind: 'cms.unknown',
|
|
94
|
+
source: { workspaceId, sourceId: candidate.variantId },
|
|
95
|
+
portableKey: { scheme: 'cms-opaque-v1', value: candidate.contentKey },
|
|
96
|
+
required: true,
|
|
97
|
+
resolution: 'unsupported',
|
|
98
|
+
});
|
|
99
|
+
warnings.push({
|
|
100
|
+
code: 'REFERENCE_UNSUPPORTED',
|
|
101
|
+
message: 'A CMS content reference has conflicting retained identity evidence.',
|
|
102
|
+
variantIds: [candidate.variantId],
|
|
103
|
+
});
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (candidate.contentId === undefined) {
|
|
107
|
+
const id = referenceId(workspaceId, 'cms.unknown', candidate.variantId);
|
|
108
|
+
externalReferences.push({
|
|
109
|
+
referenceId: id,
|
|
110
|
+
owner: 'cms',
|
|
111
|
+
kind: 'cms.unknown',
|
|
112
|
+
source: { workspaceId, sourceId: candidate.variantId },
|
|
113
|
+
portableKey: { scheme: 'cms-opaque-v1', value: candidate.contentKey },
|
|
114
|
+
required: true,
|
|
115
|
+
resolution: 'unresolved',
|
|
116
|
+
});
|
|
117
|
+
warnings.push({
|
|
118
|
+
code: 'REFERENCE_UNRESOLVED',
|
|
119
|
+
message: 'A CMS content reference lacks retained canonical content identity.',
|
|
120
|
+
variantIds: [candidate.variantId],
|
|
121
|
+
});
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const id = referenceId(workspaceId, 'cms.content', candidate.contentId);
|
|
125
|
+
itemReferenceIds.set(candidate.variantId, id);
|
|
126
|
+
if (!includedById.has(candidate.contentId)) {
|
|
127
|
+
includedById.set(candidate.contentId, {
|
|
128
|
+
referenceId: id,
|
|
129
|
+
owner: 'cms',
|
|
130
|
+
kind: 'cms.content',
|
|
131
|
+
source: { workspaceId, sourceId: candidate.contentId },
|
|
132
|
+
portableKey: { scheme: 'cms-opaque-v1', value: candidate.contentKey },
|
|
133
|
+
required: true,
|
|
134
|
+
resolution: 'included',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
externalReferences.push(...includedById.values());
|
|
139
|
+
externalReferences.sort(compareReferences);
|
|
140
|
+
warnings.sort((left, right) => [left.code, left.variantIds.join('\u0000')]
|
|
141
|
+
.join('\u0000')
|
|
142
|
+
.localeCompare([right.code, right.variantIds.join('\u0000')].join('\u0000')));
|
|
143
|
+
return {
|
|
144
|
+
dependencies: [],
|
|
145
|
+
externalReferences,
|
|
146
|
+
itemReferenceIds,
|
|
147
|
+
warnings,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=export-references.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-references.js","sourceRoot":"","sources":["../../src/services/export-references.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Connection } from '@salesforce/core';
|
|
2
|
+
import { type WorkspaceExportManifest } from '../contracts/workspace-export.js';
|
|
3
|
+
export type { WorkspaceExportManifest } from '../contracts/workspace-export.js';
|
|
2
4
|
import { type JsonRequestOptions } from '../transport/json-request.js';
|
|
3
5
|
export type WorkspaceExportWarning = {
|
|
4
|
-
code: 'COUNT_MISMATCH' | 'DETAIL_FAILED' | 'DUPLICATE_VARIANTS' | 'OWNERSHIP_MISMATCH' | 'PREMATURE_EMPTY_PAGE' | 'UNSUPPORTED_WILDCARD';
|
|
6
|
+
code: 'COUNT_MISMATCH' | 'DETAIL_FAILED' | 'DUPLICATE_VARIANTS' | 'OWNERSHIP_MISMATCH' | 'PREMATURE_EMPTY_PAGE' | 'REFERENCE_UNRESOLVED' | 'REFERENCE_UNSUPPORTED' | 'UNSUPPORTED_WILDCARD';
|
|
5
7
|
message: string;
|
|
6
8
|
variantIds?: string[];
|
|
7
9
|
};
|
|
@@ -9,31 +11,17 @@ export type WorkspaceExportEntry = {
|
|
|
9
11
|
file: string;
|
|
10
12
|
variantId: string;
|
|
11
13
|
};
|
|
12
|
-
export type WorkspaceExportManifest = {
|
|
13
|
-
schemaVersion: 1;
|
|
14
|
-
mode: 'experimental-best-effort';
|
|
15
|
-
workspaceId: string;
|
|
16
|
-
search: {
|
|
17
|
-
contentSpaceOrFolderIds: [string];
|
|
18
|
-
languages: ['All'];
|
|
19
|
-
pageSize: 250;
|
|
20
|
-
queryTerm: '*';
|
|
21
|
-
};
|
|
22
|
-
expectedCount: number;
|
|
23
|
-
foundCount: number;
|
|
24
|
-
exportedCount: number;
|
|
25
|
-
pagesRequested: number;
|
|
26
|
-
entries: WorkspaceExportEntry[];
|
|
27
|
-
rejectedVariantIds: string[];
|
|
28
|
-
failedVariantIds: string[];
|
|
29
|
-
warnings: WorkspaceExportWarning[];
|
|
30
|
-
};
|
|
31
14
|
export type ExportWorkspaceResult = {
|
|
32
15
|
destination: string;
|
|
33
16
|
manifest: WorkspaceExportManifest;
|
|
17
|
+
manifestSha256: string;
|
|
18
|
+
};
|
|
19
|
+
export type ExportWorkspaceOptions = JsonRequestOptions & {
|
|
20
|
+
sourceOrgId?: string;
|
|
21
|
+
pluginVersion?: string;
|
|
22
|
+
generatedAt?: string;
|
|
34
23
|
};
|
|
35
24
|
type RequestConnection = Pick<Connection, 'request'>;
|
|
36
25
|
export declare function safeWorkspaceDirectoryName(workspaceName: string): string;
|
|
37
26
|
export declare function defaultWorkspaceDestination(workspaceName: string): string;
|
|
38
|
-
export declare function exportWorkspace(connection: RequestConnection, workspaceId: string, destination: string, options?:
|
|
39
|
-
export {};
|
|
27
|
+
export declare function exportWorkspace(connection: RequestConnection, workspaceId: string, destination: string, options?: ExportWorkspaceOptions): Promise<ExportWorkspaceResult>;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { lstat, mkdir, open, readdir, readFile, rename, rm, stat } from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
4
|
+
import { assertWorkspaceExportManifest, WORKSPACE_EXPORT_MANIFEST_CONTRACT, } from '../contracts/workspace-export.js';
|
|
3
5
|
import { getSelectedOperation, requestJson, } from '../transport/json-request.js';
|
|
6
|
+
import { inventoryExportReferences } from './export-references.js';
|
|
4
7
|
import { getVariant } from './read.js';
|
|
5
8
|
const PAGE_SIZE = 250;
|
|
6
9
|
const ABSOLUTE_PAGE_CAP = 1000;
|
|
@@ -39,6 +42,9 @@ function detailWorkspaceId(detail) {
|
|
|
39
42
|
function jsonBytes(value) {
|
|
40
43
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
41
44
|
}
|
|
45
|
+
function sha256(bytes) {
|
|
46
|
+
return createHash('sha256').update(bytes).digest('hex');
|
|
47
|
+
}
|
|
42
48
|
const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu;
|
|
43
49
|
export function safeWorkspaceDirectoryName(workspaceName) {
|
|
44
50
|
const replaced = [...workspaceName]
|
|
@@ -81,6 +87,34 @@ async function writeExclusive(path, value) {
|
|
|
81
87
|
await handle.close();
|
|
82
88
|
}
|
|
83
89
|
}
|
|
90
|
+
async function assertRegularPackageFiles(root, expectedPaths) {
|
|
91
|
+
const found = new Set();
|
|
92
|
+
const visit = async (directory) => {
|
|
93
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
const absolute = path.join(directory, entry.name);
|
|
96
|
+
const relative = path.relative(root, absolute).split(path.sep).join('/');
|
|
97
|
+
const information = await lstat(absolute);
|
|
98
|
+
if (information.isSymbolicLink())
|
|
99
|
+
throw new Error(`Package entry is a symlink: ${relative}`);
|
|
100
|
+
if (information.isDirectory()) {
|
|
101
|
+
await visit(absolute);
|
|
102
|
+
}
|
|
103
|
+
else if (information.isFile()) {
|
|
104
|
+
if (relative !== 'manifest.json')
|
|
105
|
+
found.add(relative);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
throw new Error(`Package entry is not a regular file or directory: ${relative}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
await visit(root);
|
|
113
|
+
if (found.size !== expectedPaths.size ||
|
|
114
|
+
[...found].some((itemPath) => !expectedPaths.has(itemPath))) {
|
|
115
|
+
throw new Error('Package regular files do not exactly match manifest items.');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
84
118
|
export async function exportWorkspace(connection, workspaceId, destination, options = {}) {
|
|
85
119
|
if (!nonemptyString(workspaceId))
|
|
86
120
|
throw new TypeError('workspaceId must be a nonempty string');
|
|
@@ -197,6 +231,22 @@ export async function exportWorkspace(connection, workspaceId, destination, opti
|
|
|
197
231
|
file: `items/${variantId}.json`,
|
|
198
232
|
variantId,
|
|
199
233
|
}));
|
|
234
|
+
const referenceInventory = inventoryExportReferences(workspaceId, details);
|
|
235
|
+
warnings.push(...referenceInventory.warnings);
|
|
236
|
+
const completeness = warnings.some(({ code }) => code !== 'UNSUPPORTED_WILDCARD') ||
|
|
237
|
+
rejected.size > 0 ||
|
|
238
|
+
failedIds.length > 0
|
|
239
|
+
? 'partial'
|
|
240
|
+
: 'complete';
|
|
241
|
+
const manifestItems = entries.map((entry) => {
|
|
242
|
+
const referenceId = referenceInventory.itemReferenceIds.get(entry.variantId);
|
|
243
|
+
return {
|
|
244
|
+
path: entry.file,
|
|
245
|
+
sha256: sha256(jsonBytes(details.get(entry.variantId))),
|
|
246
|
+
kind: 'cms.content',
|
|
247
|
+
...(referenceId === undefined ? {} : { referenceId }),
|
|
248
|
+
};
|
|
249
|
+
});
|
|
200
250
|
const manifest = {
|
|
201
251
|
schemaVersion: 1,
|
|
202
252
|
mode: 'experimental-best-effort',
|
|
@@ -215,7 +265,21 @@ export async function exportWorkspace(connection, workspaceId, destination, opti
|
|
|
215
265
|
rejectedVariantIds: [...rejected].toSorted(),
|
|
216
266
|
failedVariantIds: failedIds.toSorted(),
|
|
217
267
|
warnings,
|
|
268
|
+
contract: WORKSPACE_EXPORT_MANIFEST_CONTRACT,
|
|
269
|
+
contractVersion: '1.0.0',
|
|
270
|
+
provenance: {
|
|
271
|
+
producer: 'sf-plugin-cms',
|
|
272
|
+
sourceOrgId: options.sourceOrgId ?? 'unknown-org',
|
|
273
|
+
sourceWorkspaceId: workspaceId,
|
|
274
|
+
pluginVersion: options.pluginVersion ?? '0.3.0',
|
|
275
|
+
generatedAt: options.generatedAt ?? new Date().toISOString(),
|
|
276
|
+
},
|
|
277
|
+
completeness,
|
|
278
|
+
dependencies: referenceInventory.dependencies,
|
|
279
|
+
externalReferences: referenceInventory.externalReferences,
|
|
280
|
+
items: manifestItems,
|
|
218
281
|
};
|
|
282
|
+
assertWorkspaceExportManifest(manifest);
|
|
219
283
|
const parent = path.dirname(destination);
|
|
220
284
|
await mkdir(parent, { recursive: true });
|
|
221
285
|
const temporary = path.join(parent, `.${path.basename(destination)}.tmp-${process.pid}-${Date.now()}`);
|
|
@@ -225,6 +289,7 @@ export async function exportWorkspace(connection, workspaceId, destination, opti
|
|
|
225
289
|
for (const entry of entries) {
|
|
226
290
|
await writeExclusive(path.join(temporary, entry.file), details.get(entry.variantId));
|
|
227
291
|
}
|
|
292
|
+
await assertRegularPackageFiles(temporary, new Set(manifest.items.map(({ path }) => path)));
|
|
228
293
|
await writeExclusive(path.join(temporary, 'manifest.json'), manifest);
|
|
229
294
|
await rename(temporary, destination);
|
|
230
295
|
}
|
|
@@ -232,6 +297,7 @@ export async function exportWorkspace(connection, workspaceId, destination, opti
|
|
|
232
297
|
await rm(temporary, { force: true, recursive: true });
|
|
233
298
|
throw error;
|
|
234
299
|
}
|
|
235
|
-
|
|
300
|
+
const manifestSha256 = sha256(await readFile(path.join(destination, 'manifest.json')));
|
|
301
|
+
return { destination, manifest, manifestSha256 };
|
|
236
302
|
}
|
|
237
303
|
//# sourceMappingURL=export-workspace.js.map
|