sf-plugin-cms 0.3.1 → 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 +334 -28
- package/lib/commands/cms/export/workspace.d.ts +6 -1
- package/lib/commands/cms/export/workspace.js +80 -3
- package/lib/commands/cms/export/workspace.js.map +1 -1
- package/lib/commands/cms/import/workspace.d.ts +14 -4
- package/lib/commands/cms/import/workspace.js +199 -24
- 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/bulk-export-workspaces.js +10 -4
- package/lib/services/bulk-export-workspaces.js.map +1 -1
- package/lib/services/editable-raw-html-export.d.ts +26 -0
- package/lib/services/editable-raw-html-export.js +100 -0
- package/lib/services/editable-raw-html-export.js.map +1 -0
- package/lib/services/editable-raw-html-import.d.ts +25 -0
- package/lib/services/editable-raw-html-import.js +107 -0
- package/lib/services/editable-raw-html-import.js.map +1 -0
- package/lib/services/editable-raw-html.d.ts +34 -0
- package/lib/services/editable-raw-html.js +83 -0
- package/lib/services/editable-raw-html.js.map +1 -0
- 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-references.js +3 -1
- package/lib/services/export-references.js.map +1 -1
- package/lib/services/export-workspace.d.ts +16 -1
- package/lib/services/export-workspace.js +222 -15
- 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-identities.d.ts +33 -0
- package/lib/services/import-identities.js +287 -0
- package/lib/services/import-identities.js.map +1 -0
- package/lib/services/import-workspace.d.ts +50 -1
- package/lib/services/import-workspace.js +445 -21
- 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/resolve-workspace.d.ts +6 -1
- package/lib/services/resolve-workspace.js +51 -4
- package/lib/services/resolve-workspace.js.map +1 -1
- package/lib/services/variant-identity.d.ts +13 -0
- package/lib/services/variant-identity.js +34 -0
- package/lib/services/variant-identity.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.export.workspace.md +12 -2
- package/messages/cms.import.workspace.md +29 -3
- package/package.json +1 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
2
|
+
const WEB_FRAGMENT_TYPE = 'sfdc_cms__webFragment';
|
|
3
|
+
const DATA_GRAPH_PROVIDER = 'sfdc_cms__dataGraphDataProvider';
|
|
4
|
+
const BODY_KEYS = [
|
|
5
|
+
'lightning:backgroundImage',
|
|
6
|
+
'lightning:brandSource',
|
|
7
|
+
'lightning:dataProviders',
|
|
8
|
+
'lightning:expressions',
|
|
9
|
+
'sfdc_cms:block',
|
|
10
|
+
'sfdc_cms:title',
|
|
11
|
+
'sfdc_cms:variants',
|
|
12
|
+
];
|
|
13
|
+
const BACKGROUND_IMAGE = { position: 'center center', repeat: 'no-repeat', size: 'cover' };
|
|
14
|
+
function record(value) {
|
|
15
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
function exactKeys(value, expected, label) {
|
|
18
|
+
if (Object.keys(value).toSorted().join('\0') !== [...expected].toSorted().join('\0')) {
|
|
19
|
+
throw new TypeError(`${label} must contain exactly ${expected.join(', ')}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function nonempty(value, label) {
|
|
23
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
24
|
+
throw new TypeError(`${label} must be a nonempty string`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function identifier(value, label) {
|
|
28
|
+
nonempty(value, label);
|
|
29
|
+
if (!/^[A-Za-z][A-Za-z\d_]*$/u.test(value)) {
|
|
30
|
+
throw new TypeError(`${label} must be an exact developer/API name`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function dataSpace(value, label) {
|
|
34
|
+
nonempty(value, label);
|
|
35
|
+
if (!/^[A-Za-z][A-Za-z\d_]*$/u.test(value)) {
|
|
36
|
+
throw new TypeError(`${label} must be an exact data-space developer name`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function assertBlock(value) {
|
|
40
|
+
if (!record(value))
|
|
41
|
+
throw new TypeError('Web fragment sfdc_cms:block must be an object');
|
|
42
|
+
if (value.definition !== 'sfdc_cms/rootContentBlock' || value.type !== 'block') {
|
|
43
|
+
throw new TypeError('Web fragment root block definition/type is unsupported');
|
|
44
|
+
}
|
|
45
|
+
nonempty(value.id, 'Web fragment root block id');
|
|
46
|
+
if (!Array.isArray(value.children))
|
|
47
|
+
throw new TypeError('Web fragment root children must be an array');
|
|
48
|
+
}
|
|
49
|
+
function provider(value, label) {
|
|
50
|
+
if (!record(value))
|
|
51
|
+
throw new TypeError(`${label} must be an object`);
|
|
52
|
+
exactKeys(value, ['attributes', 'definition', 'sfdcExpressionKey'], label);
|
|
53
|
+
if (value.definition !== DATA_GRAPH_PROVIDER || value.sfdcExpressionKey !== '$dataGraph') {
|
|
54
|
+
throw new TypeError(`${label} must use the exact Data Graph provider definition`);
|
|
55
|
+
}
|
|
56
|
+
if (!record(value.attributes))
|
|
57
|
+
throw new TypeError(`${label}.attributes must be an object`);
|
|
58
|
+
exactKeys(value.attributes, ['dataGraphApiName', 'dataspace'], `${label}.attributes`);
|
|
59
|
+
identifier(value.attributes.dataGraphApiName, `${label}.attributes.dataGraphApiName`);
|
|
60
|
+
dataSpace(value.attributes.dataspace, `${label}.attributes.dataspace`);
|
|
61
|
+
return {
|
|
62
|
+
developerName: value.attributes.dataGraphApiName,
|
|
63
|
+
dataSpace: value.attributes.dataspace,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate the evidenced landing-page content-block shape.
|
|
68
|
+
* @param {WorkspaceImportItem} item - Integrity-verified web-fragment item.
|
|
69
|
+
* @returns {{developerName: string, dataSpace: string}[]} Exact named Data Graph prerequisites.
|
|
70
|
+
*/
|
|
71
|
+
export function assertWebFragmentItem(item) {
|
|
72
|
+
if (item.contentType !== WEB_FRAGMENT_TYPE) {
|
|
73
|
+
throw new TypeError(`Web fragment profile requires exact content type ${WEB_FRAGMENT_TYPE}`);
|
|
74
|
+
}
|
|
75
|
+
identifier(item.apiName, 'Web fragment apiName');
|
|
76
|
+
nonempty(item.title, 'Web fragment title');
|
|
77
|
+
exactKeys(item.contentBody, BODY_KEYS, 'Web fragment contentBody');
|
|
78
|
+
if (item.contentBody['sfdc_cms:title'] !== item.title) {
|
|
79
|
+
throw new TypeError('Web fragment body title must match the top-level title');
|
|
80
|
+
}
|
|
81
|
+
if (!isDeepStrictEqual(item.contentBody['lightning:brandSource'], {
|
|
82
|
+
defaultBrandOption: 'sfdcBrand',
|
|
83
|
+
})) {
|
|
84
|
+
throw new TypeError('Web fragment brand source must match the evidenced Salesforce-brand profile');
|
|
85
|
+
}
|
|
86
|
+
if (!isDeepStrictEqual(item.contentBody['lightning:backgroundImage'], BACKGROUND_IMAGE)) {
|
|
87
|
+
throw new TypeError('Web fragment background image must match the evidenced profile');
|
|
88
|
+
}
|
|
89
|
+
if (!Array.isArray(item.contentBody['lightning:expressions'])) {
|
|
90
|
+
throw new TypeError('Web fragment lightning:expressions must be an array');
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(item.contentBody['sfdc_cms:variants'])) {
|
|
93
|
+
throw new TypeError('Web fragment sfdc_cms:variants must be an array');
|
|
94
|
+
}
|
|
95
|
+
assertBlock(item.contentBody['sfdc_cms:block']);
|
|
96
|
+
const providers = item.contentBody['lightning:dataProviders'];
|
|
97
|
+
if (!Array.isArray(providers) || providers.length === 0) {
|
|
98
|
+
throw new TypeError('Web fragment requires at least one exact named Data Graph provider');
|
|
99
|
+
}
|
|
100
|
+
const prerequisites = providers.map((value, index) => provider(value, `Data Graph provider ${index}`));
|
|
101
|
+
const identities = new Set(prerequisites.map(({ developerName, dataSpace: space }) => `${developerName}\0${space}`));
|
|
102
|
+
if (identities.size !== prerequisites.length) {
|
|
103
|
+
throw new TypeError('Web fragment Data Graph providers must be unique');
|
|
104
|
+
}
|
|
105
|
+
return prerequisites;
|
|
106
|
+
}
|
|
107
|
+
function mappingRows(value) {
|
|
108
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((row) => !record(row))) {
|
|
109
|
+
throw new TypeError('Web fragment mappings must be a nonempty array of objects');
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Plan one or many exact type-qualified web-fragment creates.
|
|
115
|
+
* @param {LoadedWorkspaceExport} source - Integrity-verified workspace package.
|
|
116
|
+
* @param {unknown} input - Explicit source, target, and Data Graph map rows.
|
|
117
|
+
* @returns {PlannedWebFragmentCopies} Selected creates and exact prerequisite evidence.
|
|
118
|
+
*/
|
|
119
|
+
export function planWebFragmentCopies(source, input) {
|
|
120
|
+
if (!source.integrity.verified)
|
|
121
|
+
throw new TypeError('Source integrity must be verified first');
|
|
122
|
+
const sourceKeys = new Set(source.items.map((item) => item.contentKey));
|
|
123
|
+
const sourceNames = new Set(source.items.flatMap((item) => (item.apiName === undefined ? [] : [item.apiName])));
|
|
124
|
+
const targetKeys = new Set();
|
|
125
|
+
const targetNames = new Set();
|
|
126
|
+
const selected = new Set();
|
|
127
|
+
const items = [];
|
|
128
|
+
const dataGraphs = [];
|
|
129
|
+
for (const [index, row] of mappingRows(input).entries()) {
|
|
130
|
+
exactKeys(row, ['source', 'target', 'dataGraphs'], `Web fragment mapping ${index}`);
|
|
131
|
+
if (!record(row.source))
|
|
132
|
+
throw new TypeError(`Web fragment mapping ${index}.source must be an object`);
|
|
133
|
+
exactKeys(row.source, ['family', 'type', 'apiName'], `Web fragment mapping ${index}.source`);
|
|
134
|
+
if (row.source.family !== 'cms' || row.source.type !== 'webFragment') {
|
|
135
|
+
throw new TypeError(`Web fragment mapping ${index}.source must select cms/webFragment`);
|
|
136
|
+
}
|
|
137
|
+
identifier(row.source.apiName, `Web fragment mapping ${index}.source.apiName`);
|
|
138
|
+
const sourceApiName = row.source.apiName;
|
|
139
|
+
if (selected.has(sourceApiName))
|
|
140
|
+
throw new TypeError(`Duplicate web fragment selection: ${sourceApiName}`);
|
|
141
|
+
const matches = source.items.filter((item) => item.contentType === WEB_FRAGMENT_TYPE && item.apiName === sourceApiName);
|
|
142
|
+
if (matches.length !== 1) {
|
|
143
|
+
throw new TypeError(`Typed source API name must match exactly one web fragment: ${sourceApiName}`);
|
|
144
|
+
}
|
|
145
|
+
const item = matches[0];
|
|
146
|
+
const sourcePrerequisites = assertWebFragmentItem(item);
|
|
147
|
+
if (!record(row.target))
|
|
148
|
+
throw new TypeError(`Web fragment mapping ${index}.target must be an object`);
|
|
149
|
+
exactKeys(row.target, ['contentKey', 'apiName'], `Web fragment mapping ${index}.target`);
|
|
150
|
+
identifier(row.target.contentKey, `Web fragment mapping ${index}.target.contentKey`);
|
|
151
|
+
identifier(row.target.apiName, `Web fragment mapping ${index}.target.apiName`);
|
|
152
|
+
const contentKey = row.target.contentKey;
|
|
153
|
+
const apiName = row.target.apiName;
|
|
154
|
+
if (sourceKeys.has(contentKey) || targetKeys.has(contentKey)) {
|
|
155
|
+
throw new TypeError('Target web fragment content keys must be fresh and unique');
|
|
156
|
+
}
|
|
157
|
+
if (sourceNames.has(apiName) || targetNames.has(apiName)) {
|
|
158
|
+
throw new TypeError('Target web fragment API names must be fresh and unique');
|
|
159
|
+
}
|
|
160
|
+
if (!Array.isArray(row.dataGraphs) || row.dataGraphs.length !== sourcePrerequisites.length) {
|
|
161
|
+
throw new TypeError('Each source Data Graph prerequisite requires exactly one mapping row');
|
|
162
|
+
}
|
|
163
|
+
const mapped = new Map();
|
|
164
|
+
for (const [graphIndex, raw] of row.dataGraphs.entries()) {
|
|
165
|
+
if (!record(raw))
|
|
166
|
+
throw new TypeError(`Web fragment mapping ${index}.dataGraphs[${graphIndex}] must be an object`);
|
|
167
|
+
exactKeys(raw, ['sourceDeveloperName', 'sourceDataSpace', 'targetDeveloperName', 'targetDataSpace'], `Web fragment mapping ${index}.dataGraphs[${graphIndex}]`);
|
|
168
|
+
identifier(raw.sourceDeveloperName, 'sourceDeveloperName');
|
|
169
|
+
dataSpace(raw.sourceDataSpace, 'sourceDataSpace');
|
|
170
|
+
identifier(raw.targetDeveloperName, 'targetDeveloperName');
|
|
171
|
+
dataSpace(raw.targetDataSpace, 'targetDataSpace');
|
|
172
|
+
const key = `${raw.sourceDeveloperName}\0${raw.sourceDataSpace}`;
|
|
173
|
+
if (mapped.has(key))
|
|
174
|
+
throw new TypeError('Duplicate Data Graph source mapping');
|
|
175
|
+
mapped.set(key, {
|
|
176
|
+
sourceDeveloperName: raw.sourceDeveloperName,
|
|
177
|
+
sourceDataSpace: raw.sourceDataSpace,
|
|
178
|
+
targetDeveloperName: raw.targetDeveloperName,
|
|
179
|
+
targetDataSpace: raw.targetDataSpace,
|
|
180
|
+
resolution: raw.sourceDeveloperName === raw.targetDeveloperName &&
|
|
181
|
+
raw.sourceDataSpace === raw.targetDataSpace
|
|
182
|
+
? 'preserved'
|
|
183
|
+
: 'explicit-map',
|
|
184
|
+
validationStatus: 'pending',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
for (const prerequisite of sourcePrerequisites) {
|
|
188
|
+
if (!mapped.has(`${prerequisite.developerName}\0${prerequisite.dataSpace}`)) {
|
|
189
|
+
throw new TypeError(`Missing exact Data Graph mapping for ${prerequisite.developerName}/${prerequisite.dataSpace}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const providers = item.contentBody['lightning:dataProviders'].map((value) => {
|
|
193
|
+
const identity = provider(value, 'Data Graph provider');
|
|
194
|
+
const target = mapped.get(`${identity.developerName}\0${identity.dataSpace}`);
|
|
195
|
+
return {
|
|
196
|
+
...value,
|
|
197
|
+
attributes: {
|
|
198
|
+
dataGraphApiName: target.targetDeveloperName,
|
|
199
|
+
dataspace: target.targetDataSpace,
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
selected.add(sourceApiName);
|
|
204
|
+
targetKeys.add(contentKey);
|
|
205
|
+
targetNames.add(apiName);
|
|
206
|
+
dataGraphs.push(...mapped.values());
|
|
207
|
+
items.push({
|
|
208
|
+
...item,
|
|
209
|
+
apiName,
|
|
210
|
+
contentKey,
|
|
211
|
+
contentBody: { ...item.contentBody, 'lightning:dataProviders': providers },
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
items: items.toSorted((left, right) => left.apiName.localeCompare(right.apiName)),
|
|
216
|
+
targetContentKeys: [...targetKeys].toSorted(),
|
|
217
|
+
dataGraphs: dataGraphs.toSorted((left, right) => `${left.targetDeveloperName}\0${left.targetDataSpace}`.localeCompare(`${right.targetDeveloperName}\0${right.targetDataSpace}`)),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function soqlString(value) {
|
|
221
|
+
return `'${value.replaceAll('\\', '\\\\').replaceAll("'", String.raw `\'`)}'`;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Prove each exact target Data Graph developer-name/data-space pair exists uniquely.
|
|
225
|
+
* @param {QueryConnection} connection - Destination org query transport.
|
|
226
|
+
* @param {readonly WebFragmentDataGraphPrerequisite[]} prerequisites - Exact target prerequisites.
|
|
227
|
+
* @returns {Promise<void>} Fulfilled only when every prerequisite resolves exactly once.
|
|
228
|
+
*/
|
|
229
|
+
export async function validateDataGraphPrerequisites(connection, prerequisites) {
|
|
230
|
+
if (connection.query === undefined) {
|
|
231
|
+
throw new TypeError('Destination Data Graph prerequisites cannot be queried');
|
|
232
|
+
}
|
|
233
|
+
const unique = new Map(prerequisites.map((value) => [`${value.targetDeveloperName}\0${value.targetDataSpace}`, value]));
|
|
234
|
+
for (const prerequisite of unique.values()) {
|
|
235
|
+
const result = await connection.query(`SELECT DeveloperName, DataSpaceDevName FROM DataGraph WHERE DeveloperName = ${soqlString(prerequisite.targetDeveloperName)} AND DataSpaceDevName = ${soqlString(prerequisite.targetDataSpace)}`);
|
|
236
|
+
const matches = result.records.filter((record) => record.DeveloperName === prerequisite.targetDeveloperName &&
|
|
237
|
+
record.DataSpaceDevName === prerequisite.targetDataSpace);
|
|
238
|
+
if (result.totalSize !== 1 || result.records.length !== 1 || matches.length !== 1) {
|
|
239
|
+
prerequisite.validationStatus = 'failed';
|
|
240
|
+
throw new TypeError(`Data Graph prerequisite must resolve exactly once: ${prerequisite.targetDeveloperName}/${prerequisite.targetDataSpace}`);
|
|
241
|
+
}
|
|
242
|
+
prerequisite.validationStatus = 'passed';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=web-fragment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-fragment.js","sourceRoot":"","sources":["../../src/services/web-fragment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { FileHandle } from 'node:fs/promises';
|
|
2
|
+
export declare const EXPERIMENTAL_MEDIA_POLICY: {
|
|
3
|
+
readonly approvedRedirectSuffix: "file.force.com";
|
|
4
|
+
readonly maxRedirects: 3;
|
|
5
|
+
readonly perImageBytes: number;
|
|
6
|
+
readonly totalBytes: number;
|
|
7
|
+
};
|
|
8
|
+
export type ExperimentalMediaDownload = {
|
|
9
|
+
readonly bytes: number;
|
|
10
|
+
readonly contentLength?: number;
|
|
11
|
+
readonly md5: string;
|
|
12
|
+
readonly mimeType: string;
|
|
13
|
+
readonly sha256: string;
|
|
14
|
+
};
|
|
15
|
+
type ExperimentalMediaFileHandle = Pick<FileHandle, 'close' | 'write'>;
|
|
16
|
+
export type ExperimentalMediaDownloadOptions = {
|
|
17
|
+
readonly accessToken: string;
|
|
18
|
+
readonly expectedMd5: string;
|
|
19
|
+
readonly expectedMimeType: string;
|
|
20
|
+
readonly expectedSize: number;
|
|
21
|
+
readonly fetch?: typeof fetch;
|
|
22
|
+
readonly instanceUrl: string;
|
|
23
|
+
readonly maxBytes?: number;
|
|
24
|
+
readonly openFile?: (file: string) => Promise<ExperimentalMediaFileHandle>;
|
|
25
|
+
readonly outputFile: string;
|
|
26
|
+
readonly unlinkFile?: (file: string) => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
export declare function isApprovedSalesforceMediaUrl(value: string | URL): boolean;
|
|
29
|
+
export declare function downloadExperimentalCmsMedia(source: URL, options: ExperimentalMediaDownloadOptions): Promise<ExperimentalMediaDownload>;
|
|
30
|
+
export {};
|
|
@@ -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":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,YAAY,GAAG,gBAAgB,CAAC;AACtC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7D,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAA4B;IAC1D,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAChD,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;CACzC,CAAC,CAAC;AACH,MAAM,eAAe,GAAgD;IACnE,WAAW,EAAE,CAAC,KAAK,CAAC;IACpB,YAAY,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;IAC7B,WAAW,EAAE,CAAC,KAAK,CAAC;IACpB,YAAY,EAAE,CAAC,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,sBAAsB,EAAE,YAAY;IACpC,YAAY,EAAE,CAAC;IACf,aAAa,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;IAC/B,UAAU,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;CACpB,CAAC;AAyBX,SAAS,uBAAuB,CAAC,KAAc;IAC7C,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtF,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,MAAmC,EACnC,UAAkB,EAClB,OAAgB,EAChB,UAA2C,EAC3C,cAAuB;IAEvB,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,CAAC,IAAI,CAAC,UAAU,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,SAAS,CAAC,IAAI,CAAC,WAAW,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,OAAO,YAAY,KAAK,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,oBAAoB,EAAE;YACnD,YAAY,EAAE,IAAI;YAClB,UAAU,EAAE,KAAK;YACjB,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;IACL,CAAC;IACD,MAAM,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,GAAQ;IAC9B,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3E,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;IACzF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,eAAe,CAAC,GAAQ;IAC/B,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;QAC5E,MAAM,IAAI,SAAS,CAAC,8DAA8D,CAAC,CAAC;IACtF,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1C,MAAM,IAAI,SAAS,CAAC,kEAAkE,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,KAAmB;IAC9D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,YAAY,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,IAAS,EAAE,KAAU;IACvC,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,eAAe,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC;AACtF,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB;IACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IACvD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC,WAAW,EAAE;QAAE,OAAO,EAAE,CAAC;IACrE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa,EAAE,QAAgB,EAAE,QAAgB;IAC5E,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,OAAO,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACrD,MAAM,IAAI,SAAS,CAAC,gDAAgD,QAAQ,EAAE,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,SAAS,CAAC,gEAAgE,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,QAAQ,KAAK,YAAY,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC;QACpF,MAAM,IAAI,SAAS,CAAC,8CAA8C,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,MAAW,EACX,OAAyC;IAEzC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9C,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,SAAS,CAAC,0EAA0E,CAAC,CAAC;IAClG,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;IACvC,IAAI,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,QAA8B,CAAC;IACnC,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,IAAI,yBAAyB,CAAC,YAAY,EAAE,SAAS,IAAI,CAAC,EAAE,CAAC;QAC5F,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACjD,IAAI,CAAC,UAAU,IAAI,CAAC,4BAA4B,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,SAAS,CACjB,6EAA6E,CAC9E,CAAC;QACJ,CAAC;QACD,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE;YAChC,OAAO,EAAE;gBACP,MAAM,EAAE,wCAAwC;gBAChD,aAAa,EAAE,UAAU,OAAO,CAAC,WAAW,EAAE;aAC/C;YACD,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;QACH,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,MAAM;QACnD,IAAI,SAAS,KAAK,yBAAyB,CAAC,YAAY,EAAE,CAAC;YACzD,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,QAAQ,KAAK,IAAI;YAAE,MAAM,IAAI,SAAS,CAAC,6CAA6C,CAAC,CAAC;QAC1F,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC1C,eAAe,CAAC,MAAM,CAAC,CAAC;QACxB,IAAI,CAAC,4BAA4B,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC3E,MAAM,IAAI,SAAS,CACjB,6EAA6E,CAC9E,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IACD,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACrE,MAAM,IAAI,SAAS,CAAC,iDAAiD,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;IACD,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACjG,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,WAAW,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAChG,MAAM,IAAI,SAAS,CAAC,yDAAyD,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,YAAY,KAAK,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,kBAAkB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClE,MAAM,cAAc,GAAG,kBAAkB,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC5F,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,IAAI,yBAAyB,CAAC,aAAa,CAAC;IACpF,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,eAAe,IAAI,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,SAAS,CAAC,6DAA6D,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,yBAAyB,CAAC,aAAa,CAAC,CAAC;IACjF,IACE,cAAc,KAAK,SAAS;QAC5B,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,CAAC,IAAI,cAAc,GAAG,KAAK,CAAC,EACvF,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,iEAAiE,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrD,IACE,QAAQ,KAAK,IAAI;QACjB,QAAQ,CAAC,MAAM,KAAK,CAAC;QACrB,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QACtB,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EACvB,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAClG,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC;IAChD,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,CAAC;QACH,IAAI,KAAK,EAAE,MAAM,UAAU,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAa,CAAC,EAAE,CAAC;YACxE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;gBACvC,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAwB,CAAC,CAAC;YAC1C,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,GAAG,KAAK,EAAE,CAAC;gBACjC,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;YAC1E,CAAC;YACD,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;YACtB,IAAI,YAAY,GAAG,EAAE,EAAE,CAAC;gBACtB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,GAAG,YAAY,CAAC,CAAC;gBACtD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACtB,YAAY,IAAI,QAAQ,CAAC,MAAM,CAAC;YAClC,CAAC;YACD,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAClB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrB,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,cAAc,KAAK,SAAS,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;YAC7D,MAAM,IAAI,SAAS,CAAC,2DAA2D,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,KAAK,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC;YACnC,MAAM,IAAI,SAAS,CAAC,+DAA+D,CAAC,CAAC;QACvF,CAAC;QACD,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,SAAS,KAAK,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,CAAC;YACpD,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;QAC/E,CAAC;QACD,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QAC/E,cAAc,GAAG,IAAI,CAAC;QACtB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO;YACL,KAAK;YACL,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC;YAC1E,GAAG,EAAE,SAAS;YACd,QAAQ,EAAE,OAAO,CAAC,gBAAgB;YAClC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;SAC7B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,CAAC,CAAC;IAC9F,CAAC;AACH,CAAC"}
|
|
@@ -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);
|