@salesforcedevs/docs-components 1.28.5-node22-1 → 1.28.5-redoc-alpha1
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/package.json +1 -1
- package/src/modules/doc/amfReference/amfReference.html +12 -1
- package/src/modules/doc/amfReference/amfReference.ts +114 -3
- package/src/modules/doc/amfReference/constants.ts +4 -0
- package/src/modules/doc/amfReference/types.ts +13 -0
- package/src/modules/doc/redocReference/redocReference.ts +125 -1
- package/.npmrc +0 -1
package/package.json
CHANGED
|
@@ -42,7 +42,18 @@
|
|
|
42
42
|
latest-version={latestVersion}
|
|
43
43
|
></doc-version-picker>
|
|
44
44
|
</div>
|
|
45
|
-
<template lwc:if={
|
|
45
|
+
<template lwc:if={showRedocReference}>
|
|
46
|
+
<doc-redoc-reference
|
|
47
|
+
reference-config={redocReferenceConfig}
|
|
48
|
+
doc-phase-info={docPhaseInfo}
|
|
49
|
+
project-title={projectTitle}
|
|
50
|
+
spec-title={redocSpecTitle}
|
|
51
|
+
origin={origin}
|
|
52
|
+
>
|
|
53
|
+
<div class="redoc-container"></div>
|
|
54
|
+
</doc-redoc-reference>
|
|
55
|
+
</template>
|
|
56
|
+
<template lwc:elseif={showSpecBasedReference}>
|
|
46
57
|
<div class="container">
|
|
47
58
|
<div class="api-documentation">
|
|
48
59
|
<doc-amf-topic
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
NAVIGATION_ITEMS,
|
|
25
25
|
URL_CONFIG,
|
|
26
26
|
REFERENCE_TYPES,
|
|
27
|
+
RENDER_WITH,
|
|
27
28
|
oldReferenceIdNewReferenceIdMap
|
|
28
29
|
} from "./constants";
|
|
29
30
|
import { restoreScroll } from "dx/scrollManager";
|
|
@@ -37,6 +38,7 @@ type NavigationItem = {
|
|
|
37
38
|
isExpanded: boolean;
|
|
38
39
|
children: ParsedMarkdownTopic[];
|
|
39
40
|
isChildrenLoading: boolean;
|
|
41
|
+
renderWith?: string;
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
export default class AmfReference extends LightningElement {
|
|
@@ -44,6 +46,12 @@ export default class AmfReference extends LightningElement {
|
|
|
44
46
|
/** Optional Twitter "via" handle (e.g. SalesforceDevs) for social share; passed to doc-content-layout. */
|
|
45
47
|
@api twitterVia: string | null = null;
|
|
46
48
|
@api sidebarHeader!: string;
|
|
49
|
+
/**
|
|
50
|
+
* Project title shown by `<doc-header>` (as its `subtitle`). Forwarded to
|
|
51
|
+
* `<doc-redoc-reference>` so it can surface the same project title inside
|
|
52
|
+
* the Redoc-rendered UI.
|
|
53
|
+
*/
|
|
54
|
+
@api projectTitle: string | null = null;
|
|
47
55
|
@api tocTitle?: string;
|
|
48
56
|
@api tocOptions?: string;
|
|
49
57
|
@api tocAriaLevel?: string;
|
|
@@ -69,7 +77,48 @@ export default class AmfReference extends LightningElement {
|
|
|
69
77
|
* Gives if the currently selected reference is spec based or not
|
|
70
78
|
*/
|
|
71
79
|
get showSpecBasedReference(): boolean {
|
|
72
|
-
return
|
|
80
|
+
return (
|
|
81
|
+
this.isSpecBasedReference(this._currentReferenceId) &&
|
|
82
|
+
!this.showRedocReference
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Whether the currently selected reference is rendered with Redoc.
|
|
88
|
+
* Driven by the backend-supplied `renderWith` attribute on the AmfConfig.
|
|
89
|
+
*/
|
|
90
|
+
get showRedocReference(): boolean {
|
|
91
|
+
return this.isRedocReference(this._currentReferenceId);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Serialized config consumed by `<doc-redoc-reference>`. Built from the
|
|
96
|
+
* currently selected redoc reference so Redoc has a single spec to render.
|
|
97
|
+
*/
|
|
98
|
+
get redocReferenceConfig(): string {
|
|
99
|
+
const ref = this.getAmfConfigWithId(this._currentReferenceId);
|
|
100
|
+
if (!ref) {
|
|
101
|
+
return JSON.stringify({ refList: [] });
|
|
102
|
+
}
|
|
103
|
+
return JSON.stringify({
|
|
104
|
+
refList: [
|
|
105
|
+
{
|
|
106
|
+
source: ref.source || ref.amf || "",
|
|
107
|
+
href: ref.href,
|
|
108
|
+
isSelected: true,
|
|
109
|
+
docPhase: ref.docPhase ?? null
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Title of the currently selected spec. Forwarded to `<doc-redoc-reference>`
|
|
117
|
+
* so it can label the spec inside the Redoc UI.
|
|
118
|
+
*/
|
|
119
|
+
get redocSpecTitle(): string {
|
|
120
|
+
const ref = this.getAmfConfigWithId(this._currentReferenceId);
|
|
121
|
+
return ref?.title ?? "";
|
|
73
122
|
}
|
|
74
123
|
|
|
75
124
|
@api
|
|
@@ -99,6 +148,19 @@ export default class AmfReference extends LightningElement {
|
|
|
99
148
|
|
|
100
149
|
this._amfConfigList = this._referenceSetConfig.refList || [];
|
|
101
150
|
|
|
151
|
+
// If the framework didn't tag the reference, infer redoc rendering
|
|
152
|
+
// from the config shape: spec-based references with no AMF URL are
|
|
153
|
+
// shipped as raw spec sources for Redoc to render directly.
|
|
154
|
+
this._amfConfigList.forEach((amfConfig) => {
|
|
155
|
+
if (
|
|
156
|
+
!amfConfig.renderWith &&
|
|
157
|
+
amfConfig.referenceType !== REFERENCE_TYPES.markdown &&
|
|
158
|
+
!amfConfig.amf
|
|
159
|
+
) {
|
|
160
|
+
amfConfig.renderWith = RENDER_WITH.redoc;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
102
164
|
this._amfConfigList.forEach((amfConfig) => {
|
|
103
165
|
this._amfConfigMap.set(amfConfig.id, amfConfig);
|
|
104
166
|
});
|
|
@@ -204,6 +266,7 @@ export default class AmfReference extends LightningElement {
|
|
|
204
266
|
|
|
205
267
|
_boundOnApiNavigationChanged;
|
|
206
268
|
_boundUpdateSelectedItemFromUrlQuery;
|
|
269
|
+
_boundOnPageShow;
|
|
207
270
|
|
|
208
271
|
constructor() {
|
|
209
272
|
super();
|
|
@@ -212,6 +275,7 @@ export default class AmfReference extends LightningElement {
|
|
|
212
275
|
this.onApiNavigationChanged.bind(this);
|
|
213
276
|
this._boundUpdateSelectedItemFromUrlQuery =
|
|
214
277
|
this.updateSelectedItemFromUrlQuery.bind(this);
|
|
278
|
+
this._boundOnPageShow = this.onPageShow.bind(this);
|
|
215
279
|
}
|
|
216
280
|
|
|
217
281
|
connectedCallback(): void {
|
|
@@ -223,6 +287,7 @@ export default class AmfReference extends LightningElement {
|
|
|
223
287
|
"popstate",
|
|
224
288
|
this._boundUpdateSelectedItemFromUrlQuery
|
|
225
289
|
);
|
|
290
|
+
window.addEventListener("pageshow", this._boundOnPageShow);
|
|
226
291
|
}
|
|
227
292
|
|
|
228
293
|
disconnectedCallback(): void {
|
|
@@ -234,6 +299,22 @@ export default class AmfReference extends LightningElement {
|
|
|
234
299
|
"popstate",
|
|
235
300
|
this._boundUpdateSelectedItemFromUrlQuery
|
|
236
301
|
);
|
|
302
|
+
window.removeEventListener("pageshow", this._boundOnPageShow);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* On bfcache restore, reset the sidebar selection so the tree re-syncs
|
|
307
|
+
* its highlighted tile with the current URL.
|
|
308
|
+
*/
|
|
309
|
+
protected onPageShow(event: PageTransitionEvent): void {
|
|
310
|
+
if (!event.persisted) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const currentPath = window.location.pathname;
|
|
314
|
+
this.selectedSidebarValue = "";
|
|
315
|
+
Promise.resolve().then(() => {
|
|
316
|
+
this.selectedSidebarValue = currentPath;
|
|
317
|
+
});
|
|
237
318
|
}
|
|
238
319
|
|
|
239
320
|
renderedCallback(): void {
|
|
@@ -296,6 +377,16 @@ export default class AmfReference extends LightningElement {
|
|
|
296
377
|
: false;
|
|
297
378
|
}
|
|
298
379
|
|
|
380
|
+
/**
|
|
381
|
+
* @param referenceId
|
|
382
|
+
* @returns whether the reference should be rendered with Redoc rather than
|
|
383
|
+
* the AMF-based topic renderer.
|
|
384
|
+
*/
|
|
385
|
+
private isRedocReference(referenceId: string): boolean {
|
|
386
|
+
const selectedReference = this.getAmfConfigWithId(referenceId);
|
|
387
|
+
return selectedReference?.renderWith === RENDER_WITH.redoc;
|
|
388
|
+
}
|
|
389
|
+
|
|
299
390
|
/*
|
|
300
391
|
* Refactor below method when sidebar allows sending extraData along with the name for each item.
|
|
301
392
|
* See if we can refactor the below method using regex.
|
|
@@ -442,7 +533,11 @@ export default class AmfReference extends LightningElement {
|
|
|
442
533
|
for (const [index, amfConfig] of this._amfConfigList.entries()) {
|
|
443
534
|
let navItemChildren = [] as ParsedMarkdownTopic[];
|
|
444
535
|
let isChildrenLoading = false;
|
|
445
|
-
if (amfConfig.
|
|
536
|
+
if (amfConfig.renderWith === RENDER_WITH.redoc) {
|
|
537
|
+
// Redoc-rendered specs have no AMF model and no sub-tree; they
|
|
538
|
+
// appear as leaf items in the sidebar and open the Redoc UI on
|
|
539
|
+
// selection.
|
|
540
|
+
} else if (amfConfig.referenceType !== REFERENCE_TYPES.markdown) {
|
|
446
541
|
if (amfConfig.isSelected) {
|
|
447
542
|
const amfPromise = this.fetchAmf(amfConfig).then(
|
|
448
543
|
(amfJson) => {
|
|
@@ -473,7 +568,8 @@ export default class AmfReference extends LightningElement {
|
|
|
473
568
|
amfConfig.isSelected ||
|
|
474
569
|
this.isExpandChildrenEnabled(amfConfig.id),
|
|
475
570
|
children: navItemChildren,
|
|
476
|
-
isChildrenLoading
|
|
571
|
+
isChildrenLoading,
|
|
572
|
+
renderWith: amfConfig.renderWith
|
|
477
573
|
};
|
|
478
574
|
this.parentReferenceUrls.push(amfConfig.href);
|
|
479
575
|
}
|
|
@@ -862,6 +958,12 @@ export default class AmfReference extends LightningElement {
|
|
|
862
958
|
const specBasedReference = this.isSpecBasedReference(
|
|
863
959
|
this._currentReferenceId
|
|
864
960
|
);
|
|
961
|
+
if (this.isRedocReference(this._currentReferenceId)) {
|
|
962
|
+
// Redoc reads its own state from referenceConfig + window.location;
|
|
963
|
+
// no metadata to sync from URL on our side.
|
|
964
|
+
restoreScroll();
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
865
967
|
if (specBasedReference) {
|
|
866
968
|
const currentMeta: RouteMeta | undefined =
|
|
867
969
|
this.getReferenceMetaInfo(window.location.href);
|
|
@@ -896,6 +998,9 @@ export default class AmfReference extends LightningElement {
|
|
|
896
998
|
* @param event
|
|
897
999
|
*/
|
|
898
1000
|
protected onApiNavigationChanged(): void {
|
|
1001
|
+
if (this.isRedocReference(this._currentReferenceId)) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
899
1004
|
const specBasedReference = this.isSpecBasedReference(
|
|
900
1005
|
this._currentReferenceId
|
|
901
1006
|
);
|
|
@@ -1165,6 +1270,12 @@ export default class AmfReference extends LightningElement {
|
|
|
1165
1270
|
}
|
|
1166
1271
|
|
|
1167
1272
|
const specBasedReference = this.isSpecBasedReference(referenceId);
|
|
1273
|
+
const redocReference = this.isRedocReference(referenceId);
|
|
1274
|
+
if (redocReference) {
|
|
1275
|
+
// Redoc-rendered references have no AMF model; the redoc component
|
|
1276
|
+
// handles its own URL/spec resolution from referenceConfig.
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1168
1279
|
if (specBasedReference) {
|
|
1169
1280
|
// Wait till the AMF is loaded.
|
|
1170
1281
|
this.amfFetchPromiseMap[referenceId].then(() => {
|
|
@@ -61,6 +61,10 @@ export const REFERENCE_TYPES = {
|
|
|
61
61
|
oa3: "rest-oa3"
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
export const RENDER_WITH = {
|
|
65
|
+
redoc: "redoc"
|
|
66
|
+
};
|
|
67
|
+
|
|
64
68
|
const oldReferenceIdNewReferenceIdMap: Record<string, string> = {
|
|
65
69
|
"commerce-api-assignments": "assignments",
|
|
66
70
|
"commerce-api-campaigns": "campaigns",
|
|
@@ -78,6 +78,19 @@ export interface AmfConfig {
|
|
|
78
78
|
|
|
79
79
|
// required for markdown based references
|
|
80
80
|
topic?: ParsedMarkdownTopic;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Optional renderer override sent by the backend.
|
|
84
|
+
* When "redoc", spec-based references are rendered with Redoc instead of
|
|
85
|
+
* the AMF-based topic view. Other values fall back to the default pipeline.
|
|
86
|
+
*/
|
|
87
|
+
renderWith?: string;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Spec URL consumed by alternate renderers (e.g. Redoc). For redoc-rendered
|
|
91
|
+
* references this is the OpenAPI document URL.
|
|
92
|
+
*/
|
|
93
|
+
source?: string;
|
|
81
94
|
}
|
|
82
95
|
|
|
83
96
|
export interface ParsedTopicModel {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { createElement, LightningElement, api } from "lwc";
|
|
3
3
|
import DocPhase from "doc/phase";
|
|
4
4
|
import DxFooter from "dx/footer";
|
|
5
|
+
import DxIcon from "dx/icon";
|
|
5
6
|
import SprigSurvey from "doc/sprigSurvey";
|
|
6
7
|
import { throttle } from "throttle-debounce";
|
|
7
8
|
import { pollUntil } from "dxUtils/async";
|
|
@@ -17,6 +18,7 @@ declare const Sprig: (eventType: string, eventName: string) => void;
|
|
|
17
18
|
type ReferenceItem = {
|
|
18
19
|
source: string;
|
|
19
20
|
href: string;
|
|
21
|
+
title?: string;
|
|
20
22
|
isSelected?: boolean;
|
|
21
23
|
docPhase?: string | null;
|
|
22
24
|
};
|
|
@@ -71,6 +73,45 @@ export default class RedocReference extends LightningElement {
|
|
|
71
73
|
/** Optional origin URL for the footer MFE (e.g. wp-json endpoint). Passed through to dx-footer. */
|
|
72
74
|
@api origin: string | null = null;
|
|
73
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Project title (same value passed to `<doc-header>` as `subtitle`). Used
|
|
78
|
+
* inside the Redoc-rendered UI to label the parent project.
|
|
79
|
+
*/
|
|
80
|
+
@api projectTitle: string | null = null;
|
|
81
|
+
|
|
82
|
+
private _specTitle: string | null = null;
|
|
83
|
+
|
|
84
|
+
/** Title of the currently selected spec, shown beneath the project title. */
|
|
85
|
+
@api
|
|
86
|
+
get specTitle(): string | null {
|
|
87
|
+
if (this._specTitle) {
|
|
88
|
+
return this._specTitle;
|
|
89
|
+
}
|
|
90
|
+
return this.getSelectedReference()?.title ?? null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
set specTitle(value: string | null) {
|
|
94
|
+
this._specTitle = value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Whether to show the project header (only for multi-spec reference sets).
|
|
99
|
+
*/
|
|
100
|
+
get showRedocHeader(): boolean {
|
|
101
|
+
const refCount = this._referenceConfig?.refList?.length ?? 0;
|
|
102
|
+
const isMultiSpecSet = refCount > 1;
|
|
103
|
+
return isMultiSpecSet && !!(this.projectTitle || this.specTitle);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Navigates back to the previous history entry when the user clicks the
|
|
108
|
+
* project-title back link rendered above the Redoc UI.
|
|
109
|
+
*/
|
|
110
|
+
private onBackClick(event: Event): void {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
window.history.back();
|
|
113
|
+
}
|
|
114
|
+
|
|
74
115
|
/** When origin is provided, pass it to the footer; otherwise use dx-footer's default. */
|
|
75
116
|
get effectiveFooterOrigin(): string {
|
|
76
117
|
return (
|
|
@@ -108,7 +149,13 @@ export default class RedocReference extends LightningElement {
|
|
|
108
149
|
}
|
|
109
150
|
|
|
110
151
|
private getRedocContainer(): HTMLElement | null {
|
|
111
|
-
|
|
152
|
+
// Prefer the slotted container in the consumer's light DOM so this
|
|
153
|
+
// component composes inside shadow trees; fall back to a global
|
|
154
|
+
// `.redoc-container` for legacy page-level integrations.
|
|
155
|
+
return (
|
|
156
|
+
this.querySelector<HTMLElement>(".redoc-container") ||
|
|
157
|
+
document.querySelector<HTMLElement>(".redoc-container")
|
|
158
|
+
);
|
|
112
159
|
}
|
|
113
160
|
|
|
114
161
|
private getSelectedReference(): ReferenceItem | null {
|
|
@@ -304,6 +351,9 @@ export default class RedocReference extends LightningElement {
|
|
|
304
351
|
|
|
305
352
|
this.appendFooterItems(apiContentDiv);
|
|
306
353
|
|
|
354
|
+
// Inject the multi-spec project header into Redoc's left menu only.
|
|
355
|
+
this.insertProjectHeaderInMenu(redocContainer);
|
|
356
|
+
|
|
307
357
|
// Wait for footer to be rendered before updating styles
|
|
308
358
|
requestAnimationFrame(() => {
|
|
309
359
|
this.updateRedocThirdColumnStyle(redocContainer);
|
|
@@ -316,6 +366,80 @@ export default class RedocReference extends LightningElement {
|
|
|
316
366
|
}
|
|
317
367
|
}
|
|
318
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Inserts the project header into Redoc for multi-spec reference sets.
|
|
371
|
+
*/
|
|
372
|
+
private insertProjectHeaderInMenu(redocContainer: HTMLElement): void {
|
|
373
|
+
if (!this.showRedocHeader) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const menuContent =
|
|
377
|
+
redocContainer.querySelector<HTMLElement>(".menu-content");
|
|
378
|
+
if (
|
|
379
|
+
menuContent &&
|
|
380
|
+
!menuContent.querySelector(":scope > .redoc-project-header")
|
|
381
|
+
) {
|
|
382
|
+
menuContent.insertBefore(
|
|
383
|
+
this.buildProjectHeaderDom(),
|
|
384
|
+
menuContent.firstChild
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const apiContent =
|
|
389
|
+
redocContainer.querySelector<HTMLElement>(".api-content");
|
|
390
|
+
if (
|
|
391
|
+
apiContent &&
|
|
392
|
+
!apiContent.querySelector(":scope > .redoc-project-header")
|
|
393
|
+
) {
|
|
394
|
+
apiContent.insertBefore(
|
|
395
|
+
this.buildProjectHeaderDom(),
|
|
396
|
+
apiContent.firstChild
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Builds a fresh project-title/spec-title header DOM node.
|
|
403
|
+
*/
|
|
404
|
+
private buildProjectHeaderDom(): HTMLElement {
|
|
405
|
+
const wrapper = document.createElement("div");
|
|
406
|
+
wrapper.className = "redoc-project-header";
|
|
407
|
+
|
|
408
|
+
if (this.projectTitle) {
|
|
409
|
+
const backLink = document.createElement("a");
|
|
410
|
+
backLink.className = "redoc-project-back";
|
|
411
|
+
backLink.href = "#";
|
|
412
|
+
backLink.addEventListener("click", (event) => {
|
|
413
|
+
event.preventDefault();
|
|
414
|
+
window.history.back();
|
|
415
|
+
});
|
|
416
|
+
// 16x16 utility/back icon. dx-icon size="medium" maps to
|
|
417
|
+
// --dx-g-icon-size-md (16px).
|
|
418
|
+
const iconEl = createElement("dx-icon", { is: DxIcon });
|
|
419
|
+
Object.assign(iconEl, {
|
|
420
|
+
sprite: "utility",
|
|
421
|
+
symbol: "back",
|
|
422
|
+
size: "medium"
|
|
423
|
+
});
|
|
424
|
+
iconEl.classList.add("redoc-project-back-arrow");
|
|
425
|
+
const label = document.createElement("span");
|
|
426
|
+
label.className = "redoc-project-title";
|
|
427
|
+
label.textContent = this.projectTitle;
|
|
428
|
+
backLink.appendChild(iconEl);
|
|
429
|
+
backLink.appendChild(label);
|
|
430
|
+
wrapper.appendChild(backLink);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (this.specTitle) {
|
|
434
|
+
const specEl = document.createElement("h2");
|
|
435
|
+
specEl.className = "redoc-spec-title";
|
|
436
|
+
specEl.textContent = this.specTitle;
|
|
437
|
+
wrapper.appendChild(specEl);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return wrapper;
|
|
441
|
+
}
|
|
442
|
+
|
|
319
443
|
// Waits for Redoc's API content element to be rendered
|
|
320
444
|
private async waitForApiContent(
|
|
321
445
|
container: HTMLElement
|
package/.npmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//registry.npmjs.org/:_authToken=${SFDOCS_NPM_AUTH_TOKEN}
|