gitsheets 0.22.4 → 1.0.3

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.
Files changed (72) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +21 -0
  3. package/bin/gitsheets +5 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.d.ts.map +1 -0
  6. package/dist/cli/index.js +256 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/errors.d.ts +72 -0
  9. package/dist/errors.d.ts.map +1 -0
  10. package/dist/errors.js +74 -0
  11. package/dist/errors.js.map +1 -0
  12. package/dist/index.d.ts +17 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +12 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/patch.d.ts +2 -0
  17. package/dist/patch.d.ts.map +1 -0
  18. package/dist/patch.js +39 -0
  19. package/dist/patch.js.map +1 -0
  20. package/dist/path-template/index.d.ts +42 -0
  21. package/dist/path-template/index.d.ts.map +1 -0
  22. package/dist/path-template/index.js +288 -0
  23. package/dist/path-template/index.js.map +1 -0
  24. package/dist/push-daemon.d.ts +53 -0
  25. package/dist/push-daemon.d.ts.map +1 -0
  26. package/dist/push-daemon.js +148 -0
  27. package/dist/push-daemon.js.map +1 -0
  28. package/dist/repository.d.ts +67 -0
  29. package/dist/repository.d.ts.map +1 -0
  30. package/dist/repository.js +322 -0
  31. package/dist/repository.js.map +1 -0
  32. package/dist/sheet.d.ts +107 -0
  33. package/dist/sheet.d.ts.map +1 -0
  34. package/dist/sheet.js +605 -0
  35. package/dist/sheet.js.map +1 -0
  36. package/dist/store.d.ts +41 -0
  37. package/dist/store.d.ts.map +1 -0
  38. package/dist/store.js +49 -0
  39. package/dist/store.js.map +1 -0
  40. package/dist/toml.d.ts +11 -0
  41. package/dist/toml.d.ts.map +1 -0
  42. package/dist/toml.js +28 -0
  43. package/dist/toml.js.map +1 -0
  44. package/dist/transaction.d.ts +96 -0
  45. package/dist/transaction.d.ts.map +1 -0
  46. package/dist/transaction.js +227 -0
  47. package/dist/transaction.js.map +1 -0
  48. package/dist/validation.d.ts +37 -0
  49. package/dist/validation.d.ts.map +1 -0
  50. package/dist/validation.js +105 -0
  51. package/dist/validation.js.map +1 -0
  52. package/package.json +41 -35
  53. package/bin/cli.js +0 -61
  54. package/commands/edit.js +0 -90
  55. package/commands/normalize.js +0 -81
  56. package/commands/query.js +0 -206
  57. package/commands/read.js +0 -64
  58. package/commands/singer-target.js +0 -214
  59. package/commands/upsert.js +0 -260
  60. package/lib/GitSheets.js +0 -464
  61. package/lib/Repository.js +0 -88
  62. package/lib/Sheet.js +0 -625
  63. package/lib/errors.js +0 -21
  64. package/lib/hologit.js +0 -1
  65. package/lib/logger.js +0 -18
  66. package/lib/path/BaseComponent.js +0 -24
  67. package/lib/path/ExpressionComponent.js +0 -26
  68. package/lib/path/FieldComponent.js +0 -13
  69. package/lib/path/LiteralComponent.js +0 -12
  70. package/lib/path/Query.js +0 -18
  71. package/lib/path/Template.js +0 -214
  72. package/server.js +0 -120
package/package.json CHANGED
@@ -1,52 +1,58 @@
1
1
  {
2
2
  "name": "gitsheets",
3
- "version": "0.22.4",
4
- "description": "A toolkit for using a git repository to store low-volume, high-touch, human-scale data",
5
- "main": "lib/GitSheets.js",
6
- "scripts": {
7
- "test": "jest"
3
+ "version": "1.0.3",
4
+ "description": "A git-backed document store for low-volume, high-touch, human-scale data",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
8
13
  },
9
14
  "bin": {
10
- "git-sheet": "./bin/cli.js"
15
+ "gitsheets": "./bin/gitsheets",
16
+ "git-sheet": "./bin/gitsheets"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "bin",
21
+ "LICENSE",
22
+ "README.md"
23
+ ],
24
+ "engines": {
25
+ "node": ">=20"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.build.json",
29
+ "test": "vitest run",
30
+ "test:watch": "vitest",
31
+ "type-check": "tsc --noEmit"
11
32
  },
12
33
  "repository": {
13
34
  "type": "git",
14
- "url": "git+ssh://git@github.com/JarvusInnovations/gitsheets.git"
35
+ "url": "git+https://github.com/JarvusInnovations/gitsheets.git"
15
36
  },
16
- "author": "timwis <tim@timwis.com>",
17
- "license": "Apache-2.0",
37
+ "homepage": "https://github.com/JarvusInnovations/gitsheets#readme",
18
38
  "bugs": {
19
39
  "url": "https://github.com/JarvusInnovations/gitsheets/issues"
20
40
  },
21
- "homepage": "https://github.com/JarvusInnovations/gitsheets#readme",
41
+ "license": "Apache-2.0",
42
+ "author": "Jarvus Innovations",
22
43
  "dependencies": {
23
44
  "@iarna/toml": "^2.2.5",
24
- "csv-parser": "^2.3.5",
25
- "deepmerge": "^4.2.2",
26
- "fast-csv": "^4.3.6",
27
- "fast-json-patch": "^3.1.1",
28
- "get-stream": "^5.2.0",
29
- "hologit": "^0.41.8",
30
- "http-assert": "^1.4.1",
31
- "koa": "^2.13.1",
32
- "koa-bodyparser": "^4.3.0",
33
- "koa-router": "^7.4.0",
34
- "maxstache": "^1.0.7",
35
- "mz": "^2.7.0",
36
- "rfc6902": "^5.0.1",
37
- "sort-keys": "^4.2.0",
38
- "streaming-json-stringify": "^3.1.0",
39
- "tmp": "^0.2.1",
40
- "to-readable-stream": "^2.1.0",
41
- "winston": "^3.3.3",
42
- "yargs": "^17.5.1"
45
+ "ajv": "^8.20.0",
46
+ "ajv-formats": "^3.0.1",
47
+ "hologit": "^0.49.1",
48
+ "sort-keys": "^6.0.0",
49
+ "yargs": "^18.0.0"
43
50
  },
44
51
  "devDependencies": {
45
- "common-tags": "^1.8.0",
46
- "del": "^5.1.0",
47
- "into-stream": "^5.1.1",
48
- "jest": "^26.6.3",
49
- "make-dir": "^3.1.0",
50
- "supertest": "^4.0.2"
52
+ "@types/node": "^25.8.0",
53
+ "@types/yargs": "^17.0.35",
54
+ "tmp": "^0.2.5",
55
+ "typescript": "^6.0.3",
56
+ "vitest": "^4.1.6"
51
57
  }
52
58
  }
package/bin/cli.js DELETED
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
-
4
- // setup logger
5
- const logger = require('winston');
6
- const loggerConsole = new logger.transports.Console({
7
- level: process.env.DEBUG ? 'debug' : 'info',
8
- format: logger.format.combine(
9
- logger.format.colorize(),
10
- logger.format.prettyPrint(),
11
- logger.format.splat(),
12
- logger.format.simple(),
13
- ),
14
-
15
- // all logger output to STDERR
16
- stderrLevels: Object.keys(require('winston/lib/winston/config').cli.levels),
17
- });
18
- logger.add(loggerConsole);
19
-
20
- module.exports = { logger };
21
-
22
-
23
- // route command line
24
- require('yargs')
25
- .version(require('../package.json').version)
26
- .option('d', {
27
- alias: 'debug',
28
- type: 'boolean',
29
- default: false,
30
- global: true,
31
- })
32
- .option('q', {
33
- alias: 'quiet',
34
- type: 'boolean',
35
- default: false,
36
- global: true,
37
- })
38
- .check(function (argv) {
39
- if (argv.debug) {
40
- loggerConsole.level = 'debug';
41
- } else if (argv.quiet) {
42
- loggerConsole.level = 'error';
43
- }
44
-
45
- return true;
46
- })
47
- .commandDir('../commands', { exclude: /\.test\.js$/ })
48
- .demandCommand()
49
- .showHelpOnFail(false, 'Specify --help for available options')
50
- .fail((msg, err) => {
51
- logger.error(msg || err.message);
52
-
53
- if (err) {
54
- logger.debug(err.stack);
55
- }
56
-
57
- process.exit(1);
58
- })
59
- .strict()
60
- .help()
61
- .argv;
package/commands/edit.js DELETED
@@ -1,90 +0,0 @@
1
- exports.command = 'edit <record-path> [resume-path]';
2
- exports.desc = 'Edit a record, validating and formatting it automatically';
3
- exports.builder = {
4
- 'record-path': {
5
- type: 'string',
6
- describe: 'The path to a record file to edit',
7
- demandOption: true,
8
- },
9
- 'resume-path': {
10
- type: 'string',
11
- describe: 'If set, read initial editor content from this file instead of the target record',
12
- },
13
- encoding: {
14
- type: 'string',
15
- default: 'utf8',
16
- },
17
- };
18
-
19
- exports.handler = async function edit({ recordPath, resumePath, encoding }) {
20
- const fs = require('fs');
21
- const { spawn } = require('child_process');
22
- const path = require('path');
23
- const tmp = require('tmp');
24
- const TOML = require('@iarna/toml');
25
- const Repository = require('../lib/Repository.js');
26
- const Sheet = require('../lib/Sheet.js')
27
- const repo = await Repository.getFromEnvironment({ working: true });
28
- const git = await repo.getGit();
29
-
30
- // open record
31
- let recordToml = fs.readFileSync(resumePath || recordPath, encoding);
32
-
33
- // try to parse and format
34
- try {
35
- const record = TOML.parse(recordToml);
36
- recordToml = Sheet.stringifyRecord(record);
37
- } catch (err) {
38
- console.warn(`Failed to parse opened record:\n${err}`);
39
- }
40
-
41
- // get temp path
42
- const { name: tempFilePath } = tmp.fileSync({
43
- prefix: path.basename(recordPath, '.toml'),
44
- postfix: '.toml',
45
- discardDescriptor: true,
46
- });
47
-
48
- // populate temp path
49
- fs.writeFileSync(tempFilePath, recordToml, encoding);
50
-
51
- // get editor
52
- const editor = (await git.var('GIT_EDITOR')) || 'vim';
53
-
54
- // invoke editor
55
- try {
56
- const editorProcess = spawn('sh', ['-c', `eval ${editor} ${tempFilePath}`], { stdio: 'inherit' });
57
- const exitCode = await new Promise(resolve => editorProcess.on('close', resolve));
58
-
59
- if (exitCode !== 0) {
60
- console.error(`editor exited with code ${exitCode}, canceling edit`);
61
- fs.unlinkSync(tempFilePath);
62
- process.exit(exitCode);
63
- }
64
- } catch (err) {
65
- console.error(`Failed to invoke editor: ${err}`);
66
- }
67
-
68
- // read and clean up temp file
69
- const editedToml = fs.readFileSync(tempFilePath, encoding);
70
-
71
- // parse toml
72
- let editedRecord;
73
- try {
74
- editedRecord = TOML.parse(editedToml);
75
- } catch (err) {
76
- console.error(`Failed to parse edited record:\n${err}`);
77
- console.error(`To resume editing, run: git sheet edit ${recordPath} ${tempFilePath}`);
78
- process.exit(1);
79
- }
80
-
81
- // delete temp file
82
- fs.unlinkSync(tempFilePath);
83
-
84
- // save normalized TOML to input path
85
- fs.writeFileSync(recordPath, Sheet.stringifyRecord(editedRecord));
86
- process.exit(0);
87
- };
88
-
89
-
90
- // library
@@ -1,81 +0,0 @@
1
- exports.command = 'normalize [sheet...]';
2
- exports.desc = 'Normalize the content of any hand-edited records to be consistent';
3
- exports.builder = {
4
- sheet: {
5
- describe: 'Name of sheet to upsert into',
6
- type: 'array',
7
- },
8
- root: {
9
- describe: 'Root path to .gitsheets in repository (defaults to GITSHEETS_ROOT or /)',
10
- },
11
- prefix: {
12
- describe: 'Path to prefix after root to all sheet paths (defaults to GITSHEETS_PREFIX or none)',
13
- },
14
- };
15
-
16
- exports.handler = async function query({
17
- sheet: selectedSheets,
18
- root,
19
- prefix,
20
- }) {
21
- const logger = require('../lib/logger.js');
22
- const Repository = require('../lib/Repository.js')
23
- const path = require('path');
24
- const fs = require('mz/fs');
25
-
26
- const { GITSHEETS_ROOT, GITSHEETS_PREFIX } = process.env;
27
-
28
- // apply dynamic defaults
29
- if (!root) {
30
- root = GITSHEETS_ROOT || '/';
31
- }
32
-
33
- if (!prefix) {
34
- prefix = GITSHEETS_PREFIX || null;
35
- }
36
-
37
- // get repo interface
38
- const repo = await Repository.getFromEnvironment({ working: true });
39
- logger.debug('instantiated repository:', repo);
40
-
41
-
42
- // get sheets
43
- const sheets = await repo.openSheets({ root, dataTree: prefix });
44
-
45
-
46
- // loop through selected, or all sheets
47
- const sheetNames = selectedSheets || Object.keys(sheets);
48
-
49
- for (const sheetName of sheetNames) {
50
- const sheet = sheets[sheetName];
51
-
52
- if (!sheet) {
53
- throw new Error(`sheet ${sheetName} is not defined`);
54
- }
55
-
56
- // loop through all records and re-upsert
57
- try {
58
- for await (const record of sheet.query()) {
59
- const originalPath = record[Symbol.for('gitsheets-path')];
60
- logger.info(`rewriting ${path.join(root, prefix||'', sheetName, originalPath)}.toml`);
61
- const { path: normalizedPath } = await sheet.upsert(record);
62
-
63
- if (normalizedPath !== originalPath) {
64
- logger.warn(`^- moved to ${path.join(root, prefix||'', sheetName, normalizedPath)}.toml`);
65
- }
66
- }
67
- } catch (err) {
68
- if (err.constructor.name == 'TomlError') {
69
- logger.error(`failed to parse ${path.join(root, prefix||'', err.file)}\n${err.message}`);
70
- process.exit(1);
71
- }
72
-
73
- throw err;
74
- }
75
- }
76
-
77
-
78
- // write changes to workspace
79
- const workspace = await repo.getWorkspace();
80
- await workspace.writeWorkingChanges();
81
- };
package/commands/query.js DELETED
@@ -1,206 +0,0 @@
1
- exports.command = 'query <sheet>';
2
- exports.desc = 'Read records from a sheet';
3
- exports.builder = {
4
- sheet: {
5
- describe: 'Name of sheet to upsert into',
6
- },
7
- root: {
8
- describe: 'Root path to .gitsheets in repository (defaults to GITSHEETS_ROOT or /)',
9
- },
10
- prefix: {
11
- describe: 'Path to prefix after root to all sheet paths (defaults to GITSHEETS_PREFIX or none)',
12
- },
13
- format: {
14
- describe: 'Format to serialize output data in (defaults to json)',
15
- choices: ['json', 'csv', 'tsv', 'toml'],
16
- default: 'json',
17
- },
18
- headers: {
19
- describe: 'Whether to show headers in output formats that have headers (i.e. csv)',
20
- type: 'boolean',
21
- default: true,
22
- },
23
- // encoding: {
24
- // describe: 'Encoding to write output with',
25
- // default: 'utf8',
26
- // },
27
- limit: {
28
- describe: 'Truncate results to given count',
29
- type: 'number'
30
- },
31
- 'filter': {
32
- group: 'Filtering',
33
- describe: 'Filter results by one or more field values',
34
- type: 'array'
35
- },
36
- 'filter.<field>[=<value>]': {
37
- group: 'Filtering',
38
- describe: 'Field to filter by'
39
- },
40
- fields: {
41
- group: 'Field selection',
42
- describe: 'List of fields to order/limit output columns with',
43
- type: 'array'
44
- },
45
- 'fields.<from>=<to>': {
46
- group: 'Field selection',
47
- describe: 'Field to remap',
48
- type: 'array'
49
- }
50
- };
51
-
52
- exports.handler = async function query({
53
- sheet: sheetName,
54
- root,
55
- prefix,
56
- format,
57
- headers,
58
- // encoding,
59
- limit,
60
- filter,
61
- fields,
62
- }) {
63
- const logger = require('../lib/logger.js');
64
- const Repository = require('../lib/Repository.js')
65
- const path = require('path');
66
- const fs = require('mz/fs');
67
-
68
- const { GITSHEETS_ROOT, GITSHEETS_PREFIX } = process.env;
69
-
70
- // apply dynamic defaults
71
- if (!root) {
72
- root = GITSHEETS_ROOT || '/';
73
- }
74
-
75
- if (!prefix) {
76
- prefix = GITSHEETS_PREFIX || null;
77
- }
78
-
79
- // get repo interface
80
- const repo = await Repository.getFromEnvironment({ working: true });
81
- logger.debug('instantiated repository:', repo);
82
-
83
-
84
- // get sheet
85
- const sheet = await repo.openSheet(sheetName, { root, dataTree: prefix });
86
-
87
- if (!sheet) {
88
- throw new Error(`sheet '${sheetName}' not found under ${root}/.gitsheets/`);
89
- }
90
-
91
- logger.debug('loaded sheet:', sheet);
92
-
93
-
94
- // query records
95
- let result = sheet.query(filter);
96
-
97
-
98
- // apply limit
99
- if (limit) {
100
- result = limitResult(result, limit);
101
- }
102
-
103
-
104
- // apply field shaping
105
- if (fields) {
106
- result = mapResult(result, fields);
107
- }
108
-
109
-
110
- // output results
111
- switch (format) {
112
- case 'json': return outputJson(result);
113
- case 'csv': return outputCsv(result, { headers });
114
- case 'tsv': return outputCsv(result, { headers, delimiter: '\t' });
115
- case 'toml': return outputToml(result);
116
- default: throw new Error(`Unsupported output format: ${format}`);
117
- }
118
- };
119
-
120
-
121
- // library
122
- async function* limitResult(result, limit) {
123
- let count = 0;
124
-
125
- for await (const record of result) {
126
- count++;
127
- yield record;
128
-
129
- if (count >= limit) {
130
- break;
131
- }
132
- }
133
- }
134
-
135
- async function* mapResult(result, fields) {
136
- if (!Array.isArray(fields)) {
137
- const fieldsObject = fields;
138
- fields = [];
139
- for (const fromKey in fieldsObject) {
140
- const fieldMap = {};
141
- fieldMap[fromKey] = fieldsObject[fromKey];
142
- fields.push(fieldMap);
143
- }
144
- }
145
-
146
- for await (const record of result) {
147
- const output = {};
148
-
149
- for (const field of fields) {
150
- if (Array.isArray(field)) {
151
- for (const fieldValue of field) {
152
- output[fieldValue] = record[fieldValue];
153
- }
154
- } else if (typeof field == 'object') {
155
- for (const from in field) {
156
- output[field[from]] = record[from];
157
- }
158
- } else {
159
- output[field] = record[field];
160
- }
161
- }
162
-
163
- yield output;
164
- }
165
- }
166
-
167
- async function outputJson(result) {
168
- let firstRecord = true;
169
-
170
- console.log('[');
171
-
172
- for await (const record of result) {
173
- if (firstRecord) {
174
- console.log(`${JSON.stringify(record)}`);
175
- firstRecord = false;
176
- } else {
177
- console.log(`,${JSON.stringify(record)}`);
178
- }
179
- }
180
-
181
- console.log(']');
182
- }
183
-
184
- async function outputCsv(result, { headers = true, delimiter = ',' } = {}) {
185
- const { Readable } = require('stream');
186
- const { format: csvFormat } = require('fast-csv');
187
- const csvStream = csvFormat({ headers, delimiter, includeEndRowDelimiter: true });
188
-
189
- csvStream.pipe(process.stdout).on('end', () => process.exit());
190
-
191
- Readable.from(result).pipe(csvStream);
192
- }
193
-
194
- async function outputToml(result) {
195
- const TOML = require('@iarna/toml');
196
- let firstRecord = true;
197
-
198
- for await (const record of result) {
199
- if (firstRecord) {
200
- console.log(`${TOML.stringify(record)}`);
201
- firstRecord = false;
202
- } else {
203
- console.log(`\0\n${TOML.stringify(record)}`);
204
- }
205
- }
206
- }
package/commands/read.js DELETED
@@ -1,64 +0,0 @@
1
- exports.command = 'read <record-path>';
2
- exports.desc = 'Read a record, converting to desired format';
3
- exports.builder = {
4
- 'record-path': {
5
- type: 'string',
6
- describe: 'The path to a record file to read',
7
- demandOption: true,
8
- },
9
- encoding: {
10
- type: 'string',
11
- default: 'utf8',
12
- },
13
- format: {
14
- describe: 'Format to serialize output data in (defaults to json)',
15
- choices: ['json', 'csv', 'tsv', 'toml'],
16
- default: 'json',
17
- },
18
- headers: {
19
- describe: 'Whether to show headers in output formats that have headers (i.e. csv)',
20
- type: 'boolean',
21
- default: true,
22
- },
23
- };
24
-
25
- exports.handler = async function edit({ recordPath, encoding, format, headers }) {
26
- const fs = require('fs');
27
- const TOML = require('@iarna/toml');
28
-
29
- // open record
30
- const recordToml = fs.readFileSync(recordPath, encoding);
31
-
32
- // parse record
33
- const record = TOML.parse(recordToml);
34
-
35
- // output results
36
- switch (format) {
37
- case 'json': return outputJson(record);
38
- case 'csv': return outputCsv(record, { headers });
39
- case 'tsv': return outputCsv(record, { headers, delimiter: '\t' });
40
- case 'toml': return outputToml(record);
41
- default: throw new Error(`Unsupported output format: ${format}`);
42
- }
43
- };
44
-
45
-
46
- // library
47
- async function outputJson(record) {
48
- console.log(`${JSON.stringify(record)}`);
49
- }
50
-
51
- async function outputCsv(record, { headers = true, delimiter = ',' } = {}) {
52
- const { format: csvFormat } = require('fast-csv');
53
- const csvStream = csvFormat({ headers, delimiter, includeEndRowDelimiter: true });
54
-
55
- csvStream.pipe(process.stdout).on('end', () => process.exit());
56
-
57
- csvStream.write(record);
58
- csvStream.end();
59
- }
60
-
61
- async function outputToml(record) {
62
- const Sheet = require('../lib/Sheet.js')
63
- console.log(`${Sheet.stringifyRecord(record)}`);
64
- }