realitydb 1.9.0 → 2.0.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 +207 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31672,15 +31672,92 @@ async function captureDatabase(config, options) {
|
|
|
31672
31672
|
foreignKeyCount: filteredFKs.length
|
|
31673
31673
|
};
|
|
31674
31674
|
const ddl = generateCreateTableDDL(filteredSchema);
|
|
31675
|
+
let aroundFilter;
|
|
31676
|
+
if (options.around) {
|
|
31677
|
+
aroundFilter = /* @__PURE__ */ new Map();
|
|
31678
|
+
const { column: aroundCol, value: aroundVal } = options.around;
|
|
31679
|
+
for (const table of filteredTables) {
|
|
31680
|
+
const hasCol = table.columns.some((c) => c.name === aroundCol);
|
|
31681
|
+
if (hasCol) {
|
|
31682
|
+
aroundFilter.set(table.name, { column: aroundCol, values: /* @__PURE__ */ new Set([aroundVal]) });
|
|
31683
|
+
}
|
|
31684
|
+
}
|
|
31685
|
+
for (const fk of filteredFKs) {
|
|
31686
|
+
const sourceFilter = aroundFilter.get(fk.targetTable);
|
|
31687
|
+
if (sourceFilter && sourceFilter.column === fk.targetColumn) {
|
|
31688
|
+
aroundFilter.set(fk.sourceTable, { column: fk.sourceColumn, values: sourceFilter.values });
|
|
31689
|
+
}
|
|
31690
|
+
}
|
|
31691
|
+
}
|
|
31692
|
+
const detectionsByTable = /* @__PURE__ */ new Map();
|
|
31693
|
+
let piiSummary;
|
|
31694
|
+
if (options.safe) {
|
|
31695
|
+
const mode = "gdpr";
|
|
31696
|
+
for (const table of filteredTables) {
|
|
31697
|
+
const tableForeignKeys = filteredFKs.filter((fk) => fk.sourceTable === table.name);
|
|
31698
|
+
const detections = detectTablePII(table.columns, tableForeignKeys, table.name, mode);
|
|
31699
|
+
detectionsByTable.set(table.name, detections);
|
|
31700
|
+
}
|
|
31701
|
+
let columnsDetected = 0;
|
|
31702
|
+
let tablesAffected = 0;
|
|
31703
|
+
const categoriesFound = /* @__PURE__ */ new Set();
|
|
31704
|
+
for (const [, detections] of detectionsByTable) {
|
|
31705
|
+
const piiCols = detections.filter((d) => d.shouldMask);
|
|
31706
|
+
if (piiCols.length > 0) {
|
|
31707
|
+
tablesAffected++;
|
|
31708
|
+
columnsDetected += piiCols.length;
|
|
31709
|
+
for (const d of piiCols) {
|
|
31710
|
+
categoriesFound.add(d.category);
|
|
31711
|
+
}
|
|
31712
|
+
}
|
|
31713
|
+
}
|
|
31714
|
+
piiSummary = {
|
|
31715
|
+
columnsDetected,
|
|
31716
|
+
tablesAffected,
|
|
31717
|
+
categoriesFound: Array.from(categoriesFound)
|
|
31718
|
+
};
|
|
31719
|
+
}
|
|
31675
31720
|
const packDataset = { tables: {} };
|
|
31676
31721
|
const tableDetails = [];
|
|
31677
31722
|
let totalRows = 0;
|
|
31723
|
+
const safeMode = options.safeMode ?? "mask";
|
|
31724
|
+
const random = options.safe ? createSeededRandom(42) : void 0;
|
|
31725
|
+
const tokenPrefix = options.safe && safeMode === "tokenize" ? generateTokenPrefix() : void 0;
|
|
31678
31726
|
for (const tableName of tablesToCapture) {
|
|
31679
31727
|
const tableSchema = filteredTables.find((t) => t.name === tableName);
|
|
31680
31728
|
if (!tableSchema)
|
|
31681
31729
|
continue;
|
|
31682
31730
|
const columns = tableSchema.columns.map((c) => c.name);
|
|
31683
|
-
|
|
31731
|
+
let rows = await readTableRows(pool, tableName, columns);
|
|
31732
|
+
const filter = aroundFilter?.get(tableName);
|
|
31733
|
+
if (filter) {
|
|
31734
|
+
rows = rows.filter((row) => {
|
|
31735
|
+
const val = row[filter.column];
|
|
31736
|
+
return val !== null && val !== void 0 && filter.values.has(String(val));
|
|
31737
|
+
});
|
|
31738
|
+
}
|
|
31739
|
+
if (options.maxRows !== void 0 && rows.length > options.maxRows) {
|
|
31740
|
+
rows = rows.slice(0, options.maxRows);
|
|
31741
|
+
}
|
|
31742
|
+
if (options.safe) {
|
|
31743
|
+
const detections = detectionsByTable.get(tableName);
|
|
31744
|
+
if (detections) {
|
|
31745
|
+
const hasPII = detections.some((d) => d.shouldMask);
|
|
31746
|
+
if (hasPII) {
|
|
31747
|
+
if (safeMode === "tokenize" && tokenPrefix) {
|
|
31748
|
+
const { tokenizedRows } = tokenizeTableRows(rows, detections, tableName, tokenPrefix);
|
|
31749
|
+
rows = tokenizedRows;
|
|
31750
|
+
} else if (safeMode === "redact") {
|
|
31751
|
+
const redactDetections = detections.map((d) => d.shouldMask ? { ...d, maskStrategy: "redact" } : d);
|
|
31752
|
+
const { maskedRows } = maskTableRows(rows, redactDetections, random, tableName);
|
|
31753
|
+
rows = maskedRows;
|
|
31754
|
+
} else {
|
|
31755
|
+
const { maskedRows } = maskTableRows(rows, detections, random, tableName);
|
|
31756
|
+
rows = maskedRows;
|
|
31757
|
+
}
|
|
31758
|
+
}
|
|
31759
|
+
}
|
|
31760
|
+
}
|
|
31684
31761
|
const rowCount = rows.length;
|
|
31685
31762
|
packDataset.tables[tableName] = {
|
|
31686
31763
|
columns,
|
|
@@ -31725,6 +31802,7 @@ async function captureDatabase(config, options) {
|
|
|
31725
31802
|
}
|
|
31726
31803
|
};
|
|
31727
31804
|
const masked = maskConnection(config.database.connectionString);
|
|
31805
|
+
const safeModeValue = options.safe ? safeMode === "tokenize" ? "tokenized" : safeMode === "redact" ? "redacted" : "masked" : "raw";
|
|
31728
31806
|
const pack = {
|
|
31729
31807
|
format: "realitydb-pack",
|
|
31730
31808
|
version: "1.0",
|
|
@@ -31736,7 +31814,9 @@ async function captureDatabase(config, options) {
|
|
|
31736
31814
|
totalRows,
|
|
31737
31815
|
tableCount: tablesToCapture.length,
|
|
31738
31816
|
ddl,
|
|
31739
|
-
capturedFrom: masked
|
|
31817
|
+
capturedFrom: masked,
|
|
31818
|
+
safeMode: safeModeValue,
|
|
31819
|
+
piiSummary
|
|
31740
31820
|
},
|
|
31741
31821
|
schema: packSchema,
|
|
31742
31822
|
plan,
|
|
@@ -31751,7 +31831,8 @@ async function captureDatabase(config, options) {
|
|
|
31751
31831
|
totalRows,
|
|
31752
31832
|
tableCount: tablesToCapture.length,
|
|
31753
31833
|
durationMs,
|
|
31754
|
-
tableDetails
|
|
31834
|
+
tableDetails,
|
|
31835
|
+
piiSummary
|
|
31755
31836
|
};
|
|
31756
31837
|
} finally {
|
|
31757
31838
|
await closeConnection(pool);
|
|
@@ -33737,6 +33818,18 @@ async function captureCommand(options) {
|
|
|
33737
33818
|
const config = await loadConfig(options.configPath);
|
|
33738
33819
|
const masked = maskConnectionString(config.database.connectionString);
|
|
33739
33820
|
const tables = options.tables ? options.tables.split(",").map((t) => t.trim()).filter((t) => t.length > 0) : void 0;
|
|
33821
|
+
const safeMode = options.safe ? ["mask", "tokenize", "redact"].includes(options.safeMode ?? "") ? options.safeMode : "mask" : void 0;
|
|
33822
|
+
const maxRows = options.maxRows ? parseInt(options.maxRows, 10) : void 0;
|
|
33823
|
+
let around;
|
|
33824
|
+
if (options.around) {
|
|
33825
|
+
const eqIdx = options.around.indexOf("=");
|
|
33826
|
+
if (eqIdx > 0) {
|
|
33827
|
+
around = {
|
|
33828
|
+
column: options.around.substring(0, eqIdx),
|
|
33829
|
+
value: options.around.substring(eqIdx + 1)
|
|
33830
|
+
};
|
|
33831
|
+
}
|
|
33832
|
+
}
|
|
33740
33833
|
if (!options.ci) {
|
|
33741
33834
|
console.log("");
|
|
33742
33835
|
console.log("RealityDB Capture");
|
|
@@ -33746,6 +33839,15 @@ async function captureCommand(options) {
|
|
|
33746
33839
|
if (tables) {
|
|
33747
33840
|
console.log(`Tables: ${tables.join(", ")}`);
|
|
33748
33841
|
}
|
|
33842
|
+
if (options.safe) {
|
|
33843
|
+
console.log(`Safe mode: ${safeMode} (PII will be sanitized)`);
|
|
33844
|
+
}
|
|
33845
|
+
if (maxRows !== void 0) {
|
|
33846
|
+
console.log(`Max rows per table: ${maxRows}`);
|
|
33847
|
+
}
|
|
33848
|
+
if (around) {
|
|
33849
|
+
console.log("");
|
|
33850
|
+
}
|
|
33749
33851
|
console.log("");
|
|
33750
33852
|
console.log("Capturing...");
|
|
33751
33853
|
}
|
|
@@ -33753,7 +33855,11 @@ async function captureCommand(options) {
|
|
|
33753
33855
|
name: options.name,
|
|
33754
33856
|
description: options.description,
|
|
33755
33857
|
tables,
|
|
33756
|
-
outputDir: options.output
|
|
33858
|
+
outputDir: options.output,
|
|
33859
|
+
safe: options.safe,
|
|
33860
|
+
safeMode,
|
|
33861
|
+
maxRows,
|
|
33862
|
+
around
|
|
33757
33863
|
});
|
|
33758
33864
|
const durationMs = Math.round(performance.now() - start);
|
|
33759
33865
|
if (options.ci) {
|
|
@@ -33769,6 +33875,8 @@ async function captureCommand(options) {
|
|
|
33769
33875
|
filePath: result.filePath,
|
|
33770
33876
|
tableCount: result.tableCount,
|
|
33771
33877
|
totalRows: result.totalRows,
|
|
33878
|
+
safeMode: result.pack.metadata.safeMode,
|
|
33879
|
+
piiSummary: result.piiSummary,
|
|
33772
33880
|
tables: result.tableDetails.map((t) => ({
|
|
33773
33881
|
name: t.name,
|
|
33774
33882
|
rowCount: t.rowCount
|
|
@@ -33778,6 +33886,21 @@ async function captureCommand(options) {
|
|
|
33778
33886
|
}));
|
|
33779
33887
|
return;
|
|
33780
33888
|
}
|
|
33889
|
+
if (options.safe && result.piiSummary) {
|
|
33890
|
+
const { columnsDetected, tablesAffected, categoriesFound } = result.piiSummary;
|
|
33891
|
+
if (columnsDetected > 0) {
|
|
33892
|
+
console.log(`PII detected: ${columnsDetected} columns across ${tablesAffected} tables. Sanitizing...`);
|
|
33893
|
+
console.log(` Categories: ${categoriesFound.join(", ")}`);
|
|
33894
|
+
console.log("");
|
|
33895
|
+
} else {
|
|
33896
|
+
console.log("No PII detected \u2014 data captured as-is.");
|
|
33897
|
+
console.log("");
|
|
33898
|
+
}
|
|
33899
|
+
}
|
|
33900
|
+
if (around) {
|
|
33901
|
+
console.log(`Capturing rows related to ${around.column}=${around.value} across ${result.tableCount} tables`);
|
|
33902
|
+
console.log("");
|
|
33903
|
+
}
|
|
33781
33904
|
for (const table of result.tableDetails) {
|
|
33782
33905
|
console.log(` ${table.name}: ${table.rowCount} rows`);
|
|
33783
33906
|
}
|
|
@@ -33785,6 +33908,9 @@ async function captureCommand(options) {
|
|
|
33785
33908
|
const sizeKb = Math.round(fileStat.size / 1024);
|
|
33786
33909
|
console.log("");
|
|
33787
33910
|
console.log(`Captured: ${result.filePath} (${sizeKb} KB)`);
|
|
33911
|
+
if (options.safe) {
|
|
33912
|
+
console.log(`Privacy: PII sanitized (${safeMode} mode). Safe to share.`);
|
|
33913
|
+
}
|
|
33788
33914
|
console.log("Schema DDL included. Share this file to reproduce the environment.");
|
|
33789
33915
|
console.log("");
|
|
33790
33916
|
} catch (err) {
|
|
@@ -33915,6 +34041,19 @@ var VERSION9 = "0.10.0";
|
|
|
33915
34041
|
function isUrl(input) {
|
|
33916
34042
|
return input.startsWith("http://") || input.startsWith("https://");
|
|
33917
34043
|
}
|
|
34044
|
+
function displaySafeModeStatus(safeMode) {
|
|
34045
|
+
if (!safeMode) {
|
|
34046
|
+
console.warn("Note: This pack was captured before privacy-safe mode was available");
|
|
34047
|
+
} else if (safeMode === "raw") {
|
|
34048
|
+
console.log("WARNING: This pack contains raw (unsanitized) data");
|
|
34049
|
+
} else if (safeMode === "masked") {
|
|
34050
|
+
console.log("This pack was captured with PII masking");
|
|
34051
|
+
} else if (safeMode === "tokenized") {
|
|
34052
|
+
console.log("This pack was captured with PII tokenization");
|
|
34053
|
+
} else if (safeMode === "redacted") {
|
|
34054
|
+
console.log("This pack was captured with PII redaction");
|
|
34055
|
+
}
|
|
34056
|
+
}
|
|
33918
34057
|
async function loadCommand(filePath, options) {
|
|
33919
34058
|
const start = performance.now();
|
|
33920
34059
|
try {
|
|
@@ -33950,6 +34089,57 @@ async function loadCommand(filePath, options) {
|
|
|
33950
34089
|
}
|
|
33951
34090
|
}
|
|
33952
34091
|
const pack = await loadRealityPack(localPath);
|
|
34092
|
+
if (options.preview) {
|
|
34093
|
+
const meta2 = pack.metadata;
|
|
34094
|
+
const safeMode2 = meta2.safeMode;
|
|
34095
|
+
const piiSummary = meta2.piiSummary;
|
|
34096
|
+
if (options.ci) {
|
|
34097
|
+
const tableNames = Object.keys(pack.dataset.tables);
|
|
34098
|
+
const tableInfo = tableNames.map((name) => ({
|
|
34099
|
+
name,
|
|
34100
|
+
rowCount: pack.dataset.tables[name].rowCount
|
|
34101
|
+
}));
|
|
34102
|
+
console.log(formatCIOutput({
|
|
34103
|
+
success: true,
|
|
34104
|
+
command: "load",
|
|
34105
|
+
version: VERSION9,
|
|
34106
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34107
|
+
durationMs: Math.round(performance.now() - start),
|
|
34108
|
+
data: {
|
|
34109
|
+
packName: pack.metadata.name,
|
|
34110
|
+
safeMode: safeMode2 ?? null,
|
|
34111
|
+
piiSummary: piiSummary ?? null,
|
|
34112
|
+
tableCount: pack.metadata.tableCount,
|
|
34113
|
+
totalRows: pack.metadata.totalRows,
|
|
34114
|
+
tables: tableInfo
|
|
34115
|
+
}
|
|
34116
|
+
}));
|
|
34117
|
+
return;
|
|
34118
|
+
}
|
|
34119
|
+
console.log("");
|
|
34120
|
+
console.log("Reality Pack Preview");
|
|
34121
|
+
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");
|
|
34122
|
+
console.log(`Name: ${pack.metadata.name}`);
|
|
34123
|
+
if (pack.metadata.description) {
|
|
34124
|
+
console.log(`Description: ${pack.metadata.description}`);
|
|
34125
|
+
}
|
|
34126
|
+
console.log(`Tables: ${pack.metadata.tableCount}`);
|
|
34127
|
+
console.log(`Total rows: ${pack.metadata.totalRows}`);
|
|
34128
|
+
console.log("");
|
|
34129
|
+
displaySafeModeStatus(safeMode2);
|
|
34130
|
+
if (piiSummary) {
|
|
34131
|
+
console.log(` PII columns detected: ${piiSummary.columnsDetected}`);
|
|
34132
|
+
console.log(` Tables affected: ${piiSummary.tablesAffected}`);
|
|
34133
|
+
console.log(` Categories: ${piiSummary.categoriesFound.join(", ")}`);
|
|
34134
|
+
}
|
|
34135
|
+
console.log("");
|
|
34136
|
+
console.log("Tables:");
|
|
34137
|
+
for (const [name, tableData] of Object.entries(pack.dataset.tables)) {
|
|
34138
|
+
console.log(` ${name}: ${tableData.rowCount} rows`);
|
|
34139
|
+
}
|
|
34140
|
+
console.log("");
|
|
34141
|
+
return;
|
|
34142
|
+
}
|
|
33953
34143
|
if (options.showDdl) {
|
|
33954
34144
|
const ddl = pack.metadata.ddl;
|
|
33955
34145
|
if (options.ci) {
|
|
@@ -33982,6 +34172,8 @@ async function loadCommand(filePath, options) {
|
|
|
33982
34172
|
}
|
|
33983
34173
|
const config = await loadConfig(options.configPath);
|
|
33984
34174
|
const masked = maskConnectionString(config.database.connectionString);
|
|
34175
|
+
const meta = pack.metadata;
|
|
34176
|
+
const safeMode = meta.safeMode;
|
|
33985
34177
|
if (!options.ci) {
|
|
33986
34178
|
console.log("");
|
|
33987
34179
|
console.log("RealityDB Load");
|
|
@@ -34001,6 +34193,8 @@ async function loadCommand(filePath, options) {
|
|
|
34001
34193
|
console.log("Schema DDL: included");
|
|
34002
34194
|
}
|
|
34003
34195
|
console.log("");
|
|
34196
|
+
displaySafeModeStatus(safeMode);
|
|
34197
|
+
console.log("");
|
|
34004
34198
|
}
|
|
34005
34199
|
if (!options.ci && !options.confirm) {
|
|
34006
34200
|
console.error("[realitydb] Load requires --confirm flag.");
|
|
@@ -34008,6 +34202,9 @@ async function loadCommand(filePath, options) {
|
|
|
34008
34202
|
console.error("");
|
|
34009
34203
|
console.error("To view the schema DDL first:");
|
|
34010
34204
|
console.error(` realitydb load ${filePath} --show-ddl`);
|
|
34205
|
+
console.error("");
|
|
34206
|
+
console.error("To preview pack contents:");
|
|
34207
|
+
console.error(` realitydb load ${filePath} --preview`);
|
|
34011
34208
|
process.exit(1);
|
|
34012
34209
|
}
|
|
34013
34210
|
if (!options.ci) {
|
|
@@ -34026,6 +34223,7 @@ async function loadCommand(filePath, options) {
|
|
|
34026
34223
|
database: masked,
|
|
34027
34224
|
packName: pack.metadata.name,
|
|
34028
34225
|
totalRows: result.totalRows,
|
|
34226
|
+
safeMode: safeMode ?? null,
|
|
34029
34227
|
source: isUrl(filePath) ? filePath : void 0,
|
|
34030
34228
|
tables: result.insertResult.tables.map((t) => ({
|
|
34031
34229
|
name: t.tableName,
|
|
@@ -34043,7 +34241,8 @@ async function loadCommand(filePath, options) {
|
|
|
34043
34241
|
}
|
|
34044
34242
|
const totalTime = (result.durationMs / 1e3).toFixed(1);
|
|
34045
34243
|
console.log("");
|
|
34046
|
-
console.log(`
|
|
34244
|
+
console.log(`Bug reproduction environment ready. ${result.insertResult.tables.length} tables, ${result.totalRows} rows loaded.`);
|
|
34245
|
+
console.log(`Load complete in ${totalTime}s`);
|
|
34047
34246
|
console.log("");
|
|
34048
34247
|
} catch (err) {
|
|
34049
34248
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -35664,7 +35863,7 @@ async function simulateWebhooksCommand(options) {
|
|
|
35664
35863
|
}
|
|
35665
35864
|
|
|
35666
35865
|
// src/cli.ts
|
|
35667
|
-
var VERSION17 = "
|
|
35866
|
+
var VERSION17 = "2.0.0";
|
|
35668
35867
|
function run(argv) {
|
|
35669
35868
|
const program2 = new Command();
|
|
35670
35869
|
program2.name("realitydb").description("RealityDB -- Developer Reality Platform").version(VERSION17).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);
|
|
@@ -35766,7 +35965,7 @@ function run(argv) {
|
|
|
35766
35965
|
const opts = program2.opts();
|
|
35767
35966
|
await simulateWebhooksCommand({ ...cmdOpts, ci: opts.ci, configPath: opts.config });
|
|
35768
35967
|
});
|
|
35769
|
-
program2.command("capture").description("Capture live database state into a Reality Pack").requiredOption("--name <name>", "Name for the captured pack").option("--description <desc>", "Pack description").option("--tables <tables>", "Comma-separated list of tables to capture").option("--output <dir>", "Output directory", ".").action(async (cmdOpts) => {
|
|
35968
|
+
program2.command("capture").description("Capture live database state into a Reality Pack").requiredOption("--name <name>", "Name for the captured pack").option("--description <desc>", "Pack description").option("--tables <tables>", "Comma-separated list of tables to capture").option("--output <dir>", "Output directory", ".").option("--safe", "Enable privacy-safe capture (sanitize PII before writing)").option("--safe-mode <mode>", "PII sanitization mode: mask (default), tokenize, redact", "mask").option("--max-rows <count>", "Maximum rows to capture per table").option("--around <column=value>", "Capture rows related to a specific entity (follows FK chains)").action(async (cmdOpts) => {
|
|
35770
35969
|
const opts = program2.opts();
|
|
35771
35970
|
await captureCommand({ ...cmdOpts, ci: opts.ci, configPath: opts.config });
|
|
35772
35971
|
});
|
|
@@ -35774,7 +35973,7 @@ function run(argv) {
|
|
|
35774
35973
|
const opts = program2.opts();
|
|
35775
35974
|
await shareCommand(filePath, { ...cmdOpts, ci: opts.ci, configPath: opts.config });
|
|
35776
35975
|
});
|
|
35777
|
-
program2.command("load <file>").description("Load a Reality Pack into the database (file path or URL)").option("--confirm", "Confirm import operation").option("--show-ddl", "Show schema DDL without importing").action(async (filePath, cmdOpts) => {
|
|
35976
|
+
program2.command("load <file>").description("Load a Reality Pack into the database (file path or URL)").option("--confirm", "Confirm import operation").option("--show-ddl", "Show schema DDL without importing").option("--preview", "Preview pack contents without importing").action(async (filePath, cmdOpts) => {
|
|
35778
35977
|
const opts = program2.opts();
|
|
35779
35978
|
await loadCommand(filePath, { ...cmdOpts, ci: opts.ci, configPath: opts.config });
|
|
35780
35979
|
});
|