@forzalabs/remora 1.0.21 → 1.1.1
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/actions/automap.js +26 -42
- package/actions/compile.js +27 -43
- package/actions/create_consumer.js +24 -40
- package/actions/create_producer.js +16 -32
- package/actions/debug.js +18 -34
- package/actions/deploy.js +30 -46
- package/actions/discover.js +13 -29
- package/actions/init.js +29 -45
- package/actions/mock.js +16 -32
- package/actions/run.js +34 -52
- package/actions/sample.js +42 -58
- package/index.js +38 -43
- package/package.json +4 -4
- package/workers/ExecutorWorker.js +18 -32
- package/Constants.js +0 -34
- package/core/Affirm.js +0 -42
- package/core/Algo.js +0 -160
- package/core/dste/DSTE.js +0 -113
- package/core/logger/DebugLogService.js +0 -48
- package/core/logger/DevelopmentLogService.js +0 -70
- package/core/logger/LocalLogService.js +0 -70
- package/core/logger/Logger.js +0 -54
- package/database/DatabaseEngine.js +0 -149
- package/database/DatabaseStructure.js +0 -27
- package/definitions/DatasetDefinitions.js +0 -2
- package/definitions/ExecutorDefinitions.js +0 -2
- package/definitions/ProcessENV.js +0 -2
- package/definitions/agents/DestinationDriver.js +0 -2
- package/definitions/agents/SourceDriver.js +0 -2
- package/definitions/cli.js +0 -2
- package/definitions/database/ApiKeys.js +0 -2
- package/definitions/database/Stored.js +0 -7
- package/definitions/database/UsageStat.js +0 -2
- package/definitions/database/User.js +0 -2
- package/definitions/json_schemas/consumer-schema.json +0 -1226
- package/definitions/json_schemas/producer-schema.json +0 -308
- package/definitions/json_schemas/project-schema.json +0 -100
- package/definitions/json_schemas/source-schema.json +0 -249
- package/definitions/requests/ConsumerRequest.js +0 -2
- package/definitions/requests/Developer.js +0 -2
- package/definitions/requests/Mapping.js +0 -2
- package/definitions/requests/ProducerRequest.js +0 -2
- package/definitions/requests/Request.js +0 -2
- package/definitions/resources/Compiled.js +0 -2
- package/definitions/resources/Consumer.js +0 -2
- package/definitions/resources/Environment.js +0 -2
- package/definitions/resources/Library.js +0 -2
- package/definitions/resources/Producer.js +0 -2
- package/definitions/resources/Project.js +0 -2
- package/definitions/resources/Schema.js +0 -2
- package/definitions/resources/Source.js +0 -2
- package/definitions/temp.js +0 -2
- package/definitions/transform/Transformations.js +0 -2
- package/drivers/DeltaShareDriver.js +0 -186
- package/drivers/DriverFactory.js +0 -72
- package/drivers/DriverHelper.js +0 -248
- package/drivers/HttpApiDriver.js +0 -208
- package/drivers/RedshiftDriver.js +0 -184
- package/drivers/files/LocalDestinationDriver.js +0 -146
- package/drivers/files/LocalSourceDriver.js +0 -405
- package/drivers/s3/S3DestinationDriver.js +0 -197
- package/drivers/s3/S3SourceDriver.js +0 -495
- package/engines/CryptoEngine.js +0 -75
- package/engines/Environment.js +0 -170
- package/engines/ProcessENVManager.js +0 -83
- package/engines/RandomEngine.js +0 -47
- package/engines/SecretManager.js +0 -23
- package/engines/UserManager.js +0 -66
- package/engines/ai/AutoMapperEngine.js +0 -37
- package/engines/ai/DeveloperEngine.js +0 -497
- package/engines/ai/LLM.js +0 -255
- package/engines/consumer/ConsumerManager.js +0 -218
- package/engines/consumer/ConsumerOnFinishManager.js +0 -202
- package/engines/dataset/Dataset.js +0 -824
- package/engines/dataset/DatasetManager.js +0 -211
- package/engines/dataset/DatasetRecord.js +0 -120
- package/engines/dataset/DatasetRecordPool.js +0 -77
- package/engines/execution/RequestExecutor.js +0 -67
- package/engines/parsing/CSVParser.js +0 -60
- package/engines/parsing/LineParser.js +0 -71
- package/engines/parsing/ParseCompression.js +0 -101
- package/engines/parsing/ParseHelper.js +0 -18
- package/engines/parsing/ParseManager.js +0 -54
- package/engines/parsing/XLSParser.js +0 -87
- package/engines/parsing/XMLParser.js +0 -115
- package/engines/producer/ProducerEngine.js +0 -127
- package/engines/producer/ProducerManager.js +0 -43
- package/engines/scheduler/CronScheduler.js +0 -222
- package/engines/scheduler/QueueManager.js +0 -314
- package/engines/schema/SchemaValidator.js +0 -67
- package/engines/transform/JoinEngine.js +0 -232
- package/engines/transform/TransformationEngine.js +0 -277
- package/engines/transform/TypeCaster.js +0 -59
- package/engines/usage/DataframeManager.js +0 -55
- package/engines/usage/UsageDataManager.js +0 -151
- package/engines/usage/UsageManager.js +0 -65
- package/engines/validation/Validator.js +0 -216
- package/executors/ConsumerExecutor.js +0 -280
- package/executors/Executor.js +0 -177
- package/executors/ExecutorOrchestrator.js +0 -331
- package/executors/ExecutorPerformance.js +0 -17
- package/executors/ExecutorProgress.js +0 -54
- package/executors/ExecutorScope.js +0 -52
- package/executors/OutputExecutor.js +0 -118
- package/executors/ProducerExecutor.js +0 -108
- package/helper/Helper.js +0 -149
- package/helper/Logger.js +0 -84
- package/helper/Runtime.js +0 -20
- package/helper/Settings.js +0 -13
- package/licencing/LicenceManager.js +0 -64
- package/settings.js +0 -12
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
const Affirm_1 = __importDefault(require("../../core/Affirm"));
|
|
16
|
-
const ProducerEngine_1 = __importDefault(require("../producer/ProducerEngine"));
|
|
17
|
-
const Environment_1 = __importDefault(require("../Environment"));
|
|
18
|
-
const path_1 = __importDefault(require("path"));
|
|
19
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
20
|
-
const dayjs_1 = __importDefault(require("dayjs"));
|
|
21
|
-
const customParseFormat_1 = __importDefault(require("dayjs/plugin/customParseFormat"));
|
|
22
|
-
dayjs_1.default.extend(customParseFormat_1.default);
|
|
23
|
-
class DeveloperEngineClass {
|
|
24
|
-
constructor() {
|
|
25
|
-
this.discover = (producer) => __awaiter(this, void 0, void 0, function* () {
|
|
26
|
-
var _a;
|
|
27
|
-
(0, Affirm_1.default)(producer, 'Invalid producer');
|
|
28
|
-
const sampleData = yield ProducerEngine_1.default.readSampleData(producer, 10, true);
|
|
29
|
-
(0, Affirm_1.default)(sampleData, 'Discover process failed: no result found');
|
|
30
|
-
const typeDefinitions = this.extractFieldTypes(sampleData);
|
|
31
|
-
const mappedProducer = {
|
|
32
|
-
name: producer.name,
|
|
33
|
-
description: producer.description,
|
|
34
|
-
source: producer.source,
|
|
35
|
-
settings: Object.assign({}, producer.settings),
|
|
36
|
-
dimensions: typeDefinitions.map(field => {
|
|
37
|
-
var _a;
|
|
38
|
-
return ({
|
|
39
|
-
name: field.name,
|
|
40
|
-
type: this.mapFieldTypeToProducerType(field.type),
|
|
41
|
-
description: `Auto-mapped field: ${field.name}`,
|
|
42
|
-
classification: ((_a = this.extractFieldClassification(field)) === null || _a === void 0 ? void 0 : _a.isPHI) ? ['PHI'] : undefined
|
|
43
|
-
});
|
|
44
|
-
}),
|
|
45
|
-
measures: [],
|
|
46
|
-
_version: (_a = producer._version) !== null && _a !== void 0 ? _a : 1
|
|
47
|
-
};
|
|
48
|
-
mappedProducer['$schema'] = producer['$schema'];
|
|
49
|
-
// Save the mapped producer to file
|
|
50
|
-
const producerPath = path_1.default.join(process.cwd(), 'remora', 'producers', `${producer.name}.json`);
|
|
51
|
-
yield promises_1.default.writeFile(producerPath, JSON.stringify(mappedProducer, null, 4), 'utf-8');
|
|
52
|
-
return { producer: mappedProducer, fields: typeDefinitions };
|
|
53
|
-
});
|
|
54
|
-
this.mapFieldTypeToProducerType = (fieldType) => {
|
|
55
|
-
switch (fieldType) {
|
|
56
|
-
case 'number':
|
|
57
|
-
return 'number';
|
|
58
|
-
case 'string':
|
|
59
|
-
return 'string';
|
|
60
|
-
case 'date':
|
|
61
|
-
case 'datetime':
|
|
62
|
-
return 'datetime';
|
|
63
|
-
default:
|
|
64
|
-
return 'string';
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
// Infer the most likely type from a single JS value
|
|
68
|
-
// Returns one of: 'number' | 'boolean' | 'date' | 'datetime' | 'string' | 'array' | 'object' | 'null'
|
|
69
|
-
this.inferType = (value) => {
|
|
70
|
-
if (value === null || value === undefined)
|
|
71
|
-
return 'string';
|
|
72
|
-
// Arrays
|
|
73
|
-
if (Array.isArray(value))
|
|
74
|
-
return 'array';
|
|
75
|
-
// Booleans (including common string representations)
|
|
76
|
-
if (typeof value === 'boolean')
|
|
77
|
-
return 'boolean';
|
|
78
|
-
if (typeof value === 'string') {
|
|
79
|
-
const trimmed = value.trim();
|
|
80
|
-
const lower = trimmed.toLowerCase();
|
|
81
|
-
if (lower === 'true' || lower === 'false')
|
|
82
|
-
return 'boolean';
|
|
83
|
-
// Numbers (numeric strings)
|
|
84
|
-
const numericRegex = /^-?\d+(?:\.\d+)?$/;
|
|
85
|
-
if (numericRegex.test(trimmed))
|
|
86
|
-
return 'number';
|
|
87
|
-
// Timestamps (10 or 13 digits)
|
|
88
|
-
const tsRegex = /^-?\d{10}(?:\d{3})?$/;
|
|
89
|
-
if (tsRegex.test(trimmed)) {
|
|
90
|
-
const n = Number(trimmed.length === 10 ? `${trimmed}000` : trimmed);
|
|
91
|
-
const d = new Date(n);
|
|
92
|
-
if (!isNaN(d.getTime()))
|
|
93
|
-
return 'datetime';
|
|
94
|
-
}
|
|
95
|
-
// Dates with common formats
|
|
96
|
-
const dateFormats = [
|
|
97
|
-
'YYYY-MM-DD',
|
|
98
|
-
'YYYY/MM/DD',
|
|
99
|
-
'DD/MM/YYYY',
|
|
100
|
-
'MM/DD/YYYY',
|
|
101
|
-
'YYYYMMDD',
|
|
102
|
-
'DD-MMM-YYYY',
|
|
103
|
-
'YYYY-MM-DD HH:mm',
|
|
104
|
-
'YYYY-MM-DD HH:mm:ss',
|
|
105
|
-
'YYYY-MM-DDTHH:mm',
|
|
106
|
-
'YYYY-MM-DDTHH:mmZ',
|
|
107
|
-
'YYYY-MM-DDTHH:mm:ss',
|
|
108
|
-
'YYYY-MM-DDTHH:mm:ssZ',
|
|
109
|
-
'YYYY-MM-DDTHH:mm:ss.SSSZ'
|
|
110
|
-
];
|
|
111
|
-
for (const fmt of dateFormats) {
|
|
112
|
-
const d = (0, dayjs_1.default)(trimmed, fmt, true);
|
|
113
|
-
if (d.isValid()) {
|
|
114
|
-
// If time components likely present, classify as datetime
|
|
115
|
-
if (/T|\d+:\d+/.test(trimmed))
|
|
116
|
-
return 'datetime';
|
|
117
|
-
return 'date';
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// ISO 8601 without specifying format
|
|
121
|
-
const iso = (0, dayjs_1.default)(trimmed);
|
|
122
|
-
if (iso.isValid() && /\d{4}-\d{2}-\d{2}/.test(trimmed)) {
|
|
123
|
-
if (/T|\d+:\d+/.test(trimmed))
|
|
124
|
-
return 'datetime';
|
|
125
|
-
return 'date';
|
|
126
|
-
}
|
|
127
|
-
return 'string';
|
|
128
|
-
}
|
|
129
|
-
if (typeof value === 'number')
|
|
130
|
-
return 'number';
|
|
131
|
-
if (typeof value === 'object') {
|
|
132
|
-
// Date instance
|
|
133
|
-
if (value instanceof Date && !isNaN(value.getTime()))
|
|
134
|
-
return 'datetime';
|
|
135
|
-
return 'object';
|
|
136
|
-
}
|
|
137
|
-
// Fallback for bigint, symbol, function -> string
|
|
138
|
-
return 'string';
|
|
139
|
-
};
|
|
140
|
-
this.inferDimensionType = (value) => {
|
|
141
|
-
const type = this.inferType(value);
|
|
142
|
-
switch (type) {
|
|
143
|
-
case 'array':
|
|
144
|
-
case 'object': return 'string';
|
|
145
|
-
case 'boolean': return 'boolean';
|
|
146
|
-
case 'date':
|
|
147
|
-
case 'datetime': return 'datetime';
|
|
148
|
-
case 'number': return 'number';
|
|
149
|
-
case 'string': return 'string';
|
|
150
|
-
default: return 'string';
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
this.extractFieldTypes = (records) => {
|
|
154
|
-
if (!records || records.length === 0)
|
|
155
|
-
return [];
|
|
156
|
-
const sample = records[0];
|
|
157
|
-
return Object.entries(sample._value).map(([key, value]) => ({
|
|
158
|
-
name: key,
|
|
159
|
-
type: this.inferType(value)
|
|
160
|
-
}));
|
|
161
|
-
};
|
|
162
|
-
this.extractFieldClassification = (field) => {
|
|
163
|
-
(0, Affirm_1.default)(field, 'Invalid field');
|
|
164
|
-
const { name, type } = field;
|
|
165
|
-
const fieldNameLower = name.toLowerCase();
|
|
166
|
-
// Rule 1: Names
|
|
167
|
-
const namePatterns = [
|
|
168
|
-
/\b(first|last|middle|full|given|family|sur|maiden|nick|display)[\s_-]?name\b/,
|
|
169
|
-
/\b(fname|lname|mname|fullname|givenname|familyname|surname)\b/,
|
|
170
|
-
/\b(patient|person|individual|customer|client|user)[\s_-]?name\b/,
|
|
171
|
-
/\bname\b/
|
|
172
|
-
];
|
|
173
|
-
// Rule 2: Geographic subdivisions (excluding state level)
|
|
174
|
-
const geoPatterns = [
|
|
175
|
-
/\b(address|addr|street|st|avenue|ave|road|rd|lane|ln|drive|dr|blvd|boulevard)\b/,
|
|
176
|
-
/\b(city|town|village|municipality)\b/,
|
|
177
|
-
/\b(county|parish|borough)\b/,
|
|
178
|
-
/\b(zip|postal|zipcode|postalcode)\b/,
|
|
179
|
-
/\b(precinct|district|ward)\b/,
|
|
180
|
-
/\b(geocode|coordinates|coord|latitude|longitude|lat|lng)\b/,
|
|
181
|
-
/\b(location|place|residence|home)\b/
|
|
182
|
-
];
|
|
183
|
-
// Rule 3: Dates related to individuals
|
|
184
|
-
const datePatterns = [
|
|
185
|
-
/\b(birth|born|dob|birthdate|date[\s_-]?of[\s_-]?birth)\b/,
|
|
186
|
-
/\b(admission|admit|admitdate|admission[\s_-]?date)\b/,
|
|
187
|
-
/\b(discharge|discharged|dischargedate|discharge[\s_-]?date)\b/,
|
|
188
|
-
/\b(death|died|dod|date[\s_-]?of[\s_-]?death|deceased)\b/,
|
|
189
|
-
/\b(age|years[\s_-]?old|yrs[\s_-]?old)\b/,
|
|
190
|
-
/\b(visit|appointment|appt|service)[\s_-]?date\b/,
|
|
191
|
-
/\b(created|updated|modified|last[\s_-]?seen)[\s_-]?date\b/
|
|
192
|
-
];
|
|
193
|
-
// Rule 4: Phone numbers
|
|
194
|
-
const phonePatterns = [
|
|
195
|
-
/\b(phone|tel|telephone|mobile|cell|cellular)\b/,
|
|
196
|
-
/\b(home|work|office|emergency)[\s_-]?phone\b/,
|
|
197
|
-
/\b(contact|phone)[\s_-]?number\b/
|
|
198
|
-
];
|
|
199
|
-
// Rule 5: Fax numbers
|
|
200
|
-
const faxPatterns = [
|
|
201
|
-
/\b(fax|facsimile)\b/,
|
|
202
|
-
/\bfax[\s_-]?number\b/
|
|
203
|
-
];
|
|
204
|
-
// Rule 6: Email addresses
|
|
205
|
-
const emailPatterns = [
|
|
206
|
-
/\b(email|e[\s_-]?mail|mail)\b/,
|
|
207
|
-
/\b(email|mail)[\s_-]?address\b/,
|
|
208
|
-
/\b(contact|personal|work)[\s_-]?email\b/
|
|
209
|
-
];
|
|
210
|
-
// Rule 7: Social Security numbers
|
|
211
|
-
const ssnPatterns = [
|
|
212
|
-
/\b(ssn|social[\s_-]?security)\b/,
|
|
213
|
-
/\bsocial[\s_-]?security[\s_-]?number\b/,
|
|
214
|
-
/\btax[\s_-]?id\b/
|
|
215
|
-
];
|
|
216
|
-
// Rule 8: Medical record numbers
|
|
217
|
-
const medicalRecordPatterns = [
|
|
218
|
-
/\b(mrn|medical[\s_-]?record)\b/,
|
|
219
|
-
/\bmedical[\s_-]?record[\s_-]?number\b/,
|
|
220
|
-
/\b(patient|chart)[\s_-]?number\b/,
|
|
221
|
-
/\b(patient|medical)[\s_-]?id\b/
|
|
222
|
-
];
|
|
223
|
-
// Rule 9: Health plan beneficiary numbers
|
|
224
|
-
const healthPlanPatterns = [
|
|
225
|
-
/\b(member|subscriber|beneficiary)[\s_-]?id\b/,
|
|
226
|
-
/\b(insurance|health[\s_-]?plan)[\s_-]?number\b/,
|
|
227
|
-
/\b(policy|plan)[\s_-]?number\b/,
|
|
228
|
-
/\b(medicaid|medicare)[\s_-]?number\b/
|
|
229
|
-
];
|
|
230
|
-
// Rule 10: Account numbers
|
|
231
|
-
const accountPatterns = [
|
|
232
|
-
/\b(account|acct)[\s_-]?number\b/,
|
|
233
|
-
/\b(account|acct)[\s_-]?id\b/,
|
|
234
|
-
/\b(billing|financial)[\s_-]?account\b/
|
|
235
|
-
];
|
|
236
|
-
// Rule 11: Certificate/license numbers
|
|
237
|
-
const licensePatterns = [
|
|
238
|
-
/\b(license|licence|cert|certificate)[\s_-]?number\b/,
|
|
239
|
-
/\b(driver|drivers)[\s_-]?license\b/,
|
|
240
|
-
/\b(professional|medical)[\s_-]?license\b/,
|
|
241
|
-
/\b(permit|registration)[\s_-]?number\b/
|
|
242
|
-
];
|
|
243
|
-
// Rule 12: Vehicle identifiers
|
|
244
|
-
const vehiclePatterns = [
|
|
245
|
-
/\b(vehicle|car|auto)[\s_-]?id\b/,
|
|
246
|
-
/\b(license[\s_-]?plate|plate[\s_-]?number)\b/,
|
|
247
|
-
/\b(vin|vehicle[\s_-]?identification)\b/,
|
|
248
|
-
/\bserial[\s_-]?number\b/
|
|
249
|
-
];
|
|
250
|
-
// Rule 13: Device identifiers
|
|
251
|
-
const devicePatterns = [
|
|
252
|
-
/\b(device|equipment)[\s_-]?id\b/,
|
|
253
|
-
/\b(serial|model)[\s_-]?number\b/,
|
|
254
|
-
/\b(imei|mac[\s_-]?address|uuid)\b/
|
|
255
|
-
];
|
|
256
|
-
// Rule 14: URLs
|
|
257
|
-
const urlPatterns = [
|
|
258
|
-
/\b(url|web[\s_-]?address|website)\b/,
|
|
259
|
-
/\b(link|hyperlink)\b/,
|
|
260
|
-
/\b(homepage|web[\s_-]?page)\b/
|
|
261
|
-
];
|
|
262
|
-
// Rule 15: IP addresses
|
|
263
|
-
const ipPatterns = [
|
|
264
|
-
/\b(ip|ip[\s_-]?address)\b/,
|
|
265
|
-
/\b(internet[\s_-]?protocol)\b/,
|
|
266
|
-
/\b(network[\s_-]?address)\b/
|
|
267
|
-
];
|
|
268
|
-
// Rule 16: Biometric identifiers
|
|
269
|
-
const biometricPatterns = [
|
|
270
|
-
/\b(biometric|fingerprint|voiceprint)\b/,
|
|
271
|
-
/\b(finger|thumb)[\s_-]?print\b/,
|
|
272
|
-
/\b(voice|speech)[\s_-]?recognition\b/,
|
|
273
|
-
/\b(retina|iris)[\s_-]?scan\b/
|
|
274
|
-
];
|
|
275
|
-
// Rule 17: Photographic images
|
|
276
|
-
const imagePatterns = [
|
|
277
|
-
/\b(photo|photograph|image|picture)\b/,
|
|
278
|
-
/\b(face|facial)[\s_-]?image\b/,
|
|
279
|
-
/\b(avatar|profile[\s_-]?picture)\b/
|
|
280
|
-
];
|
|
281
|
-
// Rule 18: Other unique identifiers
|
|
282
|
-
const uniqueIdPatterns = [
|
|
283
|
-
/\b(unique|universal)[\s_-]?id\b/,
|
|
284
|
-
/\b(id|identifier|tracking)[\s_-]?code\b/,
|
|
285
|
-
/\b(reference|ref)[\s_-]?number\b/,
|
|
286
|
-
/\b(token|key|hash)\b/,
|
|
287
|
-
/\b(guid|uuid)\b/
|
|
288
|
-
];
|
|
289
|
-
// Check each pattern category
|
|
290
|
-
const patternCategories = [
|
|
291
|
-
{ patterns: namePatterns, category: 'Names' },
|
|
292
|
-
{ patterns: geoPatterns, category: 'Geographic Information' },
|
|
293
|
-
{ patterns: datePatterns, category: 'Dates' },
|
|
294
|
-
{ patterns: phonePatterns, category: 'Phone Numbers' },
|
|
295
|
-
{ patterns: faxPatterns, category: 'Fax Numbers' },
|
|
296
|
-
{ patterns: emailPatterns, category: 'Email Addresses' },
|
|
297
|
-
{ patterns: ssnPatterns, category: 'Social Security Numbers' },
|
|
298
|
-
{ patterns: medicalRecordPatterns, category: 'Medical Record Numbers' },
|
|
299
|
-
{ patterns: healthPlanPatterns, category: 'Health Plan Beneficiary Numbers' },
|
|
300
|
-
{ patterns: accountPatterns, category: 'Account Numbers' },
|
|
301
|
-
{ patterns: licensePatterns, category: 'Certificate/License Numbers' },
|
|
302
|
-
{ patterns: vehiclePatterns, category: 'Vehicle Identifiers' },
|
|
303
|
-
{ patterns: devicePatterns, category: 'Device Identifiers' },
|
|
304
|
-
{ patterns: urlPatterns, category: 'URLs' },
|
|
305
|
-
{ patterns: ipPatterns, category: 'IP Addresses' },
|
|
306
|
-
{ patterns: biometricPatterns, category: 'Biometric Identifiers' },
|
|
307
|
-
{ patterns: imagePatterns, category: 'Photographic Images' },
|
|
308
|
-
{ patterns: uniqueIdPatterns, category: 'Unique Identifiers' }
|
|
309
|
-
];
|
|
310
|
-
for (const { patterns, category } of patternCategories) {
|
|
311
|
-
for (const pattern of patterns) {
|
|
312
|
-
if (pattern.test(fieldNameLower)) {
|
|
313
|
-
return {
|
|
314
|
-
isPHI: true,
|
|
315
|
-
category,
|
|
316
|
-
fieldName: name,
|
|
317
|
-
fieldType: type,
|
|
318
|
-
reason: `Field name matches pattern for ${category}`
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return {
|
|
324
|
-
isPHI: false,
|
|
325
|
-
category: null,
|
|
326
|
-
fieldName: name,
|
|
327
|
-
fieldType: type,
|
|
328
|
-
reason: 'No PHI/PII patterns detected'
|
|
329
|
-
};
|
|
330
|
-
};
|
|
331
|
-
this.createMockData = (producer, records) => __awaiter(this, void 0, void 0, function* () {
|
|
332
|
-
(0, Affirm_1.default)(producer, 'Invalid producer');
|
|
333
|
-
(0, Affirm_1.default)(records > 0, 'Record count must be greater than 0');
|
|
334
|
-
const source = Environment_1.default.getSource(producer.source);
|
|
335
|
-
(0, Affirm_1.default)(source, `No source found for producer "${producer.name}" with name "${producer.source}"`);
|
|
336
|
-
(0, Affirm_1.default)(source.engine === 'local', `Mock data generation only supports local file-based producers. Source engine "${source.engine}" is not supported.`);
|
|
337
|
-
const { fileKey, fileType, delimiter } = producer.settings;
|
|
338
|
-
(0, Affirm_1.default)(fileKey, 'Producer must have a fileKey setting for mock data generation');
|
|
339
|
-
(0, Affirm_1.default)(fileType, 'Producer must have a fileType setting for mock data generation');
|
|
340
|
-
// Generate mock records
|
|
341
|
-
const mockRecords = this.generateMockRecords(producer.dimensions, records);
|
|
342
|
-
// Get the file path
|
|
343
|
-
const basePath = source.authentication.path || process.cwd();
|
|
344
|
-
const filePath = path_1.default.join(basePath, fileKey.replace('%', 'mock'));
|
|
345
|
-
// Ensure directory exists
|
|
346
|
-
yield promises_1.default.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
347
|
-
// Write to file based on type
|
|
348
|
-
const content = this.formatMockData(mockRecords, fileType, delimiter);
|
|
349
|
-
yield promises_1.default.writeFile(filePath, content, 'utf-8');
|
|
350
|
-
return { filePath, recordCount: records };
|
|
351
|
-
});
|
|
352
|
-
this.generateMockRecords = (dimensions, count) => {
|
|
353
|
-
const records = [];
|
|
354
|
-
for (let i = 0; i < count; i++) {
|
|
355
|
-
const record = {};
|
|
356
|
-
for (const dimension of dimensions) {
|
|
357
|
-
// Skip sourceFilename dimensions as they are auto-populated
|
|
358
|
-
if (dimension.sourceFilename)
|
|
359
|
-
continue;
|
|
360
|
-
record[dimension.name] = this.generateMockValue(dimension, i);
|
|
361
|
-
}
|
|
362
|
-
records.push(record);
|
|
363
|
-
}
|
|
364
|
-
return records;
|
|
365
|
-
};
|
|
366
|
-
this.generateMockValue = (dimension, index) => {
|
|
367
|
-
const { name, type } = dimension;
|
|
368
|
-
const nameLower = name.toLowerCase();
|
|
369
|
-
// Generate contextual mock data based on field name patterns
|
|
370
|
-
if (this.matchesPattern(nameLower, ['id', 'identifier', 'key', 'pk'])) {
|
|
371
|
-
return `${index + 1}`;
|
|
372
|
-
}
|
|
373
|
-
if (this.matchesPattern(nameLower, ['first_name', 'firstname', 'fname', 'given_name'])) {
|
|
374
|
-
return this.pickRandom(['John', 'Jane', 'Michael', 'Sarah', 'David', 'Emily', 'Robert', 'Lisa', 'James', 'Mary']);
|
|
375
|
-
}
|
|
376
|
-
if (this.matchesPattern(nameLower, ['last_name', 'lastname', 'lname', 'surname', 'family_name'])) {
|
|
377
|
-
return this.pickRandom(['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Martinez', 'Wilson']);
|
|
378
|
-
}
|
|
379
|
-
if (this.matchesPattern(nameLower, ['name', 'full_name', 'fullname'])) {
|
|
380
|
-
const firstNames = ['John', 'Jane', 'Michael', 'Sarah', 'David', 'Emily'];
|
|
381
|
-
const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia'];
|
|
382
|
-
return `${this.pickRandom(firstNames)} ${this.pickRandom(lastNames)}`;
|
|
383
|
-
}
|
|
384
|
-
if (this.matchesPattern(nameLower, ['email', 'mail'])) {
|
|
385
|
-
return `user${index + 1}@example.com`;
|
|
386
|
-
}
|
|
387
|
-
if (this.matchesPattern(nameLower, ['phone', 'telephone', 'mobile', 'cell'])) {
|
|
388
|
-
return `555-${String(Math.floor(Math.random() * 900) + 100).padStart(3, '0')}-${String(Math.floor(Math.random() * 9000) + 1000).padStart(4, '0')}`;
|
|
389
|
-
}
|
|
390
|
-
if (this.matchesPattern(nameLower, ['address', 'street', 'addr'])) {
|
|
391
|
-
const streets = ['Main St', 'Oak Ave', 'Elm Dr', 'Pine Rd', 'Maple Ln', 'Cedar Blvd'];
|
|
392
|
-
return `${Math.floor(Math.random() * 9999) + 1} ${this.pickRandom(streets)}`;
|
|
393
|
-
}
|
|
394
|
-
if (this.matchesPattern(nameLower, ['city', 'town'])) {
|
|
395
|
-
return this.pickRandom(['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix', 'Philadelphia', 'San Antonio', 'San Diego']);
|
|
396
|
-
}
|
|
397
|
-
if (this.matchesPattern(nameLower, ['state', 'province'])) {
|
|
398
|
-
return this.pickRandom(['CA', 'TX', 'FL', 'NY', 'PA', 'IL', 'OH', 'GA', 'NC', 'MI']);
|
|
399
|
-
}
|
|
400
|
-
if (this.matchesPattern(nameLower, ['zip', 'postal', 'zipcode'])) {
|
|
401
|
-
return String(Math.floor(Math.random() * 90000) + 10000);
|
|
402
|
-
}
|
|
403
|
-
if (this.matchesPattern(nameLower, ['country'])) {
|
|
404
|
-
return this.pickRandom(['USA', 'Canada', 'UK', 'Germany', 'France', 'Australia']);
|
|
405
|
-
}
|
|
406
|
-
if (this.matchesPattern(nameLower, ['age', 'years'])) {
|
|
407
|
-
return Math.floor(Math.random() * 80) + 18;
|
|
408
|
-
}
|
|
409
|
-
if (this.matchesPattern(nameLower, ['sex', 'gender'])) {
|
|
410
|
-
return this.pickRandom(['M', 'F', 'Male', 'Female']);
|
|
411
|
-
}
|
|
412
|
-
if (this.matchesPattern(nameLower, ['birth', 'dob', 'birthdate'])) {
|
|
413
|
-
const year = Math.floor(Math.random() * 60) + 1940;
|
|
414
|
-
const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
|
|
415
|
-
const day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
|
|
416
|
-
return `${year}-${month}-${day}`;
|
|
417
|
-
}
|
|
418
|
-
if (this.matchesPattern(nameLower, ['date', 'created', 'updated', 'timestamp'])) {
|
|
419
|
-
const year = Math.floor(Math.random() * 5) + 2020;
|
|
420
|
-
const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
|
|
421
|
-
const day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
|
|
422
|
-
return `${year}-${month}-${day}`;
|
|
423
|
-
}
|
|
424
|
-
if (this.matchesPattern(nameLower, ['amount', 'price', 'cost', 'total', 'balance'])) {
|
|
425
|
-
return (Math.random() * 1000).toFixed(2);
|
|
426
|
-
}
|
|
427
|
-
if (this.matchesPattern(nameLower, ['quantity', 'count', 'qty'])) {
|
|
428
|
-
return Math.floor(Math.random() * 100) + 1;
|
|
429
|
-
}
|
|
430
|
-
if (this.matchesPattern(nameLower, ['status'])) {
|
|
431
|
-
return this.pickRandom(['active', 'inactive', 'pending', 'completed', 'cancelled']);
|
|
432
|
-
}
|
|
433
|
-
if (this.matchesPattern(nameLower, ['type', 'category'])) {
|
|
434
|
-
return this.pickRandom(['TypeA', 'TypeB', 'TypeC', 'TypeD']);
|
|
435
|
-
}
|
|
436
|
-
if (this.matchesPattern(nameLower, ['description', 'desc', 'notes', 'comment'])) {
|
|
437
|
-
return `Sample description for record ${index + 1}`;
|
|
438
|
-
}
|
|
439
|
-
// Fall back to type-based generation
|
|
440
|
-
return this.generateValueByType(type, index);
|
|
441
|
-
};
|
|
442
|
-
this.matchesPattern = (fieldName, patterns) => {
|
|
443
|
-
return patterns.some(pattern => fieldName.includes(pattern));
|
|
444
|
-
};
|
|
445
|
-
this.pickRandom = (arr) => {
|
|
446
|
-
return arr[Math.floor(Math.random() * arr.length)];
|
|
447
|
-
};
|
|
448
|
-
this.generateValueByType = (type, index) => {
|
|
449
|
-
switch (type) {
|
|
450
|
-
case 'string':
|
|
451
|
-
return `value_${index + 1}`;
|
|
452
|
-
case 'number':
|
|
453
|
-
return Math.floor(Math.random() * 1000);
|
|
454
|
-
case 'boolean':
|
|
455
|
-
return Math.random() > 0.5;
|
|
456
|
-
case 'datetime': {
|
|
457
|
-
const year = Math.floor(Math.random() * 5) + 2020;
|
|
458
|
-
const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
|
|
459
|
-
const day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
|
|
460
|
-
return `${year}-${month}-${day}`;
|
|
461
|
-
}
|
|
462
|
-
default:
|
|
463
|
-
return `value_${index + 1}`;
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
this.formatMockData = (records, fileType, delimiter) => {
|
|
467
|
-
switch (fileType) {
|
|
468
|
-
case 'JSON':
|
|
469
|
-
return JSON.stringify(records, null, 2);
|
|
470
|
-
case 'JSONL':
|
|
471
|
-
return records.map(r => JSON.stringify(r)).join('\n');
|
|
472
|
-
case 'CSV':
|
|
473
|
-
case 'TXT': {
|
|
474
|
-
const delim = delimiter || ',';
|
|
475
|
-
if (records.length === 0)
|
|
476
|
-
return '';
|
|
477
|
-
const headers = Object.keys(records[0]);
|
|
478
|
-
const headerLine = headers.join(delim);
|
|
479
|
-
const dataLines = records.map(record => headers.map(h => {
|
|
480
|
-
const val = record[h];
|
|
481
|
-
const strVal = val === null || val === undefined ? '' : String(val);
|
|
482
|
-
// Escape delimiter and quotes in values
|
|
483
|
-
if (strVal.includes(delim) || strVal.includes('"') || strVal.includes('\n')) {
|
|
484
|
-
return `"${strVal.replace(/"/g, '""')}"`;
|
|
485
|
-
}
|
|
486
|
-
return strVal;
|
|
487
|
-
}).join(delim));
|
|
488
|
-
return [headerLine, ...dataLines].join('\n');
|
|
489
|
-
}
|
|
490
|
-
default:
|
|
491
|
-
throw new Error(`Unsupported file type for mock data generation: ${fileType}`);
|
|
492
|
-
}
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
const DeveloperEngine = new DeveloperEngineClass();
|
|
497
|
-
exports.default = DeveloperEngine;
|