@heybox/hb-sdk 0.6.8-alpha.1 → 0.6.8
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 +48 -0
- package/README.md +2 -0
- package/dist/cli-chunks/{build-Do18KfQL.cjs → build-D4crgajb.cjs} +29 -25
- package/dist/cli-chunks/{context-CQ5481xZ.cjs → context-fRWWSXDj.cjs} +122 -23
- package/dist/cli-chunks/{create-BU7nViJ9.cjs → create-BU_EFgDz.cjs} +1 -1
- package/dist/cli-chunks/{dev-DJY-_cK4.cjs → dev-BrkChQ7v.cjs} +142 -108
- package/dist/cli-chunks/{doctor-BduOKHnV.cjs → doctor-DZXVO4rL.cjs} +1 -1
- package/dist/cli-chunks/{index-e2GK8bAH.cjs → index-CSjJrnkO.cjs} +14 -14
- package/dist/cli-chunks/{index-BVNo9ygg.cjs → index-DxH41nsb.cjs} +3 -3
- package/dist/cli-chunks/{login-C2wtKo_W.cjs → login-DLVozxrs.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-D769ezhw.cjs → project-vite-CwmMfCk3.cjs} +1 -1
- package/dist/cli-chunks/{remote-Cf4i1DSL.cjs → remote-CfpkkC9I.cjs} +40 -22
- package/dist/cli-chunks/{runtime-gate-DfMJQGH9.cjs → runtime-gate-CgN_v4Te.cjs} +3 -3
- package/dist/cli-chunks/{index-v4-6fbXX.cjs → runtime-permission-env-B0jK9Rt1.cjs} +71 -0
- package/dist/cli-chunks/{session-z1D8xVrB.cjs → session-DB7ARwm4.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/mock-host/main.js +609 -6
- package/dist/index.cjs.js +403 -7
- package/dist/index.esm.js +403 -7
- package/dist/vite.cjs.js +17 -2
- package/dist/vite.esm.js +17 -2
- package/package.json +2 -2
- package/skill/SKILL.md +7 -6
- package/skill/references/api-root.md +7 -2
- package/skill/references/cli.md +4 -0
- package/skill/references/examples.md +17 -2
- package/skill/references/safety-boundaries.md +2 -0
- package/skill/scripts/sync-references.mjs +17 -2
- package/skill/skill.json +4 -4
- package/types/core/network-sanitize.d.ts +31 -0
- package/types/modules/network/index.d.ts +2 -0
- package/types/modules/network/observability.d.ts +39 -0
- package/types/modules/network/request-shape.d.ts +44 -0
- package/types/vite/html-policy.d.ts +1 -0
- package/types/vite/runtime-permission-env.d.ts +9 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var promises = require('node:readline/promises');
|
|
4
|
-
var index = require('./index-
|
|
5
|
-
var session = require('./session-
|
|
4
|
+
var index = require('./index-CSjJrnkO.cjs');
|
|
5
|
+
var session = require('./session-DB7ARwm4.cjs');
|
|
6
6
|
var childProcess = require('node:child_process');
|
|
7
7
|
var fs = require('node:fs');
|
|
8
8
|
var fs$1 = require('node:fs/promises');
|
|
9
9
|
var path = require('node:path');
|
|
10
|
-
var
|
|
11
|
-
var runtimeGate = require('./runtime-gate-
|
|
12
|
-
var context = require('./context-
|
|
10
|
+
var runtimePermissionEnv = require('./runtime-permission-env-B0jK9Rt1.cjs');
|
|
11
|
+
var runtimeGate = require('./runtime-gate-CgN_v4Te.cjs');
|
|
12
|
+
var context = require('./context-fRWWSXDj.cjs');
|
|
13
13
|
require('node:module');
|
|
14
14
|
require('path');
|
|
15
15
|
require('os');
|
|
@@ -23,6 +23,7 @@ require('util');
|
|
|
23
23
|
require('node:crypto');
|
|
24
24
|
require('fs');
|
|
25
25
|
require('constants');
|
|
26
|
+
require('node:async_hooks');
|
|
26
27
|
|
|
27
28
|
async function getCDNUploadInfo(options, runtime = {}) {
|
|
28
29
|
const body = new URLSearchParams();
|
|
@@ -69,7 +70,7 @@ async function runUpload(options, runtime = {}) {
|
|
|
69
70
|
const postCDNUploadCallback$1 = runtime.postCDNUploadCallback ?? postCDNUploadCallback;
|
|
70
71
|
const createReadStream = runtime.createReadStream ?? fs.createReadStream;
|
|
71
72
|
const orderedFiles = [...options.files].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
72
|
-
const batches = chunkUploadFiles(orderedFiles,
|
|
73
|
+
const batches = chunkUploadFiles(orderedFiles, runtimePermissionEnv.MINIAPP_UPLOAD_BATCH_SIZE);
|
|
73
74
|
await logger.task(`正在上传 ${orderedFiles.length} 个文件`, async (taskContext) => {
|
|
74
75
|
logger.debug(`上传并发数: ${concurrency}`);
|
|
75
76
|
logger.debug(`上传批次数: ${batches.length}`);
|
|
@@ -79,7 +80,7 @@ async function runUpload(options, runtime = {}) {
|
|
|
79
80
|
logger.debug(`正在处理上传批次 ${batchIndex + 1}/${batches.length},文件数: ${batch.length}`);
|
|
80
81
|
logger.debug('正在获取 CDN 上传信息');
|
|
81
82
|
const fileInfos = createUploadFileInfos(batch, options);
|
|
82
|
-
const uploadInfo = await getCDNUploadInfo$1({ session: options.session, scope:
|
|
83
|
+
const uploadInfo = await getCDNUploadInfo$1({ session: options.session, scope: runtimePermissionEnv.MINIAPP_UPLOAD_SCOPE, fileInfos, needCache: false }, { baseUrl: runtime.baseUrl, fetchImpl });
|
|
83
84
|
logger.debug(`CDN bucket: ${uploadInfo.bucket}`);
|
|
84
85
|
logger.debug(`CDN region: ${uploadInfo.region}`);
|
|
85
86
|
validateUploadInfoKeys(batch, uploadInfo.keys, options);
|
|
@@ -140,7 +141,7 @@ function createUploadFileInfos(files, options) {
|
|
|
140
141
|
name: file.relativePath.split('/').pop() ?? file.relativePath,
|
|
141
142
|
mimetype: file.mimeType,
|
|
142
143
|
fsize: file.size,
|
|
143
|
-
path:
|
|
144
|
+
path: runtimePermissionEnv.getMiniappUploadKey({
|
|
144
145
|
miniProgramId: options.miniProgramId,
|
|
145
146
|
version: options.version,
|
|
146
147
|
relativePath: file.relativePath,
|
|
@@ -151,15 +152,15 @@ function validateUploadInfoKeys(files, keys, options) {
|
|
|
151
152
|
if (keys.length !== files.length) {
|
|
152
153
|
throw new index.CliError(`CDN 上传接口返回 key 数量异常:期望 ${files.length},实际 ${keys.length}`);
|
|
153
154
|
}
|
|
154
|
-
for (let index$
|
|
155
|
-
const expectedKey =
|
|
155
|
+
for (let index$1 = 0; index$1 < files.length; index$1 += 1) {
|
|
156
|
+
const expectedKey = runtimePermissionEnv.getMiniappUploadKey({
|
|
156
157
|
miniProgramId: options.miniProgramId,
|
|
157
158
|
version: options.version,
|
|
158
|
-
relativePath: files[index$
|
|
159
|
+
relativePath: files[index$1].relativePath,
|
|
159
160
|
});
|
|
160
|
-
const actualKey = keys[index$
|
|
161
|
+
const actualKey = keys[index$1];
|
|
161
162
|
if (actualKey !== expectedKey) {
|
|
162
|
-
throw new index.CliError(`CDN 上传接口返回 key 异常:${files[index$
|
|
163
|
+
throw new index.CliError(`CDN 上传接口返回 key 异常:${files[index$1].relativePath}`, [`CDN 上传接口返回 key 异常:${files[index$1].relativePath}`, `expected: ${expectedKey}`, `actual: ${actualKey}`].join('\n'));
|
|
163
164
|
}
|
|
164
165
|
}
|
|
165
166
|
}
|
|
@@ -191,7 +192,7 @@ async function createDefaultCosClient(uploadToken) {
|
|
|
191
192
|
};
|
|
192
193
|
}
|
|
193
194
|
async function loadCosConstructor() {
|
|
194
|
-
const cosModule = await Promise.resolve().then(function () { return require('./index-
|
|
195
|
+
const cosModule = await Promise.resolve().then(function () { return require('./index-DxH41nsb.cjs'); }).then(function (n) { return n.index; });
|
|
195
196
|
return cosModule.default;
|
|
196
197
|
}
|
|
197
198
|
function formatSize(bytes) {
|
|
@@ -235,7 +236,7 @@ function isRecord(value) {
|
|
|
235
236
|
}
|
|
236
237
|
|
|
237
238
|
async function precheckUserMiniprogramVersion(options, runtime = {}) {
|
|
238
|
-
return postUserMiniprogramVersionForm(
|
|
239
|
+
return postUserMiniprogramVersionForm(runtimePermissionEnv.PRECHECK_USER_MINIPROGRAM_VERSION_API_PATH, options, {
|
|
239
240
|
// 展示资料由服务端沿用 last approved 或注入默认名/默认图;CLI 不传 name/icon/cover。
|
|
240
241
|
...(options.version ? { version: options.version } : {}),
|
|
241
242
|
...(options.source_version ? { source_version: options.source_version } : {}),
|
|
@@ -244,7 +245,7 @@ async function precheckUserMiniprogramVersion(options, runtime = {}) {
|
|
|
244
245
|
}, runtime);
|
|
245
246
|
}
|
|
246
247
|
async function submitUserMiniprogramAudit(options, runtime = {}) {
|
|
247
|
-
return postUserMiniprogramVersionForm(
|
|
248
|
+
return postUserMiniprogramVersionForm(runtimePermissionEnv.SUBMIT_USER_MINIPROGRAM_AUDIT_API_PATH, options, {
|
|
248
249
|
// 展示资料由服务端沿用 last approved 或注入默认名/默认图;CLI 不传 name/icon/cover。
|
|
249
250
|
...(options.manifest ? { manifest: JSON.stringify(options.manifest) } : {}),
|
|
250
251
|
...(options.source_version ? { source_version: options.source_version } : {}),
|
|
@@ -341,12 +342,29 @@ async function runDeployCommand(options, runtime = {}) {
|
|
|
341
342
|
releaseNote,
|
|
342
343
|
autoPublish,
|
|
343
344
|
}, runtime, apiBaseUrl, projectRoot, logger);
|
|
345
|
+
let skipPlatformCsp = false;
|
|
346
|
+
try {
|
|
347
|
+
const { detail } = await logger.task('正在读取远端小程序权限', () => (runtime.getBoundMiniProgram ?? context.getBoundMiniProgram)({
|
|
348
|
+
allowUnsafeApiBaseUrl: options.allowUnsafeApiBaseUrl,
|
|
349
|
+
apiBaseUrl,
|
|
350
|
+
cwd: projectRoot,
|
|
351
|
+
env,
|
|
352
|
+
fetchImpl: runtime.fetchImpl,
|
|
353
|
+
loginBaseUrl,
|
|
354
|
+
session: session$1,
|
|
355
|
+
}), { successText: '已读取远端小程序权限' });
|
|
356
|
+
skipPlatformCsp = context.shouldSkipMiniappPlatformCsp(detail.runtime_permissions);
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
logger.warn('未读取到远端 Runtime 权限,构建将继续注入平台 CSP。');
|
|
360
|
+
logger.debug(`远端 Runtime 权限读取失败详情:${index.readRedactedErrorMessage(error, { verbose: true })}`);
|
|
361
|
+
}
|
|
344
362
|
const pm = detectPackageManager(projectRoot);
|
|
345
363
|
const platform = runtime.platform ?? process.platform;
|
|
346
364
|
const buildMode = validateBuildMode(env.HB_SDK_ENV?.trim(), platform);
|
|
347
365
|
logger.info(`开始构建: ${pm} ${createBuildArgs(pm, buildMode).join(' ')}`);
|
|
348
366
|
await runBuildScript(pm, projectRoot, runtime.spawn ?? childProcess.spawn, {
|
|
349
|
-
env,
|
|
367
|
+
env: runtimePermissionEnv.createMiniappPlatformCspEnv(env, skipPlatformCsp),
|
|
350
368
|
mode: buildMode,
|
|
351
369
|
outputMode: options.buildOutputMode ?? 'inherit',
|
|
352
370
|
platform,
|
|
@@ -366,16 +384,16 @@ async function runDeployCommand(options, runtime = {}) {
|
|
|
366
384
|
}
|
|
367
385
|
const uploadFiles = await logger.task('正在校验 dist 上传文件', async () => {
|
|
368
386
|
const allFiles = await walkDistFiles(distDir);
|
|
369
|
-
const blocked = allFiles.find((entry) =>
|
|
387
|
+
const blocked = allFiles.find((entry) => runtimePermissionEnv.relativePathContainsNodeModulesSegment(entry.relativePath));
|
|
370
388
|
if (blocked) {
|
|
371
389
|
throw new Error(`dist 目录含 node_modules:${blocked.relativePath}`);
|
|
372
390
|
}
|
|
373
|
-
const files = allFiles.filter((entry) =>
|
|
374
|
-
const pathError =
|
|
391
|
+
const files = allFiles.filter((entry) => runtimePermissionEnv.shouldUploadDistFile(entry.relativePath));
|
|
392
|
+
const pathError = runtimePermissionEnv.validateUploadPaths(files, { miniProgramId, version, maxLength: runtimePermissionEnv.ACTIVITY_UPLOAD_KEY_MAX_LENGTH });
|
|
375
393
|
if (pathError) {
|
|
376
394
|
throw new Error(pathError);
|
|
377
395
|
}
|
|
378
|
-
const sizeError =
|
|
396
|
+
const sizeError = runtimePermissionEnv.validateUploadTotalSize(files);
|
|
379
397
|
if (sizeError) {
|
|
380
398
|
throw new Error(sizeError);
|
|
381
399
|
}
|
|
@@ -602,7 +620,7 @@ async function walkDistFiles(distDir) {
|
|
|
602
620
|
}
|
|
603
621
|
const fileStat = await fs$1.stat(absPath);
|
|
604
622
|
results.push({
|
|
605
|
-
relativePath:
|
|
623
|
+
relativePath: runtimePermissionEnv.normalizeRelativePath(relPath),
|
|
606
624
|
absolutePath: absPath,
|
|
607
625
|
size: fileStat.size,
|
|
608
626
|
mimeType: inferMimeType(entry.name),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var runtimePermissionEnv = require('./runtime-permission-env-B0jK9Rt1.cjs');
|
|
4
4
|
|
|
5
5
|
function isValidMiniappManifestVersion(version) {
|
|
6
6
|
return getMiniappManifestVersionError(version) === undefined;
|
|
@@ -41,7 +41,7 @@ function validateSdkVersion(version, sourceLabel = 'sdkVersion') {
|
|
|
41
41
|
throw new Error(`${sourceLabel} 必须是合法 SemVer:${String(version)}`);
|
|
42
42
|
}
|
|
43
43
|
const normalized = version.trim();
|
|
44
|
-
const parsed =
|
|
44
|
+
const parsed = runtimePermissionEnv.semverExports.parse(normalized);
|
|
45
45
|
if (!parsed || parsed.version !== normalized || parsed.build.length > 0) {
|
|
46
46
|
throw new Error(`${sourceLabel} 必须是合法 SemVer:${normalized}`);
|
|
47
47
|
}
|
|
@@ -55,7 +55,7 @@ function getMiniappManifestVersionError(version) {
|
|
|
55
55
|
return `必须是合法 SemVer:${String(version)}`;
|
|
56
56
|
}
|
|
57
57
|
const normalized = version.trim();
|
|
58
|
-
const parsed =
|
|
58
|
+
const parsed = runtimePermissionEnv.semverExports.parse(normalized);
|
|
59
59
|
if (!parsed) {
|
|
60
60
|
return `必须是合法 SemVer:${normalized}`;
|
|
61
61
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var node_async_hooks = require('node:async_hooks');
|
|
4
|
+
|
|
3
5
|
var re = {exports: {}};
|
|
4
6
|
|
|
5
7
|
var constants;
|
|
@@ -2906,6 +2908,72 @@ function shouldUploadDistFile(relativePath) {
|
|
|
2906
2908
|
return true;
|
|
2907
2909
|
}
|
|
2908
2910
|
|
|
2911
|
+
const HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV = 'HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN';
|
|
2912
|
+
const HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV = 'HB_SDK_RUNTIME_PERMISSION_CONTEXT';
|
|
2913
|
+
const VERIFIED_RUNTIME_PERMISSION_CONTEXT = 'verified';
|
|
2914
|
+
const environmentScopeStorage = new node_async_hooks.AsyncLocalStorage();
|
|
2915
|
+
let environmentScopeTail = Promise.resolve();
|
|
2916
|
+
function shouldSkipMiniappPlatformCspFromEnv(env = process.env) {
|
|
2917
|
+
return env[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV] === VERIFIED_RUNTIME_PERMISSION_CONTEXT && env[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV] === '1';
|
|
2918
|
+
}
|
|
2919
|
+
function createMiniappPlatformCspEnv(env, skipPlatformCsp) {
|
|
2920
|
+
return {
|
|
2921
|
+
...env,
|
|
2922
|
+
[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV]: VERIFIED_RUNTIME_PERMISSION_CONTEXT,
|
|
2923
|
+
[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV]: skipPlatformCsp ? '1' : '0',
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
async function withMiniappPlatformCspEnv(skipPlatformCsp, action, env = process.env) {
|
|
2927
|
+
if (environmentScopeStorage.getStore()?.active) {
|
|
2928
|
+
return runMiniappPlatformCspEnvScope(skipPlatformCsp, action, env);
|
|
2929
|
+
}
|
|
2930
|
+
let releaseScope = () => undefined;
|
|
2931
|
+
const previousScope = environmentScopeTail;
|
|
2932
|
+
environmentScopeTail = new Promise((resolve) => {
|
|
2933
|
+
releaseScope = resolve;
|
|
2934
|
+
});
|
|
2935
|
+
await previousScope;
|
|
2936
|
+
const scope = { active: true };
|
|
2937
|
+
try {
|
|
2938
|
+
return await environmentScopeStorage.run(scope, () => runMiniappPlatformCspEnvScope(skipPlatformCsp, action, env));
|
|
2939
|
+
}
|
|
2940
|
+
finally {
|
|
2941
|
+
scope.active = false;
|
|
2942
|
+
releaseScope();
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
async function runMiniappPlatformCspEnvScope(skipPlatformCsp, action, env) {
|
|
2946
|
+
const targets = env === process.env ? [env] : [env, process.env];
|
|
2947
|
+
const previousValues = targets.map((target) => ({
|
|
2948
|
+
context: target[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV],
|
|
2949
|
+
officialDomain: target[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV],
|
|
2950
|
+
}));
|
|
2951
|
+
for (const target of targets) {
|
|
2952
|
+
target[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV] = VERIFIED_RUNTIME_PERMISSION_CONTEXT;
|
|
2953
|
+
target[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV] = skipPlatformCsp ? '1' : '0';
|
|
2954
|
+
}
|
|
2955
|
+
try {
|
|
2956
|
+
return await action();
|
|
2957
|
+
}
|
|
2958
|
+
finally {
|
|
2959
|
+
targets.forEach((target, index) => {
|
|
2960
|
+
const previousValue = previousValues[index];
|
|
2961
|
+
if (previousValue?.context === undefined) {
|
|
2962
|
+
delete target[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV];
|
|
2963
|
+
}
|
|
2964
|
+
else {
|
|
2965
|
+
target[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV] = previousValue.context;
|
|
2966
|
+
}
|
|
2967
|
+
if (previousValue?.officialDomain === undefined) {
|
|
2968
|
+
delete target[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV];
|
|
2969
|
+
}
|
|
2970
|
+
else {
|
|
2971
|
+
target[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV] = previousValue.officialDomain;
|
|
2972
|
+
}
|
|
2973
|
+
});
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2909
2977
|
exports.ACTIVITY_UPLOAD_KEY_MAX_LENGTH = ACTIVITY_UPLOAD_KEY_MAX_LENGTH;
|
|
2910
2978
|
exports.CREATE_USER_MINIPROGRAM_API_PATH = CREATE_USER_MINIPROGRAM_API_PATH;
|
|
2911
2979
|
exports.CREATE_USER_MINIPROGRAM_LEADERBOARD_API_PATH = CREATE_USER_MINIPROGRAM_LEADERBOARD_API_PATH;
|
|
@@ -2930,11 +2998,14 @@ exports.USER_MINIPROGRAM_ACCESS_STATUS_API_PATH = USER_MINIPROGRAM_ACCESS_STATUS
|
|
|
2930
2998
|
exports.USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH = USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH;
|
|
2931
2999
|
exports.USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH = USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH;
|
|
2932
3000
|
exports.WITHDRAW_USER_MINIPROGRAM_VERSION_API_PATH = WITHDRAW_USER_MINIPROGRAM_VERSION_API_PATH;
|
|
3001
|
+
exports.createMiniappPlatformCspEnv = createMiniappPlatformCspEnv;
|
|
2933
3002
|
exports.getMiniappUploadKey = getMiniappUploadKey;
|
|
2934
3003
|
exports.normalizeRelativePath = normalizeRelativePath;
|
|
2935
3004
|
exports.relativePathContainsNodeModulesSegment = relativePathContainsNodeModulesSegment;
|
|
2936
3005
|
exports.requireSemver = requireSemver;
|
|
2937
3006
|
exports.semverExports = semverExports;
|
|
3007
|
+
exports.shouldSkipMiniappPlatformCspFromEnv = shouldSkipMiniappPlatformCspFromEnv;
|
|
2938
3008
|
exports.shouldUploadDistFile = shouldUploadDistFile;
|
|
2939
3009
|
exports.validateUploadPaths = validateUploadPaths;
|
|
2940
3010
|
exports.validateUploadTotalSize = validateUploadTotalSize;
|
|
3011
|
+
exports.withMiniappPlatformCspEnv = withMiniappPlatformCspEnv;
|