@world-engines/spatial-authoring 0.1.0-alpha.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/LICENSE +46 -0
- package/dist/SpatialWorldCanvas.d.ts +18 -0
- package/dist/SpatialWorldCanvas.js +24 -0
- package/dist/SpatialWorldEditor.d.ts +38 -0
- package/dist/SpatialWorldEditor.js +61 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/plane-generation.d.ts +48 -0
- package/dist/plane-generation.js +126 -0
- package/dist/plane-generator/package.json +15 -0
- package/dist/plane-generator/plane-generator.d.ts +57 -0
- package/dist/plane-generator/plane-generator.js +310 -0
- package/dist/plane-generator/plane-generator_bg.wasm +0 -0
- package/dist/plane-generator/plane-generator_bg.wasm.d.ts +13 -0
- package/dist/procedural-city.d.ts +135 -0
- package/dist/procedural-city.js +595 -0
- package/dist/protobuf-source-codec.d.ts +57 -0
- package/dist/protobuf-source-codec.js +317 -0
- package/dist/road-generation.d.ts +52 -0
- package/dist/road-generation.js +129 -0
- package/dist/road-parcels.d.ts +83 -0
- package/dist/road-parcels.js +1584 -0
- package/dist/schema.d.ts +47 -0
- package/dist/schema.js +82 -0
- package/dist/source-adapter.d.ts +15 -0
- package/dist/source-adapter.js +23 -0
- package/dist/spatial-runtime.d.ts +210 -0
- package/dist/spatial-runtime.js +1231 -0
- package/dist/spatial-templates.d.ts +117 -0
- package/dist/spatial-templates.js +200 -0
- package/dist/worker.d.ts +13 -0
- package/dist/worker.js +9 -0
- package/package.json +155 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/** 可持久化的空间模板选择;参数是预设的冻结快照,而不是可随意调节的 UI 草稿。 */
|
|
2
|
+
export declare const SPATIAL_TEMPLATE_VERSION: 1;
|
|
3
|
+
export type SpatialTemplateKind = "city" | "parcel" | "building" | "interior";
|
|
4
|
+
export interface SpatialCityTemplateParameters {
|
|
5
|
+
readonly radiusMeters: number;
|
|
6
|
+
readonly blockSizeMeters: number;
|
|
7
|
+
readonly roadWidthMeters: number;
|
|
8
|
+
readonly landUseWeights: Readonly<{
|
|
9
|
+
residential: number;
|
|
10
|
+
commercial: number;
|
|
11
|
+
industrial: number;
|
|
12
|
+
civic: number;
|
|
13
|
+
park: number;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
export interface SpatialParcelTemplateParameters {
|
|
17
|
+
readonly subdivision: 1 | 2 | 3;
|
|
18
|
+
readonly roadParcelMaxFaceAreaMeters: number;
|
|
19
|
+
}
|
|
20
|
+
export interface SpatialBuildingTemplateParameters {
|
|
21
|
+
readonly maxBuildings: 1 | 2 | 3 | 4;
|
|
22
|
+
readonly fillScale: number;
|
|
23
|
+
}
|
|
24
|
+
export interface SpatialInteriorTemplateParameters {
|
|
25
|
+
readonly roomDensity: 1 | 2 | 3;
|
|
26
|
+
readonly corridorRatio: number;
|
|
27
|
+
}
|
|
28
|
+
export interface SpatialTemplatePresetV1<K extends SpatialTemplateKind = SpatialTemplateKind> {
|
|
29
|
+
readonly kind: K;
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly revision: number;
|
|
32
|
+
readonly label: string;
|
|
33
|
+
readonly parameters: K extends "city" ? SpatialCityTemplateParameters : K extends "parcel" ? SpatialParcelTemplateParameters : K extends "building" ? SpatialBuildingTemplateParameters : SpatialInteriorTemplateParameters;
|
|
34
|
+
}
|
|
35
|
+
export interface SpatialTemplateSelectionV1<K extends SpatialTemplateKind = SpatialTemplateKind> {
|
|
36
|
+
readonly presetId: string;
|
|
37
|
+
readonly revision: number;
|
|
38
|
+
readonly parameters: K extends "city" ? SpatialCityTemplateParameters : K extends "parcel" ? SpatialParcelTemplateParameters : K extends "building" ? SpatialBuildingTemplateParameters : SpatialInteriorTemplateParameters;
|
|
39
|
+
}
|
|
40
|
+
export interface SpatialTemplateConfigurationV1 {
|
|
41
|
+
readonly version: typeof SPATIAL_TEMPLATE_VERSION;
|
|
42
|
+
readonly city?: SpatialTemplateSelectionV1<"city">;
|
|
43
|
+
readonly parcel?: SpatialTemplateSelectionV1<"parcel">;
|
|
44
|
+
readonly building?: SpatialTemplateSelectionV1<"building">;
|
|
45
|
+
readonly interior?: SpatialTemplateSelectionV1<"interior">;
|
|
46
|
+
}
|
|
47
|
+
/** Host 的可编辑模板库;selection 保留 parameter snapshot,故删除库项不会改变已应用世界。 */
|
|
48
|
+
export interface SpatialTemplateLibraryV1 {
|
|
49
|
+
readonly version: typeof SPATIAL_TEMPLATE_VERSION;
|
|
50
|
+
readonly presets: readonly SpatialTemplatePresetV1[];
|
|
51
|
+
}
|
|
52
|
+
/** terrain-road 的唯一模板消费结果;subdivision 显式缩小 face 上限而非只影响 identity。 */
|
|
53
|
+
export interface ResolvedRoadParcelTemplateSettings {
|
|
54
|
+
readonly maxFaceAreaMeters: number;
|
|
55
|
+
}
|
|
56
|
+
/** 每类一个可直接套用的生产预设;可由 Host 另行扩充 library。 */
|
|
57
|
+
export declare const SPATIAL_TEMPLATE_PRESETS: {
|
|
58
|
+
city: {
|
|
59
|
+
kind: "city";
|
|
60
|
+
id: string;
|
|
61
|
+
revision: number;
|
|
62
|
+
label: string;
|
|
63
|
+
parameters: {
|
|
64
|
+
radiusMeters: number;
|
|
65
|
+
blockSizeMeters: number;
|
|
66
|
+
roadWidthMeters: number;
|
|
67
|
+
landUseWeights: {
|
|
68
|
+
residential: number;
|
|
69
|
+
commercial: number;
|
|
70
|
+
industrial: number;
|
|
71
|
+
civic: number;
|
|
72
|
+
park: number;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
parcel: {
|
|
77
|
+
kind: "parcel";
|
|
78
|
+
id: string;
|
|
79
|
+
revision: number;
|
|
80
|
+
label: string;
|
|
81
|
+
parameters: {
|
|
82
|
+
subdivision: 2;
|
|
83
|
+
roadParcelMaxFaceAreaMeters: number;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
building: {
|
|
87
|
+
kind: "building";
|
|
88
|
+
id: string;
|
|
89
|
+
revision: number;
|
|
90
|
+
label: string;
|
|
91
|
+
parameters: {
|
|
92
|
+
maxBuildings: 3;
|
|
93
|
+
fillScale: number;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
interior: {
|
|
97
|
+
kind: "interior";
|
|
98
|
+
id: string;
|
|
99
|
+
revision: number;
|
|
100
|
+
label: string;
|
|
101
|
+
parameters: {
|
|
102
|
+
roomDensity: 2;
|
|
103
|
+
corridorRatio: number;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
type PresetFor<K extends SpatialTemplateKind> = SpatialTemplatePresetV1<K>;
|
|
108
|
+
/** 查找内置模板。用户模板由 Host library 持久化后可用同一 selection shape 套用。 */
|
|
109
|
+
export declare function getSpatialTemplatePreset<K extends SpatialTemplateKind>(kind: K, presetId: string): PresetFor<K>;
|
|
110
|
+
export declare function validateSpatialTemplateLibrary(input: unknown): SpatialTemplateLibraryV1;
|
|
111
|
+
/** 严格 decode 旧世界可省略的 templates 字段;一旦出现则不可降级或容忍旧版本。 */
|
|
112
|
+
export declare function validateSpatialTemplateConfiguration(input: unknown): SpatialTemplateConfigurationV1;
|
|
113
|
+
/** 未选择 parcel 时返回 undefined 以保留 road-parcels 的既有默认;选择后两项参数都实际生效。 */
|
|
114
|
+
export declare function resolveRoadParcelTemplateSettings(templates: SpatialTemplateConfigurationV1 | undefined): ResolvedRoadParcelTemplateSettings | undefined;
|
|
115
|
+
/** 用已发布 preset 生成可保存的完整选择快照,调用端无需复制默认参数。 */
|
|
116
|
+
export declare function createSpatialTemplateConfiguration<K extends SpatialTemplateKind>(kind: K, preset?: SpatialTemplatePresetV1<K>): SpatialTemplateConfigurationV1;
|
|
117
|
+
export {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/** 可持久化的空间模板选择;参数是预设的冻结快照,而不是可随意调节的 UI 草稿。 */
|
|
2
|
+
export const SPATIAL_TEMPLATE_VERSION = 1;
|
|
3
|
+
const freeze = (value) => Object.freeze(value);
|
|
4
|
+
/** 每类一个可直接套用的生产预设;可由 Host 另行扩充 library。 */
|
|
5
|
+
export const SPATIAL_TEMPLATE_PRESETS = freeze({
|
|
6
|
+
city: freeze({
|
|
7
|
+
kind: "city",
|
|
8
|
+
id: "city.compact-town",
|
|
9
|
+
revision: 1,
|
|
10
|
+
label: "紧凑城镇",
|
|
11
|
+
parameters: freeze({
|
|
12
|
+
radiusMeters: 480,
|
|
13
|
+
blockSizeMeters: 80,
|
|
14
|
+
roadWidthMeters: 12,
|
|
15
|
+
landUseWeights: freeze({ residential: 56, commercial: 21, industrial: 9, civic: 8, park: 6 }),
|
|
16
|
+
}),
|
|
17
|
+
}),
|
|
18
|
+
parcel: freeze({
|
|
19
|
+
kind: "parcel",
|
|
20
|
+
id: "parcel.fine-grain",
|
|
21
|
+
revision: 1,
|
|
22
|
+
label: "细分街区",
|
|
23
|
+
parameters: freeze({ subdivision: 2, roadParcelMaxFaceAreaMeters: 2_400 }),
|
|
24
|
+
}),
|
|
25
|
+
building: freeze({
|
|
26
|
+
kind: "building",
|
|
27
|
+
id: "building.courtyard-cluster",
|
|
28
|
+
revision: 1,
|
|
29
|
+
label: "庭院组团",
|
|
30
|
+
parameters: freeze({ maxBuildings: 3, fillScale: 0.72 }),
|
|
31
|
+
}),
|
|
32
|
+
interior: freeze({
|
|
33
|
+
kind: "interior",
|
|
34
|
+
id: "interior.compact-corridor",
|
|
35
|
+
revision: 1,
|
|
36
|
+
label: "紧凑走廊",
|
|
37
|
+
parameters: freeze({ roomDensity: 2, corridorRatio: 0.22 }),
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
function isRecord(value) {
|
|
41
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
function exact(value, keys, label) {
|
|
44
|
+
if (!isRecord(value))
|
|
45
|
+
throw new TypeError(`${label} 必须是对象`);
|
|
46
|
+
const actual = Object.keys(value);
|
|
47
|
+
const unknown = actual.filter((key) => !keys.includes(key));
|
|
48
|
+
const missing = keys.filter((key) => !Object.hasOwn(value, key));
|
|
49
|
+
if (unknown.length > 0)
|
|
50
|
+
throw new TypeError(`${label} 包含未知字段:${unknown.join(", ")}`);
|
|
51
|
+
if (missing.length > 0)
|
|
52
|
+
throw new TypeError(`${label} 缺少字段:${missing.join(", ")}`);
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
function presetFor(kind) {
|
|
56
|
+
return SPATIAL_TEMPLATE_PRESETS[kind];
|
|
57
|
+
}
|
|
58
|
+
/** 查找内置模板。用户模板由 Host library 持久化后可用同一 selection shape 套用。 */
|
|
59
|
+
export function getSpatialTemplatePreset(kind, presetId) {
|
|
60
|
+
const preset = presetFor(kind);
|
|
61
|
+
if (preset.id !== presetId)
|
|
62
|
+
throw new TypeError(`${kind} 模板 presetId 不存在:${presetId}`);
|
|
63
|
+
return preset;
|
|
64
|
+
}
|
|
65
|
+
function identifier(value, label) {
|
|
66
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value)) {
|
|
67
|
+
throw new TypeError(`${label} 必须是稳定 identifier`);
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function revision(value, label) {
|
|
72
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 1_000_000) {
|
|
73
|
+
throw new RangeError(`${label} 必须是 1..1000000 的整数`);
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
function numberIn(value, label, min, max) {
|
|
78
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
|
|
79
|
+
throw new RangeError(`${label} 必须位于 ${min}..${max}`);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
function cityParameters(value) {
|
|
84
|
+
const source = exact(value, ["radiusMeters", "blockSizeMeters", "roadWidthMeters", "landUseWeights"], "city.parameters");
|
|
85
|
+
const weights = exact(source.landUseWeights, ["residential", "commercial", "industrial", "civic", "park"], "city.parameters.landUseWeights");
|
|
86
|
+
const result = freeze({
|
|
87
|
+
radiusMeters: numberIn(source.radiusMeters, "city.parameters.radiusMeters", 32, 200_000),
|
|
88
|
+
blockSizeMeters: numberIn(source.blockSizeMeters, "city.parameters.blockSizeMeters", 16, 2_000),
|
|
89
|
+
roadWidthMeters: numberIn(source.roadWidthMeters, "city.parameters.roadWidthMeters", 3, 40),
|
|
90
|
+
landUseWeights: freeze({
|
|
91
|
+
residential: numberIn(weights.residential, "city.parameters.landUseWeights.residential", 0, 1_000_000),
|
|
92
|
+
commercial: numberIn(weights.commercial, "city.parameters.landUseWeights.commercial", 0, 1_000_000),
|
|
93
|
+
industrial: numberIn(weights.industrial, "city.parameters.landUseWeights.industrial", 0, 1_000_000),
|
|
94
|
+
civic: numberIn(weights.civic, "city.parameters.landUseWeights.civic", 0, 1_000_000),
|
|
95
|
+
park: numberIn(weights.park, "city.parameters.landUseWeights.park", 0, 1_000_000),
|
|
96
|
+
}),
|
|
97
|
+
});
|
|
98
|
+
if (result.radiusMeters < result.blockSizeMeters * 2 || result.roadWidthMeters > result.blockSizeMeters * 0.45
|
|
99
|
+
|| result.blockSizeMeters - result.roadWidthMeters < 8)
|
|
100
|
+
throw new RangeError("city.parameters 无法形成可用地块");
|
|
101
|
+
if (Object.values(result.landUseWeights).every((weight) => weight === 0))
|
|
102
|
+
throw new RangeError("city.parameters 至少需要一个正用途权重");
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
function parcelParameters(value) {
|
|
106
|
+
const source = exact(value, ["subdivision", "roadParcelMaxFaceAreaMeters"], "parcel.parameters");
|
|
107
|
+
if (source.subdivision !== 1 && source.subdivision !== 2 && source.subdivision !== 3)
|
|
108
|
+
throw new RangeError("parcel.parameters.subdivision 必须为 1、2 或 3");
|
|
109
|
+
return freeze({ subdivision: source.subdivision, roadParcelMaxFaceAreaMeters: numberIn(source.roadParcelMaxFaceAreaMeters, "parcel.parameters.roadParcelMaxFaceAreaMeters", 64, 2_000_000) });
|
|
110
|
+
}
|
|
111
|
+
function buildingParameters(value) {
|
|
112
|
+
const source = exact(value, ["maxBuildings", "fillScale"], "building.parameters");
|
|
113
|
+
if (source.maxBuildings !== 1 && source.maxBuildings !== 2 && source.maxBuildings !== 3 && source.maxBuildings !== 4)
|
|
114
|
+
throw new RangeError("building.parameters.maxBuildings 必须为 1..4 的整数");
|
|
115
|
+
return freeze({ maxBuildings: source.maxBuildings, fillScale: numberIn(source.fillScale, "building.parameters.fillScale", 0.25, 1) });
|
|
116
|
+
}
|
|
117
|
+
function interiorParameters(value) {
|
|
118
|
+
const source = exact(value, ["roomDensity", "corridorRatio"], "interior.parameters");
|
|
119
|
+
if (source.roomDensity !== 1 && source.roomDensity !== 2 && source.roomDensity !== 3)
|
|
120
|
+
throw new RangeError("interior.parameters.roomDensity 必须为 1、2 或 3");
|
|
121
|
+
return freeze({ roomDensity: source.roomDensity, corridorRatio: numberIn(source.corridorRatio, "interior.parameters.corridorRatio", 0.1, 0.35) });
|
|
122
|
+
}
|
|
123
|
+
function validateSelection(kind, value) {
|
|
124
|
+
const selection = exact(value, ["presetId", "revision", "parameters"], `${kind} 模板选择`);
|
|
125
|
+
const parameters = kind === "city" ? cityParameters(selection.parameters)
|
|
126
|
+
: kind === "parcel" ? parcelParameters(selection.parameters)
|
|
127
|
+
: kind === "building" ? buildingParameters(selection.parameters)
|
|
128
|
+
: interiorParameters(selection.parameters);
|
|
129
|
+
return freeze({ presetId: identifier(selection.presetId, `${kind}.presetId`), revision: revision(selection.revision, `${kind}.revision`), parameters });
|
|
130
|
+
}
|
|
131
|
+
function validatePreset(value) {
|
|
132
|
+
const source = exact(value, ["kind", "id", "revision", "label", "parameters"], "SpatialTemplatePresetV1");
|
|
133
|
+
if (source.kind !== "city" && source.kind !== "parcel" && source.kind !== "building" && source.kind !== "interior")
|
|
134
|
+
throw new TypeError("template.kind 非法");
|
|
135
|
+
if (typeof source.label !== "string" || source.label.trim().length === 0 || source.label.trim().length > 128)
|
|
136
|
+
throw new TypeError("template.label 必须是 1..128 字符的文本");
|
|
137
|
+
const selection = validateSelection(source.kind, { presetId: source.id, revision: source.revision, parameters: source.parameters });
|
|
138
|
+
return freeze({ kind: source.kind, id: selection.presetId, revision: selection.revision, label: source.label.trim(), parameters: selection.parameters });
|
|
139
|
+
}
|
|
140
|
+
export function validateSpatialTemplateLibrary(input) {
|
|
141
|
+
const source = exact(input, ["version", "presets"], "SpatialTemplateLibraryV1");
|
|
142
|
+
if (source.version !== SPATIAL_TEMPLATE_VERSION || !Array.isArray(source.presets) || source.presets.length > 128)
|
|
143
|
+
throw new RangeError("templates library version 或 preset 数量非法");
|
|
144
|
+
const presets = source.presets.map(validatePreset);
|
|
145
|
+
const ids = new Set();
|
|
146
|
+
for (const preset of presets) {
|
|
147
|
+
const key = `${preset.kind}:${preset.id}:${preset.revision}`;
|
|
148
|
+
if (ids.has(key))
|
|
149
|
+
throw new TypeError(`模板 revision 重复:${key}`);
|
|
150
|
+
ids.add(key);
|
|
151
|
+
}
|
|
152
|
+
return freeze({ version: SPATIAL_TEMPLATE_VERSION, presets: freeze(presets) });
|
|
153
|
+
}
|
|
154
|
+
/** 严格 decode 旧世界可省略的 templates 字段;一旦出现则不可降级或容忍旧版本。 */
|
|
155
|
+
export function validateSpatialTemplateConfiguration(input) {
|
|
156
|
+
if (!isRecord(input))
|
|
157
|
+
throw new TypeError("SpatialTemplateConfigurationV1 必须是对象");
|
|
158
|
+
const allowed = ["version", "city", "parcel", "building", "interior"];
|
|
159
|
+
const unknown = Object.keys(input).filter((key) => !allowed.includes(key));
|
|
160
|
+
if (unknown.length > 0)
|
|
161
|
+
throw new TypeError(`SpatialTemplateConfigurationV1 包含未知字段:${unknown.join(", ")}`);
|
|
162
|
+
if (!Object.hasOwn(input, "version"))
|
|
163
|
+
throw new TypeError("SpatialTemplateConfigurationV1 缺少字段:version");
|
|
164
|
+
const value = input;
|
|
165
|
+
if (value.version !== SPATIAL_TEMPLATE_VERSION) {
|
|
166
|
+
throw new RangeError(`templates.version 必须为 ${SPATIAL_TEMPLATE_VERSION}`);
|
|
167
|
+
}
|
|
168
|
+
const selected = ["city", "parcel", "building", "interior"].filter((kind) => Object.hasOwn(value, kind));
|
|
169
|
+
if (selected.length === 0)
|
|
170
|
+
throw new TypeError("SpatialTemplateConfigurationV1 至少需要一个 active selection");
|
|
171
|
+
return freeze({
|
|
172
|
+
version: SPATIAL_TEMPLATE_VERSION,
|
|
173
|
+
...(Object.hasOwn(value, "city") ? { city: validateSelection("city", value.city) } : {}),
|
|
174
|
+
...(Object.hasOwn(value, "parcel") ? { parcel: validateSelection("parcel", value.parcel) } : {}),
|
|
175
|
+
...(Object.hasOwn(value, "building") ? { building: validateSelection("building", value.building) } : {}),
|
|
176
|
+
...(Object.hasOwn(value, "interior") ? { interior: validateSelection("interior", value.interior) } : {}),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/** 未选择 parcel 时返回 undefined 以保留 road-parcels 的既有默认;选择后两项参数都实际生效。 */
|
|
180
|
+
export function resolveRoadParcelTemplateSettings(templates) {
|
|
181
|
+
if (templates === undefined)
|
|
182
|
+
return undefined;
|
|
183
|
+
const parcel = validateSpatialTemplateConfiguration(templates).parcel;
|
|
184
|
+
if (parcel === undefined)
|
|
185
|
+
return undefined;
|
|
186
|
+
return freeze({
|
|
187
|
+
// road-parcels 的 minimumArea 为 36;钳制避免合法模板生成非法 settings。
|
|
188
|
+
maxFaceAreaMeters: Math.max(36, parcel.parameters.roadParcelMaxFaceAreaMeters / (parcel.parameters.subdivision ** 2)),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/** 用已发布 preset 生成可保存的完整选择快照,调用端无需复制默认参数。 */
|
|
192
|
+
export function createSpatialTemplateConfiguration(kind, preset = presetFor(kind)) {
|
|
193
|
+
const validated = validatePreset(preset);
|
|
194
|
+
if (validated.kind !== kind)
|
|
195
|
+
throw new TypeError(`template kind 不匹配:${validated.kind} != ${kind}`);
|
|
196
|
+
return validateSpatialTemplateConfiguration({
|
|
197
|
+
version: SPATIAL_TEMPLATE_VERSION,
|
|
198
|
+
[kind]: { presetId: validated.id, revision: validated.revision, parameters: validated.parameters },
|
|
199
|
+
});
|
|
200
|
+
}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SpatialWorldSourceV1 } from "./schema.js";
|
|
2
|
+
export interface SpatialValidationWorkResult {
|
|
3
|
+
readonly sourceRevision: number;
|
|
4
|
+
readonly objectCount: number;
|
|
5
|
+
}
|
|
6
|
+
export interface SpatialValidationWorkInput {
|
|
7
|
+
/** Worker 只消费宿主已读回的 source,不自行读取任何浏览器或桌面持久化。 */
|
|
8
|
+
readonly source: SpatialWorldSourceV1;
|
|
9
|
+
/** 由调用方随任务提交;必须与 source authority 的 revision 完全一致。 */
|
|
10
|
+
readonly sourceRevision: number;
|
|
11
|
+
}
|
|
12
|
+
/** 可被 Web Worker 或 WASM adapter 调用的纯输入输出边界,绝不读取浏览器持久化。 */
|
|
13
|
+
export declare function validateSpatialWorldWork(input: SpatialValidationWorkInput): SpatialValidationWorkResult;
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { validateSpatialWorldSourceV1 } from "./schema.js";
|
|
2
|
+
/** 可被 Web Worker 或 WASM adapter 调用的纯输入输出边界,绝不读取浏览器持久化。 */
|
|
3
|
+
export function validateSpatialWorldWork(input) {
|
|
4
|
+
const validSource = validateSpatialWorldSourceV1(input.source);
|
|
5
|
+
if (input.sourceRevision !== validSource.revision) {
|
|
6
|
+
throw new Error("worker input 的 source revision 与 source authority 不一致");
|
|
7
|
+
}
|
|
8
|
+
return { sourceRevision: validSource.revision, objectCount: validSource.objects.length };
|
|
9
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@world-engines/spatial-authoring",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./plane-generation": {
|
|
12
|
+
"types": "./dist/plane-generation.d.ts",
|
|
13
|
+
"import": "./dist/plane-generation.js"
|
|
14
|
+
},
|
|
15
|
+
"./plane-generator/*": "./dist/plane-generator/*",
|
|
16
|
+
"./plane-generator/plane-generator.js": {
|
|
17
|
+
"types": "./dist/plane-generator/plane-generator.d.ts",
|
|
18
|
+
"import": "./dist/plane-generator/plane-generator.js"
|
|
19
|
+
},
|
|
20
|
+
"./plane-generator/plane-generator_bg.wasm": "./dist/plane-generator/plane-generator_bg.wasm",
|
|
21
|
+
"./procedural-city": {
|
|
22
|
+
"types": "./dist/procedural-city.d.ts",
|
|
23
|
+
"import": "./dist/procedural-city.js"
|
|
24
|
+
},
|
|
25
|
+
"./road-generation": {
|
|
26
|
+
"types": "./dist/road-generation.d.ts",
|
|
27
|
+
"import": "./dist/road-generation.js"
|
|
28
|
+
},
|
|
29
|
+
"./road-parcels": {
|
|
30
|
+
"types": "./dist/road-parcels.d.ts",
|
|
31
|
+
"import": "./dist/road-parcels.js"
|
|
32
|
+
},
|
|
33
|
+
"./spatial-runtime": {
|
|
34
|
+
"types": "./dist/spatial-runtime.d.ts",
|
|
35
|
+
"import": "./dist/spatial-runtime.js"
|
|
36
|
+
},
|
|
37
|
+
"./spatial-templates": {
|
|
38
|
+
"types": "./dist/spatial-templates.d.ts",
|
|
39
|
+
"import": "./dist/spatial-templates.js"
|
|
40
|
+
},
|
|
41
|
+
"./protobuf-source-codec": {
|
|
42
|
+
"types": "./dist/protobuf-source-codec.d.ts",
|
|
43
|
+
"import": "./dist/protobuf-source-codec.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist"
|
|
48
|
+
],
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"react": "^18.3.1"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"worldengine_source_sha256": "cf90e8e3819afad7fe7bd1a836c8e7c0fd8338ccb258c76bebc8a6f4d6cb6287",
|
|
56
|
+
"worldengine_candidate_provenance": {
|
|
57
|
+
"schema": "worldengine-map-v10-r12-overlay/v1",
|
|
58
|
+
"base_archive_sha256": "e6fb6328ce1d20cf8849225ee501d59939ee9221e9fc12e81d94517adc336fce",
|
|
59
|
+
"overlay_receipt_sha256": "17fea2a57164d40a3aae09a705fd5648921b50293a99839228fcf44efc36e997",
|
|
60
|
+
"overlay_files": [
|
|
61
|
+
{
|
|
62
|
+
"path": "dist/plane-generator/plane-generator_bg.wasm",
|
|
63
|
+
"input_sha256": "66ac0d04c19e4944b359cc016021abf20270565d805c28cdde094e2c4119c22b",
|
|
64
|
+
"published_sha256": "66ac0d04c19e4944b359cc016021abf20270565d805c28cdde094e2c4119c22b",
|
|
65
|
+
"bytes": 587996,
|
|
66
|
+
"source_directive_stripped": false
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"path": "dist/plane-generator/plane-generator.d.ts",
|
|
70
|
+
"input_sha256": "d048baf23bc5720e986bdb064a9a95e1696fbb747ecbebe41ec2ba77b07d3ea4",
|
|
71
|
+
"published_sha256": "d048baf23bc5720e986bdb064a9a95e1696fbb747ecbebe41ec2ba77b07d3ea4",
|
|
72
|
+
"bytes": 2435,
|
|
73
|
+
"source_directive_stripped": false
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"path": "dist/plane-generator/plane-generator.js",
|
|
77
|
+
"input_sha256": "16425a19d458887c07d686712e4e23caa87afead9f6aac9f4bc6ffd32a5ab5c1",
|
|
78
|
+
"published_sha256": "16425a19d458887c07d686712e4e23caa87afead9f6aac9f4bc6ffd32a5ab5c1",
|
|
79
|
+
"bytes": 9716,
|
|
80
|
+
"source_directive_stripped": false
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"path": "dist/road-generation.d.ts",
|
|
84
|
+
"input_sha256": "85d77e1f98400cacb0f1076565834bc877a645f5ab1e8b665a031a35c2cfe6fc",
|
|
85
|
+
"published_sha256": "adb8b2cee970a8d891b442f3ee3d12550dded10cee6ba0f4feadc6641016133a",
|
|
86
|
+
"bytes": 30697,
|
|
87
|
+
"source_directive_stripped": true
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"path": "dist/road-generation.js",
|
|
91
|
+
"input_sha256": "5bb1274676bff8b7a19964fd902b902416887328edb0a0a03550a9fdc188fc6b",
|
|
92
|
+
"published_sha256": "19c070732fc08d8fefd25c58f47fbfcb70f6065a2a30c925c6ab306d96685455",
|
|
93
|
+
"bytes": 32284,
|
|
94
|
+
"source_directive_stripped": true
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"path": "dist/road-parcels.d.ts",
|
|
98
|
+
"input_sha256": "d0812067e9c0fee38a6763d1b088055306bee58dd6a84deb88d4d1d9fbce8cb5",
|
|
99
|
+
"published_sha256": "1f39740d69412f580a58c22f291de8ab922ae1bbb6a65263a9c5d19228c55324",
|
|
100
|
+
"bytes": 4275,
|
|
101
|
+
"source_directive_stripped": true
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"path": "dist/road-parcels.js",
|
|
105
|
+
"input_sha256": "4df096f20abeddd36a2fb4b14c86fb35b67ad051509af80e52bd08bb3274bb01",
|
|
106
|
+
"published_sha256": "15166a37d1ba5c467f5c5fa0ff69b12055c3abd610a2b46a3cd861c18987f385",
|
|
107
|
+
"bytes": 79871,
|
|
108
|
+
"source_directive_stripped": true
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"path": "dist/spatial-runtime.d.ts",
|
|
112
|
+
"input_sha256": "f24457975c81df5ffee0d60c64df58abe8daafcbd058366d5024446d79e5e265",
|
|
113
|
+
"published_sha256": "3db6bfb1843be023b2f10e94673985e5ba77cd69d744fe9196ae5ff80246c9b1",
|
|
114
|
+
"bytes": 8843,
|
|
115
|
+
"source_directive_stripped": true
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"path": "dist/spatial-runtime.js",
|
|
119
|
+
"input_sha256": "83603df2381e1e5c406c93c2505e25a40d7005fdff5b05fdb008f2068350c5cf",
|
|
120
|
+
"published_sha256": "c1f1f07aa6eb858ce856fd0486a58efca766587314b208991a7fd46b479c7338",
|
|
121
|
+
"bytes": 61293,
|
|
122
|
+
"source_directive_stripped": true
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
"content_sha256_excluding_package_json": "db83fdbc431a09a413557096608cad322e37ca6c22a4191b44342e8f75167e6a"
|
|
126
|
+
},
|
|
127
|
+
"worldengine_source_identity_provenance": {
|
|
128
|
+
"schema": "worldengine-public-namespace-projection/v1",
|
|
129
|
+
"kind": "composite-source-identity; namespace projection; not-full-source-recompile",
|
|
130
|
+
"original_name": "@worldengine/spatial-authoring",
|
|
131
|
+
"original_archive_sha256": "eefdff3edb4d6a25b44fd63990ef336fb56b74020e428593381c4e2971d254e8",
|
|
132
|
+
"original_source_sha256": "db83fdbc431a09a413557096608cad322e37ca6c22a4191b44342e8f75167e6a",
|
|
133
|
+
"projected_name": "@world-engines/spatial-authoring",
|
|
134
|
+
"package_name_mapping": {
|
|
135
|
+
"@chat/blocks-editor": "@world-engines/blocks-editor",
|
|
136
|
+
"@chat/ladybug-bridge": "@world-engines/ladybug-bridge",
|
|
137
|
+
"@chat/monaco-host": "@world-engines/monaco-host",
|
|
138
|
+
"@chat/protocol-ts": "@world-engines/protocol-ts",
|
|
139
|
+
"@chat/scenario-author-source": "@world-engines/scenario-author-source",
|
|
140
|
+
"@chat/scenario-review-snapshot": "@world-engines/scenario-review-snapshot",
|
|
141
|
+
"@chat/tmw": "@world-engines/tmw",
|
|
142
|
+
"@chat/view-exposure": "@world-engines/view-exposure",
|
|
143
|
+
"@world-engines/chatplay-vite-plugin": "@world-engines/chatplay-vite-plugin",
|
|
144
|
+
"@worldengine/agent-kit": "@world-engines/agent-kit",
|
|
145
|
+
"@worldengine/authoring-bridge": "@world-engines/authoring-bridge",
|
|
146
|
+
"@worldengine/authoring-ui": "@world-engines/authoring-ui",
|
|
147
|
+
"@worldengine/create-project": "@world-engines/create-project",
|
|
148
|
+
"@worldengine/project-format": "@world-engines/project-format",
|
|
149
|
+
"@worldengine/project-host": "@world-engines/project-host",
|
|
150
|
+
"@worldengine/project-setup": "@world-engines/project-setup",
|
|
151
|
+
"@worldengine/spatial-authoring": "@world-engines/spatial-authoring",
|
|
152
|
+
"@worldengine/view-sdk": "@world-engines/view-sdk"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|