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