@holoscript/core 6.0.3 → 7.0.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/README.md +40 -40
- package/dist/codebase/index.d.ts +25 -0
- package/dist/compiler/index.d.ts +82 -0
- package/dist/constants.d.ts +4 -0
- package/dist/coordinators/index.d.ts +189 -0
- package/dist/entries/interop.d.ts +9 -0
- package/dist/entries/scripting.d.ts +7 -0
- package/dist/index.d.ts +520 -19
- package/dist/paper-0c-spike/index.d.ts +29 -0
- package/dist/reconstruction/index.d.ts +78 -0
- package/dist/self-improvement/index.d.ts +0 -48
- package/dist/storage/index.d.ts +24 -0
- package/dist/tools/index.d.ts +33 -0
- package/dist/traitDocs/traitDocs.d.ts +69 -0
- package/dist/traits/index.d.ts +61 -0
- package/package.json +102 -19
- package/dist/GLTFPipeline-3KLWWUQO.cjs +0 -34
- package/dist/GLTFPipeline-LYII2ZVQ.js +0 -13
- package/dist/HoloScriptPlusParser-BZR5DELQ.js +0 -692
- package/dist/HoloScriptPlusParser-SBMYDNXO.cjs +0 -697
- package/dist/USDZExporter-MHEHXZB4.cjs +0 -709
- package/dist/USDZExporter-YA55ZS35.js +0 -707
- package/dist/chunk-26TLYBFD.js +0 -309
- package/dist/chunk-27LWW5FU.cjs +0 -410
- package/dist/chunk-2UX5LRAP.cjs +0 -833
- package/dist/chunk-32TWR3HE.js +0 -70
- package/dist/chunk-3FXV5S25.cjs +0 -633
- package/dist/chunk-3FYZOR7S.cjs +0 -69
- package/dist/chunk-3KEU5QYY.cjs +0 -175
- package/dist/chunk-4FCZDTD5.js +0 -610
- package/dist/chunk-4KJ2R7VP.cjs +0 -507
- package/dist/chunk-4SMUJFHL.js +0 -1213
- package/dist/chunk-54FHG2MX.js +0 -655
- package/dist/chunk-56IF4NTA.js +0 -56
- package/dist/chunk-5GLCTO44.js +0 -161
- package/dist/chunk-5TOFIR6L.js +0 -407
- package/dist/chunk-65RFOWZI.js +0 -765
- package/dist/chunk-6OQBLABR.js +0 -5240
- package/dist/chunk-6WNCRE6F.js +0 -126
- package/dist/chunk-74YCHHTE.js +0 -1872
- package/dist/chunk-7H5UNJZD.cjs +0 -1973
- package/dist/chunk-7KPI4EKH.cjs +0 -701
- package/dist/chunk-7LCYLPW2.cjs +0 -663
- package/dist/chunk-7N3JIJMT.js +0 -1395
- package/dist/chunk-7X2IEJIE.cjs +0 -2526
- package/dist/chunk-A6GO3DPZ.cjs +0 -1002
- package/dist/chunk-AC4BSHFV.js +0 -765
- package/dist/chunk-AIUXRS74.cjs +0 -3681
- package/dist/chunk-ARNKA274.cjs +0 -130
- package/dist/chunk-AW7WAELW.js +0 -975
- package/dist/chunk-BKIGPPVM.js +0 -629
- package/dist/chunk-BO2OKHIY.js +0 -6055
- package/dist/chunk-BU5ZAFMC.js +0 -115
- package/dist/chunk-BY3B7ZYV.cjs +0 -457
- package/dist/chunk-C2QHVHZF.js +0 -4066
- package/dist/chunk-C7BNX4XJ.js +0 -1043
- package/dist/chunk-CLYEIWA5.cjs +0 -408
- package/dist/chunk-CMYAWUX3.js +0 -4455
- package/dist/chunk-CN4NOESF.cjs +0 -416
- package/dist/chunk-CNVM7J3M.js +0 -883
- package/dist/chunk-CO2VM2DK.js +0 -1965
- package/dist/chunk-CQDOF3G7.cjs +0 -1404
- package/dist/chunk-DH5G2JUA.cjs +0 -614
- package/dist/chunk-DIEDKX5B.cjs +0 -660
- package/dist/chunk-DN7UFU63.cjs +0 -172
- package/dist/chunk-DXVCEFZB.js +0 -2027
- package/dist/chunk-EGIZHYJP.cjs +0 -85
- package/dist/chunk-EJA7G2C4.cjs +0 -428
- package/dist/chunk-ELLQPFAF.js +0 -830
- package/dist/chunk-EMO7HAKJ.cjs +0 -1875
- package/dist/chunk-ENV7K6XA.js +0 -282
- package/dist/chunk-EPWRXL6S.js +0 -2522
- package/dist/chunk-EUFLX2PI.js +0 -56956
- package/dist/chunk-F5LVGHNT.js +0 -449
- package/dist/chunk-FEFHPUEM.cjs +0 -6122
- package/dist/chunk-FF3Q7ADR.js +0 -380
- package/dist/chunk-GC3YU46J.js +0 -762
- package/dist/chunk-H6WMMLQK.cjs +0 -4492
- package/dist/chunk-H7XMORZI.js +0 -2731
- package/dist/chunk-HAN4V3PF.cjs +0 -2037
- package/dist/chunk-HBZYCASG.js +0 -4208
- package/dist/chunk-HF4OFY25.cjs +0 -4097
- package/dist/chunk-HHS6FMOU.cjs +0 -3813
- package/dist/chunk-HJJLD366.js +0 -1169
- package/dist/chunk-HKCVM6OK.cjs +0 -2237
- package/dist/chunk-HUFNKFA6.js +0 -698
- package/dist/chunk-I3XGTIHM.cjs +0 -1505
- package/dist/chunk-I6XZ5DZF.cjs +0 -488
- package/dist/chunk-IHOUYQIA.js +0 -406
- package/dist/chunk-ILRBGFVD.cjs +0 -72
- package/dist/chunk-KDB6BUMB.js +0 -3666
- package/dist/chunk-KUJRR4FJ.js +0 -657
- package/dist/chunk-KYM4XRFG.js +0 -421
- package/dist/chunk-L3Z2HIWJ.cjs +0 -793
- package/dist/chunk-LBPEZQAF.js +0 -2232
- package/dist/chunk-LER4WXW5.cjs +0 -286
- package/dist/chunk-LK5IAVH6.cjs +0 -1173
- package/dist/chunk-M6FU6S22.js +0 -2911
- package/dist/chunk-MWEFR6YQ.js +0 -1857
- package/dist/chunk-N5FTS757.cjs +0 -415
- package/dist/chunk-NCUHGRTZ.js +0 -752
- package/dist/chunk-NKRKT6V2.js +0 -69
- package/dist/chunk-NLPSZT4C.js +0 -630
- package/dist/chunk-NRUB55IT.cjs +0 -771
- package/dist/chunk-NTMZSDXM.cjs +0 -633
- package/dist/chunk-ODGMVILH.js +0 -9662
- package/dist/chunk-PBA6NXCT.cjs +0 -1216
- package/dist/chunk-PHENW4MQ.js +0 -65
- package/dist/chunk-PJMOXFPR.cjs +0 -1861
- package/dist/chunk-PLMYCCA4.js +0 -504
- package/dist/chunk-PPULB4GG.cjs +0 -9728
- package/dist/chunk-PQLGZKMC.cjs +0 -755
- package/dist/chunk-Q56TT7IL.js +0 -483
- package/dist/chunk-QHVVVN47.cjs +0 -2033
- package/dist/chunk-QWKUKVRE.js +0 -2026
- package/dist/chunk-RE3OKSYF.cjs +0 -424
- package/dist/chunk-RT7LJRSF.cjs +0 -57378
- package/dist/chunk-SPDELRRV.cjs +0 -319
- package/dist/chunk-SUCBB66F.js +0 -229
- package/dist/chunk-TSWTWZ42.cjs +0 -4214
- package/dist/chunk-UITWA6DV.cjs +0 -2829
- package/dist/chunk-UQW6SLM5.js +0 -172
- package/dist/chunk-V2ILLPHK.cjs +0 -237
- package/dist/chunk-V42NTCFH.js +0 -3645
- package/dist/chunk-VJ3L7CHT.cjs +0 -169
- package/dist/chunk-VJVCD5T5.cjs +0 -769
- package/dist/chunk-VK5AXKO3.js +0 -746
- package/dist/chunk-VSXOIUCF.cjs +0 -473
- package/dist/chunk-VZNKJZTT.cjs +0 -749
- package/dist/chunk-W76ETJTI.js +0 -678
- package/dist/chunk-WFJIDI2N.cjs +0 -424
- package/dist/chunk-WN3YF33G.cjs +0 -118
- package/dist/chunk-WUXIRGZP.cjs +0 -2915
- package/dist/chunk-WYH4GVZ5.js +0 -1488
- package/dist/chunk-X4YVN7H3.cjs +0 -888
- package/dist/chunk-X67XRI2T.js +0 -410
- package/dist/chunk-XDXZM3ZP.cjs +0 -1046
- package/dist/chunk-XH7SE4HH.cjs +0 -72
- package/dist/chunk-XNMEH2BI.js +0 -126
- package/dist/chunk-XSF76QRU.js +0 -468
- package/dist/chunk-XSUZMPVQ.cjs +0 -682
- package/dist/chunk-XUILI4KB.cjs +0 -430
- package/dist/chunk-Y5MUAYTO.js +0 -167
- package/dist/chunk-Y7VK5TH3.cjs +0 -129
- package/dist/chunk-YCMQQQ5U.cjs +0 -5244
- package/dist/chunk-YYXH45E2.js +0 -426
- package/dist/chunk-ZGTGVSTZ.js +0 -422
- package/dist/chunk-ZL6VJ6SN.js +0 -421
- package/dist/cli/holoscript-runner.cjs +0 -2258
- package/dist/cli/holoscript-runner.js +0 -2233
- package/dist/codebase/index.cjs +0 -336
- package/dist/codebase/index.js +0 -331
- package/dist/compiler/agent-inference.cjs +0 -29
- package/dist/compiler/agent-inference.js +0 -6
- package/dist/compiler/android-xr.cjs +0 -20
- package/dist/compiler/android-xr.js +0 -10
- package/dist/compiler/android.cjs +0 -21
- package/dist/compiler/android.js +0 -11
- package/dist/compiler/ar.cjs +0 -21
- package/dist/compiler/ar.js +0 -9
- package/dist/compiler/babylon.cjs +0 -18
- package/dist/compiler/babylon.js +0 -12
- package/dist/compiler/coco.cjs +0 -27
- package/dist/compiler/coco.js +0 -4
- package/dist/compiler/domain-block-utils.cjs +0 -595
- package/dist/compiler/domain-block-utils.js +0 -9
- package/dist/compiler/dtdl.cjs +0 -25
- package/dist/compiler/dtdl.js +0 -9
- package/dist/compiler/gltf-pipeline.cjs +0 -39
- package/dist/compiler/gltf-pipeline.js +0 -4
- package/dist/compiler/godot.cjs +0 -16
- package/dist/compiler/godot.js +0 -10
- package/dist/compiler/incremental.cjs +0 -30
- package/dist/compiler/incremental.js +0 -5
- package/dist/compiler/index.cjs +0 -1522
- package/dist/compiler/index.js +0 -1321
- package/dist/compiler/ios.cjs +0 -21
- package/dist/compiler/ios.js +0 -11
- package/dist/compiler/multi-layer.cjs +0 -27
- package/dist/compiler/multi-layer.js +0 -12
- package/dist/compiler/nodetoy.cjs +0 -23
- package/dist/compiler/nodetoy.js +0 -4
- package/dist/compiler/openxr.cjs +0 -16
- package/dist/compiler/openxr.js +0 -10
- package/dist/compiler/playcanvas.cjs +0 -17
- package/dist/compiler/playcanvas.js +0 -11
- package/dist/compiler/r3f.cjs +0 -35
- package/dist/compiler/r3f.js +0 -13
- package/dist/compiler/remotion.cjs +0 -23
- package/dist/compiler/remotion.js +0 -4
- package/dist/compiler/reproducibility.cjs +0 -39
- package/dist/compiler/reproducibility.js +0 -4
- package/dist/compiler/sdf.cjs +0 -22
- package/dist/compiler/sdf.js +0 -10
- package/dist/compiler/semantic-scene.cjs +0 -31
- package/dist/compiler/semantic-scene.js +0 -4
- package/dist/compiler/state.cjs +0 -18
- package/dist/compiler/state.js +0 -8
- package/dist/compiler/trait-composition.cjs +0 -27
- package/dist/compiler/trait-composition.js +0 -6
- package/dist/compiler/unity.cjs +0 -16
- package/dist/compiler/unity.js +0 -10
- package/dist/compiler/unreal.cjs +0 -20
- package/dist/compiler/unreal.js +0 -10
- package/dist/compiler/urdf.cjs +0 -46
- package/dist/compiler/urdf.js +0 -10
- package/dist/compiler/usd-physics.cjs +0 -24
- package/dist/compiler/usd-physics.js +0 -7
- package/dist/compiler/visionos.cjs +0 -16
- package/dist/compiler/visionos.js +0 -10
- package/dist/compiler/vrchat.cjs +0 -20
- package/dist/compiler/vrchat.js +0 -10
- package/dist/compiler/vrr.cjs +0 -22
- package/dist/compiler/vrr.js +0 -10
- package/dist/compiler/wasm.cjs +0 -34
- package/dist/compiler/wasm.js +0 -10
- package/dist/compiler/webgpu.cjs +0 -16
- package/dist/compiler/webgpu.js +0 -10
- package/dist/debugger.cjs +0 -21
- package/dist/debugger.js +0 -8
- package/dist/entries/interop.cjs +0 -62
- package/dist/entries/interop.js +0 -5
- package/dist/entries/scripting.cjs +0 -67
- package/dist/entries/scripting.js +0 -10
- package/dist/index.cjs +0 -61703
- package/dist/index.js +0 -58652
- package/dist/math/vec3.cjs +0 -57
- package/dist/math/vec3.js +0 -4
- package/dist/parser.cjs +0 -26
- package/dist/parser.js +0 -9
- package/dist/playwright-7DTEQCBD.cjs +0 -98673
- package/dist/playwright-BIZXMLD2.js +0 -98660
- package/dist/post-quantum-JTTAAGO3.cjs +0 -6
- package/dist/post-quantum-RVPVDEPI.js +0 -4
- package/dist/runtime.cjs +0 -16
- package/dist/runtime.js +0 -7
- package/dist/storage/index.cjs +0 -37
- package/dist/storage/index.js +0 -4
- package/dist/traits/index.cjs +0 -14331
- package/dist/traits/index.js +0 -12126
- package/dist/type-checker.cjs +0 -18
- package/dist/type-checker.js +0 -5
- package/dist/wot/index.cjs +0 -29
- package/dist/wot/index.js +0 -4
package/dist/chunk-7H5UNJZD.cjs
DELETED
|
@@ -1,1973 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunkAIUXRS74_cjs = require('./chunk-AIUXRS74.cjs');
|
|
4
|
-
var crypto = require('crypto');
|
|
5
|
-
|
|
6
|
-
// src/compiler/TraitDependencyGraph.ts
|
|
7
|
-
function hashConfig(config) {
|
|
8
|
-
const keys = Object.keys(config).sort();
|
|
9
|
-
const parts = [];
|
|
10
|
-
for (const key of keys) {
|
|
11
|
-
const value = config[key];
|
|
12
|
-
parts.push(`${key}:${JSON.stringify(value)}`);
|
|
13
|
-
}
|
|
14
|
-
const str = parts.join("|");
|
|
15
|
-
let hash = 5381;
|
|
16
|
-
for (let i = 0; i < str.length; i++) {
|
|
17
|
-
hash = (hash << 5) + hash ^ str.charCodeAt(i);
|
|
18
|
-
}
|
|
19
|
-
return (hash >>> 0).toString(36);
|
|
20
|
-
}
|
|
21
|
-
var TraitDependencyGraph = class _TraitDependencyGraph {
|
|
22
|
-
constructor() {
|
|
23
|
-
/** Trait → required traits (what this trait depends on) */
|
|
24
|
-
this.traitDependencies = /* @__PURE__ */ new Map();
|
|
25
|
-
/** Trait → conflicting traits */
|
|
26
|
-
this.traitConflicts = /* @__PURE__ */ new Map();
|
|
27
|
-
/** Object → trait info (current state) */
|
|
28
|
-
this.objectTraits = /* @__PURE__ */ new Map();
|
|
29
|
-
/** Trait → objects using this trait */
|
|
30
|
-
this.traitToObjects = /* @__PURE__ */ new Map();
|
|
31
|
-
/** Source → objects defined in this source */
|
|
32
|
-
this.sourceToObjects = /* @__PURE__ */ new Map();
|
|
33
|
-
/** Template → objects extending this template */
|
|
34
|
-
this.templateToObjects = /* @__PURE__ */ new Map();
|
|
35
|
-
/** Previous state for diff comparison */
|
|
36
|
-
this.previousObjectTraits = /* @__PURE__ */ new Map();
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// IMPORT GRAPH — cross-file dependency edges
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
/**
|
|
41
|
-
* File → set of files it directly imports.
|
|
42
|
-
* Edge: `importer → importee`
|
|
43
|
-
* e.g., 'a.hs' → { 'b.hs', 'c.hs' }
|
|
44
|
-
*/
|
|
45
|
-
this.importEdges = /* @__PURE__ */ new Map();
|
|
46
|
-
/**
|
|
47
|
-
* Reverse map: file → set of files that import it.
|
|
48
|
-
* Edge: `importee → importers`
|
|
49
|
-
* e.g., 'b.hs' → { 'a.hs', 'd.hs' }
|
|
50
|
-
*/
|
|
51
|
-
this.reverseImportEdges = /* @__PURE__ */ new Map();
|
|
52
|
-
}
|
|
53
|
-
// ===========================================================================
|
|
54
|
-
// TRAIT REGISTRATION
|
|
55
|
-
// ===========================================================================
|
|
56
|
-
/**
|
|
57
|
-
* Register a trait definition with its dependencies and conflicts
|
|
58
|
-
*/
|
|
59
|
-
registerTrait(definition) {
|
|
60
|
-
const { name, requires, conflicts } = definition;
|
|
61
|
-
if (!this.traitDependencies.has(name)) {
|
|
62
|
-
this.traitDependencies.set(name, /* @__PURE__ */ new Set());
|
|
63
|
-
}
|
|
64
|
-
for (const req of requires) {
|
|
65
|
-
this.traitDependencies.get(name).add(req);
|
|
66
|
-
}
|
|
67
|
-
if (!this.traitConflicts.has(name)) {
|
|
68
|
-
this.traitConflicts.set(name, /* @__PURE__ */ new Set());
|
|
69
|
-
}
|
|
70
|
-
for (const conflict of conflicts) {
|
|
71
|
-
this.traitConflicts.get(name).add(conflict);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Register common VR trait dependencies
|
|
76
|
-
*/
|
|
77
|
-
registerBuiltinTraits() {
|
|
78
|
-
this.registerTrait({
|
|
79
|
-
name: "physics",
|
|
80
|
-
requires: [],
|
|
81
|
-
conflicts: ["static"]
|
|
82
|
-
});
|
|
83
|
-
this.registerTrait({
|
|
84
|
-
name: "static",
|
|
85
|
-
requires: [],
|
|
86
|
-
conflicts: ["physics", "kinematic"]
|
|
87
|
-
});
|
|
88
|
-
this.registerTrait({
|
|
89
|
-
name: "kinematic",
|
|
90
|
-
requires: [],
|
|
91
|
-
conflicts: ["static"]
|
|
92
|
-
});
|
|
93
|
-
this.registerTrait({
|
|
94
|
-
name: "collidable",
|
|
95
|
-
requires: [],
|
|
96
|
-
conflicts: []
|
|
97
|
-
});
|
|
98
|
-
this.registerTrait({
|
|
99
|
-
name: "grabbable",
|
|
100
|
-
requires: ["collidable"],
|
|
101
|
-
conflicts: []
|
|
102
|
-
});
|
|
103
|
-
this.registerTrait({
|
|
104
|
-
name: "throwable",
|
|
105
|
-
requires: ["grabbable"],
|
|
106
|
-
conflicts: []
|
|
107
|
-
});
|
|
108
|
-
this.registerTrait({
|
|
109
|
-
name: "draggable",
|
|
110
|
-
requires: ["collidable"],
|
|
111
|
-
conflicts: []
|
|
112
|
-
});
|
|
113
|
-
this.registerTrait({
|
|
114
|
-
name: "visible",
|
|
115
|
-
requires: [],
|
|
116
|
-
conflicts: ["invisible"]
|
|
117
|
-
});
|
|
118
|
-
this.registerTrait({
|
|
119
|
-
name: "invisible",
|
|
120
|
-
requires: [],
|
|
121
|
-
conflicts: ["visible"]
|
|
122
|
-
});
|
|
123
|
-
this.registerTrait({
|
|
124
|
-
name: "audio",
|
|
125
|
-
requires: [],
|
|
126
|
-
conflicts: []
|
|
127
|
-
});
|
|
128
|
-
this.registerTrait({
|
|
129
|
-
name: "spatial_audio",
|
|
130
|
-
requires: ["audio"],
|
|
131
|
-
conflicts: []
|
|
132
|
-
});
|
|
133
|
-
this.registerTrait({
|
|
134
|
-
name: "networked",
|
|
135
|
-
requires: [],
|
|
136
|
-
conflicts: []
|
|
137
|
-
});
|
|
138
|
-
this.registerTrait({
|
|
139
|
-
name: "lobby",
|
|
140
|
-
requires: [],
|
|
141
|
-
conflicts: []
|
|
142
|
-
});
|
|
143
|
-
this.registerTrait({
|
|
144
|
-
name: "crdt-room",
|
|
145
|
-
requires: [],
|
|
146
|
-
conflicts: ["lobby"]
|
|
147
|
-
// crdt-room replaces lobby-based room management
|
|
148
|
-
});
|
|
149
|
-
this.registerTrait({
|
|
150
|
-
name: "crdt-room.entity",
|
|
151
|
-
requires: ["crdt-room"],
|
|
152
|
-
conflicts: []
|
|
153
|
-
});
|
|
154
|
-
this.registerTrait({
|
|
155
|
-
name: "cultural_profile",
|
|
156
|
-
requires: [],
|
|
157
|
-
conflicts: []
|
|
158
|
-
});
|
|
159
|
-
this.registerTrait({
|
|
160
|
-
name: "norm_compliant",
|
|
161
|
-
requires: ["cultural_profile"],
|
|
162
|
-
conflicts: []
|
|
163
|
-
});
|
|
164
|
-
this.registerTrait({
|
|
165
|
-
name: "cultural_memory",
|
|
166
|
-
requires: ["cultural_profile"],
|
|
167
|
-
conflicts: []
|
|
168
|
-
});
|
|
169
|
-
this.registerTrait({
|
|
170
|
-
name: "cultural_trace",
|
|
171
|
-
requires: [],
|
|
172
|
-
conflicts: []
|
|
173
|
-
});
|
|
174
|
-
this.registerV6Traits();
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Register v6 universal service/data/pipeline/container/resilience traits
|
|
178
|
-
*/
|
|
179
|
-
registerV6Traits() {
|
|
180
|
-
this.registerTrait({ name: "service", requires: [], conflicts: [] });
|
|
181
|
-
this.registerTrait({ name: "endpoint", requires: ["service"], conflicts: [] });
|
|
182
|
-
this.registerTrait({ name: "handler", requires: ["http"], conflicts: [] });
|
|
183
|
-
this.registerTrait({ name: "middleware", requires: ["service"], conflicts: [] });
|
|
184
|
-
this.registerTrait({ name: "http", requires: [], conflicts: [] });
|
|
185
|
-
this.registerTrait({ name: "gateway", requires: [], conflicts: ["proxy"] });
|
|
186
|
-
this.registerTrait({ name: "proxy", requires: [], conflicts: ["gateway"] });
|
|
187
|
-
this.registerTrait({ name: "db", requires: [], conflicts: [] });
|
|
188
|
-
this.registerTrait({ name: "data_model", requires: ["db"], conflicts: [] });
|
|
189
|
-
this.registerTrait({ name: "migration", requires: ["db"], conflicts: [] });
|
|
190
|
-
this.registerTrait({ name: "query", requires: ["db"], conflicts: [] });
|
|
191
|
-
this.registerTrait({ name: "cache", requires: [], conflicts: [] });
|
|
192
|
-
this.registerTrait({ name: "pipeline", requires: [], conflicts: [] });
|
|
193
|
-
this.registerTrait({ name: "stream", requires: ["pipeline"], conflicts: [] });
|
|
194
|
-
this.registerTrait({ name: "queue", requires: [], conflicts: [] });
|
|
195
|
-
this.registerTrait({ name: "worker", requires: ["pipeline"], conflicts: ["scheduler"] });
|
|
196
|
-
this.registerTrait({ name: "scheduler", requires: [], conflicts: ["worker"] });
|
|
197
|
-
this.registerTrait({ name: "container", requires: [], conflicts: ["serverless"] });
|
|
198
|
-
this.registerTrait({ name: "serverless", requires: [], conflicts: ["container"] });
|
|
199
|
-
this.registerTrait({ name: "deployment", requires: [], conflicts: [] });
|
|
200
|
-
this.registerTrait({ name: "metric", requires: [], conflicts: [] });
|
|
201
|
-
this.registerTrait({ name: "trace", requires: ["service"], conflicts: [] });
|
|
202
|
-
this.registerTrait({ name: "health_check", requires: ["service"], conflicts: [] });
|
|
203
|
-
this.registerTrait({ name: "log", requires: [], conflicts: [] });
|
|
204
|
-
this.registerTrait({ name: "circuit_breaker", requires: ["service"], conflicts: [] });
|
|
205
|
-
this.registerTrait({ name: "retry", requires: ["service"], conflicts: [] });
|
|
206
|
-
this.registerTrait({ name: "timeout", requires: ["service"], conflicts: [] });
|
|
207
|
-
this.registerTrait({ name: "fallback", requires: [], conflicts: [] });
|
|
208
|
-
this.registerTrait({ name: "bulkhead", requires: ["service"], conflicts: [] });
|
|
209
|
-
this.registerTrait({ name: "schema", requires: [], conflicts: [] });
|
|
210
|
-
this.registerTrait({ name: "validator", requires: ["schema"], conflicts: [] });
|
|
211
|
-
this.registerTrait({ name: "serializer", requires: ["schema"], conflicts: [] });
|
|
212
|
-
this.registerTrait({ name: "contract", requires: [], conflicts: [] });
|
|
213
|
-
}
|
|
214
|
-
// ===========================================================================
|
|
215
|
-
// IMPORT EDGE TRACKING (@import cross-file dependencies)
|
|
216
|
-
// ===========================================================================
|
|
217
|
-
/**
|
|
218
|
-
* Register a cross-file @import relationship.
|
|
219
|
-
*
|
|
220
|
-
* Call this once per `@import` directive after parsing.
|
|
221
|
-
* Idempotent — registering the same edge twice is safe.
|
|
222
|
-
*
|
|
223
|
-
* @param importingFile Canonical path of the file that contains `@import`
|
|
224
|
-
* @param importedFile Canonical path of the file being imported
|
|
225
|
-
*/
|
|
226
|
-
registerImport(importingFile, importedFile) {
|
|
227
|
-
if (!this.importEdges.has(importingFile)) {
|
|
228
|
-
this.importEdges.set(importingFile, /* @__PURE__ */ new Set());
|
|
229
|
-
}
|
|
230
|
-
this.importEdges.get(importingFile).add(importedFile);
|
|
231
|
-
if (!this.reverseImportEdges.has(importedFile)) {
|
|
232
|
-
this.reverseImportEdges.set(importedFile, /* @__PURE__ */ new Set());
|
|
233
|
-
}
|
|
234
|
-
this.reverseImportEdges.get(importedFile).add(importingFile);
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Remove all import edges for a file (call before re-registering after a parse).
|
|
238
|
-
* Cleans both the forward edge and the corresponding reverse entries.
|
|
239
|
-
*/
|
|
240
|
-
clearImportsForFile(importingFile) {
|
|
241
|
-
const imported = this.importEdges.get(importingFile);
|
|
242
|
-
if (!imported) return;
|
|
243
|
-
for (const dep of imported) {
|
|
244
|
-
this.reverseImportEdges.get(dep)?.delete(importingFile);
|
|
245
|
-
}
|
|
246
|
-
this.importEdges.delete(importingFile);
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Return the set of files that `sourceFile` directly imports.
|
|
250
|
-
*/
|
|
251
|
-
getImportedFiles(sourceFile) {
|
|
252
|
-
return new Set(this.importEdges.get(sourceFile) ?? []);
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Return the set of files that directly import `sourceFile`.
|
|
256
|
-
*/
|
|
257
|
-
getFilesThatImport(sourceFile) {
|
|
258
|
-
return new Set(this.reverseImportEdges.get(sourceFile) ?? []);
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Walk the reverse-import graph (BFS) to compute the full set of files that
|
|
262
|
-
* need to be recompiled when `changedFiles` are modified.
|
|
263
|
-
*
|
|
264
|
-
* Includes the changed files themselves and all direct/transitive importers.
|
|
265
|
-
*
|
|
266
|
-
* @param changedFiles Canonical paths of the files that changed on disk
|
|
267
|
-
* @returns Set of canonical paths that need recompilation
|
|
268
|
-
*/
|
|
269
|
-
getFilesAffectedByChange(changedFiles) {
|
|
270
|
-
const affected = new Set(changedFiles);
|
|
271
|
-
const queue = [...changedFiles];
|
|
272
|
-
while (queue.length > 0) {
|
|
273
|
-
const current = queue.shift();
|
|
274
|
-
const importers = this.reverseImportEdges.get(current);
|
|
275
|
-
if (!importers) continue;
|
|
276
|
-
for (const importer of importers) {
|
|
277
|
-
if (!affected.has(importer)) {
|
|
278
|
-
affected.add(importer);
|
|
279
|
-
queue.push(importer);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
return affected;
|
|
284
|
-
}
|
|
285
|
-
// ===========================================================================
|
|
286
|
-
// OBJECT TRACKING
|
|
287
|
-
// ===========================================================================
|
|
288
|
-
/**
|
|
289
|
-
* Register an object and its trait usage
|
|
290
|
-
*/
|
|
291
|
-
registerObject(info) {
|
|
292
|
-
const { objectName, sourceId, traits, template } = info;
|
|
293
|
-
const traitsWithHashes = traits.map((t) => ({
|
|
294
|
-
...t,
|
|
295
|
-
configHash: t.configHash || hashConfig(t.config)
|
|
296
|
-
}));
|
|
297
|
-
const fullInfo = {
|
|
298
|
-
...info,
|
|
299
|
-
traits: traitsWithHashes
|
|
300
|
-
};
|
|
301
|
-
this.objectTraits.set(objectName, fullInfo);
|
|
302
|
-
for (const trait of traitsWithHashes) {
|
|
303
|
-
if (!this.traitToObjects.has(trait.name)) {
|
|
304
|
-
this.traitToObjects.set(trait.name, /* @__PURE__ */ new Set());
|
|
305
|
-
}
|
|
306
|
-
this.traitToObjects.get(trait.name).add(objectName);
|
|
307
|
-
}
|
|
308
|
-
if (!this.sourceToObjects.has(sourceId)) {
|
|
309
|
-
this.sourceToObjects.set(sourceId, /* @__PURE__ */ new Set());
|
|
310
|
-
}
|
|
311
|
-
this.sourceToObjects.get(sourceId).add(objectName);
|
|
312
|
-
if (template) {
|
|
313
|
-
if (!this.templateToObjects.has(template)) {
|
|
314
|
-
this.templateToObjects.set(template, /* @__PURE__ */ new Set());
|
|
315
|
-
}
|
|
316
|
-
this.templateToObjects.get(template).add(objectName);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Remove an object from tracking
|
|
321
|
-
*/
|
|
322
|
-
unregisterObject(objectName) {
|
|
323
|
-
const info = this.objectTraits.get(objectName);
|
|
324
|
-
if (!info) return;
|
|
325
|
-
for (const trait of info.traits) {
|
|
326
|
-
this.traitToObjects.get(trait.name)?.delete(objectName);
|
|
327
|
-
}
|
|
328
|
-
this.sourceToObjects.get(info.sourceId)?.delete(objectName);
|
|
329
|
-
if (info.template) {
|
|
330
|
-
this.templateToObjects.get(info.template)?.delete(objectName);
|
|
331
|
-
}
|
|
332
|
-
this.objectTraits.delete(objectName);
|
|
333
|
-
}
|
|
334
|
-
// ===========================================================================
|
|
335
|
-
// CHANGE DETECTION
|
|
336
|
-
// ===========================================================================
|
|
337
|
-
/**
|
|
338
|
-
* Save current state for comparison
|
|
339
|
-
*/
|
|
340
|
-
saveSnapshot() {
|
|
341
|
-
this.previousObjectTraits = new Map(
|
|
342
|
-
Array.from(this.objectTraits.entries()).map(([k, v]) => [
|
|
343
|
-
k,
|
|
344
|
-
{
|
|
345
|
-
...v,
|
|
346
|
-
traits: [...v.traits]
|
|
347
|
-
}
|
|
348
|
-
])
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Detect trait changes for an object
|
|
353
|
-
*/
|
|
354
|
-
detectTraitChanges(objectName, newTraits) {
|
|
355
|
-
const changes = [];
|
|
356
|
-
const oldInfo = this.previousObjectTraits.get(objectName);
|
|
357
|
-
const newTraitsWithHashes = newTraits.map((t) => ({
|
|
358
|
-
...t,
|
|
359
|
-
configHash: t.configHash || hashConfig(t.config)
|
|
360
|
-
}));
|
|
361
|
-
const oldTraitMap = /* @__PURE__ */ new Map();
|
|
362
|
-
const newTraitMap = /* @__PURE__ */ new Map();
|
|
363
|
-
if (oldInfo) {
|
|
364
|
-
for (const t of oldInfo.traits) {
|
|
365
|
-
oldTraitMap.set(t.name, t);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
for (const t of newTraitsWithHashes) {
|
|
369
|
-
newTraitMap.set(t.name, t);
|
|
370
|
-
}
|
|
371
|
-
for (const [name, newTrait] of newTraitMap) {
|
|
372
|
-
if (!oldTraitMap.has(name)) {
|
|
373
|
-
changes.push({
|
|
374
|
-
traitName: name,
|
|
375
|
-
changeType: "added",
|
|
376
|
-
newConfigHash: newTrait.configHash
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
for (const [name, oldTrait] of oldTraitMap) {
|
|
381
|
-
if (!newTraitMap.has(name)) {
|
|
382
|
-
changes.push({
|
|
383
|
-
traitName: name,
|
|
384
|
-
changeType: "removed",
|
|
385
|
-
oldConfigHash: oldTrait.configHash
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
for (const [name, newTrait] of newTraitMap) {
|
|
390
|
-
const oldTrait = oldTraitMap.get(name);
|
|
391
|
-
if (oldTrait && oldTrait.configHash !== newTrait.configHash) {
|
|
392
|
-
changes.push({
|
|
393
|
-
traitName: name,
|
|
394
|
-
changeType: "config_changed",
|
|
395
|
-
oldConfigHash: oldTrait.configHash,
|
|
396
|
-
newConfigHash: newTrait.configHash
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return changes;
|
|
401
|
-
}
|
|
402
|
-
// ===========================================================================
|
|
403
|
-
// AFFECTED SET CALCULATION
|
|
404
|
-
// ===========================================================================
|
|
405
|
-
/**
|
|
406
|
-
* Get all objects using a specific trait
|
|
407
|
-
*/
|
|
408
|
-
getObjectsUsingTrait(traitName) {
|
|
409
|
-
return new Set(this.traitToObjects.get(traitName) || []);
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Get all traits that depend on a given trait
|
|
413
|
-
*/
|
|
414
|
-
getDependentTraits(traitName) {
|
|
415
|
-
const dependents = /* @__PURE__ */ new Set();
|
|
416
|
-
for (const [trait, deps] of this.traitDependencies) {
|
|
417
|
-
if (deps.has(traitName)) {
|
|
418
|
-
dependents.add(trait);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return dependents;
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Calculate the affected set when traits change
|
|
425
|
-
*/
|
|
426
|
-
calculateAffectedSet(changes) {
|
|
427
|
-
const objects = /* @__PURE__ */ new Set();
|
|
428
|
-
const sources = /* @__PURE__ */ new Set();
|
|
429
|
-
const reasons = /* @__PURE__ */ new Map();
|
|
430
|
-
for (const change of changes) {
|
|
431
|
-
const { traitName, changeType } = change;
|
|
432
|
-
const directUsers = this.getObjectsUsingTrait(traitName);
|
|
433
|
-
for (const obj of directUsers) {
|
|
434
|
-
objects.add(obj);
|
|
435
|
-
reasons.set(obj, `Uses trait @${traitName} (${changeType})`);
|
|
436
|
-
const info = this.objectTraits.get(obj);
|
|
437
|
-
if (info) {
|
|
438
|
-
sources.add(info.sourceId);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
const dependentTraits = this.getDependentTraits(traitName);
|
|
442
|
-
for (const depTrait of dependentTraits) {
|
|
443
|
-
const depUsers = this.getObjectsUsingTrait(depTrait);
|
|
444
|
-
for (const obj of depUsers) {
|
|
445
|
-
if (!objects.has(obj)) {
|
|
446
|
-
objects.add(obj);
|
|
447
|
-
reasons.set(obj, `Uses trait @${depTrait} which depends on @${traitName}`);
|
|
448
|
-
const info = this.objectTraits.get(obj);
|
|
449
|
-
if (info) {
|
|
450
|
-
sources.add(info.sourceId);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
return { objects, sources, reasons };
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Calculate minimal recompilation set for changed objects
|
|
460
|
-
* Considers trait dependencies and template inheritance
|
|
461
|
-
*/
|
|
462
|
-
calculateRecompilationSet(changedObjects) {
|
|
463
|
-
const toRecompile = new Set(changedObjects);
|
|
464
|
-
const queue = [...changedObjects];
|
|
465
|
-
while (queue.length > 0) {
|
|
466
|
-
const current = queue.shift();
|
|
467
|
-
const info = this.objectTraits.get(current);
|
|
468
|
-
if (!info) continue;
|
|
469
|
-
const extending = this.templateToObjects.get(current);
|
|
470
|
-
if (extending) {
|
|
471
|
-
for (const obj of extending) {
|
|
472
|
-
if (!toRecompile.has(obj)) {
|
|
473
|
-
toRecompile.add(obj);
|
|
474
|
-
queue.push(obj);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return toRecompile;
|
|
480
|
-
}
|
|
481
|
-
// ===========================================================================
|
|
482
|
-
// SERIALIZATION (for cache persistence)
|
|
483
|
-
// ===========================================================================
|
|
484
|
-
/**
|
|
485
|
-
* Serialize the graph for persistence
|
|
486
|
-
*/
|
|
487
|
-
serialize() {
|
|
488
|
-
const data = {
|
|
489
|
-
version: 2,
|
|
490
|
-
traitDependencies: Array.from(this.traitDependencies.entries()).map(([k, v]) => [
|
|
491
|
-
k,
|
|
492
|
-
Array.from(v)
|
|
493
|
-
]),
|
|
494
|
-
traitConflicts: Array.from(this.traitConflicts.entries()).map(([k, v]) => [k, Array.from(v)]),
|
|
495
|
-
objectTraits: Array.from(this.objectTraits.entries()),
|
|
496
|
-
importEdges: Array.from(this.importEdges.entries()).map(([k, v]) => [k, Array.from(v)]),
|
|
497
|
-
timestamp: Date.now()
|
|
498
|
-
};
|
|
499
|
-
return JSON.stringify(data);
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Deserialize the graph from persisted data
|
|
503
|
-
*/
|
|
504
|
-
static deserialize(json) {
|
|
505
|
-
const graph = new _TraitDependencyGraph();
|
|
506
|
-
const data = JSON.parse(json);
|
|
507
|
-
if (data.version !== 1 && data.version !== 2) {
|
|
508
|
-
throw new Error(`Unsupported trait graph version: ${data.version}`);
|
|
509
|
-
}
|
|
510
|
-
for (const [trait, deps] of data.traitDependencies) {
|
|
511
|
-
graph.traitDependencies.set(trait, new Set(deps));
|
|
512
|
-
}
|
|
513
|
-
for (const [trait, conflicts] of data.traitConflicts) {
|
|
514
|
-
graph.traitConflicts.set(trait, new Set(conflicts));
|
|
515
|
-
}
|
|
516
|
-
for (const [_name, info] of data.objectTraits) {
|
|
517
|
-
graph.registerObject(info);
|
|
518
|
-
}
|
|
519
|
-
if (data.importEdges) {
|
|
520
|
-
for (const [importer, imported] of data.importEdges) {
|
|
521
|
-
for (const dep of imported) {
|
|
522
|
-
graph.registerImport(importer, dep);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return graph;
|
|
527
|
-
}
|
|
528
|
-
// ===========================================================================
|
|
529
|
-
// STATS & DEBUGGING
|
|
530
|
-
// ===========================================================================
|
|
531
|
-
/**
|
|
532
|
-
* Get graph statistics
|
|
533
|
-
*/
|
|
534
|
-
getStats() {
|
|
535
|
-
let dependencyEdges = 0;
|
|
536
|
-
for (const deps of this.traitDependencies.values()) {
|
|
537
|
-
dependencyEdges += deps.size;
|
|
538
|
-
}
|
|
539
|
-
let importEdges = 0;
|
|
540
|
-
for (const deps of this.importEdges.values()) {
|
|
541
|
-
importEdges += deps.size;
|
|
542
|
-
}
|
|
543
|
-
return {
|
|
544
|
-
traitCount: this.traitDependencies.size,
|
|
545
|
-
objectCount: this.objectTraits.size,
|
|
546
|
-
sourceCount: this.sourceToObjects.size,
|
|
547
|
-
dependencyEdges,
|
|
548
|
-
importEdges
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* Clear all data
|
|
553
|
-
*/
|
|
554
|
-
clear() {
|
|
555
|
-
this.traitDependencies.clear();
|
|
556
|
-
this.traitConflicts.clear();
|
|
557
|
-
this.objectTraits.clear();
|
|
558
|
-
this.traitToObjects.clear();
|
|
559
|
-
this.sourceToObjects.clear();
|
|
560
|
-
this.templateToObjects.clear();
|
|
561
|
-
this.previousObjectTraits.clear();
|
|
562
|
-
this.importEdges.clear();
|
|
563
|
-
this.reverseImportEdges.clear();
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
var globalTraitGraph = new TraitDependencyGraph();
|
|
567
|
-
globalTraitGraph.registerBuiltinTraits();
|
|
568
|
-
|
|
569
|
-
// src/parser/ImportResolver.ts
|
|
570
|
-
function resolveImportPath(importPath, baseDir) {
|
|
571
|
-
importPath = importPath.replace(/\\/g, "/");
|
|
572
|
-
baseDir = baseDir.replace(/\\/g, "/");
|
|
573
|
-
if (importPath.startsWith("/") || /^[A-Za-z]:/.test(importPath)) {
|
|
574
|
-
return importPath;
|
|
575
|
-
}
|
|
576
|
-
const baseResolved = normalizePath(baseDir);
|
|
577
|
-
const base = baseResolved.replace(/\/$/, "");
|
|
578
|
-
const combined = base + "/" + importPath;
|
|
579
|
-
return normalizePath(combined);
|
|
580
|
-
}
|
|
581
|
-
function normalizePath(p) {
|
|
582
|
-
const isAbsolute = p.startsWith("/");
|
|
583
|
-
const parts = p.split("/");
|
|
584
|
-
const resolved = [];
|
|
585
|
-
for (const part of parts) {
|
|
586
|
-
if (part === "" || part === ".") {
|
|
587
|
-
if (resolved.length === 0 && part === "" && isAbsolute) {
|
|
588
|
-
resolved.push("");
|
|
589
|
-
}
|
|
590
|
-
continue;
|
|
591
|
-
}
|
|
592
|
-
if (part === "..") {
|
|
593
|
-
if (resolved.length > (isAbsolute ? 1 : 0)) {
|
|
594
|
-
resolved.pop();
|
|
595
|
-
}
|
|
596
|
-
} else {
|
|
597
|
-
resolved.push(part);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
let result = resolved.join("/");
|
|
601
|
-
if (isAbsolute && !result.startsWith("/")) result = "/" + result;
|
|
602
|
-
return result || "/";
|
|
603
|
-
}
|
|
604
|
-
var REGISTRY_IMPORT_RE = /^@[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+$/;
|
|
605
|
-
function isRegistryImport(importPath) {
|
|
606
|
-
return REGISTRY_IMPORT_RE.test(importPath);
|
|
607
|
-
}
|
|
608
|
-
function isCrdtImport(importPath) {
|
|
609
|
-
return importPath.startsWith("crdt://");
|
|
610
|
-
}
|
|
611
|
-
function parseRegistryImport(importPath) {
|
|
612
|
-
if (!isRegistryImport(importPath)) return null;
|
|
613
|
-
const parts = importPath.slice(1).split("/");
|
|
614
|
-
return { username: parts[0], name: parts[1] };
|
|
615
|
-
}
|
|
616
|
-
var ImportResolver = class {
|
|
617
|
-
constructor() {
|
|
618
|
-
/** Already-resolved modules keyed by canonical path */
|
|
619
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
620
|
-
/** Paths currently being resolved (DFS cycle detection) */
|
|
621
|
-
this.inProgress = /* @__PURE__ */ new Set();
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Resolve all @import directives from a parsed result and return a merged scope.
|
|
625
|
-
*
|
|
626
|
-
* @param result - The top-level parse result (from HoloScriptPlusParser.parse)
|
|
627
|
-
* @param sourceFile - Canonical path of the file that produced `result`
|
|
628
|
-
* @param options - Resolution options
|
|
629
|
-
*/
|
|
630
|
-
async resolve(result, sourceFile, options) {
|
|
631
|
-
if (options.disabled) {
|
|
632
|
-
return { scope: /* @__PURE__ */ new Map(), modules: /* @__PURE__ */ new Map(), errors: [] };
|
|
633
|
-
}
|
|
634
|
-
const scope = /* @__PURE__ */ new Map();
|
|
635
|
-
const modules = /* @__PURE__ */ new Map();
|
|
636
|
-
const errors = [];
|
|
637
|
-
const imports = result.ast?.imports ?? [];
|
|
638
|
-
if (imports.length === 0) {
|
|
639
|
-
return { scope, modules, errors };
|
|
640
|
-
}
|
|
641
|
-
const baseDir = options.baseDir.replace(/\\/g, "/");
|
|
642
|
-
for (const imp of imports) {
|
|
643
|
-
const canonicalPath = isRegistryImport(imp.path) || isCrdtImport(imp.path) ? imp.path : resolveImportPath(imp.path, baseDir);
|
|
644
|
-
let mod;
|
|
645
|
-
try {
|
|
646
|
-
mod = await this._resolveModule(canonicalPath, sourceFile, options, 0, errors);
|
|
647
|
-
} catch (err) {
|
|
648
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
649
|
-
errors.push({
|
|
650
|
-
importPath: imp.path,
|
|
651
|
-
message: msg,
|
|
652
|
-
code: msg.startsWith("Circular") ? "cycle" : msg.startsWith("Max depth") ? "max_depth" : msg.startsWith("Parse error") ? "parse_error" : msg.startsWith("Registry") ? "registry_unavailable" : "not_found",
|
|
653
|
-
...msg.startsWith("Circular") ? { cycle: [canonicalPath] } : {}
|
|
654
|
-
});
|
|
655
|
-
continue;
|
|
656
|
-
}
|
|
657
|
-
modules.set(canonicalPath, mod);
|
|
658
|
-
if (imp.namedImports && imp.namedImports.length > 0) {
|
|
659
|
-
for (const named of imp.namedImports) {
|
|
660
|
-
const node = mod.exports.get(named);
|
|
661
|
-
if (!node) {
|
|
662
|
-
errors.push({
|
|
663
|
-
importPath: imp.path,
|
|
664
|
-
message: `'${named}' is not exported from '${imp.path}'`,
|
|
665
|
-
code: "named_not_exported"
|
|
666
|
-
});
|
|
667
|
-
} else {
|
|
668
|
-
scope.set(named, node);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
} else if (imp.isWildcard) {
|
|
672
|
-
for (const [exportName, node] of mod.exports) {
|
|
673
|
-
scope.set(`${imp.alias}.${exportName}`, node);
|
|
674
|
-
}
|
|
675
|
-
} else {
|
|
676
|
-
for (const [exportName, node] of mod.exports) {
|
|
677
|
-
scope.set(`${imp.alias}.${exportName}`, node);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
return { scope, modules, errors };
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Recursively resolve a single module file.
|
|
685
|
-
* Cycle detection: if `canonicalPath` is already in `inProgress`, throws.
|
|
686
|
-
* Transitive errors (cycles, missing, max depth) are pushed to `errors` array.
|
|
687
|
-
*/
|
|
688
|
-
async _resolveModule(canonicalPath, importedBy, options, depth, errors) {
|
|
689
|
-
const maxDepth = options.maxDepth ?? 32;
|
|
690
|
-
if (depth > maxDepth) {
|
|
691
|
-
throw new Error(`Max depth (${maxDepth}) exceeded resolving '${canonicalPath}'`);
|
|
692
|
-
}
|
|
693
|
-
if (this.inProgress.has(canonicalPath)) {
|
|
694
|
-
const chain = [...this.inProgress, canonicalPath];
|
|
695
|
-
throw new Error(`Circular import detected: ${chain.join(" \u2192 ")}`);
|
|
696
|
-
}
|
|
697
|
-
if (this.cache.has(canonicalPath)) {
|
|
698
|
-
return this.cache.get(canonicalPath);
|
|
699
|
-
}
|
|
700
|
-
this.inProgress.add(canonicalPath);
|
|
701
|
-
try {
|
|
702
|
-
let source;
|
|
703
|
-
if (isRegistryImport(canonicalPath) && options.registryBaseUrl) {
|
|
704
|
-
try {
|
|
705
|
-
source = await this._fetchRegistrySource(canonicalPath, options.registryBaseUrl);
|
|
706
|
-
} catch {
|
|
707
|
-
throw new Error(
|
|
708
|
-
`Registry unavailable for '${canonicalPath}' (imported from '${importedBy}')`
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
} else if (isCrdtImport(canonicalPath)) {
|
|
712
|
-
try {
|
|
713
|
-
source = await this._fetchCrdtSource(canonicalPath);
|
|
714
|
-
} catch (err) {
|
|
715
|
-
throw new Error(
|
|
716
|
-
`CRDT feed unavailable for '${canonicalPath}' (imported from '${importedBy}'): ${err instanceof Error ? err.message : String(err)}`
|
|
717
|
-
);
|
|
718
|
-
}
|
|
719
|
-
} else {
|
|
720
|
-
try {
|
|
721
|
-
const reader = options.readFile ?? this._defaultReader();
|
|
722
|
-
source = await reader(canonicalPath);
|
|
723
|
-
} catch {
|
|
724
|
-
throw new Error(`File not found: '${canonicalPath}' (imported from '${importedBy}')`);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
const { HoloScriptPlusParser } = await import('./parser.cjs');
|
|
728
|
-
const parser = new HoloScriptPlusParser({ enableTypeScriptImports: true });
|
|
729
|
-
const result = parser.parse(source);
|
|
730
|
-
if (!result.success && result.errors.length > 0) {
|
|
731
|
-
throw new Error(
|
|
732
|
-
`Parse error in '${canonicalPath}': ${result.errors[0]?.message ?? "unknown error"}`
|
|
733
|
-
);
|
|
734
|
-
}
|
|
735
|
-
const exports$1 = this._extractExports(result);
|
|
736
|
-
const transitiveDeps = [];
|
|
737
|
-
const subImports = result.ast?.imports ?? [];
|
|
738
|
-
const baseDir = canonicalPath.replace(/\/[^/]+$/, "");
|
|
739
|
-
for (const subImp of subImports) {
|
|
740
|
-
const subPath = isRegistryImport(subImp.path) || isCrdtImport(subImp.path) ? subImp.path : resolveImportPath(subImp.path, baseDir);
|
|
741
|
-
transitiveDeps.push(subPath);
|
|
742
|
-
if (!this.cache.has(subPath)) {
|
|
743
|
-
try {
|
|
744
|
-
const subMod = await this._resolveModule(
|
|
745
|
-
subPath,
|
|
746
|
-
canonicalPath,
|
|
747
|
-
options,
|
|
748
|
-
depth + 1,
|
|
749
|
-
errors
|
|
750
|
-
);
|
|
751
|
-
for (const [k, v] of subMod.exports) {
|
|
752
|
-
if (!exports$1.has(k)) exports$1.set(k, v);
|
|
753
|
-
}
|
|
754
|
-
} catch (err) {
|
|
755
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
756
|
-
errors.push({
|
|
757
|
-
importPath: subImp.path,
|
|
758
|
-
message: msg,
|
|
759
|
-
code: msg.startsWith("Circular") ? "cycle" : msg.startsWith("Max depth") ? "max_depth" : msg.startsWith("Parse error") ? "parse_error" : msg.startsWith("Registry") ? "registry_unavailable" : "not_found",
|
|
760
|
-
...msg.startsWith("Circular") ? { cycle: [subPath] } : {}
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
const mod = {
|
|
766
|
-
canonicalPath,
|
|
767
|
-
result,
|
|
768
|
-
exports: exports$1,
|
|
769
|
-
dependencies: transitiveDeps
|
|
770
|
-
};
|
|
771
|
-
this.cache.set(canonicalPath, mod);
|
|
772
|
-
return mod;
|
|
773
|
-
} finally {
|
|
774
|
-
this.inProgress.delete(canonicalPath);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* Fetch native string sources from the local LoroText Holographic CRDT.
|
|
779
|
-
* This bridges the compiler directly into the decentralized HoloMesh feed.
|
|
780
|
-
*/
|
|
781
|
-
async _fetchCrdtSource(importPath) {
|
|
782
|
-
if (importPath !== "crdt://holomesh/feed") {
|
|
783
|
-
throw new Error(`Unknown CRDT namespace. Only crdt://holomesh/feed is supported.`);
|
|
784
|
-
}
|
|
785
|
-
try {
|
|
786
|
-
const fs = await import('fs/promises');
|
|
787
|
-
const path = await import('path');
|
|
788
|
-
const { LoroDoc } = await import('@loro/loro');
|
|
789
|
-
const crdtPath = path.resolve(process.cwd(), ".holomesh/worldstate.crdt");
|
|
790
|
-
const bytes = await fs.readFile(crdtPath);
|
|
791
|
-
const doc = new LoroDoc();
|
|
792
|
-
doc.import(bytes);
|
|
793
|
-
const feed = doc.getText("feed");
|
|
794
|
-
return feed.toString();
|
|
795
|
-
} catch (e) {
|
|
796
|
-
throw new Error(`CRDT Sync Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
/**
|
|
800
|
-
* Fetch source code from the community registry for @username/name imports.
|
|
801
|
-
* Calls GET {registryBaseUrl}/api/scene/{name} and returns the scene code.
|
|
802
|
-
*/
|
|
803
|
-
async _fetchRegistrySource(importPath, registryBaseUrl) {
|
|
804
|
-
const parsed = parseRegistryImport(importPath);
|
|
805
|
-
if (!parsed) {
|
|
806
|
-
throw new Error(`Invalid registry import path: '${importPath}'`);
|
|
807
|
-
}
|
|
808
|
-
const url = `${registryBaseUrl.replace(/\/$/, "")}/api/registry/${encodeURIComponent(parsed.username)}/${encodeURIComponent(parsed.name)}`;
|
|
809
|
-
const response = await fetch(url);
|
|
810
|
-
if (!response.ok) {
|
|
811
|
-
throw new Error(`Registry returned ${response.status} for '${importPath}'`);
|
|
812
|
-
}
|
|
813
|
-
const data = await response.json();
|
|
814
|
-
if (!data.code || typeof data.code !== "string") {
|
|
815
|
-
throw new Error(`Registry returned no code for '${importPath}'`);
|
|
816
|
-
}
|
|
817
|
-
return data.code;
|
|
818
|
-
}
|
|
819
|
-
/**
|
|
820
|
-
* Extract @exported nodes from a parse result.
|
|
821
|
-
*
|
|
822
|
-
* Handles two AST shapes that HoloScriptPlusParser can produce:
|
|
823
|
-
* 1. Single node file: result.ast.root IS the node; its directives contain @export
|
|
824
|
-
* 2. Fragment file: result.ast.root.children[] are nodes, each may have @export directives
|
|
825
|
-
*/
|
|
826
|
-
_extractExports(result) {
|
|
827
|
-
const exports$1 = /* @__PURE__ */ new Map();
|
|
828
|
-
const ast = result.ast;
|
|
829
|
-
if (!ast) return exports$1;
|
|
830
|
-
const root = ast.root;
|
|
831
|
-
if (!root) return exports$1;
|
|
832
|
-
const seen = /* @__PURE__ */ new Set();
|
|
833
|
-
const nodesToCheck = [];
|
|
834
|
-
const addNode = (n) => {
|
|
835
|
-
if (!n || typeof n !== "object") return;
|
|
836
|
-
const node = n;
|
|
837
|
-
if (!seen.has(node)) {
|
|
838
|
-
seen.add(node);
|
|
839
|
-
nodesToCheck.push(node);
|
|
840
|
-
}
|
|
841
|
-
};
|
|
842
|
-
addNode(root);
|
|
843
|
-
if (Array.isArray(root.children)) {
|
|
844
|
-
for (const child of root.children) addNode(child);
|
|
845
|
-
}
|
|
846
|
-
if (Array.isArray(ast.body)) {
|
|
847
|
-
for (const n of ast.body) addNode(n);
|
|
848
|
-
}
|
|
849
|
-
if (Array.isArray(ast.children)) {
|
|
850
|
-
for (const n of ast.children) addNode(n);
|
|
851
|
-
}
|
|
852
|
-
for (const node of nodesToCheck) {
|
|
853
|
-
const nodeRec = node;
|
|
854
|
-
const directives = nodeRec.directives ?? [];
|
|
855
|
-
const exportDir = directives.find((d) => d?.type === "export");
|
|
856
|
-
if (exportDir) {
|
|
857
|
-
const exportName = exportDir.exportName ?? nodeRec.id ?? nodeRec.name ?? nodeRec.type;
|
|
858
|
-
exports$1.set(exportName, node);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
return exports$1;
|
|
862
|
-
}
|
|
863
|
-
/**
|
|
864
|
-
* Default Node.js file reader. Only used on server-side / CLI.
|
|
865
|
-
* Throws in environments without the 'fs' module (browser, XR).
|
|
866
|
-
*/
|
|
867
|
-
_defaultReader() {
|
|
868
|
-
return async (filePath) => {
|
|
869
|
-
try {
|
|
870
|
-
const fs = await import('fs/promises');
|
|
871
|
-
return await fs.readFile(filePath, "utf-8");
|
|
872
|
-
} catch {
|
|
873
|
-
throw new Error(
|
|
874
|
-
`Cannot read '${filePath}': no readFile option provided and fs is unavailable`
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
/** Clear the module cache (useful between test runs) */
|
|
880
|
-
clearCache() {
|
|
881
|
-
this.cache.clear();
|
|
882
|
-
this.inProgress.clear();
|
|
883
|
-
}
|
|
884
|
-
/** Return the set of canonical paths currently cached */
|
|
885
|
-
getCachedPaths() {
|
|
886
|
-
return [...this.cache.keys()];
|
|
887
|
-
}
|
|
888
|
-
/** Return a resolved module from cache, or undefined */
|
|
889
|
-
getCached(canonicalPath) {
|
|
890
|
-
return this.cache.get(canonicalPath);
|
|
891
|
-
}
|
|
892
|
-
};
|
|
893
|
-
function hashSourceCode(source) {
|
|
894
|
-
return crypto.createHash("sha256").update(source, "utf8").digest("hex");
|
|
895
|
-
}
|
|
896
|
-
function generateCacheKey(contentHash, type, prefix) {
|
|
897
|
-
return `${prefix}:${type}:${contentHash}`;
|
|
898
|
-
}
|
|
899
|
-
function serializeASTNode(node) {
|
|
900
|
-
return JSON.stringify(node, null, 0);
|
|
901
|
-
}
|
|
902
|
-
function hashASTSubtree(node) {
|
|
903
|
-
const serialized = serializeASTNode(node);
|
|
904
|
-
return hashSourceCode(serialized);
|
|
905
|
-
}
|
|
906
|
-
var SemanticCache = class {
|
|
907
|
-
constructor(options = {}) {
|
|
908
|
-
this.redis = null;
|
|
909
|
-
this.memoryCache = /* @__PURE__ */ new Map();
|
|
910
|
-
this.stats = {
|
|
911
|
-
hits: 0,
|
|
912
|
-
misses: 0,
|
|
913
|
-
totalLatencyMs: 0,
|
|
914
|
-
lookupCount: 0
|
|
915
|
-
};
|
|
916
|
-
this.redisConnected = false;
|
|
917
|
-
this.initialized = false;
|
|
918
|
-
this.options = {
|
|
919
|
-
redisUrl: options.redisUrl ?? "redis://localhost:6379",
|
|
920
|
-
keyPrefix: options.keyPrefix ?? "holoscript:semantic",
|
|
921
|
-
ttl: options.ttl ?? 7 * 24 * 60 * 60,
|
|
922
|
-
// 7 days in seconds
|
|
923
|
-
version: options.version ?? "1.0.0",
|
|
924
|
-
debug: options.debug ?? false,
|
|
925
|
-
connectionTimeout: options.connectionTimeout ?? 5e3,
|
|
926
|
-
maxRetries: options.maxRetries ?? 3
|
|
927
|
-
};
|
|
928
|
-
}
|
|
929
|
-
/**
|
|
930
|
-
* Initialize the cache (connect to Redis if available)
|
|
931
|
-
*/
|
|
932
|
-
async initialize() {
|
|
933
|
-
if (this.initialized) return;
|
|
934
|
-
try {
|
|
935
|
-
const Redis = await this.loadRedisClient();
|
|
936
|
-
if (Redis) {
|
|
937
|
-
this.redis = new Redis(this.options.redisUrl, {
|
|
938
|
-
connectTimeout: this.options.connectionTimeout,
|
|
939
|
-
maxRetriesPerRequest: this.options.maxRetries,
|
|
940
|
-
lazyConnect: true,
|
|
941
|
-
enableOfflineQueue: false
|
|
942
|
-
});
|
|
943
|
-
this.redis.on("connect", () => {
|
|
944
|
-
this.redisConnected = true;
|
|
945
|
-
this.log("Redis connected");
|
|
946
|
-
});
|
|
947
|
-
this.redis.on("error", (error) => {
|
|
948
|
-
this.log(`Redis error: ${error.message}`);
|
|
949
|
-
this.redisConnected = false;
|
|
950
|
-
});
|
|
951
|
-
this.redis.on("close", () => {
|
|
952
|
-
this.redisConnected = false;
|
|
953
|
-
this.log("Redis disconnected");
|
|
954
|
-
});
|
|
955
|
-
await this.redis.ping();
|
|
956
|
-
this.redisConnected = true;
|
|
957
|
-
this.log("Redis cache initialized");
|
|
958
|
-
}
|
|
959
|
-
} catch (error) {
|
|
960
|
-
this.log(`Redis initialization failed: ${error}. Falling back to in-memory cache.`);
|
|
961
|
-
this.redis = null;
|
|
962
|
-
this.redisConnected = false;
|
|
963
|
-
}
|
|
964
|
-
this.initialized = true;
|
|
965
|
-
this.log(`Semantic cache initialized (backend: ${this.getBackend()})`);
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Load Redis client dynamically (optional dependency)
|
|
969
|
-
*/
|
|
970
|
-
async loadRedisClient() {
|
|
971
|
-
try {
|
|
972
|
-
const ioredis = await import('ioredis');
|
|
973
|
-
return ioredis.default;
|
|
974
|
-
} catch (error) {
|
|
975
|
-
this.log("ioredis not installed, using in-memory cache");
|
|
976
|
-
return null;
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
/**
|
|
980
|
-
* Get cache entry
|
|
981
|
-
*/
|
|
982
|
-
async get(contentHash, type) {
|
|
983
|
-
if (!this.initialized) {
|
|
984
|
-
await this.initialize();
|
|
985
|
-
}
|
|
986
|
-
const startTime = Date.now();
|
|
987
|
-
const key = generateCacheKey(contentHash, type, this.options.keyPrefix);
|
|
988
|
-
try {
|
|
989
|
-
let entry = null;
|
|
990
|
-
if (this.redis && this.redisConnected) {
|
|
991
|
-
const raw = await this.redis.get(key);
|
|
992
|
-
if (raw) {
|
|
993
|
-
entry = JSON.parse(raw);
|
|
994
|
-
}
|
|
995
|
-
} else {
|
|
996
|
-
const memEntry = this.memoryCache.get(key);
|
|
997
|
-
if (memEntry) {
|
|
998
|
-
entry = memEntry;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
const latencyMs = Date.now() - startTime;
|
|
1002
|
-
this.stats.totalLatencyMs += latencyMs;
|
|
1003
|
-
this.stats.lookupCount++;
|
|
1004
|
-
if (!entry) {
|
|
1005
|
-
this.stats.misses++;
|
|
1006
|
-
return {
|
|
1007
|
-
hit: false,
|
|
1008
|
-
reason: "not_found",
|
|
1009
|
-
latencyMs
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
if (entry.version !== this.options.version) {
|
|
1013
|
-
this.stats.misses++;
|
|
1014
|
-
await this.delete(contentHash, type);
|
|
1015
|
-
return {
|
|
1016
|
-
hit: false,
|
|
1017
|
-
reason: "version_mismatch",
|
|
1018
|
-
latencyMs
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
if (!this.redis) {
|
|
1022
|
-
const age = Date.now() - entry.createdAt;
|
|
1023
|
-
const ttlMs = this.options.ttl * 1e3;
|
|
1024
|
-
if (age > ttlMs) {
|
|
1025
|
-
this.stats.misses++;
|
|
1026
|
-
this.memoryCache.delete(key);
|
|
1027
|
-
return {
|
|
1028
|
-
hit: false,
|
|
1029
|
-
reason: "expired",
|
|
1030
|
-
latencyMs
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
entry.accessedAt = Date.now();
|
|
1035
|
-
entry.accessCount++;
|
|
1036
|
-
if (this.redis && this.redisConnected) {
|
|
1037
|
-
await this.redis.set(key, JSON.stringify(entry), "EX", this.options.ttl);
|
|
1038
|
-
} else {
|
|
1039
|
-
this.memoryCache.set(key, entry);
|
|
1040
|
-
}
|
|
1041
|
-
this.stats.hits++;
|
|
1042
|
-
this.log(`Cache HIT: ${type} (${contentHash.slice(0, 8)}...) [${latencyMs}ms]`);
|
|
1043
|
-
return {
|
|
1044
|
-
hit: true,
|
|
1045
|
-
entry,
|
|
1046
|
-
latencyMs
|
|
1047
|
-
};
|
|
1048
|
-
} catch (error) {
|
|
1049
|
-
this.stats.misses++;
|
|
1050
|
-
this.log(`Cache lookup error: ${error}`);
|
|
1051
|
-
return {
|
|
1052
|
-
hit: false,
|
|
1053
|
-
reason: "redis_error",
|
|
1054
|
-
latencyMs: Date.now() - startTime
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
/**
|
|
1059
|
-
* Set cache entry
|
|
1060
|
-
*/
|
|
1061
|
-
async set(contentHash, type, data, options) {
|
|
1062
|
-
if (!this.initialized) {
|
|
1063
|
-
await this.initialize();
|
|
1064
|
-
}
|
|
1065
|
-
const key = generateCacheKey(contentHash, type, this.options.keyPrefix);
|
|
1066
|
-
const serialized = JSON.stringify(data);
|
|
1067
|
-
const entry = {
|
|
1068
|
-
contentHash,
|
|
1069
|
-
type,
|
|
1070
|
-
data,
|
|
1071
|
-
sourcePath: options?.sourcePath,
|
|
1072
|
-
dependencies: options?.dependencies,
|
|
1073
|
-
createdAt: Date.now(),
|
|
1074
|
-
accessedAt: Date.now(),
|
|
1075
|
-
accessCount: 0,
|
|
1076
|
-
size: serialized.length,
|
|
1077
|
-
version: this.options.version
|
|
1078
|
-
};
|
|
1079
|
-
try {
|
|
1080
|
-
if (this.redis && this.redisConnected) {
|
|
1081
|
-
await this.redis.set(key, JSON.stringify(entry), "EX", this.options.ttl);
|
|
1082
|
-
} else {
|
|
1083
|
-
this.memoryCache.set(key, entry);
|
|
1084
|
-
}
|
|
1085
|
-
this.log(`Cache SET: ${type} (${contentHash.slice(0, 8)}...) [${entry.size} bytes]`);
|
|
1086
|
-
} catch (error) {
|
|
1087
|
-
this.log(`Cache set error: ${error}`);
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Delete cache entry
|
|
1092
|
-
*/
|
|
1093
|
-
async delete(contentHash, type) {
|
|
1094
|
-
if (!this.initialized) {
|
|
1095
|
-
await this.initialize();
|
|
1096
|
-
}
|
|
1097
|
-
const key = generateCacheKey(contentHash, type, this.options.keyPrefix);
|
|
1098
|
-
try {
|
|
1099
|
-
if (this.redis && this.redisConnected) {
|
|
1100
|
-
await this.redis.del(key);
|
|
1101
|
-
} else {
|
|
1102
|
-
this.memoryCache.delete(key);
|
|
1103
|
-
}
|
|
1104
|
-
this.log(`Cache DELETE: ${type} (${contentHash.slice(0, 8)}...)`);
|
|
1105
|
-
} catch (error) {
|
|
1106
|
-
this.log(`Cache delete error: ${error}`);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
/**
|
|
1110
|
-
* Invalidate all cache entries for a type
|
|
1111
|
-
*/
|
|
1112
|
-
async invalidateType(type) {
|
|
1113
|
-
if (!this.initialized) {
|
|
1114
|
-
await this.initialize();
|
|
1115
|
-
}
|
|
1116
|
-
let count = 0;
|
|
1117
|
-
try {
|
|
1118
|
-
if (this.redis && this.redisConnected) {
|
|
1119
|
-
const pattern = `${this.options.keyPrefix}:${type}:*`;
|
|
1120
|
-
const keys = await this.redis.keys(pattern);
|
|
1121
|
-
for (const key of keys) {
|
|
1122
|
-
await this.redis.del(key);
|
|
1123
|
-
count++;
|
|
1124
|
-
}
|
|
1125
|
-
} else {
|
|
1126
|
-
for (const [key, entry] of this.memoryCache.entries()) {
|
|
1127
|
-
if (entry.type === type) {
|
|
1128
|
-
this.memoryCache.delete(key);
|
|
1129
|
-
count++;
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
this.log(`Invalidated ${count} entries of type: ${type}`);
|
|
1134
|
-
} catch (error) {
|
|
1135
|
-
this.log(`Cache invalidation error: ${error}`);
|
|
1136
|
-
}
|
|
1137
|
-
return count;
|
|
1138
|
-
}
|
|
1139
|
-
/**
|
|
1140
|
-
* Clear all cache entries
|
|
1141
|
-
*/
|
|
1142
|
-
async clear() {
|
|
1143
|
-
if (!this.initialized) {
|
|
1144
|
-
await this.initialize();
|
|
1145
|
-
}
|
|
1146
|
-
try {
|
|
1147
|
-
if (this.redis && this.redisConnected) {
|
|
1148
|
-
const pattern = `${this.options.keyPrefix}:*`;
|
|
1149
|
-
const keys = await this.redis.keys(pattern);
|
|
1150
|
-
for (const key of keys) {
|
|
1151
|
-
await this.redis.del(key);
|
|
1152
|
-
}
|
|
1153
|
-
} else {
|
|
1154
|
-
this.memoryCache.clear();
|
|
1155
|
-
}
|
|
1156
|
-
this.stats.hits = 0;
|
|
1157
|
-
this.stats.misses = 0;
|
|
1158
|
-
this.stats.totalLatencyMs = 0;
|
|
1159
|
-
this.stats.lookupCount = 0;
|
|
1160
|
-
this.log("Cache cleared");
|
|
1161
|
-
} catch (error) {
|
|
1162
|
-
this.log(`Cache clear error: ${error}`);
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
/**
|
|
1166
|
-
* Get cache statistics
|
|
1167
|
-
*/
|
|
1168
|
-
async getStats() {
|
|
1169
|
-
const totalRequests = this.stats.hits + this.stats.misses;
|
|
1170
|
-
const hitRate = totalRequests > 0 ? this.stats.hits / totalRequests : 0;
|
|
1171
|
-
const avgLatencyMs = this.stats.lookupCount > 0 ? this.stats.totalLatencyMs / this.stats.lookupCount : 0;
|
|
1172
|
-
let totalEntries = 0;
|
|
1173
|
-
const entriesByType = {
|
|
1174
|
-
"ast-subtree": 0,
|
|
1175
|
-
"compiled-module": 0,
|
|
1176
|
-
"compiled-object": 0,
|
|
1177
|
-
"trait-composition": 0,
|
|
1178
|
-
"import-resolution": 0
|
|
1179
|
-
};
|
|
1180
|
-
try {
|
|
1181
|
-
if (this.redis && this.redisConnected) {
|
|
1182
|
-
const pattern = `${this.options.keyPrefix}:*`;
|
|
1183
|
-
const keys = await this.redis.keys(pattern);
|
|
1184
|
-
totalEntries = keys.length;
|
|
1185
|
-
for (const key of keys) {
|
|
1186
|
-
const parts = key.split(":");
|
|
1187
|
-
const type = parts[2];
|
|
1188
|
-
if (type in entriesByType) {
|
|
1189
|
-
entriesByType[type]++;
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
} else {
|
|
1193
|
-
totalEntries = this.memoryCache.size;
|
|
1194
|
-
for (const entry of this.memoryCache.values()) {
|
|
1195
|
-
entriesByType[entry.type]++;
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
} catch (error) {
|
|
1199
|
-
this.log(`Stats error: ${error}`);
|
|
1200
|
-
}
|
|
1201
|
-
return {
|
|
1202
|
-
hits: this.stats.hits,
|
|
1203
|
-
misses: this.stats.misses,
|
|
1204
|
-
hitRate,
|
|
1205
|
-
avgLatencyMs,
|
|
1206
|
-
totalEntries,
|
|
1207
|
-
backend: this.getBackend(),
|
|
1208
|
-
redisConnected: this.redisConnected,
|
|
1209
|
-
entriesByType
|
|
1210
|
-
};
|
|
1211
|
-
}
|
|
1212
|
-
/**
|
|
1213
|
-
* Get cache backend type
|
|
1214
|
-
*/
|
|
1215
|
-
getBackend() {
|
|
1216
|
-
return this.redis && this.redisConnected ? "redis" : "memory";
|
|
1217
|
-
}
|
|
1218
|
-
/**
|
|
1219
|
-
* Close Redis connection
|
|
1220
|
-
*/
|
|
1221
|
-
async close() {
|
|
1222
|
-
if (this.redis) {
|
|
1223
|
-
try {
|
|
1224
|
-
await this.redis.quit();
|
|
1225
|
-
this.log("Redis connection closed");
|
|
1226
|
-
} catch (error) {
|
|
1227
|
-
this.log(`Error closing Redis: ${error}`);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
/**
|
|
1232
|
-
* Debug logging
|
|
1233
|
-
*/
|
|
1234
|
-
log(message) {
|
|
1235
|
-
if (this.options.debug) {
|
|
1236
|
-
console.log(`[SemanticCache] ${message}`);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
};
|
|
1240
|
-
function createSemanticCache(options) {
|
|
1241
|
-
return new SemanticCache(options);
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
// src/compiler/IncrementalCompiler.ts
|
|
1245
|
-
chunkAIUXRS74_cjs.init_AgentRBAC();
|
|
1246
|
-
function hashContent(content) {
|
|
1247
|
-
let hash = 0;
|
|
1248
|
-
for (let i = 0; i < content.length; i++) {
|
|
1249
|
-
const char = content.charCodeAt(i);
|
|
1250
|
-
hash = (hash << 5) - hash + char;
|
|
1251
|
-
hash = hash & hash;
|
|
1252
|
-
}
|
|
1253
|
-
return hash.toString(36);
|
|
1254
|
-
}
|
|
1255
|
-
function hashConfig2(config) {
|
|
1256
|
-
const keys = Object.keys(config).sort();
|
|
1257
|
-
const parts = [];
|
|
1258
|
-
for (const key of keys) {
|
|
1259
|
-
const value = config[key];
|
|
1260
|
-
parts.push(`${key}:${JSON.stringify(value)}`);
|
|
1261
|
-
}
|
|
1262
|
-
const str = parts.join("|");
|
|
1263
|
-
let hash = 5381;
|
|
1264
|
-
for (let i = 0; i < str.length; i++) {
|
|
1265
|
-
hash = (hash << 5) + hash ^ str.charCodeAt(i);
|
|
1266
|
-
}
|
|
1267
|
-
return (hash >>> 0).toString(36);
|
|
1268
|
-
}
|
|
1269
|
-
function serializeObject(obj) {
|
|
1270
|
-
return JSON.stringify({
|
|
1271
|
-
name: obj.name,
|
|
1272
|
-
properties: obj.properties,
|
|
1273
|
-
traits: obj.traits,
|
|
1274
|
-
children: obj.children?.map(serializeObject),
|
|
1275
|
-
logic: obj.logic
|
|
1276
|
-
// logic might not be directly on HoloObjectDecl but used in diff
|
|
1277
|
-
});
|
|
1278
|
-
}
|
|
1279
|
-
function serializeProperty(prop) {
|
|
1280
|
-
return JSON.stringify({ key: prop.key, value: prop.value });
|
|
1281
|
-
}
|
|
1282
|
-
var IncrementalCompiler = class _IncrementalCompiler {
|
|
1283
|
-
constructor(traitGraph, options) {
|
|
1284
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
1285
|
-
this.previousAST = null;
|
|
1286
|
-
this.stateSnapshot = null;
|
|
1287
|
-
this.dependencyGraph = /* @__PURE__ */ new Map();
|
|
1288
|
-
this.semanticCache = null;
|
|
1289
|
-
this.traitGraph = traitGraph || globalTraitGraph;
|
|
1290
|
-
if (options?.enableSemanticCache) {
|
|
1291
|
-
this.semanticCache = createSemanticCache({
|
|
1292
|
-
redisUrl: options.redisUrl,
|
|
1293
|
-
ttl: options.cacheTTL,
|
|
1294
|
-
debug: false
|
|
1295
|
-
});
|
|
1296
|
-
this.semanticCache.initialize().catch((err) => {
|
|
1297
|
-
console.warn(`Semantic cache initialization failed: ${err}`);
|
|
1298
|
-
this.semanticCache = null;
|
|
1299
|
-
});
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
/**
|
|
1303
|
-
* Diff two ASTs and return the changes
|
|
1304
|
-
*/
|
|
1305
|
-
diff(oldAST, newAST) {
|
|
1306
|
-
const changes = [];
|
|
1307
|
-
const addedObjects = [];
|
|
1308
|
-
const removedObjects = [];
|
|
1309
|
-
const modifiedObjects = [];
|
|
1310
|
-
const unchangedObjects = [];
|
|
1311
|
-
if (!oldAST) {
|
|
1312
|
-
const allObjects = this.collectObjectNames(newAST);
|
|
1313
|
-
return {
|
|
1314
|
-
hasChanges: true,
|
|
1315
|
-
changes: allObjects.map((name) => ({
|
|
1316
|
-
type: "added",
|
|
1317
|
-
path: [name],
|
|
1318
|
-
nodeName: name,
|
|
1319
|
-
nodeType: "object",
|
|
1320
|
-
newValue: this.findObject(newAST, name)
|
|
1321
|
-
})),
|
|
1322
|
-
addedObjects: allObjects,
|
|
1323
|
-
removedObjects: [],
|
|
1324
|
-
modifiedObjects: [],
|
|
1325
|
-
unchangedObjects: []
|
|
1326
|
-
};
|
|
1327
|
-
}
|
|
1328
|
-
const oldObjects = /* @__PURE__ */ new Map();
|
|
1329
|
-
const newObjects = /* @__PURE__ */ new Map();
|
|
1330
|
-
this.collectObjects(oldAST, oldObjects);
|
|
1331
|
-
this.collectObjects(newAST, newObjects);
|
|
1332
|
-
for (const [name, obj] of newObjects) {
|
|
1333
|
-
if (!oldObjects.has(name)) {
|
|
1334
|
-
addedObjects.push(name);
|
|
1335
|
-
changes.push({
|
|
1336
|
-
type: "added",
|
|
1337
|
-
path: [name],
|
|
1338
|
-
nodeName: name,
|
|
1339
|
-
nodeType: "object",
|
|
1340
|
-
newValue: obj
|
|
1341
|
-
});
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
for (const [name, obj] of oldObjects) {
|
|
1345
|
-
if (!newObjects.has(name)) {
|
|
1346
|
-
removedObjects.push(name);
|
|
1347
|
-
changes.push({
|
|
1348
|
-
type: "removed",
|
|
1349
|
-
path: [name],
|
|
1350
|
-
nodeName: name,
|
|
1351
|
-
nodeType: "object",
|
|
1352
|
-
oldValue: obj
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
for (const [name, newObj] of newObjects) {
|
|
1357
|
-
if (oldObjects.has(name)) {
|
|
1358
|
-
const oldObj = oldObjects.get(name);
|
|
1359
|
-
const objectChanges = this.diffObject(oldObj, newObj, [name]);
|
|
1360
|
-
if (objectChanges.length > 0) {
|
|
1361
|
-
modifiedObjects.push(name);
|
|
1362
|
-
changes.push(...objectChanges);
|
|
1363
|
-
} else {
|
|
1364
|
-
unchangedObjects.push(name);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
return {
|
|
1369
|
-
hasChanges: changes.length > 0,
|
|
1370
|
-
changes,
|
|
1371
|
-
addedObjects,
|
|
1372
|
-
removedObjects,
|
|
1373
|
-
modifiedObjects,
|
|
1374
|
-
unchangedObjects
|
|
1375
|
-
};
|
|
1376
|
-
}
|
|
1377
|
-
/**
|
|
1378
|
-
* Diff two objects and return property-level changes
|
|
1379
|
-
*/
|
|
1380
|
-
diffObject(oldObj, newObj, path) {
|
|
1381
|
-
const changes = [];
|
|
1382
|
-
const oldProps = new Map(oldObj.properties?.map((p) => [p.key, p]) || []);
|
|
1383
|
-
const newProps = new Map(newObj.properties?.map((p) => [p.key, p]) || []);
|
|
1384
|
-
for (const [key, newProp] of newProps) {
|
|
1385
|
-
if (!oldProps.has(key)) {
|
|
1386
|
-
changes.push({
|
|
1387
|
-
type: "added",
|
|
1388
|
-
path: [...path, key],
|
|
1389
|
-
nodeName: key,
|
|
1390
|
-
nodeType: "property",
|
|
1391
|
-
newValue: newProp.value
|
|
1392
|
-
});
|
|
1393
|
-
} else {
|
|
1394
|
-
const oldProp = oldProps.get(key);
|
|
1395
|
-
if (serializeProperty(oldProp) !== serializeProperty(newProp)) {
|
|
1396
|
-
changes.push({
|
|
1397
|
-
type: "modified",
|
|
1398
|
-
path: [...path, key],
|
|
1399
|
-
nodeName: key,
|
|
1400
|
-
nodeType: "property",
|
|
1401
|
-
oldValue: oldProp.value,
|
|
1402
|
-
newValue: newProp.value
|
|
1403
|
-
});
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
for (const key of oldProps.keys()) {
|
|
1408
|
-
if (!newProps.has(key)) {
|
|
1409
|
-
changes.push({
|
|
1410
|
-
type: "removed",
|
|
1411
|
-
path: [...path, key],
|
|
1412
|
-
nodeName: key,
|
|
1413
|
-
nodeType: "property",
|
|
1414
|
-
oldValue: oldProps.get(key).value
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
const oldTraitUsages = this.extractTraitUsages(oldObj.traits || []);
|
|
1419
|
-
const newTraitUsages = this.extractTraitUsages(newObj.traits || []);
|
|
1420
|
-
const oldTraitMap = new Map(oldTraitUsages.map((t) => [t.name, t]));
|
|
1421
|
-
const newTraitMap = new Map(newTraitUsages.map((t) => [t.name, t]));
|
|
1422
|
-
for (const [name, newTrait] of newTraitMap) {
|
|
1423
|
-
if (!oldTraitMap.has(name)) {
|
|
1424
|
-
changes.push({
|
|
1425
|
-
type: "added",
|
|
1426
|
-
path: [...path, "@" + name],
|
|
1427
|
-
nodeName: name,
|
|
1428
|
-
nodeType: "trait",
|
|
1429
|
-
newValue: newTrait
|
|
1430
|
-
});
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
for (const [name, oldTrait] of oldTraitMap) {
|
|
1434
|
-
if (!newTraitMap.has(name)) {
|
|
1435
|
-
changes.push({
|
|
1436
|
-
type: "removed",
|
|
1437
|
-
path: [...path, "@" + name],
|
|
1438
|
-
nodeName: name,
|
|
1439
|
-
nodeType: "trait",
|
|
1440
|
-
oldValue: oldTrait
|
|
1441
|
-
});
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
for (const [name, newTrait] of newTraitMap) {
|
|
1445
|
-
const oldTrait = oldTraitMap.get(name);
|
|
1446
|
-
if (oldTrait && oldTrait.configHash !== newTrait.configHash) {
|
|
1447
|
-
changes.push({
|
|
1448
|
-
type: "modified",
|
|
1449
|
-
path: [...path, "@" + name],
|
|
1450
|
-
nodeName: name,
|
|
1451
|
-
nodeType: "trait",
|
|
1452
|
-
oldValue: oldTrait,
|
|
1453
|
-
newValue: newTrait
|
|
1454
|
-
});
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
if (JSON.stringify(oldObj.logic) !== JSON.stringify(newObj.logic)) {
|
|
1458
|
-
changes.push({
|
|
1459
|
-
type: "modified",
|
|
1460
|
-
path: [...path, "logic"],
|
|
1461
|
-
nodeName: "logic",
|
|
1462
|
-
nodeType: "logic",
|
|
1463
|
-
oldValue: oldObj.logic,
|
|
1464
|
-
newValue: newObj.logic
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
const oldChildren = new Map((oldObj.children || []).map((c) => [c.name, c]));
|
|
1468
|
-
const newChildren = new Map((newObj.children || []).map((c) => [c.name, c]));
|
|
1469
|
-
for (const [name, newChild] of newChildren) {
|
|
1470
|
-
if (!oldChildren.has(name)) {
|
|
1471
|
-
changes.push({
|
|
1472
|
-
type: "added",
|
|
1473
|
-
path: [...path, name],
|
|
1474
|
-
nodeName: name,
|
|
1475
|
-
nodeType: "object",
|
|
1476
|
-
newValue: newChild
|
|
1477
|
-
});
|
|
1478
|
-
} else {
|
|
1479
|
-
const childChanges = this.diffObject(oldChildren.get(name), newChild, [...path, name]);
|
|
1480
|
-
changes.push(...childChanges);
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
for (const name of oldChildren.keys()) {
|
|
1484
|
-
if (!newChildren.has(name)) {
|
|
1485
|
-
changes.push({
|
|
1486
|
-
type: "removed",
|
|
1487
|
-
path: [...path, name],
|
|
1488
|
-
nodeName: name,
|
|
1489
|
-
nodeType: "object",
|
|
1490
|
-
oldValue: oldChildren.get(name)
|
|
1491
|
-
});
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
return changes;
|
|
1495
|
-
}
|
|
1496
|
-
/**
|
|
1497
|
-
* Extract trait usages with config hashes from trait array
|
|
1498
|
-
* Handles both string traits and trait objects with config
|
|
1499
|
-
*/
|
|
1500
|
-
extractTraitUsages(traits) {
|
|
1501
|
-
return traits.map((t) => {
|
|
1502
|
-
if (typeof t === "string") {
|
|
1503
|
-
return {
|
|
1504
|
-
name: t,
|
|
1505
|
-
config: {},
|
|
1506
|
-
configHash: hashConfig2({})
|
|
1507
|
-
};
|
|
1508
|
-
}
|
|
1509
|
-
if (t && typeof t === "object") {
|
|
1510
|
-
const traitObj = t;
|
|
1511
|
-
if ("name" in traitObj) {
|
|
1512
|
-
const name = String(traitObj.name);
|
|
1513
|
-
const config = {};
|
|
1514
|
-
for (const [key, value] of Object.entries(traitObj)) {
|
|
1515
|
-
if (key !== "name") {
|
|
1516
|
-
config[key] = value;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
return {
|
|
1520
|
-
name,
|
|
1521
|
-
config,
|
|
1522
|
-
configHash: hashConfig2(config)
|
|
1523
|
-
};
|
|
1524
|
-
}
|
|
1525
|
-
const keys = Object.keys(traitObj);
|
|
1526
|
-
if (keys.length === 1) {
|
|
1527
|
-
const name = keys[0];
|
|
1528
|
-
const config = traitObj[name] || {};
|
|
1529
|
-
return {
|
|
1530
|
-
name,
|
|
1531
|
-
config: typeof config === "object" ? config : {},
|
|
1532
|
-
configHash: hashConfig2(typeof config === "object" ? config : {})
|
|
1533
|
-
};
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
return {
|
|
1537
|
-
name: String(t),
|
|
1538
|
-
config: {},
|
|
1539
|
-
configHash: hashConfig2({})
|
|
1540
|
-
};
|
|
1541
|
-
});
|
|
1542
|
-
}
|
|
1543
|
-
/**
|
|
1544
|
-
* Collect all object names from a composition
|
|
1545
|
-
*/
|
|
1546
|
-
collectObjectNames(composition) {
|
|
1547
|
-
const names = [];
|
|
1548
|
-
const collectFromObject = (obj) => {
|
|
1549
|
-
names.push(obj.name);
|
|
1550
|
-
obj.children?.forEach(collectFromObject);
|
|
1551
|
-
};
|
|
1552
|
-
composition.objects?.forEach(collectFromObject);
|
|
1553
|
-
composition.spatialGroups?.forEach((group) => {
|
|
1554
|
-
names.push(group.name);
|
|
1555
|
-
group.objects?.forEach(collectFromObject);
|
|
1556
|
-
});
|
|
1557
|
-
return names;
|
|
1558
|
-
}
|
|
1559
|
-
/**
|
|
1560
|
-
* Collect all objects into a map
|
|
1561
|
-
*/
|
|
1562
|
-
collectObjects(composition, map) {
|
|
1563
|
-
const collectFromObject = (obj) => {
|
|
1564
|
-
map.set(obj.name, obj);
|
|
1565
|
-
obj.children?.forEach(collectFromObject);
|
|
1566
|
-
};
|
|
1567
|
-
composition.objects?.forEach(collectFromObject);
|
|
1568
|
-
composition.spatialGroups?.forEach((group) => {
|
|
1569
|
-
group.objects?.forEach(collectFromObject);
|
|
1570
|
-
});
|
|
1571
|
-
}
|
|
1572
|
-
/**
|
|
1573
|
-
* Find an object by name in a composition
|
|
1574
|
-
*/
|
|
1575
|
-
findObject(composition, name) {
|
|
1576
|
-
const searchInObject = (obj) => {
|
|
1577
|
-
if (obj.name === name) return obj;
|
|
1578
|
-
for (const child of obj.children || []) {
|
|
1579
|
-
const found = searchInObject(child);
|
|
1580
|
-
if (found) return found;
|
|
1581
|
-
}
|
|
1582
|
-
return void 0;
|
|
1583
|
-
};
|
|
1584
|
-
for (const obj of composition.objects || []) {
|
|
1585
|
-
const found = searchInObject(obj);
|
|
1586
|
-
if (found) return found;
|
|
1587
|
-
}
|
|
1588
|
-
for (const group of composition.spatialGroups || []) {
|
|
1589
|
-
for (const obj of group.objects || []) {
|
|
1590
|
-
const found = searchInObject(obj);
|
|
1591
|
-
if (found) return found;
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
return void 0;
|
|
1595
|
-
}
|
|
1596
|
-
/**
|
|
1597
|
-
* Get cached compilation for an object if still valid
|
|
1598
|
-
*/
|
|
1599
|
-
getCached(objectName, currentHash) {
|
|
1600
|
-
const entry = this.cache.get(objectName);
|
|
1601
|
-
if (entry && entry.hash === currentHash) {
|
|
1602
|
-
return entry;
|
|
1603
|
-
}
|
|
1604
|
-
return null;
|
|
1605
|
-
}
|
|
1606
|
-
/**
|
|
1607
|
-
* Store compilation result in cache
|
|
1608
|
-
*/
|
|
1609
|
-
setCached(objectName, hash, compiledCode, dependencies) {
|
|
1610
|
-
this.cache.set(objectName, {
|
|
1611
|
-
hash,
|
|
1612
|
-
compiledCode,
|
|
1613
|
-
dependencies,
|
|
1614
|
-
timestamp: Date.now()
|
|
1615
|
-
});
|
|
1616
|
-
}
|
|
1617
|
-
/**
|
|
1618
|
-
* Save current state for hot reload
|
|
1619
|
-
*/
|
|
1620
|
-
saveState(states) {
|
|
1621
|
-
this.stateSnapshot = {
|
|
1622
|
-
objectStates: new Map(states),
|
|
1623
|
-
timestamp: Date.now()
|
|
1624
|
-
};
|
|
1625
|
-
}
|
|
1626
|
-
/**
|
|
1627
|
-
* Restore state after hot reload
|
|
1628
|
-
*/
|
|
1629
|
-
restoreState() {
|
|
1630
|
-
return this.stateSnapshot;
|
|
1631
|
-
}
|
|
1632
|
-
/**
|
|
1633
|
-
* Clear state snapshot
|
|
1634
|
-
*/
|
|
1635
|
-
clearState() {
|
|
1636
|
-
this.stateSnapshot = null;
|
|
1637
|
-
}
|
|
1638
|
-
/**
|
|
1639
|
-
* Update dependency graph
|
|
1640
|
-
*/
|
|
1641
|
-
updateDependencies(objectName, dependencies) {
|
|
1642
|
-
this.dependencyGraph.set(objectName, new Set(dependencies));
|
|
1643
|
-
}
|
|
1644
|
-
/**
|
|
1645
|
-
* Get objects that depend on a given object
|
|
1646
|
-
*/
|
|
1647
|
-
getDependents(objectName) {
|
|
1648
|
-
const dependents = [];
|
|
1649
|
-
for (const [name, deps] of this.dependencyGraph) {
|
|
1650
|
-
if (deps.has(objectName)) {
|
|
1651
|
-
dependents.push(name);
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
return dependents;
|
|
1655
|
-
}
|
|
1656
|
-
/**
|
|
1657
|
-
* Get all objects that need recompilation due to a change
|
|
1658
|
-
* Enhanced with trait-aware dependency tracking
|
|
1659
|
-
*/
|
|
1660
|
-
getRecompilationSet(changedObjects) {
|
|
1661
|
-
const toRecompile = new Set(changedObjects);
|
|
1662
|
-
const queue = [...changedObjects];
|
|
1663
|
-
while (queue.length > 0) {
|
|
1664
|
-
const current = queue.shift();
|
|
1665
|
-
const dependents = this.getDependents(current);
|
|
1666
|
-
for (const dep of dependents) {
|
|
1667
|
-
if (!toRecompile.has(dep)) {
|
|
1668
|
-
toRecompile.add(dep);
|
|
1669
|
-
queue.push(dep);
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
const traitRecompileSet = this.traitGraph.calculateRecompilationSet(changedObjects);
|
|
1674
|
-
for (const obj of traitRecompileSet) {
|
|
1675
|
-
toRecompile.add(obj);
|
|
1676
|
-
}
|
|
1677
|
-
return toRecompile;
|
|
1678
|
-
}
|
|
1679
|
-
/**
|
|
1680
|
-
* Get trait changes for specific objects
|
|
1681
|
-
* Used for fine-grained trait config change detection
|
|
1682
|
-
*/
|
|
1683
|
-
getTraitChanges(objectName, newTraits) {
|
|
1684
|
-
return this.traitGraph.detectTraitChanges(objectName, newTraits);
|
|
1685
|
-
}
|
|
1686
|
-
/**
|
|
1687
|
-
* Get the trait dependency graph for external access
|
|
1688
|
-
*/
|
|
1689
|
-
getTraitGraph() {
|
|
1690
|
-
return this.traitGraph;
|
|
1691
|
-
}
|
|
1692
|
-
// ===========================================================================
|
|
1693
|
-
// IMPORT RESOLUTION (Asset Pipeline)
|
|
1694
|
-
// ===========================================================================
|
|
1695
|
-
/**
|
|
1696
|
-
* Resolve all `@import` directives in a `HSPlusCompileResult`, merge the
|
|
1697
|
-
* imported exports into a scope, and register cross-file edges in the
|
|
1698
|
-
* `TraitDependencyGraph` for incremental recompilation.
|
|
1699
|
-
*
|
|
1700
|
-
* This method is **async** because it may read files from disk (or any
|
|
1701
|
-
* injected async reader).
|
|
1702
|
-
*
|
|
1703
|
-
* ### Typical usage
|
|
1704
|
-
* ```ts
|
|
1705
|
-
* const parser = new HoloScriptPlusParser({ enableTypeScriptImports: true });
|
|
1706
|
-
* const result = parser.parse(source);
|
|
1707
|
-
*
|
|
1708
|
-
* const compiler = new IncrementalCompiler();
|
|
1709
|
-
* const { importScope, importErrors } = await compiler.resolveImports(
|
|
1710
|
-
* result,
|
|
1711
|
-
* { baseDir: '/project', sourceFile: '/project/scene.hs' }
|
|
1712
|
-
* );
|
|
1713
|
-
* ```
|
|
1714
|
-
*
|
|
1715
|
-
* @param result Parse result that may contain `ast.imports`
|
|
1716
|
-
* @param options Must include `baseDir` (and optionally `sourceFile`, `readFile`)
|
|
1717
|
-
* @returns `{ scope, modules, errors }` from `ImportResolver.resolve()`
|
|
1718
|
-
*/
|
|
1719
|
-
async resolveImports(result, options) {
|
|
1720
|
-
const baseDir = options.baseDir ?? "/";
|
|
1721
|
-
const sourceFile = options.sourceFile ?? `${baseDir}/unknown.hs`;
|
|
1722
|
-
const resolver = new ImportResolver();
|
|
1723
|
-
const resolution = await resolver.resolve(result, sourceFile, {
|
|
1724
|
-
baseDir,
|
|
1725
|
-
readFile: options.readFile
|
|
1726
|
-
});
|
|
1727
|
-
const imports = result.ast?.imports ?? [];
|
|
1728
|
-
this.traitGraph.clearImportsForFile(sourceFile);
|
|
1729
|
-
for (const imp of imports) {
|
|
1730
|
-
const canonicalPath = resolveImportPath(imp.path, baseDir);
|
|
1731
|
-
this.traitGraph.registerImport(sourceFile, canonicalPath);
|
|
1732
|
-
}
|
|
1733
|
-
return resolution;
|
|
1734
|
-
}
|
|
1735
|
-
// ===========================================================================
|
|
1736
|
-
// RBAC ENFORCEMENT
|
|
1737
|
-
// ===========================================================================
|
|
1738
|
-
/**
|
|
1739
|
-
* Validate agent has permission for incremental compilation.
|
|
1740
|
-
* Skips validation when no token is provided (backwards compatibility / testing).
|
|
1741
|
-
*/
|
|
1742
|
-
validateAccess(agentToken) {
|
|
1743
|
-
if (!agentToken) return;
|
|
1744
|
-
const rbac = chunkAIUXRS74_cjs.getRBAC();
|
|
1745
|
-
const decision = rbac.checkAccess({
|
|
1746
|
-
token: agentToken,
|
|
1747
|
-
resourceType: "ast" /* AST */,
|
|
1748
|
-
operation: "read",
|
|
1749
|
-
expectedWorkflowStep: "generate_assembly" /* GENERATE_ASSEMBLY */
|
|
1750
|
-
});
|
|
1751
|
-
if (!decision.allowed) {
|
|
1752
|
-
throw new UnauthorizedIncrementalCompilerAccessError(decision, "IncrementalCompiler");
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
/**
|
|
1756
|
-
* Perform incremental compilation
|
|
1757
|
-
*/
|
|
1758
|
-
async compile(newAST, compileObject, options = {}) {
|
|
1759
|
-
const { preserveState = true, forceRecompile = [], skipUnchanged = true, agentToken } = options;
|
|
1760
|
-
this.validateAccess(agentToken);
|
|
1761
|
-
const diffResult = this.diff(this.previousAST, newAST);
|
|
1762
|
-
const changedObjects = [
|
|
1763
|
-
...diffResult.addedObjects,
|
|
1764
|
-
...diffResult.modifiedObjects,
|
|
1765
|
-
...forceRecompile
|
|
1766
|
-
];
|
|
1767
|
-
const recompileSet = this.getRecompilationSet(changedObjects);
|
|
1768
|
-
for (const removed of diffResult.removedObjects) {
|
|
1769
|
-
const dependents = this.getDependents(removed);
|
|
1770
|
-
for (const dep of dependents) {
|
|
1771
|
-
recompileSet.add(dep);
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
const recompiledObjects = [];
|
|
1775
|
-
const cachedObjects = [];
|
|
1776
|
-
const compiledParts = [];
|
|
1777
|
-
const allObjects = /* @__PURE__ */ new Map();
|
|
1778
|
-
this.collectObjects(newAST, allObjects);
|
|
1779
|
-
for (const [name, obj] of allObjects) {
|
|
1780
|
-
const hash = hashContent(serializeObject(obj));
|
|
1781
|
-
const traitUsages = this.extractTraitUsages(obj.traits || []);
|
|
1782
|
-
this.traitGraph.registerObject({
|
|
1783
|
-
objectName: name,
|
|
1784
|
-
sourceId: newAST.name || "default",
|
|
1785
|
-
traits: traitUsages,
|
|
1786
|
-
template: obj.template
|
|
1787
|
-
});
|
|
1788
|
-
if (skipUnchanged && !recompileSet.has(name)) {
|
|
1789
|
-
if (this.semanticCache) {
|
|
1790
|
-
const astHash = hashASTSubtree(obj);
|
|
1791
|
-
const semanticResult = await this.semanticCache.get(astHash, "compiled-object");
|
|
1792
|
-
if (semanticResult.hit) {
|
|
1793
|
-
cachedObjects.push(name);
|
|
1794
|
-
compiledParts.push(semanticResult.entry.data);
|
|
1795
|
-
continue;
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
const cached = this.getCached(name, hash);
|
|
1799
|
-
if (cached) {
|
|
1800
|
-
cachedObjects.push(name);
|
|
1801
|
-
compiledParts.push(cached.compiledCode);
|
|
1802
|
-
continue;
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
const compiled = compileObject(obj);
|
|
1806
|
-
recompiledObjects.push(name);
|
|
1807
|
-
compiledParts.push(compiled);
|
|
1808
|
-
this.setCached(name, hash, compiled, []);
|
|
1809
|
-
if (this.semanticCache) {
|
|
1810
|
-
const astHash = hashASTSubtree(obj);
|
|
1811
|
-
await this.semanticCache.set(astHash, "compiled-object", compiled, {
|
|
1812
|
-
sourcePath: newAST.name,
|
|
1813
|
-
dependencies: []
|
|
1814
|
-
});
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
this.previousAST = newAST;
|
|
1818
|
-
return {
|
|
1819
|
-
fullRecompile: cachedObjects.length === 0,
|
|
1820
|
-
recompiledObjects,
|
|
1821
|
-
cachedObjects,
|
|
1822
|
-
statePreserved: preserveState && this.stateSnapshot !== null,
|
|
1823
|
-
compiledCode: compiledParts.join("\n\n"),
|
|
1824
|
-
changes: diffResult
|
|
1825
|
-
};
|
|
1826
|
-
}
|
|
1827
|
-
/**
|
|
1828
|
-
* Reset compiler state
|
|
1829
|
-
*/
|
|
1830
|
-
reset() {
|
|
1831
|
-
this.cache.clear();
|
|
1832
|
-
this.previousAST = null;
|
|
1833
|
-
this.stateSnapshot = null;
|
|
1834
|
-
this.dependencyGraph.clear();
|
|
1835
|
-
}
|
|
1836
|
-
/**
|
|
1837
|
-
* Get cache statistics including trait graph info
|
|
1838
|
-
*/
|
|
1839
|
-
async getStats() {
|
|
1840
|
-
let dependencyEdges = 0;
|
|
1841
|
-
for (const deps of this.dependencyGraph.values()) {
|
|
1842
|
-
dependencyEdges += deps.size;
|
|
1843
|
-
}
|
|
1844
|
-
const stats = {
|
|
1845
|
-
cacheSize: this.cache.size,
|
|
1846
|
-
objectsCached: Array.from(this.cache.keys()),
|
|
1847
|
-
dependencyEdges,
|
|
1848
|
-
traitGraphStats: this.traitGraph.getStats()
|
|
1849
|
-
};
|
|
1850
|
-
if (this.semanticCache) {
|
|
1851
|
-
const semanticStats = await this.semanticCache.getStats();
|
|
1852
|
-
return {
|
|
1853
|
-
...stats,
|
|
1854
|
-
semanticCache: semanticStats
|
|
1855
|
-
};
|
|
1856
|
-
}
|
|
1857
|
-
return stats;
|
|
1858
|
-
}
|
|
1859
|
-
/**
|
|
1860
|
-
* Get semantic cache statistics
|
|
1861
|
-
*/
|
|
1862
|
-
async getSemanticCacheStats() {
|
|
1863
|
-
if (!this.semanticCache) {
|
|
1864
|
-
return null;
|
|
1865
|
-
}
|
|
1866
|
-
return this.semanticCache.getStats();
|
|
1867
|
-
}
|
|
1868
|
-
/**
|
|
1869
|
-
* Clear semantic cache
|
|
1870
|
-
*/
|
|
1871
|
-
async clearSemanticCache() {
|
|
1872
|
-
if (this.semanticCache) {
|
|
1873
|
-
await this.semanticCache.clear();
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
/**
|
|
1877
|
-
* Close semantic cache connection
|
|
1878
|
-
*/
|
|
1879
|
-
async closeSemanticCache() {
|
|
1880
|
-
if (this.semanticCache) {
|
|
1881
|
-
await this.semanticCache.close();
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
/**
|
|
1885
|
-
* Serialize compiler state for persistence
|
|
1886
|
-
* Includes cache, dependency graph, and trait graph
|
|
1887
|
-
*/
|
|
1888
|
-
serialize() {
|
|
1889
|
-
const data = {
|
|
1890
|
-
version: 1,
|
|
1891
|
-
entries: Array.from(this.cache.entries()).map(([name, entry]) => ({
|
|
1892
|
-
name,
|
|
1893
|
-
entry
|
|
1894
|
-
})),
|
|
1895
|
-
dependencies: Array.from(this.dependencyGraph.entries()).map(([name, deps]) => ({
|
|
1896
|
-
name,
|
|
1897
|
-
deps: Array.from(deps)
|
|
1898
|
-
})),
|
|
1899
|
-
traitGraph: this.traitGraph.serialize(),
|
|
1900
|
-
timestamp: Date.now()
|
|
1901
|
-
};
|
|
1902
|
-
return JSON.stringify(data);
|
|
1903
|
-
}
|
|
1904
|
-
/**
|
|
1905
|
-
* Restore compiler state from serialized data
|
|
1906
|
-
*/
|
|
1907
|
-
static deserialize(json) {
|
|
1908
|
-
const data = JSON.parse(json);
|
|
1909
|
-
if (data.version !== 1) {
|
|
1910
|
-
throw new Error(`Unsupported cache version: ${data.version}`);
|
|
1911
|
-
}
|
|
1912
|
-
const traitGraph = TraitDependencyGraph.deserialize(data.traitGraph);
|
|
1913
|
-
const compiler = new _IncrementalCompiler(traitGraph);
|
|
1914
|
-
for (const { name, entry } of data.entries) {
|
|
1915
|
-
compiler.cache.set(name, entry);
|
|
1916
|
-
}
|
|
1917
|
-
for (const { name, deps } of data.dependencies) {
|
|
1918
|
-
compiler.dependencyGraph.set(name, new Set(deps));
|
|
1919
|
-
}
|
|
1920
|
-
return compiler;
|
|
1921
|
-
}
|
|
1922
|
-
};
|
|
1923
|
-
function createIncrementalCompiler() {
|
|
1924
|
-
return new IncrementalCompiler();
|
|
1925
|
-
}
|
|
1926
|
-
function serializeCache(compiler) {
|
|
1927
|
-
compiler.getStats();
|
|
1928
|
-
const data = {
|
|
1929
|
-
version: 1,
|
|
1930
|
-
entries: [],
|
|
1931
|
-
dependencies: [],
|
|
1932
|
-
traitGraph: compiler.getTraitGraph().serialize(),
|
|
1933
|
-
timestamp: Date.now()
|
|
1934
|
-
};
|
|
1935
|
-
return JSON.stringify(data);
|
|
1936
|
-
}
|
|
1937
|
-
function deserializeCache(json) {
|
|
1938
|
-
const data = JSON.parse(json);
|
|
1939
|
-
if (data.version !== 1) {
|
|
1940
|
-
throw new Error(`Unsupported cache version: ${data.version}`);
|
|
1941
|
-
}
|
|
1942
|
-
const traitGraph = TraitDependencyGraph.deserialize(data.traitGraph);
|
|
1943
|
-
const compiler = new IncrementalCompiler(traitGraph);
|
|
1944
|
-
for (const { name, entry } of data.entries) {
|
|
1945
|
-
compiler.setCached(name, entry.hash, entry.compiledCode, entry.dependencies);
|
|
1946
|
-
}
|
|
1947
|
-
for (const { name, deps } of data.dependencies) {
|
|
1948
|
-
compiler.updateDependencies(name, deps);
|
|
1949
|
-
}
|
|
1950
|
-
return compiler;
|
|
1951
|
-
}
|
|
1952
|
-
var UnauthorizedIncrementalCompilerAccessError = class extends Error {
|
|
1953
|
-
constructor(decision, compilerName) {
|
|
1954
|
-
super(
|
|
1955
|
-
`[${compilerName}] Unauthorized access: ${decision.reason || "Access denied"}
|
|
1956
|
-
Agent Role: ${decision.agentRole || "unknown"}
|
|
1957
|
-
Required Permission: ${decision.requiredPermission || "unknown"}`
|
|
1958
|
-
);
|
|
1959
|
-
this.decision = decision;
|
|
1960
|
-
this.compilerName = compilerName;
|
|
1961
|
-
this.name = "UnauthorizedIncrementalCompilerAccessError";
|
|
1962
|
-
}
|
|
1963
|
-
};
|
|
1964
|
-
|
|
1965
|
-
exports.IncrementalCompiler = IncrementalCompiler;
|
|
1966
|
-
exports.TraitDependencyGraph = TraitDependencyGraph;
|
|
1967
|
-
exports.UnauthorizedIncrementalCompilerAccessError = UnauthorizedIncrementalCompilerAccessError;
|
|
1968
|
-
exports.createIncrementalCompiler = createIncrementalCompiler;
|
|
1969
|
-
exports.deserializeCache = deserializeCache;
|
|
1970
|
-
exports.globalTraitGraph = globalTraitGraph;
|
|
1971
|
-
exports.serializeCache = serializeCache;
|
|
1972
|
-
//# sourceMappingURL=chunk-7H5UNJZD.cjs.map
|
|
1973
|
-
//# sourceMappingURL=chunk-7H5UNJZD.cjs.map
|