realitydb 1.6.3 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1097 -159
- 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
|
|
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 =
|
|
16449
|
-
const stage2 =
|
|
16450
|
-
const stage3 =
|
|
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(
|
|
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
|
-
|
|
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((
|
|
20994
|
-
const done = makeDoneCb(
|
|
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((
|
|
21003
|
+
return new this.Promise((resolve13) => {
|
|
21004
21004
|
this.statement.close();
|
|
21005
|
-
|
|
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((
|
|
21075
|
-
const done = makeDoneCb(
|
|
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((
|
|
21092
|
-
const done = makeDoneCb(
|
|
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((
|
|
21102
|
-
this.connection.end(
|
|
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((
|
|
21114
|
-
const done = makeDoneCb(
|
|
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((
|
|
21122
|
-
const done = makeDoneCb(
|
|
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((
|
|
21130
|
-
const done = makeDoneCb(
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
21571
|
-
const done = makeDoneCb(
|
|
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((
|
|
21588
|
-
const done = makeDoneCb(
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
22073
|
-
const done = makeDoneCb(
|
|
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((
|
|
22086
|
-
const done = makeDoneCb(
|
|
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((
|
|
22121
|
+
return new thePromise((resolve13, reject) => {
|
|
22122
22122
|
coreConnection.once("connect", () => {
|
|
22123
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
22176
|
-
const done = makeDoneCb(
|
|
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((
|
|
22189
|
-
const done = makeDoneCb(
|
|
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((
|
|
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
|
-
|
|
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
|
}
|
|
@@ -22920,11 +22938,13 @@ function generateEnum(ctx, values, weights) {
|
|
|
22920
22938
|
if (!weights || weights.length === 0) {
|
|
22921
22939
|
result = ctx.seed.pick(values);
|
|
22922
22940
|
} else {
|
|
22941
|
+
const sum = weights.reduce((a, b) => a + b, 0);
|
|
22942
|
+
const normalized = sum > 0 && Math.abs(sum - 1) > 1e-3 ? weights.map((w) => w / sum) : weights;
|
|
22923
22943
|
const roll = ctx.seed.next();
|
|
22924
22944
|
let cumulative = 0;
|
|
22925
22945
|
result = values[values.length - 1];
|
|
22926
22946
|
for (let i = 0; i < values.length; i++) {
|
|
22927
|
-
cumulative +=
|
|
22947
|
+
cumulative += normalized[i];
|
|
22928
22948
|
if (roll < cumulative) {
|
|
22929
22949
|
result = values[i];
|
|
22930
22950
|
break;
|
|
@@ -23007,7 +23027,7 @@ function createGeneratorRegistry() {
|
|
|
23007
23027
|
const padLength = strategy.options?.["padLength"] ?? 6;
|
|
23008
23028
|
return (ctx) => generateSequential(ctx, prefix, padLength);
|
|
23009
23029
|
}
|
|
23010
|
-
console.warn(`
|
|
23030
|
+
console.warn(`Unknown custom generator "${name}", falling back to text.`);
|
|
23011
23031
|
return (ctx) => generateText(ctx, "short");
|
|
23012
23032
|
}
|
|
23013
23033
|
};
|
|
@@ -23075,6 +23095,19 @@ function resolveForeignKey(ctx, ref) {
|
|
|
23075
23095
|
}
|
|
23076
23096
|
|
|
23077
23097
|
// ../../packages/generators/dist/engine.js
|
|
23098
|
+
var INTEGER_TYPES = /* @__PURE__ */ new Set([
|
|
23099
|
+
"int2",
|
|
23100
|
+
"int4",
|
|
23101
|
+
"int8",
|
|
23102
|
+
"integer",
|
|
23103
|
+
"serial",
|
|
23104
|
+
"bigserial",
|
|
23105
|
+
"smallint",
|
|
23106
|
+
"bigint",
|
|
23107
|
+
"int",
|
|
23108
|
+
"mediumint",
|
|
23109
|
+
"tinyint"
|
|
23110
|
+
]);
|
|
23078
23111
|
function generateDataset(plan) {
|
|
23079
23112
|
const seed = createSeededRandom(plan.reproducibility.randomSeed);
|
|
23080
23113
|
const registry = createGeneratorRegistry();
|
|
@@ -23090,6 +23123,7 @@ function generateDataset(plan) {
|
|
|
23090
23123
|
const isSelfRef = colPlan.foreignKeyRef.referencedTable === tableName;
|
|
23091
23124
|
return {
|
|
23092
23125
|
columnName: colPlan.columnName,
|
|
23126
|
+
dataType: colPlan.dataType,
|
|
23093
23127
|
generator: null,
|
|
23094
23128
|
isForeignKey: true,
|
|
23095
23129
|
isSelfReference: isSelfRef,
|
|
@@ -23103,6 +23137,7 @@ function generateDataset(plan) {
|
|
|
23103
23137
|
}
|
|
23104
23138
|
return {
|
|
23105
23139
|
columnName: colPlan.columnName,
|
|
23140
|
+
dataType: colPlan.dataType,
|
|
23106
23141
|
generator: registry.getGenerator(colPlan.strategy),
|
|
23107
23142
|
isForeignKey: false,
|
|
23108
23143
|
isSelfReference: false,
|
|
@@ -23144,7 +23179,18 @@ function generateDataset(plan) {
|
|
|
23144
23179
|
} else if (colGen.generator) {
|
|
23145
23180
|
row[colGen.columnName] = colGen.generator(ctx);
|
|
23146
23181
|
}
|
|
23182
|
+
const val = row[colGen.columnName];
|
|
23183
|
+
if (typeof val === "number" && !Number.isInteger(val)) {
|
|
23184
|
+
const dt = colGen.dataType.toLowerCase();
|
|
23185
|
+
if (INTEGER_TYPES.has(dt)) {
|
|
23186
|
+
row[colGen.columnName] = Math.floor(val);
|
|
23187
|
+
}
|
|
23188
|
+
}
|
|
23189
|
+
}
|
|
23190
|
+
if (tablePlan.temporalConstraints && tablePlan.temporalConstraints.length > 0) {
|
|
23191
|
+
applyTemporalFixup(row, tablePlan.temporalConstraints, seed);
|
|
23147
23192
|
}
|
|
23193
|
+
applyLifecycleRules(row, tablePlan.columns);
|
|
23148
23194
|
rows.push(row);
|
|
23149
23195
|
}
|
|
23150
23196
|
const generatedTable = {
|
|
@@ -23166,6 +23212,49 @@ function generateDataset(plan) {
|
|
|
23166
23212
|
totalRows
|
|
23167
23213
|
};
|
|
23168
23214
|
}
|
|
23215
|
+
function applyTemporalFixup(row, constraints, seed) {
|
|
23216
|
+
for (const constraint of constraints) {
|
|
23217
|
+
if (constraint.mode !== "dependent" || !constraint.afterColumn)
|
|
23218
|
+
continue;
|
|
23219
|
+
const baseValue = row[constraint.afterColumn];
|
|
23220
|
+
const depValue = row[constraint.columnName];
|
|
23221
|
+
if (baseValue == null || depValue == null)
|
|
23222
|
+
continue;
|
|
23223
|
+
if (typeof baseValue !== "string" && !(baseValue instanceof Date))
|
|
23224
|
+
continue;
|
|
23225
|
+
const baseDate = new Date(baseValue);
|
|
23226
|
+
if (isNaN(baseDate.getTime()))
|
|
23227
|
+
continue;
|
|
23228
|
+
const depDate = new Date(depValue);
|
|
23229
|
+
const withinDays = constraint.withinDays ?? 90;
|
|
23230
|
+
if (isNaN(depDate.getTime()) || depDate <= baseDate) {
|
|
23231
|
+
const offsetMs = (1 + Math.floor(seed.next() * withinDays)) * 864e5;
|
|
23232
|
+
const newDate = new Date(baseDate.getTime() + offsetMs);
|
|
23233
|
+
row[constraint.columnName] = newDate.toISOString();
|
|
23234
|
+
}
|
|
23235
|
+
}
|
|
23236
|
+
}
|
|
23237
|
+
function applyLifecycleRules(row, columns) {
|
|
23238
|
+
for (const colPlan of columns) {
|
|
23239
|
+
if (colPlan.strategy.kind !== "enum")
|
|
23240
|
+
continue;
|
|
23241
|
+
const lifecycleRules = colPlan.strategy.options?.["lifecycleRules"];
|
|
23242
|
+
if (!lifecycleRules || !Array.isArray(lifecycleRules))
|
|
23243
|
+
continue;
|
|
23244
|
+
const currentValue = row[colPlan.columnName];
|
|
23245
|
+
if (typeof currentValue !== "string")
|
|
23246
|
+
continue;
|
|
23247
|
+
for (const rule of lifecycleRules) {
|
|
23248
|
+
if (rule.value === currentValue && Array.isArray(rule.nullFields)) {
|
|
23249
|
+
for (const field of rule.nullFields) {
|
|
23250
|
+
if (field in row) {
|
|
23251
|
+
row[field] = null;
|
|
23252
|
+
}
|
|
23253
|
+
}
|
|
23254
|
+
}
|
|
23255
|
+
}
|
|
23256
|
+
}
|
|
23257
|
+
}
|
|
23169
23258
|
|
|
23170
23259
|
// ../../packages/generators/dist/exporters/json.js
|
|
23171
23260
|
var import_promises = require("fs/promises");
|
|
@@ -24215,7 +24304,7 @@ function applyScenarios(dataset, scenarios, random) {
|
|
|
24215
24304
|
for (const config of scenarios) {
|
|
24216
24305
|
const definition = registry.get(config.name);
|
|
24217
24306
|
if (!definition) {
|
|
24218
|
-
console.warn(`
|
|
24307
|
+
console.warn(`Scenario "${config.name}" not found. Available: ${registry.list().map((s) => s.name).join(", ")}`);
|
|
24219
24308
|
continue;
|
|
24220
24309
|
}
|
|
24221
24310
|
const beforeTotalRows = countTotalValues(dataset);
|
|
@@ -24300,7 +24389,7 @@ function composeScenarios(dataset, configs, random, customScenarios) {
|
|
|
24300
24389
|
for (const config of configs) {
|
|
24301
24390
|
const definition = registry.get(config.name);
|
|
24302
24391
|
if (!definition) {
|
|
24303
|
-
console.warn(`
|
|
24392
|
+
console.warn(`Scenario "${config.name}" not found, skipping.`);
|
|
24304
24393
|
continue;
|
|
24305
24394
|
}
|
|
24306
24395
|
const beforeRows = countRows(dataset);
|
|
@@ -24379,7 +24468,7 @@ function applyScheduledScenarios(dataset, scheduled, random, totalMonths, custom
|
|
|
24379
24468
|
for (const scheduled_item of scheduled) {
|
|
24380
24469
|
const definition = registry.get(scheduled_item.config.name);
|
|
24381
24470
|
if (!definition) {
|
|
24382
|
-
console.warn(`
|
|
24471
|
+
console.warn(`Scheduled scenario "${scheduled_item.config.name}" not found, skipping.`);
|
|
24383
24472
|
continue;
|
|
24384
24473
|
}
|
|
24385
24474
|
const { subset, complementTables } = extractTimeWindow(dataset, scheduled_item.startMonth, scheduled_item.endMonth, totalMonths);
|
|
@@ -25583,6 +25672,11 @@ function generateTemplate(analyses, databaseName) {
|
|
|
25583
25672
|
for (const col of table.columns) {
|
|
25584
25673
|
if (col.detection.isPrimaryKey || col.detection.isForeignKey)
|
|
25585
25674
|
continue;
|
|
25675
|
+
if (col.refinedStrategy.kind === "enum" && col.refinedStrategy.options?.["values"] && Array.isArray(col.refinedStrategy.options["values"])) {
|
|
25676
|
+
const vals = col.refinedStrategy.options["values"];
|
|
25677
|
+
if (vals.length === 1 && vals[0] === "default")
|
|
25678
|
+
continue;
|
|
25679
|
+
}
|
|
25586
25680
|
const entry = {
|
|
25587
25681
|
strategy: col.refinedStrategy.kind
|
|
25588
25682
|
};
|
|
@@ -25977,6 +26071,7 @@ function replaceText(random) {
|
|
|
25977
26071
|
}
|
|
25978
26072
|
|
|
25979
26073
|
// ../../packages/generators/dist/mask/auditLog.js
|
|
26074
|
+
var import_node_crypto = require("crypto");
|
|
25980
26075
|
function buildAuditLog(detectionsByTable, maskResults, mode, seed, databaseName) {
|
|
25981
26076
|
const tables = [];
|
|
25982
26077
|
let totalPIIColumnsDetected = 0;
|
|
@@ -26017,8 +26112,9 @@ function buildAuditLog(detectionsByTable, maskResults, mode, seed, databaseName)
|
|
|
26017
26112
|
columns
|
|
26018
26113
|
});
|
|
26019
26114
|
}
|
|
26115
|
+
const integrityChain = buildIntegrityChain(tables, mode, seed, databaseName);
|
|
26020
26116
|
return {
|
|
26021
|
-
version: "1.
|
|
26117
|
+
version: "1.1",
|
|
26022
26118
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26023
26119
|
complianceMode: mode,
|
|
26024
26120
|
seed,
|
|
@@ -26030,7 +26126,8 @@ function buildAuditLog(detectionsByTable, maskResults, mode, seed, databaseName)
|
|
|
26030
26126
|
totalColumnsMasked,
|
|
26031
26127
|
totalRowsMasked
|
|
26032
26128
|
},
|
|
26033
|
-
tables
|
|
26129
|
+
tables,
|
|
26130
|
+
integrityChain
|
|
26034
26131
|
};
|
|
26035
26132
|
}
|
|
26036
26133
|
function formatAuditLog(log) {
|
|
@@ -26063,11 +26160,362 @@ function formatAuditLog(log) {
|
|
|
26063
26160
|
}
|
|
26064
26161
|
lines.push("");
|
|
26065
26162
|
}
|
|
26163
|
+
if (log.integrityChain) {
|
|
26164
|
+
lines.push("Integrity Chain");
|
|
26165
|
+
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");
|
|
26166
|
+
lines.push(` Algorithm: ${log.integrityChain.algorithm}`);
|
|
26167
|
+
lines.push(` Genesis: ${log.integrityChain.genesisHash.substring(0, 16)}...`);
|
|
26168
|
+
lines.push(` Final: ${log.integrityChain.finalHash.substring(0, 16)}...`);
|
|
26169
|
+
const verification = verifyAuditLogIntegrity(log);
|
|
26170
|
+
lines.push(` Status: ${verification.valid ? "VERIFIED" : `BROKEN at ${verification.brokenAt}`}`);
|
|
26171
|
+
lines.push("");
|
|
26172
|
+
}
|
|
26066
26173
|
return lines.join("\n");
|
|
26067
26174
|
}
|
|
26068
26175
|
function serializeAuditLog(log) {
|
|
26069
26176
|
return JSON.stringify(log, null, 2);
|
|
26070
26177
|
}
|
|
26178
|
+
function buildIntegrityChain(tables, mode, seed, databaseName) {
|
|
26179
|
+
const genesisHash = sha256(`genesis:${mode}:${seed}:${databaseName}`);
|
|
26180
|
+
const tableHashes = [];
|
|
26181
|
+
let previousHash = genesisHash;
|
|
26182
|
+
for (const table of tables) {
|
|
26183
|
+
const payload = `${previousHash}:${JSON.stringify(table)}`;
|
|
26184
|
+
const hash = sha256(payload);
|
|
26185
|
+
tableHashes.push({ tableName: table.tableName, hash });
|
|
26186
|
+
previousHash = hash;
|
|
26187
|
+
}
|
|
26188
|
+
return {
|
|
26189
|
+
algorithm: "sha256",
|
|
26190
|
+
genesisHash,
|
|
26191
|
+
tableHashes,
|
|
26192
|
+
finalHash: previousHash
|
|
26193
|
+
};
|
|
26194
|
+
}
|
|
26195
|
+
function verifyAuditLogIntegrity(log) {
|
|
26196
|
+
if (!log.integrityChain) {
|
|
26197
|
+
return { valid: false, brokenAt: "missing integrityChain" };
|
|
26198
|
+
}
|
|
26199
|
+
const chain = log.integrityChain;
|
|
26200
|
+
const expectedGenesis = sha256(`genesis:${log.complianceMode}:${log.seed}:${log.databaseName}`);
|
|
26201
|
+
if (chain.genesisHash !== expectedGenesis) {
|
|
26202
|
+
return { valid: false, brokenAt: "genesisHash" };
|
|
26203
|
+
}
|
|
26204
|
+
let previousHash = chain.genesisHash;
|
|
26205
|
+
for (let i = 0; i < log.tables.length; i++) {
|
|
26206
|
+
const table = log.tables[i];
|
|
26207
|
+
const payload = `${previousHash}:${JSON.stringify(table)}`;
|
|
26208
|
+
const expectedHash = sha256(payload);
|
|
26209
|
+
const recorded = chain.tableHashes[i];
|
|
26210
|
+
if (!recorded || recorded.hash !== expectedHash) {
|
|
26211
|
+
return { valid: false, brokenAt: table.tableName };
|
|
26212
|
+
}
|
|
26213
|
+
previousHash = expectedHash;
|
|
26214
|
+
}
|
|
26215
|
+
if (previousHash !== chain.finalHash) {
|
|
26216
|
+
return { valid: false, brokenAt: "finalHash" };
|
|
26217
|
+
}
|
|
26218
|
+
return { valid: true };
|
|
26219
|
+
}
|
|
26220
|
+
function sha256(input) {
|
|
26221
|
+
return (0, import_node_crypto.createHash)("sha256").update(input, "utf8").digest("hex");
|
|
26222
|
+
}
|
|
26223
|
+
|
|
26224
|
+
// ../../packages/generators/dist/mask/tokenizer.js
|
|
26225
|
+
var import_node_crypto2 = require("crypto");
|
|
26226
|
+
function tokenizeTableRows(rows, detections, tableName, tokenPrefix = "TOK") {
|
|
26227
|
+
const columnsToMask = detections.filter((d) => d.shouldMask);
|
|
26228
|
+
const entries = [];
|
|
26229
|
+
const tokenCache = /* @__PURE__ */ new Map();
|
|
26230
|
+
const tokenizedRows = rows.map((row) => {
|
|
26231
|
+
const newRow = { ...row };
|
|
26232
|
+
for (const detection of columnsToMask) {
|
|
26233
|
+
const originalValue = row[detection.columnName];
|
|
26234
|
+
if (originalValue === null || originalValue === void 0)
|
|
26235
|
+
continue;
|
|
26236
|
+
const cacheKey = `${detection.columnName}:${String(originalValue)}`;
|
|
26237
|
+
let token = tokenCache.get(cacheKey);
|
|
26238
|
+
if (!token) {
|
|
26239
|
+
const hash = (0, import_node_crypto2.createHash)("sha256").update(`${tableName}:${cacheKey}`, "utf8").digest("hex").substring(0, 12);
|
|
26240
|
+
token = `${tokenPrefix}-${hash}`;
|
|
26241
|
+
tokenCache.set(cacheKey, token);
|
|
26242
|
+
entries.push({
|
|
26243
|
+
token,
|
|
26244
|
+
originalValue,
|
|
26245
|
+
columnName: detection.columnName,
|
|
26246
|
+
tableName,
|
|
26247
|
+
piiCategory: detection.category
|
|
26248
|
+
});
|
|
26249
|
+
}
|
|
26250
|
+
newRow[detection.columnName] = token;
|
|
26251
|
+
}
|
|
26252
|
+
return newRow;
|
|
26253
|
+
});
|
|
26254
|
+
return { tokenizedRows, entries };
|
|
26255
|
+
}
|
|
26256
|
+
function buildTokenMap(entries, tokenPrefix = "TOK") {
|
|
26257
|
+
return {
|
|
26258
|
+
version: "1.0",
|
|
26259
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26260
|
+
tokenPrefix,
|
|
26261
|
+
totalTokens: entries.length,
|
|
26262
|
+
entries
|
|
26263
|
+
};
|
|
26264
|
+
}
|
|
26265
|
+
function generateTokenPrefix() {
|
|
26266
|
+
return `TOK-${(0, import_node_crypto2.randomBytes)(3).toString("hex").toUpperCase()}`;
|
|
26267
|
+
}
|
|
26268
|
+
function encryptTokenMap(tokenMap, passphrase) {
|
|
26269
|
+
const salt = (0, import_node_crypto2.randomBytes)(16);
|
|
26270
|
+
const key = (0, import_node_crypto2.pbkdf2Sync)(passphrase, salt, 1e5, 32, "sha512");
|
|
26271
|
+
const iv = (0, import_node_crypto2.randomBytes)(12);
|
|
26272
|
+
const cipher = (0, import_node_crypto2.createCipheriv)("aes-256-gcm", key, iv);
|
|
26273
|
+
const plaintext = JSON.stringify(tokenMap);
|
|
26274
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
26275
|
+
const authTag = cipher.getAuthTag();
|
|
26276
|
+
const packed = Buffer.concat([salt, iv, authTag, encrypted]);
|
|
26277
|
+
return packed.toString("base64");
|
|
26278
|
+
}
|
|
26279
|
+
function decryptTokenMap(encryptedBase64, passphrase) {
|
|
26280
|
+
const packed = Buffer.from(encryptedBase64, "base64");
|
|
26281
|
+
const salt = packed.subarray(0, 16);
|
|
26282
|
+
const iv = packed.subarray(16, 28);
|
|
26283
|
+
const authTag = packed.subarray(28, 44);
|
|
26284
|
+
const ciphertext = packed.subarray(44);
|
|
26285
|
+
const key = (0, import_node_crypto2.pbkdf2Sync)(passphrase, salt, 1e5, 32, "sha512");
|
|
26286
|
+
const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
|
|
26287
|
+
decipher.setAuthTag(authTag);
|
|
26288
|
+
try {
|
|
26289
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
26290
|
+
return JSON.parse(decrypted.toString("utf8"));
|
|
26291
|
+
} catch {
|
|
26292
|
+
throw new Error("[realitydb] Decryption failed \u2014 wrong passphrase or corrupted data");
|
|
26293
|
+
}
|
|
26294
|
+
}
|
|
26295
|
+
|
|
26296
|
+
// ../../packages/generators/dist/mask/valueScanners.js
|
|
26297
|
+
var VALUE_PATTERNS = [
|
|
26298
|
+
// === Direct identifiers ===
|
|
26299
|
+
{
|
|
26300
|
+
category: "email",
|
|
26301
|
+
patterns: [/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/],
|
|
26302
|
+
label: "email address",
|
|
26303
|
+
strategy: "replace_email",
|
|
26304
|
+
threshold: 0.3,
|
|
26305
|
+
action: "tokenize"
|
|
26306
|
+
},
|
|
26307
|
+
{
|
|
26308
|
+
category: "ssn",
|
|
26309
|
+
patterns: [/\b\d{3}-\d{2}-\d{4}\b/],
|
|
26310
|
+
label: "SSN",
|
|
26311
|
+
strategy: "replace_ssn",
|
|
26312
|
+
threshold: 0.1,
|
|
26313
|
+
action: "block"
|
|
26314
|
+
},
|
|
26315
|
+
{
|
|
26316
|
+
category: "phone",
|
|
26317
|
+
patterns: [
|
|
26318
|
+
/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/,
|
|
26319
|
+
/\+1\d{10}\b/,
|
|
26320
|
+
/\(\d{3}\)\s?\d{3}[-.]?\d{4}/
|
|
26321
|
+
],
|
|
26322
|
+
label: "phone number",
|
|
26323
|
+
strategy: "replace_phone",
|
|
26324
|
+
threshold: 0.2,
|
|
26325
|
+
action: "tokenize"
|
|
26326
|
+
},
|
|
26327
|
+
{
|
|
26328
|
+
category: "ip_address",
|
|
26329
|
+
patterns: [/\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/],
|
|
26330
|
+
label: "IP address",
|
|
26331
|
+
strategy: "replace_ip",
|
|
26332
|
+
threshold: 0.2,
|
|
26333
|
+
action: "mask"
|
|
26334
|
+
},
|
|
26335
|
+
{
|
|
26336
|
+
category: "name",
|
|
26337
|
+
patterns: [
|
|
26338
|
+
/\b(Mr|Mrs|Ms|Dr|Prof)\.?\s+[A-Z][a-z]+/,
|
|
26339
|
+
/\b[A-Z][a-z]+ [A-Z][a-z]+\b/
|
|
26340
|
+
],
|
|
26341
|
+
label: "person name",
|
|
26342
|
+
strategy: "replace_name",
|
|
26343
|
+
threshold: 0.3,
|
|
26344
|
+
action: "tokenize"
|
|
26345
|
+
},
|
|
26346
|
+
{
|
|
26347
|
+
category: "address",
|
|
26348
|
+
patterns: [
|
|
26349
|
+
/\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
|
|
26350
|
+
],
|
|
26351
|
+
label: "street address",
|
|
26352
|
+
strategy: "replace_address",
|
|
26353
|
+
threshold: 0.2,
|
|
26354
|
+
action: "tokenize"
|
|
26355
|
+
},
|
|
26356
|
+
// === Financial ===
|
|
26357
|
+
{
|
|
26358
|
+
category: "financial",
|
|
26359
|
+
patterns: [
|
|
26360
|
+
/\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/,
|
|
26361
|
+
/\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}(?:[A-Z0-9]{0,16})?\b/
|
|
26362
|
+
],
|
|
26363
|
+
label: "financial identifier",
|
|
26364
|
+
strategy: "replace_ssn",
|
|
26365
|
+
threshold: 0.1,
|
|
26366
|
+
action: "block"
|
|
26367
|
+
},
|
|
26368
|
+
{
|
|
26369
|
+
category: "bank_routing",
|
|
26370
|
+
patterns: [
|
|
26371
|
+
/\b0[0-9]\d{7}\b/,
|
|
26372
|
+
/\b1[0-2]\d{7}\b/,
|
|
26373
|
+
/\b[2-3][0-9]\d{7}\b/
|
|
26374
|
+
],
|
|
26375
|
+
label: "bank routing number",
|
|
26376
|
+
strategy: "replace_ssn",
|
|
26377
|
+
threshold: 0.1,
|
|
26378
|
+
action: "mask"
|
|
26379
|
+
},
|
|
26380
|
+
// === Dates ===
|
|
26381
|
+
{
|
|
26382
|
+
category: "date_of_birth",
|
|
26383
|
+
patterns: [
|
|
26384
|
+
/\b(0[1-9]|1[0-2])[/-](0[1-9]|[12]\d|3[01])[/-](19|20)\d{2}\b/,
|
|
26385
|
+
/\b(19|20)\d{2}[/-](0[1-9]|1[0-2])[/-](0[1-9]|[12]\d|3[01])\b/
|
|
26386
|
+
],
|
|
26387
|
+
label: "date of birth",
|
|
26388
|
+
strategy: "shift_date",
|
|
26389
|
+
threshold: 0.15,
|
|
26390
|
+
action: "mask"
|
|
26391
|
+
},
|
|
26392
|
+
// === Government IDs ===
|
|
26393
|
+
{
|
|
26394
|
+
category: "drivers_license",
|
|
26395
|
+
patterns: [
|
|
26396
|
+
/\b[A-Z]\d{3}[-\s]?\d{3}[-\s]?\d{3}[-\s]?\d{3}\b/
|
|
26397
|
+
],
|
|
26398
|
+
label: "drivers license",
|
|
26399
|
+
strategy: "replace_ssn",
|
|
26400
|
+
threshold: 0.1,
|
|
26401
|
+
action: "block"
|
|
26402
|
+
},
|
|
26403
|
+
{
|
|
26404
|
+
category: "passport",
|
|
26405
|
+
patterns: [
|
|
26406
|
+
/\b[A-Z]\d{8}\b/
|
|
26407
|
+
],
|
|
26408
|
+
label: "passport number",
|
|
26409
|
+
strategy: "replace_ssn",
|
|
26410
|
+
threshold: 0.1,
|
|
26411
|
+
action: "block"
|
|
26412
|
+
},
|
|
26413
|
+
// === URLs ===
|
|
26414
|
+
{
|
|
26415
|
+
category: "url",
|
|
26416
|
+
patterns: [/https?:\/\/[^\s"'<>]+/],
|
|
26417
|
+
label: "URL",
|
|
26418
|
+
strategy: "replace_url",
|
|
26419
|
+
threshold: 0.3,
|
|
26420
|
+
action: "tokenize"
|
|
26421
|
+
},
|
|
26422
|
+
// === Healthcare vertical ===
|
|
26423
|
+
{
|
|
26424
|
+
category: "medical",
|
|
26425
|
+
patterns: [
|
|
26426
|
+
/\bMRN[-:\s]?\d{6,10}\b/i,
|
|
26427
|
+
/\bNPI[-:\s]?\d{10}\b/i
|
|
26428
|
+
],
|
|
26429
|
+
label: "medical record number",
|
|
26430
|
+
strategy: "redact",
|
|
26431
|
+
threshold: 0.1,
|
|
26432
|
+
action: "tokenize"
|
|
26433
|
+
},
|
|
26434
|
+
// === Education vertical ===
|
|
26435
|
+
{
|
|
26436
|
+
category: "student_id",
|
|
26437
|
+
patterns: [
|
|
26438
|
+
/\b(SID|STU|STUDENT)[-:\s]?\d{5,10}\b/i
|
|
26439
|
+
],
|
|
26440
|
+
label: "student ID",
|
|
26441
|
+
strategy: "replace_ssn",
|
|
26442
|
+
threshold: 0.15,
|
|
26443
|
+
action: "tokenize"
|
|
26444
|
+
},
|
|
26445
|
+
// === Legal vertical ===
|
|
26446
|
+
{
|
|
26447
|
+
category: "case_number",
|
|
26448
|
+
patterns: [
|
|
26449
|
+
/\b\d{2,4}[-]?[A-Z]{2,4}[-]?\d{3,8}\b/i
|
|
26450
|
+
],
|
|
26451
|
+
label: "case number",
|
|
26452
|
+
strategy: "redact",
|
|
26453
|
+
threshold: 0.1,
|
|
26454
|
+
action: "tokenize"
|
|
26455
|
+
},
|
|
26456
|
+
// === Automotive ===
|
|
26457
|
+
{
|
|
26458
|
+
category: "vin",
|
|
26459
|
+
patterns: [
|
|
26460
|
+
/\b[A-HJ-NPR-Z0-9]{17}\b/
|
|
26461
|
+
],
|
|
26462
|
+
label: "VIN",
|
|
26463
|
+
strategy: "redact",
|
|
26464
|
+
threshold: 0.1,
|
|
26465
|
+
action: "block"
|
|
26466
|
+
}
|
|
26467
|
+
];
|
|
26468
|
+
var FREE_TEXT_NAME_PATTERNS = /^(notes|bio|biography|description|comments|remarks|narrative|summary|details|message|body|content|text|memo|reason|observation|feedback|review)$/i;
|
|
26469
|
+
var FREE_TEXT_TYPE_PATTERNS = ["text", "mediumtext", "longtext"];
|
|
26470
|
+
function isFreeTextColumn(columnName, dataType, maxLength) {
|
|
26471
|
+
if (FREE_TEXT_NAME_PATTERNS.test(columnName))
|
|
26472
|
+
return true;
|
|
26473
|
+
if (FREE_TEXT_TYPE_PATTERNS.includes(dataType.toLowerCase()))
|
|
26474
|
+
return true;
|
|
26475
|
+
if (maxLength !== null && maxLength > 500)
|
|
26476
|
+
return true;
|
|
26477
|
+
return false;
|
|
26478
|
+
}
|
|
26479
|
+
function scanColumnValues(values, optionsOrMaxSampleSize) {
|
|
26480
|
+
let maxSampleSize = 100;
|
|
26481
|
+
let isFreeText = false;
|
|
26482
|
+
if (typeof optionsOrMaxSampleSize === "number") {
|
|
26483
|
+
maxSampleSize = optionsOrMaxSampleSize;
|
|
26484
|
+
} else if (optionsOrMaxSampleSize) {
|
|
26485
|
+
maxSampleSize = optionsOrMaxSampleSize.maxSampleSize ?? 100;
|
|
26486
|
+
isFreeText = optionsOrMaxSampleSize.isFreeText ?? false;
|
|
26487
|
+
}
|
|
26488
|
+
if (isFreeText) {
|
|
26489
|
+
maxSampleSize = Math.min(300, Math.max(maxSampleSize * 3, values.length));
|
|
26490
|
+
}
|
|
26491
|
+
const stringValues = values.filter((v) => typeof v === "string" && v.length > 0).slice(0, maxSampleSize);
|
|
26492
|
+
if (stringValues.length === 0)
|
|
26493
|
+
return [];
|
|
26494
|
+
const results = [];
|
|
26495
|
+
for (const check of VALUE_PATTERNS) {
|
|
26496
|
+
let matches = 0;
|
|
26497
|
+
for (const value of stringValues) {
|
|
26498
|
+
const hasMatch = check.patterns.some((pattern) => pattern.test(value));
|
|
26499
|
+
if (hasMatch)
|
|
26500
|
+
matches++;
|
|
26501
|
+
}
|
|
26502
|
+
const hitRate = matches / stringValues.length;
|
|
26503
|
+
const effectiveThreshold = isFreeText ? check.threshold * 0.5 : check.threshold;
|
|
26504
|
+
if (hitRate >= effectiveThreshold) {
|
|
26505
|
+
const confidence = hitRate >= 0.7 ? "high" : hitRate >= 0.3 ? "medium" : "low";
|
|
26506
|
+
results.push({
|
|
26507
|
+
category: check.category,
|
|
26508
|
+
confidence,
|
|
26509
|
+
hitRate: Math.round(hitRate * 100) / 100,
|
|
26510
|
+
matchedPattern: check.label,
|
|
26511
|
+
suggestedStrategy: check.strategy,
|
|
26512
|
+
action: check.action,
|
|
26513
|
+
isFreeTextField: isFreeText
|
|
26514
|
+
});
|
|
26515
|
+
}
|
|
26516
|
+
}
|
|
26517
|
+
return results;
|
|
26518
|
+
}
|
|
26071
26519
|
|
|
26072
26520
|
// ../../packages/generators/dist/classroom/courseRegistry.js
|
|
26073
26521
|
var CourseRegistry = class {
|
|
@@ -29155,6 +29603,19 @@ function validateTemplateJSON(json) {
|
|
|
29155
29603
|
if (c.options !== void 0 && (typeof c.options !== "object" || Array.isArray(c.options))) {
|
|
29156
29604
|
errors.push(`Table "${tableName}".columns."${colName}": "options" must be an object`);
|
|
29157
29605
|
}
|
|
29606
|
+
if (c.foreignKey !== void 0) {
|
|
29607
|
+
if (typeof c.foreignKey !== "object" || Array.isArray(c.foreignKey) || c.foreignKey === null) {
|
|
29608
|
+
errors.push(`Table "${tableName}".columns."${colName}": "foreignKey" must be an object`);
|
|
29609
|
+
} else {
|
|
29610
|
+
const fk = c.foreignKey;
|
|
29611
|
+
if (typeof fk.table !== "string" || !fk.table) {
|
|
29612
|
+
errors.push(`Table "${tableName}".columns."${colName}": "foreignKey.table" must be a non-empty string`);
|
|
29613
|
+
}
|
|
29614
|
+
if (typeof fk.column !== "string" || !fk.column) {
|
|
29615
|
+
errors.push(`Table "${tableName}".columns."${colName}": "foreignKey.column" must be a non-empty string`);
|
|
29616
|
+
}
|
|
29617
|
+
}
|
|
29618
|
+
}
|
|
29158
29619
|
}
|
|
29159
29620
|
}
|
|
29160
29621
|
return { valid: errors.length === 0, errors };
|
|
@@ -29195,15 +29656,31 @@ function convertToDomainTemplate(json) {
|
|
|
29195
29656
|
const columnOverrides = [];
|
|
29196
29657
|
for (const [colName, colJson] of Object.entries(tableJson.columns)) {
|
|
29197
29658
|
const col = colJson;
|
|
29198
|
-
|
|
29199
|
-
|
|
29200
|
-
|
|
29201
|
-
|
|
29202
|
-
|
|
29203
|
-
|
|
29204
|
-
|
|
29205
|
-
|
|
29206
|
-
|
|
29659
|
+
if (col.foreignKey) {
|
|
29660
|
+
columnOverrides.push({
|
|
29661
|
+
columnName: colName,
|
|
29662
|
+
matchPattern: col.match,
|
|
29663
|
+
strategy: {
|
|
29664
|
+
kind: "foreign_key",
|
|
29665
|
+
options: {
|
|
29666
|
+
...col.options,
|
|
29667
|
+
referencedTable: col.foreignKey.table,
|
|
29668
|
+
referencedColumn: col.foreignKey.column
|
|
29669
|
+
}
|
|
29670
|
+
},
|
|
29671
|
+
description: col.description
|
|
29672
|
+
});
|
|
29673
|
+
} else {
|
|
29674
|
+
columnOverrides.push({
|
|
29675
|
+
columnName: colName,
|
|
29676
|
+
matchPattern: col.match,
|
|
29677
|
+
strategy: {
|
|
29678
|
+
kind: col.strategy,
|
|
29679
|
+
options: col.options
|
|
29680
|
+
},
|
|
29681
|
+
description: col.description
|
|
29682
|
+
});
|
|
29683
|
+
}
|
|
29207
29684
|
}
|
|
29208
29685
|
tableConfigs.set(tableName, {
|
|
29209
29686
|
tableName,
|
|
@@ -29217,7 +29694,9 @@ function convertToDomainTemplate(json) {
|
|
|
29217
29694
|
version: json.version,
|
|
29218
29695
|
description: json.description,
|
|
29219
29696
|
targetTables,
|
|
29220
|
-
tableConfigs
|
|
29697
|
+
tableConfigs,
|
|
29698
|
+
simulation: json.simulation,
|
|
29699
|
+
generationConfig: json.generationConfig
|
|
29221
29700
|
};
|
|
29222
29701
|
}
|
|
29223
29702
|
|
|
@@ -29574,7 +30053,7 @@ var fintechLifecycle = {
|
|
|
29574
30053
|
|
|
29575
30054
|
// ../../packages/core/dist/planning/buildPlan.js
|
|
29576
30055
|
function buildGenerationPlan(schema, config, timelineConfig) {
|
|
29577
|
-
|
|
30056
|
+
let defaultRowCount = config.seed.defaultRecords;
|
|
29578
30057
|
const batchSize = config.seed.batchSize;
|
|
29579
30058
|
const environment = config.seed.environment ?? "dev";
|
|
29580
30059
|
const randomSeed = config.seed.randomSeed ?? 42;
|
|
@@ -29595,7 +30074,7 @@ function buildGenerationPlan(schema, config, timelineConfig) {
|
|
|
29595
30074
|
}
|
|
29596
30075
|
if (config.template && !template) {
|
|
29597
30076
|
const available = getDefaultRegistry().list().map((t) => t.name).join(", ");
|
|
29598
|
-
console.warn(`
|
|
30077
|
+
console.warn(`Template "${config.template}" not found. Available: ${available || "none"}`);
|
|
29599
30078
|
}
|
|
29600
30079
|
const graph = buildDependencyGraph(schema.foreignKeys);
|
|
29601
30080
|
const allTableNames = schema.tables.map((t) => t.name);
|
|
@@ -29621,10 +30100,17 @@ function buildGenerationPlan(schema, config, timelineConfig) {
|
|
|
29621
30100
|
const dependencies = tableForeignKeys.map((fk) => fk.targetTable);
|
|
29622
30101
|
const uniqueDeps = [...new Set(dependencies)];
|
|
29623
30102
|
const tableConfig = template && registry && templateLookupName ? registry.matchTable(templateLookupName, table.name) : null;
|
|
29624
|
-
const
|
|
30103
|
+
const insertableColumns = table.columns.filter((col) => !col.isGenerated);
|
|
30104
|
+
const columns = insertableColumns.map((column) => {
|
|
29625
30105
|
let strategy;
|
|
29626
30106
|
if (template && registry && templateLookupName) {
|
|
29627
|
-
|
|
30107
|
+
let override = resolveColumnOverride(templateLookupName, table.name, column.name, registry);
|
|
30108
|
+
if (override?.kind === "enum" && override.options?.["values"] && Array.isArray(override.options["values"])) {
|
|
30109
|
+
const vals = override.options["values"];
|
|
30110
|
+
if (vals.length === 1 && vals[0] === "default") {
|
|
30111
|
+
override = null;
|
|
30112
|
+
}
|
|
30113
|
+
}
|
|
29628
30114
|
strategy = override ?? inferColumnStrategy(column, tableForeignKeys, table.name);
|
|
29629
30115
|
} else {
|
|
29630
30116
|
strategy = inferColumnStrategy(column, tableForeignKeys, table.name);
|
|
@@ -29658,6 +30144,13 @@ function buildGenerationPlan(schema, config, timelineConfig) {
|
|
|
29658
30144
|
};
|
|
29659
30145
|
}
|
|
29660
30146
|
}
|
|
30147
|
+
const INTEGER_TYPES2 = ["int2", "int4", "int8", "integer", "serial", "bigserial", "smallint", "bigint", "int", "mediumint", "tinyint"];
|
|
30148
|
+
if (strategy.kind === "float" && INTEGER_TYPES2.includes(column.udtName.toLowerCase())) {
|
|
30149
|
+
strategy = {
|
|
30150
|
+
kind: "integer",
|
|
30151
|
+
options: strategy.options
|
|
30152
|
+
};
|
|
30153
|
+
}
|
|
29661
30154
|
const columnPlan = {
|
|
29662
30155
|
columnName: column.name,
|
|
29663
30156
|
dataType: column.udtName,
|
|
@@ -29677,18 +30170,41 @@ function buildGenerationPlan(schema, config, timelineConfig) {
|
|
|
29677
30170
|
selectionMode: "uniform"
|
|
29678
30171
|
};
|
|
29679
30172
|
columnPlan.foreignKeyRef = ref;
|
|
30173
|
+
} else if (strategy.kind === "foreign_key" && strategy.options?.["referencedTable"] && strategy.options?.["referencedColumn"]) {
|
|
30174
|
+
columnPlan.strategy = { kind: "foreign_key" };
|
|
30175
|
+
columnPlan.foreignKeyRef = {
|
|
30176
|
+
referencedTable: strategy.options["referencedTable"],
|
|
30177
|
+
referencedColumn: strategy.options["referencedColumn"],
|
|
30178
|
+
selectionMode: "uniform"
|
|
30179
|
+
};
|
|
29680
30180
|
}
|
|
29681
30181
|
return columnPlan;
|
|
29682
30182
|
});
|
|
30183
|
+
const templateTemporalConstraints = [];
|
|
30184
|
+
for (const colPlan of columns) {
|
|
30185
|
+
const opts = colPlan.strategy.options;
|
|
30186
|
+
if (opts?.["dependsOn"] && typeof opts["dependsOn"] === "string") {
|
|
30187
|
+
templateTemporalConstraints.push({
|
|
30188
|
+
columnName: colPlan.columnName,
|
|
30189
|
+
afterColumn: opts["dependsOn"],
|
|
30190
|
+
mode: "dependent",
|
|
30191
|
+
withinDays: 90
|
|
30192
|
+
});
|
|
30193
|
+
}
|
|
30194
|
+
}
|
|
29683
30195
|
const rowCount = tableConfig?.rowCountMultiplier ? Math.round(defaultRowCount * tableConfig.rowCountMultiplier) : defaultRowCount;
|
|
29684
30196
|
const enabled = template ? tableConfig !== null : true;
|
|
29685
|
-
|
|
30197
|
+
const tablePlan = {
|
|
29686
30198
|
tableName: table.name,
|
|
29687
30199
|
rowCount,
|
|
29688
30200
|
dependencies: uniqueDeps,
|
|
29689
30201
|
columns,
|
|
29690
30202
|
enabled
|
|
29691
30203
|
};
|
|
30204
|
+
if (templateTemporalConstraints.length > 0) {
|
|
30205
|
+
tablePlan.temporalConstraints = templateTemporalConstraints;
|
|
30206
|
+
}
|
|
30207
|
+
return tablePlan;
|
|
29692
30208
|
});
|
|
29693
30209
|
if (template) {
|
|
29694
30210
|
const enabledSet = new Set(tables.filter((t) => t.enabled).map((t) => t.tableName));
|
|
@@ -29933,7 +30449,7 @@ function createDatabaseClient(client, connectionString) {
|
|
|
29933
30449
|
case "mysql":
|
|
29934
30450
|
return createMysqlPool(connectionString);
|
|
29935
30451
|
default:
|
|
29936
|
-
throw new Error(`
|
|
30452
|
+
throw new Error(`Unsupported database client: ${client}`);
|
|
29937
30453
|
}
|
|
29938
30454
|
}
|
|
29939
30455
|
|
|
@@ -29949,7 +30465,7 @@ async function testConnection(pool) {
|
|
|
29949
30465
|
}
|
|
29950
30466
|
} catch (err) {
|
|
29951
30467
|
const message = err instanceof Error ? err.message : String(err);
|
|
29952
|
-
throw new Error(`
|
|
30468
|
+
throw new Error(`Database connection failed: ${message}`);
|
|
29953
30469
|
}
|
|
29954
30470
|
}
|
|
29955
30471
|
async function closeConnection(pool) {
|
|
@@ -29957,7 +30473,7 @@ async function closeConnection(pool) {
|
|
|
29957
30473
|
await pool.end();
|
|
29958
30474
|
} catch (err) {
|
|
29959
30475
|
const message = err instanceof Error ? err.message : String(err);
|
|
29960
|
-
throw new Error(`
|
|
30476
|
+
throw new Error(`Failed to close database connection: ${message}`);
|
|
29961
30477
|
}
|
|
29962
30478
|
}
|
|
29963
30479
|
|
|
@@ -29985,6 +30501,9 @@ function toMySQLValue(value) {
|
|
|
29985
30501
|
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
|
|
29986
30502
|
return value.replace("T", " ").replace(/\.\d{3}Z$/, "").replace(/Z$/, "");
|
|
29987
30503
|
}
|
|
30504
|
+
if (value !== null && typeof value === "object") {
|
|
30505
|
+
return JSON.stringify(value);
|
|
30506
|
+
}
|
|
29988
30507
|
return value;
|
|
29989
30508
|
}
|
|
29990
30509
|
async function batchInsertTable(client, table, batchSize, dialect = "postgres") {
|
|
@@ -30036,6 +30555,17 @@ async function batchInsertDataset(client, dataset, tableOrder, batchSize, dialec
|
|
|
30036
30555
|
const start = performance.now();
|
|
30037
30556
|
const results = [];
|
|
30038
30557
|
let totalRows = 0;
|
|
30558
|
+
const targetTables = tableOrder.filter((name) => dataset.tables.has(name));
|
|
30559
|
+
for (let i = targetTables.length - 1; i >= 0; i--) {
|
|
30560
|
+
const tableName = quoteIdent(dialect, targetTables[i]);
|
|
30561
|
+
if (dialect === "mysql") {
|
|
30562
|
+
await client.query("SET FOREIGN_KEY_CHECKS = 0");
|
|
30563
|
+
await client.query(`TRUNCATE TABLE ${tableName}`);
|
|
30564
|
+
await client.query("SET FOREIGN_KEY_CHECKS = 1");
|
|
30565
|
+
} else {
|
|
30566
|
+
await client.query(`TRUNCATE TABLE ${tableName} CASCADE`);
|
|
30567
|
+
}
|
|
30568
|
+
}
|
|
30039
30569
|
for (const tableName of tableOrder) {
|
|
30040
30570
|
const table = dataset.tables.get(tableName);
|
|
30041
30571
|
if (!table)
|
|
@@ -30080,11 +30610,15 @@ async function truncateTables(pool, tableNames, cascade) {
|
|
|
30080
30610
|
}
|
|
30081
30611
|
|
|
30082
30612
|
// ../../packages/db/dist/readTable.js
|
|
30083
|
-
async function readTableRows(pool, tableName, columns) {
|
|
30613
|
+
async function readTableRows(pool, tableName, columns, limit) {
|
|
30084
30614
|
const dialect = pool.dialect;
|
|
30085
30615
|
const quotedColumns = columns.map((c) => quoteIdent(dialect, c)).join(", ");
|
|
30086
30616
|
const quotedTable = quoteIdent(dialect, tableName);
|
|
30087
|
-
|
|
30617
|
+
let sql = `SELECT ${quotedColumns} FROM ${quotedTable}`;
|
|
30618
|
+
if (limit !== void 0 && limit > 0) {
|
|
30619
|
+
sql += ` LIMIT ${Math.floor(limit)}`;
|
|
30620
|
+
}
|
|
30621
|
+
const result = await pool.query(sql);
|
|
30088
30622
|
return result.rows;
|
|
30089
30623
|
}
|
|
30090
30624
|
|
|
@@ -30117,25 +30651,32 @@ function normalizeSchema(raw) {
|
|
|
30117
30651
|
for (const rawTable of raw.tables) {
|
|
30118
30652
|
const rawCols = columnsByTable.get(rawTable.table_name);
|
|
30119
30653
|
if (!rawCols || rawCols.length === 0) {
|
|
30120
|
-
console.warn(`
|
|
30654
|
+
console.warn(`Table "${rawTable.table_name}" has no columns \u2014 excluding from schema`);
|
|
30121
30655
|
continue;
|
|
30122
30656
|
}
|
|
30123
30657
|
const pk = primaryKeyMap.get(rawTable.table_name) ?? null;
|
|
30124
30658
|
const primaryKey = pk ? { columnName: pk.column_name, constraintName: pk.constraint_name } : null;
|
|
30125
|
-
const columns = rawCols.map((col) =>
|
|
30126
|
-
|
|
30127
|
-
|
|
30128
|
-
|
|
30129
|
-
|
|
30130
|
-
|
|
30131
|
-
|
|
30132
|
-
|
|
30133
|
-
|
|
30134
|
-
|
|
30135
|
-
|
|
30136
|
-
|
|
30137
|
-
|
|
30138
|
-
|
|
30659
|
+
const columns = rawCols.map((col) => {
|
|
30660
|
+
const extra = (col.extra ?? "").toLowerCase();
|
|
30661
|
+
const isAutoIncrement = extra.includes("auto_increment");
|
|
30662
|
+
const isGenerated = extra.includes("generated");
|
|
30663
|
+
return {
|
|
30664
|
+
name: col.column_name,
|
|
30665
|
+
dataType: col.data_type,
|
|
30666
|
+
udtName: col.udt_name,
|
|
30667
|
+
isNullable: col.is_nullable === "YES",
|
|
30668
|
+
hasDefault: col.column_default !== null || isAutoIncrement || isGenerated,
|
|
30669
|
+
defaultValue: col.column_default,
|
|
30670
|
+
maxLength: col.character_maximum_length,
|
|
30671
|
+
numericPrecision: col.numeric_precision ?? null,
|
|
30672
|
+
numericScale: col.numeric_scale ?? null,
|
|
30673
|
+
isPrimaryKey: pk !== null && pk.column_name === col.column_name,
|
|
30674
|
+
isUnique: pk !== null && pk.column_name === col.column_name || uniqueColumns.has(`${rawTable.table_name}.${col.column_name}`),
|
|
30675
|
+
ordinalPosition: col.ordinal_position,
|
|
30676
|
+
isGenerated
|
|
30677
|
+
};
|
|
30678
|
+
});
|
|
30679
|
+
columns.sort((a, b) => a.ordinalPosition - b.ordinalPosition);
|
|
30139
30680
|
tables.push({
|
|
30140
30681
|
name: rawTable.table_name,
|
|
30141
30682
|
schema: rawTable.table_schema,
|
|
@@ -30238,7 +30779,8 @@ async function getColumns(pool, schemaName = "public") {
|
|
|
30238
30779
|
IS_NULLABLE AS is_nullable, COLUMN_DEFAULT AS column_default,
|
|
30239
30780
|
CHARACTER_MAXIMUM_LENGTH AS character_maximum_length,
|
|
30240
30781
|
NUMERIC_PRECISION AS numeric_precision, NUMERIC_SCALE AS numeric_scale,
|
|
30241
|
-
ORDINAL_POSITION AS ordinal_position
|
|
30782
|
+
ORDINAL_POSITION AS ordinal_position,
|
|
30783
|
+
EXTRA AS extra
|
|
30242
30784
|
FROM information_schema.columns
|
|
30243
30785
|
WHERE TABLE_SCHEMA = ?
|
|
30244
30786
|
ORDER BY TABLE_NAME, ORDINAL_POSITION`, [schemaName]);
|
|
@@ -30369,15 +30911,15 @@ async function introspectDatabase(pool, schemaName, options) {
|
|
|
30369
30911
|
const schema = normalizeSchema({ tables, columns, foreignKeys, primaryKeys, uniqueConstraints });
|
|
30370
30912
|
const validation = validateSchema(schema);
|
|
30371
30913
|
for (const warning of validation.warnings) {
|
|
30372
|
-
console.warn(`
|
|
30914
|
+
console.warn(`Schema warning: ${warning}`);
|
|
30373
30915
|
}
|
|
30374
30916
|
if (options?.verbose) {
|
|
30375
30917
|
for (const vw of validation.verboseWarnings) {
|
|
30376
|
-
console.warn(`
|
|
30918
|
+
console.warn(`Schema warning: ${vw}`);
|
|
30377
30919
|
}
|
|
30378
30920
|
}
|
|
30379
30921
|
for (const error of validation.errors) {
|
|
30380
|
-
console.error(`
|
|
30922
|
+
console.error(`Schema error: ${error}`);
|
|
30381
30923
|
}
|
|
30382
30924
|
return schema;
|
|
30383
30925
|
}
|
|
@@ -30660,7 +31202,7 @@ async function scanDatabase(config) {
|
|
|
30660
31202
|
try {
|
|
30661
31203
|
const connected = await testConnection(pool);
|
|
30662
31204
|
if (!connected) {
|
|
30663
|
-
throw new Error("
|
|
31205
|
+
throw new Error("Database connection test returned false");
|
|
30664
31206
|
}
|
|
30665
31207
|
const schema = await introspectDatabase(pool);
|
|
30666
31208
|
const graph = buildDependencyGraph(schema.foreignKeys);
|
|
@@ -30708,6 +31250,18 @@ async function seedDatabase(config, options) {
|
|
|
30708
31250
|
if (options?.template !== void 0) {
|
|
30709
31251
|
effectiveConfig.template = options.template;
|
|
30710
31252
|
}
|
|
31253
|
+
if (options?.records === void 0 && effectiveConfig.template) {
|
|
31254
|
+
const isFilePath = effectiveConfig.template.includes("/") || effectiveConfig.template.includes("\\") || effectiveConfig.template.endsWith(".json");
|
|
31255
|
+
if (isFilePath) {
|
|
31256
|
+
try {
|
|
31257
|
+
const tmpl = loadTemplateFromJSON(effectiveConfig.template);
|
|
31258
|
+
if (tmpl.generationConfig?.seed?.defaultRecords) {
|
|
31259
|
+
effectiveConfig.seed.defaultRecords = tmpl.generationConfig.seed.defaultRecords;
|
|
31260
|
+
}
|
|
31261
|
+
} catch {
|
|
31262
|
+
}
|
|
31263
|
+
}
|
|
31264
|
+
}
|
|
30711
31265
|
const timelineConfig = options?.timeline ? parseTimelineString(options.timeline) : void 0;
|
|
30712
31266
|
if (timelineConfig) {
|
|
30713
31267
|
timelineConfig.growthModel.finalCount = effectiveConfig.seed.defaultRecords;
|
|
@@ -30793,7 +31347,7 @@ function applyLifecycleOverlay(dataset, lifecycle, plan, schema) {
|
|
|
30793
31347
|
if (validColumns && !validColumns.has(col)) {
|
|
30794
31348
|
const key = `${lifecycle.rootTable}.${col}`;
|
|
30795
31349
|
if (!warnedColumns.has(key)) {
|
|
30796
|
-
console.warn(`
|
|
31350
|
+
console.warn(`Skipping lifecycle column '${col}' on table '${lifecycle.rootTable}' \u2014 not in schema`);
|
|
30797
31351
|
warnedColumns.add(key);
|
|
30798
31352
|
}
|
|
30799
31353
|
continue;
|
|
@@ -30819,7 +31373,7 @@ function applyLifecycleOverlay(dataset, lifecycle, plan, schema) {
|
|
|
30819
31373
|
if (validColumns && !validColumns.has(col)) {
|
|
30820
31374
|
const key = `${tableName}.${col}`;
|
|
30821
31375
|
if (!warnedColumns.has(key)) {
|
|
30822
|
-
console.warn(`
|
|
31376
|
+
console.warn(`Skipping lifecycle column '${col}' on table '${tableName}' \u2014 not in schema`);
|
|
30823
31377
|
warnedColumns.add(key);
|
|
30824
31378
|
}
|
|
30825
31379
|
continue;
|
|
@@ -31225,7 +31779,7 @@ async function uploadToGist(content, options) {
|
|
|
31225
31779
|
[options.filename]: { content }
|
|
31226
31780
|
}
|
|
31227
31781
|
});
|
|
31228
|
-
return new Promise((
|
|
31782
|
+
return new Promise((resolve13, reject) => {
|
|
31229
31783
|
const req = import_node_https.default.request({
|
|
31230
31784
|
hostname: "api.github.com",
|
|
31231
31785
|
path: "/gists",
|
|
@@ -31247,7 +31801,7 @@ async function uploadToGist(content, options) {
|
|
|
31247
31801
|
try {
|
|
31248
31802
|
const parsed = JSON.parse(data);
|
|
31249
31803
|
const fileEntry = parsed.files[options.filename];
|
|
31250
|
-
|
|
31804
|
+
resolve13({
|
|
31251
31805
|
url: parsed.html_url,
|
|
31252
31806
|
rawUrl: fileEntry?.raw_url ?? parsed.html_url,
|
|
31253
31807
|
gistId: parsed.id
|
|
@@ -31374,7 +31928,7 @@ function fetchUrl(url, redirectCount = 0) {
|
|
|
31374
31928
|
throw new Error("Too many redirects");
|
|
31375
31929
|
}
|
|
31376
31930
|
const lib = url.startsWith("https") ? import_node_https2.default : import_node_http.default;
|
|
31377
|
-
return new Promise((
|
|
31931
|
+
return new Promise((resolve13, reject) => {
|
|
31378
31932
|
const req = lib.get(url, {
|
|
31379
31933
|
headers: {
|
|
31380
31934
|
"User-Agent": "realitydb-cli",
|
|
@@ -31382,7 +31936,7 @@ function fetchUrl(url, redirectCount = 0) {
|
|
|
31382
31936
|
}
|
|
31383
31937
|
}, (res) => {
|
|
31384
31938
|
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
31385
|
-
|
|
31939
|
+
resolve13(fetchUrl(res.headers.location, redirectCount + 1));
|
|
31386
31940
|
return;
|
|
31387
31941
|
}
|
|
31388
31942
|
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
|
|
@@ -31395,7 +31949,7 @@ function fetchUrl(url, redirectCount = 0) {
|
|
|
31395
31949
|
});
|
|
31396
31950
|
res.on("end", () => {
|
|
31397
31951
|
const buffer = Buffer.concat(chunks);
|
|
31398
|
-
|
|
31952
|
+
resolve13(buffer.toString("utf-8"));
|
|
31399
31953
|
});
|
|
31400
31954
|
});
|
|
31401
31955
|
req.on("error", (err) => {
|
|
@@ -31405,17 +31959,65 @@ function fetchUrl(url, redirectCount = 0) {
|
|
|
31405
31959
|
}
|
|
31406
31960
|
|
|
31407
31961
|
// ../../packages/core/dist/analyzePipeline.js
|
|
31962
|
+
var PII_TO_KIND = {
|
|
31963
|
+
email: "email",
|
|
31964
|
+
phone: "phone",
|
|
31965
|
+
name: "full_name",
|
|
31966
|
+
address: "address",
|
|
31967
|
+
ssn: "text",
|
|
31968
|
+
ip_address: "text",
|
|
31969
|
+
url: "url",
|
|
31970
|
+
financial: "text",
|
|
31971
|
+
medical: "text",
|
|
31972
|
+
date_of_birth: "timestamp"
|
|
31973
|
+
};
|
|
31408
31974
|
async function analyzeDatabase(config, options) {
|
|
31409
31975
|
const start = performance.now();
|
|
31410
31976
|
const sampleSize = options?.sampleSize ?? 1e3;
|
|
31977
|
+
const safeMode = options?.safeMode ?? true;
|
|
31411
31978
|
const pool = createDatabaseClient(config.database.client, config.database.connectionString);
|
|
31412
31979
|
try {
|
|
31413
31980
|
await testConnection(pool);
|
|
31414
31981
|
const schema = await introspectDatabase(pool);
|
|
31415
31982
|
const tableAnalyses = [];
|
|
31983
|
+
let totalScanned = 0;
|
|
31984
|
+
let totalDetections = 0;
|
|
31985
|
+
const byCategory = {};
|
|
31416
31986
|
for (const table of schema.tables) {
|
|
31417
31987
|
const tableForeignKeys = schema.foreignKeys.filter((fk) => fk.sourceTable === table.name);
|
|
31418
31988
|
const detections = detectTableColumns(table.columns, tableForeignKeys, table.name);
|
|
31989
|
+
if (safeMode && table.estimatedRowCount > 0) {
|
|
31990
|
+
const scanSampleSize = Math.min(sampleSize, 100);
|
|
31991
|
+
const columnNames = table.columns.map((c) => c.name);
|
|
31992
|
+
const sampleRows = await readTableRows(pool, table.name, columnNames, scanSampleSize);
|
|
31993
|
+
if (sampleRows.length > 0) {
|
|
31994
|
+
for (let i = 0; i < detections.length; i++) {
|
|
31995
|
+
const detection = detections[i];
|
|
31996
|
+
if (detection.isPrimaryKey || detection.isForeignKey)
|
|
31997
|
+
continue;
|
|
31998
|
+
const col = table.columns.find((c) => c.name === detection.columnName);
|
|
31999
|
+
const values = sampleRows.map((r) => r[detection.columnName]);
|
|
32000
|
+
const freeText = col ? isFreeTextColumn(col.name, col.udtName, col.maxLength) : false;
|
|
32001
|
+
const scanResults = scanColumnValues(values, { isFreeText: freeText });
|
|
32002
|
+
totalScanned++;
|
|
32003
|
+
if (scanResults.length > 0) {
|
|
32004
|
+
for (const result of scanResults) {
|
|
32005
|
+
totalDetections++;
|
|
32006
|
+
byCategory[result.category] = (byCategory[result.category] ?? 0) + 1;
|
|
32007
|
+
}
|
|
32008
|
+
const best = scanResults.sort((a, b) => b.hitRate - a.hitRate)[0];
|
|
32009
|
+
const mappedKind = PII_TO_KIND[best.category];
|
|
32010
|
+
if (mappedKind && (detection.confidence === "low" || detection.confidence === "medium")) {
|
|
32011
|
+
detections[i] = {
|
|
32012
|
+
...detection,
|
|
32013
|
+
confidence: best.confidence === "high" ? "high" : "medium",
|
|
32014
|
+
reason: `${detection.reason}; value scan confirmed ${best.matchedPattern} (${Math.round(best.hitRate * 100)}% hit rate)`
|
|
32015
|
+
};
|
|
32016
|
+
}
|
|
32017
|
+
}
|
|
32018
|
+
}
|
|
32019
|
+
}
|
|
32020
|
+
}
|
|
31419
32021
|
const queryFn = async (sql) => {
|
|
31420
32022
|
const result = await pool.query(sql);
|
|
31421
32023
|
return result.rows;
|
|
@@ -31435,13 +32037,28 @@ async function analyzeDatabase(config, options) {
|
|
|
31435
32037
|
const dbName = extractDatabaseName(config.database.connectionString);
|
|
31436
32038
|
const hasSampleData = schema.tables.some((t) => t.estimatedRowCount > 0);
|
|
31437
32039
|
const report = buildAnalysisReport(tableAnalyses, dbName, sampleSize, hasSampleData);
|
|
32040
|
+
const confidenceBreakdown = { high: 0, medium: 0, low: 0 };
|
|
32041
|
+
for (const analysis of tableAnalyses) {
|
|
32042
|
+
for (const col of analysis.columns) {
|
|
32043
|
+
confidenceBreakdown[col.detection.confidence]++;
|
|
32044
|
+
}
|
|
32045
|
+
}
|
|
31438
32046
|
let templateJson;
|
|
31439
|
-
if (options?.output) {
|
|
32047
|
+
if (options?.output || options?.autoTemplate) {
|
|
31440
32048
|
const template = generateTemplate(tableAnalyses, dbName);
|
|
31441
32049
|
templateJson = serializeTemplate(template);
|
|
31442
32050
|
}
|
|
32051
|
+
const sanitizationReport = safeMode ? { totalScanned, totalDetections, byCategory } : void 0;
|
|
31443
32052
|
const durationMs = Math.round(performance.now() - start);
|
|
31444
|
-
return {
|
|
32053
|
+
return {
|
|
32054
|
+
schema,
|
|
32055
|
+
report,
|
|
32056
|
+
templateJson,
|
|
32057
|
+
templatePath: options?.output,
|
|
32058
|
+
confidenceBreakdown,
|
|
32059
|
+
sanitizationReport,
|
|
32060
|
+
durationMs
|
|
32061
|
+
};
|
|
31445
32062
|
} finally {
|
|
31446
32063
|
await closeConnection(pool);
|
|
31447
32064
|
}
|
|
@@ -31480,8 +32097,45 @@ async function maskDatabase(config, options) {
|
|
|
31480
32097
|
const detections = detectTablePII(table.columns, tableForeignKeys, table.name, mode);
|
|
31481
32098
|
detectionsByTable.set(table.name, detections);
|
|
31482
32099
|
}
|
|
32100
|
+
if (options?.deepScan) {
|
|
32101
|
+
for (const table of schema.tables) {
|
|
32102
|
+
const detections = detectionsByTable.get(table.name);
|
|
32103
|
+
if (!detections)
|
|
32104
|
+
continue;
|
|
32105
|
+
const safeCandidates = detections.filter((d) => d.category === "safe" && !d.isPrimaryKey && !d.isForeignKey);
|
|
32106
|
+
if (safeCandidates.length === 0)
|
|
32107
|
+
continue;
|
|
32108
|
+
const candidateNames = safeCandidates.map((d) => d.columnName);
|
|
32109
|
+
const sampleRows = await readTableRows(pool, table.name, candidateNames, 100);
|
|
32110
|
+
if (sampleRows.length === 0)
|
|
32111
|
+
continue;
|
|
32112
|
+
for (const candidate of safeCandidates) {
|
|
32113
|
+
const values = sampleRows.map((r) => r[candidate.columnName]);
|
|
32114
|
+
const col = table.columns.find((c) => c.name === candidate.columnName);
|
|
32115
|
+
const freeText = col ? isFreeTextColumn(col.name, col.udtName, col.maxLength) : false;
|
|
32116
|
+
const scanResults = scanColumnValues(values, { isFreeText: freeText });
|
|
32117
|
+
if (scanResults.length > 0) {
|
|
32118
|
+
const best = scanResults.sort((a, b) => b.hitRate - a.hitRate)[0];
|
|
32119
|
+
const idx = detections.findIndex((d) => d.columnName === candidate.columnName);
|
|
32120
|
+
if (idx !== -1) {
|
|
32121
|
+
detections[idx] = {
|
|
32122
|
+
...detections[idx],
|
|
32123
|
+
category: best.category,
|
|
32124
|
+
confidence: best.confidence,
|
|
32125
|
+
reason: `Value scan: ${Math.round(best.hitRate * 100)}% of samples contain ${best.matchedPattern}`,
|
|
32126
|
+
shouldMask: true,
|
|
32127
|
+
maskStrategy: best.suggestedStrategy
|
|
32128
|
+
};
|
|
32129
|
+
}
|
|
32130
|
+
}
|
|
32131
|
+
}
|
|
32132
|
+
}
|
|
32133
|
+
}
|
|
31483
32134
|
const maskResults = [];
|
|
31484
32135
|
const random = createSeededRandom(seed);
|
|
32136
|
+
const useTokenization = options?.tokenize ?? false;
|
|
32137
|
+
const tokenPrefix = useTokenization ? generateTokenPrefix() : "TOK";
|
|
32138
|
+
const allTokenEntries = [];
|
|
31485
32139
|
const maskedTables = /* @__PURE__ */ new Map();
|
|
31486
32140
|
for (const tableName of tableOrder) {
|
|
31487
32141
|
const table = schema.tables.find((t) => t.name === tableName);
|
|
@@ -31513,15 +32167,36 @@ async function maskDatabase(config, options) {
|
|
|
31513
32167
|
});
|
|
31514
32168
|
continue;
|
|
31515
32169
|
}
|
|
31516
|
-
|
|
31517
|
-
|
|
32170
|
+
let outputRows;
|
|
32171
|
+
if (useTokenization) {
|
|
32172
|
+
const { tokenizedRows, entries } = tokenizeTableRows(rows, detections, tableName, tokenPrefix);
|
|
32173
|
+
outputRows = tokenizedRows;
|
|
32174
|
+
allTokenEntries.push(...entries);
|
|
32175
|
+
const columnsToMask = detections.filter((d) => d.shouldMask);
|
|
32176
|
+
const maskedColumns = columnsToMask.map((d) => ({
|
|
32177
|
+
columnName: d.columnName,
|
|
32178
|
+
strategy: d.maskStrategy,
|
|
32179
|
+
rowsMasked: tokenizedRows.filter((r) => r[d.columnName] !== null && r[d.columnName] !== void 0).length
|
|
32180
|
+
}));
|
|
32181
|
+
maskResults.push({
|
|
32182
|
+
tableName,
|
|
32183
|
+
rowCount: rows.length,
|
|
32184
|
+
columnsMatched: detections.length,
|
|
32185
|
+
columnsMasked: columnsToMask.length,
|
|
32186
|
+
maskedColumns
|
|
32187
|
+
});
|
|
32188
|
+
} else {
|
|
32189
|
+
const { maskedRows, result } = maskTableRows(rows, detections, random, tableName);
|
|
32190
|
+
outputRows = maskedRows;
|
|
32191
|
+
maskResults.push(result);
|
|
32192
|
+
}
|
|
31518
32193
|
if (dryRun)
|
|
31519
32194
|
continue;
|
|
31520
32195
|
maskedTables.set(tableName, {
|
|
31521
32196
|
tableName,
|
|
31522
32197
|
columns,
|
|
31523
|
-
rows:
|
|
31524
|
-
rowCount:
|
|
32198
|
+
rows: outputRows,
|
|
32199
|
+
rowCount: outputRows.length
|
|
31525
32200
|
});
|
|
31526
32201
|
}
|
|
31527
32202
|
let outputFiles;
|
|
@@ -31563,6 +32238,10 @@ async function maskDatabase(config, options) {
|
|
|
31563
32238
|
}
|
|
31564
32239
|
const dbName = extractDatabaseName2(config.database.connectionString);
|
|
31565
32240
|
const auditLog = buildAuditLog(detectionsByTable, maskResults, mode, seed, dbName);
|
|
32241
|
+
let tokenMap;
|
|
32242
|
+
if (useTokenization && allTokenEntries.length > 0) {
|
|
32243
|
+
tokenMap = buildTokenMap(allTokenEntries, tokenPrefix);
|
|
32244
|
+
}
|
|
31566
32245
|
const durationMs = Math.round(performance.now() - start);
|
|
31567
32246
|
return {
|
|
31568
32247
|
schema,
|
|
@@ -31571,7 +32250,8 @@ async function maskDatabase(config, options) {
|
|
|
31571
32250
|
dryRun,
|
|
31572
32251
|
tablesProcessed: maskResults.length,
|
|
31573
32252
|
totalRowsMasked: auditLog.summary.totalRowsMasked,
|
|
31574
|
-
outputFiles
|
|
32253
|
+
outputFiles,
|
|
32254
|
+
tokenMap
|
|
31575
32255
|
};
|
|
31576
32256
|
} finally {
|
|
31577
32257
|
await closeConnection(pool);
|
|
@@ -31777,9 +32457,9 @@ var DEFAULT_CONNECTIONS = {
|
|
|
31777
32457
|
};
|
|
31778
32458
|
var SUPPORTED_CLIENTS = ["postgres", "mysql"];
|
|
31779
32459
|
function ask(rl, prompt) {
|
|
31780
|
-
return new Promise((
|
|
32460
|
+
return new Promise((resolve13) => {
|
|
31781
32461
|
rl.question(prompt, (answer) => {
|
|
31782
|
-
|
|
32462
|
+
resolve13(answer);
|
|
31783
32463
|
});
|
|
31784
32464
|
});
|
|
31785
32465
|
}
|
|
@@ -32059,7 +32739,7 @@ async function loadConfig(filePath) {
|
|
|
32059
32739
|
} else {
|
|
32060
32740
|
const found = await findConfigFile(".");
|
|
32061
32741
|
if (!found) {
|
|
32062
|
-
throw new Error(`
|
|
32742
|
+
throw new Error(`Config file not found.
|
|
32063
32743
|
Create a realitydb.config.json or specify a path with --config.`);
|
|
32064
32744
|
}
|
|
32065
32745
|
resolvedPath = found;
|
|
@@ -32068,23 +32748,23 @@ Create a realitydb.config.json or specify a path with --config.`);
|
|
|
32068
32748
|
try {
|
|
32069
32749
|
raw = await (0, import_promises9.readFile)(resolvedPath, "utf-8");
|
|
32070
32750
|
} catch {
|
|
32071
|
-
throw new Error(`
|
|
32751
|
+
throw new Error(`Config file not found: ${resolvedPath}
|
|
32072
32752
|
Create a realitydb.config.json or specify a path with --config.`);
|
|
32073
32753
|
}
|
|
32074
32754
|
let parsed;
|
|
32075
32755
|
try {
|
|
32076
32756
|
parsed = JSON.parse(raw);
|
|
32077
32757
|
} catch {
|
|
32078
|
-
throw new Error(`
|
|
32758
|
+
throw new Error(`Invalid JSON in config file: ${resolvedPath}`);
|
|
32079
32759
|
}
|
|
32080
32760
|
const config = parsed;
|
|
32081
32761
|
const database = config["database"];
|
|
32082
32762
|
if (!database || typeof database["connectionString"] !== "string") {
|
|
32083
|
-
throw new Error("
|
|
32763
|
+
throw new Error("Config validation failed: database.connectionString is required.");
|
|
32084
32764
|
}
|
|
32085
32765
|
const client = database["client"] ?? "postgres";
|
|
32086
32766
|
if (client !== "postgres" && client !== "mysql") {
|
|
32087
|
-
throw new Error(`
|
|
32767
|
+
throw new Error(`Config validation failed: database.client must be 'postgres' or 'mysql', got '${client}'.`);
|
|
32088
32768
|
}
|
|
32089
32769
|
return {
|
|
32090
32770
|
database: {
|
|
@@ -32196,6 +32876,7 @@ async function scanCommand(options) {
|
|
|
32196
32876
|
|
|
32197
32877
|
// src/commands/seed.ts
|
|
32198
32878
|
var import_node_path10 = require("path");
|
|
32879
|
+
var import_node_fs8 = require("fs");
|
|
32199
32880
|
|
|
32200
32881
|
// src/resolveTemplate.ts
|
|
32201
32882
|
function resolveTemplate(nameOrPath) {
|
|
@@ -32232,7 +32913,26 @@ async function seedCommand(options) {
|
|
|
32232
32913
|
const records = options.records ? parseInt(options.records, 10) : void 0;
|
|
32233
32914
|
const seed = options.seed ? parseInt(options.seed, 10) : void 0;
|
|
32234
32915
|
const rawTemplateName = options.template ?? config.template;
|
|
32235
|
-
|
|
32916
|
+
let templateName = rawTemplateName && (rawTemplateName.includes("/") || rawTemplateName.includes("\\") || rawTemplateName.endsWith(".json")) ? (0, import_node_path10.resolve)(rawTemplateName) : rawTemplateName;
|
|
32917
|
+
if (options.autoTemplate && !templateName) {
|
|
32918
|
+
if (!options.ci) {
|
|
32919
|
+
console.log("");
|
|
32920
|
+
console.log("Running auto-template analysis...");
|
|
32921
|
+
}
|
|
32922
|
+
const analyzeResult = await analyzeDatabase(config, {
|
|
32923
|
+
sampleSize: 100,
|
|
32924
|
+
autoTemplate: true,
|
|
32925
|
+
safeMode: true
|
|
32926
|
+
});
|
|
32927
|
+
if (analyzeResult.templateJson) {
|
|
32928
|
+
const autoPath = (0, import_node_path10.resolve)(".realitydb-auto-template.json");
|
|
32929
|
+
(0, import_node_fs8.writeFileSync)(autoPath, analyzeResult.templateJson + "\n", "utf-8");
|
|
32930
|
+
templateName = autoPath;
|
|
32931
|
+
if (!options.ci) {
|
|
32932
|
+
console.log(`Auto-template generated: ${autoPath}`);
|
|
32933
|
+
}
|
|
32934
|
+
}
|
|
32935
|
+
}
|
|
32236
32936
|
const timeline = options.timeline;
|
|
32237
32937
|
const scenario = options.scenario;
|
|
32238
32938
|
const scenarioIntensity = options.scenarioIntensity ?? "medium";
|
|
@@ -32656,7 +33356,7 @@ async function exportCommand(options) {
|
|
|
32656
33356
|
}
|
|
32657
33357
|
|
|
32658
33358
|
// src/commands/templates.ts
|
|
32659
|
-
var
|
|
33359
|
+
var import_node_fs9 = require("fs");
|
|
32660
33360
|
var VERSION6 = "0.10.0";
|
|
32661
33361
|
function templatesCommand() {
|
|
32662
33362
|
const registry = getDefaultRegistry();
|
|
@@ -32683,7 +33383,7 @@ function templatesCommand() {
|
|
|
32683
33383
|
}
|
|
32684
33384
|
function templatesInitCommand() {
|
|
32685
33385
|
const fileName = "realitydb.template.json";
|
|
32686
|
-
if ((0,
|
|
33386
|
+
if ((0, import_node_fs9.existsSync)(fileName)) {
|
|
32687
33387
|
console.error(`[realitydb] ${fileName} already exists in this directory.`);
|
|
32688
33388
|
process.exit(1);
|
|
32689
33389
|
}
|
|
@@ -32717,7 +33417,7 @@ function templatesInitCommand() {
|
|
|
32717
33417
|
}
|
|
32718
33418
|
}
|
|
32719
33419
|
};
|
|
32720
|
-
(0,
|
|
33420
|
+
(0, import_node_fs9.writeFileSync)(fileName, JSON.stringify(scaffold, null, 2) + "\n");
|
|
32721
33421
|
console.log("");
|
|
32722
33422
|
console.log(`Created ${fileName}`);
|
|
32723
33423
|
console.log("Edit this file to define your custom template.");
|
|
@@ -32728,7 +33428,7 @@ function templatesInitCommand() {
|
|
|
32728
33428
|
}
|
|
32729
33429
|
function templatesValidateCommand(filePath, options) {
|
|
32730
33430
|
const start = performance.now();
|
|
32731
|
-
if (!(0,
|
|
33431
|
+
if (!(0, import_node_fs9.existsSync)(filePath)) {
|
|
32732
33432
|
if (options.ci) {
|
|
32733
33433
|
console.log(formatCIOutput({
|
|
32734
33434
|
success: false,
|
|
@@ -32745,7 +33445,7 @@ function templatesValidateCommand(filePath, options) {
|
|
|
32745
33445
|
}
|
|
32746
33446
|
let json;
|
|
32747
33447
|
try {
|
|
32748
|
-
const raw = (0,
|
|
33448
|
+
const raw = (0, import_node_fs9.readFileSync)(filePath, "utf-8");
|
|
32749
33449
|
json = JSON.parse(raw);
|
|
32750
33450
|
} catch {
|
|
32751
33451
|
if (options.ci) {
|
|
@@ -32808,7 +33508,7 @@ function templatesValidateCommand(filePath, options) {
|
|
|
32808
33508
|
}
|
|
32809
33509
|
|
|
32810
33510
|
// src/commands/scenarios.ts
|
|
32811
|
-
var
|
|
33511
|
+
var import_node_fs10 = require("fs");
|
|
32812
33512
|
var import_node_path12 = require("path");
|
|
32813
33513
|
function scenariosCommand() {
|
|
32814
33514
|
const registry = getDefaultScenarioRegistry();
|
|
@@ -32833,12 +33533,12 @@ function scenariosCreateCommand(name) {
|
|
|
32833
33533
|
const sanitized = name.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
32834
33534
|
const fileName = `${sanitized}.scenario.json`;
|
|
32835
33535
|
const filePath = (0, import_node_path12.resolve)(fileName);
|
|
32836
|
-
if ((0,
|
|
33536
|
+
if ((0, import_node_fs10.existsSync)(filePath)) {
|
|
32837
33537
|
console.error(`[realitydb] File already exists: ${fileName}`);
|
|
32838
33538
|
process.exit(1);
|
|
32839
33539
|
}
|
|
32840
33540
|
const scaffold = scaffoldCustomScenario(sanitized);
|
|
32841
|
-
(0,
|
|
33541
|
+
(0, import_node_fs10.writeFileSync)(filePath, JSON.stringify(scaffold, null, 2) + "\n", "utf-8");
|
|
32842
33542
|
console.log("");
|
|
32843
33543
|
console.log(`Created custom scenario: ${fileName}`);
|
|
32844
33544
|
console.log("");
|
|
@@ -33593,7 +34293,7 @@ function getDefaultSchema() {
|
|
|
33593
34293
|
}
|
|
33594
34294
|
|
|
33595
34295
|
// src/commands/analyze.ts
|
|
33596
|
-
var
|
|
34296
|
+
var import_node_fs11 = require("fs");
|
|
33597
34297
|
var import_node_path16 = require("path");
|
|
33598
34298
|
var VERSION12 = "1.3.1";
|
|
33599
34299
|
async function analyzeCommand(options) {
|
|
@@ -33602,21 +34302,26 @@ async function analyzeCommand(options) {
|
|
|
33602
34302
|
const config = await loadConfig(options.configPath);
|
|
33603
34303
|
const sampleSize = options.sampleSize ? parseInt(options.sampleSize, 10) : 1e3;
|
|
33604
34304
|
const masked = maskConnectionString(config.database.connectionString);
|
|
34305
|
+
const safeMode = !options.unsafeAnalyze;
|
|
34306
|
+
const autoTemplate = options.autoTemplate ?? false;
|
|
34307
|
+
const outputPath = options.output ?? (autoTemplate ? "./realitydb-template.json" : void 0);
|
|
33605
34308
|
if (!options.ci) {
|
|
33606
34309
|
console.log("");
|
|
33607
|
-
console.log("RealityDB
|
|
34310
|
+
console.log("RealityDB Schema Analysis");
|
|
33608
34311
|
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
34312
|
console.log(`Database: ${masked}`);
|
|
33610
34313
|
console.log(`Sample size: ${sampleSize} rows per table`);
|
|
33611
|
-
if (
|
|
33612
|
-
console.log(`Output: ${
|
|
34314
|
+
if (outputPath) {
|
|
34315
|
+
console.log(`Output: ${outputPath}`);
|
|
33613
34316
|
}
|
|
33614
34317
|
console.log("");
|
|
33615
34318
|
console.log("Analyzing schema...");
|
|
33616
34319
|
}
|
|
33617
34320
|
const result = await analyzeDatabase(config, {
|
|
33618
34321
|
sampleSize,
|
|
33619
|
-
output:
|
|
34322
|
+
output: outputPath,
|
|
34323
|
+
safeMode,
|
|
34324
|
+
autoTemplate
|
|
33620
34325
|
});
|
|
33621
34326
|
const durationMs = Math.round(performance.now() - start);
|
|
33622
34327
|
if (options.ci) {
|
|
@@ -33629,20 +34334,46 @@ async function analyzeCommand(options) {
|
|
|
33629
34334
|
durationMs,
|
|
33630
34335
|
data: {
|
|
33631
34336
|
...ciData,
|
|
33632
|
-
|
|
33633
|
-
|
|
34337
|
+
confidenceBreakdown: result.confidenceBreakdown,
|
|
34338
|
+
sanitizationReport: result.sanitizationReport ?? null,
|
|
34339
|
+
templateGenerated: !!outputPath,
|
|
34340
|
+
templateFile: outputPath ?? null
|
|
33634
34341
|
}
|
|
33635
34342
|
}));
|
|
33636
|
-
if (result.templateJson &&
|
|
33637
|
-
const filePath = (0, import_node_path16.resolve)(
|
|
33638
|
-
(0,
|
|
34343
|
+
if (result.templateJson && outputPath) {
|
|
34344
|
+
const filePath = (0, import_node_path16.resolve)(outputPath);
|
|
34345
|
+
(0, import_node_fs11.writeFileSync)(filePath, result.templateJson + "\n", "utf-8");
|
|
33639
34346
|
}
|
|
33640
34347
|
return;
|
|
33641
34348
|
}
|
|
33642
34349
|
console.log(formatAnalysisReport(result.report));
|
|
33643
|
-
|
|
33644
|
-
|
|
33645
|
-
|
|
34350
|
+
const total = result.confidenceBreakdown.high + result.confidenceBreakdown.medium + result.confidenceBreakdown.low;
|
|
34351
|
+
if (total > 0) {
|
|
34352
|
+
console.log("Analysis Summary:");
|
|
34353
|
+
const pct = (n) => total > 0 ? Math.round(n / total * 100) : 0;
|
|
34354
|
+
console.log(` High confidence: ${result.confidenceBreakdown.high} columns (${pct(result.confidenceBreakdown.high)}%)`);
|
|
34355
|
+
console.log(` Medium confidence: ${result.confidenceBreakdown.medium} columns (${pct(result.confidenceBreakdown.medium)}%)`);
|
|
34356
|
+
console.log(` Low confidence: ${result.confidenceBreakdown.low} columns (${pct(result.confidenceBreakdown.low)}%)`);
|
|
34357
|
+
console.log("");
|
|
34358
|
+
}
|
|
34359
|
+
if (safeMode && result.sanitizationReport) {
|
|
34360
|
+
const sr = result.sanitizationReport;
|
|
34361
|
+
console.log("PII Sanitization: ENABLED");
|
|
34362
|
+
console.log(` Values scanned: ${sr.totalScanned.toLocaleString()}`);
|
|
34363
|
+
console.log(` PII detected: ${sr.totalDetections} instances`);
|
|
34364
|
+
if (Object.keys(sr.byCategory).length > 0) {
|
|
34365
|
+
const cats = Object.entries(sr.byCategory).map(([k, v]) => `${k} (${v})`).join(", ");
|
|
34366
|
+
console.log(` Categories: ${cats}`);
|
|
34367
|
+
}
|
|
34368
|
+
console.log("");
|
|
34369
|
+
} else if (!safeMode) {
|
|
34370
|
+
console.log("PII Sanitization: DISABLED (--unsafe-analyze)");
|
|
34371
|
+
console.log(" Warning: Sample values were NOT sanitized. Do not use on production databases.");
|
|
34372
|
+
console.log("");
|
|
34373
|
+
}
|
|
34374
|
+
if (result.templateJson && outputPath) {
|
|
34375
|
+
const filePath = (0, import_node_path16.resolve)(outputPath);
|
|
34376
|
+
(0, import_node_fs11.writeFileSync)(filePath, result.templateJson + "\n", "utf-8");
|
|
33646
34377
|
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
34378
|
console.log(`Template generated: ${filePath}`);
|
|
33648
34379
|
console.log("");
|
|
@@ -33651,10 +34382,11 @@ async function analyzeCommand(options) {
|
|
|
33651
34382
|
console.log("");
|
|
33652
34383
|
console.log("Validate it with:");
|
|
33653
34384
|
console.log(` realitydb templates validate "${filePath}"`);
|
|
33654
|
-
} else if (!
|
|
34385
|
+
} else if (!outputPath) {
|
|
33655
34386
|
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
34387
|
console.log("To generate a template from this analysis:");
|
|
33657
34388
|
console.log(" realitydb analyze --output my-template.json");
|
|
34389
|
+
console.log(" realitydb analyze --auto-template");
|
|
33658
34390
|
}
|
|
33659
34391
|
const totalTime = (durationMs / 1e3).toFixed(1);
|
|
33660
34392
|
console.log("");
|
|
@@ -33687,9 +34419,32 @@ async function analyzeCommand(options) {
|
|
|
33687
34419
|
}
|
|
33688
34420
|
|
|
33689
34421
|
// src/commands/mask.ts
|
|
33690
|
-
var
|
|
34422
|
+
var import_node_fs12 = require("fs");
|
|
33691
34423
|
var import_node_path17 = require("path");
|
|
34424
|
+
var import_node_readline2 = require("readline");
|
|
34425
|
+
var import_node_process2 = require("process");
|
|
33692
34426
|
var VERSION13 = "1.3.1";
|
|
34427
|
+
function askPassphrase(prompt) {
|
|
34428
|
+
return new Promise((resolvePromise) => {
|
|
34429
|
+
const rl = (0, import_node_readline2.createInterface)({ input: import_node_process2.stdin, output: import_node_process2.stdout });
|
|
34430
|
+
const originalWrite = import_node_process2.stdout.write.bind(import_node_process2.stdout);
|
|
34431
|
+
let muted = false;
|
|
34432
|
+
import_node_process2.stdout.write = ((chunk, ...args) => {
|
|
34433
|
+
if (muted && typeof chunk === "string" && !chunk.includes(prompt)) {
|
|
34434
|
+
return originalWrite("*");
|
|
34435
|
+
}
|
|
34436
|
+
return originalWrite(chunk, ...args);
|
|
34437
|
+
});
|
|
34438
|
+
rl.question(prompt, (answer) => {
|
|
34439
|
+
muted = false;
|
|
34440
|
+
import_node_process2.stdout.write = originalWrite;
|
|
34441
|
+
console.log("");
|
|
34442
|
+
rl.close();
|
|
34443
|
+
resolvePromise(answer);
|
|
34444
|
+
});
|
|
34445
|
+
muted = true;
|
|
34446
|
+
});
|
|
34447
|
+
}
|
|
33693
34448
|
async function maskCommand(options) {
|
|
33694
34449
|
const start = performance.now();
|
|
33695
34450
|
try {
|
|
@@ -33747,6 +34502,12 @@ async function maskCommand(options) {
|
|
|
33747
34502
|
} else {
|
|
33748
34503
|
console.log("Mode: write to database (--confirm)");
|
|
33749
34504
|
}
|
|
34505
|
+
if (options.tokenize) {
|
|
34506
|
+
console.log("Masking: tokenization (reversible)");
|
|
34507
|
+
if (options.tokenMap) {
|
|
34508
|
+
console.log(`Token map: ${options.tokenMap} (AES-256-GCM encrypted)`);
|
|
34509
|
+
}
|
|
34510
|
+
}
|
|
33750
34511
|
if (options.auditLog) {
|
|
33751
34512
|
console.log(`Audit log: ${options.auditLog}`);
|
|
33752
34513
|
}
|
|
@@ -33760,12 +34521,30 @@ async function maskCommand(options) {
|
|
|
33760
34521
|
output: options.output,
|
|
33761
34522
|
outputFormat,
|
|
33762
34523
|
auditLog: options.auditLog,
|
|
33763
|
-
confirm: options.confirm
|
|
34524
|
+
confirm: options.confirm,
|
|
34525
|
+
tokenize: options.tokenize,
|
|
34526
|
+
deepScan: options.deepScan
|
|
33764
34527
|
});
|
|
33765
34528
|
const durationMs = Math.round(performance.now() - start);
|
|
33766
34529
|
if (options.auditLog) {
|
|
33767
34530
|
const auditPath = (0, import_node_path17.resolve)(options.auditLog);
|
|
33768
|
-
(0,
|
|
34531
|
+
(0, import_node_fs12.writeFileSync)(auditPath, serializeAuditLog(result.auditLog) + "\n", "utf-8");
|
|
34532
|
+
}
|
|
34533
|
+
if (options.tokenMap && result.tokenMap) {
|
|
34534
|
+
const passphrase = await askPassphrase("Enter passphrase for token map encryption: ");
|
|
34535
|
+
if (!passphrase || passphrase.length === 0) {
|
|
34536
|
+
console.error("[realitydb] Passphrase cannot be empty. Token map not written.");
|
|
34537
|
+
} else {
|
|
34538
|
+
const encryptedData = encryptTokenMap(result.tokenMap, passphrase);
|
|
34539
|
+
const tokenMapPath = (0, import_node_path17.resolve)(options.tokenMap);
|
|
34540
|
+
(0, import_node_fs12.writeFileSync)(tokenMapPath, encryptedData + "\n", "utf-8");
|
|
34541
|
+
if (!options.ci) {
|
|
34542
|
+
console.log(`Token map exported (AES-256-GCM encrypted) \u2192 ${tokenMapPath}`);
|
|
34543
|
+
console.log(` ${result.tokenMap.totalTokens} unique tokens generated`);
|
|
34544
|
+
console.log(" Store the passphrase securely \u2014 it cannot be recovered");
|
|
34545
|
+
console.log("");
|
|
34546
|
+
}
|
|
34547
|
+
}
|
|
33769
34548
|
}
|
|
33770
34549
|
if (options.ci) {
|
|
33771
34550
|
console.log(formatCIOutput({
|
|
@@ -33839,9 +34618,155 @@ async function maskCommand(options) {
|
|
|
33839
34618
|
}
|
|
33840
34619
|
}
|
|
33841
34620
|
|
|
33842
|
-
// src/commands/
|
|
33843
|
-
var
|
|
34621
|
+
// src/commands/audit.ts
|
|
34622
|
+
var import_node_fs13 = require("fs");
|
|
33844
34623
|
var import_node_path18 = require("path");
|
|
34624
|
+
var import_node_readline3 = require("readline");
|
|
34625
|
+
var import_node_process3 = require("process");
|
|
34626
|
+
function askPassphrase2(prompt) {
|
|
34627
|
+
return new Promise((resolvePromise) => {
|
|
34628
|
+
const rl = (0, import_node_readline3.createInterface)({ input: import_node_process3.stdin, output: import_node_process3.stdout });
|
|
34629
|
+
const originalWrite = import_node_process3.stdout.write.bind(import_node_process3.stdout);
|
|
34630
|
+
let muted = false;
|
|
34631
|
+
import_node_process3.stdout.write = ((chunk, ...args) => {
|
|
34632
|
+
if (muted && typeof chunk === "string" && !chunk.includes(prompt)) {
|
|
34633
|
+
return originalWrite("*");
|
|
34634
|
+
}
|
|
34635
|
+
return originalWrite(chunk, ...args);
|
|
34636
|
+
});
|
|
34637
|
+
rl.question(prompt, (answer) => {
|
|
34638
|
+
muted = false;
|
|
34639
|
+
import_node_process3.stdout.write = originalWrite;
|
|
34640
|
+
console.log("");
|
|
34641
|
+
rl.close();
|
|
34642
|
+
resolvePromise(answer);
|
|
34643
|
+
});
|
|
34644
|
+
muted = true;
|
|
34645
|
+
});
|
|
34646
|
+
}
|
|
34647
|
+
async function auditVerifyCommand(logFile, options) {
|
|
34648
|
+
try {
|
|
34649
|
+
const filePath = (0, import_node_path18.resolve)(logFile);
|
|
34650
|
+
const raw = (0, import_node_fs13.readFileSync)(filePath, "utf-8");
|
|
34651
|
+
const log = JSON.parse(raw);
|
|
34652
|
+
const result = verifyAuditLogIntegrity(log);
|
|
34653
|
+
const entryCount = log.tables?.length ?? 0;
|
|
34654
|
+
if (options.ci) {
|
|
34655
|
+
console.log(JSON.stringify({
|
|
34656
|
+
file: filePath,
|
|
34657
|
+
entries: entryCount,
|
|
34658
|
+
valid: result.valid,
|
|
34659
|
+
brokenAt: result.brokenAt ?? null
|
|
34660
|
+
}));
|
|
34661
|
+
process.exit(result.valid ? 0 : 1);
|
|
34662
|
+
}
|
|
34663
|
+
console.log("");
|
|
34664
|
+
console.log("Audit Chain Verification");
|
|
34665
|
+
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");
|
|
34666
|
+
console.log(`File: ${logFile}`);
|
|
34667
|
+
console.log(`Entries: ${entryCount}`);
|
|
34668
|
+
if (result.valid) {
|
|
34669
|
+
console.log("Chain integrity: VERIFIED (all hashes valid)");
|
|
34670
|
+
} else {
|
|
34671
|
+
console.log(`Chain integrity: BROKEN at table "${result.brokenAt}"`);
|
|
34672
|
+
console.log("The audit log has been tampered with.");
|
|
34673
|
+
}
|
|
34674
|
+
console.log("");
|
|
34675
|
+
process.exit(result.valid ? 0 : 1);
|
|
34676
|
+
} catch (err) {
|
|
34677
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
34678
|
+
console.error(`[realitydb] Audit verify failed: ${message}`);
|
|
34679
|
+
process.exit(1);
|
|
34680
|
+
}
|
|
34681
|
+
}
|
|
34682
|
+
async function auditSummaryCommand(logFile, options) {
|
|
34683
|
+
try {
|
|
34684
|
+
const filePath = (0, import_node_path18.resolve)(logFile);
|
|
34685
|
+
const raw = (0, import_node_fs13.readFileSync)(filePath, "utf-8");
|
|
34686
|
+
const log = JSON.parse(raw);
|
|
34687
|
+
if (options.ci) {
|
|
34688
|
+
console.log(JSON.stringify({
|
|
34689
|
+
file: filePath,
|
|
34690
|
+
complianceMode: log.complianceMode,
|
|
34691
|
+
summary: log.summary,
|
|
34692
|
+
tables: log.tables.map((t) => ({
|
|
34693
|
+
tableName: t.tableName,
|
|
34694
|
+
rowCount: t.rowCount,
|
|
34695
|
+
piiColumnsDetected: t.piiColumnsDetected,
|
|
34696
|
+
columnsMasked: t.columnsMasked
|
|
34697
|
+
}))
|
|
34698
|
+
}));
|
|
34699
|
+
return;
|
|
34700
|
+
}
|
|
34701
|
+
console.log("");
|
|
34702
|
+
console.log(formatAuditLog(log));
|
|
34703
|
+
} catch (err) {
|
|
34704
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
34705
|
+
console.error(`[realitydb] Audit summary failed: ${message}`);
|
|
34706
|
+
process.exit(1);
|
|
34707
|
+
}
|
|
34708
|
+
}
|
|
34709
|
+
async function auditReIdentifyCommand(options) {
|
|
34710
|
+
try {
|
|
34711
|
+
if (!options.tokenMap) {
|
|
34712
|
+
console.error("[realitydb] --token-map <file> is required for re-identify");
|
|
34713
|
+
process.exit(1);
|
|
34714
|
+
}
|
|
34715
|
+
const filePath = (0, import_node_path18.resolve)(options.tokenMap);
|
|
34716
|
+
const encryptedData = (0, import_node_fs13.readFileSync)(filePath, "utf-8").trim();
|
|
34717
|
+
const passphrase = await askPassphrase2("Enter passphrase to decrypt token map: ");
|
|
34718
|
+
if (!passphrase) {
|
|
34719
|
+
console.error("[realitydb] Passphrase cannot be empty.");
|
|
34720
|
+
process.exit(1);
|
|
34721
|
+
}
|
|
34722
|
+
const tokenMap = decryptTokenMap(encryptedData, passphrase);
|
|
34723
|
+
if (options.ci) {
|
|
34724
|
+
console.log(JSON.stringify({
|
|
34725
|
+
totalTokens: tokenMap.totalTokens,
|
|
34726
|
+
tokenPrefix: tokenMap.tokenPrefix,
|
|
34727
|
+
createdAt: tokenMap.createdAt,
|
|
34728
|
+
entries: tokenMap.entries.length
|
|
34729
|
+
}));
|
|
34730
|
+
return;
|
|
34731
|
+
}
|
|
34732
|
+
console.log("");
|
|
34733
|
+
console.log("Token Map Re-Identification");
|
|
34734
|
+
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");
|
|
34735
|
+
console.log(`Token prefix: ${tokenMap.tokenPrefix}`);
|
|
34736
|
+
console.log(`Created: ${tokenMap.createdAt}`);
|
|
34737
|
+
console.log("");
|
|
34738
|
+
const byTable = /* @__PURE__ */ new Map();
|
|
34739
|
+
for (const entry of tokenMap.entries) {
|
|
34740
|
+
const existing = byTable.get(entry.tableName) ?? [];
|
|
34741
|
+
existing.push(entry);
|
|
34742
|
+
byTable.set(entry.tableName, existing);
|
|
34743
|
+
}
|
|
34744
|
+
for (const [tableName, entries] of byTable) {
|
|
34745
|
+
console.log(`Table: ${tableName}`);
|
|
34746
|
+
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");
|
|
34747
|
+
for (const entry of entries) {
|
|
34748
|
+
console.log(` ${entry.token} \u2192 ${String(entry.originalValue)} (${entry.columnName}, ${entry.piiCategory})`);
|
|
34749
|
+
}
|
|
34750
|
+
console.log("");
|
|
34751
|
+
}
|
|
34752
|
+
console.log(`Restored ${tokenMap.totalTokens} token mappings`);
|
|
34753
|
+
console.log("");
|
|
34754
|
+
console.log("These are real PII values. Handle according to your data policy.");
|
|
34755
|
+
console.log("");
|
|
34756
|
+
} catch (err) {
|
|
34757
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
34758
|
+
if (message.includes("Decryption failed")) {
|
|
34759
|
+
console.error(message);
|
|
34760
|
+
} else {
|
|
34761
|
+
console.error(`[realitydb] Re-identify failed: ${message}`);
|
|
34762
|
+
}
|
|
34763
|
+
process.exit(1);
|
|
34764
|
+
}
|
|
34765
|
+
}
|
|
34766
|
+
|
|
34767
|
+
// src/commands/classroom.ts
|
|
34768
|
+
var import_node_fs14 = require("fs");
|
|
34769
|
+
var import_node_path19 = require("path");
|
|
33845
34770
|
var VERSION14 = "1.3.1";
|
|
33846
34771
|
async function classroomListCommand(options) {
|
|
33847
34772
|
const start = performance.now();
|
|
@@ -34104,8 +35029,8 @@ async function classroomCreateCommand(name, options) {
|
|
|
34104
35029
|
try {
|
|
34105
35030
|
const content = classroomCreate(name);
|
|
34106
35031
|
const filename = `${name}.course.json`;
|
|
34107
|
-
const filePath = (0,
|
|
34108
|
-
(0,
|
|
35032
|
+
const filePath = (0, import_node_path19.resolve)(filename);
|
|
35033
|
+
(0, import_node_fs14.writeFileSync)(filePath, content + "\n", "utf-8");
|
|
34109
35034
|
const durationMs = Math.round(performance.now() - start);
|
|
34110
35035
|
if (options.ci) {
|
|
34111
35036
|
console.log(formatCIOutput({
|
|
@@ -34355,22 +35280,22 @@ async function simulateWebhooksCommand(options) {
|
|
|
34355
35280
|
}
|
|
34356
35281
|
|
|
34357
35282
|
// src/cli.ts
|
|
34358
|
-
var VERSION16 = "1.
|
|
35283
|
+
var VERSION16 = "1.8.0";
|
|
34359
35284
|
function run(argv) {
|
|
34360
35285
|
const program2 = new Command();
|
|
34361
|
-
program2.name("realitydb").description("RealityDB \xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\
|
|
34362
|
-
program2.command("init").description("Interactive setup wizard \xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\
|
|
35286
|
+
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);
|
|
35287
|
+
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
35288
|
await initCommand();
|
|
34364
35289
|
});
|
|
34365
35290
|
program2.command("scan").description("Scan database schema").action(async () => {
|
|
34366
35291
|
const opts = program2.opts();
|
|
34367
35292
|
await scanCommand({ ci: opts.ci, configPath: opts.config });
|
|
34368
35293
|
});
|
|
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) => {
|
|
35294
|
+
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
35295
|
const opts = program2.opts();
|
|
34371
35296
|
await analyzeCommand({ ...cmdOpts, ci: opts.ci, configPath: opts.config });
|
|
34372
35297
|
});
|
|
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) => {
|
|
35298
|
+
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
35299
|
const opts = program2.opts();
|
|
34375
35300
|
await seedCommand({ ...cmdOpts, ci: opts.ci, configPath: opts.config });
|
|
34376
35301
|
});
|
|
@@ -34398,10 +35323,23 @@ function run(argv) {
|
|
|
34398
35323
|
scenarios.command("create <name>").description("Scaffold a custom scenario JSON file").action((name) => {
|
|
34399
35324
|
scenariosCreateCommand(name);
|
|
34400
35325
|
});
|
|
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) => {
|
|
35326
|
+
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
35327
|
const opts = program2.opts();
|
|
34403
35328
|
await maskCommand({ ...cmdOpts, ci: opts.ci, configPath: opts.config });
|
|
34404
35329
|
});
|
|
35330
|
+
const audit = program2.command("audit").description("Audit log operations");
|
|
35331
|
+
audit.command("verify <log-file>").description("Verify audit log hash chain integrity").action(async (logFile) => {
|
|
35332
|
+
const opts = program2.opts();
|
|
35333
|
+
await auditVerifyCommand(logFile, { ci: opts.ci });
|
|
35334
|
+
});
|
|
35335
|
+
audit.command("summary <log-file>").description("Print formatted compliance summary from audit log").action(async (logFile) => {
|
|
35336
|
+
const opts = program2.opts();
|
|
35337
|
+
await auditSummaryCommand(logFile, { ci: opts.ci });
|
|
35338
|
+
});
|
|
35339
|
+
audit.command("re-identify").description("Decrypt token map and display original PII mappings").requiredOption("--token-map <file>", "Encrypted token map file").action(async (cmdOpts) => {
|
|
35340
|
+
const opts = program2.opts();
|
|
35341
|
+
await auditReIdentifyCommand({ ...cmdOpts, ci: opts.ci });
|
|
35342
|
+
});
|
|
34405
35343
|
const classroom = program2.command("classroom").description("Education and classroom mode");
|
|
34406
35344
|
classroom.command("list", { isDefault: true }).description("List available courses").action(async () => {
|
|
34407
35345
|
const opts = program2.opts();
|
|
@@ -34472,7 +35410,7 @@ function run(argv) {
|
|
|
34472
35410
|
console.log(JSON.stringify({ name: "realitydb", version: VERSION16 }));
|
|
34473
35411
|
} else {
|
|
34474
35412
|
console.log("");
|
|
34475
|
-
console.log(`RealityDB v${VERSION16} \xC3\u0192\xC6\u2019\xC3\u201A\xC2\xA2\xC3\u0192\xC2\xA2\xC3\xA2\xE2\
|
|
35413
|
+
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
35414
|
console.log("Run `realitydb --help` for available commands.");
|
|
34477
35415
|
console.log("");
|
|
34478
35416
|
}
|