@esengine/blueprint 4.4.0 → 4.5.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/dist/index.js CHANGED
@@ -98,6 +98,784 @@ function validateBlueprintAsset(asset) {
98
98
  }
99
99
  __name(validateBlueprintAsset, "validateBlueprintAsset");
100
100
 
101
+ // src/types/schema.ts
102
+ var _SchemaRegistry = class _SchemaRegistry {
103
+ /**
104
+ * @zh 注册 Schema
105
+ * @en Register a schema
106
+ */
107
+ static register(id, schema) {
108
+ this.schemas.set(id, schema);
109
+ }
110
+ /**
111
+ * @zh 获取 Schema
112
+ * @en Get a schema
113
+ */
114
+ static get(id) {
115
+ return this.schemas.get(id);
116
+ }
117
+ /**
118
+ * @zh 解析引用,返回实际 Schema
119
+ * @en Resolve reference, return actual schema
120
+ */
121
+ static resolve(schema) {
122
+ if (schema.type === "ref") {
123
+ const resolved = this.schemas.get(schema.ref);
124
+ if (!resolved) {
125
+ console.warn(`[SchemaRegistry] Schema not found: ${schema.ref}`);
126
+ return {
127
+ type: "primitive",
128
+ primitive: "any"
129
+ };
130
+ }
131
+ return this.resolve(resolved);
132
+ }
133
+ return schema;
134
+ }
135
+ /**
136
+ * @zh 检查 Schema 是否已注册
137
+ * @en Check if schema is registered
138
+ */
139
+ static has(id) {
140
+ return this.schemas.has(id);
141
+ }
142
+ /**
143
+ * @zh 获取所有已注册的 Schema ID
144
+ * @en Get all registered schema IDs
145
+ */
146
+ static keys() {
147
+ return Array.from(this.schemas.keys());
148
+ }
149
+ /**
150
+ * @zh 清空注册表
151
+ * @en Clear registry
152
+ */
153
+ static clear() {
154
+ this.schemas.clear();
155
+ }
156
+ };
157
+ __name(_SchemaRegistry, "SchemaRegistry");
158
+ __publicField(_SchemaRegistry, "schemas", /* @__PURE__ */ new Map());
159
+ var SchemaRegistry = _SchemaRegistry;
160
+ function getSchemaDefaultValue(schema) {
161
+ const resolved = SchemaRegistry.resolve(schema);
162
+ switch (resolved.type) {
163
+ case "primitive":
164
+ if (resolved.defaultValue !== void 0) return resolved.defaultValue;
165
+ return getPrimitiveDefaultValue(resolved.primitive);
166
+ case "array":
167
+ if (resolved.defaultValue !== void 0) return [
168
+ ...resolved.defaultValue
169
+ ];
170
+ return [];
171
+ case "object": {
172
+ const obj = {};
173
+ for (const [key, propSchema] of Object.entries(resolved.properties)) {
174
+ obj[key] = getSchemaDefaultValue(propSchema);
175
+ }
176
+ return obj;
177
+ }
178
+ case "enum":
179
+ if (resolved.defaultValue !== void 0) return resolved.defaultValue;
180
+ return resolved.options[0]?.value;
181
+ default:
182
+ return void 0;
183
+ }
184
+ }
185
+ __name(getSchemaDefaultValue, "getSchemaDefaultValue");
186
+ function getPrimitiveDefaultValue(primitive) {
187
+ switch (primitive) {
188
+ case "bool":
189
+ return false;
190
+ case "int":
191
+ return 0;
192
+ case "float":
193
+ return 0;
194
+ case "string":
195
+ return "";
196
+ case "vector2":
197
+ return {
198
+ x: 0,
199
+ y: 0
200
+ };
201
+ case "vector3":
202
+ return {
203
+ x: 0,
204
+ y: 0,
205
+ z: 0
206
+ };
207
+ case "color":
208
+ return {
209
+ r: 255,
210
+ g: 255,
211
+ b: 255,
212
+ a: 255
213
+ };
214
+ case "entity":
215
+ return null;
216
+ case "component":
217
+ return null;
218
+ case "object":
219
+ return null;
220
+ case "array":
221
+ return [];
222
+ case "any":
223
+ return null;
224
+ case "exec":
225
+ return void 0;
226
+ default:
227
+ return null;
228
+ }
229
+ }
230
+ __name(getPrimitiveDefaultValue, "getPrimitiveDefaultValue");
231
+ function schemaToPinType(schema) {
232
+ const resolved = SchemaRegistry.resolve(schema);
233
+ switch (resolved.type) {
234
+ case "primitive":
235
+ return resolved.primitive;
236
+ case "array":
237
+ return "array";
238
+ case "object":
239
+ return "object";
240
+ case "enum":
241
+ return typeof resolved.options[0]?.value === "number" ? "int" : "string";
242
+ default:
243
+ return "any";
244
+ }
245
+ }
246
+ __name(schemaToPinType, "schemaToPinType");
247
+ function validateSchema(schema, data, path = "") {
248
+ const resolved = SchemaRegistry.resolve(schema);
249
+ const errors = [];
250
+ switch (resolved.type) {
251
+ case "primitive":
252
+ validatePrimitive(resolved, data, path, errors);
253
+ break;
254
+ case "array":
255
+ validateArray(resolved, data, path, errors);
256
+ break;
257
+ case "object":
258
+ validateObject(resolved, data, path, errors);
259
+ break;
260
+ case "enum":
261
+ validateEnum(resolved, data, path, errors);
262
+ break;
263
+ }
264
+ return {
265
+ valid: errors.length === 0,
266
+ errors
267
+ };
268
+ }
269
+ __name(validateSchema, "validateSchema");
270
+ function validatePrimitive(schema, data, path, errors) {
271
+ if (data === null || data === void 0) {
272
+ return;
273
+ }
274
+ const expectedType = getPrimitiveJsType(schema.primitive);
275
+ const actualType = typeof data;
276
+ if (expectedType === "object") {
277
+ if (typeof data !== "object") {
278
+ errors.push({
279
+ path,
280
+ message: `Expected ${schema.primitive}, got ${actualType}`,
281
+ expected: schema.primitive,
282
+ received: actualType
283
+ });
284
+ }
285
+ } else if (expectedType !== "any" && actualType !== expectedType) {
286
+ errors.push({
287
+ path,
288
+ message: `Expected ${expectedType}, got ${actualType}`,
289
+ expected: expectedType,
290
+ received: actualType
291
+ });
292
+ }
293
+ if ((schema.primitive === "int" || schema.primitive === "float") && typeof data === "number") {
294
+ if (schema.min !== void 0 && data < schema.min) {
295
+ errors.push({
296
+ path,
297
+ message: `Value ${data} is less than minimum ${schema.min}`
298
+ });
299
+ }
300
+ if (schema.max !== void 0 && data > schema.max) {
301
+ errors.push({
302
+ path,
303
+ message: `Value ${data} is greater than maximum ${schema.max}`
304
+ });
305
+ }
306
+ }
307
+ }
308
+ __name(validatePrimitive, "validatePrimitive");
309
+ function validateArray(schema, data, path, errors) {
310
+ if (!Array.isArray(data)) {
311
+ errors.push({
312
+ path,
313
+ message: `Expected array, got ${typeof data}`,
314
+ expected: "array",
315
+ received: typeof data
316
+ });
317
+ return;
318
+ }
319
+ if (schema.minItems !== void 0 && data.length < schema.minItems) {
320
+ errors.push({
321
+ path,
322
+ message: `Array has ${data.length} items, minimum is ${schema.minItems}`
323
+ });
324
+ }
325
+ if (schema.maxItems !== void 0 && data.length > schema.maxItems) {
326
+ errors.push({
327
+ path,
328
+ message: `Array has ${data.length} items, maximum is ${schema.maxItems}`
329
+ });
330
+ }
331
+ for (let i = 0; i < data.length; i++) {
332
+ const itemResult = validateSchema(schema.items, data[i], `${path}[${i}]`);
333
+ errors.push(...itemResult.errors);
334
+ }
335
+ }
336
+ __name(validateArray, "validateArray");
337
+ function validateObject(schema, data, path, errors) {
338
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
339
+ errors.push({
340
+ path,
341
+ message: `Expected object, got ${Array.isArray(data) ? "array" : typeof data}`,
342
+ expected: "object",
343
+ received: Array.isArray(data) ? "array" : typeof data
344
+ });
345
+ return;
346
+ }
347
+ const obj = data;
348
+ if (schema.required) {
349
+ for (const key of schema.required) {
350
+ if (!(key in obj)) {
351
+ errors.push({
352
+ path: path ? `${path}.${key}` : key,
353
+ message: `Missing required field: ${key}`
354
+ });
355
+ }
356
+ }
357
+ }
358
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
359
+ if (key in obj) {
360
+ const propPath = path ? `${path}.${key}` : key;
361
+ const propResult = validateSchema(propSchema, obj[key], propPath);
362
+ errors.push(...propResult.errors);
363
+ }
364
+ }
365
+ }
366
+ __name(validateObject, "validateObject");
367
+ function validateEnum(schema, data, path, errors) {
368
+ if (data === null || data === void 0) {
369
+ return;
370
+ }
371
+ const validValues = schema.options.map((o) => o.value);
372
+ if (!validValues.includes(data)) {
373
+ errors.push({
374
+ path,
375
+ message: `Invalid enum value: ${data}`,
376
+ expected: validValues.join(" | "),
377
+ received: String(data)
378
+ });
379
+ }
380
+ }
381
+ __name(validateEnum, "validateEnum");
382
+ function getPrimitiveJsType(primitive) {
383
+ switch (primitive) {
384
+ case "bool":
385
+ return "boolean";
386
+ case "int":
387
+ case "float":
388
+ return "number";
389
+ case "string":
390
+ return "string";
391
+ case "vector2":
392
+ case "vector3":
393
+ case "color":
394
+ case "entity":
395
+ case "component":
396
+ case "object":
397
+ case "array":
398
+ return "object";
399
+ case "any":
400
+ return "any";
401
+ case "exec":
402
+ return "undefined";
403
+ default:
404
+ return "any";
405
+ }
406
+ }
407
+ __name(getPrimitiveJsType, "getPrimitiveJsType");
408
+ function cloneSchema(schema) {
409
+ return JSON.parse(JSON.stringify(schema));
410
+ }
411
+ __name(cloneSchema, "cloneSchema");
412
+ function mergeObjectSchemas(base, override) {
413
+ return {
414
+ ...base,
415
+ ...override,
416
+ properties: {
417
+ ...base.properties,
418
+ ...override.properties || {}
419
+ },
420
+ required: [
421
+ ...base.required || [],
422
+ ...override.required || []
423
+ ]
424
+ };
425
+ }
426
+ __name(mergeObjectSchemas, "mergeObjectSchemas");
427
+ var Schema = {
428
+ // Primitives
429
+ bool(options) {
430
+ return {
431
+ type: "primitive",
432
+ primitive: "bool",
433
+ ...options
434
+ };
435
+ },
436
+ int(options) {
437
+ return {
438
+ type: "primitive",
439
+ primitive: "int",
440
+ ...options
441
+ };
442
+ },
443
+ float(options) {
444
+ return {
445
+ type: "primitive",
446
+ primitive: "float",
447
+ ...options
448
+ };
449
+ },
450
+ string(options) {
451
+ return {
452
+ type: "primitive",
453
+ primitive: "string",
454
+ ...options
455
+ };
456
+ },
457
+ vector2(options) {
458
+ return {
459
+ type: "primitive",
460
+ primitive: "vector2",
461
+ ...options
462
+ };
463
+ },
464
+ vector3(options) {
465
+ return {
466
+ type: "primitive",
467
+ primitive: "vector3",
468
+ ...options
469
+ };
470
+ },
471
+ color(options) {
472
+ return {
473
+ type: "primitive",
474
+ primitive: "color",
475
+ ...options
476
+ };
477
+ },
478
+ entity(options) {
479
+ return {
480
+ type: "primitive",
481
+ primitive: "entity",
482
+ ...options
483
+ };
484
+ },
485
+ component(options) {
486
+ return {
487
+ type: "primitive",
488
+ primitive: "component",
489
+ ...options
490
+ };
491
+ },
492
+ object_ref(options) {
493
+ return {
494
+ type: "primitive",
495
+ primitive: "object",
496
+ ...options
497
+ };
498
+ },
499
+ any(options) {
500
+ return {
501
+ type: "primitive",
502
+ primitive: "any",
503
+ ...options
504
+ };
505
+ },
506
+ // Complex types
507
+ array(items, options) {
508
+ return {
509
+ type: "array",
510
+ items,
511
+ ...options
512
+ };
513
+ },
514
+ object(properties, options) {
515
+ return {
516
+ type: "object",
517
+ properties,
518
+ ...options
519
+ };
520
+ },
521
+ enum(options, extra) {
522
+ return {
523
+ type: "enum",
524
+ options,
525
+ ...extra
526
+ };
527
+ },
528
+ ref(id) {
529
+ return {
530
+ type: "ref",
531
+ ref: id
532
+ };
533
+ }
534
+ };
535
+
536
+ // src/types/path-utils.ts
537
+ function parsePath(path) {
538
+ const parts = [];
539
+ const regex = /([^.\[\]]+)|\[(\*|\d+)\]/g;
540
+ let match;
541
+ while ((match = regex.exec(path)) !== null) {
542
+ if (match[1]) {
543
+ parts.push({
544
+ type: "property",
545
+ name: match[1]
546
+ });
547
+ } else if (match[2] === "*") {
548
+ parts.push({
549
+ type: "wildcard"
550
+ });
551
+ } else {
552
+ parts.push({
553
+ type: "index",
554
+ index: parseInt(match[2], 10)
555
+ });
556
+ }
557
+ }
558
+ return parts;
559
+ }
560
+ __name(parsePath, "parsePath");
561
+ function parsePortPath(path) {
562
+ const result = {
563
+ baseName: "",
564
+ indices: [],
565
+ subPath: [],
566
+ original: path
567
+ };
568
+ const parts = parsePath(path);
569
+ let foundFirstIndex = false;
570
+ let afterIndices = false;
571
+ for (const part of parts) {
572
+ if (part.type === "property") {
573
+ if (!foundFirstIndex) {
574
+ if (result.baseName) {
575
+ result.baseName += "." + part.name;
576
+ } else {
577
+ result.baseName = part.name;
578
+ }
579
+ } else {
580
+ afterIndices = true;
581
+ result.subPath.push(part.name);
582
+ }
583
+ } else if (part.type === "index") {
584
+ foundFirstIndex = true;
585
+ if (!afterIndices) {
586
+ result.indices.push(part.index);
587
+ } else {
588
+ result.subPath.push(`[${part.index}]`);
589
+ }
590
+ }
591
+ }
592
+ return result;
593
+ }
594
+ __name(parsePortPath, "parsePortPath");
595
+ function buildPath(parts) {
596
+ let path = "";
597
+ for (const part of parts) {
598
+ switch (part.type) {
599
+ case "property":
600
+ if (path && !path.endsWith("[")) {
601
+ path += ".";
602
+ }
603
+ path += part.name;
604
+ break;
605
+ case "index":
606
+ path += `[${part.index}]`;
607
+ break;
608
+ case "wildcard":
609
+ path += "[*]";
610
+ break;
611
+ }
612
+ }
613
+ return path;
614
+ }
615
+ __name(buildPath, "buildPath");
616
+ function buildPortPath(address) {
617
+ let path = address.baseName;
618
+ for (const index of address.indices) {
619
+ path += `[${index}]`;
620
+ }
621
+ if (address.subPath.length > 0) {
622
+ for (const sub of address.subPath) {
623
+ if (sub.startsWith("[")) {
624
+ path += sub;
625
+ } else {
626
+ path += "." + sub;
627
+ }
628
+ }
629
+ }
630
+ return path;
631
+ }
632
+ __name(buildPortPath, "buildPortPath");
633
+ function getByPath(data, path) {
634
+ if (!path) return data;
635
+ const parts = parsePath(path);
636
+ let current = data;
637
+ for (const part of parts) {
638
+ if (current === null || current === void 0) {
639
+ return void 0;
640
+ }
641
+ switch (part.type) {
642
+ case "property":
643
+ if (typeof current === "object" && current !== null) {
644
+ current = current[part.name];
645
+ } else {
646
+ return void 0;
647
+ }
648
+ break;
649
+ case "index":
650
+ if (Array.isArray(current)) {
651
+ current = current[part.index];
652
+ } else {
653
+ return void 0;
654
+ }
655
+ break;
656
+ case "wildcard":
657
+ if (Array.isArray(current)) {
658
+ return current;
659
+ }
660
+ return void 0;
661
+ }
662
+ }
663
+ return current;
664
+ }
665
+ __name(getByPath, "getByPath");
666
+ function setByPath(data, path, value) {
667
+ if (!path) return false;
668
+ const parts = parsePath(path);
669
+ let current = data;
670
+ for (let i = 0; i < parts.length - 1; i++) {
671
+ const part = parts[i];
672
+ if (current === null || current === void 0) {
673
+ return false;
674
+ }
675
+ switch (part.type) {
676
+ case "property":
677
+ if (typeof current === "object" && current !== null) {
678
+ current = current[part.name];
679
+ } else {
680
+ return false;
681
+ }
682
+ break;
683
+ case "index":
684
+ if (Array.isArray(current)) {
685
+ current = current[part.index];
686
+ } else {
687
+ return false;
688
+ }
689
+ break;
690
+ case "wildcard":
691
+ return false;
692
+ }
693
+ }
694
+ const lastPart = parts[parts.length - 1];
695
+ if (current === null || current === void 0) {
696
+ return false;
697
+ }
698
+ switch (lastPart.type) {
699
+ case "property":
700
+ if (typeof current === "object" && current !== null) {
701
+ current[lastPart.name] = value;
702
+ return true;
703
+ }
704
+ break;
705
+ case "index":
706
+ if (Array.isArray(current)) {
707
+ current[lastPart.index] = value;
708
+ return true;
709
+ }
710
+ break;
711
+ }
712
+ return false;
713
+ }
714
+ __name(setByPath, "setByPath");
715
+ function hasPath(data, path) {
716
+ return getByPath(data, path) !== void 0;
717
+ }
718
+ __name(hasPath, "hasPath");
719
+ function deleteByPath(data, path) {
720
+ if (!path) return false;
721
+ const parts = parsePath(path);
722
+ let current = data;
723
+ for (let i = 0; i < parts.length - 1; i++) {
724
+ const part = parts[i];
725
+ if (current === null || current === void 0) {
726
+ return false;
727
+ }
728
+ switch (part.type) {
729
+ case "property":
730
+ if (typeof current === "object" && current !== null) {
731
+ current = current[part.name];
732
+ } else {
733
+ return false;
734
+ }
735
+ break;
736
+ case "index":
737
+ if (Array.isArray(current)) {
738
+ current = current[part.index];
739
+ } else {
740
+ return false;
741
+ }
742
+ break;
743
+ default:
744
+ return false;
745
+ }
746
+ }
747
+ const lastPart = parts[parts.length - 1];
748
+ if (current === null || current === void 0) {
749
+ return false;
750
+ }
751
+ switch (lastPart.type) {
752
+ case "property":
753
+ if (typeof current === "object" && current !== null) {
754
+ delete current[lastPart.name];
755
+ return true;
756
+ }
757
+ break;
758
+ case "index":
759
+ if (Array.isArray(current)) {
760
+ current.splice(lastPart.index, 1);
761
+ return true;
762
+ }
763
+ break;
764
+ }
765
+ return false;
766
+ }
767
+ __name(deleteByPath, "deleteByPath");
768
+ function updatePathOnArrayChange(path, arrayPath, operation, index, toIndex) {
769
+ if (!path.startsWith(arrayPath + "[")) {
770
+ return path;
771
+ }
772
+ const address = parsePortPath(path);
773
+ if (address.indices.length === 0) {
774
+ return path;
775
+ }
776
+ const currentIndex = address.indices[0];
777
+ switch (operation) {
778
+ case "insert":
779
+ if (currentIndex >= index) {
780
+ address.indices[0]++;
781
+ }
782
+ break;
783
+ case "remove":
784
+ if (currentIndex === index) {
785
+ return "";
786
+ }
787
+ if (currentIndex > index) {
788
+ address.indices[0]--;
789
+ }
790
+ break;
791
+ case "move":
792
+ if (toIndex !== void 0) {
793
+ if (currentIndex === index) {
794
+ address.indices[0] = toIndex;
795
+ } else if (index < toIndex) {
796
+ if (currentIndex > index && currentIndex <= toIndex) {
797
+ address.indices[0]--;
798
+ }
799
+ } else {
800
+ if (currentIndex >= toIndex && currentIndex < index) {
801
+ address.indices[0]++;
802
+ }
803
+ }
804
+ }
805
+ break;
806
+ }
807
+ return buildPortPath(address);
808
+ }
809
+ __name(updatePathOnArrayChange, "updatePathOnArrayChange");
810
+ function expandWildcardPath(path, data) {
811
+ const parts = parsePath(path);
812
+ const results = [];
813
+ function expand(currentParts, currentData, index) {
814
+ if (index >= parts.length) {
815
+ results.push(buildPath(currentParts));
816
+ return;
817
+ }
818
+ const part = parts[index];
819
+ if (part.type === "wildcard") {
820
+ if (Array.isArray(currentData)) {
821
+ for (let i = 0; i < currentData.length; i++) {
822
+ const newParts = [
823
+ ...currentParts,
824
+ {
825
+ type: "index",
826
+ index: i
827
+ }
828
+ ];
829
+ expand(newParts, currentData[i], index + 1);
830
+ }
831
+ }
832
+ } else {
833
+ const newParts = [
834
+ ...currentParts,
835
+ part
836
+ ];
837
+ let nextData;
838
+ if (part.type === "property" && typeof currentData === "object" && currentData !== null) {
839
+ nextData = currentData[part.name];
840
+ } else if (part.type === "index" && Array.isArray(currentData)) {
841
+ nextData = currentData[part.index];
842
+ }
843
+ expand(newParts, nextData, index + 1);
844
+ }
845
+ }
846
+ __name(expand, "expand");
847
+ expand([], data, 0);
848
+ return results;
849
+ }
850
+ __name(expandWildcardPath, "expandWildcardPath");
851
+ function hasWildcard(path) {
852
+ return path.includes("[*]");
853
+ }
854
+ __name(hasWildcard, "hasWildcard");
855
+ function getParentPath(path) {
856
+ const parts = parsePath(path);
857
+ if (parts.length <= 1) {
858
+ return "";
859
+ }
860
+ return buildPath(parts.slice(0, -1));
861
+ }
862
+ __name(getParentPath, "getParentPath");
863
+ function getPathLastName(path) {
864
+ const parts = parsePath(path);
865
+ if (parts.length === 0) {
866
+ return "";
867
+ }
868
+ const last = parts[parts.length - 1];
869
+ if (last.type === "property") {
870
+ return last.name;
871
+ } else if (last.type === "index") {
872
+ return `[${last.index}]`;
873
+ } else {
874
+ return "[*]";
875
+ }
876
+ }
877
+ __name(getPathLastName, "getPathLastName");
878
+
101
879
  // src/registry/BlueprintDecorators.ts
102
880
  var registeredComponents = /* @__PURE__ */ new Map();
103
881
  function getRegisteredBlueprintComponents() {
@@ -155,6 +933,67 @@ function BlueprintProperty(options = {}) {
155
933
  };
156
934
  }
157
935
  __name(BlueprintProperty, "BlueprintProperty");
936
+ function BlueprintArray(options) {
937
+ return function(target, propertyKey) {
938
+ const key = String(propertyKey);
939
+ const metadata = getOrCreateMetadata(target.constructor);
940
+ const arraySchema = {
941
+ type: "array",
942
+ items: options.itemSchema,
943
+ defaultValue: options.defaultValue,
944
+ minItems: options.minItems,
945
+ maxItems: options.maxItems,
946
+ reorderable: options.reorderable,
947
+ collapsible: options.collapsible,
948
+ itemLabel: options.itemLabel
949
+ };
950
+ const propMeta = {
951
+ propertyKey: key,
952
+ displayName: options.displayName ?? key,
953
+ description: options.description,
954
+ pinType: "array",
955
+ readonly: false,
956
+ defaultValue: options.defaultValue,
957
+ schema: arraySchema,
958
+ isDynamicArray: true,
959
+ exposeElementPorts: options.exposeElementPorts,
960
+ portNameTemplate: options.portNameTemplate
961
+ };
962
+ const existingIndex = metadata.properties.findIndex((p) => p.propertyKey === key);
963
+ if (existingIndex >= 0) {
964
+ metadata.properties[existingIndex] = propMeta;
965
+ } else {
966
+ metadata.properties.push(propMeta);
967
+ }
968
+ };
969
+ }
970
+ __name(BlueprintArray, "BlueprintArray");
971
+ function BlueprintObject(options) {
972
+ return function(target, propertyKey) {
973
+ const key = String(propertyKey);
974
+ const metadata = getOrCreateMetadata(target.constructor);
975
+ const objectSchema = {
976
+ type: "object",
977
+ properties: options.properties,
978
+ collapsible: options.collapsible
979
+ };
980
+ const propMeta = {
981
+ propertyKey: key,
982
+ displayName: options.displayName ?? key,
983
+ description: options.description,
984
+ pinType: "object",
985
+ readonly: false,
986
+ schema: objectSchema
987
+ };
988
+ const existingIndex = metadata.properties.findIndex((p) => p.propertyKey === key);
989
+ if (existingIndex >= 0) {
990
+ metadata.properties[existingIndex] = propMeta;
991
+ } else {
992
+ metadata.properties.push(propMeta);
993
+ }
994
+ };
995
+ }
996
+ __name(BlueprintObject, "BlueprintObject");
158
997
  function BlueprintMethod(options = {}) {
159
998
  return function(target, propertyKey, descriptor) {
160
999
  const key = String(propertyKey);
@@ -6149,6 +6988,548 @@ var SignExecutor = _SignExecutor;
6149
6988
  SignExecutor = _ts_decorate10([
6150
6989
  RegisterNode(SignTemplate)
6151
6990
  ], SignExecutor);
6991
+ var WrapTemplate = {
6992
+ type: "Wrap",
6993
+ title: "Wrap",
6994
+ category: "math",
6995
+ color: "#4CAF50",
6996
+ description: "Wraps value to stay within min and max range (\u5C06\u503C\u5FAA\u73AF\u9650\u5236\u5728\u8303\u56F4\u5185)",
6997
+ keywords: [
6998
+ "wrap",
6999
+ "loop",
7000
+ "cycle",
7001
+ "range",
7002
+ "math"
7003
+ ],
7004
+ isPure: true,
7005
+ inputs: [
7006
+ {
7007
+ name: "value",
7008
+ type: "float",
7009
+ displayName: "Value",
7010
+ defaultValue: 0
7011
+ },
7012
+ {
7013
+ name: "min",
7014
+ type: "float",
7015
+ displayName: "Min",
7016
+ defaultValue: 0
7017
+ },
7018
+ {
7019
+ name: "max",
7020
+ type: "float",
7021
+ displayName: "Max",
7022
+ defaultValue: 1
7023
+ }
7024
+ ],
7025
+ outputs: [
7026
+ {
7027
+ name: "result",
7028
+ type: "float",
7029
+ displayName: "Result"
7030
+ }
7031
+ ]
7032
+ };
7033
+ var _WrapExecutor = class _WrapExecutor {
7034
+ execute(node, context) {
7035
+ const value = Number(context.evaluateInput(node.id, "value", 0));
7036
+ const min = Number(context.evaluateInput(node.id, "min", 0));
7037
+ const max = Number(context.evaluateInput(node.id, "max", 1));
7038
+ const range = max - min;
7039
+ if (range <= 0) return {
7040
+ outputs: {
7041
+ result: min
7042
+ }
7043
+ };
7044
+ const wrapped = ((value - min) % range + range) % range + min;
7045
+ return {
7046
+ outputs: {
7047
+ result: wrapped
7048
+ }
7049
+ };
7050
+ }
7051
+ };
7052
+ __name(_WrapExecutor, "WrapExecutor");
7053
+ var WrapExecutor = _WrapExecutor;
7054
+ WrapExecutor = _ts_decorate10([
7055
+ RegisterNode(WrapTemplate)
7056
+ ], WrapExecutor);
7057
+ var SinTemplate = {
7058
+ type: "Sin",
7059
+ title: "Sin",
7060
+ category: "math",
7061
+ color: "#4CAF50",
7062
+ description: "Returns the sine of angle in radians (\u8FD4\u56DE\u5F27\u5EA6\u89D2\u7684\u6B63\u5F26\u503C)",
7063
+ keywords: [
7064
+ "sin",
7065
+ "sine",
7066
+ "trig",
7067
+ "math"
7068
+ ],
7069
+ isPure: true,
7070
+ inputs: [
7071
+ {
7072
+ name: "radians",
7073
+ type: "float",
7074
+ displayName: "Radians",
7075
+ defaultValue: 0
7076
+ }
7077
+ ],
7078
+ outputs: [
7079
+ {
7080
+ name: "result",
7081
+ type: "float",
7082
+ displayName: "Result"
7083
+ }
7084
+ ]
7085
+ };
7086
+ var _SinExecutor = class _SinExecutor {
7087
+ execute(node, context) {
7088
+ const radians = Number(context.evaluateInput(node.id, "radians", 0));
7089
+ return {
7090
+ outputs: {
7091
+ result: Math.sin(radians)
7092
+ }
7093
+ };
7094
+ }
7095
+ };
7096
+ __name(_SinExecutor, "SinExecutor");
7097
+ var SinExecutor = _SinExecutor;
7098
+ SinExecutor = _ts_decorate10([
7099
+ RegisterNode(SinTemplate)
7100
+ ], SinExecutor);
7101
+ var CosTemplate = {
7102
+ type: "Cos",
7103
+ title: "Cos",
7104
+ category: "math",
7105
+ color: "#4CAF50",
7106
+ description: "Returns the cosine of angle in radians (\u8FD4\u56DE\u5F27\u5EA6\u89D2\u7684\u4F59\u5F26\u503C)",
7107
+ keywords: [
7108
+ "cos",
7109
+ "cosine",
7110
+ "trig",
7111
+ "math"
7112
+ ],
7113
+ isPure: true,
7114
+ inputs: [
7115
+ {
7116
+ name: "radians",
7117
+ type: "float",
7118
+ displayName: "Radians",
7119
+ defaultValue: 0
7120
+ }
7121
+ ],
7122
+ outputs: [
7123
+ {
7124
+ name: "result",
7125
+ type: "float",
7126
+ displayName: "Result"
7127
+ }
7128
+ ]
7129
+ };
7130
+ var _CosExecutor = class _CosExecutor {
7131
+ execute(node, context) {
7132
+ const radians = Number(context.evaluateInput(node.id, "radians", 0));
7133
+ return {
7134
+ outputs: {
7135
+ result: Math.cos(radians)
7136
+ }
7137
+ };
7138
+ }
7139
+ };
7140
+ __name(_CosExecutor, "CosExecutor");
7141
+ var CosExecutor = _CosExecutor;
7142
+ CosExecutor = _ts_decorate10([
7143
+ RegisterNode(CosTemplate)
7144
+ ], CosExecutor);
7145
+ var TanTemplate = {
7146
+ type: "Tan",
7147
+ title: "Tan",
7148
+ category: "math",
7149
+ color: "#4CAF50",
7150
+ description: "Returns the tangent of angle in radians (\u8FD4\u56DE\u5F27\u5EA6\u89D2\u7684\u6B63\u5207\u503C)",
7151
+ keywords: [
7152
+ "tan",
7153
+ "tangent",
7154
+ "trig",
7155
+ "math"
7156
+ ],
7157
+ isPure: true,
7158
+ inputs: [
7159
+ {
7160
+ name: "radians",
7161
+ type: "float",
7162
+ displayName: "Radians",
7163
+ defaultValue: 0
7164
+ }
7165
+ ],
7166
+ outputs: [
7167
+ {
7168
+ name: "result",
7169
+ type: "float",
7170
+ displayName: "Result"
7171
+ }
7172
+ ]
7173
+ };
7174
+ var _TanExecutor = class _TanExecutor {
7175
+ execute(node, context) {
7176
+ const radians = Number(context.evaluateInput(node.id, "radians", 0));
7177
+ return {
7178
+ outputs: {
7179
+ result: Math.tan(radians)
7180
+ }
7181
+ };
7182
+ }
7183
+ };
7184
+ __name(_TanExecutor, "TanExecutor");
7185
+ var TanExecutor = _TanExecutor;
7186
+ TanExecutor = _ts_decorate10([
7187
+ RegisterNode(TanTemplate)
7188
+ ], TanExecutor);
7189
+ var AsinTemplate = {
7190
+ type: "Asin",
7191
+ title: "Asin",
7192
+ category: "math",
7193
+ color: "#4CAF50",
7194
+ description: "Returns the arc sine in radians (\u8FD4\u56DE\u53CD\u6B63\u5F26\u503C\uFF0C\u5355\u4F4D\u4E3A\u5F27\u5EA6)",
7195
+ keywords: [
7196
+ "asin",
7197
+ "arc",
7198
+ "sine",
7199
+ "inverse",
7200
+ "trig",
7201
+ "math"
7202
+ ],
7203
+ isPure: true,
7204
+ inputs: [
7205
+ {
7206
+ name: "value",
7207
+ type: "float",
7208
+ displayName: "Value",
7209
+ defaultValue: 0
7210
+ }
7211
+ ],
7212
+ outputs: [
7213
+ {
7214
+ name: "result",
7215
+ type: "float",
7216
+ displayName: "Radians"
7217
+ }
7218
+ ]
7219
+ };
7220
+ var _AsinExecutor = class _AsinExecutor {
7221
+ execute(node, context) {
7222
+ const value = Number(context.evaluateInput(node.id, "value", 0));
7223
+ return {
7224
+ outputs: {
7225
+ result: Math.asin(Math.max(-1, Math.min(1, value)))
7226
+ }
7227
+ };
7228
+ }
7229
+ };
7230
+ __name(_AsinExecutor, "AsinExecutor");
7231
+ var AsinExecutor = _AsinExecutor;
7232
+ AsinExecutor = _ts_decorate10([
7233
+ RegisterNode(AsinTemplate)
7234
+ ], AsinExecutor);
7235
+ var AcosTemplate = {
7236
+ type: "Acos",
7237
+ title: "Acos",
7238
+ category: "math",
7239
+ color: "#4CAF50",
7240
+ description: "Returns the arc cosine in radians (\u8FD4\u56DE\u53CD\u4F59\u5F26\u503C\uFF0C\u5355\u4F4D\u4E3A\u5F27\u5EA6)",
7241
+ keywords: [
7242
+ "acos",
7243
+ "arc",
7244
+ "cosine",
7245
+ "inverse",
7246
+ "trig",
7247
+ "math"
7248
+ ],
7249
+ isPure: true,
7250
+ inputs: [
7251
+ {
7252
+ name: "value",
7253
+ type: "float",
7254
+ displayName: "Value",
7255
+ defaultValue: 0
7256
+ }
7257
+ ],
7258
+ outputs: [
7259
+ {
7260
+ name: "result",
7261
+ type: "float",
7262
+ displayName: "Radians"
7263
+ }
7264
+ ]
7265
+ };
7266
+ var _AcosExecutor = class _AcosExecutor {
7267
+ execute(node, context) {
7268
+ const value = Number(context.evaluateInput(node.id, "value", 0));
7269
+ return {
7270
+ outputs: {
7271
+ result: Math.acos(Math.max(-1, Math.min(1, value)))
7272
+ }
7273
+ };
7274
+ }
7275
+ };
7276
+ __name(_AcosExecutor, "AcosExecutor");
7277
+ var AcosExecutor = _AcosExecutor;
7278
+ AcosExecutor = _ts_decorate10([
7279
+ RegisterNode(AcosTemplate)
7280
+ ], AcosExecutor);
7281
+ var AtanTemplate = {
7282
+ type: "Atan",
7283
+ title: "Atan",
7284
+ category: "math",
7285
+ color: "#4CAF50",
7286
+ description: "Returns the arc tangent in radians (\u8FD4\u56DE\u53CD\u6B63\u5207\u503C\uFF0C\u5355\u4F4D\u4E3A\u5F27\u5EA6)",
7287
+ keywords: [
7288
+ "atan",
7289
+ "arc",
7290
+ "tangent",
7291
+ "inverse",
7292
+ "trig",
7293
+ "math"
7294
+ ],
7295
+ isPure: true,
7296
+ inputs: [
7297
+ {
7298
+ name: "value",
7299
+ type: "float",
7300
+ displayName: "Value",
7301
+ defaultValue: 0
7302
+ }
7303
+ ],
7304
+ outputs: [
7305
+ {
7306
+ name: "result",
7307
+ type: "float",
7308
+ displayName: "Radians"
7309
+ }
7310
+ ]
7311
+ };
7312
+ var _AtanExecutor = class _AtanExecutor {
7313
+ execute(node, context) {
7314
+ const value = Number(context.evaluateInput(node.id, "value", 0));
7315
+ return {
7316
+ outputs: {
7317
+ result: Math.atan(value)
7318
+ }
7319
+ };
7320
+ }
7321
+ };
7322
+ __name(_AtanExecutor, "AtanExecutor");
7323
+ var AtanExecutor = _AtanExecutor;
7324
+ AtanExecutor = _ts_decorate10([
7325
+ RegisterNode(AtanTemplate)
7326
+ ], AtanExecutor);
7327
+ var Atan2Template = {
7328
+ type: "Atan2",
7329
+ title: "Atan2",
7330
+ category: "math",
7331
+ color: "#4CAF50",
7332
+ description: "Returns the angle in radians between the positive X axis and the point (x, y) (\u8FD4\u56DE\u70B9(x,y)\u4E0E\u6B63X\u8F74\u4E4B\u95F4\u7684\u5F27\u5EA6\u89D2)",
7333
+ keywords: [
7334
+ "atan2",
7335
+ "angle",
7336
+ "direction",
7337
+ "trig",
7338
+ "math"
7339
+ ],
7340
+ isPure: true,
7341
+ inputs: [
7342
+ {
7343
+ name: "y",
7344
+ type: "float",
7345
+ displayName: "Y",
7346
+ defaultValue: 0
7347
+ },
7348
+ {
7349
+ name: "x",
7350
+ type: "float",
7351
+ displayName: "X",
7352
+ defaultValue: 1
7353
+ }
7354
+ ],
7355
+ outputs: [
7356
+ {
7357
+ name: "result",
7358
+ type: "float",
7359
+ displayName: "Radians"
7360
+ }
7361
+ ]
7362
+ };
7363
+ var _Atan2Executor = class _Atan2Executor {
7364
+ execute(node, context) {
7365
+ const y = Number(context.evaluateInput(node.id, "y", 0));
7366
+ const x = Number(context.evaluateInput(node.id, "x", 1));
7367
+ return {
7368
+ outputs: {
7369
+ result: Math.atan2(y, x)
7370
+ }
7371
+ };
7372
+ }
7373
+ };
7374
+ __name(_Atan2Executor, "Atan2Executor");
7375
+ var Atan2Executor = _Atan2Executor;
7376
+ Atan2Executor = _ts_decorate10([
7377
+ RegisterNode(Atan2Template)
7378
+ ], Atan2Executor);
7379
+ var DegToRadTemplate = {
7380
+ type: "DegToRad",
7381
+ title: "Degrees to Radians",
7382
+ category: "math",
7383
+ color: "#4CAF50",
7384
+ description: "Converts degrees to radians (\u5C06\u89D2\u5EA6\u8F6C\u6362\u4E3A\u5F27\u5EA6)",
7385
+ keywords: [
7386
+ "degrees",
7387
+ "radians",
7388
+ "convert",
7389
+ "angle",
7390
+ "math"
7391
+ ],
7392
+ isPure: true,
7393
+ inputs: [
7394
+ {
7395
+ name: "degrees",
7396
+ type: "float",
7397
+ displayName: "Degrees",
7398
+ defaultValue: 0
7399
+ }
7400
+ ],
7401
+ outputs: [
7402
+ {
7403
+ name: "result",
7404
+ type: "float",
7405
+ displayName: "Radians"
7406
+ }
7407
+ ]
7408
+ };
7409
+ var _DegToRadExecutor = class _DegToRadExecutor {
7410
+ execute(node, context) {
7411
+ const degrees = Number(context.evaluateInput(node.id, "degrees", 0));
7412
+ return {
7413
+ outputs: {
7414
+ result: degrees * (Math.PI / 180)
7415
+ }
7416
+ };
7417
+ }
7418
+ };
7419
+ __name(_DegToRadExecutor, "DegToRadExecutor");
7420
+ var DegToRadExecutor = _DegToRadExecutor;
7421
+ DegToRadExecutor = _ts_decorate10([
7422
+ RegisterNode(DegToRadTemplate)
7423
+ ], DegToRadExecutor);
7424
+ var RadToDegTemplate = {
7425
+ type: "RadToDeg",
7426
+ title: "Radians to Degrees",
7427
+ category: "math",
7428
+ color: "#4CAF50",
7429
+ description: "Converts radians to degrees (\u5C06\u5F27\u5EA6\u8F6C\u6362\u4E3A\u89D2\u5EA6)",
7430
+ keywords: [
7431
+ "radians",
7432
+ "degrees",
7433
+ "convert",
7434
+ "angle",
7435
+ "math"
7436
+ ],
7437
+ isPure: true,
7438
+ inputs: [
7439
+ {
7440
+ name: "radians",
7441
+ type: "float",
7442
+ displayName: "Radians",
7443
+ defaultValue: 0
7444
+ }
7445
+ ],
7446
+ outputs: [
7447
+ {
7448
+ name: "result",
7449
+ type: "float",
7450
+ displayName: "Degrees"
7451
+ }
7452
+ ]
7453
+ };
7454
+ var _RadToDegExecutor = class _RadToDegExecutor {
7455
+ execute(node, context) {
7456
+ const radians = Number(context.evaluateInput(node.id, "radians", 0));
7457
+ return {
7458
+ outputs: {
7459
+ result: radians * (180 / Math.PI)
7460
+ }
7461
+ };
7462
+ }
7463
+ };
7464
+ __name(_RadToDegExecutor, "RadToDegExecutor");
7465
+ var RadToDegExecutor = _RadToDegExecutor;
7466
+ RadToDegExecutor = _ts_decorate10([
7467
+ RegisterNode(RadToDegTemplate)
7468
+ ], RadToDegExecutor);
7469
+ var InverseLerpTemplate = {
7470
+ type: "InverseLerp",
7471
+ title: "Inverse Lerp",
7472
+ category: "math",
7473
+ color: "#4CAF50",
7474
+ description: "Returns the percentage of Value between A and B (\u8FD4\u56DE\u503C\u5728 A \u548C B \u4E4B\u95F4\u7684\u767E\u5206\u6BD4\u4F4D\u7F6E)",
7475
+ keywords: [
7476
+ "inverse",
7477
+ "lerp",
7478
+ "percentage",
7479
+ "ratio",
7480
+ "math"
7481
+ ],
7482
+ isPure: true,
7483
+ inputs: [
7484
+ {
7485
+ name: "a",
7486
+ type: "float",
7487
+ displayName: "A",
7488
+ defaultValue: 0
7489
+ },
7490
+ {
7491
+ name: "b",
7492
+ type: "float",
7493
+ displayName: "B",
7494
+ defaultValue: 1
7495
+ },
7496
+ {
7497
+ name: "value",
7498
+ type: "float",
7499
+ displayName: "Value",
7500
+ defaultValue: 0.5
7501
+ }
7502
+ ],
7503
+ outputs: [
7504
+ {
7505
+ name: "result",
7506
+ type: "float",
7507
+ displayName: "Alpha (0-1)"
7508
+ }
7509
+ ]
7510
+ };
7511
+ var _InverseLerpExecutor = class _InverseLerpExecutor {
7512
+ execute(node, context) {
7513
+ const a = Number(context.evaluateInput(node.id, "a", 0));
7514
+ const b = Number(context.evaluateInput(node.id, "b", 1));
7515
+ const value = Number(context.evaluateInput(node.id, "value", 0.5));
7516
+ if (b === a) return {
7517
+ outputs: {
7518
+ result: 0
7519
+ }
7520
+ };
7521
+ return {
7522
+ outputs: {
7523
+ result: (value - a) / (b - a)
7524
+ }
7525
+ };
7526
+ }
7527
+ };
7528
+ __name(_InverseLerpExecutor, "InverseLerpExecutor");
7529
+ var InverseLerpExecutor = _InverseLerpExecutor;
7530
+ InverseLerpExecutor = _ts_decorate10([
7531
+ RegisterNode(InverseLerpTemplate)
7532
+ ], InverseLerpExecutor);
6152
7533
 
6153
7534
  // src/nodes/logic/ComparisonNodes.ts
6154
7535
  function _ts_decorate11(decorators, target, key, desc) {
@@ -7123,11 +8504,13 @@ __name(registerComponentClass, "registerComponentClass");
7123
8504
  export {
7124
8505
  AlwaysFalseCondition,
7125
8506
  AlwaysTrueCondition,
8507
+ BlueprintArray,
7126
8508
  BlueprintComponent,
7127
8509
  BlueprintComposer,
7128
8510
  BlueprintExpose,
7129
8511
  BlueprintFragment,
7130
8512
  BlueprintMethod,
8513
+ BlueprintObject,
7131
8514
  BlueprintProperty,
7132
8515
  BlueprintSystem,
7133
8516
  BlueprintTrigger,
@@ -7146,6 +8529,8 @@ export {
7146
8529
  NodeRegistry,
7147
8530
  NotCondition,
7148
8531
  RegisterNode,
8532
+ Schema,
8533
+ SchemaRegistry,
7149
8534
  StateNameCondition,
7150
8535
  TimerIdCondition,
7151
8536
  TriggerDispatcher,
@@ -7153,7 +8538,10 @@ export {
7153
8538
  TriggerTypeCondition,
7154
8539
  TriggerTypes,
7155
8540
  arePinTypesCompatible,
8541
+ buildPath,
8542
+ buildPortPath,
7156
8543
  clearRegisteredComponents,
8544
+ cloneSchema,
7157
8545
  condition,
7158
8546
  createCollisionContext,
7159
8547
  createCollisionTrigger,
@@ -7179,17 +8567,33 @@ export {
7179
8567
  createTrigger,
7180
8568
  createTriggerDispatcher,
7181
8569
  defaultFragmentRegistry,
8570
+ deleteByPath,
8571
+ expandWildcardPath,
7182
8572
  fragmentFromAsset,
7183
8573
  fragmentToAsset,
7184
8574
  generateComponentNodes,
7185
8575
  getBlueprintMetadata,
8576
+ getByPath,
7186
8577
  getNodeCategoryColor,
8578
+ getParentPath,
8579
+ getPathLastName,
7187
8580
  getPinTypeColor,
8581
+ getPrimitiveDefaultValue,
7188
8582
  getRegisteredBlueprintComponents,
8583
+ getSchemaDefaultValue,
8584
+ hasPath,
8585
+ hasWildcard,
7189
8586
  inferPinType,
8587
+ mergeObjectSchemas,
8588
+ parsePath,
8589
+ parsePortPath,
7190
8590
  registerAllComponentNodes,
7191
8591
  registerComponentClass,
7192
8592
  registerComponentNodes,
7193
- validateBlueprintAsset
8593
+ schemaToPinType,
8594
+ setByPath,
8595
+ updatePathOnArrayChange,
8596
+ validateBlueprintAsset,
8597
+ validateSchema
7194
8598
  };
7195
8599
  //# sourceMappingURL=index.js.map