@dp-bohrium/widget-cli 0.1.0 → 0.2.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/AGENTS.md +131 -8
- package/CHANGELOG.md +61 -0
- package/README.md +56 -98
- package/assets/licenses/ajv@8.20.0/LICENSE +22 -0
- package/assets/licenses/fast-deep-equal@3.1.3/LICENSE +21 -0
- package/assets/licenses/fast-uri@3.1.6/LICENSE +30 -0
- package/assets/licenses/json-schema-traverse@1.0.0/LICENSE +21 -0
- package/assets/licenses/require-from-string@2.0.2/license +21 -0
- package/assets/licenses/widget-contracts.LICENSE +21 -0
- package/assets/licenses/widget-sdk.LICENSE +21 -0
- package/assets/schemas/host-runtime-config.schema.json +305 -0
- package/assets/schemas/widget-manifest.schema.json +360 -0
- package/assets/schemas/widget-plugin-manifest.schema.json +162 -0
- package/assets/schemas/widget-runtime-contracts.schema.json +2725 -0
- package/assets/toolchain.json +24 -0
- package/dist/cli.mjs +1066 -143
- package/dist/core.mjs +1 -1
- package/dist/{dsh-DgkNH7L-.mjs → dsh-BzxDpEcZ.mjs} +371 -78
- package/llms.txt +52 -4
- package/package.json +13 -12
package/dist/core.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as resolveWidgetProjects, l as compileAuthorManifest, n as scaffoldDshWidget, r as buildWidgetPackage, s as checkWidgetProject, t as auditDshPlugin } from "./dsh-BzxDpEcZ.mjs";
|
|
2
2
|
//#region src/core.mjs
|
|
3
3
|
/** Validate a community template before registering it in a custom CLI. */
|
|
4
4
|
function defineWidgetTemplate(template) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { lstat, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -95,52 +95,78 @@ function collectReferencedFileIds(request) {
|
|
|
95
95
|
return referenced;
|
|
96
96
|
}
|
|
97
97
|
//#endregion
|
|
98
|
-
//#region ../widget-
|
|
99
|
-
|
|
98
|
+
//#region ../widget-sdk/dist/runtime-RyAEhkyn.js
|
|
99
|
+
/** 从当前安装的 SDK package.json 读取版本,插件作者不需要重复填写。 */
|
|
100
|
+
const WIDGET_SDK_VERSION = "0.4.1";
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region ../widget-sdk/dist/index.js
|
|
103
|
+
function compiledContribution(contribution) {
|
|
104
|
+
switch (contribution.type) {
|
|
105
|
+
case "panel": return {
|
|
106
|
+
type: "panel",
|
|
107
|
+
id: contribution.id,
|
|
108
|
+
...contribution.placement === void 0 ? {} : { placement: contribution.placement }
|
|
109
|
+
};
|
|
110
|
+
case "file-preview": return {
|
|
111
|
+
type: "file-preview",
|
|
112
|
+
extensions: [...contribution.extensions]
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
100
116
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
117
|
+
* 将开发者维护的插件清单编译为宿主可加载的宿主清单。
|
|
118
|
+
*
|
|
119
|
+
* 这是构建工具的边界函数,不是类型提示入口:作者直接编辑带 `$schema` 的 JSON,
|
|
120
|
+
* SDK 版本从当前安装包读取,宿主入口默认落到 dist/widget.js,内部 contribution 标识只在这里生成。
|
|
105
121
|
*/
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
122
|
+
function compileWidgetPluginManifest(manifest, options = {}) {
|
|
123
|
+
const samples = options.samples ?? manifest.samples;
|
|
124
|
+
if (!samples || samples.length === 0) throw new Error("Widget Plugin Manifest must provide at least one sample before packaging");
|
|
109
125
|
const contribution = manifest.contributes.widget;
|
|
110
|
-
if (
|
|
111
|
-
const
|
|
112
|
-
type: "panel",
|
|
113
|
-
id: contribution.id
|
|
114
|
-
} : {
|
|
115
|
-
type: "file-preview",
|
|
116
|
-
extensions: [...contribution.extensions]
|
|
117
|
-
};
|
|
126
|
+
if (contribution.type === "panel" && contribution.placement?.kind === "region" && (manifest.capabilities?.region?.mode !== contribution.placement.mode || !manifest.capabilities.region.regions.includes(contribution.placement.region))) throw new Error("A region Widget contribution must declare a matching region capability");
|
|
127
|
+
const activationEvents = manifest.activationEvents ? [...manifest.activationEvents] : void 0;
|
|
118
128
|
return {
|
|
119
129
|
name: manifest.name,
|
|
120
130
|
...manifest.description === void 0 ? {} : { description: manifest.description },
|
|
121
131
|
widgetConfig: {
|
|
122
132
|
schemaVersion: 1,
|
|
123
133
|
hostApiVersion: 1,
|
|
124
|
-
widgetSdkVersion:
|
|
134
|
+
widgetSdkVersion: WIDGET_SDK_VERSION,
|
|
125
135
|
entry: options.hostEntry ?? "dist/widget.js",
|
|
126
|
-
contribution: compiledContribution,
|
|
127
|
-
...
|
|
136
|
+
contribution: compiledContribution(manifest.contributes.widget),
|
|
137
|
+
...activationEvents === void 0 ? {} : { activationEvents },
|
|
128
138
|
...manifest.capabilities === void 0 ? {} : { requestedCapabilities: manifest.capabilities },
|
|
129
139
|
...manifest.runtime === void 0 ? {} : { runtime: manifest.runtime },
|
|
130
|
-
samples
|
|
140
|
+
samples
|
|
131
141
|
}
|
|
132
142
|
};
|
|
133
143
|
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region ../widget-project/src/manifest.mjs
|
|
146
|
+
/**
|
|
147
|
+
* Convert the author-facing VS Code-style plugin manifest to the Host
|
|
148
|
+
* manifest consumed by Host Runtime Config. This is deliberately a tooling
|
|
149
|
+
* boundary: source code and editor tooling use `contributes`, while only the
|
|
150
|
+
* upload/preview pipeline knows about `widgetConfig.contribution` and protocol fields.
|
|
151
|
+
*/
|
|
152
|
+
function compileAuthorManifest(manifest, options = {}) {
|
|
153
|
+
if (!isAuthorManifest(manifest)) return manifest;
|
|
154
|
+
const contribution = manifest.contributes?.widget;
|
|
155
|
+
if (!contribution || typeof contribution !== "object" || !["panel", "file-preview"].includes(contribution.type) || contribution.type === "file-preview" && !Array.isArray(contribution.extensions)) return manifest;
|
|
156
|
+
const compiled = (options.compiler ?? compileWidgetPluginManifest)(manifest, {
|
|
157
|
+
hostEntry: options.hostEntry,
|
|
158
|
+
samples: options.samples
|
|
159
|
+
});
|
|
160
|
+
if (options.sdkVersion !== void 0 && compiled.widgetConfig.widgetSdkVersion !== options.sdkVersion) {
|
|
161
|
+
const error = /* @__PURE__ */ new Error(`Manifest compiler SDK version ${compiled.widgetConfig.widgetSdkVersion} does not match requested SDK version ${options.sdkVersion}`);
|
|
162
|
+
error.code = "WIDGET_CLI_PROJECT_SDK_MISMATCH";
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
return compiled;
|
|
166
|
+
}
|
|
134
167
|
function isAuthorManifest(value) {
|
|
135
168
|
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "contributes" in value && "version" in value);
|
|
136
169
|
}
|
|
137
|
-
function installedSdkVersion() {
|
|
138
|
-
try {
|
|
139
|
-
return require$2("@dp-bohrium/widget-sdk/package.json").version;
|
|
140
|
-
} catch {
|
|
141
|
-
return "0.0.0-dev";
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
170
|
//#endregion
|
|
145
171
|
//#region ../../node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/codegen/code.js
|
|
146
172
|
var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -6798,7 +6824,7 @@ var require_json_schema_2020_12 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6798
6824
|
exports.default = addMetaSchema2020;
|
|
6799
6825
|
}));
|
|
6800
6826
|
//#endregion
|
|
6801
|
-
//#region ../contract-testing/dist/manifest-validator-
|
|
6827
|
+
//#region ../contract-testing/dist/manifest-validator-DCdGSuVM.js
|
|
6802
6828
|
var import__2020 = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
6803
6829
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6804
6830
|
exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = exports.Ajv2020 = void 0;
|
|
@@ -6978,7 +7004,7 @@ const widgetManifestSchema = {
|
|
|
6978
7004
|
}
|
|
6979
7005
|
},
|
|
6980
7006
|
"FilesCapabilityRequest": {
|
|
6981
|
-
"description": "
|
|
7007
|
+
"description": "文件能力的独立申请开关;保存已有文件必须单独申请 files.write 并获得 Host 授权,可写范围由 Host grant 决定。",
|
|
6982
7008
|
"title": "FilesCapabilityRequest",
|
|
6983
7009
|
"type": "object",
|
|
6984
7010
|
"additionalProperties": false,
|
|
@@ -6986,7 +7012,8 @@ const widgetManifestSchema = {
|
|
|
6986
7012
|
"list": { "type": "boolean" },
|
|
6987
7013
|
"metadata": { "type": "boolean" },
|
|
6988
7014
|
"read": { "type": "boolean" },
|
|
6989
|
-
"download": { "type": "boolean" }
|
|
7015
|
+
"download": { "type": "boolean" },
|
|
7016
|
+
"write": { "type": "boolean" }
|
|
6990
7017
|
}
|
|
6991
7018
|
},
|
|
6992
7019
|
"EventsCapabilityRequest": {
|
|
@@ -7439,6 +7466,8 @@ const widgetRuntimeContractsSchema = {
|
|
|
7439
7466
|
"presentationCapabilityResult": { "$ref": "#/$defs/PresentationCapabilityResult" },
|
|
7440
7467
|
"regionCapabilityResult": { "$ref": "#/$defs/RegionCapabilityResult" },
|
|
7441
7468
|
"fileDownloadResult": { "$ref": "#/$defs/FileDownloadResult" },
|
|
7469
|
+
"fileReadResult": { "$ref": "#/$defs/FileReadResult" },
|
|
7470
|
+
"fileWriteResult": { "$ref": "#/$defs/FileWriteResult" },
|
|
7442
7471
|
"taskObservationSnapshot": { "$ref": "#/$defs/TaskObservationSnapshot" },
|
|
7443
7472
|
"taskObservationEvent": { "$ref": "#/$defs/TaskObservationEvent" },
|
|
7444
7473
|
"taskObservationBatch": { "$ref": "#/$defs/TaskObservationBatch" },
|
|
@@ -7671,7 +7700,7 @@ const widgetRuntimeContractsSchema = {
|
|
|
7671
7700
|
}
|
|
7672
7701
|
},
|
|
7673
7702
|
"FileReference": {
|
|
7674
|
-
"description": "
|
|
7703
|
+
"description": "Host 签发的文件引用;仅含不透明 id、可选内容版本和展示元数据。引用本身不授予写权限,不含路径或 URL。",
|
|
7675
7704
|
"title": "FileReference",
|
|
7676
7705
|
"type": "object",
|
|
7677
7706
|
"additionalProperties": false,
|
|
@@ -7703,7 +7732,121 @@ const widgetRuntimeContractsSchema = {
|
|
|
7703
7732
|
"size": {
|
|
7704
7733
|
"type": "integer",
|
|
7705
7734
|
"minimum": 0
|
|
7706
|
-
}
|
|
7735
|
+
},
|
|
7736
|
+
"revision": { "$ref": "#/$defs/FileRevision" }
|
|
7737
|
+
}
|
|
7738
|
+
},
|
|
7739
|
+
"FileRevision": {
|
|
7740
|
+
"description": "由存储执行端签发的文件内容版本,用于条件读取和保存;不是 RenderRequest.revision。",
|
|
7741
|
+
"title": "FileRevision",
|
|
7742
|
+
"type": "string",
|
|
7743
|
+
"minLength": 1,
|
|
7744
|
+
"maxLength": 256
|
|
7745
|
+
},
|
|
7746
|
+
"FileMutationId": {
|
|
7747
|
+
"description": "一次逻辑保存的稳定标识;重试或确认结果时必须复用,相同标识不得提交不同内容。",
|
|
7748
|
+
"title": "FileMutationId",
|
|
7749
|
+
"type": "string",
|
|
7750
|
+
"minLength": 1,
|
|
7751
|
+
"maxLength": 128,
|
|
7752
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]*$"
|
|
7753
|
+
},
|
|
7754
|
+
"FileReadInput": {
|
|
7755
|
+
"description": "分块读取已授权文件;后续分块应携带首块返回的 expectedRevision,防止拼接不同版本。",
|
|
7756
|
+
"title": "FileReadInput",
|
|
7757
|
+
"type": "object",
|
|
7758
|
+
"additionalProperties": false,
|
|
7759
|
+
"required": [
|
|
7760
|
+
"file",
|
|
7761
|
+
"offset",
|
|
7762
|
+
"length",
|
|
7763
|
+
"encoding"
|
|
7764
|
+
],
|
|
7765
|
+
"properties": {
|
|
7766
|
+
"file": { "$ref": "#/$defs/FileReference" },
|
|
7767
|
+
"offset": {
|
|
7768
|
+
"type": "integer",
|
|
7769
|
+
"minimum": 0,
|
|
7770
|
+
"maximum": 9007199254740991
|
|
7771
|
+
},
|
|
7772
|
+
"length": {
|
|
7773
|
+
"type": "integer",
|
|
7774
|
+
"minimum": 1,
|
|
7775
|
+
"maximum": 9007199254740991
|
|
7776
|
+
},
|
|
7777
|
+
"encoding": { "enum": ["utf8", "base64"] },
|
|
7778
|
+
"expectedRevision": { "$ref": "#/$defs/FileRevision" }
|
|
7779
|
+
}
|
|
7780
|
+
},
|
|
7781
|
+
"FileReadResult": {
|
|
7782
|
+
"description": "文件读取结果;普通读取只要求内容与编码,原有游标和截断信息按 Host 能力返回。revision 可选且必须与本次读取的内容属于同一快照;请求 expectedRevision 时必须返回匹配版本。缺少 revision 的结果仅用于只读展示,不能据此条件保存。提供字节游标时 nextOffset 等于请求 offset + bytesRead,不按字符串长度计数。",
|
|
7783
|
+
"title": "FileReadResult",
|
|
7784
|
+
"type": "object",
|
|
7785
|
+
"additionalProperties": false,
|
|
7786
|
+
"required": ["content", "encoding"],
|
|
7787
|
+
"properties": {
|
|
7788
|
+
"content": { "type": "string" },
|
|
7789
|
+
"encoding": { "enum": ["utf8", "base64"] },
|
|
7790
|
+
"offset": {
|
|
7791
|
+
"type": "integer",
|
|
7792
|
+
"minimum": 0,
|
|
7793
|
+
"maximum": 9007199254740991
|
|
7794
|
+
},
|
|
7795
|
+
"bytesRead": {
|
|
7796
|
+
"type": "integer",
|
|
7797
|
+
"minimum": 0,
|
|
7798
|
+
"maximum": 9007199254740991
|
|
7799
|
+
},
|
|
7800
|
+
"nextOffset": {
|
|
7801
|
+
"type": "integer",
|
|
7802
|
+
"minimum": 0,
|
|
7803
|
+
"maximum": 9007199254740991
|
|
7804
|
+
},
|
|
7805
|
+
"eof": { "type": "boolean" },
|
|
7806
|
+
"truncated": { "type": "boolean" },
|
|
7807
|
+
"revision": { "$ref": "#/$defs/FileRevision" }
|
|
7808
|
+
}
|
|
7809
|
+
},
|
|
7810
|
+
"FileWriteInput": {
|
|
7811
|
+
"description": "按 Host 授权范围对已有文件作整文件条件替换;Host 校验引用、作用域、权限和实际字节预算。超时不表示未写入,使用同一 mutationId 核对结果。",
|
|
7812
|
+
"title": "FileWriteInput",
|
|
7813
|
+
"type": "object",
|
|
7814
|
+
"additionalProperties": false,
|
|
7815
|
+
"required": [
|
|
7816
|
+
"file",
|
|
7817
|
+
"content",
|
|
7818
|
+
"encoding",
|
|
7819
|
+
"expectedRevision",
|
|
7820
|
+
"mutationId"
|
|
7821
|
+
],
|
|
7822
|
+
"properties": {
|
|
7823
|
+
"file": { "$ref": "#/$defs/FileReference" },
|
|
7824
|
+
"content": { "type": "string" },
|
|
7825
|
+
"encoding": { "enum": ["utf8", "base64"] },
|
|
7826
|
+
"expectedRevision": { "$ref": "#/$defs/FileRevision" },
|
|
7827
|
+
"mutationId": { "$ref": "#/$defs/FileMutationId" }
|
|
7828
|
+
}
|
|
7829
|
+
},
|
|
7830
|
+
"FileWriteResult": {
|
|
7831
|
+
"description": "执行端已确认持久化的保存回执;file.id 保持原文件引用身份,revision 是新内容版本。",
|
|
7832
|
+
"title": "FileWriteResult",
|
|
7833
|
+
"type": "object",
|
|
7834
|
+
"additionalProperties": false,
|
|
7835
|
+
"required": [
|
|
7836
|
+
"file",
|
|
7837
|
+
"revision",
|
|
7838
|
+
"bytesWritten",
|
|
7839
|
+
"mutationId"
|
|
7840
|
+
],
|
|
7841
|
+
"properties": {
|
|
7842
|
+
"file": { "$ref": "#/$defs/FileReference" },
|
|
7843
|
+
"revision": { "$ref": "#/$defs/FileRevision" },
|
|
7844
|
+
"bytesWritten": {
|
|
7845
|
+
"type": "integer",
|
|
7846
|
+
"minimum": 0,
|
|
7847
|
+
"maximum": 9007199254740991
|
|
7848
|
+
},
|
|
7849
|
+
"mutationId": { "$ref": "#/$defs/FileMutationId" }
|
|
7707
7850
|
}
|
|
7708
7851
|
},
|
|
7709
7852
|
"FileDownloadResult": {
|
|
@@ -8119,6 +8262,40 @@ const widgetRuntimeContractsSchema = {
|
|
|
8119
8262
|
}
|
|
8120
8263
|
}
|
|
8121
8264
|
},
|
|
8265
|
+
{
|
|
8266
|
+
"type": "object",
|
|
8267
|
+
"additionalProperties": false,
|
|
8268
|
+
"required": ["name", "limits"],
|
|
8269
|
+
"properties": {
|
|
8270
|
+
"name": { "const": "files.write" },
|
|
8271
|
+
"limits": {
|
|
8272
|
+
"type": "object",
|
|
8273
|
+
"additionalProperties": false,
|
|
8274
|
+
"required": [
|
|
8275
|
+
"bytesPerRequest",
|
|
8276
|
+
"bytesPerRun",
|
|
8277
|
+
"callsPerMinute"
|
|
8278
|
+
],
|
|
8279
|
+
"properties": {
|
|
8280
|
+
"bytesPerRequest": {
|
|
8281
|
+
"type": "integer",
|
|
8282
|
+
"minimum": 1,
|
|
8283
|
+
"maximum": 9007199254740991
|
|
8284
|
+
},
|
|
8285
|
+
"bytesPerRun": {
|
|
8286
|
+
"type": "integer",
|
|
8287
|
+
"minimum": 1,
|
|
8288
|
+
"maximum": 9007199254740991
|
|
8289
|
+
},
|
|
8290
|
+
"callsPerMinute": {
|
|
8291
|
+
"type": "integer",
|
|
8292
|
+
"minimum": 1,
|
|
8293
|
+
"maximum": 9007199254740991
|
|
8294
|
+
}
|
|
8295
|
+
}
|
|
8296
|
+
}
|
|
8297
|
+
}
|
|
8298
|
+
},
|
|
8122
8299
|
{
|
|
8123
8300
|
"type": "object",
|
|
8124
8301
|
"additionalProperties": false,
|
|
@@ -8620,28 +8797,16 @@ const widgetRuntimeContractsSchema = {
|
|
|
8620
8797
|
"required": ["operation", "input"],
|
|
8621
8798
|
"properties": {
|
|
8622
8799
|
"operation": { "const": "files.read" },
|
|
8623
|
-
"input": {
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
"file": { "$ref": "#/$defs/FileReference" },
|
|
8634
|
-
"offset": {
|
|
8635
|
-
"type": "integer",
|
|
8636
|
-
"minimum": 0
|
|
8637
|
-
},
|
|
8638
|
-
"length": {
|
|
8639
|
-
"type": "integer",
|
|
8640
|
-
"minimum": 1
|
|
8641
|
-
},
|
|
8642
|
-
"encoding": { "enum": ["utf8", "base64"] }
|
|
8643
|
-
}
|
|
8644
|
-
}
|
|
8800
|
+
"input": { "$ref": "#/$defs/FileReadInput" }
|
|
8801
|
+
}
|
|
8802
|
+
},
|
|
8803
|
+
{
|
|
8804
|
+
"type": "object",
|
|
8805
|
+
"additionalProperties": false,
|
|
8806
|
+
"required": ["operation", "input"],
|
|
8807
|
+
"properties": {
|
|
8808
|
+
"operation": { "const": "files.write" },
|
|
8809
|
+
"input": { "$ref": "#/$defs/FileWriteInput" }
|
|
8645
8810
|
}
|
|
8646
8811
|
},
|
|
8647
8812
|
{
|
|
@@ -9201,7 +9366,10 @@ const widgetRuntimeContractsSchema = {
|
|
|
9201
9366
|
"network-credential-unavailable",
|
|
9202
9367
|
"network-scope-invalid",
|
|
9203
9368
|
"network-dns-denied",
|
|
9204
|
-
"network-error"
|
|
9369
|
+
"network-error",
|
|
9370
|
+
"revision-conflict",
|
|
9371
|
+
"mutation-conflict",
|
|
9372
|
+
"write-outcome-unknown"
|
|
9205
9373
|
] },
|
|
9206
9374
|
"message": {
|
|
9207
9375
|
"type": "string",
|
|
@@ -9360,6 +9528,18 @@ const widgetRuntimeContractsSchema = {
|
|
|
9360
9528
|
"listPageSize": {
|
|
9361
9529
|
"type": "integer",
|
|
9362
9530
|
"minimum": 1
|
|
9531
|
+
},
|
|
9532
|
+
"writeBytesPerRequest": {
|
|
9533
|
+
"description": "写入单次原始字节上限;未签发时不得授权 files.write。",
|
|
9534
|
+
"type": "integer",
|
|
9535
|
+
"minimum": 1,
|
|
9536
|
+
"maximum": 9007199254740991
|
|
9537
|
+
},
|
|
9538
|
+
"writeBytesPerRun": {
|
|
9539
|
+
"description": "当前运行的累计写入原始字节上限;未签发时不得授权 files.write。",
|
|
9540
|
+
"type": "integer",
|
|
9541
|
+
"minimum": 1,
|
|
9542
|
+
"maximum": 9007199254740991
|
|
9363
9543
|
}
|
|
9364
9544
|
}
|
|
9365
9545
|
},
|
|
@@ -10735,14 +10915,51 @@ async function checkWidgetProject(projectRoot, options = {}) {
|
|
|
10735
10915
|
projectRoot: root
|
|
10736
10916
|
};
|
|
10737
10917
|
}
|
|
10738
|
-
const
|
|
10918
|
+
const toolchain = options.toolchain ?? {};
|
|
10919
|
+
const { validateWidgetManifest, validateRenderRequest, validateCapabilityResult } = options.validators ?? toolchain.validators ?? await loadContractValidators();
|
|
10739
10920
|
const sourceManifest = manifest;
|
|
10740
|
-
|
|
10921
|
+
const sdkVersion = options.sdkVersion ?? toolchain.sdkVersion;
|
|
10922
|
+
try {
|
|
10923
|
+
manifest = compileAuthorManifest(manifest, {
|
|
10924
|
+
hostEntry: options.hostEntry,
|
|
10925
|
+
samples: options.samples,
|
|
10926
|
+
sdkVersion,
|
|
10927
|
+
compiler: options.compiler ?? toolchain.compiler
|
|
10928
|
+
});
|
|
10929
|
+
} catch (error) {
|
|
10930
|
+
checks.push({
|
|
10931
|
+
name: "manifest 编译",
|
|
10932
|
+
ok: false
|
|
10933
|
+
});
|
|
10934
|
+
errors.push(`manifest compilation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
10935
|
+
return {
|
|
10936
|
+
ok: false,
|
|
10937
|
+
label,
|
|
10938
|
+
checks,
|
|
10939
|
+
errors,
|
|
10940
|
+
manifest: sourceManifest,
|
|
10941
|
+
sourceManifest,
|
|
10942
|
+
manifestKind: isAuthorManifest(sourceManifest) ? "author" : "host",
|
|
10943
|
+
sampleRequest: null,
|
|
10944
|
+
sampleRequests: [],
|
|
10945
|
+
fixtures: null,
|
|
10946
|
+
fixturesPath: null,
|
|
10947
|
+
entryPath: null,
|
|
10948
|
+
projectRoot: root
|
|
10949
|
+
};
|
|
10950
|
+
}
|
|
10741
10951
|
const manifestResult = validateWidgetManifest(manifest);
|
|
10742
10952
|
checks.push({
|
|
10743
10953
|
name: "manifest 契约校验 (AJV)",
|
|
10744
10954
|
ok: manifestResult.ok
|
|
10745
10955
|
});
|
|
10956
|
+
if (manifestResult.ok && sdkVersion !== void 0 && manifest.widgetConfig.widgetSdkVersion !== sdkVersion) {
|
|
10957
|
+
checks.push({
|
|
10958
|
+
name: "manifest 工具链 SDK 版本",
|
|
10959
|
+
ok: false
|
|
10960
|
+
});
|
|
10961
|
+
errors.push(`manifest SDK version ${manifest.widgetConfig.widgetSdkVersion} does not match toolchain SDK version ${sdkVersion}`);
|
|
10962
|
+
}
|
|
10746
10963
|
if (!manifestResult.ok) {
|
|
10747
10964
|
errors.push(...formatContractErrors(manifestResult));
|
|
10748
10965
|
return {
|
|
@@ -10883,8 +11100,29 @@ function resolveWidgetProjects(input, root = repoRoot) {
|
|
|
10883
11100
|
throw new Error("请传入 Widget 工程目录、manifest.json 或 all");
|
|
10884
11101
|
}
|
|
10885
11102
|
//#endregion
|
|
10886
|
-
//#region ../widget-project/src/
|
|
11103
|
+
//#region ../widget-project/src/zip.mjs
|
|
11104
|
+
/** 固定归档顺序与时间戳,统一路径安全与重复条目处理。 */
|
|
11105
|
+
function assertArchivePath(path) {
|
|
11106
|
+
if (typeof path !== "string" || path.length === 0 || path.startsWith("/") || path.includes("..") || path.includes("\\") || path.includes("://") || /^[A-Za-z]:/u.test(path) || [...path].some((character) => {
|
|
11107
|
+
const code = character.charCodeAt(0);
|
|
11108
|
+
return code < 32 || code === 127;
|
|
11109
|
+
})) throw new Error(`Invalid archive path: ${path}`);
|
|
11110
|
+
}
|
|
11111
|
+
function normalizeArchivePath(path) {
|
|
11112
|
+
assertArchivePath(path);
|
|
11113
|
+
const normalized = path.split("/").filter((part) => part !== "" && part !== ".").join("/");
|
|
11114
|
+
assertArchivePath(normalized);
|
|
11115
|
+
return normalized;
|
|
11116
|
+
}
|
|
10887
11117
|
function storedZip(files) {
|
|
11118
|
+
const uniqueFiles = /* @__PURE__ */ new Map();
|
|
11119
|
+
for (const [declaredPath, value] of files) {
|
|
11120
|
+
const path = normalizeArchivePath(declaredPath);
|
|
11121
|
+
const bytes = Buffer.from(value);
|
|
11122
|
+
const previous = uniqueFiles.get(path);
|
|
11123
|
+
if (previous && !previous.equals(bytes)) throw new Error(`Conflicting duplicate archive path: ${path}`);
|
|
11124
|
+
uniqueFiles.set(path, bytes);
|
|
11125
|
+
}
|
|
10888
11126
|
const table = Array.from({ length: 256 }, (_, index) => {
|
|
10889
11127
|
let value = index;
|
|
10890
11128
|
for (let bit = 0; bit < 8; bit++) value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
@@ -10893,10 +11131,8 @@ function storedZip(files) {
|
|
|
10893
11131
|
const locals = [];
|
|
10894
11132
|
const central = [];
|
|
10895
11133
|
let offset = 0;
|
|
10896
|
-
for (const [path,
|
|
10897
|
-
if (!/^[a-zA-Z0-9._/-]+$/.test(path) || path.startsWith("/") || path.split("/").includes("..")) throw new Error("Invalid archive path");
|
|
11134
|
+
for (const [path, bytes] of [...uniqueFiles].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
10898
11135
|
const name = Buffer.from(path);
|
|
10899
|
-
const bytes = Buffer.from(value);
|
|
10900
11136
|
let crc = 4294967295;
|
|
10901
11137
|
for (const byte of bytes) crc = table[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
10902
11138
|
crc = (crc ^ 4294967295) >>> 0;
|
|
@@ -10927,8 +11163,8 @@ function storedZip(files) {
|
|
|
10927
11163
|
const centralBytes = Buffer.concat(central);
|
|
10928
11164
|
const end = Buffer.alloc(22);
|
|
10929
11165
|
end.writeUInt32LE(101010256);
|
|
10930
|
-
end.writeUInt16LE(
|
|
10931
|
-
end.writeUInt16LE(
|
|
11166
|
+
end.writeUInt16LE(uniqueFiles.size, 8);
|
|
11167
|
+
end.writeUInt16LE(uniqueFiles.size, 10);
|
|
10932
11168
|
end.writeUInt32LE(centralBytes.length, 12);
|
|
10933
11169
|
end.writeUInt32LE(offset, 16);
|
|
10934
11170
|
return Buffer.concat([
|
|
@@ -10937,24 +11173,81 @@ function storedZip(files) {
|
|
|
10937
11173
|
end
|
|
10938
11174
|
]);
|
|
10939
11175
|
}
|
|
10940
|
-
|
|
10941
|
-
|
|
11176
|
+
//#endregion
|
|
11177
|
+
//#region ../widget-project/src/export.mjs
|
|
11178
|
+
const EXCLUDED_BUILD_NAMES = /* @__PURE__ */ new Set([
|
|
11179
|
+
"node_modules",
|
|
11180
|
+
".git",
|
|
11181
|
+
".widget-build",
|
|
11182
|
+
".widget-dev",
|
|
11183
|
+
"widget-toolchain.json",
|
|
11184
|
+
".widget-build-receipt.json"
|
|
11185
|
+
]);
|
|
11186
|
+
function bundleFileMetadata(root, path) {
|
|
11187
|
+
const normalized = normalizeArchivePath(path);
|
|
11188
|
+
let file = root;
|
|
11189
|
+
let metadata;
|
|
11190
|
+
for (const part of normalized.split("/")) {
|
|
11191
|
+
file = join(file, part);
|
|
11192
|
+
metadata = lstatSync(file);
|
|
11193
|
+
if (metadata.isSymbolicLink()) throw new Error(`ZIP 不允许 symbolic link(符号链接):${path}`);
|
|
11194
|
+
}
|
|
11195
|
+
return {
|
|
11196
|
+
file,
|
|
11197
|
+
metadata
|
|
11198
|
+
};
|
|
11199
|
+
}
|
|
11200
|
+
function readBundleFile(root, path) {
|
|
11201
|
+
const { file, metadata } = bundleFileMetadata(root, path);
|
|
11202
|
+
if (!metadata.isFile()) throw new Error(`ZIP 资源不是普通文件:${path}`);
|
|
11203
|
+
return readFileSync(file);
|
|
11204
|
+
}
|
|
11205
|
+
function collectBuildAssets(root, entry) {
|
|
11206
|
+
entry = normalizeArchivePath(entry);
|
|
11207
|
+
if (!entry.includes("/")) return [[entry, readBundleFile(root, entry)]];
|
|
11208
|
+
const files = [];
|
|
11209
|
+
const rootName = entry.split("/")[0];
|
|
11210
|
+
if (EXCLUDED_BUILD_NAMES.has(rootName)) throw new Error(`构建资源目录不能是工程内部目录:${rootName}`);
|
|
11211
|
+
const visit = (path) => {
|
|
11212
|
+
const { file, metadata } = bundleFileMetadata(root, path);
|
|
11213
|
+
if (metadata.isDirectory()) for (const name of readdirSync(file)) {
|
|
11214
|
+
if (EXCLUDED_BUILD_NAMES.has(name) || /\.zip$/iu.test(name)) continue;
|
|
11215
|
+
visit(`${path}/${name}`);
|
|
11216
|
+
}
|
|
11217
|
+
else files.push([path, readBundleFile(root, path)]);
|
|
11218
|
+
};
|
|
11219
|
+
visit(rootName);
|
|
11220
|
+
return files;
|
|
10942
11221
|
}
|
|
10943
11222
|
function buildWidgetPackage(project, options = {}) {
|
|
10944
|
-
|
|
11223
|
+
return storedZip(collectWidgetPackageFiles(project, options));
|
|
11224
|
+
}
|
|
11225
|
+
/** 返回将写入 ZIP 的 `[路径, 字节]` 列表;同一路径可能重复出现,由 `storedZip` 去重。 */
|
|
11226
|
+
function collectWidgetPackageFiles(project, options = {}) {
|
|
11227
|
+
const root = resolve(project.projectRoot);
|
|
11228
|
+
const sourceManifest = project.manifest ?? compileAuthorManifest(JSON.parse(readBundleFile(root, "manifest.json").toString("utf8")), { hostEntry: options.hostEntry });
|
|
11229
|
+
const manifest = {
|
|
11230
|
+
...sourceManifest,
|
|
11231
|
+
widgetConfig: {
|
|
11232
|
+
...sourceManifest.widgetConfig,
|
|
11233
|
+
entry: normalizeArchivePath(sourceManifest.widgetConfig.entry),
|
|
11234
|
+
samples: sourceManifest.widgetConfig.samples?.map((sample) => ({
|
|
11235
|
+
...sample,
|
|
11236
|
+
request: normalizeArchivePath(sample.request)
|
|
11237
|
+
}))
|
|
11238
|
+
}
|
|
11239
|
+
};
|
|
10945
11240
|
const files = [["manifest.json", Buffer.from(`${JSON.stringify(manifest)}\n`)]];
|
|
10946
|
-
const entryPath = join(
|
|
11241
|
+
const entryPath = join(root, manifest.widgetConfig.entry);
|
|
10947
11242
|
if (!existsSync(entryPath)) throw new Error("Widget entry is missing; build the project first");
|
|
10948
|
-
files.push(
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
if (existsSync(samplePath)) files.push([safeBundlePath(sample.request), readFileSync(samplePath)]);
|
|
10952
|
-
}
|
|
11243
|
+
files.push(...collectBuildAssets(root, manifest.widgetConfig.entry));
|
|
11244
|
+
files.push([manifest.widgetConfig.entry, readBundleFile(root, manifest.widgetConfig.entry)]);
|
|
11245
|
+
for (const sample of manifest.widgetConfig.samples ?? []) files.push([sample.request, readBundleFile(root, sample.request)]);
|
|
10953
11246
|
if (options.includePreviewFixtures) {
|
|
10954
|
-
const fixturesPath = join(
|
|
10955
|
-
if (existsSync(fixturesPath)) files.push(["preview/fixtures.json",
|
|
11247
|
+
const fixturesPath = join(root, "preview", "fixtures.json");
|
|
11248
|
+
if (existsSync(fixturesPath)) files.push(["preview/fixtures.json", readBundleFile(root, "preview/fixtures.json")]);
|
|
10956
11249
|
}
|
|
10957
|
-
return
|
|
11250
|
+
return files;
|
|
10958
11251
|
}
|
|
10959
11252
|
//#endregion
|
|
10960
11253
|
//#region src/migration/recipes.mjs
|
|
@@ -11392,4 +11685,4 @@ async function scaffoldDshWidget(input, output, options) {
|
|
|
11392
11685
|
};
|
|
11393
11686
|
}
|
|
11394
11687
|
//#endregion
|
|
11395
|
-
export {
|
|
11688
|
+
export { normalizeArchivePath as a, resolveWidgetProjects as c, collectWidgetPackageFiles as i, compileAuthorManifest as l, scaffoldDshWidget as n, storedZip as o, buildWidgetPackage as r, checkWidgetProject as s, auditDshPlugin as t };
|