sf-plugin-cms 0.4.0 → 0.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/README.md +241 -20
- package/lib/commands/cms/export/workspace.d.ts +5 -1
- package/lib/commands/cms/export/workspace.js +60 -2
- package/lib/commands/cms/export/workspace.js.map +1 -1
- package/lib/commands/cms/import/workspace.d.ts +12 -4
- package/lib/commands/cms/import/workspace.js +167 -20
- package/lib/commands/cms/import/workspace.js.map +1 -1
- package/lib/commands/cms/info.js +21 -2
- package/lib/commands/cms/info.js.map +1 -1
- package/lib/contracts/info.d.ts +7 -4
- package/lib/contracts/info.js +17 -8
- package/lib/contracts/info.js.map +1 -1
- package/lib/contracts/shared.d.ts +2 -1
- package/lib/contracts/shared.js +11 -3
- package/lib/contracts/shared.js.map +1 -1
- package/lib/contracts/workspace-export.d.ts +23 -3
- package/lib/contracts/workspace-export.js +118 -5
- package/lib/contracts/workspace-export.js.map +1 -1
- package/lib/contracts/workspace-import.d.ts +64 -0
- package/lib/contracts/workspace-import.js +119 -0
- package/lib/contracts/workspace-import.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/services/email-fragment.d.ts +15 -0
- package/lib/services/email-fragment.js +199 -0
- package/lib/services/email-fragment.js.map +1 -0
- package/lib/services/export-workspace.d.ts +13 -1
- package/lib/services/export-workspace.js +183 -7
- package/lib/services/export-workspace.js.map +1 -1
- package/lib/services/image-import.d.ts +91 -0
- package/lib/services/image-import.js +579 -0
- package/lib/services/image-import.js.map +1 -0
- package/lib/services/import-workspace.d.ts +16 -3
- package/lib/services/import-workspace.js +142 -14
- package/lib/services/import-workspace.js.map +1 -1
- package/lib/services/landing-page-template.d.ts +52 -0
- package/lib/services/landing-page-template.js +421 -0
- package/lib/services/landing-page-template.js.map +1 -0
- package/lib/services/landing-page.d.ts +43 -0
- package/lib/services/landing-page.js +117 -0
- package/lib/services/landing-page.js.map +1 -0
- package/lib/services/web-fragment.d.ts +41 -0
- package/lib/services/web-fragment.js +245 -0
- package/lib/services/web-fragment.js.map +1 -0
- package/lib/transport/experimental-media.d.ts +30 -0
- package/lib/transport/experimental-media.js +224 -0
- package/lib/transport/experimental-media.js.map +1 -0
- package/lib/transport/json-request.d.ts +9 -1
- package/lib/transport/json-request.js +30 -3
- package/lib/transport/json-request.js.map +1 -1
- package/lib/transport/multipart-image-create.d.ts +28 -0
- package/lib/transport/multipart-image-create.js +189 -0
- package/lib/transport/multipart-image-create.js.map +1 -0
- package/messages/cms.import.workspace.md +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { domainToASCII } from 'node:url';
|
|
3
|
+
import { open, unlink } from 'node:fs/promises';
|
|
4
|
+
import { Readable } from 'node:stream';
|
|
5
|
+
import { redactSecrets } from './redact-secrets.js';
|
|
6
|
+
const MEDIA_SUFFIX = 'file.force.com';
|
|
7
|
+
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
8
|
+
const IMAGE_SIGNATURES = new Map([
|
|
9
|
+
['image/gif', [0x47, 0x49, 0x46, 0x38]],
|
|
10
|
+
['image/jpeg', [255, 216, 255]],
|
|
11
|
+
['image/png', [137, 80, 78, 71, 13, 10, 26, 10]],
|
|
12
|
+
['image/webp', [0x52, 0x49, 0x46, 0x46]],
|
|
13
|
+
]);
|
|
14
|
+
const MIME_EXTENSIONS = {
|
|
15
|
+
'image/gif': ['gif'],
|
|
16
|
+
'image/jpeg': ['jpeg', 'jpg'],
|
|
17
|
+
'image/png': ['png'],
|
|
18
|
+
'image/webp': ['webp'],
|
|
19
|
+
};
|
|
20
|
+
export const EXPERIMENTAL_MEDIA_POLICY = {
|
|
21
|
+
approvedRedirectSuffix: MEDIA_SUFFIX,
|
|
22
|
+
maxRedirects: 3,
|
|
23
|
+
perImageBytes: 10 * 1024 * 1024,
|
|
24
|
+
totalBytes: 50 * 1024 * 1024,
|
|
25
|
+
};
|
|
26
|
+
function safeSecondaryDiagnostic(error) {
|
|
27
|
+
const message = redactSecrets(error instanceof Error ? error.message : String(error));
|
|
28
|
+
return message.slice(0, 256);
|
|
29
|
+
}
|
|
30
|
+
async function cleanupFailedDownload(handle, outputFile, primary, unlinkFile, closeAttempted) {
|
|
31
|
+
const secondary = [];
|
|
32
|
+
if (!closeAttempted) {
|
|
33
|
+
try {
|
|
34
|
+
await handle.close();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
secondary.push(`close: ${safeSecondaryDiagnostic(error)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
await unlinkFile(outputFile);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
secondary.push(`unlink: ${safeSecondaryDiagnostic(error)}`);
|
|
45
|
+
}
|
|
46
|
+
if (primary instanceof Error && secondary.length > 0) {
|
|
47
|
+
Object.defineProperty(primary, 'cleanupDiagnostics', {
|
|
48
|
+
configurable: true,
|
|
49
|
+
enumerable: false,
|
|
50
|
+
value: secondary,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
throw primary;
|
|
54
|
+
}
|
|
55
|
+
function normalizedHost(url) {
|
|
56
|
+
const host = domainToASCII(url.hostname.toLowerCase().replace(/\.$/u, ''));
|
|
57
|
+
if (host.length === 0)
|
|
58
|
+
throw new TypeError('Experimental media URL hostname is invalid');
|
|
59
|
+
return host;
|
|
60
|
+
}
|
|
61
|
+
function assertSafeHttps(url) {
|
|
62
|
+
if (url.protocol !== 'https:' || url.username !== '' || url.password !== '') {
|
|
63
|
+
throw new TypeError('Experimental media transport requires HTTPS without userinfo');
|
|
64
|
+
}
|
|
65
|
+
if (url.port !== '' && url.port !== '443') {
|
|
66
|
+
throw new TypeError('Experimental media transport permits only the default HTTPS port');
|
|
67
|
+
}
|
|
68
|
+
return normalizedHost(url);
|
|
69
|
+
}
|
|
70
|
+
export function isApprovedSalesforceMediaUrl(value) {
|
|
71
|
+
try {
|
|
72
|
+
const url = value instanceof URL ? value : new URL(value);
|
|
73
|
+
const host = assertSafeHttps(url);
|
|
74
|
+
return host.endsWith(`.${MEDIA_SUFFIX}`) && host.length > MEDIA_SUFFIX.length + 1;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function sameOrigin(left, right) {
|
|
81
|
+
return assertSafeHttps(left) === assertSafeHttps(right) && left.port === right.port;
|
|
82
|
+
}
|
|
83
|
+
function extension(fileName) {
|
|
84
|
+
const last = fileName.split('.').at(-1)?.toLowerCase();
|
|
85
|
+
if (last === undefined || last === fileName.toLowerCase())
|
|
86
|
+
return '';
|
|
87
|
+
return last;
|
|
88
|
+
}
|
|
89
|
+
function assertImageEvidence(bytes, mimeType, fileName) {
|
|
90
|
+
const allowed = MIME_EXTENSIONS[mimeType];
|
|
91
|
+
const signature = IMAGE_SIGNATURES.get(mimeType);
|
|
92
|
+
if (allowed === undefined || signature === undefined) {
|
|
93
|
+
throw new TypeError(`Experimental media MIME type is unsupported: ${mimeType}`);
|
|
94
|
+
}
|
|
95
|
+
const fileExtension = extension(fileName);
|
|
96
|
+
if (fileExtension.length > 0 && !allowed.includes(fileExtension)) {
|
|
97
|
+
throw new TypeError('Experimental media filename extension does not match MIME type');
|
|
98
|
+
}
|
|
99
|
+
if (!signature.every((byte, index) => bytes[index] === byte)) {
|
|
100
|
+
throw new TypeError('Experimental media signature does not match MIME type');
|
|
101
|
+
}
|
|
102
|
+
if (mimeType === 'image/webp' && bytes.subarray(8, 12).toString('ascii') !== 'WEBP') {
|
|
103
|
+
throw new TypeError('Experimental media WebP signature is invalid');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export async function downloadExperimentalCmsMedia(source, options) {
|
|
107
|
+
const instance = new URL(options.instanceUrl);
|
|
108
|
+
assertSafeHttps(instance);
|
|
109
|
+
if (!sameOrigin(source, instance)) {
|
|
110
|
+
throw new TypeError('Experimental media source must use the authenticated Salesforce instance');
|
|
111
|
+
}
|
|
112
|
+
if (!source.pathname.startsWith('/cms/media/')) {
|
|
113
|
+
throw new TypeError('Experimental media source path is unsupported');
|
|
114
|
+
}
|
|
115
|
+
const fetcher = options.fetch ?? fetch;
|
|
116
|
+
let current = source;
|
|
117
|
+
let response;
|
|
118
|
+
for (let redirects = 0; redirects <= EXPERIMENTAL_MEDIA_POLICY.maxRedirects; redirects += 1) {
|
|
119
|
+
const onInstance = sameOrigin(current, instance);
|
|
120
|
+
if (!onInstance && !isApprovedSalesforceMediaUrl(current)) {
|
|
121
|
+
throw new TypeError('Experimental media redirect target is outside the approved Salesforce class');
|
|
122
|
+
}
|
|
123
|
+
response = await fetcher(current, {
|
|
124
|
+
headers: {
|
|
125
|
+
accept: 'image/*,application/octet-stream;q=0.8',
|
|
126
|
+
authorization: `Bearer ${options.accessToken}`,
|
|
127
|
+
},
|
|
128
|
+
redirect: 'manual',
|
|
129
|
+
});
|
|
130
|
+
if (!REDIRECT_STATUSES.has(response.status))
|
|
131
|
+
break;
|
|
132
|
+
if (redirects === EXPERIMENTAL_MEDIA_POLICY.maxRedirects) {
|
|
133
|
+
throw new TypeError('Experimental media redirect count exceeded');
|
|
134
|
+
}
|
|
135
|
+
const location = response.headers.get('location');
|
|
136
|
+
if (location === null)
|
|
137
|
+
throw new TypeError('Experimental media redirect has no location');
|
|
138
|
+
const target = new URL(location, current);
|
|
139
|
+
assertSafeHttps(target);
|
|
140
|
+
if (!isApprovedSalesforceMediaUrl(target) && !sameOrigin(target, instance)) {
|
|
141
|
+
throw new TypeError('Experimental media redirect target is outside the approved Salesforce class');
|
|
142
|
+
}
|
|
143
|
+
current = target;
|
|
144
|
+
}
|
|
145
|
+
if (response === undefined || !response.ok || response.body === null) {
|
|
146
|
+
throw new TypeError(`Experimental media request failed with status ${response?.status ?? 0}`);
|
|
147
|
+
}
|
|
148
|
+
const responseType = response.headers.get('content-type')?.split(';', 1)[0].trim().toLowerCase();
|
|
149
|
+
if (responseType === undefined || responseType === 'text/html' || responseType.includes('json')) {
|
|
150
|
+
throw new TypeError('Experimental media response is not binary image content');
|
|
151
|
+
}
|
|
152
|
+
if (responseType !== options.expectedMimeType) {
|
|
153
|
+
throw new TypeError('Experimental media response MIME type changed');
|
|
154
|
+
}
|
|
155
|
+
const declaredLengthText = response.headers.get('content-length');
|
|
156
|
+
const declaredLength = declaredLengthText === null ? undefined : Number(declaredLengthText);
|
|
157
|
+
const configuredLimit = options.maxBytes ?? EXPERIMENTAL_MEDIA_POLICY.perImageBytes;
|
|
158
|
+
if (!Number.isSafeInteger(configuredLimit) || configuredLimit <= 0) {
|
|
159
|
+
throw new TypeError('Experimental media byte cap must be a positive safe integer');
|
|
160
|
+
}
|
|
161
|
+
const limit = Math.min(configuredLimit, EXPERIMENTAL_MEDIA_POLICY.perImageBytes);
|
|
162
|
+
if (declaredLength !== undefined &&
|
|
163
|
+
(!Number.isSafeInteger(declaredLength) || declaredLength < 0 || declaredLength > limit)) {
|
|
164
|
+
throw new TypeError('Experimental media Content-Length is invalid or exceeds the cap');
|
|
165
|
+
}
|
|
166
|
+
const fileName = source.searchParams.get('fileName');
|
|
167
|
+
if (fileName === null ||
|
|
168
|
+
fileName.length === 0 ||
|
|
169
|
+
fileName.includes('/') ||
|
|
170
|
+
fileName.includes('\\')) {
|
|
171
|
+
throw new TypeError('Experimental media filename is invalid');
|
|
172
|
+
}
|
|
173
|
+
const md5 = createHash('md5');
|
|
174
|
+
const sha256 = createHash('sha256');
|
|
175
|
+
const prefix = [];
|
|
176
|
+
let prefixLength = 0;
|
|
177
|
+
let bytes = 0;
|
|
178
|
+
const handle = await (options.openFile ?? (async (file) => open(file, 'wx')))(options.outputFile);
|
|
179
|
+
const unlinkFile = options.unlinkFile ?? unlink;
|
|
180
|
+
let closeAttempted = false;
|
|
181
|
+
try {
|
|
182
|
+
for await (const chunkValue of Readable.fromWeb(response.body)) {
|
|
183
|
+
const chunk = Buffer.isBuffer(chunkValue)
|
|
184
|
+
? chunkValue
|
|
185
|
+
: Buffer.from(chunkValue);
|
|
186
|
+
if (chunk.length > limit - bytes) {
|
|
187
|
+
throw new TypeError('Experimental media response exceeds the byte cap');
|
|
188
|
+
}
|
|
189
|
+
bytes += chunk.length;
|
|
190
|
+
if (prefixLength < 12) {
|
|
191
|
+
const selected = chunk.subarray(0, 12 - prefixLength);
|
|
192
|
+
prefix.push(selected);
|
|
193
|
+
prefixLength += selected.length;
|
|
194
|
+
}
|
|
195
|
+
md5.update(chunk);
|
|
196
|
+
sha256.update(chunk);
|
|
197
|
+
await handle.write(chunk);
|
|
198
|
+
}
|
|
199
|
+
if (declaredLength !== undefined && bytes !== declaredLength) {
|
|
200
|
+
throw new TypeError('Experimental media byte count differs from Content-Length');
|
|
201
|
+
}
|
|
202
|
+
if (bytes !== options.expectedSize) {
|
|
203
|
+
throw new TypeError('Experimental media byte count differs from authoring metadata');
|
|
204
|
+
}
|
|
205
|
+
const actualMd5 = md5.digest('hex');
|
|
206
|
+
if (actualMd5 !== options.expectedMd5.toLowerCase()) {
|
|
207
|
+
throw new TypeError('Experimental media MD5 differs from the authoring URL');
|
|
208
|
+
}
|
|
209
|
+
assertImageEvidence(Buffer.concat(prefix), options.expectedMimeType, fileName);
|
|
210
|
+
closeAttempted = true;
|
|
211
|
+
await handle.close();
|
|
212
|
+
return {
|
|
213
|
+
bytes,
|
|
214
|
+
...(declaredLength === undefined ? {} : { contentLength: declaredLength }),
|
|
215
|
+
md5: actualMd5,
|
|
216
|
+
mimeType: options.expectedMimeType,
|
|
217
|
+
sha256: sha256.digest('hex'),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
return cleanupFailedDownload(handle, options.outputFile, error, unlinkFile, closeAttempted);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=experimental-media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experimental-media.js","sourceRoot":"","sources":["../../src/transport/experimental-media.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,10 +16,18 @@ export type JsonSuccess<T> = {
|
|
|
16
16
|
};
|
|
17
17
|
type RequestConnection = Pick<Connection, 'request'>;
|
|
18
18
|
export declare class CmsRequestError extends Error {
|
|
19
|
+
readonly errorCode?: string;
|
|
20
|
+
readonly errorEntryCount: number;
|
|
19
21
|
readonly operationKey: SelectedOperation['localKey'];
|
|
22
|
+
readonly responseMessage?: string;
|
|
20
23
|
readonly status?: number;
|
|
21
|
-
constructor(operationKey: SelectedOperation['localKey'], message: string, status?: number);
|
|
24
|
+
constructor(operationKey: SelectedOperation['localKey'], message: string, status?: number, evidence?: ErrorEvidence | string);
|
|
22
25
|
}
|
|
26
|
+
type ErrorEvidence = {
|
|
27
|
+
errorCode?: string;
|
|
28
|
+
errorEntryCount?: number;
|
|
29
|
+
responseMessage?: string;
|
|
30
|
+
};
|
|
23
31
|
export declare function buildOperationUrl(operation: SelectedOperation, values?: JsonRequestValues): string;
|
|
24
32
|
export declare function requestJson<T>(connection: RequestConnection, operation: SelectedOperation, values?: JsonRequestValues, options?: JsonRequestOptions): Promise<JsonSuccess<T>>;
|
|
25
33
|
export declare function getSelectedOperation(operationKey: SelectedOperation['localKey']): SelectedOperation;
|
|
@@ -2,12 +2,22 @@ import { SELECTED_OPERATIONS } from '../generated/operations.js';
|
|
|
2
2
|
import { redactSecrets } from './redact-secrets.js';
|
|
3
3
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
4
4
|
export class CmsRequestError extends Error {
|
|
5
|
+
errorCode;
|
|
6
|
+
errorEntryCount;
|
|
5
7
|
operationKey;
|
|
8
|
+
responseMessage;
|
|
6
9
|
status;
|
|
7
|
-
constructor(operationKey, message, status) {
|
|
8
|
-
|
|
10
|
+
constructor(operationKey, message, status, evidence = {}) {
|
|
11
|
+
const normalizedEvidence = typeof evidence === 'string' ? { errorCode: evidence } : evidence;
|
|
12
|
+
super(redactSecrets(message));
|
|
9
13
|
this.name = 'CmsRequestError';
|
|
14
|
+
this.errorCode = normalizedEvidence.errorCode;
|
|
15
|
+
this.errorEntryCount = normalizedEvidence.errorEntryCount ?? 0;
|
|
10
16
|
this.operationKey = operationKey;
|
|
17
|
+
this.responseMessage =
|
|
18
|
+
normalizedEvidence.responseMessage === undefined
|
|
19
|
+
? undefined
|
|
20
|
+
: redactSecrets(normalizedEvidence.responseMessage);
|
|
11
21
|
this.status = status;
|
|
12
22
|
}
|
|
13
23
|
}
|
|
@@ -28,6 +38,21 @@ function errorStatus(error) {
|
|
|
28
38
|
return 404;
|
|
29
39
|
return undefined;
|
|
30
40
|
}
|
|
41
|
+
function errorEvidence(error) {
|
|
42
|
+
if (!isRecord(error))
|
|
43
|
+
return { errorEntryCount: 0 };
|
|
44
|
+
const data = error.data;
|
|
45
|
+
const entries = Array.isArray(data) ? data : [data];
|
|
46
|
+
if (entries.length !== 1 || !isRecord(entries[0])) {
|
|
47
|
+
return { errorEntryCount: Array.isArray(data) ? data.length : 0 };
|
|
48
|
+
}
|
|
49
|
+
const entry = entries[0];
|
|
50
|
+
return {
|
|
51
|
+
...(typeof entry.errorCode === 'string' ? { errorCode: entry.errorCode } : {}),
|
|
52
|
+
errorEntryCount: 1,
|
|
53
|
+
...(typeof entry.message === 'string' ? { responseMessage: redactSecrets(entry.message) } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
31
56
|
function safeErrorMessage(error) {
|
|
32
57
|
const message = error instanceof Error ? error.message : String(error);
|
|
33
58
|
return redactSecrets(message || 'CMS request failed');
|
|
@@ -119,8 +144,10 @@ export async function requestJson(connection, operation, values = {}, options =
|
|
|
119
144
|
return { data, operationKey: operation.localKey, status: successStatus(operation) };
|
|
120
145
|
}
|
|
121
146
|
catch (error) {
|
|
147
|
+
if (error instanceof CmsRequestError)
|
|
148
|
+
throw error;
|
|
122
149
|
const message = options.signal?.aborted ? 'CMS request cancelled' : safeErrorMessage(error);
|
|
123
|
-
throw new CmsRequestError(operation.localKey, message, errorStatus(error));
|
|
150
|
+
throw new CmsRequestError(operation.localKey, message, errorStatus(error), errorEvidence(error));
|
|
124
151
|
}
|
|
125
152
|
finally {
|
|
126
153
|
options.signal?.removeEventListener('abort', cancel);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-request.js","sourceRoot":"","sources":["../../src/transport/json-request.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAA0B,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAkCpD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxB,YAAY,CAAgC;IAC5C,MAAM,CAAU;IAEhC,YACE,YAA2C,EAC3C,OAAe,EACf,MAAe;
|
|
1
|
+
{"version":3,"file":"json-request.js","sourceRoot":"","sources":["../../src/transport/json-request.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Connection } from '@salesforce/core';
|
|
2
|
+
import { type JsonRequestOptions } from './json-request.js';
|
|
3
|
+
export declare const MAX_IMAGE_BYTES: number;
|
|
4
|
+
export type ImageCreateInput = {
|
|
5
|
+
apiName?: string;
|
|
6
|
+
contentKey?: string;
|
|
7
|
+
contentSpaceOrFolderId: string;
|
|
8
|
+
title: string;
|
|
9
|
+
urlName?: string;
|
|
10
|
+
};
|
|
11
|
+
export type ManagedContentDocumentBinding = {
|
|
12
|
+
contentKey: string;
|
|
13
|
+
managedContentId: string;
|
|
14
|
+
managedContentVariantId: string;
|
|
15
|
+
};
|
|
16
|
+
export type MultipartImageCreateOptions = JsonRequestOptions & {
|
|
17
|
+
boundaryFactory?: () => string;
|
|
18
|
+
maxBoundaryAttempts?: number;
|
|
19
|
+
};
|
|
20
|
+
type RequestConnection = Pick<Connection, 'request'> & Partial<Pick<Connection, 'accessToken' | 'instanceUrl' | 'version'>>;
|
|
21
|
+
type MultipartParts = {
|
|
22
|
+
body: Buffer;
|
|
23
|
+
boundary: string;
|
|
24
|
+
input: Readonly<Record<string, unknown>>;
|
|
25
|
+
};
|
|
26
|
+
export declare function buildImageCreateMultipart(input: ImageCreateInput, filename: string, image: Uint8Array, options?: Pick<MultipartImageCreateOptions, 'boundaryFactory' | 'maxBoundaryAttempts'>): MultipartParts;
|
|
27
|
+
export declare function createImageContent(connection: RequestConnection, input: ImageCreateInput, filename: string, image: Uint8Array, options?: MultipartImageCreateOptions): Promise<ManagedContentDocumentBinding>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Connection as JsforceConnection } from '@jsforce/jsforce-node';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { CmsRequestError } from './json-request.js';
|
|
4
|
+
import { redactSecrets } from './redact-secrets.js';
|
|
5
|
+
export const MAX_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
6
|
+
const OPERATION_KEY = 'content.create';
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
8
|
+
const DEFAULT_BOUNDARY_ATTEMPTS = 8;
|
|
9
|
+
const ALLOWED_INPUT_KEYS = new Set([
|
|
10
|
+
'apiName',
|
|
11
|
+
'contentKey',
|
|
12
|
+
'contentSpaceOrFolderId',
|
|
13
|
+
'title',
|
|
14
|
+
'urlName',
|
|
15
|
+
]);
|
|
16
|
+
const REQUIRED_RESPONSE_KEYS = [
|
|
17
|
+
'contentKey',
|
|
18
|
+
'managedContentId',
|
|
19
|
+
'managedContentVariantId',
|
|
20
|
+
];
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function errorStatus(error) {
|
|
25
|
+
if (!isRecord(error))
|
|
26
|
+
return undefined;
|
|
27
|
+
for (const key of ['statusCode', 'status']) {
|
|
28
|
+
const value = error[key];
|
|
29
|
+
if (typeof value === 'number' && Number.isInteger(value))
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
function safeErrorMessage(error) {
|
|
35
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
36
|
+
return redactSecrets(message || 'CMS image create failed').slice(0, 1024);
|
|
37
|
+
}
|
|
38
|
+
function withoutRefreshReplay(connection) {
|
|
39
|
+
return connection.accessToken && connection.instanceUrl
|
|
40
|
+
? new JsforceConnection({
|
|
41
|
+
accessToken: connection.accessToken,
|
|
42
|
+
instanceUrl: connection.instanceUrl,
|
|
43
|
+
version: connection.version,
|
|
44
|
+
})
|
|
45
|
+
: connection;
|
|
46
|
+
}
|
|
47
|
+
function hasControlCharacter(value) {
|
|
48
|
+
return [...value].some((character) => {
|
|
49
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
50
|
+
return codePoint <= 31 || codePoint === 127;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function assertPlainNonemptyString(value, name) {
|
|
54
|
+
if (typeof value !== 'string' || value.length === 0 || hasControlCharacter(value)) {
|
|
55
|
+
throw new CmsRequestError(OPERATION_KEY, `Invalid image create field: ${name}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function validateInput(input) {
|
|
59
|
+
if (!isRecord(input)) {
|
|
60
|
+
throw new CmsRequestError(OPERATION_KEY, 'Image create input must be an object');
|
|
61
|
+
}
|
|
62
|
+
for (const key of Object.keys(input)) {
|
|
63
|
+
if (!ALLOWED_INPUT_KEYS.has(key)) {
|
|
64
|
+
throw new CmsRequestError(OPERATION_KEY, `Unsupported image create field: ${key}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
assertPlainNonemptyString(input.contentSpaceOrFolderId, 'contentSpaceOrFolderId');
|
|
68
|
+
assertPlainNonemptyString(input.title, 'title');
|
|
69
|
+
for (const key of ['apiName', 'contentKey', 'urlName']) {
|
|
70
|
+
if (input[key] !== undefined)
|
|
71
|
+
assertPlainNonemptyString(input[key], key);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function validateFilename(filename) {
|
|
75
|
+
if (filename.length === 0 ||
|
|
76
|
+
filename === '.' ||
|
|
77
|
+
filename === '..' ||
|
|
78
|
+
filename.includes('/') ||
|
|
79
|
+
filename.includes('\\') ||
|
|
80
|
+
filename.includes('"') ||
|
|
81
|
+
hasControlCharacter(filename)) {
|
|
82
|
+
throw new CmsRequestError(OPERATION_KEY, 'Invalid image filename');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function defaultBoundaryFactory() {
|
|
86
|
+
return `sf-plugin-cms-${randomBytes(24).toString('hex')}`;
|
|
87
|
+
}
|
|
88
|
+
function validateBoundary(boundary) {
|
|
89
|
+
if (!/^[A-Za-z0-9'()+_,./:=?-]{1,70}$/u.test(boundary)) {
|
|
90
|
+
throw new CmsRequestError(OPERATION_KEY, 'Invalid multipart boundary');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function containsBoundary(buffer, boundary) {
|
|
94
|
+
return buffer.includes(Buffer.from(boundary, 'ascii'));
|
|
95
|
+
}
|
|
96
|
+
function selectBoundary(json, image, factory, maxAttempts) {
|
|
97
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) {
|
|
98
|
+
throw new CmsRequestError(OPERATION_KEY, 'Boundary attempt limit must be a positive integer');
|
|
99
|
+
}
|
|
100
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
101
|
+
const boundary = factory();
|
|
102
|
+
validateBoundary(boundary);
|
|
103
|
+
if (!containsBoundary(json, boundary) && !containsBoundary(image, boundary))
|
|
104
|
+
return boundary;
|
|
105
|
+
}
|
|
106
|
+
throw new CmsRequestError(OPERATION_KEY, 'Unable to generate collision-free multipart boundary');
|
|
107
|
+
}
|
|
108
|
+
export function buildImageCreateMultipart(input, filename, image, options = {}) {
|
|
109
|
+
validateInput(input);
|
|
110
|
+
validateFilename(filename);
|
|
111
|
+
if (!(image instanceof Uint8Array)) {
|
|
112
|
+
throw new CmsRequestError(OPERATION_KEY, 'Image data must be binary');
|
|
113
|
+
}
|
|
114
|
+
if (image.byteLength === 0) {
|
|
115
|
+
throw new CmsRequestError(OPERATION_KEY, 'Image data must not be empty');
|
|
116
|
+
}
|
|
117
|
+
if (image.byteLength > MAX_IMAGE_BYTES) {
|
|
118
|
+
throw new CmsRequestError(OPERATION_KEY, `Image exceeds ${MAX_IMAGE_BYTES} byte limit`);
|
|
119
|
+
}
|
|
120
|
+
const managedContentInput = {
|
|
121
|
+
...input,
|
|
122
|
+
contentType: 'sfdc_cms__image',
|
|
123
|
+
contentBody: { 'sfdc_cms:media': { source: { type: 'file' } } },
|
|
124
|
+
};
|
|
125
|
+
const json = Buffer.from(JSON.stringify(managedContentInput), 'utf8');
|
|
126
|
+
const binary = Buffer.from(image.buffer, image.byteOffset, image.byteLength);
|
|
127
|
+
const boundary = selectBoundary(json, binary, options.boundaryFactory ?? defaultBoundaryFactory, options.maxBoundaryAttempts ?? DEFAULT_BOUNDARY_ATTEMPTS);
|
|
128
|
+
const firstHeader = Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="ManagedContentInputParam"\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n`, 'ascii');
|
|
129
|
+
const secondHeader = Buffer.from(`\r\n--${boundary}\r\nContent-Disposition: form-data; name="contentData"; filename="${filename}"\r\nContent-Type: application/octet-stream; charset=ISO-8859-1\r\n\r\n`, 'ascii');
|
|
130
|
+
const closing = Buffer.from(`\r\n--${boundary}--\r\n`, 'ascii');
|
|
131
|
+
return {
|
|
132
|
+
body: Buffer.concat([firstHeader, json, secondHeader, binary, closing]),
|
|
133
|
+
boundary,
|
|
134
|
+
input: managedContentInput,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function parseManagedContentDocument(value) {
|
|
138
|
+
if (!isRecord(value)) {
|
|
139
|
+
throw new CmsRequestError(OPERATION_KEY, 'Malformed CMS image create response');
|
|
140
|
+
}
|
|
141
|
+
for (const key of REQUIRED_RESPONSE_KEYS) {
|
|
142
|
+
if (typeof value[key] !== 'string' || value[key].length === 0) {
|
|
143
|
+
throw new CmsRequestError(OPERATION_KEY, 'Malformed CMS image create response');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
contentKey: value.contentKey,
|
|
148
|
+
managedContentId: value.managedContentId,
|
|
149
|
+
managedContentVariantId: value.managedContentVariantId,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
export async function createImageContent(connection, input, filename, image, options = {}) {
|
|
153
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
154
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
155
|
+
throw new CmsRequestError(OPERATION_KEY, 'Request timeout must be a positive number');
|
|
156
|
+
}
|
|
157
|
+
if (options.signal?.aborted) {
|
|
158
|
+
throw new CmsRequestError(OPERATION_KEY, 'CMS image create cancelled');
|
|
159
|
+
}
|
|
160
|
+
const multipart = buildImageCreateMultipart(input, filename, image, options);
|
|
161
|
+
const request = withoutRefreshReplay(connection).request({
|
|
162
|
+
body: multipart.body,
|
|
163
|
+
headers: {
|
|
164
|
+
'content-length': String(multipart.body.byteLength),
|
|
165
|
+
'content-type': `multipart/form-data; boundary=${multipart.boundary}`,
|
|
166
|
+
},
|
|
167
|
+
method: 'POST',
|
|
168
|
+
url: '/connect/cms/contents',
|
|
169
|
+
}, { retry: { maxRetries: 0 }, timeout: timeoutMs });
|
|
170
|
+
const cancel = () => {
|
|
171
|
+
request.stream().destroy(new Error('CMS image create cancelled'));
|
|
172
|
+
};
|
|
173
|
+
options.signal?.addEventListener('abort', cancel, { once: true });
|
|
174
|
+
try {
|
|
175
|
+
return parseManagedContentDocument(await request);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (error instanceof CmsRequestError)
|
|
179
|
+
throw error;
|
|
180
|
+
const message = options.signal?.aborted
|
|
181
|
+
? 'CMS image create cancelled'
|
|
182
|
+
: safeErrorMessage(error);
|
|
183
|
+
throw new CmsRequestError(OPERATION_KEY, message, errorStatus(error));
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
options.signal?.removeEventListener('abort', cancel);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=multipart-image-create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multipart-image-create.js","sourceRoot":"","sources":["../../src/transport/multipart-image-create.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,6 +28,8 @@ Validates the complete source workspace export locally before any org request, s
|
|
|
28
28
|
|
|
29
29
|
`--editable-dir` requires `--native-copy-map`. `--source-dir` must remain the unchanged integrity baseline; the companion may change only declared literal HTML files and is not itself an export package. The complete baseline, including unselected items, is integrity-validated. Structured/package reference and non-HTML metadata guards remain active, but native-copy and edited `rawHtml` are treated as opaque literal content through Phase 7. Embedded dependencies, media, references, dynamic syntax, and URLs are not discovered, resolved, rewritten, sanitized, or scanned. Dry-run success is not apply readiness or dependency/safety proof.
|
|
30
30
|
|
|
31
|
+
`--email-fragment-map` rows contain exact `source` and `target` objects. `source` must be `{ "family": "cms", "type": "emailFragment", "apiName": "..." }` and resolve exactly once to `sfdc_cms__emailFragment`; missing, ambiguous, wrong-type, and duplicate selections are rejected. `target` contains fresh `contentKey` and `apiName`. Source content keys remain provenance rather than selectors.
|
|
32
|
+
|
|
31
33
|
JSON uses `sf-cms-workspace-import@1`. Package integrity and the independently declared manifest major are validated before org access. Exit 0 means success, 2 means partial with a usable result, and 1 means failed or blocked.
|
|
32
34
|
|
|
33
35
|
# flags.target-org.summary
|
|
@@ -58,6 +60,10 @@ Unchanged source workspace export containing manifest.json and items/. The edita
|
|
|
58
60
|
|
|
59
61
|
JSON array selecting eligible native raw-HTML email/template variants by sourceContentKey and language, with fresh apiName and urlName values. CREATE uses server-generated keys; destination name availability is not prevalidated.
|
|
60
62
|
|
|
63
|
+
# flags.email-fragment-map.summary
|
|
64
|
+
|
|
65
|
+
JSON array selecting exact typed cms/emailFragment API names with fresh target contentKey and apiName values. Source content keys remain provenance.
|
|
66
|
+
|
|
61
67
|
# flags.editable-dir.summary
|
|
62
68
|
|
|
63
69
|
Verified HTML-only companion for --native-copy-map. Requires the unchanged --source-dir baseline; opaque rawHtml is not dependency-resolved, sanitized, or scanned through Phase 7.
|