json-fs-query 3.4.0 → 3.5.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 +97 -1
- package/browser/jsonFsQuery.js +47 -5
- package/browser/jsonFsQuery.min.js +1 -1
- package/dist/cli/chooseDatabase.d.ts +13 -0
- package/dist/cli/chooseDatabase.js +37 -0
- package/dist/cli/chooseDatabase.min.js +1 -0
- package/dist/cli/formatCsv.d.ts +9 -0
- package/dist/cli/formatCsv.js +53 -0
- package/dist/cli/formatCsv.min.js +1 -0
- package/dist/cli/formatResult.d.ts +30 -0
- package/dist/cli/formatResult.js +139 -0
- package/dist/cli/formatResult.min.js +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +52 -0
- package/dist/cli/index.min.js +2 -0
- package/dist/cli/interactive.d.ts +15 -0
- package/dist/cli/interactive.js +257 -0
- package/dist/cli/interactive.min.js +1 -0
- package/dist/cli/output.d.ts +31 -0
- package/dist/cli/output.js +64 -0
- package/dist/cli/output.min.js +1 -0
- package/dist/cli/runDirect.d.ts +11 -0
- package/dist/cli/runDirect.js +28 -0
- package/dist/cli/runDirect.min.js +1 -0
- package/dist/data/introspect/alter.js +2 -5
- package/dist/data/introspect/alter.min.js +1 -1
- package/dist/data/introspect/create.js +15 -0
- package/dist/data/introspect/create.min.js +1 -1
- package/dist/data/introspect/describe.js +2 -0
- package/dist/data/introspect/describe.min.js +1 -1
- package/dist/data/introspect/drop.js +3 -0
- package/dist/data/introspect/drop.min.js +1 -1
- package/dist/data/utilities/assertValidName.d.ts +14 -0
- package/dist/data/utilities/assertValidName.js +34 -0
- package/dist/data/utilities/assertValidName.min.js +1 -0
- package/package.json +7 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Markers } from './formatResult';
|
|
2
|
+
export declare const outputFormats: readonly ["table", "json", "csv", "dump"];
|
|
3
|
+
export type OutputFormat = typeof outputFormats[number];
|
|
4
|
+
/**
|
|
5
|
+
* How results print: the format (-o / /output), and the table's markers for missing and null (--missing, --null, or
|
|
6
|
+
* /missing and /null at the prompt). The prompt changes these during a session, so one object is shared.
|
|
7
|
+
*/
|
|
8
|
+
export type OutputSettings = {
|
|
9
|
+
format: OutputFormat;
|
|
10
|
+
markers: Partial<Markers>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Refuse a format that can't print yet, before the query runs (so `-o dump -q "insert ..."` doesn't insert and then
|
|
14
|
+
* fail to show it).
|
|
15
|
+
* @param settings
|
|
16
|
+
* @throws {Error} For dump, which needs the record's name and query() results don't carry it.
|
|
17
|
+
*/
|
|
18
|
+
export declare const assertPrintable: (settings: OutputSettings) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Print each query's result in the chosen format, each followed by a blank line (as psql does) so results stay apart
|
|
21
|
+
* when several print in a row. Several JSON results are one JSON value each, which jq reads as a stream.
|
|
22
|
+
* @param {Array<*>} results One result per query
|
|
23
|
+
* @param settings
|
|
24
|
+
*/
|
|
25
|
+
export declare const printResults: (results: Array<unknown>, settings: OutputSettings) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Print an error in red, without its stack.
|
|
28
|
+
* @param {*} error
|
|
29
|
+
*/
|
|
30
|
+
export declare const printError: (error: unknown) => void;
|
|
31
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.printResults = exports.printError = exports.outputFormats = exports.assertPrintable = void 0;
|
|
7
|
+
require("core-js/modules/es.json.stringify.js");
|
|
8
|
+
var _util = require("util");
|
|
9
|
+
var _formatCsv = require("./formatCsv");
|
|
10
|
+
var _formatResult = require("./formatResult");
|
|
11
|
+
const outputFormats = exports.outputFormats = ['table', 'json', 'csv', 'dump'];
|
|
12
|
+
// Dim the markers only on a terminal, and never with NO_COLOR set (https://no-color.org)
|
|
13
|
+
const color = () => Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
14
|
+
/**
|
|
15
|
+
* Refuse a format that can't print yet, before the query runs (so `-o dump -q "insert ..."` doesn't insert and then
|
|
16
|
+
* fail to show it).
|
|
17
|
+
* @param settings
|
|
18
|
+
* @throws {Error} For dump, which needs the record's name and query() results don't carry it.
|
|
19
|
+
*/
|
|
20
|
+
const assertPrintable = settings => {
|
|
21
|
+
if (settings.format === 'dump') {
|
|
22
|
+
throw new Error('dump output isn\'t available yet (it needs the record\'s name, which results don\'t carry): use table, json or csv');
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Format one result in the chosen format.
|
|
27
|
+
* @param result
|
|
28
|
+
* @param settings
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
exports.assertPrintable = assertPrintable;
|
|
32
|
+
const format = (result, settings) => {
|
|
33
|
+
switch (settings.format) {
|
|
34
|
+
case 'json':
|
|
35
|
+
return JSON.stringify(result, null, 2);
|
|
36
|
+
case 'csv':
|
|
37
|
+
return (0, _formatCsv.formatCsv)(result);
|
|
38
|
+
default:
|
|
39
|
+
return (0, _formatResult.formatResult)(result, {
|
|
40
|
+
color: color(),
|
|
41
|
+
markers: settings.markers
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Print each query's result in the chosen format, each followed by a blank line (as psql does) so results stay apart
|
|
47
|
+
* when several print in a row. Several JSON results are one JSON value each, which jq reads as a stream.
|
|
48
|
+
* @param {Array<*>} results One result per query
|
|
49
|
+
* @param settings
|
|
50
|
+
*/
|
|
51
|
+
const printResults = (results, settings) => {
|
|
52
|
+
for (const result of results) {
|
|
53
|
+
console.log(`${format(result, settings)}\n`);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Print an error in red, without its stack.
|
|
58
|
+
* @param {*} error
|
|
59
|
+
*/
|
|
60
|
+
exports.printResults = printResults;
|
|
61
|
+
const printError = error => {
|
|
62
|
+
console.error((0, _util.styleText)('red', `Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
63
|
+
};
|
|
64
|
+
exports.printError = printError;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.printResults=exports.printError=exports.outputFormats=exports.assertPrintable=void 0,require("core-js/modules/es.json.stringify.js");var _util=require("util"),_formatCsv=require("./formatCsv"),_formatResult=require("./formatResult");const outputFormats=exports.outputFormats=["table","json","csv","dump"],color=()=>Boolean(process.stdout.isTTY)&&!process.env.NO_COLOR,assertPrintable=r=>{if("dump"===r.format)throw new Error("dump output isn't available yet (it needs the record's name, which results don't carry): use table, json or csv")};exports.assertPrintable=assertPrintable;const format=(r,t)=>{switch(t.format){case"json":return JSON.stringify(r,null,2);case"csv":return(0,_formatCsv.formatCsv)(r);default:return(0,_formatResult.formatResult)(r,{color:color(),markers:t.markers})}},printResults=(r,t)=>{for(const s of r)console.log(`${format(s,t)}\n`)};exports.printResults=printResults;const printError=r=>{console.error((0,_util.styleText)("red",`Error: ${r instanceof Error?r.message:String(r)}`))};exports.printError=printError;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DatabaseOptions } from './chooseDatabase';
|
|
2
|
+
import { OutputSettings } from './output';
|
|
3
|
+
/**
|
|
4
|
+
* Run one query string (one or more queries) without opening the prompt: print each result, or the error.
|
|
5
|
+
* @param queryString What `-q` / `--query` was given
|
|
6
|
+
* @param options The database options
|
|
7
|
+
* @param settings The output format and markers
|
|
8
|
+
* @returns {Promise<number>} The exit code: 0, or 1 when choosing the database or the query failed.
|
|
9
|
+
*/
|
|
10
|
+
export declare const runDirect: (queryString: string, options: DatabaseOptions, settings: OutputSettings) => Promise<number>;
|
|
11
|
+
//# sourceMappingURL=runDirect.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.runDirect = void 0;
|
|
7
|
+
var _query = require("../data/queries/query");
|
|
8
|
+
var _chooseDatabase = require("./chooseDatabase");
|
|
9
|
+
var _output = require("./output");
|
|
10
|
+
/**
|
|
11
|
+
* Run one query string (one or more queries) without opening the prompt: print each result, or the error.
|
|
12
|
+
* @param queryString What `-q` / `--query` was given
|
|
13
|
+
* @param options The database options
|
|
14
|
+
* @param settings The output format and markers
|
|
15
|
+
* @returns {Promise<number>} The exit code: 0, or 1 when choosing the database or the query failed.
|
|
16
|
+
*/
|
|
17
|
+
const runDirect = async (queryString, options, settings) => {
|
|
18
|
+
try {
|
|
19
|
+
(0, _output.assertPrintable)(settings);
|
|
20
|
+
await (0, _chooseDatabase.chooseDatabase)(options);
|
|
21
|
+
(0, _output.printResults)(await (0, _query.query)(queryString), settings);
|
|
22
|
+
return 0;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
(0, _output.printError)(error);
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.runDirect = runDirect;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.runDirect=void 0;var _query=require("../data/queries/query"),_chooseDatabase=require("./chooseDatabase"),_output=require("./output");const runDirect=async(e,r,t)=>{try{return(0,_output.assertPrintable)(t),await(0,_chooseDatabase.chooseDatabase)(r),(0,_output.printResults)(await(0,_query.query)(e),t),0}catch(e){return(0,_output.printError)(e),1}};exports.runDirect=runDirect;
|
|
@@ -5,10 +5,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.alter = void 0;
|
|
7
7
|
require("core-js/modules/es.array.includes.js");
|
|
8
|
-
require("core-js/modules/es.json.stringify.js");
|
|
9
8
|
var _field = require("./definitions/field");
|
|
10
9
|
var _create = require("./create");
|
|
11
10
|
var _parseCreate = require("../utilities/parsers/parseCreate");
|
|
11
|
+
var _assertValidName = require("../utilities/assertValidName");
|
|
12
12
|
var _retrieveRecord = require("../utilities/retrieveRecord");
|
|
13
13
|
var _retrieveFiles = require("../utilities/retrieveFiles");
|
|
14
14
|
var _retrieveRecords = require("../utilities/retrieveRecords");
|
|
@@ -278,10 +278,7 @@ const alter = async (entity, change) => {
|
|
|
278
278
|
// The same names the query language allows: a name becomes a key in every row and part of index file paths, so
|
|
279
279
|
// one like `../x` from a direct call must not get that far
|
|
280
280
|
const names = [entity, ...('field' in change ? [change.field?.name] : []), ...('name' in change ? [change.name] : []), ...('to' in change ? [change.to] : [])];
|
|
281
|
-
|
|
282
|
-
if (invalid !== -1) {
|
|
283
|
-
throw new Error(`Invalid name ${JSON.stringify(names[invalid]) ?? 'undefined'} in alter: names use letters, digits, _ and - only`);
|
|
284
|
-
}
|
|
281
|
+
names.forEach(name => (0, _assertValidName.assertValidName)(name, 'alter'));
|
|
285
282
|
const record = await (0, _retrieveRecord.retrieveRecord)(entity);
|
|
286
283
|
switch (change.action) {
|
|
287
284
|
case 'add':
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.alter=void 0,require("core-js/modules/es.array.includes.js")
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.alter=void 0,require("core-js/modules/es.array.includes.js");var _field=require("./definitions/field"),_create=require("./create"),_parseCreate=require("../utilities/parsers/parseCreate"),_assertValidName=require("../utilities/assertValidName"),_retrieveRecord=require("../utilities/retrieveRecord"),_retrieveFiles=require("../utilities/retrieveFiles"),_retrieveRecords=require("../utilities/retrieveRecords"),_knownFields=require("../utilities/knownFields"),_packedTable=require("../utilities/packedTable"),_requireNode=require("../utilities/requireNode"),_showRecords=require("../queries/showRecords"),_updateEntity=require("../queries/updateEntity"),_typeCheck=require("./policies/typeCheck"),_storage=require("../storage");const rowsOf=async e=>(await(0,_retrieveFiles.retrieveFiles)(e.entries.map((t=>`${e.path}/${t}`)),_retrieveRecords.retrieveEntry)).map(((t,a)=>({fileName:e.entries[a],data:t}))),has=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),keysUsing=(e,t)=>e.keys.filter((e=>e.fields.includes(t))),describeKey=e=>`${e.type} key (${e.fields.join(", ")})`,referencesTo=async(e,t)=>{const a=[];for(const i of await(0,_showRecords.showRecords)()){const r=await(0,_retrieveRecord.retrieveRecord)(i);for(const o of r.keys)"foreign"===o.type&&(o.references||[]).some((a=>a.fields.includes(`${e}.${t}`)))&&a.push({entity:i,record:r,key:o})}return a},writeRecord=async(e,t)=>{(0,_create.assertValidDefinition)(e,t),await(0,_storage.writeJson)(`${e}.json`,t)},setField=async(e,t,a,i,r)=>{if(a.length)if(await(0,_packedTable.discardPacked)(t.path),keysUsing(t,i).length&&"function"!=typeof r){if(!t.keys.some((e=>"primary"===e.type)))throw new Error(`Can't change "${i}" on ${e}'s rows: a key uses it, and rows are found by the primary key, which ${e} doesn't have`);await(0,_updateEntity.updateEntity)(e,{[i]:void 0===r?null:r},{[e]:a.map((e=>e.data))})}else for(const e of a){const a=Object.assign({},e.data),o="function"==typeof r?r(e):r;void 0===o?delete a[i]:a[i]=o,await(0,_storage.writeJson)(`${t.path}/${e.fileName}`,a)}},addField=async(e,t,a)=>{if(t.definition.some((e=>e.name===a.name)))throw new Error(`Field "${a.name}" already exists on ${e}`);const i={...t,definition:[...t.definition,a]},r=await rowsOf(t);for(const t of r.filter((e=>has(e.data,a.name))))try{(0,_typeCheck.typeCheck)(a.type,t.data[a.name],a.nullable)}catch(i){throw new Error(`Can't add "${a.name}" to ${e}: row ${t.fileName} already holds a value for it that doesn't fit (${i.message})`)}const o=r.filter((e=>!has(e.data,a.name))),n=has(a,"default");if(o.length&&!a.optional&&!n&&!a.autoGenerate)throw new Error(`Can't add required field "${a.name}" to ${e}: its ${o.length} row(s) have no value for it (give it a default, or make it optional)`);return await writeRecord(e,i),a.autoGenerate?await setField(e,i,o,a.name,(()=>crypto.randomUUID())):n&&await setField(e,i,o,a.name,a.default),i},dropField=async(e,t,a)=>{(0,_knownFields.assertFieldsDefined)(e,t,[a],"alter");const i=keysUsing(t,a)[0];if(i)throw new Error(`Can't drop "${a}" from ${e}: its ${describeKey(i)} uses it`);const r=(await referencesTo(e,a))[0];if(r)throw new Error(`Can't drop "${a}" from ${e}: ${r.entity}'s foreign key "${r.key.fields[0]}" references it`);const o={...t,definition:t.definition.filter((e=>e.name!==a))};return await writeRecord(e,o),await setField(e,o,(await rowsOf(t)).filter((e=>has(e.data,a))),a,void 0),o},renameField=async(e,t,a,i)=>{if((0,_knownFields.assertFieldsDefined)(e,t,[a],"alter"),t.definition.some((e=>e.name===i)))throw new Error(`Can't rename "${a}" on ${e} to "${i}": a field of that name already exists`);const r=await referencesTo(e,a),o=t.keys.map((e=>{if(!e.fields.includes(a))return e;const t=e.fields.map((e=>e===a?i:e)),r=e.lookup&&e.lookup===(0,_parseCreate.keyLookup)(e.type,e.fields)?(0,_parseCreate.keyLookup)(e.type,t):e.lookup;return{...e,fields:t,lookup:r}})),n=t=>"foreign"!==t.type?t:{...t,references:(t.references||[]).map((t=>({...t,fields:t.fields.map((t=>t===`${e}.${a}`?`${e}.${i}`:t))})))},s={...t,definition:t.definition.map((e=>e.name===a?{...e,name:i}:e)),keys:o.map(n)};for(const[e,a]of t.keys.entries()){const i=o[e].lookup;a.lookup&&i!==a.lookup&&(0,_storage.fileExists)(`__indexes/${t.path}/${a.lookup}`)&&(await(0,_storage.writeJson)(`__indexes/${t.path}/${i}`,await(0,_storage.readJson)(`__indexes/${t.path}/${a.lookup}`)),await(0,_storage.removeFile)(`__indexes/${t.path}/${a.lookup}`))}await writeRecord(e,s);for(const t of r.filter((t=>t.entity!==e)))await(0,_storage.writeJson)(`${t.entity}.json`,{...t.record,keys:t.record.keys.map(n)});const d=(await rowsOf(t)).filter((e=>has(e.data,a)));d.length&&await(0,_packedTable.discardPacked)(t.path);for(const e of d){const r=Object.assign({},e.data);r[i]=r[a],delete r[a],await(0,_storage.writeJson)(`${t.path}/${e.fileName}`,r)}return s},modifyField=async(e,t,a)=>{(0,_knownFields.assertFieldsDefined)(e,t,[a.name],"alter");const i=t.definition.find((e=>e.name===a.name));if(i.type!==a.type)throw new Error(`Can't change the type of "${a.name}" on ${e} (${i.type} to ${a.type}): only optional, null and default can change`);if(Boolean(i.autoGenerate)!==Boolean(a.autoGenerate))throw new Error(`Can't change whether "${a.name}" on ${e} is auto: only optional, null and default can change`);const r={...t,definition:t.definition.map((e=>e.name===a.name?a:e))},o=await rowsOf(t),n=has(a,"default"),s=a.nullable?[]:o.filter((e=>null===e.data[a.name])),d=a.optional?[]:o.filter((e=>!has(e.data,a.name))),l=a.optional?[]:s;if((d.length||l.length)&&!n)throw d.length?new Error(`Can't make "${a.name}" required on ${e}: ${d.length} row(s) have no value for it (give it a default to fill them in)`):new Error(`Can't make "${a.name}" not null on ${e}: ${l.length} row(s) hold null (give it a default to fill them in, or make it optional)`);return await writeRecord(e,r),n&&await setField(e,r,[...d,...l],a.name,a.default),a.optional&&s.length&&await setField(e,r,s,a.name,void 0),r},alter=async(e,t)=>{if((0,_requireNode.requireNode)("alter"),"__RECORDS"===e)throw new Error("__RECORDS is reserved for the list of records");[e,..."field"in t?[t.field?.name]:[],..."name"in t?[t.name]:[],..."to"in t?[t.to]:[]].forEach((e=>(0,_assertValidName.assertValidName)(e,"alter")));const a=await(0,_retrieveRecord.retrieveRecord)(e);switch(t.action){case"add":return(await addField(e,a,(0,_field.field)(t.field))).definition;case"drop":return(await dropField(e,a,t.name)).definition;case"rename":return(await renameField(e,a,t.name,t.to)).definition;case"modify":return(await modifyField(e,a,(0,_field.field)(t.field))).definition;default:throw new Error(`Unknown alter action: ${t.action}`)}};exports.alter=alter;
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.create = exports.assertValidDefinition = void 0;
|
|
7
7
|
require("core-js/modules/es.array.includes.js");
|
|
8
|
+
var _assertValidName = require("../utilities/assertValidName");
|
|
8
9
|
var _record = require("./definitions/record");
|
|
9
10
|
var _registerRecord = require("../utilities/registerRecord");
|
|
10
11
|
var _storage = require("../storage");
|
|
@@ -80,6 +81,20 @@ const create = async (recordName, definition = [], keys = []) => {
|
|
|
80
81
|
if (recordName === '__RECORDS') {
|
|
81
82
|
return null;
|
|
82
83
|
}
|
|
84
|
+
// Names become file paths and row keys: refuse what the query language would (e.g. `../x` from a direct call)
|
|
85
|
+
(0, _assertValidName.assertValidName)(recordName, 'create()');
|
|
86
|
+
for (const field of definition) {
|
|
87
|
+
(0, _assertValidName.assertValidName)(field?.name, `create() of ${recordName}`);
|
|
88
|
+
}
|
|
89
|
+
for (const key of keys) {
|
|
90
|
+
(key?.fields ?? []).forEach(name => (0, _assertValidName.assertValidName)(name, `create() of ${recordName}`));
|
|
91
|
+
(0, _assertValidName.assertValidLookup)(key?.lookup, `create() of ${recordName}`);
|
|
92
|
+
for (const reference of key?.references ?? []) {
|
|
93
|
+
// A reference names the other record's file (`people.json`) and its fields as `people._id`
|
|
94
|
+
(0, _assertValidName.assertValidLookup)(reference?.lookup ?? null, `create() of ${recordName}`);
|
|
95
|
+
(reference?.fields ?? []).forEach(name => String(name).split('.').forEach(part => (0, _assertValidName.assertValidName)(part, `create() of ${recordName}`)));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
83
98
|
if ((0, _storage.fileExists)(`${recordName}.json`)) {
|
|
84
99
|
return null;
|
|
85
100
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.create=exports.assertValidDefinition=void 0,require("core-js/modules/es.array.includes.js");var _record=require("./definitions/record"),_registerRecord=require("../utilities/registerRecord"),_storage=require("../storage"),_requireNode=require("../utilities/requireNode"),_typeCheck=require("./policies/typeCheck");const knownKeyProperties=["type","fields","lookup","references"],assertKnownKeyProperties=(e,r)=>{for(const
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.create=exports.assertValidDefinition=void 0,require("core-js/modules/es.array.includes.js");var _assertValidName=require("../utilities/assertValidName"),_record=require("./definitions/record"),_registerRecord=require("../utilities/registerRecord"),_storage=require("../storage"),_requireNode=require("../utilities/requireNode"),_typeCheck=require("./policies/typeCheck");const knownKeyProperties=["type","fields","lookup","references"],assertKnownKeyProperties=(e,r)=>{for(const t of r)for(const r of Object.keys(t))if(!knownKeyProperties.includes(r))throw new Error(`Unknown property "${r}" on ${t.type??"index"} key of ${e}; must be ${knownKeyProperties.join(", ")}`)},assertValidDefinition=(e,r)=>{for(const t of r.definition)if(Object.prototype.hasOwnProperty.call(t,"default"))try{(0,_typeCheck.typeCheck)(t.type,t.default,t.nullable)}catch(r){throw new Error(`Invalid default for field "${t.name}" of ${e}: ${r.message}`)}for(const t of r.keys){if(t.fields.length<1)throw new Error(`The ${t.type} key of ${e} must have at least one field`);for(const o of t.fields)if(!r.definition.some((e=>e.name===o)))throw new Error(`The ${t.type} key of ${e} names field "${o}", which isn't in the definition`)}const t=r.keys.find((e=>"primary"===e.type));for(const o of t?t.fields:[]){const t=r.definition.find((e=>e.name===o));if(t&&t.nullable)throw new Error(`Primary key field "${o}" of ${e} can't be nullable`);if(t&&t.optional&&!t.autoGenerate)throw new Error(`Primary key field "${o}" of ${e} can't be optional`)}};exports.assertValidDefinition=assertValidDefinition;const create=async(e,r=[],t=[])=>{if((0,_requireNode.requireNode)("create()"),"__RECORDS"===e)return null;(0,_assertValidName.assertValidName)(e,"create()");for(const t of r)(0,_assertValidName.assertValidName)(t?.name,`create() of ${e}`);for(const r of t){(r?.fields??[]).forEach((r=>(0,_assertValidName.assertValidName)(r,`create() of ${e}`))),(0,_assertValidName.assertValidLookup)(r?.lookup,`create() of ${e}`);for(const t of r?.references??[])(0,_assertValidName.assertValidLookup)(t?.lookup??null,`create() of ${e}`),(t?.fields??[]).forEach((r=>String(r).split(".").forEach((r=>(0,_assertValidName.assertValidName)(r,`create() of ${e}`)))))}if((0,_storage.fileExists)(`${e}.json`))return null;assertKnownKeyProperties(e,t);const o=(0,_record.record)({path:e,definition:r,keys:t});assertValidDefinition(e,o),await(0,_storage.makeDirectory)(),await(0,_storage.writeJson)(`${e}.json`,o),await(0,_storage.makeDirectory)(e);const i=o.keys.filter((e=>e.lookup));if(i.length){await(0,_storage.makeDirectory)(`__indexes/${e}`);for(const r of i)await(0,_storage.writeJson)(`__indexes/${e}/${r.lookup}`,[])}return await(0,_registerRecord.registerRecord)(e),o};exports.create=create;
|
|
@@ -5,7 +5,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.describe = void 0;
|
|
7
7
|
var _retrieveFile = require("../utilities/retrieveFile");
|
|
8
|
+
var _assertValidName = require("../utilities/assertValidName");
|
|
8
9
|
const describe = async (record = '__RECORDS') => {
|
|
10
|
+
(0, _assertValidName.assertValidName)(record, 'describe()');
|
|
9
11
|
const recordFile = await (0, _retrieveFile.retrieveFile)(`${record}.json`);
|
|
10
12
|
if (record === '__RECORDS') {
|
|
11
13
|
return recordFile.entries;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.describe=void 0;var _retrieveFile=require("../utilities/retrieveFile");const describe=async(e="__RECORDS")=>{const i=await(0,_retrieveFile.retrieveFile)(`${e}.json`);return"__RECORDS"===e?i.entries:i.definition};exports.describe=describe;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.describe=void 0;var _retrieveFile=require("../utilities/retrieveFile"),_assertValidName=require("../utilities/assertValidName");const describe=async(e="__RECORDS")=>{(0,_assertValidName.assertValidName)(e,"describe()");const i=await(0,_retrieveFile.retrieveFile)(`${e}.json`);return"__RECORDS"===e?i.entries:i.definition};exports.describe=describe;
|
|
@@ -9,11 +9,14 @@ var _deregisterRecord = require("../utilities/deregisterRecord");
|
|
|
9
9
|
var _requireNode = require("../utilities/requireNode");
|
|
10
10
|
var _storage = require("../storage");
|
|
11
11
|
var _packedTable = require("../utilities/packedTable");
|
|
12
|
+
var _assertValidName = require("../utilities/assertValidName");
|
|
12
13
|
const drop = async record => {
|
|
13
14
|
(0, _requireNode.requireNode)('drop()');
|
|
14
15
|
if (record === '__RECORDS') {
|
|
15
16
|
return null;
|
|
16
17
|
}
|
|
18
|
+
// drop() removes files: a name like `../x` from a direct call must never reach storage
|
|
19
|
+
(0, _assertValidName.assertValidName)(record, 'drop()');
|
|
17
20
|
const recordFile = await (0, _retrieveFile.retrieveFile)(`${record}.json`);
|
|
18
21
|
for (const file of recordFile.entries) {
|
|
19
22
|
await (0, _storage.removeFile)(`${recordFile.path}/${file}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.drop=void 0;var _retrieveFile=require("../utilities/retrieveFile"),_deregisterRecord=require("../utilities/deregisterRecord"),_requireNode=require("../utilities/requireNode"),_storage=require("../storage"),_packedTable=require("../utilities/packedTable");const drop=async e=>{if((0,_requireNode.requireNode)("drop()"),"__RECORDS"===e)return null;const r=await(0,_retrieveFile.retrieveFile)(`${e}.json`);for(const e of r.entries)await(0,_storage.removeFile)(`${r.path}/${e}`);await(0,_storage.removeDirectory)(r.path),await(0,_storage.removeDirectory)(`__indexes/${r.path}`),await(0,_packedTable.discardPacked)(r.path),await(0,_storage.removeFile)(`${e}.json`),await(0,_deregisterRecord.deregisterRecord)(e)};exports.drop=drop;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.drop=void 0;var _retrieveFile=require("../utilities/retrieveFile"),_deregisterRecord=require("../utilities/deregisterRecord"),_requireNode=require("../utilities/requireNode"),_storage=require("../storage"),_packedTable=require("../utilities/packedTable"),_assertValidName=require("../utilities/assertValidName");const drop=async e=>{if((0,_requireNode.requireNode)("drop()"),"__RECORDS"===e)return null;(0,_assertValidName.assertValidName)(e,"drop()");const r=await(0,_retrieveFile.retrieveFile)(`${e}.json`);for(const e of r.entries)await(0,_storage.removeFile)(`${r.path}/${e}`);await(0,_storage.removeDirectory)(r.path),await(0,_storage.removeDirectory)(`__indexes/${r.path}`),await(0,_packedTable.discardPacked)(r.path),await(0,_storage.removeFile)(`${e}.json`),await(0,_deregisterRecord.deregisterRecord)(e)};exports.drop=drop;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Throw unless this is a name the query language allows (letters, digits, `_` and `-`). Record and field names end up
|
|
3
|
+
* in file paths and row keys, so a name like `../x` from a direct JS call must be refused before it reaches storage.
|
|
4
|
+
* @param name
|
|
5
|
+
* @param where What is being named, for the error (e.g. `create()`).
|
|
6
|
+
*/
|
|
7
|
+
export declare const assertValidName: (name: unknown, where: string) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Throw unless this is a valid index file name (`<name>.json`); an empty lookup (no index file) is allowed.
|
|
10
|
+
* @param lookup
|
|
11
|
+
* @param where
|
|
12
|
+
*/
|
|
13
|
+
export declare const assertValidLookup: (lookup: unknown, where: string) => void;
|
|
14
|
+
//# sourceMappingURL=assertValidName.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.assertValidName = exports.assertValidLookup = void 0;
|
|
7
|
+
require("core-js/modules/es.json.stringify.js");
|
|
8
|
+
var _parseCreate = require("./parsers/parseCreate");
|
|
9
|
+
/** An index file name: a name plus `.json`, as keyLookup makes them (e.g. `pk__id.json`). */
|
|
10
|
+
const lookupMatch = /^[a-z0-9_-]+\.json$/i;
|
|
11
|
+
/**
|
|
12
|
+
* Throw unless this is a name the query language allows (letters, digits, `_` and `-`). Record and field names end up
|
|
13
|
+
* in file paths and row keys, so a name like `../x` from a direct JS call must be refused before it reaches storage.
|
|
14
|
+
* @param name
|
|
15
|
+
* @param where What is being named, for the error (e.g. `create()`).
|
|
16
|
+
*/
|
|
17
|
+
const assertValidName = (name, where) => {
|
|
18
|
+
if (typeof name !== 'string' || !_parseCreate.nameMatch.test(name)) {
|
|
19
|
+
throw new Error(`Invalid name ${JSON.stringify(name) ?? 'undefined'} in ${where}: names use letters, digits, _ and - only`);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Throw unless this is a valid index file name (`<name>.json`); an empty lookup (no index file) is allowed.
|
|
24
|
+
* @param lookup
|
|
25
|
+
* @param where
|
|
26
|
+
*/
|
|
27
|
+
exports.assertValidName = assertValidName;
|
|
28
|
+
const assertValidLookup = (lookup, where) => {
|
|
29
|
+
if (lookup === '' || typeof lookup === 'undefined') return;
|
|
30
|
+
if (typeof lookup !== 'string' || !lookupMatch.test(lookup)) {
|
|
31
|
+
throw new Error(`Invalid index file ${JSON.stringify(lookup) ?? 'undefined'} in ${where}: use a name plus .json`);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
exports.assertValidLookup = assertValidLookup;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.assertValidName=exports.assertValidLookup=void 0,require("core-js/modules/es.json.stringify.js");var _parseCreate=require("./parsers/parseCreate");const lookupMatch=/^[a-z0-9_-]+\.json$/i,assertValidName=(e,s)=>{if("string"!=typeof e||!_parseCreate.nameMatch.test(e))throw new Error(`Invalid name ${JSON.stringify(e)??"undefined"} in ${s}: names use letters, digits, _ and - only`)};exports.assertValidName=assertValidName;const assertValidLookup=(e,s)=>{if(""!==e&&void 0!==e&&("string"!=typeof e||!lookupMatch.test(e)))throw new Error(`Invalid index file ${JSON.stringify(e)??"undefined"} in ${s}: use a name plus .json`)};exports.assertValidLookup=assertValidLookup;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-fs-query",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "A git-native JSON data store for static sites: JSON files with a schema, enforced keys and a SQL-like query language, read straight from static hosting.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"prepublishOnly": "npm run build",
|
|
28
28
|
"benchmark": "node scripts/benchmark.cjs"
|
|
29
29
|
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"json-fs-query": "dist/cli/index.js",
|
|
32
|
+
"jfsq": "dist/cli/index.js"
|
|
33
|
+
},
|
|
30
34
|
"repository": {
|
|
31
35
|
"type": "git",
|
|
32
36
|
"url": "git+https://github.com/jheagle/json-fs-query.git"
|
|
@@ -49,6 +53,7 @@
|
|
|
49
53
|
"homepage": "https://github.com/jheagle/json-fs-query#readme",
|
|
50
54
|
"devDependencies": {
|
|
51
55
|
"@babel/core": "^8.1.0",
|
|
56
|
+
"@types/node": "^26.6.5",
|
|
52
57
|
"gulp": "^5.0.1",
|
|
53
58
|
"js-build-tools": "^8.2.0",
|
|
54
59
|
"playwright": "^1.65.0",
|
|
@@ -58,6 +63,7 @@
|
|
|
58
63
|
},
|
|
59
64
|
"dependencies": {
|
|
60
65
|
"browser-or-node": "^3.0.0",
|
|
66
|
+
"commander": "^14.0.3",
|
|
61
67
|
"core-js": "^3.51.0",
|
|
62
68
|
"si-funciona": "^2.10.0"
|
|
63
69
|
},
|