@woodsportal/hubspot-kit 5.0.2 → 5.0.3-beta.1
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/CHANGELOG.md +6 -0
- package/package.json +2 -2
- package/scripts/build-cdn.mjs +30 -0
- package/scripts/cdn-release-artifacts.mjs +76 -0
- package/scripts/cdn-release-artifacts.test.mjs +20 -0
- package/scripts/generate-cdn-manifest.mjs +48 -15
- package/scripts/hubspot-module-postbuild.mjs +8 -20
- package/scripts/resolve-cdn-base-url.mjs +2 -2
- package/scripts/tailwind/prefix-plugin.js +131 -182
- package/scripts/tailwind/prefix-plugin.test.mjs +110 -0
- package/scripts/tailwind/tailwind-content.js +29 -28
- package/scripts/vite/create-dev-server-config.mjs +14 -3
- package/scripts/vite/portal-build-shared.js +5 -3
- package/src/dev-module-config/__tests__/build-api-routes-custom-menu.test.ts +32 -0
- package/src/dev-module-config/__tests__/custom-menu.test.ts +77 -0
- package/src/dev-module-config/__tests__/drill-down-panels.test.ts +65 -0
- package/src/dev-module-config/__tests__/fixtures/golden-module.fixture.ts +2 -0
- package/src/dev-module-config/__tests__/flatten-visible-fields.test.ts +34 -1
- package/src/dev-module-config/__tests__/module-config-transform.test.ts +27 -0
- package/src/dev-module-config/runtime/module-config-runtime.ts +3 -1
- package/src/dev-module-config/schema/custom-menu.ts +124 -0
- package/src/dev-module-config/schema/validate-module-config.ts +2 -0
- package/src/dev-module-config/transform/build-hubspot-data.ts +14 -0
- package/src/dev-module-config/ui/FieldRenderer.tsx +2 -0
- package/src/dev-module-config/ui/ModuleConfigAnimatedPresence.tsx +4 -2
- package/src/dev-module-config/ui/ModuleConfigShell.tsx +16 -4
- package/src/dev-module-config/ui/editors/MenuOptionsEditor.tsx +34 -0
- package/src/dev-module-config/ui/hubspot/HsOccurrenceGroupEditor.tsx +16 -11
- package/src/dev-module-config/ui/navigation/ModuleConfigNavContext.tsx +27 -23
- package/src/dev-module-config/ui/navigation/drill-down-panels.ts +48 -0
- package/src/routing/build-api-routes.ts +45 -15
- package/src/routing/custom-menu-path.ts +14 -0
- package/templates/module-hybrid-cdn/src/assets/css/main.prod.css +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
### CSS prefix
|
|
6
|
+
- **Auto-prefix** — class string bindings ending in `_CLASS` (e.g. `CARDS_GRID_CLASS`) are prefixed the same as `*DynamicClassName*`
|
|
7
|
+
- **Auto-prefix** — JSX props ending in `ClassName` (`containerClassName`, `pageLinkClassName`, …) and helpers ending in `Class` (`paginationItemBaseClass`, `navBtnClass`) are prefixed
|
|
8
|
+
|
|
3
9
|
## [1.0.44] - 2026-08-06
|
|
4
10
|
|
|
5
11
|
### Portal auth scaffold
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@woodsportal/hubspot-kit",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.3-beta.1",
|
|
4
4
|
"packageManager": "pnpm@10.12.1",
|
|
5
5
|
"description": "WoodsCLI — HubSpot CMS dev kit by WoodsPortal. wp CLI, Vite presets, module-config overlay, and theme/module build pipelines.",
|
|
6
6
|
"type": "module",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"build": "tsup",
|
|
53
53
|
"dev": "tsup --watch",
|
|
54
54
|
"typecheck": "tsc --noEmit",
|
|
55
|
-
"test": "node --import tsx --test 'src/lib/**/*.test.ts' 'src/cli/**/*.test.ts' 'src/schema/**/*.test.ts' 'src/vite/**/*.test.ts' 'scripts/portal-build-id.test.mjs' 'scripts/resolve-consumer-dep.test.mjs' 'scripts/wp-cli-smoke.test.mjs' 'scripts/vite/ensure-dev-kit-shims.test.mjs' 'scripts/vite/consumer-react-aliases.test.mjs' 'scripts/vite/resolve-from-project-root.test.mjs' && vitest run src/dev-module-config/__tests__/normalize-repeater-items.test.ts src/dev-module-config/__tests__/module-config-transform.test.ts src/dev-module-config/__tests__/flatten-visible-fields.test.ts",
|
|
55
|
+
"test": "node --import tsx --test 'src/lib/**/*.test.ts' 'src/cli/**/*.test.ts' 'src/schema/**/*.test.ts' 'src/vite/**/*.test.ts' 'scripts/portal-build-id.test.mjs' 'scripts/resolve-consumer-dep.test.mjs' 'scripts/wp-cli-smoke.test.mjs' 'scripts/vite/ensure-dev-kit-shims.test.mjs' 'scripts/vite/consumer-react-aliases.test.mjs' 'scripts/vite/resolve-from-project-root.test.mjs' 'scripts/tailwind/prefix-plugin.test.mjs' && vitest run src/dev-module-config/__tests__/normalize-repeater-items.test.ts src/dev-module-config/__tests__/module-config-transform.test.ts src/dev-module-config/__tests__/flatten-visible-fields.test.ts src/dev-module-config/__tests__/drill-down-panels.test.ts",
|
|
56
56
|
"test:examples": "node examples/smoke/run-matrix.mjs --tier static",
|
|
57
57
|
"test:dist-pack": "node --test scripts/dist-cli-pack-smoke.test.mjs",
|
|
58
58
|
"lint": "eslint src",
|
package/scripts/build-cdn.mjs
CHANGED
|
@@ -19,7 +19,35 @@ import {
|
|
|
19
19
|
} from './portal-subprocess-stdio.mjs'
|
|
20
20
|
import { spawnViteSync } from './resolve-vite-spawn.mjs'
|
|
21
21
|
|
|
22
|
+
import { existsSync, mkdirSync, readdirSync, rmSync } from 'node:fs'
|
|
23
|
+
|
|
24
|
+
/** Drop versioned CDN outputs from prior builds on the same worker. */
|
|
25
|
+
function purgeStaleCdnArtifacts(cdnDir, buildId) {
|
|
26
|
+
if (!existsSync(cdnDir)) {
|
|
27
|
+
mkdirSync(cdnDir, { recursive: true })
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const entry of readdirSync(cdnDir)) {
|
|
32
|
+
if (entry === 'assets') continue
|
|
33
|
+
const full = resolve(cdnDir, entry)
|
|
34
|
+
if (entry.endsWith('.js') || entry.endsWith('.css') || entry === 'manifest.json' || entry === 'n.json') {
|
|
35
|
+
rmSync(full, { force: true })
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
if (entry === 'chunks') {
|
|
39
|
+
rmSync(full, { recursive: true, force: true })
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (buildId) {
|
|
44
|
+
console.log(`[build-cdn] purged prior dist/cdn artifacts before buildId ${buildId}`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
22
49
|
const root = findProjectRoot()
|
|
50
|
+
const cdnDir = resolve(root, 'dist/cdn')
|
|
23
51
|
|
|
24
52
|
const mode = resolvePortalViteMode()
|
|
25
53
|
|
|
@@ -31,6 +59,8 @@ const env = envForPortalBuild(mode, root, {
|
|
|
31
59
|
VITE_DATADOG_RUM_ENABLED: defaultDatadogFlag(mode),
|
|
32
60
|
})
|
|
33
61
|
|
|
62
|
+
purgeStaleCdnArtifacts(cdnDir, env.MODULE_BUILD_ID ?? env.PORTAL_BUILD_ID)
|
|
63
|
+
|
|
34
64
|
function runWithEnv(config, buildMode) {
|
|
35
65
|
const result = spawnViteSync(
|
|
36
66
|
root,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve vendor/app/css filenames for the active CDN build id.
|
|
3
|
+
* Prevents stale dist/cdn artifacts from an older build id winning .find(name === 'vendor').
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} buildId
|
|
8
|
+
* @param {string} file
|
|
9
|
+
*/
|
|
10
|
+
export function cdnArtifactMatchesBuildId(buildId, file) {
|
|
11
|
+
return String(file).includes(buildId)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} role vendor | app
|
|
16
|
+
* @param {string} buildId
|
|
17
|
+
*/
|
|
18
|
+
export function expectedCdnScriptBasename(role, buildId) {
|
|
19
|
+
return `${role}.${buildId}.js`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** @param {string} buildId */
|
|
23
|
+
export function expectedCdnAppCssBasename(buildId) {
|
|
24
|
+
return `app.${buildId}.css`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {{ buildId: string, scripts?: Array<{ name?: string, file?: string }>, styles?: Array<{ name?: string, file?: string }> }} manifest
|
|
29
|
+
*/
|
|
30
|
+
export function resolveCdnPrimaryScripts(manifest) {
|
|
31
|
+
const buildId = manifest.buildId?.trim()
|
|
32
|
+
if (!buildId) {
|
|
33
|
+
throw new Error('manifest.json missing buildId')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const vendorExpected = expectedCdnScriptBasename('vendor', buildId)
|
|
37
|
+
const appExpected = expectedCdnScriptBasename('app', buildId)
|
|
38
|
+
const appCssExpected = expectedCdnAppCssBasename(buildId)
|
|
39
|
+
|
|
40
|
+
const vendor = manifest.scripts?.find((s) => s.file === vendorExpected)
|
|
41
|
+
const app = manifest.scripts?.find((s) => s.file === appExpected)
|
|
42
|
+
const appCss =
|
|
43
|
+
manifest.styles?.find((s) => s.file === appCssExpected) ??
|
|
44
|
+
manifest.styles?.find((s) => s.name === 'app' && s.file?.includes(buildId))
|
|
45
|
+
|
|
46
|
+
if (!vendor?.file || !app?.file) {
|
|
47
|
+
const vendorCandidates = (manifest.scripts ?? [])
|
|
48
|
+
.filter((s) => s.name === 'vendor')
|
|
49
|
+
.map((s) => s.file)
|
|
50
|
+
.join(', ')
|
|
51
|
+
const appCandidates = (manifest.scripts ?? [])
|
|
52
|
+
.filter((s) => s.name === 'app')
|
|
53
|
+
.map((s) => s.file)
|
|
54
|
+
.join(', ')
|
|
55
|
+
throw new Error(
|
|
56
|
+
`manifest.json must include ${vendorExpected} and ${appExpected} (buildId=${buildId}). ` +
|
|
57
|
+
`Found vendor=[${vendorCandidates || 'none'}], app=[${appCandidates || 'none'}]`,
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!appCss?.file) {
|
|
62
|
+
throw new Error(`manifest.json must include ${appCssExpected} for buildId ${buildId}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const [label, file] of [
|
|
66
|
+
['vendor', vendor.file],
|
|
67
|
+
['app', app.file],
|
|
68
|
+
['app css', appCss.file],
|
|
69
|
+
]) {
|
|
70
|
+
if (!cdnArtifactMatchesBuildId(buildId, file)) {
|
|
71
|
+
throw new Error(`manifest ${label} file ${file} does not match buildId ${buildId}`)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { buildId, vendorFile: vendor.file, appFile: app.file, appCssFile: appCss.file }
|
|
76
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import test from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { resolveCdnPrimaryScripts } from './cdn-release-artifacts.mjs'
|
|
5
|
+
|
|
6
|
+
test('resolveCdnPrimaryScripts picks scripts for manifest buildId, not lexicographically first vendor', () => {
|
|
7
|
+
const manifest = {
|
|
8
|
+
buildId: '5.0.2-beta.802',
|
|
9
|
+
scripts: [
|
|
10
|
+
{ name: 'vendor', file: 'vendor.5.0.2-beta.800.js' },
|
|
11
|
+
{ name: 'vendor', file: 'vendor.5.0.2-beta.802.js' },
|
|
12
|
+
{ name: 'app', file: 'app.5.0.2-beta.802.js' },
|
|
13
|
+
],
|
|
14
|
+
styles: [{ name: 'app', file: 'app.5.0.2-beta.802.css' }],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const resolved = resolveCdnPrimaryScripts(manifest)
|
|
18
|
+
assert.equal(resolved.vendorFile, 'vendor.5.0.2-beta.802.js')
|
|
19
|
+
assert.equal(resolved.appFile, 'app.5.0.2-beta.802.js')
|
|
20
|
+
})
|
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Write dist/cdn/manifest.json after vendor + app builds.
|
|
4
4
|
*/
|
|
5
|
-
import { readdirSync,
|
|
6
|
-
import { resolve
|
|
7
|
-
import { fileURLToPath } from 'node:url'
|
|
8
|
-
import { findProjectRoot } from './project-root.mjs'
|
|
5
|
+
import { readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
6
|
+
import { resolve } from 'node:path'
|
|
9
7
|
import { gzipSync } from 'node:zlib'
|
|
10
|
-
|
|
11
|
-
import {
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
cdnArtifactMatchesBuildId,
|
|
11
|
+
expectedCdnAppCssBasename,
|
|
12
|
+
expectedCdnScriptBasename,
|
|
13
|
+
} from './cdn-release-artifacts.mjs'
|
|
14
|
+
import { findProjectRoot } from './project-root.mjs'
|
|
15
|
+
import { clearPortalBuildIdCache, resolveCdnBaseUrl, portalBuildId } from './resolve-cdn-base-url.mjs'
|
|
16
|
+
|
|
17
|
+
if (process.env.MODULE_BUILD_ID?.trim() || process.env.PORTAL_BUILD_ID?.trim()) {
|
|
18
|
+
clearPortalBuildIdCache()
|
|
19
|
+
}
|
|
12
20
|
|
|
13
21
|
const root = findProjectRoot()
|
|
14
22
|
const cdnDir = resolve(root, 'dist/cdn')
|
|
@@ -37,10 +45,20 @@ function scriptEntry(file) {
|
|
|
37
45
|
return { name: file, order: 99, preload: false }
|
|
38
46
|
}
|
|
39
47
|
|
|
40
|
-
const
|
|
48
|
+
const allJsRelPaths = readdirSync(cdnDir, { recursive: true })
|
|
41
49
|
.filter((f) => String(f).endsWith('.js'))
|
|
42
|
-
.map(
|
|
43
|
-
|
|
50
|
+
.map(String)
|
|
51
|
+
|
|
52
|
+
const staleJs = allJsRelPaths.filter((file) => !cdnArtifactMatchesBuildId(buildId, file))
|
|
53
|
+
if (staleJs.length > 0) {
|
|
54
|
+
console.warn(
|
|
55
|
+
`[cdn-manifest] ignoring ${staleJs.length} stale .js artifact(s) for buildId ${buildId}: ${staleJs.join(', ')}`,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const jsFiles = allJsRelPaths
|
|
60
|
+
.filter((file) => cdnArtifactMatchesBuildId(buildId, file))
|
|
61
|
+
.map((file) => {
|
|
44
62
|
const metrics = fileMetrics(file)
|
|
45
63
|
const entry = scriptEntry(file)
|
|
46
64
|
return {
|
|
@@ -50,8 +68,16 @@ const jsFiles = readdirSync(cdnDir, { recursive: true })
|
|
|
50
68
|
})
|
|
51
69
|
.sort((a, b) => a.order - b.order || a.file.localeCompare(b.file))
|
|
52
70
|
|
|
53
|
-
const
|
|
54
|
-
|
|
71
|
+
const allCssFiles = readdirSync(cdnDir).filter((f) => f.endsWith('.css'))
|
|
72
|
+
const staleCss = allCssFiles.filter((file) => !cdnArtifactMatchesBuildId(buildId, file))
|
|
73
|
+
if (staleCss.length > 0) {
|
|
74
|
+
console.warn(
|
|
75
|
+
`[cdn-manifest] ignoring ${staleCss.length} stale .css artifact(s) for buildId ${buildId}: ${staleCss.join(', ')}`,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const cssFiles = allCssFiles
|
|
80
|
+
.filter((file) => cdnArtifactMatchesBuildId(buildId, file))
|
|
55
81
|
.map((file) => {
|
|
56
82
|
const metrics = fileMetrics(file)
|
|
57
83
|
return {
|
|
@@ -61,15 +87,22 @@ const cssFiles = readdirSync(cdnDir)
|
|
|
61
87
|
}
|
|
62
88
|
})
|
|
63
89
|
|
|
64
|
-
const
|
|
90
|
+
const vendorExpected = expectedCdnScriptBasename('vendor', buildId)
|
|
91
|
+
const appExpected = expectedCdnScriptBasename('app', buildId)
|
|
92
|
+
const appCssExpected = expectedCdnAppCssBasename(buildId)
|
|
65
93
|
|
|
66
|
-
if (!jsFiles.some((f) => f.
|
|
67
|
-
console.error(
|
|
94
|
+
if (!jsFiles.some((f) => f.file === vendorExpected) || !jsFiles.some((f) => f.file === appExpected)) {
|
|
95
|
+
console.error(
|
|
96
|
+
`[cdn-manifest] expected ${vendorExpected} and ${appExpected} in dist/cdn/ (buildId=${buildId})`,
|
|
97
|
+
)
|
|
98
|
+
console.error(`[cdn-manifest] found js: ${jsFiles.map((f) => f.file).join(', ') || '(none)'}`)
|
|
68
99
|
process.exit(1)
|
|
69
100
|
}
|
|
70
101
|
|
|
102
|
+
const appCss = cssFiles.find((f) => f.file === appCssExpected) ?? cssFiles.find((f) => f.name === 'app')
|
|
103
|
+
|
|
71
104
|
if (!appCss) {
|
|
72
|
-
console.error(
|
|
105
|
+
console.error(`[cdn-manifest] expected ${appCssExpected} in dist/cdn/ — component styles load from CDN`)
|
|
73
106
|
process.exit(1)
|
|
74
107
|
}
|
|
75
108
|
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
writeFileSync,
|
|
14
14
|
} from 'node:fs'
|
|
15
15
|
import { resolve } from 'node:path'
|
|
16
|
+
import { resolveCdnPrimaryScripts } from './cdn-release-artifacts.mjs'
|
|
16
17
|
|
|
17
18
|
/** Substrings that break when HubSpot tokenizes module.js (Jinja/HubL). */
|
|
18
19
|
export const FORBIDDEN_HUBL_IN_MODULE_JS = ['{{', '{%', '<%', '%>']
|
|
@@ -70,24 +71,14 @@ export function injectCdnManifestIntoModuleHtml(html, manifestPath) {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
|
|
73
|
-
const
|
|
74
|
-
const app = manifest.scripts?.find((s) => s.name === 'app')
|
|
75
|
-
const appCss = manifest.styles?.find((s) => s.name === 'app') ?? manifest.styles?.[0]
|
|
76
|
-
|
|
77
|
-
if (!vendor?.file || !app?.file) {
|
|
78
|
-
throw new Error('manifest.json must include vendor and app scripts')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (!appCss?.file) {
|
|
82
|
-
throw new Error('manifest.json must include app styles (app.*.css from yarn build:cdn:dev or build:cdn:prod)')
|
|
83
|
-
}
|
|
74
|
+
const { buildId, vendorFile, appFile, appCssFile } = resolveCdnPrimaryScripts(manifest)
|
|
84
75
|
|
|
85
76
|
return html
|
|
86
77
|
.replaceAll('__PORTAL_CDN_BASE__', manifest.baseUrl)
|
|
87
|
-
.replaceAll('__PORTAL_BUILD_ID__',
|
|
88
|
-
.replaceAll('__PORTAL_VENDOR_FILE__',
|
|
89
|
-
.replaceAll('__PORTAL_APP_FILE__',
|
|
90
|
-
.replaceAll('__PORTAL_APP_CSS_FILE__',
|
|
78
|
+
.replaceAll('__PORTAL_BUILD_ID__', buildId)
|
|
79
|
+
.replaceAll('__PORTAL_VENDOR_FILE__', vendorFile)
|
|
80
|
+
.replaceAll('__PORTAL_APP_FILE__', appFile)
|
|
81
|
+
.replaceAll('__PORTAL_APP_CSS_FILE__', appCssFile)
|
|
91
82
|
}
|
|
92
83
|
|
|
93
84
|
/**
|
|
@@ -100,12 +91,9 @@ export function mergeCdnAppStylesIntoModuleCss(moduleDir, manifestPath) {
|
|
|
100
91
|
}
|
|
101
92
|
|
|
102
93
|
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
|
|
103
|
-
const
|
|
104
|
-
if (!appCss?.file) {
|
|
105
|
-
throw new Error('manifest.json must include app styles for module.css merge')
|
|
106
|
-
}
|
|
94
|
+
const { appCssFile } = resolveCdnPrimaryScripts(manifest)
|
|
107
95
|
|
|
108
|
-
const cdnCssPath = resolve(moduleDir, '../cdn',
|
|
96
|
+
const cdnCssPath = resolve(moduleDir, '../cdn', appCssFile)
|
|
109
97
|
if (!existsSync(cdnCssPath)) {
|
|
110
98
|
throw new Error(
|
|
111
99
|
`Missing ${cdnCssPath} — run build:cdn before hubspot post-build (needed for module.css)`,
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Resolve CDN base URL from env (jsDelivr GitHub, custom, or local).
|
|
3
3
|
*/
|
|
4
4
|
import { loadPortalCdnConfig } from './load-portal-cdn-config.mjs'
|
|
5
|
-
import { portalBuildId, resolvePortalBuildId } from './resolve-portal-build-id.mjs'
|
|
5
|
+
import { clearPortalBuildIdCache, portalBuildId, resolvePortalBuildId } from './resolve-portal-build-id.mjs'
|
|
6
6
|
|
|
7
|
-
export { portalBuildId, resolvePortalBuildId }
|
|
7
|
+
export { clearPortalBuildIdCache, portalBuildId, resolvePortalBuildId }
|
|
8
8
|
|
|
9
9
|
export function resolveCdnBaseUrl(env = process.env) {
|
|
10
10
|
const config = loadPortalCdnConfig(env)
|