lula2 0.7.3 → 0.7.5

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 (35) hide show
  1. package/README.md +0 -1
  2. package/dist/_app/immutable/chunks/{DqNseCFX.js → BI-GirXZ.js} +1 -1
  3. package/dist/_app/immutable/chunks/{DHBD-O6i.js → BIdjJ0zz.js} +1 -1
  4. package/dist/_app/immutable/chunks/BSyVkqhj.js +2 -0
  5. package/dist/_app/immutable/chunks/{DzukiT8m.js → Cng7c2CG.js} +1 -1
  6. package/dist/_app/immutable/chunks/CxBMFlfX.js +1 -0
  7. package/dist/_app/immutable/chunks/{D5ermNRH.js → DArZRX9-.js} +36 -36
  8. package/dist/_app/immutable/chunks/{CUt21Xol.js → DH2IP9c7.js} +1 -1
  9. package/dist/_app/immutable/chunks/{BBITAbWI.js → DXSHWIjJ.js} +1 -1
  10. package/dist/_app/immutable/chunks/{Cih4fRH-.js → DsuS1uUo.js} +1 -1
  11. package/dist/_app/immutable/chunks/urFjAlpd.js +1 -0
  12. package/dist/_app/immutable/entry/{app.BvGur0mr.js → app.CjycYot0.js} +2 -2
  13. package/dist/_app/immutable/entry/start.Bgy9x4Qb.js +1 -0
  14. package/dist/_app/immutable/nodes/{0.Dx6r-xPK.js → 0.CGKh5y4X.js} +1 -1
  15. package/dist/_app/immutable/nodes/{1.COpX6TiD.js → 1.D5L7DxSG.js} +1 -1
  16. package/dist/_app/immutable/nodes/{2.zlTZskRg.js → 2.Hrl6uq-b.js} +1 -1
  17. package/dist/_app/immutable/nodes/{3.BafDPdLR.js → 3.BoHxdRm3.js} +1 -1
  18. package/dist/_app/immutable/nodes/{4.CoEqSeTv.js → 4.DAVWsDkK.js} +1 -1
  19. package/dist/_app/version.json +1 -1
  20. package/dist/cli/commands/ui.js +46 -41
  21. package/dist/cli/server/index.js +46 -41
  22. package/dist/cli/server/server.js +46 -41
  23. package/dist/cli/server/serverState.js +23 -11
  24. package/dist/cli/server/websocketServer.js +40 -41
  25. package/dist/index.html +11 -11
  26. package/dist/index.js +49 -44
  27. package/package.json +2 -2
  28. package/src/lib/components/controls/MappingCard.svelte +3 -3
  29. package/src/lib/components/controls/tabs/MappingsTab.svelte +27 -17
  30. package/src/lib/types.ts +2 -0
  31. package/src/lib/websocket.ts +2 -6
  32. package/dist/_app/immutable/chunks/B3ME9fYB.js +0 -1
  33. package/dist/_app/immutable/chunks/DtiGB2s7.js +0 -2
  34. package/dist/_app/immutable/chunks/R-CI0WwD.js +0 -1
  35. package/dist/_app/immutable/entry/start.yE51hRYN.js +0 -1
@@ -1680,6 +1680,7 @@ import {
1680
1680
  } from "fs";
1681
1681
  import * as yaml2 from "js-yaml";
1682
1682
  import { join as join2 } from "path";
1683
+ import { createHash as createHash2 } from "crypto";
1683
1684
  var FileStore;
1684
1685
  var init_fileStore = __esm({
1685
1686
  "cli/server/infrastructure/fileStore.ts"() {
@@ -1977,6 +1978,10 @@ var init_fileStore = __esm({
1977
1978
  const content = readFileSync2(mappingFile, "utf8");
1978
1979
  const parsed = yaml2.load(content);
1979
1980
  if (Array.isArray(parsed)) {
1981
+ parsed.forEach((mapping) => {
1982
+ mapping.hash = createHash2("sha256").update(JSON.stringify(mapping)).digest("hex");
1983
+ return mapping;
1984
+ });
1980
1985
  mappings.push(...parsed);
1981
1986
  }
1982
1987
  } catch (error) {
@@ -2011,12 +2016,9 @@ var init_fileStore = __esm({
2011
2016
  existingMappings = [];
2012
2017
  }
2013
2018
  }
2014
- const existingIndex = existingMappings.findIndex((m) => m.uuid === mapping.uuid);
2015
- if (existingIndex >= 0) {
2016
- existingMappings[existingIndex] = mapping;
2017
- } else {
2018
- existingMappings.push(mapping);
2019
- }
2019
+ const cleanMapping = { ...mapping };
2020
+ delete cleanMapping.hash;
2021
+ existingMappings.push(cleanMapping);
2020
2022
  try {
2021
2023
  const yamlContent = yaml2.dump(existingMappings, {
2022
2024
  indent: 2,
@@ -2041,7 +2043,8 @@ var init_fileStore = __esm({
2041
2043
  let mappings = yaml2.load(content) || [];
2042
2044
  const originalLength = mappings.length;
2043
2045
  mappings = mappings.filter((m) => {
2044
- return `${m.control_id}:${m.uuid}` !== compositeKey;
2046
+ const hash = createHash2("sha256").update(JSON.stringify(m)).digest("hex");
2047
+ return `${m.control_id}:${hash}` !== compositeKey;
2045
2048
  });
2046
2049
  if (mappings.length < originalLength) {
2047
2050
  if (mappings.length === 0) {
@@ -2104,8 +2107,13 @@ var init_fileStore = __esm({
2104
2107
  if (!existsSync2(familyDir)) {
2105
2108
  mkdirSync(familyDir, { recursive: true });
2106
2109
  }
2110
+ const cleanMappings = controlMappings.map((m) => {
2111
+ const clean = { ...m };
2112
+ delete clean.hash;
2113
+ return clean;
2114
+ });
2107
2115
  try {
2108
- const yamlContent = yaml2.dump(controlMappings, {
2116
+ const yamlContent = yaml2.dump(cleanMappings, {
2109
2117
  indent: 2,
2110
2118
  lineWidth: -1,
2111
2119
  noRefs: true
@@ -3024,6 +3032,7 @@ __export(serverState_exports, {
3024
3032
  saveMappingsToFile: () => saveMappingsToFile
3025
3033
  });
3026
3034
  import { join as join3 } from "path";
3035
+ import { createHash as createHash3 } from "crypto";
3027
3036
  function initializeServerState(controlSetDir, subdir = ".") {
3028
3037
  const fullPath = subdir === "." ? controlSetDir : join3(controlSetDir, subdir);
3029
3038
  serverState = {
@@ -3066,11 +3075,11 @@ function addMappingToIndexes(mapping) {
3066
3075
  if (!state.mappingsByFamily.has(family)) {
3067
3076
  state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
3068
3077
  }
3069
- state.mappingsByFamily.get(family).add(mapping.uuid);
3078
+ state.mappingsByFamily.get(family).add(mapping.hash);
3070
3079
  if (!state.mappingsByControl.has(mapping.control_id)) {
3071
3080
  state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
3072
3081
  }
3073
- state.mappingsByControl.get(mapping.control_id).add(mapping.uuid);
3082
+ state.mappingsByControl.get(mapping.control_id).add(mapping.hash);
3074
3083
  }
3075
3084
  async function loadAllData() {
3076
3085
  const state = getServerState();
@@ -3084,7 +3093,10 @@ async function loadAllData() {
3084
3093
  debug(`Loaded ${controls.length} controls from individual files`);
3085
3094
  const mappings = await state.fileStore.loadMappings();
3086
3095
  for (const mapping of mappings) {
3087
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
3096
+ if (!mapping.hash) {
3097
+ mapping.hash = createHash3("sha256").update(JSON.stringify(mapping)).digest("hex");
3098
+ }
3099
+ const compositeKey = `${mapping.control_id}:${mapping.hash}`;
3088
3100
  state.mappingsCache.set(compositeKey, mapping);
3089
3101
  addMappingToIndexes(mapping);
3090
3102
  }
@@ -5132,6 +5144,7 @@ init_gitHistory();
5132
5144
  import * as yaml5 from "js-yaml";
5133
5145
  import { join as join5 } from "path";
5134
5146
  import { WebSocket, WebSocketServer } from "ws";
5147
+ import crypto2 from "node:crypto";
5135
5148
  var WebSocketManager = class {
5136
5149
  wss = null;
5137
5150
  clients = /* @__PURE__ */ new Set();
@@ -5196,21 +5209,24 @@ var WebSocketManager = class {
5196
5209
  if (payload && payload.control_id) {
5197
5210
  const mapping = payload;
5198
5211
  if (!mapping.uuid) {
5199
- const crypto2 = await import("crypto");
5200
- mapping.uuid = crypto2.randomUUID();
5212
+ const crypto3 = await import("crypto");
5213
+ mapping.uuid = crypto3.randomUUID();
5201
5214
  }
5215
+ if (!mapping.hash || mapping.hash === "") {
5216
+ mapping.hash = crypto2.createHash("sha256").update(JSON.stringify(mapping)).digest("hex");
5217
+ }
5218
+ const compositeKey = `${mapping.control_id}:${mapping.hash}`;
5202
5219
  await state.fileStore.saveMapping(mapping);
5203
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
5204
5220
  state.mappingsCache.set(compositeKey, mapping);
5205
5221
  const family = mapping.control_id.split("-")[0];
5206
5222
  if (!state.mappingsByFamily.has(family)) {
5207
5223
  state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
5208
5224
  }
5209
- state.mappingsByFamily.get(family)?.add(mapping.uuid);
5225
+ state.mappingsByFamily.get(family)?.add(mapping.hash);
5210
5226
  if (!state.mappingsByControl.has(mapping.control_id)) {
5211
5227
  state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
5212
5228
  }
5213
- state.mappingsByControl.get(mapping.control_id)?.add(mapping.uuid);
5229
+ state.mappingsByControl.get(mapping.control_id)?.add(mapping.hash);
5214
5230
  ws.send(
5215
5231
  JSON.stringify({
5216
5232
  type: "mapping-created",
@@ -5221,38 +5237,21 @@ var WebSocketManager = class {
5221
5237
  }
5222
5238
  break;
5223
5239
  }
5224
- case "update-mapping": {
5225
- const state = getServerState();
5226
- if (payload && payload.uuid) {
5227
- const mapping = payload;
5228
- await state.fileStore.saveMapping(mapping);
5229
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
5230
- state.mappingsCache.set(compositeKey, mapping);
5231
- ws.send(
5232
- JSON.stringify({
5233
- type: "mapping-updated",
5234
- payload: { uuid: mapping.uuid, success: true }
5235
- })
5236
- );
5237
- this.broadcastState();
5238
- }
5239
- break;
5240
- }
5241
5240
  case "delete-mapping": {
5242
5241
  const state = getServerState();
5243
- if (payload && payload.uuid) {
5244
- const uuid = payload.uuid;
5245
- const mapping = state.mappingsCache.get(uuid);
5242
+ if (payload && payload.composite_key) {
5243
+ const composite_key = payload.composite_key;
5244
+ const mapping = state.mappingsCache.get(composite_key);
5246
5245
  if (mapping) {
5247
- await state.fileStore.deleteMapping(uuid);
5248
- state.mappingsCache.delete(uuid);
5246
+ await state.fileStore.deleteMapping(composite_key);
5247
+ state.mappingsCache.delete(composite_key);
5249
5248
  const family = mapping.control_id.split("-")[0];
5250
- state.mappingsByFamily.get(family)?.delete(uuid);
5251
- state.mappingsByControl.get(mapping.control_id)?.delete(uuid);
5249
+ state.mappingsByFamily.get(family)?.delete(mapping.hash);
5250
+ state.mappingsByControl.get(mapping.control_id)?.delete(mapping.hash);
5252
5251
  ws.send(
5253
5252
  JSON.stringify({
5254
5253
  type: "mapping-deleted",
5255
- payload: { uuid, success: true }
5254
+ payload: { hash: mapping.hash, success: true }
5256
5255
  })
5257
5256
  );
5258
5257
  this.broadcastState();
@@ -5729,6 +5728,7 @@ var WebSocketManager = class {
5729
5728
  var wsManager = new WebSocketManager();
5730
5729
 
5731
5730
  // cli/server/server.ts
5731
+ import { createHash as createHash4 } from "crypto";
5732
5732
  var __filename = fileURLToPath(import.meta.url);
5733
5733
  var __dirname = dirname2(__filename);
5734
5734
  async function createServer(options) {
@@ -5754,6 +5754,11 @@ async function createServer(options) {
5754
5754
  app.get("/*splat", (req, res) => {
5755
5755
  res.sendFile("index.html", { root: distPath });
5756
5756
  });
5757
+ app.post("/hash", (req, res) => {
5758
+ delete req.body.hash;
5759
+ const hash = createHash4("sha256").update(JSON.stringify(req.body)).digest("hex");
5760
+ res.status(200).json({ hash });
5761
+ });
5757
5762
  const httpServer = createHttpServer(app);
5758
5763
  wsManager.initialize(httpServer);
5759
5764
  return {
@@ -1662,6 +1662,7 @@ import {
1662
1662
  } from "fs";
1663
1663
  import * as yaml2 from "js-yaml";
1664
1664
  import { join as join2 } from "path";
1665
+ import { createHash as createHash2 } from "crypto";
1665
1666
  var FileStore;
1666
1667
  var init_fileStore = __esm({
1667
1668
  "cli/server/infrastructure/fileStore.ts"() {
@@ -1959,6 +1960,10 @@ var init_fileStore = __esm({
1959
1960
  const content = readFileSync2(mappingFile, "utf8");
1960
1961
  const parsed = yaml2.load(content);
1961
1962
  if (Array.isArray(parsed)) {
1963
+ parsed.forEach((mapping) => {
1964
+ mapping.hash = createHash2("sha256").update(JSON.stringify(mapping)).digest("hex");
1965
+ return mapping;
1966
+ });
1962
1967
  mappings.push(...parsed);
1963
1968
  }
1964
1969
  } catch (error) {
@@ -1993,12 +1998,9 @@ var init_fileStore = __esm({
1993
1998
  existingMappings = [];
1994
1999
  }
1995
2000
  }
1996
- const existingIndex = existingMappings.findIndex((m) => m.uuid === mapping.uuid);
1997
- if (existingIndex >= 0) {
1998
- existingMappings[existingIndex] = mapping;
1999
- } else {
2000
- existingMappings.push(mapping);
2001
- }
2001
+ const cleanMapping = { ...mapping };
2002
+ delete cleanMapping.hash;
2003
+ existingMappings.push(cleanMapping);
2002
2004
  try {
2003
2005
  const yamlContent = yaml2.dump(existingMappings, {
2004
2006
  indent: 2,
@@ -2023,7 +2025,8 @@ var init_fileStore = __esm({
2023
2025
  let mappings = yaml2.load(content) || [];
2024
2026
  const originalLength = mappings.length;
2025
2027
  mappings = mappings.filter((m) => {
2026
- return `${m.control_id}:${m.uuid}` !== compositeKey;
2028
+ const hash = createHash2("sha256").update(JSON.stringify(m)).digest("hex");
2029
+ return `${m.control_id}:${hash}` !== compositeKey;
2027
2030
  });
2028
2031
  if (mappings.length < originalLength) {
2029
2032
  if (mappings.length === 0) {
@@ -2086,8 +2089,13 @@ var init_fileStore = __esm({
2086
2089
  if (!existsSync2(familyDir)) {
2087
2090
  mkdirSync(familyDir, { recursive: true });
2088
2091
  }
2092
+ const cleanMappings = controlMappings.map((m) => {
2093
+ const clean = { ...m };
2094
+ delete clean.hash;
2095
+ return clean;
2096
+ });
2089
2097
  try {
2090
- const yamlContent = yaml2.dump(controlMappings, {
2098
+ const yamlContent = yaml2.dump(cleanMappings, {
2091
2099
  indent: 2,
2092
2100
  lineWidth: -1,
2093
2101
  noRefs: true
@@ -3006,6 +3014,7 @@ __export(serverState_exports, {
3006
3014
  saveMappingsToFile: () => saveMappingsToFile
3007
3015
  });
3008
3016
  import { join as join3 } from "path";
3017
+ import { createHash as createHash3 } from "crypto";
3009
3018
  function initializeServerState(controlSetDir, subdir = ".") {
3010
3019
  const fullPath = subdir === "." ? controlSetDir : join3(controlSetDir, subdir);
3011
3020
  serverState = {
@@ -3048,11 +3057,11 @@ function addMappingToIndexes(mapping) {
3048
3057
  if (!state.mappingsByFamily.has(family)) {
3049
3058
  state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
3050
3059
  }
3051
- state.mappingsByFamily.get(family).add(mapping.uuid);
3060
+ state.mappingsByFamily.get(family).add(mapping.hash);
3052
3061
  if (!state.mappingsByControl.has(mapping.control_id)) {
3053
3062
  state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
3054
3063
  }
3055
- state.mappingsByControl.get(mapping.control_id).add(mapping.uuid);
3064
+ state.mappingsByControl.get(mapping.control_id).add(mapping.hash);
3056
3065
  }
3057
3066
  async function loadAllData() {
3058
3067
  const state = getServerState();
@@ -3066,7 +3075,10 @@ async function loadAllData() {
3066
3075
  debug(`Loaded ${controls.length} controls from individual files`);
3067
3076
  const mappings = await state.fileStore.loadMappings();
3068
3077
  for (const mapping of mappings) {
3069
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
3078
+ if (!mapping.hash) {
3079
+ mapping.hash = createHash3("sha256").update(JSON.stringify(mapping)).digest("hex");
3080
+ }
3081
+ const compositeKey = `${mapping.control_id}:${mapping.hash}`;
3070
3082
  state.mappingsCache.set(compositeKey, mapping);
3071
3083
  addMappingToIndexes(mapping);
3072
3084
  }
@@ -5109,6 +5121,7 @@ init_gitHistory();
5109
5121
  import * as yaml5 from "js-yaml";
5110
5122
  import { join as join5 } from "path";
5111
5123
  import { WebSocket, WebSocketServer } from "ws";
5124
+ import crypto2 from "node:crypto";
5112
5125
  var WebSocketManager = class {
5113
5126
  wss = null;
5114
5127
  clients = /* @__PURE__ */ new Set();
@@ -5173,21 +5186,24 @@ var WebSocketManager = class {
5173
5186
  if (payload && payload.control_id) {
5174
5187
  const mapping = payload;
5175
5188
  if (!mapping.uuid) {
5176
- const crypto2 = await import("crypto");
5177
- mapping.uuid = crypto2.randomUUID();
5189
+ const crypto3 = await import("crypto");
5190
+ mapping.uuid = crypto3.randomUUID();
5178
5191
  }
5192
+ if (!mapping.hash || mapping.hash === "") {
5193
+ mapping.hash = crypto2.createHash("sha256").update(JSON.stringify(mapping)).digest("hex");
5194
+ }
5195
+ const compositeKey = `${mapping.control_id}:${mapping.hash}`;
5179
5196
  await state.fileStore.saveMapping(mapping);
5180
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
5181
5197
  state.mappingsCache.set(compositeKey, mapping);
5182
5198
  const family = mapping.control_id.split("-")[0];
5183
5199
  if (!state.mappingsByFamily.has(family)) {
5184
5200
  state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
5185
5201
  }
5186
- state.mappingsByFamily.get(family)?.add(mapping.uuid);
5202
+ state.mappingsByFamily.get(family)?.add(mapping.hash);
5187
5203
  if (!state.mappingsByControl.has(mapping.control_id)) {
5188
5204
  state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
5189
5205
  }
5190
- state.mappingsByControl.get(mapping.control_id)?.add(mapping.uuid);
5206
+ state.mappingsByControl.get(mapping.control_id)?.add(mapping.hash);
5191
5207
  ws.send(
5192
5208
  JSON.stringify({
5193
5209
  type: "mapping-created",
@@ -5198,38 +5214,21 @@ var WebSocketManager = class {
5198
5214
  }
5199
5215
  break;
5200
5216
  }
5201
- case "update-mapping": {
5202
- const state = getServerState();
5203
- if (payload && payload.uuid) {
5204
- const mapping = payload;
5205
- await state.fileStore.saveMapping(mapping);
5206
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
5207
- state.mappingsCache.set(compositeKey, mapping);
5208
- ws.send(
5209
- JSON.stringify({
5210
- type: "mapping-updated",
5211
- payload: { uuid: mapping.uuid, success: true }
5212
- })
5213
- );
5214
- this.broadcastState();
5215
- }
5216
- break;
5217
- }
5218
5217
  case "delete-mapping": {
5219
5218
  const state = getServerState();
5220
- if (payload && payload.uuid) {
5221
- const uuid = payload.uuid;
5222
- const mapping = state.mappingsCache.get(uuid);
5219
+ if (payload && payload.composite_key) {
5220
+ const composite_key = payload.composite_key;
5221
+ const mapping = state.mappingsCache.get(composite_key);
5223
5222
  if (mapping) {
5224
- await state.fileStore.deleteMapping(uuid);
5225
- state.mappingsCache.delete(uuid);
5223
+ await state.fileStore.deleteMapping(composite_key);
5224
+ state.mappingsCache.delete(composite_key);
5226
5225
  const family = mapping.control_id.split("-")[0];
5227
- state.mappingsByFamily.get(family)?.delete(uuid);
5228
- state.mappingsByControl.get(mapping.control_id)?.delete(uuid);
5226
+ state.mappingsByFamily.get(family)?.delete(mapping.hash);
5227
+ state.mappingsByControl.get(mapping.control_id)?.delete(mapping.hash);
5229
5228
  ws.send(
5230
5229
  JSON.stringify({
5231
5230
  type: "mapping-deleted",
5232
- payload: { uuid, success: true }
5231
+ payload: { hash: mapping.hash, success: true }
5233
5232
  })
5234
5233
  );
5235
5234
  this.broadcastState();
@@ -5706,6 +5705,7 @@ var WebSocketManager = class {
5706
5705
  var wsManager = new WebSocketManager();
5707
5706
 
5708
5707
  // cli/server/server.ts
5708
+ import { createHash as createHash4 } from "crypto";
5709
5709
  var __filename = fileURLToPath(import.meta.url);
5710
5710
  var __dirname = dirname2(__filename);
5711
5711
  async function createServer(options) {
@@ -5731,6 +5731,11 @@ async function createServer(options) {
5731
5731
  app.get("/*splat", (req, res) => {
5732
5732
  res.sendFile("index.html", { root: distPath });
5733
5733
  });
5734
+ app.post("/hash", (req, res) => {
5735
+ delete req.body.hash;
5736
+ const hash = createHash4("sha256").update(JSON.stringify(req.body)).digest("hex");
5737
+ res.status(200).json({ hash });
5738
+ });
5734
5739
  const httpServer = createHttpServer(app);
5735
5740
  wsManager.initialize(httpServer);
5736
5741
  return {
@@ -1662,6 +1662,7 @@ import {
1662
1662
  } from "fs";
1663
1663
  import * as yaml2 from "js-yaml";
1664
1664
  import { join as join2 } from "path";
1665
+ import { createHash as createHash2 } from "crypto";
1665
1666
  var FileStore;
1666
1667
  var init_fileStore = __esm({
1667
1668
  "cli/server/infrastructure/fileStore.ts"() {
@@ -1959,6 +1960,10 @@ var init_fileStore = __esm({
1959
1960
  const content = readFileSync2(mappingFile, "utf8");
1960
1961
  const parsed = yaml2.load(content);
1961
1962
  if (Array.isArray(parsed)) {
1963
+ parsed.forEach((mapping) => {
1964
+ mapping.hash = createHash2("sha256").update(JSON.stringify(mapping)).digest("hex");
1965
+ return mapping;
1966
+ });
1962
1967
  mappings.push(...parsed);
1963
1968
  }
1964
1969
  } catch (error) {
@@ -1993,12 +1998,9 @@ var init_fileStore = __esm({
1993
1998
  existingMappings = [];
1994
1999
  }
1995
2000
  }
1996
- const existingIndex = existingMappings.findIndex((m) => m.uuid === mapping.uuid);
1997
- if (existingIndex >= 0) {
1998
- existingMappings[existingIndex] = mapping;
1999
- } else {
2000
- existingMappings.push(mapping);
2001
- }
2001
+ const cleanMapping = { ...mapping };
2002
+ delete cleanMapping.hash;
2003
+ existingMappings.push(cleanMapping);
2002
2004
  try {
2003
2005
  const yamlContent = yaml2.dump(existingMappings, {
2004
2006
  indent: 2,
@@ -2023,7 +2025,8 @@ var init_fileStore = __esm({
2023
2025
  let mappings = yaml2.load(content) || [];
2024
2026
  const originalLength = mappings.length;
2025
2027
  mappings = mappings.filter((m) => {
2026
- return `${m.control_id}:${m.uuid}` !== compositeKey;
2028
+ const hash = createHash2("sha256").update(JSON.stringify(m)).digest("hex");
2029
+ return `${m.control_id}:${hash}` !== compositeKey;
2027
2030
  });
2028
2031
  if (mappings.length < originalLength) {
2029
2032
  if (mappings.length === 0) {
@@ -2086,8 +2089,13 @@ var init_fileStore = __esm({
2086
2089
  if (!existsSync2(familyDir)) {
2087
2090
  mkdirSync(familyDir, { recursive: true });
2088
2091
  }
2092
+ const cleanMappings = controlMappings.map((m) => {
2093
+ const clean = { ...m };
2094
+ delete clean.hash;
2095
+ return clean;
2096
+ });
2089
2097
  try {
2090
- const yamlContent = yaml2.dump(controlMappings, {
2098
+ const yamlContent = yaml2.dump(cleanMappings, {
2091
2099
  indent: 2,
2092
2100
  lineWidth: -1,
2093
2101
  noRefs: true
@@ -3006,6 +3014,7 @@ __export(serverState_exports, {
3006
3014
  saveMappingsToFile: () => saveMappingsToFile
3007
3015
  });
3008
3016
  import { join as join3 } from "path";
3017
+ import { createHash as createHash3 } from "crypto";
3009
3018
  function initializeServerState(controlSetDir, subdir = ".") {
3010
3019
  const fullPath = subdir === "." ? controlSetDir : join3(controlSetDir, subdir);
3011
3020
  serverState = {
@@ -3048,11 +3057,11 @@ function addMappingToIndexes(mapping) {
3048
3057
  if (!state.mappingsByFamily.has(family)) {
3049
3058
  state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
3050
3059
  }
3051
- state.mappingsByFamily.get(family).add(mapping.uuid);
3060
+ state.mappingsByFamily.get(family).add(mapping.hash);
3052
3061
  if (!state.mappingsByControl.has(mapping.control_id)) {
3053
3062
  state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
3054
3063
  }
3055
- state.mappingsByControl.get(mapping.control_id).add(mapping.uuid);
3064
+ state.mappingsByControl.get(mapping.control_id).add(mapping.hash);
3056
3065
  }
3057
3066
  async function loadAllData() {
3058
3067
  const state = getServerState();
@@ -3066,7 +3075,10 @@ async function loadAllData() {
3066
3075
  debug(`Loaded ${controls.length} controls from individual files`);
3067
3076
  const mappings = await state.fileStore.loadMappings();
3068
3077
  for (const mapping of mappings) {
3069
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
3078
+ if (!mapping.hash) {
3079
+ mapping.hash = createHash3("sha256").update(JSON.stringify(mapping)).digest("hex");
3080
+ }
3081
+ const compositeKey = `${mapping.control_id}:${mapping.hash}`;
3070
3082
  state.mappingsCache.set(compositeKey, mapping);
3071
3083
  addMappingToIndexes(mapping);
3072
3084
  }
@@ -5109,6 +5121,7 @@ init_gitHistory();
5109
5121
  import * as yaml5 from "js-yaml";
5110
5122
  import { join as join5 } from "path";
5111
5123
  import { WebSocket, WebSocketServer } from "ws";
5124
+ import crypto2 from "node:crypto";
5112
5125
  var WebSocketManager = class {
5113
5126
  wss = null;
5114
5127
  clients = /* @__PURE__ */ new Set();
@@ -5173,21 +5186,24 @@ var WebSocketManager = class {
5173
5186
  if (payload && payload.control_id) {
5174
5187
  const mapping = payload;
5175
5188
  if (!mapping.uuid) {
5176
- const crypto2 = await import("crypto");
5177
- mapping.uuid = crypto2.randomUUID();
5189
+ const crypto3 = await import("crypto");
5190
+ mapping.uuid = crypto3.randomUUID();
5178
5191
  }
5192
+ if (!mapping.hash || mapping.hash === "") {
5193
+ mapping.hash = crypto2.createHash("sha256").update(JSON.stringify(mapping)).digest("hex");
5194
+ }
5195
+ const compositeKey = `${mapping.control_id}:${mapping.hash}`;
5179
5196
  await state.fileStore.saveMapping(mapping);
5180
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
5181
5197
  state.mappingsCache.set(compositeKey, mapping);
5182
5198
  const family = mapping.control_id.split("-")[0];
5183
5199
  if (!state.mappingsByFamily.has(family)) {
5184
5200
  state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
5185
5201
  }
5186
- state.mappingsByFamily.get(family)?.add(mapping.uuid);
5202
+ state.mappingsByFamily.get(family)?.add(mapping.hash);
5187
5203
  if (!state.mappingsByControl.has(mapping.control_id)) {
5188
5204
  state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
5189
5205
  }
5190
- state.mappingsByControl.get(mapping.control_id)?.add(mapping.uuid);
5206
+ state.mappingsByControl.get(mapping.control_id)?.add(mapping.hash);
5191
5207
  ws.send(
5192
5208
  JSON.stringify({
5193
5209
  type: "mapping-created",
@@ -5198,38 +5214,21 @@ var WebSocketManager = class {
5198
5214
  }
5199
5215
  break;
5200
5216
  }
5201
- case "update-mapping": {
5202
- const state = getServerState();
5203
- if (payload && payload.uuid) {
5204
- const mapping = payload;
5205
- await state.fileStore.saveMapping(mapping);
5206
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
5207
- state.mappingsCache.set(compositeKey, mapping);
5208
- ws.send(
5209
- JSON.stringify({
5210
- type: "mapping-updated",
5211
- payload: { uuid: mapping.uuid, success: true }
5212
- })
5213
- );
5214
- this.broadcastState();
5215
- }
5216
- break;
5217
- }
5218
5217
  case "delete-mapping": {
5219
5218
  const state = getServerState();
5220
- if (payload && payload.uuid) {
5221
- const uuid = payload.uuid;
5222
- const mapping = state.mappingsCache.get(uuid);
5219
+ if (payload && payload.composite_key) {
5220
+ const composite_key = payload.composite_key;
5221
+ const mapping = state.mappingsCache.get(composite_key);
5223
5222
  if (mapping) {
5224
- await state.fileStore.deleteMapping(uuid);
5225
- state.mappingsCache.delete(uuid);
5223
+ await state.fileStore.deleteMapping(composite_key);
5224
+ state.mappingsCache.delete(composite_key);
5226
5225
  const family = mapping.control_id.split("-")[0];
5227
- state.mappingsByFamily.get(family)?.delete(uuid);
5228
- state.mappingsByControl.get(mapping.control_id)?.delete(uuid);
5226
+ state.mappingsByFamily.get(family)?.delete(mapping.hash);
5227
+ state.mappingsByControl.get(mapping.control_id)?.delete(mapping.hash);
5229
5228
  ws.send(
5230
5229
  JSON.stringify({
5231
5230
  type: "mapping-deleted",
5232
- payload: { uuid, success: true }
5231
+ payload: { hash: mapping.hash, success: true }
5233
5232
  })
5234
5233
  );
5235
5234
  this.broadcastState();
@@ -5706,6 +5705,7 @@ var WebSocketManager = class {
5706
5705
  var wsManager = new WebSocketManager();
5707
5706
 
5708
5707
  // cli/server/server.ts
5708
+ import { createHash as createHash4 } from "crypto";
5709
5709
  var __filename = fileURLToPath(import.meta.url);
5710
5710
  var __dirname = dirname2(__filename);
5711
5711
  async function createServer(options) {
@@ -5731,6 +5731,11 @@ async function createServer(options) {
5731
5731
  app.get("/*splat", (req, res) => {
5732
5732
  res.sendFile("index.html", { root: distPath });
5733
5733
  });
5734
+ app.post("/hash", (req, res) => {
5735
+ delete req.body.hash;
5736
+ const hash = createHash4("sha256").update(JSON.stringify(req.body)).digest("hex");
5737
+ res.status(200).json({ hash });
5738
+ });
5734
5739
  const httpServer = createHttpServer(app);
5735
5740
  wsManager.initialize(httpServer);
5736
5741
  return {
@@ -1,5 +1,6 @@
1
1
  // cli/server/serverState.ts
2
2
  import { join as join3 } from "path";
3
+ import { createHash as createHash2 } from "crypto";
3
4
 
4
5
  // cli/utils/debug.ts
5
6
  var debugEnabled = false;
@@ -88,6 +89,7 @@ function getControlId(control, baseDir) {
88
89
  }
89
90
 
90
91
  // cli/server/infrastructure/fileStore.ts
92
+ import { createHash } from "crypto";
91
93
  var FileStore = class {
92
94
  baseDir;
93
95
  controlsDir;
@@ -380,6 +382,10 @@ var FileStore = class {
380
382
  const content = readFileSync2(mappingFile, "utf8");
381
383
  const parsed = yaml2.load(content);
382
384
  if (Array.isArray(parsed)) {
385
+ parsed.forEach((mapping) => {
386
+ mapping.hash = createHash("sha256").update(JSON.stringify(mapping)).digest("hex");
387
+ return mapping;
388
+ });
383
389
  mappings.push(...parsed);
384
390
  }
385
391
  } catch (error) {
@@ -414,12 +420,9 @@ var FileStore = class {
414
420
  existingMappings = [];
415
421
  }
416
422
  }
417
- const existingIndex = existingMappings.findIndex((m) => m.uuid === mapping.uuid);
418
- if (existingIndex >= 0) {
419
- existingMappings[existingIndex] = mapping;
420
- } else {
421
- existingMappings.push(mapping);
422
- }
423
+ const cleanMapping = { ...mapping };
424
+ delete cleanMapping.hash;
425
+ existingMappings.push(cleanMapping);
423
426
  try {
424
427
  const yamlContent = yaml2.dump(existingMappings, {
425
428
  indent: 2,
@@ -444,7 +447,8 @@ var FileStore = class {
444
447
  let mappings = yaml2.load(content) || [];
445
448
  const originalLength = mappings.length;
446
449
  mappings = mappings.filter((m) => {
447
- return `${m.control_id}:${m.uuid}` !== compositeKey;
450
+ const hash = createHash("sha256").update(JSON.stringify(m)).digest("hex");
451
+ return `${m.control_id}:${hash}` !== compositeKey;
448
452
  });
449
453
  if (mappings.length < originalLength) {
450
454
  if (mappings.length === 0) {
@@ -507,8 +511,13 @@ var FileStore = class {
507
511
  if (!existsSync2(familyDir)) {
508
512
  mkdirSync(familyDir, { recursive: true });
509
513
  }
514
+ const cleanMappings = controlMappings.map((m) => {
515
+ const clean = { ...m };
516
+ delete clean.hash;
517
+ return clean;
518
+ });
510
519
  try {
511
- const yamlContent = yaml2.dump(controlMappings, {
520
+ const yamlContent = yaml2.dump(cleanMappings, {
512
521
  indent: 2,
513
522
  lineWidth: -1,
514
523
  noRefs: true
@@ -1447,11 +1456,11 @@ function addMappingToIndexes(mapping) {
1447
1456
  if (!state.mappingsByFamily.has(family)) {
1448
1457
  state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
1449
1458
  }
1450
- state.mappingsByFamily.get(family).add(mapping.uuid);
1459
+ state.mappingsByFamily.get(family).add(mapping.hash);
1451
1460
  if (!state.mappingsByControl.has(mapping.control_id)) {
1452
1461
  state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
1453
1462
  }
1454
- state.mappingsByControl.get(mapping.control_id).add(mapping.uuid);
1463
+ state.mappingsByControl.get(mapping.control_id).add(mapping.hash);
1455
1464
  }
1456
1465
  async function loadAllData() {
1457
1466
  const state = getServerState();
@@ -1465,7 +1474,10 @@ async function loadAllData() {
1465
1474
  debug(`Loaded ${controls.length} controls from individual files`);
1466
1475
  const mappings = await state.fileStore.loadMappings();
1467
1476
  for (const mapping of mappings) {
1468
- const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
1477
+ if (!mapping.hash) {
1478
+ mapping.hash = createHash2("sha256").update(JSON.stringify(mapping)).digest("hex");
1479
+ }
1480
+ const compositeKey = `${mapping.control_id}:${mapping.hash}`;
1469
1481
  state.mappingsCache.set(compositeKey, mapping);
1470
1482
  addMappingToIndexes(mapping);
1471
1483
  }