realitydb 1.6.2 → 1.7.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 (2) hide show
  1. package/dist/index.js +958 -146
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16436,7 +16436,7 @@ var require_caching_sha2_password = __commonJS({
16436
16436
  var STATE_TOKEN_SENT = 1;
16437
16437
  var STATE_WAIT_SERVER_KEY = 2;
16438
16438
  var STATE_FINAL = -1;
16439
- function sha256(msg) {
16439
+ function sha2562(msg) {
16440
16440
  const hash = crypto.createHash("sha256");
16441
16441
  hash.update(msg);
16442
16442
  return hash.digest();
@@ -16445,9 +16445,9 @@ var require_caching_sha2_password = __commonJS({
16445
16445
  if (!password) {
16446
16446
  return Buffer.alloc(0);
16447
16447
  }
16448
- const stage1 = sha256(Buffer.from(password));
16449
- const stage2 = sha256(stage1);
16450
- const stage3 = sha256(Buffer.concat([stage2, scramble]));
16448
+ const stage1 = sha2562(Buffer.from(password));
16449
+ const stage2 = sha2562(stage1);
16450
+ const stage3 = sha2562(Buffer.concat([stage2, scramble]));
16451
16451
  return xor(stage1, stage3);
16452
16452
  }
16453
16453
  function encrypt(password, scramble, key) {
@@ -20958,7 +20958,7 @@ var require_pool_connection = __commonJS({
20958
20958
  var require_make_done_cb = __commonJS({
20959
20959
  "../../node_modules/.pnpm/mysql2@3.19.1_@types+node@25.3.5/node_modules/mysql2/lib/promise/make_done_cb.js"(exports2, module2) {
20960
20960
  "use strict";
20961
- function makeDoneCb(resolve12, reject, localErr) {
20961
+ function makeDoneCb(resolve13, reject, localErr) {
20962
20962
  return function(err, rows, fields) {
20963
20963
  if (err) {
20964
20964
  localErr.message = err.message;
@@ -20969,7 +20969,7 @@ var require_make_done_cb = __commonJS({
20969
20969
  localErr.sqlMessage = err.sqlMessage;
20970
20970
  reject(localErr);
20971
20971
  } else {
20972
- resolve12([rows, fields]);
20972
+ resolve13([rows, fields]);
20973
20973
  }
20974
20974
  };
20975
20975
  }
@@ -20990,8 +20990,8 @@ var require_prepared_statement_info = __commonJS({
20990
20990
  execute(parameters) {
20991
20991
  const s = this.statement;
20992
20992
  const localErr = new Error();
20993
- return new this.Promise((resolve12, reject) => {
20994
- const done = makeDoneCb(resolve12, reject, localErr);
20993
+ return new this.Promise((resolve13, reject) => {
20994
+ const done = makeDoneCb(resolve13, reject, localErr);
20995
20995
  if (parameters) {
20996
20996
  s.execute(parameters, done);
20997
20997
  } else {
@@ -21000,9 +21000,9 @@ var require_prepared_statement_info = __commonJS({
21000
21000
  });
21001
21001
  }
21002
21002
  close() {
21003
- return new this.Promise((resolve12) => {
21003
+ return new this.Promise((resolve13) => {
21004
21004
  this.statement.close();
21005
- resolve12();
21005
+ resolve13();
21006
21006
  });
21007
21007
  }
21008
21008
  };
@@ -21071,8 +21071,8 @@ var require_connection2 = __commonJS({
21071
21071
  "Callback function is not available with promise clients."
21072
21072
  );
21073
21073
  }
21074
- return new this.Promise((resolve12, reject) => {
21075
- const done = makeDoneCb(resolve12, reject, localErr);
21074
+ return new this.Promise((resolve13, reject) => {
21075
+ const done = makeDoneCb(resolve13, reject, localErr);
21076
21076
  if (params !== void 0) {
21077
21077
  c.query(query, params, done);
21078
21078
  } else {
@@ -21088,8 +21088,8 @@ var require_connection2 = __commonJS({
21088
21088
  "Callback function is not available with promise clients."
21089
21089
  );
21090
21090
  }
21091
- return new this.Promise((resolve12, reject) => {
21092
- const done = makeDoneCb(resolve12, reject, localErr);
21091
+ return new this.Promise((resolve13, reject) => {
21092
+ const done = makeDoneCb(resolve13, reject, localErr);
21093
21093
  if (params !== void 0) {
21094
21094
  c.execute(query, params, done);
21095
21095
  } else {
@@ -21098,8 +21098,8 @@ var require_connection2 = __commonJS({
21098
21098
  });
21099
21099
  }
21100
21100
  end() {
21101
- return new this.Promise((resolve12) => {
21102
- this.connection.end(resolve12);
21101
+ return new this.Promise((resolve13) => {
21102
+ this.connection.end(resolve13);
21103
21103
  });
21104
21104
  }
21105
21105
  async [Symbol.asyncDispose]() {
@@ -21110,31 +21110,31 @@ var require_connection2 = __commonJS({
21110
21110
  beginTransaction() {
21111
21111
  const c = this.connection;
21112
21112
  const localErr = new Error();
21113
- return new this.Promise((resolve12, reject) => {
21114
- const done = makeDoneCb(resolve12, reject, localErr);
21113
+ return new this.Promise((resolve13, reject) => {
21114
+ const done = makeDoneCb(resolve13, reject, localErr);
21115
21115
  c.beginTransaction(done);
21116
21116
  });
21117
21117
  }
21118
21118
  commit() {
21119
21119
  const c = this.connection;
21120
21120
  const localErr = new Error();
21121
- return new this.Promise((resolve12, reject) => {
21122
- const done = makeDoneCb(resolve12, reject, localErr);
21121
+ return new this.Promise((resolve13, reject) => {
21122
+ const done = makeDoneCb(resolve13, reject, localErr);
21123
21123
  c.commit(done);
21124
21124
  });
21125
21125
  }
21126
21126
  rollback() {
21127
21127
  const c = this.connection;
21128
21128
  const localErr = new Error();
21129
- return new this.Promise((resolve12, reject) => {
21130
- const done = makeDoneCb(resolve12, reject, localErr);
21129
+ return new this.Promise((resolve13, reject) => {
21130
+ const done = makeDoneCb(resolve13, reject, localErr);
21131
21131
  c.rollback(done);
21132
21132
  });
21133
21133
  }
21134
21134
  ping() {
21135
21135
  const c = this.connection;
21136
21136
  const localErr = new Error();
21137
- return new this.Promise((resolve12, reject) => {
21137
+ return new this.Promise((resolve13, reject) => {
21138
21138
  c.ping((err) => {
21139
21139
  if (err) {
21140
21140
  localErr.message = err.message;
@@ -21144,7 +21144,7 @@ var require_connection2 = __commonJS({
21144
21144
  localErr.sqlMessage = err.sqlMessage;
21145
21145
  reject(localErr);
21146
21146
  } else {
21147
- resolve12(true);
21147
+ resolve13(true);
21148
21148
  }
21149
21149
  });
21150
21150
  });
@@ -21152,7 +21152,7 @@ var require_connection2 = __commonJS({
21152
21152
  connect() {
21153
21153
  const c = this.connection;
21154
21154
  const localErr = new Error();
21155
- return new this.Promise((resolve12, reject) => {
21155
+ return new this.Promise((resolve13, reject) => {
21156
21156
  c.connect((err, param) => {
21157
21157
  if (err) {
21158
21158
  localErr.message = err.message;
@@ -21162,7 +21162,7 @@ var require_connection2 = __commonJS({
21162
21162
  localErr.sqlMessage = err.sqlMessage;
21163
21163
  reject(localErr);
21164
21164
  } else {
21165
- resolve12(param);
21165
+ resolve13(param);
21166
21166
  }
21167
21167
  });
21168
21168
  });
@@ -21171,7 +21171,7 @@ var require_connection2 = __commonJS({
21171
21171
  const c = this.connection;
21172
21172
  const promiseImpl = this.Promise;
21173
21173
  const localErr = new Error();
21174
- return new this.Promise((resolve12, reject) => {
21174
+ return new this.Promise((resolve13, reject) => {
21175
21175
  c.prepare(options, (err, statement) => {
21176
21176
  if (err) {
21177
21177
  localErr.message = err.message;
@@ -21185,7 +21185,7 @@ var require_connection2 = __commonJS({
21185
21185
  statement,
21186
21186
  promiseImpl
21187
21187
  );
21188
- resolve12(wrappedStatement);
21188
+ resolve13(wrappedStatement);
21189
21189
  }
21190
21190
  });
21191
21191
  });
@@ -21193,7 +21193,7 @@ var require_connection2 = __commonJS({
21193
21193
  changeUser(options) {
21194
21194
  const c = this.connection;
21195
21195
  const localErr = new Error();
21196
- return new this.Promise((resolve12, reject) => {
21196
+ return new this.Promise((resolve13, reject) => {
21197
21197
  c.changeUser(options, (err) => {
21198
21198
  if (err) {
21199
21199
  localErr.message = err.message;
@@ -21203,7 +21203,7 @@ var require_connection2 = __commonJS({
21203
21203
  localErr.sqlMessage = err.sqlMessage;
21204
21204
  reject(localErr);
21205
21205
  } else {
21206
- resolve12();
21206
+ resolve13();
21207
21207
  }
21208
21208
  });
21209
21209
  });
@@ -21546,12 +21546,12 @@ var require_pool2 = __commonJS({
21546
21546
  }
21547
21547
  getConnection() {
21548
21548
  const corePool = this.pool;
21549
- return new this.Promise((resolve12, reject) => {
21549
+ return new this.Promise((resolve13, reject) => {
21550
21550
  corePool.getConnection((err, coreConnection) => {
21551
21551
  if (err) {
21552
21552
  reject(err);
21553
21553
  } else {
21554
- resolve12(new PromisePoolConnection(coreConnection, this.Promise));
21554
+ resolve13(new PromisePoolConnection(coreConnection, this.Promise));
21555
21555
  }
21556
21556
  });
21557
21557
  });
@@ -21567,8 +21567,8 @@ var require_pool2 = __commonJS({
21567
21567
  "Callback function is not available with promise clients."
21568
21568
  );
21569
21569
  }
21570
- return new this.Promise((resolve12, reject) => {
21571
- const done = makeDoneCb(resolve12, reject, localErr);
21570
+ return new this.Promise((resolve13, reject) => {
21571
+ const done = makeDoneCb(resolve13, reject, localErr);
21572
21572
  if (args !== void 0) {
21573
21573
  corePool.query(sql, args, done);
21574
21574
  } else {
@@ -21584,8 +21584,8 @@ var require_pool2 = __commonJS({
21584
21584
  "Callback function is not available with promise clients."
21585
21585
  );
21586
21586
  }
21587
- return new this.Promise((resolve12, reject) => {
21588
- const done = makeDoneCb(resolve12, reject, localErr);
21587
+ return new this.Promise((resolve13, reject) => {
21588
+ const done = makeDoneCb(resolve13, reject, localErr);
21589
21589
  if (args) {
21590
21590
  corePool.execute(sql, args, done);
21591
21591
  } else {
@@ -21596,7 +21596,7 @@ var require_pool2 = __commonJS({
21596
21596
  end() {
21597
21597
  const corePool = this.pool;
21598
21598
  const localErr = new Error();
21599
- return new this.Promise((resolve12, reject) => {
21599
+ return new this.Promise((resolve13, reject) => {
21600
21600
  corePool.end((err) => {
21601
21601
  if (err) {
21602
21602
  localErr.message = err.message;
@@ -21606,7 +21606,7 @@ var require_pool2 = __commonJS({
21606
21606
  localErr.sqlMessage = err.sqlMessage;
21607
21607
  reject(localErr);
21608
21608
  } else {
21609
- resolve12();
21609
+ resolve13();
21610
21610
  }
21611
21611
  });
21612
21612
  });
@@ -22051,12 +22051,12 @@ var require_pool_cluster2 = __commonJS({
22051
22051
  }
22052
22052
  getConnection() {
22053
22053
  const corePoolNamespace = this.poolNamespace;
22054
- return new this.Promise((resolve12, reject) => {
22054
+ return new this.Promise((resolve13, reject) => {
22055
22055
  corePoolNamespace.getConnection((err, coreConnection) => {
22056
22056
  if (err) {
22057
22057
  reject(err);
22058
22058
  } else {
22059
- resolve12(new PromisePoolConnection(coreConnection, this.Promise));
22059
+ resolve13(new PromisePoolConnection(coreConnection, this.Promise));
22060
22060
  }
22061
22061
  });
22062
22062
  });
@@ -22069,8 +22069,8 @@ var require_pool_cluster2 = __commonJS({
22069
22069
  "Callback function is not available with promise clients."
22070
22070
  );
22071
22071
  }
22072
- return new this.Promise((resolve12, reject) => {
22073
- const done = makeDoneCb(resolve12, reject, localErr);
22072
+ return new this.Promise((resolve13, reject) => {
22073
+ const done = makeDoneCb(resolve13, reject, localErr);
22074
22074
  corePoolNamespace.query(sql, values, done);
22075
22075
  });
22076
22076
  }
@@ -22082,8 +22082,8 @@ var require_pool_cluster2 = __commonJS({
22082
22082
  "Callback function is not available with promise clients."
22083
22083
  );
22084
22084
  }
22085
- return new this.Promise((resolve12, reject) => {
22086
- const done = makeDoneCb(resolve12, reject, localErr);
22085
+ return new this.Promise((resolve13, reject) => {
22086
+ const done = makeDoneCb(resolve13, reject, localErr);
22087
22087
  corePoolNamespace.execute(sql, values, done);
22088
22088
  });
22089
22089
  }
@@ -22118,9 +22118,9 @@ var require_promise = __commonJS({
22118
22118
  "no Promise implementation available.Use promise-enabled node version or pass userland Promise implementation as parameter, for example: { Promise: require('bluebird') }"
22119
22119
  );
22120
22120
  }
22121
- return new thePromise((resolve12, reject) => {
22121
+ return new thePromise((resolve13, reject) => {
22122
22122
  coreConnection.once("connect", () => {
22123
- resolve12(new PromiseConnection(coreConnection, thePromise));
22123
+ resolve13(new PromiseConnection(coreConnection, thePromise));
22124
22124
  });
22125
22125
  coreConnection.once("error", (err) => {
22126
22126
  createConnectionErr.message = err.message;
@@ -22150,7 +22150,7 @@ var require_promise = __commonJS({
22150
22150
  }
22151
22151
  getConnection(pattern, selector) {
22152
22152
  const corePoolCluster = this.poolCluster;
22153
- return new this.Promise((resolve12, reject) => {
22153
+ return new this.Promise((resolve13, reject) => {
22154
22154
  corePoolCluster.getConnection(
22155
22155
  pattern,
22156
22156
  selector,
@@ -22158,7 +22158,7 @@ var require_promise = __commonJS({
22158
22158
  if (err) {
22159
22159
  reject(err);
22160
22160
  } else {
22161
- resolve12(new PromisePoolConnection(coreConnection, this.Promise));
22161
+ resolve13(new PromisePoolConnection(coreConnection, this.Promise));
22162
22162
  }
22163
22163
  }
22164
22164
  );
@@ -22172,8 +22172,8 @@ var require_promise = __commonJS({
22172
22172
  "Callback function is not available with promise clients."
22173
22173
  );
22174
22174
  }
22175
- return new this.Promise((resolve12, reject) => {
22176
- const done = makeDoneCb(resolve12, reject, localErr);
22175
+ return new this.Promise((resolve13, reject) => {
22176
+ const done = makeDoneCb(resolve13, reject, localErr);
22177
22177
  corePoolCluster.query(sql, args, done);
22178
22178
  });
22179
22179
  }
@@ -22185,8 +22185,8 @@ var require_promise = __commonJS({
22185
22185
  "Callback function is not available with promise clients."
22186
22186
  );
22187
22187
  }
22188
- return new this.Promise((resolve12, reject) => {
22189
- const done = makeDoneCb(resolve12, reject, localErr);
22188
+ return new this.Promise((resolve13, reject) => {
22189
+ const done = makeDoneCb(resolve13, reject, localErr);
22190
22190
  corePoolCluster.execute(sql, args, done);
22191
22191
  });
22192
22192
  }
@@ -22199,7 +22199,7 @@ var require_promise = __commonJS({
22199
22199
  end() {
22200
22200
  const corePoolCluster = this.poolCluster;
22201
22201
  const localErr = new Error();
22202
- return new this.Promise((resolve12, reject) => {
22202
+ return new this.Promise((resolve13, reject) => {
22203
22203
  corePoolCluster.end((err) => {
22204
22204
  if (err) {
22205
22205
  localErr.message = err.message;
@@ -22209,7 +22209,7 @@ var require_promise = __commonJS({
22209
22209
  localErr.sqlMessage = err.sqlMessage;
22210
22210
  reject(localErr);
22211
22211
  } else {
22212
- resolve12();
22212
+ resolve13();
22213
22213
  }
22214
22214
  });
22215
22215
  });
@@ -22428,11 +22428,29 @@ function inferColumnStrategy(column, tableForeignKeys, tableName) {
22428
22428
  return { kind: "enum", options: { values: enumValues } };
22429
22429
  }
22430
22430
  }
22431
+ if (column.dataType === "set" || udtLower.startsWith("set(")) {
22432
+ const setValues = parseMySQLEnumValues(column.udtName.replace(/^set/i, "enum"));
22433
+ if (setValues.length > 0) {
22434
+ return { kind: "enum", options: { values: setValues } };
22435
+ }
22436
+ }
22431
22437
  const dataType = udtLower;
22432
22438
  const baseType = column.dataType.toLowerCase();
22433
22439
  if (dataType === "uuid" || baseType === "char" && column.maxLength === 36) {
22434
22440
  return { kind: "uuid" };
22435
22441
  }
22442
+ if (baseType === "json" || baseType === "jsonb") {
22443
+ return { kind: "text", options: { mode: "short" } };
22444
+ }
22445
+ if (baseType === "year") {
22446
+ return { kind: "integer", options: { min: 2e3, max: 2030 } };
22447
+ }
22448
+ if (baseType === "tinytext") {
22449
+ return { kind: "text", options: { mode: "short" } };
22450
+ }
22451
+ if (baseType === "mediumtext" || baseType === "longtext") {
22452
+ return { kind: "text", options: { mode: "long" } };
22453
+ }
22436
22454
  if ((dataType === "varchar" || dataType === "text" || baseType === "varchar" || baseType === "text") && column.maxLength !== null && column.maxLength <= 10) {
22437
22455
  return { kind: "text", options: { mode: "short" } };
22438
22456
  }
@@ -23007,7 +23025,7 @@ function createGeneratorRegistry() {
23007
23025
  const padLength = strategy.options?.["padLength"] ?? 6;
23008
23026
  return (ctx) => generateSequential(ctx, prefix, padLength);
23009
23027
  }
23010
- console.warn(`[databox] Unknown custom generator "${name}", falling back to text.`);
23028
+ console.warn(`Unknown custom generator "${name}", falling back to text.`);
23011
23029
  return (ctx) => generateText(ctx, "short");
23012
23030
  }
23013
23031
  };
@@ -23075,6 +23093,19 @@ function resolveForeignKey(ctx, ref) {
23075
23093
  }
23076
23094
 
23077
23095
  // ../../packages/generators/dist/engine.js
23096
+ var INTEGER_TYPES = /* @__PURE__ */ new Set([
23097
+ "int2",
23098
+ "int4",
23099
+ "int8",
23100
+ "integer",
23101
+ "serial",
23102
+ "bigserial",
23103
+ "smallint",
23104
+ "bigint",
23105
+ "int",
23106
+ "mediumint",
23107
+ "tinyint"
23108
+ ]);
23078
23109
  function generateDataset(plan) {
23079
23110
  const seed = createSeededRandom(plan.reproducibility.randomSeed);
23080
23111
  const registry = createGeneratorRegistry();
@@ -23090,6 +23121,7 @@ function generateDataset(plan) {
23090
23121
  const isSelfRef = colPlan.foreignKeyRef.referencedTable === tableName;
23091
23122
  return {
23092
23123
  columnName: colPlan.columnName,
23124
+ dataType: colPlan.dataType,
23093
23125
  generator: null,
23094
23126
  isForeignKey: true,
23095
23127
  isSelfReference: isSelfRef,
@@ -23103,6 +23135,7 @@ function generateDataset(plan) {
23103
23135
  }
23104
23136
  return {
23105
23137
  columnName: colPlan.columnName,
23138
+ dataType: colPlan.dataType,
23106
23139
  generator: registry.getGenerator(colPlan.strategy),
23107
23140
  isForeignKey: false,
23108
23141
  isSelfReference: false,
@@ -23144,6 +23177,13 @@ function generateDataset(plan) {
23144
23177
  } else if (colGen.generator) {
23145
23178
  row[colGen.columnName] = colGen.generator(ctx);
23146
23179
  }
23180
+ const val = row[colGen.columnName];
23181
+ if (typeof val === "number" && !Number.isInteger(val)) {
23182
+ const dt = colGen.dataType.toLowerCase();
23183
+ if (INTEGER_TYPES.has(dt)) {
23184
+ row[colGen.columnName] = Math.floor(val);
23185
+ }
23186
+ }
23147
23187
  }
23148
23188
  rows.push(row);
23149
23189
  }
@@ -24215,7 +24255,7 @@ function applyScenarios(dataset, scenarios, random) {
24215
24255
  for (const config of scenarios) {
24216
24256
  const definition = registry.get(config.name);
24217
24257
  if (!definition) {
24218
- console.warn(`[databox] Scenario "${config.name}" not found. Available: ${registry.list().map((s) => s.name).join(", ")}`);
24258
+ console.warn(`Scenario "${config.name}" not found. Available: ${registry.list().map((s) => s.name).join(", ")}`);
24219
24259
  continue;
24220
24260
  }
24221
24261
  const beforeTotalRows = countTotalValues(dataset);
@@ -24300,7 +24340,7 @@ function composeScenarios(dataset, configs, random, customScenarios) {
24300
24340
  for (const config of configs) {
24301
24341
  const definition = registry.get(config.name);
24302
24342
  if (!definition) {
24303
- console.warn(`[databox] Scenario "${config.name}" not found, skipping.`);
24343
+ console.warn(`Scenario "${config.name}" not found, skipping.`);
24304
24344
  continue;
24305
24345
  }
24306
24346
  const beforeRows = countRows(dataset);
@@ -24379,7 +24419,7 @@ function applyScheduledScenarios(dataset, scheduled, random, totalMonths, custom
24379
24419
  for (const scheduled_item of scheduled) {
24380
24420
  const definition = registry.get(scheduled_item.config.name);
24381
24421
  if (!definition) {
24382
- console.warn(`[databox] Scheduled scenario "${scheduled_item.config.name}" not found, skipping.`);
24422
+ console.warn(`Scheduled scenario "${scheduled_item.config.name}" not found, skipping.`);
24383
24423
  continue;
24384
24424
  }
24385
24425
  const { subset, complementTables } = extractTimeWindow(dataset, scheduled_item.startMonth, scheduled_item.endMonth, totalMonths);
@@ -25583,6 +25623,11 @@ function generateTemplate(analyses, databaseName) {
25583
25623
  for (const col of table.columns) {
25584
25624
  if (col.detection.isPrimaryKey || col.detection.isForeignKey)
25585
25625
  continue;
25626
+ if (col.refinedStrategy.kind === "enum" && col.refinedStrategy.options?.["values"] && Array.isArray(col.refinedStrategy.options["values"])) {
25627
+ const vals = col.refinedStrategy.options["values"];
25628
+ if (vals.length === 1 && vals[0] === "default")
25629
+ continue;
25630
+ }
25586
25631
  const entry = {
25587
25632
  strategy: col.refinedStrategy.kind
25588
25633
  };
@@ -25977,6 +26022,7 @@ function replaceText(random) {
25977
26022
  }
25978
26023
 
25979
26024
  // ../../packages/generators/dist/mask/auditLog.js
26025
+ var import_node_crypto = require("crypto");
25980
26026
  function buildAuditLog(detectionsByTable, maskResults, mode, seed, databaseName) {
25981
26027
  const tables = [];
25982
26028
  let totalPIIColumnsDetected = 0;
@@ -26017,8 +26063,9 @@ function buildAuditLog(detectionsByTable, maskResults, mode, seed, databaseName)
26017
26063
  columns
26018
26064
  });
26019
26065
  }
26066
+ const integrityChain = buildIntegrityChain(tables, mode, seed, databaseName);
26020
26067
  return {
26021
- version: "1.0",
26068
+ version: "1.1",
26022
26069
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
26023
26070
  complianceMode: mode,
26024
26071
  seed,
@@ -26030,7 +26077,8 @@ function buildAuditLog(detectionsByTable, maskResults, mode, seed, databaseName)
26030
26077
  totalColumnsMasked,
26031
26078
  totalRowsMasked
26032
26079
  },
26033
- tables
26080
+ tables,
26081
+ integrityChain
26034
26082
  };
26035
26083
  }
26036
26084
  function formatAuditLog(log) {
@@ -26063,11 +26111,362 @@ function formatAuditLog(log) {
26063
26111
  }
26064
26112
  lines.push("");
26065
26113
  }
26114
+ if (log.integrityChain) {
26115
+ lines.push("Integrity Chain");
26116
+ lines.push("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
26117
+ lines.push(` Algorithm: ${log.integrityChain.algorithm}`);
26118
+ lines.push(` Genesis: ${log.integrityChain.genesisHash.substring(0, 16)}...`);
26119
+ lines.push(` Final: ${log.integrityChain.finalHash.substring(0, 16)}...`);
26120
+ const verification = verifyAuditLogIntegrity(log);
26121
+ lines.push(` Status: ${verification.valid ? "VERIFIED" : `BROKEN at ${verification.brokenAt}`}`);
26122
+ lines.push("");
26123
+ }
26066
26124
  return lines.join("\n");
26067
26125
  }
26068
26126
  function serializeAuditLog(log) {
26069
26127
  return JSON.stringify(log, null, 2);
26070
26128
  }
26129
+ function buildIntegrityChain(tables, mode, seed, databaseName) {
26130
+ const genesisHash = sha256(`genesis:${mode}:${seed}:${databaseName}`);
26131
+ const tableHashes = [];
26132
+ let previousHash = genesisHash;
26133
+ for (const table of tables) {
26134
+ const payload = `${previousHash}:${JSON.stringify(table)}`;
26135
+ const hash = sha256(payload);
26136
+ tableHashes.push({ tableName: table.tableName, hash });
26137
+ previousHash = hash;
26138
+ }
26139
+ return {
26140
+ algorithm: "sha256",
26141
+ genesisHash,
26142
+ tableHashes,
26143
+ finalHash: previousHash
26144
+ };
26145
+ }
26146
+ function verifyAuditLogIntegrity(log) {
26147
+ if (!log.integrityChain) {
26148
+ return { valid: false, brokenAt: "missing integrityChain" };
26149
+ }
26150
+ const chain = log.integrityChain;
26151
+ const expectedGenesis = sha256(`genesis:${log.complianceMode}:${log.seed}:${log.databaseName}`);
26152
+ if (chain.genesisHash !== expectedGenesis) {
26153
+ return { valid: false, brokenAt: "genesisHash" };
26154
+ }
26155
+ let previousHash = chain.genesisHash;
26156
+ for (let i = 0; i < log.tables.length; i++) {
26157
+ const table = log.tables[i];
26158
+ const payload = `${previousHash}:${JSON.stringify(table)}`;
26159
+ const expectedHash = sha256(payload);
26160
+ const recorded = chain.tableHashes[i];
26161
+ if (!recorded || recorded.hash !== expectedHash) {
26162
+ return { valid: false, brokenAt: table.tableName };
26163
+ }
26164
+ previousHash = expectedHash;
26165
+ }
26166
+ if (previousHash !== chain.finalHash) {
26167
+ return { valid: false, brokenAt: "finalHash" };
26168
+ }
26169
+ return { valid: true };
26170
+ }
26171
+ function sha256(input) {
26172
+ return (0, import_node_crypto.createHash)("sha256").update(input, "utf8").digest("hex");
26173
+ }
26174
+
26175
+ // ../../packages/generators/dist/mask/tokenizer.js
26176
+ var import_node_crypto2 = require("crypto");
26177
+ function tokenizeTableRows(rows, detections, tableName, tokenPrefix = "TOK") {
26178
+ const columnsToMask = detections.filter((d) => d.shouldMask);
26179
+ const entries = [];
26180
+ const tokenCache = /* @__PURE__ */ new Map();
26181
+ const tokenizedRows = rows.map((row) => {
26182
+ const newRow = { ...row };
26183
+ for (const detection of columnsToMask) {
26184
+ const originalValue = row[detection.columnName];
26185
+ if (originalValue === null || originalValue === void 0)
26186
+ continue;
26187
+ const cacheKey = `${detection.columnName}:${String(originalValue)}`;
26188
+ let token = tokenCache.get(cacheKey);
26189
+ if (!token) {
26190
+ const hash = (0, import_node_crypto2.createHash)("sha256").update(`${tableName}:${cacheKey}`, "utf8").digest("hex").substring(0, 12);
26191
+ token = `${tokenPrefix}-${hash}`;
26192
+ tokenCache.set(cacheKey, token);
26193
+ entries.push({
26194
+ token,
26195
+ originalValue,
26196
+ columnName: detection.columnName,
26197
+ tableName,
26198
+ piiCategory: detection.category
26199
+ });
26200
+ }
26201
+ newRow[detection.columnName] = token;
26202
+ }
26203
+ return newRow;
26204
+ });
26205
+ return { tokenizedRows, entries };
26206
+ }
26207
+ function buildTokenMap(entries, tokenPrefix = "TOK") {
26208
+ return {
26209
+ version: "1.0",
26210
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
26211
+ tokenPrefix,
26212
+ totalTokens: entries.length,
26213
+ entries
26214
+ };
26215
+ }
26216
+ function generateTokenPrefix() {
26217
+ return `TOK-${(0, import_node_crypto2.randomBytes)(3).toString("hex").toUpperCase()}`;
26218
+ }
26219
+ function encryptTokenMap(tokenMap, passphrase) {
26220
+ const salt = (0, import_node_crypto2.randomBytes)(16);
26221
+ const key = (0, import_node_crypto2.pbkdf2Sync)(passphrase, salt, 1e5, 32, "sha512");
26222
+ const iv = (0, import_node_crypto2.randomBytes)(12);
26223
+ const cipher = (0, import_node_crypto2.createCipheriv)("aes-256-gcm", key, iv);
26224
+ const plaintext = JSON.stringify(tokenMap);
26225
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
26226
+ const authTag = cipher.getAuthTag();
26227
+ const packed = Buffer.concat([salt, iv, authTag, encrypted]);
26228
+ return packed.toString("base64");
26229
+ }
26230
+ function decryptTokenMap(encryptedBase64, passphrase) {
26231
+ const packed = Buffer.from(encryptedBase64, "base64");
26232
+ const salt = packed.subarray(0, 16);
26233
+ const iv = packed.subarray(16, 28);
26234
+ const authTag = packed.subarray(28, 44);
26235
+ const ciphertext = packed.subarray(44);
26236
+ const key = (0, import_node_crypto2.pbkdf2Sync)(passphrase, salt, 1e5, 32, "sha512");
26237
+ const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
26238
+ decipher.setAuthTag(authTag);
26239
+ try {
26240
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
26241
+ return JSON.parse(decrypted.toString("utf8"));
26242
+ } catch {
26243
+ throw new Error("[realitydb] Decryption failed \u2014 wrong passphrase or corrupted data");
26244
+ }
26245
+ }
26246
+
26247
+ // ../../packages/generators/dist/mask/valueScanners.js
26248
+ var VALUE_PATTERNS = [
26249
+ // === Direct identifiers ===
26250
+ {
26251
+ category: "email",
26252
+ patterns: [/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/],
26253
+ label: "email address",
26254
+ strategy: "replace_email",
26255
+ threshold: 0.3,
26256
+ action: "tokenize"
26257
+ },
26258
+ {
26259
+ category: "ssn",
26260
+ patterns: [/\b\d{3}-\d{2}-\d{4}\b/],
26261
+ label: "SSN",
26262
+ strategy: "replace_ssn",
26263
+ threshold: 0.1,
26264
+ action: "block"
26265
+ },
26266
+ {
26267
+ category: "phone",
26268
+ patterns: [
26269
+ /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/,
26270
+ /\+1\d{10}\b/,
26271
+ /\(\d{3}\)\s?\d{3}[-.]?\d{4}/
26272
+ ],
26273
+ label: "phone number",
26274
+ strategy: "replace_phone",
26275
+ threshold: 0.2,
26276
+ action: "tokenize"
26277
+ },
26278
+ {
26279
+ category: "ip_address",
26280
+ patterns: [/\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/],
26281
+ label: "IP address",
26282
+ strategy: "replace_ip",
26283
+ threshold: 0.2,
26284
+ action: "mask"
26285
+ },
26286
+ {
26287
+ category: "name",
26288
+ patterns: [
26289
+ /\b(Mr|Mrs|Ms|Dr|Prof)\.?\s+[A-Z][a-z]+/,
26290
+ /\b[A-Z][a-z]+ [A-Z][a-z]+\b/
26291
+ ],
26292
+ label: "person name",
26293
+ strategy: "replace_name",
26294
+ threshold: 0.3,
26295
+ action: "tokenize"
26296
+ },
26297
+ {
26298
+ category: "address",
26299
+ patterns: [
26300
+ /\b\d{1,5}\s+[A-Z][a-z]+(\s+[A-Z][a-z]+)*\s+(St|Ave|Blvd|Dr|Ln|Rd|Way|Ct|Pl|Ter|Cir|Pike|Highway|Hwy)\b/i
26301
+ ],
26302
+ label: "street address",
26303
+ strategy: "replace_address",
26304
+ threshold: 0.2,
26305
+ action: "tokenize"
26306
+ },
26307
+ // === Financial ===
26308
+ {
26309
+ category: "financial",
26310
+ patterns: [
26311
+ /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/,
26312
+ /\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}(?:[A-Z0-9]{0,16})?\b/
26313
+ ],
26314
+ label: "financial identifier",
26315
+ strategy: "replace_ssn",
26316
+ threshold: 0.1,
26317
+ action: "block"
26318
+ },
26319
+ {
26320
+ category: "bank_routing",
26321
+ patterns: [
26322
+ /\b0[0-9]\d{7}\b/,
26323
+ /\b1[0-2]\d{7}\b/,
26324
+ /\b[2-3][0-9]\d{7}\b/
26325
+ ],
26326
+ label: "bank routing number",
26327
+ strategy: "replace_ssn",
26328
+ threshold: 0.1,
26329
+ action: "mask"
26330
+ },
26331
+ // === Dates ===
26332
+ {
26333
+ category: "date_of_birth",
26334
+ patterns: [
26335
+ /\b(0[1-9]|1[0-2])[/-](0[1-9]|[12]\d|3[01])[/-](19|20)\d{2}\b/,
26336
+ /\b(19|20)\d{2}[/-](0[1-9]|1[0-2])[/-](0[1-9]|[12]\d|3[01])\b/
26337
+ ],
26338
+ label: "date of birth",
26339
+ strategy: "shift_date",
26340
+ threshold: 0.15,
26341
+ action: "mask"
26342
+ },
26343
+ // === Government IDs ===
26344
+ {
26345
+ category: "drivers_license",
26346
+ patterns: [
26347
+ /\b[A-Z]\d{3}[-\s]?\d{3}[-\s]?\d{3}[-\s]?\d{3}\b/
26348
+ ],
26349
+ label: "drivers license",
26350
+ strategy: "replace_ssn",
26351
+ threshold: 0.1,
26352
+ action: "block"
26353
+ },
26354
+ {
26355
+ category: "passport",
26356
+ patterns: [
26357
+ /\b[A-Z]\d{8}\b/
26358
+ ],
26359
+ label: "passport number",
26360
+ strategy: "replace_ssn",
26361
+ threshold: 0.1,
26362
+ action: "block"
26363
+ },
26364
+ // === URLs ===
26365
+ {
26366
+ category: "url",
26367
+ patterns: [/https?:\/\/[^\s"'<>]+/],
26368
+ label: "URL",
26369
+ strategy: "replace_url",
26370
+ threshold: 0.3,
26371
+ action: "tokenize"
26372
+ },
26373
+ // === Healthcare vertical ===
26374
+ {
26375
+ category: "medical",
26376
+ patterns: [
26377
+ /\bMRN[-:\s]?\d{6,10}\b/i,
26378
+ /\bNPI[-:\s]?\d{10}\b/i
26379
+ ],
26380
+ label: "medical record number",
26381
+ strategy: "redact",
26382
+ threshold: 0.1,
26383
+ action: "tokenize"
26384
+ },
26385
+ // === Education vertical ===
26386
+ {
26387
+ category: "student_id",
26388
+ patterns: [
26389
+ /\b(SID|STU|STUDENT)[-:\s]?\d{5,10}\b/i
26390
+ ],
26391
+ label: "student ID",
26392
+ strategy: "replace_ssn",
26393
+ threshold: 0.15,
26394
+ action: "tokenize"
26395
+ },
26396
+ // === Legal vertical ===
26397
+ {
26398
+ category: "case_number",
26399
+ patterns: [
26400
+ /\b\d{2,4}[-]?[A-Z]{2,4}[-]?\d{3,8}\b/i
26401
+ ],
26402
+ label: "case number",
26403
+ strategy: "redact",
26404
+ threshold: 0.1,
26405
+ action: "tokenize"
26406
+ },
26407
+ // === Automotive ===
26408
+ {
26409
+ category: "vin",
26410
+ patterns: [
26411
+ /\b[A-HJ-NPR-Z0-9]{17}\b/
26412
+ ],
26413
+ label: "VIN",
26414
+ strategy: "redact",
26415
+ threshold: 0.1,
26416
+ action: "block"
26417
+ }
26418
+ ];
26419
+ var FREE_TEXT_NAME_PATTERNS = /^(notes|bio|biography|description|comments|remarks|narrative|summary|details|message|body|content|text|memo|reason|observation|feedback|review)$/i;
26420
+ var FREE_TEXT_TYPE_PATTERNS = ["text", "mediumtext", "longtext"];
26421
+ function isFreeTextColumn(columnName, dataType, maxLength) {
26422
+ if (FREE_TEXT_NAME_PATTERNS.test(columnName))
26423
+ return true;
26424
+ if (FREE_TEXT_TYPE_PATTERNS.includes(dataType.toLowerCase()))
26425
+ return true;
26426
+ if (maxLength !== null && maxLength > 500)
26427
+ return true;
26428
+ return false;
26429
+ }
26430
+ function scanColumnValues(values, optionsOrMaxSampleSize) {
26431
+ let maxSampleSize = 100;
26432
+ let isFreeText = false;
26433
+ if (typeof optionsOrMaxSampleSize === "number") {
26434
+ maxSampleSize = optionsOrMaxSampleSize;
26435
+ } else if (optionsOrMaxSampleSize) {
26436
+ maxSampleSize = optionsOrMaxSampleSize.maxSampleSize ?? 100;
26437
+ isFreeText = optionsOrMaxSampleSize.isFreeText ?? false;
26438
+ }
26439
+ if (isFreeText) {
26440
+ maxSampleSize = Math.min(300, Math.max(maxSampleSize * 3, values.length));
26441
+ }
26442
+ const stringValues = values.filter((v) => typeof v === "string" && v.length > 0).slice(0, maxSampleSize);
26443
+ if (stringValues.length === 0)
26444
+ return [];
26445
+ const results = [];
26446
+ for (const check of VALUE_PATTERNS) {
26447
+ let matches = 0;
26448
+ for (const value of stringValues) {
26449
+ const hasMatch = check.patterns.some((pattern) => pattern.test(value));
26450
+ if (hasMatch)
26451
+ matches++;
26452
+ }
26453
+ const hitRate = matches / stringValues.length;
26454
+ const effectiveThreshold = isFreeText ? check.threshold * 0.5 : check.threshold;
26455
+ if (hitRate >= effectiveThreshold) {
26456
+ const confidence = hitRate >= 0.7 ? "high" : hitRate >= 0.3 ? "medium" : "low";
26457
+ results.push({
26458
+ category: check.category,
26459
+ confidence,
26460
+ hitRate: Math.round(hitRate * 100) / 100,
26461
+ matchedPattern: check.label,
26462
+ suggestedStrategy: check.strategy,
26463
+ action: check.action,
26464
+ isFreeTextField: isFreeText
26465
+ });
26466
+ }
26467
+ }
26468
+ return results;
26469
+ }
26071
26470
 
26072
26471
  // ../../packages/generators/dist/classroom/courseRegistry.js
26073
26472
  var CourseRegistry = class {
@@ -29595,7 +29994,7 @@ function buildGenerationPlan(schema, config, timelineConfig) {
29595
29994
  }
29596
29995
  if (config.template && !template) {
29597
29996
  const available = getDefaultRegistry().list().map((t) => t.name).join(", ");
29598
- console.warn(`[databox] Template "${config.template}" not found. Available: ${available || "none"}`);
29997
+ console.warn(`Template "${config.template}" not found. Available: ${available || "none"}`);
29599
29998
  }
29600
29999
  const graph = buildDependencyGraph(schema.foreignKeys);
29601
30000
  const allTableNames = schema.tables.map((t) => t.name);
@@ -29621,10 +30020,17 @@ function buildGenerationPlan(schema, config, timelineConfig) {
29621
30020
  const dependencies = tableForeignKeys.map((fk) => fk.targetTable);
29622
30021
  const uniqueDeps = [...new Set(dependencies)];
29623
30022
  const tableConfig = template && registry && templateLookupName ? registry.matchTable(templateLookupName, table.name) : null;
29624
- const columns = table.columns.map((column) => {
30023
+ const insertableColumns = table.columns.filter((col) => !col.isGenerated);
30024
+ const columns = insertableColumns.map((column) => {
29625
30025
  let strategy;
29626
30026
  if (template && registry && templateLookupName) {
29627
- const override = resolveColumnOverride(templateLookupName, table.name, column.name, registry);
30027
+ let override = resolveColumnOverride(templateLookupName, table.name, column.name, registry);
30028
+ if (override?.kind === "enum" && override.options?.["values"] && Array.isArray(override.options["values"])) {
30029
+ const vals = override.options["values"];
30030
+ if (vals.length === 1 && vals[0] === "default") {
30031
+ override = null;
30032
+ }
30033
+ }
29628
30034
  strategy = override ?? inferColumnStrategy(column, tableForeignKeys, table.name);
29629
30035
  } else {
29630
30036
  strategy = inferColumnStrategy(column, tableForeignKeys, table.name);
@@ -29658,6 +30064,13 @@ function buildGenerationPlan(schema, config, timelineConfig) {
29658
30064
  };
29659
30065
  }
29660
30066
  }
30067
+ const INTEGER_TYPES2 = ["int2", "int4", "int8", "integer", "serial", "bigserial", "smallint", "bigint", "int", "mediumint", "tinyint"];
30068
+ if (strategy.kind === "float" && INTEGER_TYPES2.includes(column.udtName.toLowerCase())) {
30069
+ strategy = {
30070
+ kind: "integer",
30071
+ options: strategy.options
30072
+ };
30073
+ }
29661
30074
  const columnPlan = {
29662
30075
  columnName: column.name,
29663
30076
  dataType: column.udtName,
@@ -29933,7 +30346,7 @@ function createDatabaseClient(client, connectionString) {
29933
30346
  case "mysql":
29934
30347
  return createMysqlPool(connectionString);
29935
30348
  default:
29936
- throw new Error(`[databox] Unsupported database client: ${client}`);
30349
+ throw new Error(`Unsupported database client: ${client}`);
29937
30350
  }
29938
30351
  }
29939
30352
 
@@ -29949,7 +30362,7 @@ async function testConnection(pool) {
29949
30362
  }
29950
30363
  } catch (err) {
29951
30364
  const message = err instanceof Error ? err.message : String(err);
29952
- throw new Error(`[databox] Database connection failed: ${message}`);
30365
+ throw new Error(`Database connection failed: ${message}`);
29953
30366
  }
29954
30367
  }
29955
30368
  async function closeConnection(pool) {
@@ -29957,7 +30370,7 @@ async function closeConnection(pool) {
29957
30370
  await pool.end();
29958
30371
  } catch (err) {
29959
30372
  const message = err instanceof Error ? err.message : String(err);
29960
- throw new Error(`[databox] Failed to close database connection: ${message}`);
30373
+ throw new Error(`Failed to close database connection: ${message}`);
29961
30374
  }
29962
30375
  }
29963
30376
 
@@ -29985,6 +30398,9 @@ function toMySQLValue(value) {
29985
30398
  if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
29986
30399
  return value.replace("T", " ").replace(/\.\d{3}Z$/, "").replace(/Z$/, "");
29987
30400
  }
30401
+ if (value !== null && typeof value === "object") {
30402
+ return JSON.stringify(value);
30403
+ }
29988
30404
  return value;
29989
30405
  }
29990
30406
  async function batchInsertTable(client, table, batchSize, dialect = "postgres") {
@@ -30080,11 +30496,15 @@ async function truncateTables(pool, tableNames, cascade) {
30080
30496
  }
30081
30497
 
30082
30498
  // ../../packages/db/dist/readTable.js
30083
- async function readTableRows(pool, tableName, columns) {
30499
+ async function readTableRows(pool, tableName, columns, limit) {
30084
30500
  const dialect = pool.dialect;
30085
30501
  const quotedColumns = columns.map((c) => quoteIdent(dialect, c)).join(", ");
30086
30502
  const quotedTable = quoteIdent(dialect, tableName);
30087
- const result = await pool.query(`SELECT ${quotedColumns} FROM ${quotedTable}`);
30503
+ let sql = `SELECT ${quotedColumns} FROM ${quotedTable}`;
30504
+ if (limit !== void 0 && limit > 0) {
30505
+ sql += ` LIMIT ${Math.floor(limit)}`;
30506
+ }
30507
+ const result = await pool.query(sql);
30088
30508
  return result.rows;
30089
30509
  }
30090
30510
 
@@ -30117,25 +30537,32 @@ function normalizeSchema(raw) {
30117
30537
  for (const rawTable of raw.tables) {
30118
30538
  const rawCols = columnsByTable.get(rawTable.table_name);
30119
30539
  if (!rawCols || rawCols.length === 0) {
30120
- console.warn(`[databox] Table "${rawTable.table_name}" has no columns \u2014 excluding from schema`);
30540
+ console.warn(`Table "${rawTable.table_name}" has no columns \u2014 excluding from schema`);
30121
30541
  continue;
30122
30542
  }
30123
30543
  const pk = primaryKeyMap.get(rawTable.table_name) ?? null;
30124
30544
  const primaryKey = pk ? { columnName: pk.column_name, constraintName: pk.constraint_name } : null;
30125
- const columns = rawCols.map((col) => ({
30126
- name: col.column_name,
30127
- dataType: col.data_type,
30128
- udtName: col.udt_name,
30129
- isNullable: col.is_nullable === "YES",
30130
- hasDefault: col.column_default !== null,
30131
- defaultValue: col.column_default,
30132
- maxLength: col.character_maximum_length,
30133
- numericPrecision: col.numeric_precision ?? null,
30134
- numericScale: col.numeric_scale ?? null,
30135
- isPrimaryKey: pk !== null && pk.column_name === col.column_name,
30136
- isUnique: pk !== null && pk.column_name === col.column_name || uniqueColumns.has(`${rawTable.table_name}.${col.column_name}`),
30137
- ordinalPosition: col.ordinal_position
30138
- }));
30545
+ const columns = rawCols.map((col) => {
30546
+ const extra = (col.extra ?? "").toLowerCase();
30547
+ const isAutoIncrement = extra.includes("auto_increment");
30548
+ const isGenerated = extra.includes("generated");
30549
+ return {
30550
+ name: col.column_name,
30551
+ dataType: col.data_type,
30552
+ udtName: col.udt_name,
30553
+ isNullable: col.is_nullable === "YES",
30554
+ hasDefault: col.column_default !== null || isAutoIncrement || isGenerated,
30555
+ defaultValue: col.column_default,
30556
+ maxLength: col.character_maximum_length,
30557
+ numericPrecision: col.numeric_precision ?? null,
30558
+ numericScale: col.numeric_scale ?? null,
30559
+ isPrimaryKey: pk !== null && pk.column_name === col.column_name,
30560
+ isUnique: pk !== null && pk.column_name === col.column_name || uniqueColumns.has(`${rawTable.table_name}.${col.column_name}`),
30561
+ ordinalPosition: col.ordinal_position,
30562
+ isGenerated
30563
+ };
30564
+ });
30565
+ columns.sort((a, b) => a.ordinalPosition - b.ordinalPosition);
30139
30566
  tables.push({
30140
30567
  name: rawTable.table_name,
30141
30568
  schema: rawTable.table_schema,
@@ -30238,7 +30665,8 @@ async function getColumns(pool, schemaName = "public") {
30238
30665
  IS_NULLABLE AS is_nullable, COLUMN_DEFAULT AS column_default,
30239
30666
  CHARACTER_MAXIMUM_LENGTH AS character_maximum_length,
30240
30667
  NUMERIC_PRECISION AS numeric_precision, NUMERIC_SCALE AS numeric_scale,
30241
- ORDINAL_POSITION AS ordinal_position
30668
+ ORDINAL_POSITION AS ordinal_position,
30669
+ EXTRA AS extra
30242
30670
  FROM information_schema.columns
30243
30671
  WHERE TABLE_SCHEMA = ?
30244
30672
  ORDER BY TABLE_NAME, ORDINAL_POSITION`, [schemaName]);
@@ -30369,15 +30797,15 @@ async function introspectDatabase(pool, schemaName, options) {
30369
30797
  const schema = normalizeSchema({ tables, columns, foreignKeys, primaryKeys, uniqueConstraints });
30370
30798
  const validation = validateSchema(schema);
30371
30799
  for (const warning of validation.warnings) {
30372
- console.warn(`[databox] Schema warning: ${warning}`);
30800
+ console.warn(`Schema warning: ${warning}`);
30373
30801
  }
30374
30802
  if (options?.verbose) {
30375
30803
  for (const vw of validation.verboseWarnings) {
30376
- console.warn(`[databox] Schema warning: ${vw}`);
30804
+ console.warn(`Schema warning: ${vw}`);
30377
30805
  }
30378
30806
  }
30379
30807
  for (const error of validation.errors) {
30380
- console.error(`[databox] Schema error: ${error}`);
30808
+ console.error(`Schema error: ${error}`);
30381
30809
  }
30382
30810
  return schema;
30383
30811
  }
@@ -30660,7 +31088,7 @@ async function scanDatabase(config) {
30660
31088
  try {
30661
31089
  const connected = await testConnection(pool);
30662
31090
  if (!connected) {
30663
- throw new Error("[databox] Database connection test returned false");
31091
+ throw new Error("Database connection test returned false");
30664
31092
  }
30665
31093
  const schema = await introspectDatabase(pool);
30666
31094
  const graph = buildDependencyGraph(schema.foreignKeys);
@@ -30793,7 +31221,7 @@ function applyLifecycleOverlay(dataset, lifecycle, plan, schema) {
30793
31221
  if (validColumns && !validColumns.has(col)) {
30794
31222
  const key = `${lifecycle.rootTable}.${col}`;
30795
31223
  if (!warnedColumns.has(key)) {
30796
- console.warn(`[lifecycle] Skipping column '${col}' on table '${lifecycle.rootTable}' \u2014 does not exist in schema`);
31224
+ console.warn(`Skipping lifecycle column '${col}' on table '${lifecycle.rootTable}' \u2014 not in schema`);
30797
31225
  warnedColumns.add(key);
30798
31226
  }
30799
31227
  continue;
@@ -30819,7 +31247,7 @@ function applyLifecycleOverlay(dataset, lifecycle, plan, schema) {
30819
31247
  if (validColumns && !validColumns.has(col)) {
30820
31248
  const key = `${tableName}.${col}`;
30821
31249
  if (!warnedColumns.has(key)) {
30822
- console.warn(`[lifecycle] Skipping column '${col}' on table '${tableName}' \u2014 does not exist in schema`);
31250
+ console.warn(`Skipping lifecycle column '${col}' on table '${tableName}' \u2014 not in schema`);
30823
31251
  warnedColumns.add(key);
30824
31252
  }
30825
31253
  continue;
@@ -31225,7 +31653,7 @@ async function uploadToGist(content, options) {
31225
31653
  [options.filename]: { content }
31226
31654
  }
31227
31655
  });
31228
- return new Promise((resolve12, reject) => {
31656
+ return new Promise((resolve13, reject) => {
31229
31657
  const req = import_node_https.default.request({
31230
31658
  hostname: "api.github.com",
31231
31659
  path: "/gists",
@@ -31247,7 +31675,7 @@ async function uploadToGist(content, options) {
31247
31675
  try {
31248
31676
  const parsed = JSON.parse(data);
31249
31677
  const fileEntry = parsed.files[options.filename];
31250
- resolve12({
31678
+ resolve13({
31251
31679
  url: parsed.html_url,
31252
31680
  rawUrl: fileEntry?.raw_url ?? parsed.html_url,
31253
31681
  gistId: parsed.id
@@ -31374,7 +31802,7 @@ function fetchUrl(url, redirectCount = 0) {
31374
31802
  throw new Error("Too many redirects");
31375
31803
  }
31376
31804
  const lib = url.startsWith("https") ? import_node_https2.default : import_node_http.default;
31377
- return new Promise((resolve12, reject) => {
31805
+ return new Promise((resolve13, reject) => {
31378
31806
  const req = lib.get(url, {
31379
31807
  headers: {
31380
31808
  "User-Agent": "realitydb-cli",
@@ -31382,7 +31810,7 @@ function fetchUrl(url, redirectCount = 0) {
31382
31810
  }
31383
31811
  }, (res) => {
31384
31812
  if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
31385
- resolve12(fetchUrl(res.headers.location, redirectCount + 1));
31813
+ resolve13(fetchUrl(res.headers.location, redirectCount + 1));
31386
31814
  return;
31387
31815
  }
31388
31816
  if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
@@ -31395,7 +31823,7 @@ function fetchUrl(url, redirectCount = 0) {
31395
31823
  });
31396
31824
  res.on("end", () => {
31397
31825
  const buffer = Buffer.concat(chunks);
31398
- resolve12(buffer.toString("utf-8"));
31826
+ resolve13(buffer.toString("utf-8"));
31399
31827
  });
31400
31828
  });
31401
31829
  req.on("error", (err) => {
@@ -31405,17 +31833,65 @@ function fetchUrl(url, redirectCount = 0) {
31405
31833
  }
31406
31834
 
31407
31835
  // ../../packages/core/dist/analyzePipeline.js
31836
+ var PII_TO_KIND = {
31837
+ email: "email",
31838
+ phone: "phone",
31839
+ name: "full_name",
31840
+ address: "address",
31841
+ ssn: "text",
31842
+ ip_address: "text",
31843
+ url: "url",
31844
+ financial: "text",
31845
+ medical: "text",
31846
+ date_of_birth: "timestamp"
31847
+ };
31408
31848
  async function analyzeDatabase(config, options) {
31409
31849
  const start = performance.now();
31410
31850
  const sampleSize = options?.sampleSize ?? 1e3;
31851
+ const safeMode = options?.safeMode ?? true;
31411
31852
  const pool = createDatabaseClient(config.database.client, config.database.connectionString);
31412
31853
  try {
31413
31854
  await testConnection(pool);
31414
31855
  const schema = await introspectDatabase(pool);
31415
31856
  const tableAnalyses = [];
31857
+ let totalScanned = 0;
31858
+ let totalDetections = 0;
31859
+ const byCategory = {};
31416
31860
  for (const table of schema.tables) {
31417
31861
  const tableForeignKeys = schema.foreignKeys.filter((fk) => fk.sourceTable === table.name);
31418
31862
  const detections = detectTableColumns(table.columns, tableForeignKeys, table.name);
31863
+ if (safeMode && table.estimatedRowCount > 0) {
31864
+ const scanSampleSize = Math.min(sampleSize, 100);
31865
+ const columnNames = table.columns.map((c) => c.name);
31866
+ const sampleRows = await readTableRows(pool, table.name, columnNames, scanSampleSize);
31867
+ if (sampleRows.length > 0) {
31868
+ for (let i = 0; i < detections.length; i++) {
31869
+ const detection = detections[i];
31870
+ if (detection.isPrimaryKey || detection.isForeignKey)
31871
+ continue;
31872
+ const col = table.columns.find((c) => c.name === detection.columnName);
31873
+ const values = sampleRows.map((r) => r[detection.columnName]);
31874
+ const freeText = col ? isFreeTextColumn(col.name, col.udtName, col.maxLength) : false;
31875
+ const scanResults = scanColumnValues(values, { isFreeText: freeText });
31876
+ totalScanned++;
31877
+ if (scanResults.length > 0) {
31878
+ for (const result of scanResults) {
31879
+ totalDetections++;
31880
+ byCategory[result.category] = (byCategory[result.category] ?? 0) + 1;
31881
+ }
31882
+ const best = scanResults.sort((a, b) => b.hitRate - a.hitRate)[0];
31883
+ const mappedKind = PII_TO_KIND[best.category];
31884
+ if (mappedKind && (detection.confidence === "low" || detection.confidence === "medium")) {
31885
+ detections[i] = {
31886
+ ...detection,
31887
+ confidence: best.confidence === "high" ? "high" : "medium",
31888
+ reason: `${detection.reason}; value scan confirmed ${best.matchedPattern} (${Math.round(best.hitRate * 100)}% hit rate)`
31889
+ };
31890
+ }
31891
+ }
31892
+ }
31893
+ }
31894
+ }
31419
31895
  const queryFn = async (sql) => {
31420
31896
  const result = await pool.query(sql);
31421
31897
  return result.rows;
@@ -31435,13 +31911,28 @@ async function analyzeDatabase(config, options) {
31435
31911
  const dbName = extractDatabaseName(config.database.connectionString);
31436
31912
  const hasSampleData = schema.tables.some((t) => t.estimatedRowCount > 0);
31437
31913
  const report = buildAnalysisReport(tableAnalyses, dbName, sampleSize, hasSampleData);
31914
+ const confidenceBreakdown = { high: 0, medium: 0, low: 0 };
31915
+ for (const analysis of tableAnalyses) {
31916
+ for (const col of analysis.columns) {
31917
+ confidenceBreakdown[col.detection.confidence]++;
31918
+ }
31919
+ }
31438
31920
  let templateJson;
31439
- if (options?.output) {
31921
+ if (options?.output || options?.autoTemplate) {
31440
31922
  const template = generateTemplate(tableAnalyses, dbName);
31441
31923
  templateJson = serializeTemplate(template);
31442
31924
  }
31925
+ const sanitizationReport = safeMode ? { totalScanned, totalDetections, byCategory } : void 0;
31443
31926
  const durationMs = Math.round(performance.now() - start);
31444
- return { schema, report, templateJson, durationMs };
31927
+ return {
31928
+ schema,
31929
+ report,
31930
+ templateJson,
31931
+ templatePath: options?.output,
31932
+ confidenceBreakdown,
31933
+ sanitizationReport,
31934
+ durationMs
31935
+ };
31445
31936
  } finally {
31446
31937
  await closeConnection(pool);
31447
31938
  }
@@ -31480,8 +31971,45 @@ async function maskDatabase(config, options) {
31480
31971
  const detections = detectTablePII(table.columns, tableForeignKeys, table.name, mode);
31481
31972
  detectionsByTable.set(table.name, detections);
31482
31973
  }
31974
+ if (options?.deepScan) {
31975
+ for (const table of schema.tables) {
31976
+ const detections = detectionsByTable.get(table.name);
31977
+ if (!detections)
31978
+ continue;
31979
+ const safeCandidates = detections.filter((d) => d.category === "safe" && !d.isPrimaryKey && !d.isForeignKey);
31980
+ if (safeCandidates.length === 0)
31981
+ continue;
31982
+ const candidateNames = safeCandidates.map((d) => d.columnName);
31983
+ const sampleRows = await readTableRows(pool, table.name, candidateNames, 100);
31984
+ if (sampleRows.length === 0)
31985
+ continue;
31986
+ for (const candidate of safeCandidates) {
31987
+ const values = sampleRows.map((r) => r[candidate.columnName]);
31988
+ const col = table.columns.find((c) => c.name === candidate.columnName);
31989
+ const freeText = col ? isFreeTextColumn(col.name, col.udtName, col.maxLength) : false;
31990
+ const scanResults = scanColumnValues(values, { isFreeText: freeText });
31991
+ if (scanResults.length > 0) {
31992
+ const best = scanResults.sort((a, b) => b.hitRate - a.hitRate)[0];
31993
+ const idx = detections.findIndex((d) => d.columnName === candidate.columnName);
31994
+ if (idx !== -1) {
31995
+ detections[idx] = {
31996
+ ...detections[idx],
31997
+ category: best.category,
31998
+ confidence: best.confidence,
31999
+ reason: `Value scan: ${Math.round(best.hitRate * 100)}% of samples contain ${best.matchedPattern}`,
32000
+ shouldMask: true,
32001
+ maskStrategy: best.suggestedStrategy
32002
+ };
32003
+ }
32004
+ }
32005
+ }
32006
+ }
32007
+ }
31483
32008
  const maskResults = [];
31484
32009
  const random = createSeededRandom(seed);
32010
+ const useTokenization = options?.tokenize ?? false;
32011
+ const tokenPrefix = useTokenization ? generateTokenPrefix() : "TOK";
32012
+ const allTokenEntries = [];
31485
32013
  const maskedTables = /* @__PURE__ */ new Map();
31486
32014
  for (const tableName of tableOrder) {
31487
32015
  const table = schema.tables.find((t) => t.name === tableName);
@@ -31513,15 +32041,36 @@ async function maskDatabase(config, options) {
31513
32041
  });
31514
32042
  continue;
31515
32043
  }
31516
- const { maskedRows, result } = maskTableRows(rows, detections, random, tableName);
31517
- maskResults.push(result);
32044
+ let outputRows;
32045
+ if (useTokenization) {
32046
+ const { tokenizedRows, entries } = tokenizeTableRows(rows, detections, tableName, tokenPrefix);
32047
+ outputRows = tokenizedRows;
32048
+ allTokenEntries.push(...entries);
32049
+ const columnsToMask = detections.filter((d) => d.shouldMask);
32050
+ const maskedColumns = columnsToMask.map((d) => ({
32051
+ columnName: d.columnName,
32052
+ strategy: d.maskStrategy,
32053
+ rowsMasked: tokenizedRows.filter((r) => r[d.columnName] !== null && r[d.columnName] !== void 0).length
32054
+ }));
32055
+ maskResults.push({
32056
+ tableName,
32057
+ rowCount: rows.length,
32058
+ columnsMatched: detections.length,
32059
+ columnsMasked: columnsToMask.length,
32060
+ maskedColumns
32061
+ });
32062
+ } else {
32063
+ const { maskedRows, result } = maskTableRows(rows, detections, random, tableName);
32064
+ outputRows = maskedRows;
32065
+ maskResults.push(result);
32066
+ }
31518
32067
  if (dryRun)
31519
32068
  continue;
31520
32069
  maskedTables.set(tableName, {
31521
32070
  tableName,
31522
32071
  columns,
31523
- rows: maskedRows,
31524
- rowCount: maskedRows.length
32072
+ rows: outputRows,
32073
+ rowCount: outputRows.length
31525
32074
  });
31526
32075
  }
31527
32076
  let outputFiles;
@@ -31563,6 +32112,10 @@ async function maskDatabase(config, options) {
31563
32112
  }
31564
32113
  const dbName = extractDatabaseName2(config.database.connectionString);
31565
32114
  const auditLog = buildAuditLog(detectionsByTable, maskResults, mode, seed, dbName);
32115
+ let tokenMap;
32116
+ if (useTokenization && allTokenEntries.length > 0) {
32117
+ tokenMap = buildTokenMap(allTokenEntries, tokenPrefix);
32118
+ }
31566
32119
  const durationMs = Math.round(performance.now() - start);
31567
32120
  return {
31568
32121
  schema,
@@ -31571,7 +32124,8 @@ async function maskDatabase(config, options) {
31571
32124
  dryRun,
31572
32125
  tablesProcessed: maskResults.length,
31573
32126
  totalRowsMasked: auditLog.summary.totalRowsMasked,
31574
- outputFiles
32127
+ outputFiles,
32128
+ tokenMap
31575
32129
  };
31576
32130
  } finally {
31577
32131
  await closeConnection(pool);
@@ -31777,9 +32331,9 @@ var DEFAULT_CONNECTIONS = {
31777
32331
  };
31778
32332
  var SUPPORTED_CLIENTS = ["postgres", "mysql"];
31779
32333
  function ask(rl, prompt) {
31780
- return new Promise((resolve12) => {
32334
+ return new Promise((resolve13) => {
31781
32335
  rl.question(prompt, (answer) => {
31782
- resolve12(answer);
32336
+ resolve13(answer);
31783
32337
  });
31784
32338
  });
31785
32339
  }
@@ -32059,7 +32613,7 @@ async function loadConfig(filePath) {
32059
32613
  } else {
32060
32614
  const found = await findConfigFile(".");
32061
32615
  if (!found) {
32062
- throw new Error(`[realitydb] Config file not found.
32616
+ throw new Error(`Config file not found.
32063
32617
  Create a realitydb.config.json or specify a path with --config.`);
32064
32618
  }
32065
32619
  resolvedPath = found;
@@ -32068,23 +32622,23 @@ Create a realitydb.config.json or specify a path with --config.`);
32068
32622
  try {
32069
32623
  raw = await (0, import_promises9.readFile)(resolvedPath, "utf-8");
32070
32624
  } catch {
32071
- throw new Error(`[realitydb] Config file not found: ${resolvedPath}
32625
+ throw new Error(`Config file not found: ${resolvedPath}
32072
32626
  Create a realitydb.config.json or specify a path with --config.`);
32073
32627
  }
32074
32628
  let parsed;
32075
32629
  try {
32076
32630
  parsed = JSON.parse(raw);
32077
32631
  } catch {
32078
- throw new Error(`[realitydb] Invalid JSON in config file: ${resolvedPath}`);
32632
+ throw new Error(`Invalid JSON in config file: ${resolvedPath}`);
32079
32633
  }
32080
32634
  const config = parsed;
32081
32635
  const database = config["database"];
32082
32636
  if (!database || typeof database["connectionString"] !== "string") {
32083
- throw new Error("[realitydb] Config validation failed: database.connectionString is required.");
32637
+ throw new Error("Config validation failed: database.connectionString is required.");
32084
32638
  }
32085
32639
  const client = database["client"] ?? "postgres";
32086
32640
  if (client !== "postgres" && client !== "mysql") {
32087
- throw new Error(`[realitydb] Config validation failed: database.client must be 'postgres' or 'mysql', got '${client}'.`);
32641
+ throw new Error(`Config validation failed: database.client must be 'postgres' or 'mysql', got '${client}'.`);
32088
32642
  }
32089
32643
  return {
32090
32644
  database: {
@@ -32196,6 +32750,7 @@ async function scanCommand(options) {
32196
32750
 
32197
32751
  // src/commands/seed.ts
32198
32752
  var import_node_path10 = require("path");
32753
+ var import_node_fs8 = require("fs");
32199
32754
 
32200
32755
  // src/resolveTemplate.ts
32201
32756
  function resolveTemplate(nameOrPath) {
@@ -32232,7 +32787,26 @@ async function seedCommand(options) {
32232
32787
  const records = options.records ? parseInt(options.records, 10) : void 0;
32233
32788
  const seed = options.seed ? parseInt(options.seed, 10) : void 0;
32234
32789
  const rawTemplateName = options.template ?? config.template;
32235
- const templateName = rawTemplateName && (rawTemplateName.includes("/") || rawTemplateName.includes("\\") || rawTemplateName.endsWith(".json")) ? (0, import_node_path10.resolve)(rawTemplateName) : rawTemplateName;
32790
+ let templateName = rawTemplateName && (rawTemplateName.includes("/") || rawTemplateName.includes("\\") || rawTemplateName.endsWith(".json")) ? (0, import_node_path10.resolve)(rawTemplateName) : rawTemplateName;
32791
+ if (options.autoTemplate && !templateName) {
32792
+ if (!options.ci) {
32793
+ console.log("");
32794
+ console.log("Running auto-template analysis...");
32795
+ }
32796
+ const analyzeResult = await analyzeDatabase(config, {
32797
+ sampleSize: 100,
32798
+ autoTemplate: true,
32799
+ safeMode: true
32800
+ });
32801
+ if (analyzeResult.templateJson) {
32802
+ const autoPath = (0, import_node_path10.resolve)(".realitydb-auto-template.json");
32803
+ (0, import_node_fs8.writeFileSync)(autoPath, analyzeResult.templateJson + "\n", "utf-8");
32804
+ templateName = autoPath;
32805
+ if (!options.ci) {
32806
+ console.log(`Auto-template generated: ${autoPath}`);
32807
+ }
32808
+ }
32809
+ }
32236
32810
  const timeline = options.timeline;
32237
32811
  const scenario = options.scenario;
32238
32812
  const scenarioIntensity = options.scenarioIntensity ?? "medium";
@@ -32656,7 +33230,7 @@ async function exportCommand(options) {
32656
33230
  }
32657
33231
 
32658
33232
  // src/commands/templates.ts
32659
- var import_node_fs8 = require("fs");
33233
+ var import_node_fs9 = require("fs");
32660
33234
  var VERSION6 = "0.10.0";
32661
33235
  function templatesCommand() {
32662
33236
  const registry = getDefaultRegistry();
@@ -32683,7 +33257,7 @@ function templatesCommand() {
32683
33257
  }
32684
33258
  function templatesInitCommand() {
32685
33259
  const fileName = "realitydb.template.json";
32686
- if ((0, import_node_fs8.existsSync)(fileName)) {
33260
+ if ((0, import_node_fs9.existsSync)(fileName)) {
32687
33261
  console.error(`[realitydb] ${fileName} already exists in this directory.`);
32688
33262
  process.exit(1);
32689
33263
  }
@@ -32717,7 +33291,7 @@ function templatesInitCommand() {
32717
33291
  }
32718
33292
  }
32719
33293
  };
32720
- (0, import_node_fs8.writeFileSync)(fileName, JSON.stringify(scaffold, null, 2) + "\n");
33294
+ (0, import_node_fs9.writeFileSync)(fileName, JSON.stringify(scaffold, null, 2) + "\n");
32721
33295
  console.log("");
32722
33296
  console.log(`Created ${fileName}`);
32723
33297
  console.log("Edit this file to define your custom template.");
@@ -32728,7 +33302,7 @@ function templatesInitCommand() {
32728
33302
  }
32729
33303
  function templatesValidateCommand(filePath, options) {
32730
33304
  const start = performance.now();
32731
- if (!(0, import_node_fs8.existsSync)(filePath)) {
33305
+ if (!(0, import_node_fs9.existsSync)(filePath)) {
32732
33306
  if (options.ci) {
32733
33307
  console.log(formatCIOutput({
32734
33308
  success: false,
@@ -32745,7 +33319,7 @@ function templatesValidateCommand(filePath, options) {
32745
33319
  }
32746
33320
  let json;
32747
33321
  try {
32748
- const raw = (0, import_node_fs8.readFileSync)(filePath, "utf-8");
33322
+ const raw = (0, import_node_fs9.readFileSync)(filePath, "utf-8");
32749
33323
  json = JSON.parse(raw);
32750
33324
  } catch {
32751
33325
  if (options.ci) {
@@ -32808,7 +33382,7 @@ function templatesValidateCommand(filePath, options) {
32808
33382
  }
32809
33383
 
32810
33384
  // src/commands/scenarios.ts
32811
- var import_node_fs9 = require("fs");
33385
+ var import_node_fs10 = require("fs");
32812
33386
  var import_node_path12 = require("path");
32813
33387
  function scenariosCommand() {
32814
33388
  const registry = getDefaultScenarioRegistry();
@@ -32833,12 +33407,12 @@ function scenariosCreateCommand(name) {
32833
33407
  const sanitized = name.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-");
32834
33408
  const fileName = `${sanitized}.scenario.json`;
32835
33409
  const filePath = (0, import_node_path12.resolve)(fileName);
32836
- if ((0, import_node_fs9.existsSync)(filePath)) {
33410
+ if ((0, import_node_fs10.existsSync)(filePath)) {
32837
33411
  console.error(`[realitydb] File already exists: ${fileName}`);
32838
33412
  process.exit(1);
32839
33413
  }
32840
33414
  const scaffold = scaffoldCustomScenario(sanitized);
32841
- (0, import_node_fs9.writeFileSync)(filePath, JSON.stringify(scaffold, null, 2) + "\n", "utf-8");
33415
+ (0, import_node_fs10.writeFileSync)(filePath, JSON.stringify(scaffold, null, 2) + "\n", "utf-8");
32842
33416
  console.log("");
32843
33417
  console.log(`Created custom scenario: ${fileName}`);
32844
33418
  console.log("");
@@ -33593,7 +34167,7 @@ function getDefaultSchema() {
33593
34167
  }
33594
34168
 
33595
34169
  // src/commands/analyze.ts
33596
- var import_node_fs10 = require("fs");
34170
+ var import_node_fs11 = require("fs");
33597
34171
  var import_node_path16 = require("path");
33598
34172
  var VERSION12 = "1.3.1";
33599
34173
  async function analyzeCommand(options) {
@@ -33602,21 +34176,26 @@ async function analyzeCommand(options) {
33602
34176
  const config = await loadConfig(options.configPath);
33603
34177
  const sampleSize = options.sampleSize ? parseInt(options.sampleSize, 10) : 1e3;
33604
34178
  const masked = maskConnectionString(config.database.connectionString);
34179
+ const safeMode = !options.unsafeAnalyze;
34180
+ const autoTemplate = options.autoTemplate ?? false;
34181
+ const outputPath = options.output ?? (autoTemplate ? "./realitydb-template.json" : void 0);
33605
34182
  if (!options.ci) {
33606
34183
  console.log("");
33607
- console.log("RealityDB Analyze");
34184
+ console.log("RealityDB Schema Analysis");
33608
34185
  console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
33609
34186
  console.log(`Database: ${masked}`);
33610
34187
  console.log(`Sample size: ${sampleSize} rows per table`);
33611
- if (options.output) {
33612
- console.log(`Output: ${options.output}`);
34188
+ if (outputPath) {
34189
+ console.log(`Output: ${outputPath}`);
33613
34190
  }
33614
34191
  console.log("");
33615
34192
  console.log("Analyzing schema...");
33616
34193
  }
33617
34194
  const result = await analyzeDatabase(config, {
33618
34195
  sampleSize,
33619
- output: options.output
34196
+ output: outputPath,
34197
+ safeMode,
34198
+ autoTemplate
33620
34199
  });
33621
34200
  const durationMs = Math.round(performance.now() - start);
33622
34201
  if (options.ci) {
@@ -33629,20 +34208,46 @@ async function analyzeCommand(options) {
33629
34208
  durationMs,
33630
34209
  data: {
33631
34210
  ...ciData,
33632
- templateGenerated: !!options.output,
33633
- templateFile: options.output ?? null
34211
+ confidenceBreakdown: result.confidenceBreakdown,
34212
+ sanitizationReport: result.sanitizationReport ?? null,
34213
+ templateGenerated: !!outputPath,
34214
+ templateFile: outputPath ?? null
33634
34215
  }
33635
34216
  }));
33636
- if (result.templateJson && options.output) {
33637
- const filePath = (0, import_node_path16.resolve)(options.output);
33638
- (0, import_node_fs10.writeFileSync)(filePath, result.templateJson + "\n", "utf-8");
34217
+ if (result.templateJson && outputPath) {
34218
+ const filePath = (0, import_node_path16.resolve)(outputPath);
34219
+ (0, import_node_fs11.writeFileSync)(filePath, result.templateJson + "\n", "utf-8");
33639
34220
  }
33640
34221
  return;
33641
34222
  }
33642
34223
  console.log(formatAnalysisReport(result.report));
33643
- if (result.templateJson && options.output) {
33644
- const filePath = (0, import_node_path16.resolve)(options.output);
33645
- (0, import_node_fs10.writeFileSync)(filePath, result.templateJson + "\n", "utf-8");
34224
+ const total = result.confidenceBreakdown.high + result.confidenceBreakdown.medium + result.confidenceBreakdown.low;
34225
+ if (total > 0) {
34226
+ console.log("Analysis Summary:");
34227
+ const pct = (n) => total > 0 ? Math.round(n / total * 100) : 0;
34228
+ console.log(` High confidence: ${result.confidenceBreakdown.high} columns (${pct(result.confidenceBreakdown.high)}%)`);
34229
+ console.log(` Medium confidence: ${result.confidenceBreakdown.medium} columns (${pct(result.confidenceBreakdown.medium)}%)`);
34230
+ console.log(` Low confidence: ${result.confidenceBreakdown.low} columns (${pct(result.confidenceBreakdown.low)}%)`);
34231
+ console.log("");
34232
+ }
34233
+ if (safeMode && result.sanitizationReport) {
34234
+ const sr = result.sanitizationReport;
34235
+ console.log("PII Sanitization: ENABLED");
34236
+ console.log(` Values scanned: ${sr.totalScanned.toLocaleString()}`);
34237
+ console.log(` PII detected: ${sr.totalDetections} instances`);
34238
+ if (Object.keys(sr.byCategory).length > 0) {
34239
+ const cats = Object.entries(sr.byCategory).map(([k, v]) => `${k} (${v})`).join(", ");
34240
+ console.log(` Categories: ${cats}`);
34241
+ }
34242
+ console.log("");
34243
+ } else if (!safeMode) {
34244
+ console.log("PII Sanitization: DISABLED (--unsafe-analyze)");
34245
+ console.log(" Warning: Sample values were NOT sanitized. Do not use on production databases.");
34246
+ console.log("");
34247
+ }
34248
+ if (result.templateJson && outputPath) {
34249
+ const filePath = (0, import_node_path16.resolve)(outputPath);
34250
+ (0, import_node_fs11.writeFileSync)(filePath, result.templateJson + "\n", "utf-8");
33646
34251
  console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
33647
34252
  console.log(`Template generated: ${filePath}`);
33648
34253
  console.log("");
@@ -33651,10 +34256,11 @@ async function analyzeCommand(options) {
33651
34256
  console.log("");
33652
34257
  console.log("Validate it with:");
33653
34258
  console.log(` realitydb templates validate "${filePath}"`);
33654
- } else if (!options.output) {
34259
+ } else if (!outputPath) {
33655
34260
  console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
33656
34261
  console.log("To generate a template from this analysis:");
33657
34262
  console.log(" realitydb analyze --output my-template.json");
34263
+ console.log(" realitydb analyze --auto-template");
33658
34264
  }
33659
34265
  const totalTime = (durationMs / 1e3).toFixed(1);
33660
34266
  console.log("");
@@ -33687,9 +34293,32 @@ async function analyzeCommand(options) {
33687
34293
  }
33688
34294
 
33689
34295
  // src/commands/mask.ts
33690
- var import_node_fs11 = require("fs");
34296
+ var import_node_fs12 = require("fs");
33691
34297
  var import_node_path17 = require("path");
34298
+ var import_node_readline2 = require("readline");
34299
+ var import_node_process2 = require("process");
33692
34300
  var VERSION13 = "1.3.1";
34301
+ function askPassphrase(prompt) {
34302
+ return new Promise((resolvePromise) => {
34303
+ const rl = (0, import_node_readline2.createInterface)({ input: import_node_process2.stdin, output: import_node_process2.stdout });
34304
+ const originalWrite = import_node_process2.stdout.write.bind(import_node_process2.stdout);
34305
+ let muted = false;
34306
+ import_node_process2.stdout.write = ((chunk, ...args) => {
34307
+ if (muted && typeof chunk === "string" && !chunk.includes(prompt)) {
34308
+ return originalWrite("*");
34309
+ }
34310
+ return originalWrite(chunk, ...args);
34311
+ });
34312
+ rl.question(prompt, (answer) => {
34313
+ muted = false;
34314
+ import_node_process2.stdout.write = originalWrite;
34315
+ console.log("");
34316
+ rl.close();
34317
+ resolvePromise(answer);
34318
+ });
34319
+ muted = true;
34320
+ });
34321
+ }
33693
34322
  async function maskCommand(options) {
33694
34323
  const start = performance.now();
33695
34324
  try {
@@ -33747,6 +34376,12 @@ async function maskCommand(options) {
33747
34376
  } else {
33748
34377
  console.log("Mode: write to database (--confirm)");
33749
34378
  }
34379
+ if (options.tokenize) {
34380
+ console.log("Masking: tokenization (reversible)");
34381
+ if (options.tokenMap) {
34382
+ console.log(`Token map: ${options.tokenMap} (AES-256-GCM encrypted)`);
34383
+ }
34384
+ }
33750
34385
  if (options.auditLog) {
33751
34386
  console.log(`Audit log: ${options.auditLog}`);
33752
34387
  }
@@ -33760,12 +34395,30 @@ async function maskCommand(options) {
33760
34395
  output: options.output,
33761
34396
  outputFormat,
33762
34397
  auditLog: options.auditLog,
33763
- confirm: options.confirm
34398
+ confirm: options.confirm,
34399
+ tokenize: options.tokenize,
34400
+ deepScan: options.deepScan
33764
34401
  });
33765
34402
  const durationMs = Math.round(performance.now() - start);
33766
34403
  if (options.auditLog) {
33767
34404
  const auditPath = (0, import_node_path17.resolve)(options.auditLog);
33768
- (0, import_node_fs11.writeFileSync)(auditPath, serializeAuditLog(result.auditLog) + "\n", "utf-8");
34405
+ (0, import_node_fs12.writeFileSync)(auditPath, serializeAuditLog(result.auditLog) + "\n", "utf-8");
34406
+ }
34407
+ if (options.tokenMap && result.tokenMap) {
34408
+ const passphrase = await askPassphrase("Enter passphrase for token map encryption: ");
34409
+ if (!passphrase || passphrase.length === 0) {
34410
+ console.error("[realitydb] Passphrase cannot be empty. Token map not written.");
34411
+ } else {
34412
+ const encryptedData = encryptTokenMap(result.tokenMap, passphrase);
34413
+ const tokenMapPath = (0, import_node_path17.resolve)(options.tokenMap);
34414
+ (0, import_node_fs12.writeFileSync)(tokenMapPath, encryptedData + "\n", "utf-8");
34415
+ if (!options.ci) {
34416
+ console.log(`Token map exported (AES-256-GCM encrypted) \u2192 ${tokenMapPath}`);
34417
+ console.log(` ${result.tokenMap.totalTokens} unique tokens generated`);
34418
+ console.log(" Store the passphrase securely \u2014 it cannot be recovered");
34419
+ console.log("");
34420
+ }
34421
+ }
33769
34422
  }
33770
34423
  if (options.ci) {
33771
34424
  console.log(formatCIOutput({
@@ -33839,9 +34492,155 @@ async function maskCommand(options) {
33839
34492
  }
33840
34493
  }
33841
34494
 
33842
- // src/commands/classroom.ts
33843
- var import_node_fs12 = require("fs");
34495
+ // src/commands/audit.ts
34496
+ var import_node_fs13 = require("fs");
33844
34497
  var import_node_path18 = require("path");
34498
+ var import_node_readline3 = require("readline");
34499
+ var import_node_process3 = require("process");
34500
+ function askPassphrase2(prompt) {
34501
+ return new Promise((resolvePromise) => {
34502
+ const rl = (0, import_node_readline3.createInterface)({ input: import_node_process3.stdin, output: import_node_process3.stdout });
34503
+ const originalWrite = import_node_process3.stdout.write.bind(import_node_process3.stdout);
34504
+ let muted = false;
34505
+ import_node_process3.stdout.write = ((chunk, ...args) => {
34506
+ if (muted && typeof chunk === "string" && !chunk.includes(prompt)) {
34507
+ return originalWrite("*");
34508
+ }
34509
+ return originalWrite(chunk, ...args);
34510
+ });
34511
+ rl.question(prompt, (answer) => {
34512
+ muted = false;
34513
+ import_node_process3.stdout.write = originalWrite;
34514
+ console.log("");
34515
+ rl.close();
34516
+ resolvePromise(answer);
34517
+ });
34518
+ muted = true;
34519
+ });
34520
+ }
34521
+ async function auditVerifyCommand(logFile, options) {
34522
+ try {
34523
+ const filePath = (0, import_node_path18.resolve)(logFile);
34524
+ const raw = (0, import_node_fs13.readFileSync)(filePath, "utf-8");
34525
+ const log = JSON.parse(raw);
34526
+ const result = verifyAuditLogIntegrity(log);
34527
+ const entryCount = log.tables?.length ?? 0;
34528
+ if (options.ci) {
34529
+ console.log(JSON.stringify({
34530
+ file: filePath,
34531
+ entries: entryCount,
34532
+ valid: result.valid,
34533
+ brokenAt: result.brokenAt ?? null
34534
+ }));
34535
+ process.exit(result.valid ? 0 : 1);
34536
+ }
34537
+ console.log("");
34538
+ console.log("Audit Chain Verification");
34539
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
34540
+ console.log(`File: ${logFile}`);
34541
+ console.log(`Entries: ${entryCount}`);
34542
+ if (result.valid) {
34543
+ console.log("Chain integrity: VERIFIED (all hashes valid)");
34544
+ } else {
34545
+ console.log(`Chain integrity: BROKEN at table "${result.brokenAt}"`);
34546
+ console.log("The audit log has been tampered with.");
34547
+ }
34548
+ console.log("");
34549
+ process.exit(result.valid ? 0 : 1);
34550
+ } catch (err) {
34551
+ const message = err instanceof Error ? err.message : String(err);
34552
+ console.error(`[realitydb] Audit verify failed: ${message}`);
34553
+ process.exit(1);
34554
+ }
34555
+ }
34556
+ async function auditSummaryCommand(logFile, options) {
34557
+ try {
34558
+ const filePath = (0, import_node_path18.resolve)(logFile);
34559
+ const raw = (0, import_node_fs13.readFileSync)(filePath, "utf-8");
34560
+ const log = JSON.parse(raw);
34561
+ if (options.ci) {
34562
+ console.log(JSON.stringify({
34563
+ file: filePath,
34564
+ complianceMode: log.complianceMode,
34565
+ summary: log.summary,
34566
+ tables: log.tables.map((t) => ({
34567
+ tableName: t.tableName,
34568
+ rowCount: t.rowCount,
34569
+ piiColumnsDetected: t.piiColumnsDetected,
34570
+ columnsMasked: t.columnsMasked
34571
+ }))
34572
+ }));
34573
+ return;
34574
+ }
34575
+ console.log("");
34576
+ console.log(formatAuditLog(log));
34577
+ } catch (err) {
34578
+ const message = err instanceof Error ? err.message : String(err);
34579
+ console.error(`[realitydb] Audit summary failed: ${message}`);
34580
+ process.exit(1);
34581
+ }
34582
+ }
34583
+ async function auditReIdentifyCommand(options) {
34584
+ try {
34585
+ if (!options.tokenMap) {
34586
+ console.error("[realitydb] --token-map <file> is required for re-identify");
34587
+ process.exit(1);
34588
+ }
34589
+ const filePath = (0, import_node_path18.resolve)(options.tokenMap);
34590
+ const encryptedData = (0, import_node_fs13.readFileSync)(filePath, "utf-8").trim();
34591
+ const passphrase = await askPassphrase2("Enter passphrase to decrypt token map: ");
34592
+ if (!passphrase) {
34593
+ console.error("[realitydb] Passphrase cannot be empty.");
34594
+ process.exit(1);
34595
+ }
34596
+ const tokenMap = decryptTokenMap(encryptedData, passphrase);
34597
+ if (options.ci) {
34598
+ console.log(JSON.stringify({
34599
+ totalTokens: tokenMap.totalTokens,
34600
+ tokenPrefix: tokenMap.tokenPrefix,
34601
+ createdAt: tokenMap.createdAt,
34602
+ entries: tokenMap.entries.length
34603
+ }));
34604
+ return;
34605
+ }
34606
+ console.log("");
34607
+ console.log("Token Map Re-Identification");
34608
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
34609
+ console.log(`Token prefix: ${tokenMap.tokenPrefix}`);
34610
+ console.log(`Created: ${tokenMap.createdAt}`);
34611
+ console.log("");
34612
+ const byTable = /* @__PURE__ */ new Map();
34613
+ for (const entry of tokenMap.entries) {
34614
+ const existing = byTable.get(entry.tableName) ?? [];
34615
+ existing.push(entry);
34616
+ byTable.set(entry.tableName, existing);
34617
+ }
34618
+ for (const [tableName, entries] of byTable) {
34619
+ console.log(`Table: ${tableName}`);
34620
+ console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
34621
+ for (const entry of entries) {
34622
+ console.log(` ${entry.token} \u2192 ${String(entry.originalValue)} (${entry.columnName}, ${entry.piiCategory})`);
34623
+ }
34624
+ console.log("");
34625
+ }
34626
+ console.log(`Restored ${tokenMap.totalTokens} token mappings`);
34627
+ console.log("");
34628
+ console.log("These are real PII values. Handle according to your data policy.");
34629
+ console.log("");
34630
+ } catch (err) {
34631
+ const message = err instanceof Error ? err.message : String(err);
34632
+ if (message.includes("Decryption failed")) {
34633
+ console.error(message);
34634
+ } else {
34635
+ console.error(`[realitydb] Re-identify failed: ${message}`);
34636
+ }
34637
+ process.exit(1);
34638
+ }
34639
+ }
34640
+
34641
+ // src/commands/classroom.ts
34642
+ var import_node_fs14 = require("fs");
34643
+ var import_node_path19 = require("path");
33845
34644
  var VERSION14 = "1.3.1";
33846
34645
  async function classroomListCommand(options) {
33847
34646
  const start = performance.now();
@@ -34104,8 +34903,8 @@ async function classroomCreateCommand(name, options) {
34104
34903
  try {
34105
34904
  const content = classroomCreate(name);
34106
34905
  const filename = `${name}.course.json`;
34107
- const filePath = (0, import_node_path18.resolve)(filename);
34108
- (0, import_node_fs12.writeFileSync)(filePath, content + "\n", "utf-8");
34906
+ const filePath = (0, import_node_path19.resolve)(filename);
34907
+ (0, import_node_fs14.writeFileSync)(filePath, content + "\n", "utf-8");
34109
34908
  const durationMs = Math.round(performance.now() - start);
34110
34909
  if (options.ci) {
34111
34910
  console.log(formatCIOutput({
@@ -34355,22 +35154,22 @@ async function simulateWebhooksCommand(options) {
34355
35154
  }
34356
35155
 
34357
35156
  // src/cli.ts
34358
- var VERSION16 = "1.6.1";
35157
+ var VERSION16 = "1.7.0";
34359
35158
  function run(argv) {
34360
35159
  const program2 = new Command();
34361
- program2.name("realitydb").description("RealityDB \xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\xA2\xE2\u201A\xAC\xC2\x9D Developer Reality Platform").version(VERSION16).option("--config <path>", "Path to config file").option("--ci", "CI mode: JSON output, no prompts, proper exit codes", false).option("--verbose", "Enable verbose output", false);
34362
- program2.command("init").description("Interactive setup wizard \xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\xA2\xE2\u201A\xAC\xC2\x9D connect, scan, and seed in one step").action(async () => {
35160
+ program2.name("realitydb").description("RealityDB \xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\u2026\xC2\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xAC\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u201A\xC2\xAC\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\x9D Developer Reality Platform").version(VERSION16).option("--config <path>", "Path to config file").option("--ci", "CI mode: JSON output, no prompts, proper exit codes", false).option("--verbose", "Enable verbose output", false);
35161
+ program2.command("init").description("Interactive setup wizard \xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\u2026\xC2\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xAC\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u201A\xC2\xAC\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\x9D connect, scan, and seed in one step").action(async () => {
34363
35162
  await initCommand();
34364
35163
  });
34365
35164
  program2.command("scan").description("Scan database schema").action(async () => {
34366
35165
  const opts = program2.opts();
34367
35166
  await scanCommand({ ci: opts.ci, configPath: opts.config });
34368
35167
  });
34369
- program2.command("analyze").description("Analyze database schema and suggest column strategies").option("--output <file>", "Generate a template JSON file from analysis").option("--sample-size <count>", "Number of rows to sample per table", "1000").action(async (cmdOpts) => {
35168
+ program2.command("analyze").description("Analyze database schema and suggest column strategies").option("--output <file>", "Generate a template JSON file from analysis").option("--sample-size <count>", "Number of rows to sample per table", "1000").option("--unsafe-analyze", "Disable PII sanitization (local dev only)").option("--auto-template", "Generate template from analysis (default: ./realitydb-template.json)").action(async (cmdOpts) => {
34370
35169
  const opts = program2.opts();
34371
35170
  await analyzeCommand({ ...cmdOpts, ci: opts.ci, configPath: opts.config });
34372
35171
  });
34373
- program2.command("seed").description("Seed database with generated data").option("--records <count>", "Number of records per table").option("--template <name|path>", "Template name or path to custom .json file").option("--seed <number>", "Random seed for reproducibility").option("--timeline <duration>", 'Timeline duration (e.g., "12-months", "1-year")').option("--scenario <names>", "Scenarios to apply (comma-separated)").option("--scenario-intensity <level>", "Scenario intensity (low|medium|high)", "medium").option("--scenario-schedule <schedule>", 'Timeline-scheduled scenarios (e.g., "fraud-spike:month-6,churn-spike:month-9")').option("--lifecycle", "Enable lifecycle simulation for causally-connected data").action(async (cmdOpts) => {
35172
+ program2.command("seed").description("Seed database with generated data").option("--records <count>", "Number of records per table").option("--template <name|path>", "Template name or path to custom .json file").option("--seed <number>", "Random seed for reproducibility").option("--timeline <duration>", 'Timeline duration (e.g., "12-months", "1-year")').option("--scenario <names>", "Scenarios to apply (comma-separated)").option("--scenario-intensity <level>", "Scenario intensity (low|medium|high)", "medium").option("--scenario-schedule <schedule>", 'Timeline-scheduled scenarios (e.g., "fraud-spike:month-6,churn-spike:month-9")').option("--lifecycle", "Enable lifecycle simulation for causally-connected data").option("--auto-template", "Run analyze \xE2\u2020\u2019 generate template \xE2\u2020\u2019 seed in one command").action(async (cmdOpts) => {
34374
35173
  const opts = program2.opts();
34375
35174
  await seedCommand({ ...cmdOpts, ci: opts.ci, configPath: opts.config });
34376
35175
  });
@@ -34398,10 +35197,23 @@ function run(argv) {
34398
35197
  scenarios.command("create <name>").description("Scaffold a custom scenario JSON file").action((name) => {
34399
35198
  scenariosCreateCommand(name);
34400
35199
  });
34401
- program2.command("mask").description("Detect and mask PII in your database").option("--mode <mode>", "Compliance mode (hipaa|gdpr|strict)", "gdpr").option("--seed <number>", "Random seed for deterministic masking").option("--dry-run", "Preview PII detection without modifying data").option("--output <dir>", "Export masked data to files instead of writing to DB").option("--output-format <format>", "Output format (json|csv|sql)", "json").option("--audit-log <file>", "Write audit log to file").option("--confirm", "Confirm writing masked data back to database").action(async (cmdOpts) => {
35200
+ program2.command("mask").description("Detect and mask PII in your database").option("--mode <mode>", "Compliance mode (hipaa|gdpr|strict)", "gdpr").option("--seed <number>", "Random seed for deterministic masking").option("--dry-run", "Preview PII detection without modifying data").option("--output <dir>", "Export masked data to files instead of writing to DB").option("--output-format <format>", "Output format (json|csv|sql)", "json").option("--audit-log <file>", "Write audit log to file").option("--confirm", "Confirm writing masked data back to database").option("--tokenize", "Use reversible tokenization instead of irreversible masking").option("--token-map <file>", "Write token map to file (requires --tokenize)").option("--deep-scan", "Scan sample values to detect PII missed by schema analysis").action(async (cmdOpts) => {
34402
35201
  const opts = program2.opts();
34403
35202
  await maskCommand({ ...cmdOpts, ci: opts.ci, configPath: opts.config });
34404
35203
  });
35204
+ const audit = program2.command("audit").description("Audit log operations");
35205
+ audit.command("verify <log-file>").description("Verify audit log hash chain integrity").action(async (logFile) => {
35206
+ const opts = program2.opts();
35207
+ await auditVerifyCommand(logFile, { ci: opts.ci });
35208
+ });
35209
+ audit.command("summary <log-file>").description("Print formatted compliance summary from audit log").action(async (logFile) => {
35210
+ const opts = program2.opts();
35211
+ await auditSummaryCommand(logFile, { ci: opts.ci });
35212
+ });
35213
+ audit.command("re-identify").description("Decrypt token map and display original PII mappings").requiredOption("--token-map <file>", "Encrypted token map file").action(async (cmdOpts) => {
35214
+ const opts = program2.opts();
35215
+ await auditReIdentifyCommand({ ...cmdOpts, ci: opts.ci });
35216
+ });
34405
35217
  const classroom = program2.command("classroom").description("Education and classroom mode");
34406
35218
  classroom.command("list", { isDefault: true }).description("List available courses").action(async () => {
34407
35219
  const opts = program2.opts();
@@ -34472,7 +35284,7 @@ function run(argv) {
34472
35284
  console.log(JSON.stringify({ name: "realitydb", version: VERSION16 }));
34473
35285
  } else {
34474
35286
  console.log("");
34475
- console.log(`RealityDB v${VERSION16} \xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\xA2\xE2\u201A\xAC\xC2\x9D Developer Reality Platform`);
35287
+ console.log(`RealityDB v${VERSION16} \xC3\u0192\xC6\u2019\xC3\u2020\xE2\u20AC\u2122\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xA2\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u20AC\u0161\xC2\xAC\xC3\u2026\xC2\xA1\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\xAC\xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\u201A\xAC\xC5\xA1\xC3\u201A\xC2\xAC\xC3\u0192\xE2\u20AC\u0161\xC3\u201A\xC2\x9D Developer Reality Platform`);
34476
35288
  console.log("Run `realitydb --help` for available commands.");
34477
35289
  console.log("");
34478
35290
  }