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
package/README.md
CHANGED
|
@@ -95,6 +95,9 @@ test that calls `checkPacked()` (see Packed tables) catches a forgotten `packDat
|
|
|
95
95
|
npm install json-fs-query
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
It comes with a command line, `jfsq` (also `json-fs-query`): `npx jfsq` opens a prompt on the `database/` folder (see
|
|
99
|
+
[Command line](#command-line-jfsq)).
|
|
100
|
+
|
|
98
101
|
## Usage
|
|
99
102
|
|
|
100
103
|
```js
|
|
@@ -517,7 +520,7 @@ new fields. The `alter()` function takes the same changes as objects, e.g.
|
|
|
517
520
|
|
|
518
521
|
### Reading a query over several lines: `queryStatus()`
|
|
519
522
|
|
|
520
|
-
For a prompt which reads a query line by line (like the
|
|
523
|
+
For a prompt which reads a query line by line (like the [command line](#command-line-jfsq)'s), `queryStatus()` says whether the text so far is ready to
|
|
521
524
|
run, and if not, what it is waiting for: `'` or `"` (a string), `/*` (a comment), `[` or `(` (a bracket), or `;`.
|
|
522
525
|
Join the lines with `'\n'`, since a `--` comment ends at the end of its line.
|
|
523
526
|
|
|
@@ -679,6 +682,99 @@ is changed. Node only, like every write.
|
|
|
679
682
|
file turns compact the next time it changes, and diffs mix both. Either run `formatDatabase('compact')` once and commit
|
|
680
683
|
the result on its own, or set `format: 'pretty'` to keep writing indented files.
|
|
681
684
|
|
|
685
|
+
## Command line: `jfsq`
|
|
686
|
+
|
|
687
|
+
`jfsq` (or `json-fs-query`) runs queries from a terminal or a script: a prompt for trying things, `-q` for one query,
|
|
688
|
+
or a file of queries piped in to set a database up.
|
|
689
|
+
|
|
690
|
+
```shell
|
|
691
|
+
npx jfsq # the prompt, on database/ in the current folder
|
|
692
|
+
npx jfsq -q "read workouts where reps > 8" # run it, print the result, exit
|
|
693
|
+
npx jfsq -d site/database < seed.txt # run every query in a file
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
| Option | |
|
|
697
|
+
| --- | --- |
|
|
698
|
+
| `-q, --query <query>` | run this query (or several, separated by `;`), print the results and exit |
|
|
699
|
+
| `-d, --database <path>` | the database folder (default `database/`); with `--databases`, the name of a database in that folder |
|
|
700
|
+
| `--databases <folder>` | a folder holding several databases (see [Several databases](#several-databases-databasespath-use)) |
|
|
701
|
+
| `-o, --output <format>` | how results print: `table` (the default), `json`, `csv` or `dump` (see Output) |
|
|
702
|
+
| `--null <text>` | how a table shows `null` (default `NULL`) |
|
|
703
|
+
| `--missing <text>` | how a table shows a missing field (default `—`) |
|
|
704
|
+
| `-v, --version` | print the version |
|
|
705
|
+
| `-h, --help` | list the options, with examples |
|
|
706
|
+
|
|
707
|
+
### The prompt
|
|
708
|
+
|
|
709
|
+
Without `-q`, `jfsq` opens a prompt. A query runs once it ends with a `;` outside any string, bracket or comment, so a
|
|
710
|
+
query can take several lines; the continuation prompt shows what it is still waiting for (`'>` for an open string,
|
|
711
|
+
`(>` for a bracket, `;>` for the end), the way psql does (see [`queryStatus()`](#reading-a-query-over-several-lines-querystatus)).
|
|
712
|
+
An error is printed and the prompt carries on. Ctrl+C clears the query being typed; on an empty prompt, Ctrl+C or
|
|
713
|
+
`/exit` quits.
|
|
714
|
+
|
|
715
|
+
```text
|
|
716
|
+
JSON-Query> read workouts
|
|
717
|
+
;> where reps > 8;
|
|
718
|
+
+--------------------------------------+------+------------+
|
|
719
|
+
| _id | reps | date |
|
|
720
|
+
+--------------------------------------+------+------------+
|
|
721
|
+
| 9f3c1e2a-5b7d-4e8f-a1c2-3d4e5f6a7b8c | 10 | 2024-06-02 |
|
|
722
|
+
+--------------------------------------+------+------------+
|
|
723
|
+
(1 row)
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
With `--databases`, the prompt names the database in use (`real> `) and follows a `use` in a query.
|
|
727
|
+
|
|
728
|
+
Commands start with a `/` and only count at the start of a query (they're never sent to the query parser, and a query
|
|
729
|
+
can still start with a `/* comment */`). Without a value, each says what it is set to:
|
|
730
|
+
|
|
731
|
+
| Command | |
|
|
732
|
+
| --- | --- |
|
|
733
|
+
| `/output <format>` | how the next results print: `table`, `json`, `csv` or `dump` |
|
|
734
|
+
| `/null <text>` | how a table shows `null` |
|
|
735
|
+
| `/missing <text>` | how a table shows a missing field |
|
|
736
|
+
| `/exit` | quit |
|
|
737
|
+
|
|
738
|
+
### Output
|
|
739
|
+
|
|
740
|
+
- **table** (the default): a table per result, with a row count. Each column is a field, in the order they first
|
|
741
|
+
appear. A field a row doesn't have shows `—`, `null` shows `NULL` and the empty string `''`, so the three read
|
|
742
|
+
differently without colour (on a terminal they are also dimmed; never with `NO_COLOR`, or when the output isn't a
|
|
743
|
+
terminal). A table that shows `—` or `NULL` has a one-line legend under it. A grouped read is a heading and a table
|
|
744
|
+
per group (`date = 2024-06-02 (3 rows)`, `date is missing (1 row)`), nested groups indented under theirs. A result
|
|
745
|
+
that isn't rows (`describe`) prints as JSON.
|
|
746
|
+
- **json**: each result as JSON, one value per query (`jq` reads several as a stream): `jfsq -o json -q "read
|
|
747
|
+
workouts" | jq '.[].reps'`.
|
|
748
|
+
- **csv**: a header line, then a line per row; a field is quoted when it holds a comma, a quote, a line break or space
|
|
749
|
+
at either end. `null` and a missing field are empty, the empty string is `""` (as psql's CSV output does). A grouped
|
|
750
|
+
read is its rows: `jfsq -o csv -q "read workouts" > workouts.csv`.
|
|
751
|
+
- **dump** can be chosen, but isn't available yet: it refuses each query (before running it). It is meant to print
|
|
752
|
+
the queries that would rebuild a record (its `create`, then an `insert` per row) for you to run or edit.
|
|
753
|
+
|
|
754
|
+
### Scripts and seed files
|
|
755
|
+
|
|
756
|
+
Queries piped in (`jfsq < seed.txt`, or `generate-queries | jfsq`) run the same way as at the prompt, but quietly:
|
|
757
|
+
only results (on stdout) and errors (on stderr), no prompts. Every query runs even after one fails, and the last one
|
|
758
|
+
runs without its closing `;`. Commands such as `/output json` work in the file too.
|
|
759
|
+
|
|
760
|
+
```sql
|
|
761
|
+
-- seed.txt
|
|
762
|
+
create if not exists workouts (_id string auto, reps int, date string, primary key (_id));
|
|
763
|
+
insert workouts values (reps = 10, date = '2024-06-02'), (reps = 12, date = '2024-06-05');
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
`if not exists` makes the `create` safe to run twice; the `insert` isn't (running the file again adds the rows again).
|
|
767
|
+
|
|
768
|
+
The exit code is 0 when everything worked and 1 when anything failed: a query, a bad option, or a database that
|
|
769
|
+
doesn't exist (then nothing runs). The same goes for `-q`, so a script can check it:
|
|
770
|
+
|
|
771
|
+
```shell
|
|
772
|
+
jfsq -d site/database < seed.txt || echo "seeding failed"
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
The command line is a separate process: a server and `jfsq` writing to the same folder at the same time aren't
|
|
776
|
+
protected from each other (see [Several calls at once](#several-calls-at-once)).
|
|
777
|
+
|
|
682
778
|
## Several calls at once
|
|
683
779
|
|
|
684
780
|
Calls to `query()`, `create()`, `drop()`, `describe()`, `formatDatabase()`, `packDatabase()` and `checkPacked()` on
|
package/browser/jsonFsQuery.js
CHANGED
|
@@ -5177,6 +5177,34 @@ var jsonFsQuery = (() => {
|
|
|
5177
5177
|
}
|
|
5178
5178
|
});
|
|
5179
5179
|
|
|
5180
|
+
// dist/data/utilities/assertValidName.js
|
|
5181
|
+
var require_assertValidName = __commonJS({
|
|
5182
|
+
"dist/data/utilities/assertValidName.js"(exports) {
|
|
5183
|
+
"use strict";
|
|
5184
|
+
init_process();
|
|
5185
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5186
|
+
value: true
|
|
5187
|
+
});
|
|
5188
|
+
exports.assertValidName = exports.assertValidLookup = void 0;
|
|
5189
|
+
require_es_json_stringify();
|
|
5190
|
+
var _parseCreate = require_parseCreate();
|
|
5191
|
+
var lookupMatch = /^[a-z0-9_-]+\.json$/i;
|
|
5192
|
+
var assertValidName = (name, where) => {
|
|
5193
|
+
if (typeof name !== "string" || !_parseCreate.nameMatch.test(name)) {
|
|
5194
|
+
throw new Error(`Invalid name ${JSON.stringify(name) ?? "undefined"} in ${where}: names use letters, digits, _ and - only`);
|
|
5195
|
+
}
|
|
5196
|
+
};
|
|
5197
|
+
exports.assertValidName = assertValidName;
|
|
5198
|
+
var assertValidLookup = (lookup, where) => {
|
|
5199
|
+
if (lookup === "" || typeof lookup === "undefined") return;
|
|
5200
|
+
if (typeof lookup !== "string" || !lookupMatch.test(lookup)) {
|
|
5201
|
+
throw new Error(`Invalid index file ${JSON.stringify(lookup) ?? "undefined"} in ${where}: use a name plus .json`);
|
|
5202
|
+
}
|
|
5203
|
+
};
|
|
5204
|
+
exports.assertValidLookup = assertValidLookup;
|
|
5205
|
+
}
|
|
5206
|
+
});
|
|
5207
|
+
|
|
5180
5208
|
// dist/data/introspect/describe.js
|
|
5181
5209
|
var require_describe = __commonJS({
|
|
5182
5210
|
"dist/data/introspect/describe.js"(exports) {
|
|
@@ -5187,7 +5215,9 @@ var jsonFsQuery = (() => {
|
|
|
5187
5215
|
});
|
|
5188
5216
|
exports.describe = void 0;
|
|
5189
5217
|
var _retrieveFile = require_retrieveFile();
|
|
5218
|
+
var _assertValidName = require_assertValidName();
|
|
5190
5219
|
var describe = async (record = "__RECORDS") => {
|
|
5220
|
+
(0, _assertValidName.assertValidName)(record, "describe()");
|
|
5191
5221
|
const recordFile = await (0, _retrieveFile.retrieveFile)(`${record}.json`);
|
|
5192
5222
|
if (record === "__RECORDS") {
|
|
5193
5223
|
return recordFile.entries;
|
|
@@ -6839,6 +6869,7 @@ var jsonFsQuery = (() => {
|
|
|
6839
6869
|
});
|
|
6840
6870
|
exports.create = exports.assertValidDefinition = void 0;
|
|
6841
6871
|
require_es_array_includes();
|
|
6872
|
+
var _assertValidName = require_assertValidName();
|
|
6842
6873
|
var _record = require_record();
|
|
6843
6874
|
var _registerRecord = require_registerRecord();
|
|
6844
6875
|
var _storage = require_storage();
|
|
@@ -6892,6 +6923,18 @@ var jsonFsQuery = (() => {
|
|
|
6892
6923
|
if (recordName === "__RECORDS") {
|
|
6893
6924
|
return null;
|
|
6894
6925
|
}
|
|
6926
|
+
(0, _assertValidName.assertValidName)(recordName, "create()");
|
|
6927
|
+
for (const field of definition) {
|
|
6928
|
+
(0, _assertValidName.assertValidName)(field?.name, `create() of ${recordName}`);
|
|
6929
|
+
}
|
|
6930
|
+
for (const key of keys) {
|
|
6931
|
+
(key?.fields ?? []).forEach((name) => (0, _assertValidName.assertValidName)(name, `create() of ${recordName}`));
|
|
6932
|
+
(0, _assertValidName.assertValidLookup)(key?.lookup, `create() of ${recordName}`);
|
|
6933
|
+
for (const reference of key?.references ?? []) {
|
|
6934
|
+
(0, _assertValidName.assertValidLookup)(reference?.lookup ?? null, `create() of ${recordName}`);
|
|
6935
|
+
(reference?.fields ?? []).forEach((name) => String(name).split(".").forEach((part) => (0, _assertValidName.assertValidName)(part, `create() of ${recordName}`)));
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6895
6938
|
if ((0, _storage.fileExists)(`${recordName}.json`)) {
|
|
6896
6939
|
return null;
|
|
6897
6940
|
}
|
|
@@ -7009,11 +7052,13 @@ var jsonFsQuery = (() => {
|
|
|
7009
7052
|
var _requireNode = require_requireNode();
|
|
7010
7053
|
var _storage = require_storage();
|
|
7011
7054
|
var _packedTable = require_packedTable();
|
|
7055
|
+
var _assertValidName = require_assertValidName();
|
|
7012
7056
|
var drop = async (record) => {
|
|
7013
7057
|
(0, _requireNode.requireNode)("drop()");
|
|
7014
7058
|
if (record === "__RECORDS") {
|
|
7015
7059
|
return null;
|
|
7016
7060
|
}
|
|
7061
|
+
(0, _assertValidName.assertValidName)(record, "drop()");
|
|
7017
7062
|
const recordFile = await (0, _retrieveFile.retrieveFile)(`${record}.json`);
|
|
7018
7063
|
for (const file of recordFile.entries) {
|
|
7019
7064
|
await (0, _storage.removeFile)(`${recordFile.path}/${file}`);
|
|
@@ -7139,10 +7184,10 @@ var jsonFsQuery = (() => {
|
|
|
7139
7184
|
});
|
|
7140
7185
|
exports.alter = void 0;
|
|
7141
7186
|
require_es_array_includes();
|
|
7142
|
-
require_es_json_stringify();
|
|
7143
7187
|
var _field = require_field();
|
|
7144
7188
|
var _create = require_create();
|
|
7145
7189
|
var _parseCreate = require_parseCreate();
|
|
7190
|
+
var _assertValidName = require_assertValidName();
|
|
7146
7191
|
var _retrieveRecord = require_retrieveRecord();
|
|
7147
7192
|
var _retrieveFiles = require_retrieveFiles();
|
|
7148
7193
|
var _retrieveRecords = require_retrieveRecords();
|
|
@@ -7352,10 +7397,7 @@ var jsonFsQuery = (() => {
|
|
|
7352
7397
|
throw new Error("__RECORDS is reserved for the list of records");
|
|
7353
7398
|
}
|
|
7354
7399
|
const names = [entity, ..."field" in change ? [change.field?.name] : [], ..."name" in change ? [change.name] : [], ..."to" in change ? [change.to] : []];
|
|
7355
|
-
|
|
7356
|
-
if (invalid !== -1) {
|
|
7357
|
-
throw new Error(`Invalid name ${JSON.stringify(names[invalid]) ?? "undefined"} in alter: names use letters, digits, _ and - only`);
|
|
7358
|
-
}
|
|
7400
|
+
names.forEach((name) => (0, _assertValidName.assertValidName)(name, "alter"));
|
|
7359
7401
|
const record = await (0, _retrieveRecord.retrieveRecord)(entity);
|
|
7360
7402
|
switch (change.action) {
|
|
7361
7403
|
case "add":
|