@jgengine/core 0.7.0 → 0.9.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.
Files changed (329) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +2 -0
  3. package/dist/ai/flock.d.ts +36 -0
  4. package/dist/ai/flock.js +182 -0
  5. package/dist/ai/groupAssist.d.ts +19 -0
  6. package/dist/ai/groupAssist.js +59 -0
  7. package/dist/ai/laneSelect.d.ts +21 -0
  8. package/dist/ai/laneSelect.js +37 -0
  9. package/dist/ai/mobBrain.d.ts +68 -0
  10. package/dist/ai/mobBrain.js +115 -0
  11. package/dist/ai/spawnDirector.d.ts +5 -0
  12. package/dist/ai/spawnDirector.js +14 -2
  13. package/dist/ai/threat.d.ts +2 -0
  14. package/dist/ai/threat.js +34 -0
  15. package/dist/anim/easing.d.ts +14 -0
  16. package/dist/anim/easing.js +55 -0
  17. package/dist/behaviour/behaviour.d.ts +136 -0
  18. package/dist/behaviour/behaviour.js +388 -0
  19. package/dist/cards/cardPile.d.ts +1 -0
  20. package/dist/cards/cardPile.js +12 -3
  21. package/dist/cartridge/runtime.d.ts +15 -0
  22. package/dist/cartridge/runtime.js +556 -0
  23. package/dist/cartridge/spec.d.ts +188 -0
  24. package/dist/cartridge/spec.js +19 -0
  25. package/dist/cartridge/validate.d.ts +2 -0
  26. package/dist/cartridge/validate.js +78 -0
  27. package/dist/combat/abilityKit.d.ts +22 -2
  28. package/dist/combat/abilityKit.js +72 -13
  29. package/dist/combat/castRunner.d.ts +40 -0
  30. package/dist/combat/castRunner.js +56 -0
  31. package/dist/combat/comboPoints.d.ts +14 -0
  32. package/dist/combat/comboPoints.js +52 -0
  33. package/dist/combat/defensiveWindow.d.ts +1 -1
  34. package/dist/combat/defensiveWindow.js +5 -3
  35. package/dist/combat/effects.d.ts +1 -1
  36. package/dist/combat/effects.js +9 -6
  37. package/dist/combat/projectiles.d.ts +64 -6
  38. package/dist/combat/projectiles.js +232 -74
  39. package/dist/combat/resistance.d.ts +16 -4
  40. package/dist/combat/resistance.js +43 -7
  41. package/dist/combat/resourcePool.d.ts +20 -0
  42. package/dist/combat/resourcePool.js +55 -0
  43. package/dist/combat/shotOrigin.d.ts +40 -0
  44. package/dist/combat/shotOrigin.js +57 -0
  45. package/dist/combat/telegraph.d.ts +30 -1
  46. package/dist/combat/telegraph.js +61 -0
  47. package/dist/commands/commandRegistry.d.ts +1 -1
  48. package/dist/commands/commandRegistry.js +2 -1
  49. package/dist/crafting/recipe.js +4 -1
  50. package/dist/data/dataSource.d.ts +26 -0
  51. package/dist/data/dataSource.js +93 -0
  52. package/dist/data/devProxy.d.ts +11 -0
  53. package/dist/data/devProxy.js +38 -0
  54. package/dist/data/fetchJson.d.ts +20 -0
  55. package/dist/data/fetchJson.js +43 -0
  56. package/dist/data/jsonDataSource.d.ts +4 -0
  57. package/dist/data/jsonDataSource.js +6 -0
  58. package/dist/devtools/devtools.d.ts +205 -0
  59. package/dist/devtools/devtools.js +832 -0
  60. package/dist/devtools/transformTunables.d.ts +14 -0
  61. package/dist/devtools/transformTunables.js +38 -0
  62. package/dist/devtools/tunableSchema.d.ts +140 -0
  63. package/dist/devtools/tunableSchema.js +476 -0
  64. package/dist/faction/factions.d.ts +33 -0
  65. package/dist/faction/factions.js +76 -0
  66. package/dist/faction/reputation.d.ts +32 -0
  67. package/dist/faction/reputation.js +82 -0
  68. package/dist/game/chat.d.ts +68 -0
  69. package/dist/game/chat.js +173 -0
  70. package/dist/game/chatFilter.d.ts +15 -0
  71. package/dist/game/chatFilter.js +55 -0
  72. package/dist/game/controlGate.d.ts +4 -0
  73. package/dist/game/controlGate.js +7 -0
  74. package/dist/game/defineGame.d.ts +10 -2
  75. package/dist/game/defineGame.js +2 -1
  76. package/dist/game/events.d.ts +35 -0
  77. package/dist/game/gamePhase.d.ts +20 -0
  78. package/dist/game/gamePhase.js +21 -0
  79. package/dist/game/levelSequence.d.ts +44 -0
  80. package/dist/game/levelSequence.js +78 -0
  81. package/dist/game/lootTable.d.ts +10 -1
  82. package/dist/game/lootTable.js +27 -7
  83. package/dist/game/playableGame.d.ts +286 -6
  84. package/dist/game/playableGame.js +6 -1
  85. package/dist/game/progression.d.ts +1 -0
  86. package/dist/game/progression.js +23 -2
  87. package/dist/game/quest.d.ts +7 -3
  88. package/dist/game/quest.js +17 -8
  89. package/dist/game/race.d.ts +44 -1
  90. package/dist/game/race.js +116 -11
  91. package/dist/game/recordBook.d.ts +29 -0
  92. package/dist/game/recordBook.js +86 -0
  93. package/dist/game/social.d.ts +42 -0
  94. package/dist/game/social.js +122 -0
  95. package/dist/game/spawnPoints.d.ts +19 -0
  96. package/dist/game/spawnPoints.js +23 -0
  97. package/dist/game/talents.d.ts +47 -0
  98. package/dist/game/talents.js +140 -0
  99. package/dist/game/worldItem.d.ts +3 -0
  100. package/dist/game/worldItem.js +12 -6
  101. package/dist/index.d.ts +1 -0
  102. package/dist/index.js +1 -0
  103. package/dist/input/actionBindings.d.ts +10 -0
  104. package/dist/input/actionBindings.js +14 -0
  105. package/dist/input/axisInput.d.ts +26 -3
  106. package/dist/input/axisInput.js +44 -4
  107. package/dist/input/bindingOverrides.d.ts +25 -0
  108. package/dist/input/bindingOverrides.js +95 -0
  109. package/dist/input/gestureSurface.d.ts +31 -0
  110. package/dist/input/gestureSurface.js +120 -0
  111. package/dist/input/pointer.d.ts +45 -0
  112. package/dist/input/pointer.js +48 -0
  113. package/dist/input/pointerAxis.d.ts +30 -0
  114. package/dist/input/pointerAxis.js +31 -0
  115. package/dist/input/touchScheme.d.ts +89 -0
  116. package/dist/input/touchScheme.js +153 -0
  117. package/dist/item/affix.d.ts +1 -1
  118. package/dist/item/affix.js +1 -19
  119. package/dist/meta/changelog.d.ts +1 -1
  120. package/dist/meta/changelog.js +59 -1
  121. package/dist/movement/dash.d.ts +3 -0
  122. package/dist/movement/dash.js +19 -5
  123. package/dist/movement/glideModel.d.ts +44 -0
  124. package/dist/movement/glideModel.js +54 -0
  125. package/dist/movement/grappleSwing.d.ts +34 -0
  126. package/dist/movement/grappleSwing.js +56 -0
  127. package/dist/movement/leaderTrail.d.ts +29 -0
  128. package/dist/movement/leaderTrail.js +67 -0
  129. package/dist/movement/movementModel.d.ts +36 -3
  130. package/dist/movement/movementModel.js +81 -5
  131. package/dist/movement/steering.d.ts +27 -0
  132. package/dist/movement/steering.js +40 -0
  133. package/dist/movement/voxelController.d.ts +61 -0
  134. package/dist/movement/voxelController.js +229 -0
  135. package/dist/multiplayer/chatContract.d.ts +42 -0
  136. package/dist/multiplayer/chatContract.js +57 -0
  137. package/dist/multiplayer/feedWriteGate.d.ts +10 -0
  138. package/dist/multiplayer/feedWriteGate.js +12 -0
  139. package/dist/multiplayer/identity.d.ts +13 -0
  140. package/dist/multiplayer/identity.js +20 -0
  141. package/dist/multiplayer/poseSyncGate.d.ts +1 -0
  142. package/dist/multiplayer/poseSyncGate.js +13 -1
  143. package/dist/multiplayer/presenceModel.d.ts +17 -2
  144. package/dist/multiplayer/presenceModel.js +30 -4
  145. package/dist/multiplayer/voiceContract.d.ts +44 -0
  146. package/dist/multiplayer/voiceContract.js +108 -0
  147. package/dist/nav/corridors.d.ts +30 -0
  148. package/dist/nav/corridors.js +69 -0
  149. package/dist/nav/navConstrain.d.ts +14 -0
  150. package/dist/nav/navConstrain.js +26 -0
  151. package/dist/nav/navFromEnvironment.d.ts +11 -0
  152. package/dist/nav/navFromEnvironment.js +16 -0
  153. package/dist/nav/navGrid.d.ts +10 -0
  154. package/dist/nav/navGrid.js +48 -5
  155. package/dist/nav/pathFollow.d.ts +4 -2
  156. package/dist/nav/pathFollow.js +5 -3
  157. package/dist/nav/railGraph.d.ts +66 -0
  158. package/dist/nav/railGraph.js +129 -0
  159. package/dist/nav/timetable.d.ts +41 -0
  160. package/dist/nav/timetable.js +94 -0
  161. package/dist/physics/ballisticSweep.d.ts +18 -0
  162. package/dist/physics/ballisticSweep.js +49 -0
  163. package/dist/physics/buoyancy.d.ts +6 -0
  164. package/dist/physics/buoyancy.js +14 -0
  165. package/dist/physics/flowTube.d.ts +32 -0
  166. package/dist/physics/flowTube.js +64 -0
  167. package/dist/physics/forceVolume.d.ts +27 -0
  168. package/dist/physics/forceVolume.js +43 -5
  169. package/dist/physics/kinematicVehicle.d.ts +52 -0
  170. package/dist/physics/kinematicVehicle.js +81 -0
  171. package/dist/physics/physicsWorld.d.ts +54 -8
  172. package/dist/physics/physicsWorld.js +400 -79
  173. package/dist/physics/ragdoll.js +1 -1
  174. package/dist/physics/spatialGrid.d.ts +1 -1
  175. package/dist/physics/spatialGrid.js +5 -0
  176. package/dist/physics/vehicleBody.d.ts +6 -0
  177. package/dist/physics/vehicleBody.js +18 -18
  178. package/dist/puzzle/cellGrid.d.ts +26 -0
  179. package/dist/puzzle/cellGrid.js +110 -0
  180. package/dist/puzzle/fallingPiece.d.ts +24 -0
  181. package/dist/puzzle/fallingPiece.js +51 -0
  182. package/dist/random/nameGen.d.ts +18 -0
  183. package/dist/random/nameGen.js +35 -0
  184. package/dist/random/rng.d.ts +2 -0
  185. package/dist/random/rng.js +23 -0
  186. package/dist/random/seedLink.d.ts +5 -0
  187. package/dist/random/seedLink.js +35 -0
  188. package/dist/runtime/adapter.d.ts +35 -0
  189. package/dist/runtime/adapter.js +37 -1
  190. package/dist/runtime/cameraDirector.d.ts +15 -0
  191. package/dist/runtime/cameraDirector.js +31 -0
  192. package/dist/runtime/commandRunner.d.ts +2 -2
  193. package/dist/runtime/commandRunner.js +2 -2
  194. package/dist/runtime/gameContext.d.ts +65 -2
  195. package/dist/runtime/gameContext.js +280 -9
  196. package/dist/runtime/gameRuntime.d.ts +5 -1
  197. package/dist/runtime/gameRuntime.js +13 -12
  198. package/dist/runtime/hostPersistence.d.ts +30 -1
  199. package/dist/runtime/hostPersistence.js +70 -5
  200. package/dist/runtime/inputSnapshot.d.ts +12 -0
  201. package/dist/runtime/inputSnapshot.js +17 -0
  202. package/dist/runtime/motionIntents.d.ts +33 -0
  203. package/dist/runtime/motionIntents.js +43 -0
  204. package/dist/runtime/snapshot.d.ts +1 -0
  205. package/dist/runtime/snapshot.js +1 -1
  206. package/dist/runtime/transport.d.ts +32 -0
  207. package/dist/runtime/visibility.d.ts +74 -0
  208. package/dist/runtime/visibility.js +127 -0
  209. package/dist/scene/colliders.d.ts +44 -0
  210. package/dist/scene/colliders.js +77 -0
  211. package/dist/scene/entityStore.d.ts +30 -5
  212. package/dist/scene/entityStore.js +83 -13
  213. package/dist/scene/movementSpeed.d.ts +35 -0
  214. package/dist/scene/movementSpeed.js +20 -0
  215. package/dist/scene/objectQuery.d.ts +29 -0
  216. package/dist/scene/objectQuery.js +140 -0
  217. package/dist/scene/objectStore.d.ts +13 -0
  218. package/dist/scene/objectStore.js +130 -6
  219. package/dist/scene/paintLayer.d.ts +15 -0
  220. package/dist/scene/paintLayer.js +53 -0
  221. package/dist/scene/sceneRaycast.d.ts +80 -0
  222. package/dist/scene/sceneRaycast.js +284 -0
  223. package/dist/scene/spatial.d.ts +17 -1
  224. package/dist/scene/spatial.js +140 -17
  225. package/dist/sensor/concealment.d.ts +21 -0
  226. package/dist/sensor/concealment.js +63 -0
  227. package/dist/sensor/freezeMonitor.d.ts +17 -0
  228. package/dist/sensor/freezeMonitor.js +35 -0
  229. package/dist/sensor/recordingBuffer.d.ts +6 -0
  230. package/dist/sensor/recordingBuffer.js +7 -0
  231. package/dist/sensor/replayLoop.d.ts +39 -0
  232. package/dist/sensor/replayLoop.js +64 -0
  233. package/dist/sensor/visionCone.d.ts +31 -0
  234. package/dist/sensor/visionCone.js +72 -0
  235. package/dist/session/extraction.d.ts +2 -2
  236. package/dist/session/extraction.js +13 -2
  237. package/dist/session/roles.d.ts +6 -0
  238. package/dist/session/roles.js +32 -0
  239. package/dist/session/roundState.d.ts +41 -17
  240. package/dist/session/roundState.js +67 -38
  241. package/dist/settings/settingsModel.d.ts +97 -0
  242. package/dist/settings/settingsModel.js +100 -0
  243. package/dist/stats/statModifiers.d.ts +5 -2
  244. package/dist/stats/statModifiers.js +32 -14
  245. package/dist/store/observableKeyedStore.js +8 -2
  246. package/dist/survival/decayMeter.d.ts +1 -1
  247. package/dist/survival/decayMeter.js +0 -1
  248. package/dist/tactics/fallingGrid.d.ts +62 -0
  249. package/dist/tactics/fallingGrid.js +158 -0
  250. package/dist/tactics/tacticalGrid.d.ts +11 -0
  251. package/dist/tactics/tacticalGrid.js +17 -0
  252. package/dist/time/beatClock.d.ts +18 -0
  253. package/dist/time/beatClock.js +24 -0
  254. package/dist/time/idleProgress.d.ts +29 -0
  255. package/dist/time/idleProgress.js +31 -0
  256. package/dist/time/serverTick.d.ts +7 -1
  257. package/dist/time/serverTick.js +27 -4
  258. package/dist/time/simClock.d.ts +25 -1
  259. package/dist/time/simClock.js +28 -3
  260. package/dist/time/stateSchedule.d.ts +57 -0
  261. package/dist/time/stateSchedule.js +121 -0
  262. package/dist/turn/commit.d.ts +2 -2
  263. package/dist/turn/commit.js +2 -2
  264. package/dist/turn/intent.d.ts +14 -0
  265. package/dist/turn/intent.js +29 -0
  266. package/dist/turn/turnLoop.d.ts +2 -0
  267. package/dist/turn/turnLoop.js +8 -1
  268. package/dist/ui/hudLayout.d.ts +58 -0
  269. package/dist/ui/hudLayout.js +207 -0
  270. package/dist/ui/hudScale.d.ts +44 -0
  271. package/dist/ui/hudScale.js +55 -0
  272. package/dist/visibility/assetStreaming.d.ts +66 -0
  273. package/dist/visibility/assetStreaming.js +201 -0
  274. package/dist/visibility/bounds.d.ts +66 -0
  275. package/dist/visibility/bounds.js +113 -0
  276. package/dist/visibility/camera.d.ts +21 -0
  277. package/dist/visibility/camera.js +3 -0
  278. package/dist/visibility/config.d.ts +21 -0
  279. package/dist/visibility/config.js +1 -0
  280. package/dist/visibility/diagnostics.d.ts +26 -0
  281. package/dist/visibility/diagnostics.js +36 -0
  282. package/dist/visibility/distance.d.ts +11 -0
  283. package/dist/visibility/distance.js +30 -0
  284. package/dist/visibility/frustum.d.ts +47 -0
  285. package/dist/visibility/frustum.js +186 -0
  286. package/dist/visibility/occlusion.d.ts +36 -0
  287. package/dist/visibility/occlusion.js +45 -0
  288. package/dist/visibility/settings.d.ts +75 -0
  289. package/dist/visibility/settings.js +49 -0
  290. package/dist/visibility/simulationCulling.d.ts +34 -0
  291. package/dist/visibility/simulationCulling.js +36 -0
  292. package/dist/visibility/spatialIndex.d.ts +45 -0
  293. package/dist/visibility/spatialIndex.js +198 -0
  294. package/dist/visibility/visibilitySystem.d.ts +90 -0
  295. package/dist/visibility/visibilitySystem.js +257 -0
  296. package/dist/world/buildings.d.ts +6 -0
  297. package/dist/world/buildings.js +152 -0
  298. package/dist/world/carve.d.ts +7 -2
  299. package/dist/world/carve.js +17 -7
  300. package/dist/world/cellStates.d.ts +45 -0
  301. package/dist/world/cellStates.js +103 -0
  302. package/dist/world/environmentSummary.d.ts +61 -0
  303. package/dist/world/environmentSummary.js +138 -0
  304. package/dist/world/features.d.ts +138 -9
  305. package/dist/world/features.js +73 -2
  306. package/dist/world/gridInstances.d.ts +12 -0
  307. package/dist/world/gridInstances.js +160 -0
  308. package/dist/world/lod.d.ts +17 -0
  309. package/dist/world/lod.js +66 -0
  310. package/dist/world/mapLayers.d.ts +60 -0
  311. package/dist/world/mapLayers.js +44 -0
  312. package/dist/world/massing.d.ts +45 -0
  313. package/dist/world/massing.js +581 -0
  314. package/dist/world/minimap.d.ts +13 -1
  315. package/dist/world/minimap.js +21 -2
  316. package/dist/world/placementController.d.ts +22 -1
  317. package/dist/world/placementController.js +56 -0
  318. package/dist/world/terrain.d.ts +62 -1
  319. package/dist/world/terrain.js +253 -9
  320. package/dist/world/voxelField.d.ts +54 -0
  321. package/dist/world/voxelField.js +241 -0
  322. package/dist/world/water.d.ts +7 -0
  323. package/dist/world/water.js +19 -4
  324. package/dist/world/weather.d.ts +3 -18
  325. package/dist/world/weather.js +4 -17
  326. package/dist/world/windZones.d.ts +47 -0
  327. package/dist/world/windZones.js +59 -0
  328. package/llms.txt +4983 -0
  329. package/package.json +14 -3
@@ -0,0 +1,14 @@
1
+ export interface TunableTransformResult {
2
+ code: string;
3
+ bound: string[];
4
+ }
5
+ export declare function transformTunableExports(code: string, table: string): TunableTransformResult;
6
+ export declare function tunableModuleTable(id: string): string | null;
7
+ export declare function tunableDiscoveryPlugin(): {
8
+ name: string;
9
+ enforce: "pre";
10
+ transform(code: string, id: string): {
11
+ code: string;
12
+ map: null;
13
+ } | null;
14
+ };
@@ -0,0 +1,38 @@
1
+ const TUNABLE_EXPORT_PATTERN = /^export const ([A-Za-z_$][\w$]*)((?:\s*:\s*[^=;\r\n]+?)?)\s*=\s*("#(?:[0-9a-fA-F]{3,8})"|'#(?:[0-9a-fA-F]{3,8})'|[^"'`;{}<>\r\n]+?)\s*;[ \t]*$/gm;
2
+ export function transformTunableExports(code, table) {
3
+ const bound = [];
4
+ const rewritten = code.replace(TUNABLE_EXPORT_PATTERN, (_match, name, annotation, expression) => {
5
+ bound.push(name);
6
+ return `export let ${name}${annotation} = ${expression};`;
7
+ });
8
+ if (bound.length === 0)
9
+ return { code, bound };
10
+ const bindings = bound
11
+ .map((name) => `__jg_devtools.discover.bind(${JSON.stringify(table)}, ${JSON.stringify(name)}, { initial: ${name}, get: () => ${name}, set: (__jg_value) => { ${name} = __jg_value as never; } });`)
12
+ .join("\n");
13
+ return {
14
+ code: `${rewritten}\nimport { devtools as __jg_devtools } from "@jgengine/core/devtools/devtools";\n${bindings}\n`,
15
+ bound,
16
+ };
17
+ }
18
+ const GAME_MODULE_PATTERN = /[/\\]Games[/\\][^/\\]+[/\\]src[/\\](.+)\.(ts|tsx)$/;
19
+ const SKIP_MODULE_PATTERN = /(\.test\.|[/\\]main\.tsx$)/;
20
+ export function tunableModuleTable(id) {
21
+ if (SKIP_MODULE_PATTERN.test(id))
22
+ return null;
23
+ const match = GAME_MODULE_PATTERN.exec(id);
24
+ return match === null ? null : match[1].replace(/\\/g, "/");
25
+ }
26
+ export function tunableDiscoveryPlugin() {
27
+ return {
28
+ name: "jgengine-tunable-discovery",
29
+ enforce: "pre",
30
+ transform(code, id) {
31
+ const table = tunableModuleTable(id);
32
+ if (table === null)
33
+ return null;
34
+ const result = transformTunableExports(code, table);
35
+ return result.bound.length === 0 ? null : { code: result.code, map: null };
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,140 @@
1
+ export type DevtoolsControlKind = "slider" | "toggle" | "color" | "select" | "text" | "vec2" | "vec3" | "vec4" | "interval" | "angle" | "enum";
2
+ export type AngleUnit = "rad" | "deg";
3
+ export type TunableVec2 = [number, number];
4
+ export type TunableVec3 = [number, number, number];
5
+ export type TunableVec4 = [number, number, number, number];
6
+ export type TunableInterval = {
7
+ min: number;
8
+ max: number;
9
+ };
10
+ export interface TunableChoice<T = unknown> {
11
+ readonly value: T;
12
+ readonly label?: string;
13
+ }
14
+ export interface TunableOptions<T = unknown> {
15
+ min?: number;
16
+ max?: number;
17
+ step?: number;
18
+ options?: readonly T[];
19
+ choices?: readonly TunableChoice<T>[];
20
+ label?: string;
21
+ group?: string;
22
+ onChange?: (value: T) => void;
23
+ kind?: DevtoolsControlKind;
24
+ unit?: AngleUnit;
25
+ displayUnit?: AngleUnit;
26
+ wrap?: boolean;
27
+ integer?: boolean;
28
+ axisLabels?: readonly string[];
29
+ axisMin?: number | readonly number[];
30
+ axisMax?: number | readonly number[];
31
+ axisStep?: number | readonly number[];
32
+ alpha?: boolean;
33
+ }
34
+ export interface DiscoverySkip {
35
+ readonly path: string;
36
+ readonly reason: string;
37
+ }
38
+ export interface ScanFieldMeta extends TunableOptions {
39
+ kind?: DevtoolsControlKind;
40
+ }
41
+ export type ScanMeta = Readonly<Record<string, ScanFieldMeta>>;
42
+ export declare const OVERRIDES_FORMAT_VERSION = 1;
43
+ export declare const CONTROL_SCHEMA_VERSION = 1;
44
+ export declare const MAX_TABLE_ENTRIES = 64;
45
+ export declare const MAX_SCAN_DEPTH = 5;
46
+ export declare const MAX_SCAN_TARGETS = 512;
47
+ export interface NormalizedColor {
48
+ readonly hex: string;
49
+ readonly rgb: string;
50
+ readonly alpha: number;
51
+ readonly hasAlpha: boolean;
52
+ }
53
+ export interface ResolvedAxisBounds {
54
+ readonly labels: readonly string[];
55
+ readonly min: readonly number[];
56
+ readonly max: readonly number[];
57
+ readonly step: readonly number[];
58
+ }
59
+ export declare function isFiniteNumber(value: unknown): value is number;
60
+ export declare function escapePathSegment(segment: string): string;
61
+ export declare function unescapePathSegment(segment: string): string;
62
+ export declare function joinTunablePath(parent: string, segment: string): string;
63
+ export declare function splitTunablePath(path: string): string[];
64
+ export declare function parseColor(input: unknown): NormalizedColor | null;
65
+ export declare function formatColor(rgb: string, alpha: number, withAlpha: boolean): string | null;
66
+ export declare function normalizeColorValue(input: unknown, forceAlpha?: boolean): string | null;
67
+ export declare function isColorString(value: unknown): value is string;
68
+ export declare function vecLength(kind: "vec2" | "vec3" | "vec4"): number;
69
+ export declare function isNumericTuple(value: unknown, length: number): value is number[];
70
+ export declare function isIntervalShape(value: unknown): value is TunableInterval;
71
+ export declare function expandAxisMeta(length: number, labels: readonly string[] | undefined, min: number | readonly number[] | undefined, max: number | readonly number[] | undefined, step: number | readonly number[] | undefined, sample: readonly number[]): ResolvedAxisBounds;
72
+ export declare function clampAxisValue(value: number, min: number, max: number): number;
73
+ export declare function parseVec(kind: "vec2" | "vec3" | "vec4", raw: unknown, bounds: ResolvedAxisBounds): number[] | null;
74
+ export declare function parseInterval(raw: unknown, options: {
75
+ min?: number;
76
+ max?: number;
77
+ step?: number;
78
+ integer?: boolean;
79
+ }): TunableInterval | null;
80
+ export declare function convertAngle(value: number, from: AngleUnit, to: AngleUnit): number;
81
+ export declare function normalizeAngle(value: number, unit: AngleUnit, options: {
82
+ min?: number;
83
+ max?: number;
84
+ wrap?: boolean;
85
+ }): number;
86
+ export declare function resolveChoices<T>(options: readonly T[] | undefined, choices: readonly TunableChoice<T>[] | undefined): readonly TunableChoice<T>[] | undefined;
87
+ export declare function choiceValues(choices: readonly TunableChoice[] | undefined): readonly unknown[] | undefined;
88
+ export declare function findChoice(choices: readonly TunableChoice[] | undefined, raw: unknown): TunableChoice | undefined;
89
+ export declare function parseEnumValue(raw: unknown, choices: readonly TunableChoice[] | undefined): unknown | null;
90
+ export declare function coerceSelectWrite(raw: unknown, choices: readonly TunableChoice[] | undefined, options: readonly unknown[] | undefined): unknown | null;
91
+ export declare function inferKind(initial: unknown, options?: TunableOptions): DevtoolsControlKind;
92
+ export declare function discoverableKind(value: unknown, meta?: ScanFieldMeta): DevtoolsControlKind | null;
93
+ export declare function isScannableContainer(value: unknown): value is Record<string, unknown>;
94
+ export declare function ownWritableDataKeys(target: Record<string, unknown>): string[];
95
+ export declare function sliderBounds(initial: number, options: {
96
+ min?: number;
97
+ max?: number;
98
+ step?: number;
99
+ } | undefined): {
100
+ min: number;
101
+ max: number;
102
+ step: number;
103
+ };
104
+ export declare function validateControlValue(kind: DevtoolsControlKind, raw: unknown, context: {
105
+ min?: number;
106
+ max?: number;
107
+ step?: number;
108
+ integer?: boolean;
109
+ unit?: AngleUnit;
110
+ wrap?: boolean;
111
+ choices?: readonly TunableChoice[];
112
+ options?: readonly unknown[];
113
+ axisBounds?: ResolvedAxisBounds;
114
+ alpha?: boolean;
115
+ }): {
116
+ ok: true;
117
+ value: unknown;
118
+ } | {
119
+ ok: false;
120
+ reason: string;
121
+ };
122
+ export interface DevtoolsOverrides {
123
+ version: number;
124
+ enabled: string[];
125
+ values: Record<string, unknown>;
126
+ schemas?: Record<string, {
127
+ kind: DevtoolsControlKind;
128
+ schemaVersion?: number;
129
+ }>;
130
+ }
131
+ export interface OverrideApplyDiagnostic {
132
+ readonly id: string;
133
+ readonly reason: string;
134
+ }
135
+ export interface OverrideParseResult {
136
+ readonly overrides: DevtoolsOverrides | null;
137
+ readonly diagnostics: readonly string[];
138
+ }
139
+ export declare function parseOverridesPayload(raw: unknown): OverrideParseResult;
140
+ export declare function cloneValue<T>(value: T): T;
@@ -0,0 +1,476 @@
1
+ export const OVERRIDES_FORMAT_VERSION = 1;
2
+ export const CONTROL_SCHEMA_VERSION = 1;
3
+ export const MAX_TABLE_ENTRIES = 64;
4
+ export const MAX_SCAN_DEPTH = 5;
5
+ export const MAX_SCAN_TARGETS = 512;
6
+ const COLOR_PATTERN = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
7
+ const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
8
+ const DEFAULT_AXIS = ["x", "y", "z", "w"];
9
+ export function isFiniteNumber(value) {
10
+ return typeof value === "number" && Number.isFinite(value);
11
+ }
12
+ export function escapePathSegment(segment) {
13
+ return segment.replace(/\\/g, "\\\\").replace(/\./g, "\\.");
14
+ }
15
+ export function unescapePathSegment(segment) {
16
+ let out = "";
17
+ for (let i = 0; i < segment.length; i += 1) {
18
+ const ch = segment[i];
19
+ if (ch === "\\" && i + 1 < segment.length) {
20
+ out += segment[i + 1];
21
+ i += 1;
22
+ continue;
23
+ }
24
+ out += ch;
25
+ }
26
+ return out;
27
+ }
28
+ export function joinTunablePath(parent, segment) {
29
+ const escaped = escapePathSegment(segment);
30
+ return parent === "" ? escaped : `${parent}.${escaped}`;
31
+ }
32
+ export function splitTunablePath(path) {
33
+ const parts = [];
34
+ let current = "";
35
+ for (let i = 0; i < path.length; i += 1) {
36
+ const ch = path[i];
37
+ if (ch === "\\" && i + 1 < path.length) {
38
+ current += ch + path[i + 1];
39
+ i += 1;
40
+ continue;
41
+ }
42
+ if (ch === ".") {
43
+ parts.push(unescapePathSegment(current));
44
+ current = "";
45
+ continue;
46
+ }
47
+ current += ch;
48
+ }
49
+ parts.push(unescapePathSegment(current));
50
+ return parts;
51
+ }
52
+ export function parseColor(input) {
53
+ if (typeof input !== "string" || !COLOR_PATTERN.test(input))
54
+ return null;
55
+ const raw = input.slice(1);
56
+ let r;
57
+ let g;
58
+ let b;
59
+ let a = null;
60
+ if (raw.length === 3) {
61
+ r = raw[0] + raw[0];
62
+ g = raw[1] + raw[1];
63
+ b = raw[2] + raw[2];
64
+ }
65
+ else if (raw.length === 6) {
66
+ r = raw.slice(0, 2);
67
+ g = raw.slice(2, 4);
68
+ b = raw.slice(4, 6);
69
+ }
70
+ else {
71
+ r = raw.slice(0, 2);
72
+ g = raw.slice(2, 4);
73
+ b = raw.slice(4, 6);
74
+ a = raw.slice(6, 8);
75
+ }
76
+ const rgb = `#${r.toLowerCase()}${g.toLowerCase()}${b.toLowerCase()}`;
77
+ if (a === null) {
78
+ return { hex: rgb, rgb, alpha: 1, hasAlpha: false };
79
+ }
80
+ const alpha = Number.parseInt(a, 16) / 255;
81
+ const hex = `${rgb}${a.toLowerCase()}`;
82
+ return { hex, rgb, alpha, hasAlpha: true };
83
+ }
84
+ export function formatColor(rgb, alpha, withAlpha) {
85
+ const parsed = parseColor(rgb);
86
+ const rgbHex = parsed?.rgb ?? (/^#[0-9a-fA-F]{6}$/.test(rgb) ? rgb.toLowerCase() : null);
87
+ if (rgbHex === null)
88
+ return null;
89
+ if (!withAlpha)
90
+ return rgbHex;
91
+ const clamped = Math.min(1, Math.max(0, alpha));
92
+ const a = Math.round(clamped * 255)
93
+ .toString(16)
94
+ .padStart(2, "0");
95
+ return `${rgbHex}${a}`;
96
+ }
97
+ export function normalizeColorValue(input, forceAlpha) {
98
+ const parsed = parseColor(input);
99
+ if (parsed === null)
100
+ return null;
101
+ if (forceAlpha === true || parsed.hasAlpha) {
102
+ return formatColor(parsed.rgb, parsed.alpha, true);
103
+ }
104
+ return parsed.rgb;
105
+ }
106
+ export function isColorString(value) {
107
+ return typeof value === "string" && COLOR_PATTERN.test(value);
108
+ }
109
+ export function vecLength(kind) {
110
+ if (kind === "vec2")
111
+ return 2;
112
+ if (kind === "vec3")
113
+ return 3;
114
+ return 4;
115
+ }
116
+ export function isNumericTuple(value, length) {
117
+ if (!Array.isArray(value) || value.length !== length)
118
+ return false;
119
+ return value.every(isFiniteNumber);
120
+ }
121
+ export function isIntervalShape(value) {
122
+ if (value === null || typeof value !== "object" || Array.isArray(value))
123
+ return false;
124
+ const record = value;
125
+ const keys = Object.keys(record);
126
+ if (keys.length !== 2 || !keys.includes("min") || !keys.includes("max"))
127
+ return false;
128
+ return isFiniteNumber(record.min) && isFiniteNumber(record.max);
129
+ }
130
+ export function expandAxisMeta(length, labels, min, max, step, sample) {
131
+ const resolvedLabels = Array.from({ length }, (_, i) => labels?.[i] ?? DEFAULT_AXIS[i] ?? `a${i}`);
132
+ const resolvedMin = Array.from({ length }, (_, i) => {
133
+ if (Array.isArray(min))
134
+ return min[i] ?? sample[i] * 2;
135
+ if (typeof min === "number")
136
+ return min;
137
+ const v = sample[i];
138
+ return v < 0 ? v * 2 : 0;
139
+ });
140
+ const resolvedMax = Array.from({ length }, (_, i) => {
141
+ if (Array.isArray(max))
142
+ return max[i] ?? (sample[i] > 0 ? sample[i] * 2 : 1);
143
+ if (typeof max === "number")
144
+ return max;
145
+ const v = sample[i];
146
+ return v > 0 ? v * 2 : v < 0 ? 0 : 1;
147
+ });
148
+ const resolvedStep = Array.from({ length }, (_, i) => {
149
+ if (Array.isArray(step))
150
+ return step[i] ?? (resolvedMax[i] - resolvedMin[i]) / 100;
151
+ if (typeof step === "number")
152
+ return step;
153
+ return (resolvedMax[i] - resolvedMin[i]) / 100;
154
+ });
155
+ return { labels: resolvedLabels, min: resolvedMin, max: resolvedMax, step: resolvedStep };
156
+ }
157
+ export function clampAxisValue(value, min, max) {
158
+ return Math.min(max, Math.max(min, value));
159
+ }
160
+ export function parseVec(kind, raw, bounds) {
161
+ const length = vecLength(kind);
162
+ if (!isNumericTuple(raw, length))
163
+ return null;
164
+ return raw.map((value, i) => clampAxisValue(value, bounds.min[i], bounds.max[i]));
165
+ }
166
+ export function parseInterval(raw, options) {
167
+ if (!isIntervalShape(raw))
168
+ return null;
169
+ let lo = raw.min;
170
+ let hi = raw.max;
171
+ if (options.integer === true) {
172
+ if (!Number.isInteger(lo) || !Number.isInteger(hi))
173
+ return null;
174
+ }
175
+ if (options.step !== undefined && options.step > 0) {
176
+ const step = options.step;
177
+ const base = options.min ?? 0;
178
+ const onStep = (n) => Math.abs((n - base) / step - Math.round((n - base) / step)) < 1e-9;
179
+ if (!onStep(lo) || !onStep(hi))
180
+ return null;
181
+ }
182
+ if (lo > hi) {
183
+ const tmp = lo;
184
+ lo = hi;
185
+ hi = tmp;
186
+ }
187
+ if (options.min !== undefined) {
188
+ lo = Math.max(options.min, lo);
189
+ hi = Math.max(options.min, hi);
190
+ }
191
+ if (options.max !== undefined) {
192
+ lo = Math.min(options.max, lo);
193
+ hi = Math.min(options.max, hi);
194
+ }
195
+ if (lo > hi) {
196
+ lo = options.min ?? lo;
197
+ hi = options.max ?? hi;
198
+ if (lo > hi)
199
+ return null;
200
+ }
201
+ return { min: lo, max: hi };
202
+ }
203
+ export function convertAngle(value, from, to) {
204
+ if (from === to)
205
+ return value;
206
+ if (from === "deg" && to === "rad")
207
+ return (value * Math.PI) / 180;
208
+ return (value * 180) / Math.PI;
209
+ }
210
+ export function normalizeAngle(value, unit, options) {
211
+ let next = value;
212
+ if (options.wrap === true) {
213
+ const min = options.min ?? (unit === "deg" ? 0 : -Math.PI);
214
+ const max = options.max ?? (unit === "deg" ? 360 : Math.PI);
215
+ const span = max - min;
216
+ if (span > 0) {
217
+ next = ((((next - min) % span) + span) % span) + min;
218
+ }
219
+ }
220
+ else {
221
+ if (options.min !== undefined)
222
+ next = Math.max(options.min, next);
223
+ if (options.max !== undefined)
224
+ next = Math.min(options.max, next);
225
+ }
226
+ return next;
227
+ }
228
+ export function resolveChoices(options, choices) {
229
+ if (choices !== undefined)
230
+ return choices;
231
+ if (options !== undefined)
232
+ return options.map((value) => ({ value }));
233
+ return undefined;
234
+ }
235
+ export function choiceValues(choices) {
236
+ if (choices === undefined)
237
+ return undefined;
238
+ return choices.map((choice) => choice.value);
239
+ }
240
+ export function findChoice(choices, raw) {
241
+ if (choices === undefined)
242
+ return undefined;
243
+ return choices.find((choice) => Object.is(choice.value, raw));
244
+ }
245
+ export function parseEnumValue(raw, choices) {
246
+ if (choices === undefined)
247
+ return null;
248
+ const match = findChoice(choices, raw);
249
+ return match === undefined ? null : match.value;
250
+ }
251
+ export function coerceSelectWrite(raw, choices, options) {
252
+ if (choices !== undefined) {
253
+ const byValue = findChoice(choices, raw);
254
+ if (byValue !== undefined)
255
+ return byValue.value;
256
+ if (typeof raw === "string") {
257
+ const byLabel = choices.find((choice) => choice.label === raw);
258
+ if (byLabel !== undefined)
259
+ return byLabel.value;
260
+ const byString = choices.find((choice) => String(choice.value) === raw);
261
+ if (byString !== undefined)
262
+ return byString.value;
263
+ }
264
+ return null;
265
+ }
266
+ if (options !== undefined) {
267
+ if (options.some((option) => Object.is(option, raw)))
268
+ return raw;
269
+ if (typeof raw === "string") {
270
+ const match = options.find((option) => String(option) === raw);
271
+ if (match !== undefined)
272
+ return match;
273
+ }
274
+ return null;
275
+ }
276
+ return raw;
277
+ }
278
+ export function inferKind(initial, options) {
279
+ if (options?.kind !== undefined)
280
+ return options.kind;
281
+ if (options?.choices !== undefined || options?.options !== undefined) {
282
+ return options.choices !== undefined ? "enum" : "select";
283
+ }
284
+ if (typeof initial === "number")
285
+ return "slider";
286
+ if (typeof initial === "boolean")
287
+ return "toggle";
288
+ if (isColorString(initial))
289
+ return "color";
290
+ return "text";
291
+ }
292
+ export function discoverableKind(value, meta) {
293
+ if (meta?.kind !== undefined) {
294
+ if (meta.kind === "vec2" || meta.kind === "vec3" || meta.kind === "vec4") {
295
+ return isNumericTuple(value, vecLength(meta.kind)) ? meta.kind : null;
296
+ }
297
+ if (meta.kind === "interval")
298
+ return isIntervalShape(value) ? "interval" : null;
299
+ if (meta.kind === "angle")
300
+ return isFiniteNumber(value) ? "angle" : null;
301
+ if (meta.kind === "enum" || meta.kind === "select") {
302
+ const choices = resolveChoices(meta.options, meta.choices);
303
+ return choices !== undefined ? meta.kind : null;
304
+ }
305
+ if (meta.kind === "color")
306
+ return isColorString(value) ? "color" : null;
307
+ if (meta.kind === "slider")
308
+ return isFiniteNumber(value) ? "slider" : null;
309
+ if (meta.kind === "toggle")
310
+ return typeof value === "boolean" ? "toggle" : null;
311
+ if (meta.kind === "text")
312
+ return typeof value === "string" ? "text" : null;
313
+ return meta.kind;
314
+ }
315
+ if (typeof value === "number" && Number.isFinite(value))
316
+ return "slider";
317
+ if (typeof value === "boolean")
318
+ return "toggle";
319
+ if (isColorString(value))
320
+ return "color";
321
+ return null;
322
+ }
323
+ export function isScannableContainer(value) {
324
+ if (value === null || typeof value !== "object")
325
+ return false;
326
+ if (Array.isArray(value))
327
+ return value.length <= MAX_TABLE_ENTRIES;
328
+ const proto = Object.getPrototypeOf(value);
329
+ return proto === Object.prototype || proto === null;
330
+ }
331
+ export function ownWritableDataKeys(target) {
332
+ return Object.keys(target).filter((key) => {
333
+ if (UNSAFE_KEYS.has(key))
334
+ return false;
335
+ const desc = Object.getOwnPropertyDescriptor(target, key);
336
+ if (desc === undefined)
337
+ return false;
338
+ if (!("value" in desc))
339
+ return false;
340
+ if (desc.writable === false)
341
+ return false;
342
+ return true;
343
+ });
344
+ }
345
+ export function sliderBounds(initial, options) {
346
+ const min = options?.min ?? (initial < 0 ? initial * 2 : 0);
347
+ const max = options?.max ?? (initial > 0 ? initial * 2 : initial < 0 ? 0 : 1);
348
+ const step = options?.step ?? (max - min) / 100;
349
+ return { min, max, step };
350
+ }
351
+ export function validateControlValue(kind, raw, context) {
352
+ switch (kind) {
353
+ case "slider": {
354
+ if (!isFiniteNumber(raw))
355
+ return { ok: false, reason: "slider requires finite number" };
356
+ let next = raw;
357
+ if (context.min !== undefined)
358
+ next = Math.max(context.min, next);
359
+ if (context.max !== undefined)
360
+ next = Math.min(context.max, next);
361
+ return { ok: true, value: next };
362
+ }
363
+ case "angle": {
364
+ if (!isFiniteNumber(raw))
365
+ return { ok: false, reason: "angle requires finite number" };
366
+ const unit = context.unit ?? "rad";
367
+ return {
368
+ ok: true,
369
+ value: normalizeAngle(raw, unit, { min: context.min, max: context.max, wrap: context.wrap }),
370
+ };
371
+ }
372
+ case "toggle":
373
+ if (typeof raw !== "boolean")
374
+ return { ok: false, reason: "toggle requires boolean" };
375
+ return { ok: true, value: raw };
376
+ case "color": {
377
+ const normalized = normalizeColorValue(raw, context.alpha);
378
+ if (normalized === null)
379
+ return { ok: false, reason: "invalid color" };
380
+ return { ok: true, value: normalized };
381
+ }
382
+ case "text":
383
+ if (typeof raw !== "string")
384
+ return { ok: false, reason: "text requires string" };
385
+ return { ok: true, value: raw };
386
+ case "select": {
387
+ const parsed = coerceSelectWrite(raw, context.choices, context.options);
388
+ if (parsed === null)
389
+ return { ok: false, reason: "value not in options" };
390
+ return { ok: true, value: parsed };
391
+ }
392
+ case "enum": {
393
+ const parsed = coerceSelectWrite(raw, context.choices, context.options);
394
+ if (parsed === null)
395
+ return { ok: false, reason: "value not in enum choices" };
396
+ return { ok: true, value: parsed };
397
+ }
398
+ case "vec2":
399
+ case "vec3":
400
+ case "vec4": {
401
+ if (context.axisBounds === undefined)
402
+ return { ok: false, reason: "missing axis bounds" };
403
+ const parsed = parseVec(kind, raw, context.axisBounds);
404
+ if (parsed === null)
405
+ return { ok: false, reason: `${kind} requires finite numeric tuple` };
406
+ return { ok: true, value: parsed };
407
+ }
408
+ case "interval": {
409
+ const parsed = parseInterval(raw, {
410
+ min: context.min,
411
+ max: context.max,
412
+ step: context.step,
413
+ integer: context.integer,
414
+ });
415
+ if (parsed === null)
416
+ return { ok: false, reason: "invalid interval" };
417
+ return { ok: true, value: parsed };
418
+ }
419
+ default:
420
+ return { ok: false, reason: "unknown kind" };
421
+ }
422
+ }
423
+ export function parseOverridesPayload(raw) {
424
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
425
+ return { overrides: null, diagnostics: ["overrides payload is not an object"] };
426
+ }
427
+ const record = raw;
428
+ if (!Array.isArray(record.enabled) || typeof record.values !== "object" || record.values === null || Array.isArray(record.values)) {
429
+ return { overrides: null, diagnostics: ["overrides missing enabled[] or values{}"] };
430
+ }
431
+ const version = typeof record.version === "number" ? record.version : 0;
432
+ if (version > OVERRIDES_FORMAT_VERSION) {
433
+ return {
434
+ overrides: null,
435
+ diagnostics: [`overrides version ${version} is newer than supported ${OVERRIDES_FORMAT_VERSION}`],
436
+ };
437
+ }
438
+ const enabled = record.enabled.filter((id) => typeof id === "string");
439
+ const values = {};
440
+ for (const [key, value] of Object.entries(record.values)) {
441
+ values[key] = value;
442
+ }
443
+ let schemas;
444
+ if (typeof record.schemas === "object" && record.schemas !== null && !Array.isArray(record.schemas)) {
445
+ schemas = {};
446
+ for (const [key, entry] of Object.entries(record.schemas)) {
447
+ if (entry !== null && typeof entry === "object" && !Array.isArray(entry)) {
448
+ const kind = entry.kind;
449
+ if (typeof kind === "string") {
450
+ schemas[key] = {
451
+ kind: kind,
452
+ schemaVersion: typeof entry.schemaVersion === "number"
453
+ ? (entry.schemaVersion)
454
+ : undefined,
455
+ };
456
+ }
457
+ }
458
+ }
459
+ }
460
+ return {
461
+ overrides: {
462
+ version: OVERRIDES_FORMAT_VERSION,
463
+ enabled,
464
+ values,
465
+ schemas,
466
+ },
467
+ diagnostics: version === 0 ? ["migrated unversioned overrides to v1"] : [],
468
+ };
469
+ }
470
+ export function cloneValue(value) {
471
+ if (Array.isArray(value))
472
+ return value.slice();
473
+ if (value !== null && typeof value === "object")
474
+ return { ...value };
475
+ return value;
476
+ }