@nmakarov/cli-toolkit 0.2.0 → 0.3.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/README.md +112 -1
- package/dist/{filestore.cjs → filedatabase.cjs} +191 -37
- package/dist/filedatabase.cjs.map +1 -0
- package/dist/{filestore.js → filedatabase.js} +187 -33
- package/dist/filedatabase.js.map +1 -0
- package/dist/http-client.cjs +356 -0
- package/dist/http-client.cjs.map +1 -0
- package/dist/http-client.js +318 -0
- package/dist/http-client.js.map +1 -0
- package/dist/index.cjs +186 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +186 -32
- package/dist/index.js.map +1 -1
- package/dist/mock-server.cjs +1483 -0
- package/dist/mock-server.cjs.map +1 -0
- package/dist/mock-server.js +1445 -0
- package/dist/mock-server.js.map +1 -0
- package/package.json +30 -11
- package/dist/filestore.cjs.map +0 -1
- package/dist/filestore.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1777,7 +1777,7 @@ function organizeFooterMessages(messages) {
|
|
|
1777
1777
|
return lines;
|
|
1778
1778
|
}
|
|
1779
1779
|
|
|
1780
|
-
// src/
|
|
1780
|
+
// src/filedatabase/index.ts
|
|
1781
1781
|
var import_fs4 = __toESM(require("fs"), 1);
|
|
1782
1782
|
var import_path4 = __toESM(require("path"), 1);
|
|
1783
1783
|
|
|
@@ -1853,7 +1853,7 @@ function isTimestampFolder(folderName) {
|
|
|
1853
1853
|
return !isNaN(date.getTime()) && date.getTime() > 0;
|
|
1854
1854
|
}
|
|
1855
1855
|
|
|
1856
|
-
// src/
|
|
1856
|
+
// src/filedatabase/serializers.ts
|
|
1857
1857
|
function detectDataType(data) {
|
|
1858
1858
|
if (Array.isArray(data)) {
|
|
1859
1859
|
return "json-array";
|
|
@@ -1885,7 +1885,7 @@ function deserializeData(rawData, dataType) {
|
|
|
1885
1885
|
}
|
|
1886
1886
|
}
|
|
1887
1887
|
|
|
1888
|
-
// src/
|
|
1888
|
+
// src/filedatabase/synopsis-functions.ts
|
|
1889
1889
|
function defaultFileSynopsisFunction(fileEntry, data) {
|
|
1890
1890
|
if (!Array.isArray(data) || data.length === 0) {
|
|
1891
1891
|
return { ...fileEntry };
|
|
@@ -1953,7 +1953,7 @@ function defaultVersionSynopsisFunction(metadata) {
|
|
|
1953
1953
|
return result;
|
|
1954
1954
|
}
|
|
1955
1955
|
|
|
1956
|
-
// src/
|
|
1956
|
+
// src/filedatabase/index.ts
|
|
1957
1957
|
var FileDatabaseError = class extends Error {
|
|
1958
1958
|
constructor(message) {
|
|
1959
1959
|
super(message);
|
|
@@ -1964,6 +1964,7 @@ var FileDatabase = class {
|
|
|
1964
1964
|
basePath;
|
|
1965
1965
|
namespace;
|
|
1966
1966
|
tableName = null;
|
|
1967
|
+
versioned;
|
|
1967
1968
|
maxVersions;
|
|
1968
1969
|
pageSize;
|
|
1969
1970
|
useMetadata;
|
|
@@ -1987,6 +1988,7 @@ var FileDatabase = class {
|
|
|
1987
1988
|
this.basePath = config2.basePath;
|
|
1988
1989
|
this.namespace = config2.namespace || "default";
|
|
1989
1990
|
this.tableName = config2.tableName || null;
|
|
1991
|
+
this.versioned = config2.versioned ?? true;
|
|
1990
1992
|
this.maxVersions = config2.maxVersions || 5;
|
|
1991
1993
|
this.pageSize = config2.pageSize || 5e3;
|
|
1992
1994
|
this.useMetadata = config2.useMetadata !== false;
|
|
@@ -2009,14 +2011,21 @@ var FileDatabase = class {
|
|
|
2009
2011
|
};
|
|
2010
2012
|
}
|
|
2011
2013
|
/**
|
|
2012
|
-
* Get the destination path (basePath/namespace/tableName)
|
|
2014
|
+
* Get the destination path (basePath/namespace/tableName[/version])
|
|
2013
2015
|
*/
|
|
2014
|
-
getDestinationPath() {
|
|
2016
|
+
getDestinationPath(version) {
|
|
2015
2017
|
const errors = ["basePath", "namespace", "tableName"].filter((prop) => !this[prop]).map((prop) => `${prop} is not set`);
|
|
2016
2018
|
if (errors.length) {
|
|
2017
2019
|
throw new FileDatabaseError(`[FileDatabase] ${errors.join("; ")}`);
|
|
2018
2020
|
}
|
|
2019
|
-
|
|
2021
|
+
let parts = [this.basePath, this.namespace];
|
|
2022
|
+
if (this.tableName) {
|
|
2023
|
+
parts.push(...this.tableName.split("/"));
|
|
2024
|
+
}
|
|
2025
|
+
if (this.versioned && version) {
|
|
2026
|
+
parts.push(version);
|
|
2027
|
+
}
|
|
2028
|
+
return import_path4.default.resolve(...parts);
|
|
2020
2029
|
}
|
|
2021
2030
|
/**
|
|
2022
2031
|
* Set current version and version folder
|
|
@@ -2027,8 +2036,13 @@ var FileDatabase = class {
|
|
|
2027
2036
|
}
|
|
2028
2037
|
/**
|
|
2029
2038
|
* Create a new version folder with comprehensive timestamp logic
|
|
2039
|
+
* Only works in versioned mode
|
|
2030
2040
|
*/
|
|
2031
2041
|
async makeNewVersion() {
|
|
2042
|
+
if (!this.versioned) {
|
|
2043
|
+
throw new FileDatabaseError("makeNewVersion() only works in versioned mode");
|
|
2044
|
+
}
|
|
2045
|
+
this.metadata = this.getDefaultMetadata();
|
|
2032
2046
|
const existingVersions = await this.getVersions();
|
|
2033
2047
|
let versionName;
|
|
2034
2048
|
if (existingVersions.length > 0) {
|
|
@@ -2056,8 +2070,12 @@ var FileDatabase = class {
|
|
|
2056
2070
|
}
|
|
2057
2071
|
/**
|
|
2058
2072
|
* Get list of all versions (sorted chronologically)
|
|
2073
|
+
* Only works in versioned mode
|
|
2059
2074
|
*/
|
|
2060
2075
|
async getVersions() {
|
|
2076
|
+
if (!this.versioned) {
|
|
2077
|
+
return [];
|
|
2078
|
+
}
|
|
2061
2079
|
const destPath = this.getDestinationPath();
|
|
2062
2080
|
try {
|
|
2063
2081
|
await ensurePath(destPath);
|
|
@@ -2072,6 +2090,86 @@ var FileDatabase = class {
|
|
|
2072
2090
|
return [];
|
|
2073
2091
|
}
|
|
2074
2092
|
}
|
|
2093
|
+
/**
|
|
2094
|
+
* Get the latest version (most recent timestamp)
|
|
2095
|
+
* Only works in versioned mode
|
|
2096
|
+
* @returns Latest version string or null if no versions
|
|
2097
|
+
*/
|
|
2098
|
+
async getLatestVersion() {
|
|
2099
|
+
if (!this.versioned) {
|
|
2100
|
+
throw new FileDatabaseError("getLatestVersion() only works in versioned mode");
|
|
2101
|
+
}
|
|
2102
|
+
const versions = await this.getVersions();
|
|
2103
|
+
if (versions.length === 0) {
|
|
2104
|
+
return null;
|
|
2105
|
+
}
|
|
2106
|
+
return versions[versions.length - 1];
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Check if any data exists in this table
|
|
2110
|
+
* Works for both versioned and non-versioned modes
|
|
2111
|
+
* @returns true if data exists
|
|
2112
|
+
*/
|
|
2113
|
+
async hasData() {
|
|
2114
|
+
const tablePath = this.getDestinationPath();
|
|
2115
|
+
if (!import_fs4.default.existsSync(tablePath)) {
|
|
2116
|
+
return false;
|
|
2117
|
+
}
|
|
2118
|
+
if (this.versioned) {
|
|
2119
|
+
const versions = await this.getVersions();
|
|
2120
|
+
return versions.length > 0;
|
|
2121
|
+
} else {
|
|
2122
|
+
const items = await import_fs4.default.promises.readdir(tablePath);
|
|
2123
|
+
return items.some(
|
|
2124
|
+
(item) => item === "metadata.json" || item.match(/^\d{6}\.(json|txt|xml)$/) || item.endsWith(".json")
|
|
2125
|
+
);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
/**
|
|
2129
|
+
* Auto-detect the data format in this table
|
|
2130
|
+
* Used when reading existing data
|
|
2131
|
+
* @returns Format detection result
|
|
2132
|
+
*/
|
|
2133
|
+
async detectDataFormat() {
|
|
2134
|
+
const tablePath = this.getDestinationPath();
|
|
2135
|
+
if (!import_fs4.default.existsSync(tablePath)) {
|
|
2136
|
+
return { versioned: false, hasMetadata: false, dataType: null };
|
|
2137
|
+
}
|
|
2138
|
+
const items = await import_fs4.default.promises.readdir(tablePath);
|
|
2139
|
+
if (items.includes("metadata.json")) {
|
|
2140
|
+
const metadata = JSON.parse(
|
|
2141
|
+
await import_fs4.default.promises.readFile(import_path4.default.join(tablePath, "metadata.json"), "utf8")
|
|
2142
|
+
);
|
|
2143
|
+
return {
|
|
2144
|
+
versioned: false,
|
|
2145
|
+
hasMetadata: true,
|
|
2146
|
+
dataType: metadata.dataType || null
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
const versionFolders = items.filter((item) => {
|
|
2150
|
+
const itemPath = import_path4.default.join(tablePath, item);
|
|
2151
|
+
const stat = import_fs4.default.statSync(itemPath);
|
|
2152
|
+
return stat.isDirectory() && isTimestampFolder(item);
|
|
2153
|
+
});
|
|
2154
|
+
if (versionFolders.length > 0) {
|
|
2155
|
+
const latestVersion = versionFolders.sort().pop();
|
|
2156
|
+
const versionMetadataPath = import_path4.default.join(tablePath, latestVersion, "metadata.json");
|
|
2157
|
+
return {
|
|
2158
|
+
versioned: true,
|
|
2159
|
+
hasMetadata: import_fs4.default.existsSync(versionMetadataPath),
|
|
2160
|
+
dataType: null
|
|
2161
|
+
};
|
|
2162
|
+
}
|
|
2163
|
+
const dataFiles = items.filter((f) => f.match(/^\d{6}\.(json|txt|xml)$/));
|
|
2164
|
+
if (dataFiles.length > 0) {
|
|
2165
|
+
return {
|
|
2166
|
+
versioned: false,
|
|
2167
|
+
hasMetadata: false,
|
|
2168
|
+
dataType: null
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
return { versioned: false, hasMetadata: false, dataType: null };
|
|
2172
|
+
}
|
|
2075
2173
|
/**
|
|
2076
2174
|
* Load metadata from JSON file
|
|
2077
2175
|
*/
|
|
@@ -2215,11 +2313,19 @@ var FileDatabase = class {
|
|
|
2215
2313
|
* Save version metadata to file
|
|
2216
2314
|
*/
|
|
2217
2315
|
async saveVersionMetadata(metadata) {
|
|
2218
|
-
if (!this.useMetadata
|
|
2316
|
+
if (!this.useMetadata) {
|
|
2219
2317
|
return;
|
|
2220
2318
|
}
|
|
2221
2319
|
const metadataToSave = metadata || this.metadata;
|
|
2222
|
-
|
|
2320
|
+
let metadataFile;
|
|
2321
|
+
if (this.versioned) {
|
|
2322
|
+
if (!this.currentVersion) {
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
metadataFile = import_path4.default.join(this.getDestinationPath(), this.currentVersion, "metadata.json");
|
|
2326
|
+
} else {
|
|
2327
|
+
metadataFile = import_path4.default.join(this.getDestinationPath(), "metadata.json");
|
|
2328
|
+
}
|
|
2223
2329
|
await import_fs4.default.promises.writeFile(metadataFile, JSON.stringify(metadataToSave, null, 4), "utf8");
|
|
2224
2330
|
}
|
|
2225
2331
|
/**
|
|
@@ -2347,31 +2453,75 @@ var FileDatabase = class {
|
|
|
2347
2453
|
*/
|
|
2348
2454
|
async prepare({ write, read, version }) {
|
|
2349
2455
|
if (write) {
|
|
2350
|
-
if (this.
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2456
|
+
if (this.versioned) {
|
|
2457
|
+
if (this.currentVersion === null) {
|
|
2458
|
+
await this.makeNewVersion();
|
|
2459
|
+
this.metadata = this.getDefaultMetadata();
|
|
2460
|
+
this.metadata.version = this.currentVersion;
|
|
2461
|
+
this.makeNewFile();
|
|
2462
|
+
} else {
|
|
2463
|
+
if (!this.metadata.files.length) {
|
|
2464
|
+
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2355
2467
|
} else {
|
|
2356
|
-
|
|
2357
|
-
|
|
2468
|
+
await ensurePath(this.getDestinationPath());
|
|
2469
|
+
if (this.useMetadata === true) {
|
|
2470
|
+
const metadataPath = import_path4.default.join(this.getDestinationPath(), "metadata.json");
|
|
2471
|
+
if (import_fs4.default.existsSync(metadataPath)) {
|
|
2472
|
+
try {
|
|
2473
|
+
const rawData = await import_fs4.default.promises.readFile(metadataPath, "utf8");
|
|
2474
|
+
this.metadata = JSON.parse(rawData);
|
|
2475
|
+
} catch (e) {
|
|
2476
|
+
this.metadata = this.getDefaultMetadata();
|
|
2477
|
+
}
|
|
2478
|
+
} else {
|
|
2479
|
+
this.metadata = this.getDefaultMetadata();
|
|
2480
|
+
this.makeNewFile();
|
|
2481
|
+
}
|
|
2482
|
+
} else {
|
|
2483
|
+
this.metadata = this.getDefaultMetadata();
|
|
2484
|
+
this.makeNewFile();
|
|
2358
2485
|
}
|
|
2359
2486
|
}
|
|
2360
2487
|
} else if (read) {
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
if (
|
|
2367
|
-
|
|
2488
|
+
if (this.versioned) {
|
|
2489
|
+
const versions = await this.getVersions();
|
|
2490
|
+
if (versions.length === 0) {
|
|
2491
|
+
throw new FileDatabaseError("[FileDatabase] No versions found, cannot read");
|
|
2492
|
+
}
|
|
2493
|
+
if (version) {
|
|
2494
|
+
if (!versions.includes(version)) {
|
|
2495
|
+
throw new FileDatabaseError(`[FileDatabase] Version "${version}" not found`);
|
|
2496
|
+
}
|
|
2497
|
+
await this.setCurrentVersion(version);
|
|
2498
|
+
} else {
|
|
2499
|
+
await this.setCurrentVersion(versions[versions.length - 1]);
|
|
2500
|
+
}
|
|
2501
|
+
if (!this.metadata.files.length) {
|
|
2502
|
+
this.metadata = await this.figureMetadata(this.currentVersion);
|
|
2368
2503
|
}
|
|
2369
|
-
await this.setCurrentVersion(version);
|
|
2370
2504
|
} else {
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2505
|
+
this.currentVersion = null;
|
|
2506
|
+
if (this.useMetadata === void 0) {
|
|
2507
|
+
const format = await this.detectDataFormat();
|
|
2508
|
+
this.useMetadata = format.hasMetadata;
|
|
2509
|
+
}
|
|
2510
|
+
if (this.useMetadata) {
|
|
2511
|
+
const metadataPath = import_path4.default.join(this.getDestinationPath(), "metadata.json");
|
|
2512
|
+
if (import_fs4.default.existsSync(metadataPath)) {
|
|
2513
|
+
try {
|
|
2514
|
+
const rawData = await import_fs4.default.promises.readFile(metadataPath, "utf8");
|
|
2515
|
+
this.metadata = JSON.parse(rawData);
|
|
2516
|
+
} catch (e) {
|
|
2517
|
+
throw new FileDatabaseError(`Failed to read metadata: ${e.message}`);
|
|
2518
|
+
}
|
|
2519
|
+
} else {
|
|
2520
|
+
throw new FileDatabaseError("[FileDatabase] No metadata found in non-versioned mode");
|
|
2521
|
+
}
|
|
2522
|
+
} else {
|
|
2523
|
+
this.metadata = await this.figureMetadataFromVersionFiles("");
|
|
2524
|
+
}
|
|
2375
2525
|
}
|
|
2376
2526
|
}
|
|
2377
2527
|
}
|
|
@@ -2379,6 +2529,9 @@ var FileDatabase = class {
|
|
|
2379
2529
|
* Write data to the file database
|
|
2380
2530
|
*/
|
|
2381
2531
|
async write(data, options = {}) {
|
|
2532
|
+
if (options.forceNewVersion && !this.versioned) {
|
|
2533
|
+
throw new FileDatabaseError("Cannot use forceNewVersion in non-versioned mode");
|
|
2534
|
+
}
|
|
2382
2535
|
await this.prepare({ write: true });
|
|
2383
2536
|
if (options.forceNewVersion) {
|
|
2384
2537
|
await this.makeNewVersion();
|
|
@@ -2387,11 +2540,12 @@ var FileDatabase = class {
|
|
|
2387
2540
|
this.makeNewFile();
|
|
2388
2541
|
}
|
|
2389
2542
|
let { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data);
|
|
2390
|
-
|
|
2543
|
+
const destPath = this.getDestinationPath(this.currentVersion || void 0);
|
|
2544
|
+
await this.safeWrite(import_path4.default.join(destPath, fileName), dataToWrite);
|
|
2391
2545
|
this.updateMetadata(dataToWrite, fileName);
|
|
2392
2546
|
while (dataLeftOver && dataLeftOver.length > 0) {
|
|
2393
2547
|
const writeContext = this.figureOutDataAndFileToWrite(dataLeftOver);
|
|
2394
|
-
await this.safeWrite(import_path4.default.join(
|
|
2548
|
+
await this.safeWrite(import_path4.default.join(destPath, writeContext.fileName), writeContext.dataToWrite);
|
|
2395
2549
|
this.updateMetadata(writeContext.dataToWrite, writeContext.fileName);
|
|
2396
2550
|
dataLeftOver = writeContext.dataLeftOver;
|
|
2397
2551
|
}
|
|
@@ -2409,7 +2563,7 @@ var FileDatabase = class {
|
|
|
2409
2563
|
const isNonPaginatedData = this.metadata.dataType === "text" || this.metadata.dataType === "xml" || this.metadata.dataType === "json-object";
|
|
2410
2564
|
if (isNonPaginatedData) {
|
|
2411
2565
|
const file = this.metadata.files[0];
|
|
2412
|
-
const filePath = import_path4.default.join(this.getDestinationPath(
|
|
2566
|
+
const filePath = import_path4.default.join(this.getDestinationPath(this.currentVersion || void 0), file.fileName);
|
|
2413
2567
|
try {
|
|
2414
2568
|
const rawData = await import_fs4.default.promises.readFile(filePath, "utf8");
|
|
2415
2569
|
return deserializeData(rawData, this.metadata.dataType);
|
|
@@ -2447,7 +2601,7 @@ var FileDatabase = class {
|
|
|
2447
2601
|
let cumulativeRecords = currentFileOffset;
|
|
2448
2602
|
for (let i = currentFileIndex; i < this.metadata.files.length && recordsRead < effectivePageSize; i++) {
|
|
2449
2603
|
const file = this.metadata.files[i];
|
|
2450
|
-
const filePath = import_path4.default.join(this.getDestinationPath(
|
|
2604
|
+
const filePath = import_path4.default.join(this.getDestinationPath(this.currentVersion || void 0), file.fileName);
|
|
2451
2605
|
try {
|
|
2452
2606
|
const rawData = await import_fs4.default.promises.readFile(filePath, "utf8");
|
|
2453
2607
|
const fileData = deserializeData(rawData, this.metadata.dataType);
|