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.
- package/LICENSE +201 -0
- package/README.md +21 -0
- package/bin/gitsheets +5 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +256 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/errors.d.ts +72 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +74 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/patch.d.ts +2 -0
- package/dist/patch.d.ts.map +1 -0
- package/dist/patch.js +39 -0
- package/dist/patch.js.map +1 -0
- package/dist/path-template/index.d.ts +42 -0
- package/dist/path-template/index.d.ts.map +1 -0
- package/dist/path-template/index.js +288 -0
- package/dist/path-template/index.js.map +1 -0
- package/dist/push-daemon.d.ts +53 -0
- package/dist/push-daemon.d.ts.map +1 -0
- package/dist/push-daemon.js +148 -0
- package/dist/push-daemon.js.map +1 -0
- package/dist/repository.d.ts +67 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +322 -0
- package/dist/repository.js.map +1 -0
- package/dist/sheet.d.ts +107 -0
- package/dist/sheet.d.ts.map +1 -0
- package/dist/sheet.js +605 -0
- package/dist/sheet.js.map +1 -0
- package/dist/store.d.ts +41 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +49 -0
- package/dist/store.js.map +1 -0
- package/dist/toml.d.ts +11 -0
- package/dist/toml.d.ts.map +1 -0
- package/dist/toml.js +28 -0
- package/dist/toml.js.map +1 -0
- package/dist/transaction.d.ts +96 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +227 -0
- package/dist/transaction.js.map +1 -0
- package/dist/validation.d.ts +37 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +105 -0
- package/dist/validation.js.map +1 -0
- package/package.json +41 -35
- package/bin/cli.js +0 -61
- package/commands/edit.js +0 -90
- package/commands/normalize.js +0 -81
- package/commands/query.js +0 -206
- package/commands/read.js +0 -64
- package/commands/singer-target.js +0 -214
- package/commands/upsert.js +0 -260
- package/lib/GitSheets.js +0 -464
- package/lib/Repository.js +0 -88
- package/lib/Sheet.js +0 -625
- package/lib/errors.js +0 -21
- package/lib/hologit.js +0 -1
- package/lib/logger.js +0 -18
- package/lib/path/BaseComponent.js +0 -24
- package/lib/path/ExpressionComponent.js +0 -26
- package/lib/path/FieldComponent.js +0 -13
- package/lib/path/LiteralComponent.js +0 -12
- package/lib/path/Query.js +0 -18
- package/lib/path/Template.js +0 -214
- package/server.js +0 -120
package/lib/Sheet.js
DELETED
|
@@ -1,625 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
// const v8 = require('v8');
|
|
3
|
-
const vm = require('vm');
|
|
4
|
-
const sortKeys = require('sort-keys');
|
|
5
|
-
const TOML = require('@iarna/toml');
|
|
6
|
-
const rfc6902 = require('rfc6902');
|
|
7
|
-
const Configurable = require('hologit/lib/Configurable');
|
|
8
|
-
const TreeObject = require('hologit/lib/TreeObject');
|
|
9
|
-
|
|
10
|
-
const PathTemplate = require('./path/Template.js');
|
|
11
|
-
|
|
12
|
-
const EMPTY_TREE_HASH = TreeObject.getEmptyTreeHash();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const WRITE_QUEUES = new Map();
|
|
16
|
-
|
|
17
|
-
const RECORD_SHEET_KEY = Symbol.for('gitsheets-sheet');
|
|
18
|
-
const RECORD_PATH_KEY = Symbol.for('gitsheets-path');
|
|
19
|
-
const SORT_CLOSURE_KEY = Symbol('sort#closure');
|
|
20
|
-
const DIFF_STATUS_MAP = {
|
|
21
|
-
A: 'added',
|
|
22
|
-
D: 'deleted',
|
|
23
|
-
M: 'modified',
|
|
24
|
-
R: 'renamed',
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// primary export
|
|
29
|
-
class Sheet extends Configurable
|
|
30
|
-
{
|
|
31
|
-
static stringifyRecord (record) {
|
|
32
|
-
return TOML.stringify(sortKeys(record, { deep: true }));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
static async finishWriting (repo) {
|
|
36
|
-
for (const [sheet, writeQueue] of WRITE_QUEUES) {
|
|
37
|
-
if (sheet.workspace.getRepo() === repo) {
|
|
38
|
-
await Promise.all(writeQueue);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// #recordCache;
|
|
44
|
-
|
|
45
|
-
constructor ({ workspace, name, dataTree = null, configPath = null }) {
|
|
46
|
-
if (!workspace) {
|
|
47
|
-
throw new Error('workspace required');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!name) {
|
|
51
|
-
throw new Error('name required');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
super(...arguments);
|
|
55
|
-
|
|
56
|
-
this.name = name;
|
|
57
|
-
this.configPath = configPath || `.gitsheets/${name}.toml`;
|
|
58
|
-
this.dataTree = dataTree || workspace.root;
|
|
59
|
-
|
|
60
|
-
// this.#recordCache = new Map();
|
|
61
|
-
|
|
62
|
-
Object.freeze(this);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
getKind () {
|
|
66
|
-
return 'gitsheet';
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
getConfigPath () {
|
|
70
|
-
return this.configPath;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async readConfig () {
|
|
74
|
-
const config = await super.readConfig();
|
|
75
|
-
const { fields } = config;
|
|
76
|
-
|
|
77
|
-
if (!config) {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (!config.path) {
|
|
82
|
-
throw new Error('gitsheet.path must be declared');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!config.root) {
|
|
86
|
-
config.root = '.';
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (fields) {
|
|
90
|
-
if (typeof fields != 'object') {
|
|
91
|
-
throw new Error('gitsheet.fields must be a table');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
for (const field in fields) {
|
|
95
|
-
const { sort } = fields[field];
|
|
96
|
-
if (typeof sort == 'object') {
|
|
97
|
-
if (Array.isArray(sort)) {
|
|
98
|
-
for (const sortField of sort) {
|
|
99
|
-
if (typeof sortField != 'string') {
|
|
100
|
-
throw new Error(`gitsheet.fields.${field}.sort[] must be string field names`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
for (const sortField in sort) {
|
|
105
|
-
const sortDir = sort[sortField];
|
|
106
|
-
if (sortDir != 'ASC' && sortDir != 'DESC') {
|
|
107
|
-
throw new Error(`gitsheet.fields.${field}.sort.${sortField} must be ASC or DESC`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return config;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async readRecord (blob, path = null) {
|
|
119
|
-
/**
|
|
120
|
-
* caching is disabled for now, because v8.serialize doesn't
|
|
121
|
-
* preserve the custom Date classes that iarna/toml uses to
|
|
122
|
-
* preserve extended date types
|
|
123
|
-
*/
|
|
124
|
-
|
|
125
|
-
// const cache = this.#recordCache.get(blob.hash);
|
|
126
|
-
|
|
127
|
-
let record;
|
|
128
|
-
|
|
129
|
-
// if (cache) {
|
|
130
|
-
// record = v8.deserialize(cache);
|
|
131
|
-
// } else {
|
|
132
|
-
const toml = await blob.read();
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
record = TOML.parse(toml);
|
|
136
|
-
} catch (err) {
|
|
137
|
-
err.file = path;
|
|
138
|
-
throw err;
|
|
139
|
-
}
|
|
140
|
-
// }
|
|
141
|
-
|
|
142
|
-
// annotate with gitsheets keys
|
|
143
|
-
record[RECORD_SHEET_KEY] = this.name;
|
|
144
|
-
if (path) {
|
|
145
|
-
record[RECORD_PATH_KEY] = path;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// fill cache
|
|
149
|
-
// if (!cache) {
|
|
150
|
-
// this.#recordCache.set(blob.hash, toml);
|
|
151
|
-
// }
|
|
152
|
-
|
|
153
|
-
return record;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
*
|
|
158
|
-
* @param {Object|Function} query
|
|
159
|
-
*/
|
|
160
|
-
async* query (query) {
|
|
161
|
-
const {
|
|
162
|
-
root: sheetRoot,
|
|
163
|
-
path: pathTemplateString,
|
|
164
|
-
} = await this.getCachedConfig();
|
|
165
|
-
|
|
166
|
-
if (typeof query == 'function') {
|
|
167
|
-
throw new Error('function queries are not yet supported');
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const pathTemplate = PathTemplate.fromString(pathTemplateString);
|
|
171
|
-
const sheetDataTree = await this.dataTree.getSubtree(sheetRoot);
|
|
172
|
-
|
|
173
|
-
BLOBS: for await (const { blob, path: blobPath } of pathTemplate.queryTree(sheetDataTree, query)) {
|
|
174
|
-
const record = await this.readRecord(blob, blobPath);
|
|
175
|
-
|
|
176
|
-
if (!queryMatches(query, record)) {
|
|
177
|
-
continue BLOBS;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
record[RECORD_PATH_KEY] = blobPath || pathTemplate.render(record);
|
|
181
|
-
|
|
182
|
-
yield record;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async queryFirst (query) {
|
|
187
|
-
return (await this.query(query).next()).value;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async queryAll (query) {
|
|
191
|
-
const records = [];
|
|
192
|
-
|
|
193
|
-
for await (const record of this.query(query)) {
|
|
194
|
-
records.push(record);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return records;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
async pathForRecord (record) {
|
|
201
|
-
const { path: pathTemplateString } = await this.getCachedConfig();
|
|
202
|
-
return PathTemplate.fromString(pathTemplateString).render(record);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async normalizeRecord (record) {
|
|
206
|
-
const { fields = {} } = await this.getCachedConfig();
|
|
207
|
-
|
|
208
|
-
// apply declared fields
|
|
209
|
-
for (const field in fields) {
|
|
210
|
-
const {
|
|
211
|
-
type = null, // JSON Schema compatible
|
|
212
|
-
enum: enumValues = null, // JSON Schema compatible
|
|
213
|
-
default: defaultValue = null, // non-standard
|
|
214
|
-
sort = null, // non-standard
|
|
215
|
-
trueValues = null, // non-standard
|
|
216
|
-
falseValues = null, // non-standard
|
|
217
|
-
[SORT_CLOSURE_KEY]: cachedSorter,
|
|
218
|
-
} = fields[field];
|
|
219
|
-
|
|
220
|
-
// read or default value
|
|
221
|
-
let value;
|
|
222
|
-
if (field in record) {
|
|
223
|
-
value = record[field];
|
|
224
|
-
} else {
|
|
225
|
-
value = defaultValue;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// null values need no further processing
|
|
229
|
-
if (value === null || value === undefined || value === '') {
|
|
230
|
-
record[field] = null
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// coerce numbers/strings/booleans to desired type
|
|
235
|
-
const valueType = typeof value;
|
|
236
|
-
switch (type) {
|
|
237
|
-
case 'number':
|
|
238
|
-
if (valueType != 'number') {
|
|
239
|
-
if (valueType == 'string') {
|
|
240
|
-
value = Number(value);
|
|
241
|
-
} else {
|
|
242
|
-
throw new Error(`field ${field} contains value of type ${typeof value} that cannot be converted to a number`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
break;
|
|
246
|
-
case 'string':
|
|
247
|
-
if (valueType != 'string') {
|
|
248
|
-
if (valueType == 'number') {
|
|
249
|
-
value = String(value);
|
|
250
|
-
} else {
|
|
251
|
-
throw new Error(`field ${field} contains value of type ${typeof value} that cannot be converted to a string`);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
break;
|
|
255
|
-
case 'boolean':
|
|
256
|
-
if (valueType != 'boolean') {
|
|
257
|
-
if (trueValues || falseValues) {
|
|
258
|
-
if (trueValues && trueValues.indexOf(value) != -1) {
|
|
259
|
-
value = true;
|
|
260
|
-
} else if (falseValues && falseValues.indexOf(value) != -1) {
|
|
261
|
-
value = false;
|
|
262
|
-
} else {
|
|
263
|
-
value = null;
|
|
264
|
-
}
|
|
265
|
-
} else {
|
|
266
|
-
value = Boolean(value);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// validate enum values
|
|
273
|
-
if (enumValues && enumValues.indexOf(value) == -1) {
|
|
274
|
-
throw new Error(`field ${field} contains invalid enum value: ${value}`);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// sort array
|
|
278
|
-
if (sort) {
|
|
279
|
-
if (Array.isArray(value)) {
|
|
280
|
-
value.sort(cachedSorter || (fields[field][SORT_CLOSURE_KEY] = buildSorter(sort)));
|
|
281
|
-
} else {
|
|
282
|
-
throw new Error(`field ${field} defines sort but contains non-array value: ${value}`);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
record[field] = value;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return record;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
async clear () {
|
|
293
|
-
const { root } = await this.getCachedConfig();
|
|
294
|
-
return this.dataTree.writeChild(root, this.dataTree.repo.createTree());
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async clone () {
|
|
298
|
-
return new Sheet({
|
|
299
|
-
workspace: this.workspace,
|
|
300
|
-
name: this.name,
|
|
301
|
-
dataTree: await this.dataTree.clone(),
|
|
302
|
-
configPath: this.configPath,
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
async upsert (record) {
|
|
307
|
-
const {
|
|
308
|
-
root: sheetRoot,
|
|
309
|
-
path: pathTemplateString,
|
|
310
|
-
} = await this.getCachedConfig();
|
|
311
|
-
|
|
312
|
-
const pathTemplate = PathTemplate.fromString(pathTemplateString);
|
|
313
|
-
|
|
314
|
-
// get write queue
|
|
315
|
-
let writeQueue = WRITE_QUEUES.get(this);
|
|
316
|
-
if (!writeQueue) {
|
|
317
|
-
writeQueue = new Set();
|
|
318
|
-
WRITE_QUEUES.set(this, writeQueue);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// apply normalization before building path
|
|
322
|
-
const normalRecord = await this.normalizeRecord(record);
|
|
323
|
-
|
|
324
|
-
// build record path
|
|
325
|
-
const recordPath = pathTemplate.render(normalRecord);
|
|
326
|
-
if (!recordPath) {
|
|
327
|
-
throw new Error('could not generate any path for record');
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// delete previous record
|
|
331
|
-
const recordExistingPath = record[RECORD_PATH_KEY];
|
|
332
|
-
if (recordExistingPath && recordExistingPath != recordPath) {
|
|
333
|
-
await this.delete(recordExistingPath);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// write record
|
|
337
|
-
const toml = this.constructor.stringifyRecord(normalRecord);
|
|
338
|
-
const writePromise = this.dataTree.writeChild(`${path.join(sheetRoot, recordPath)}.toml`, toml);
|
|
339
|
-
|
|
340
|
-
writeQueue.add(writePromise);
|
|
341
|
-
const blob = await writePromise;
|
|
342
|
-
writeQueue.delete(writePromise);
|
|
343
|
-
|
|
344
|
-
// return compound data object
|
|
345
|
-
return {
|
|
346
|
-
blob,
|
|
347
|
-
path: recordPath,
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
async delete (record) {
|
|
352
|
-
const { root: sheetRoot } = await this.getCachedConfig()
|
|
353
|
-
|
|
354
|
-
if (typeof record !== 'string') {
|
|
355
|
-
record = await this.pathForRecord(record);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
return this.dataTree.deleteChild(`${path.join(sheetRoot, record)}.toml`);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
async getAttachments (record) {
|
|
362
|
-
const { root: sheetRoot } = await this.getCachedConfig()
|
|
363
|
-
|
|
364
|
-
if (typeof record !== 'string') {
|
|
365
|
-
record = await this.pathForRecord(record);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const attachmentsTree = await this.dataTree.getChild(path.join(sheetRoot, record));
|
|
369
|
-
|
|
370
|
-
return attachmentsTree
|
|
371
|
-
? attachmentsTree.getBlobMap()
|
|
372
|
-
: null;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
async getAttachment (record, attachment) {
|
|
376
|
-
const { root: sheetRoot } = await this.getCachedConfig()
|
|
377
|
-
|
|
378
|
-
if (typeof record !== 'string') {
|
|
379
|
-
record = await this.pathForRecord(record);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
return this.dataTree.getChild(path.join(sheetRoot, record, attachment));
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
async setAttachments (record, attachments) {
|
|
386
|
-
const { root: sheetRoot } = await this.getCachedConfig()
|
|
387
|
-
|
|
388
|
-
if (typeof record !== 'string') {
|
|
389
|
-
record = await this.pathForRecord(record);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return Promise.all(Object.keys(attachments).map(
|
|
393
|
-
attachment =>
|
|
394
|
-
this.dataTree.writeChild(
|
|
395
|
-
path.join(sheetRoot, record, attachment),
|
|
396
|
-
attachments[attachment]
|
|
397
|
-
)
|
|
398
|
-
));
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
async setAttachment (record, attachment, blob) {
|
|
402
|
-
const attachments = {};
|
|
403
|
-
attachments[attachment] = blob;
|
|
404
|
-
return this.setAttachments(record, attachments);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
async finishWriting() {
|
|
408
|
-
const writeQueue = WRITE_QUEUES.get(this);
|
|
409
|
-
|
|
410
|
-
if (!writeQueue) {
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
return Promise.all(writeQueue);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
async* diffFrom (srcCommitHash = null, { blobs = false, records = false, patches = false } = {}) {
|
|
418
|
-
const repo = this.getRepo();
|
|
419
|
-
const {
|
|
420
|
-
root: sheetRoot,
|
|
421
|
-
} = await this.getCachedConfig();
|
|
422
|
-
|
|
423
|
-
const srcTreeHash = srcCommitHash
|
|
424
|
-
? await repo.resolveRef(`${srcCommitHash}:${sheetRoot}`)
|
|
425
|
-
: EMPTY_TREE_HASH;
|
|
426
|
-
|
|
427
|
-
if (srcCommitHash && !srcTreeHash) {
|
|
428
|
-
throw new Error(`unable to resolve src tree ${srcCommitHash}:${sheetRoot}`);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const dstTree = await this.dataTree.getChild(sheetRoot);
|
|
432
|
-
const dstTreeHash = await dstTree.write();
|
|
433
|
-
|
|
434
|
-
let diff = diffTrees(repo, srcTreeHash, dstTreeHash);
|
|
435
|
-
|
|
436
|
-
if (blobs || records || patches) {
|
|
437
|
-
diff = this.loadDiffBlobs(diff);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if (records || patches) {
|
|
441
|
-
diff = this.loadDiffRecords(diff);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (patches) {
|
|
445
|
-
diff = this.loadDiffPatches(diff);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
yield* diff;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
async* loadDiffBlobs (diff) {
|
|
452
|
-
const repo = this.getRepo();
|
|
453
|
-
|
|
454
|
-
for await (const change of diff) {
|
|
455
|
-
change.srcBlob = change.srcMode == '000000'
|
|
456
|
-
? null
|
|
457
|
-
: repo.createBlob({ mode: change.srcMode, hash: change.srcHash });
|
|
458
|
-
|
|
459
|
-
change.dstBlob = change.dstMode == '000000'
|
|
460
|
-
? null
|
|
461
|
-
: repo.createBlob({ mode: change.dstMode, hash: change.dstHash });
|
|
462
|
-
|
|
463
|
-
yield change;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
async* loadDiffRecords (diff) {
|
|
469
|
-
for await (const change of diff) {
|
|
470
|
-
[ change.src, change.dst ] = await Promise.all([
|
|
471
|
-
change.srcBlob ? this.readRecord(change.srcBlob) : null,
|
|
472
|
-
change.dstBlob ? this.readRecord(change.dstBlob, change.path) : null,
|
|
473
|
-
]);
|
|
474
|
-
|
|
475
|
-
yield change;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
async* loadDiffPatches (diff) {
|
|
480
|
-
for await (const change of diff) {
|
|
481
|
-
change.patch = rfc6902.createPatch(change.src, change.dst, rfc6902DiffAny);
|
|
482
|
-
yield change;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
module.exports = Sheet;
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
// private library
|
|
491
|
-
function queryMatches(query, record) {
|
|
492
|
-
KEYS: for (const key in query) {
|
|
493
|
-
const queryValue = query[key]
|
|
494
|
-
const recordValue = record[key]
|
|
495
|
-
|
|
496
|
-
switch (typeof queryValue) {
|
|
497
|
-
case 'function':
|
|
498
|
-
if (!queryValue(recordValue, record)) {
|
|
499
|
-
return false;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
continue KEYS;
|
|
503
|
-
case 'object':
|
|
504
|
-
if (!queryMatches(queryValue, recordValue)) {
|
|
505
|
-
return false;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
continue KEYS;
|
|
509
|
-
default:
|
|
510
|
-
if (record[key] !== queryValue) {
|
|
511
|
-
return false;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
continue KEYS;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
return true;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function buildSorter (config) {
|
|
522
|
-
switch (typeof config) {
|
|
523
|
-
case 'boolean':
|
|
524
|
-
return vm.runInNewContext(`(a, b) => a.localeCompare(b, undefined, {
|
|
525
|
-
sensitivity: 'base',
|
|
526
|
-
ignorePunctuation: true,
|
|
527
|
-
numeric: true
|
|
528
|
-
})`);
|
|
529
|
-
case 'object':
|
|
530
|
-
if (Array.isArray(config)) {
|
|
531
|
-
const configMap = {};
|
|
532
|
-
for (const field of config) {
|
|
533
|
-
configMap[field] = 'ASC';
|
|
534
|
-
}
|
|
535
|
-
config = configMap;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
const expression = [];
|
|
539
|
-
for (const field in config) {
|
|
540
|
-
const direction = config[field] == 'ASC' ? 1 : -1;
|
|
541
|
-
expression.push(
|
|
542
|
-
`if ((a.${field}) < (b.${field})) return ${-1 * direction}`,
|
|
543
|
-
`if ((a.${field}) > (b.${field})) return ${1 * direction}`,
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
expression.push('return 0');
|
|
547
|
-
config = expression.join(';\n');
|
|
548
|
-
// fall through now that config is a string
|
|
549
|
-
case 'string':
|
|
550
|
-
return vm.runInNewContext(`(a, b) => {\n${config}\n}`);
|
|
551
|
-
default:
|
|
552
|
-
throw new Error('sort must be an expression in a string, a field:direction table, or field array');
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
async function* diffTrees (repo, src, dst) {
|
|
557
|
-
const git = await repo.getGit();
|
|
558
|
-
|
|
559
|
-
const diffProcess = await git.diffTree(
|
|
560
|
-
{ $spawn: true, z: true, r: true },
|
|
561
|
-
src,
|
|
562
|
-
dst,
|
|
563
|
-
'--',
|
|
564
|
-
'*.toml',
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
const exitCodePromise = new Promise(resolve => diffProcess.on('close', resolve));
|
|
568
|
-
|
|
569
|
-
// read error
|
|
570
|
-
let error = '';
|
|
571
|
-
diffProcess.stderr.on('data', chunk => error += chunk);
|
|
572
|
-
|
|
573
|
-
// read output
|
|
574
|
-
let output = '';
|
|
575
|
-
let status;
|
|
576
|
-
for await (const chunk of diffProcess.stdout) {
|
|
577
|
-
output += chunk;
|
|
578
|
-
|
|
579
|
-
let nullIndex;
|
|
580
|
-
while ((nullIndex = output.indexOf('\0')) >= 0) {
|
|
581
|
-
const value = output.slice(0, nullIndex);
|
|
582
|
-
|
|
583
|
-
if (status) {
|
|
584
|
-
yield parseDiffLine(status, value);
|
|
585
|
-
status = null;
|
|
586
|
-
} else {
|
|
587
|
-
status = value;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
output = output.slice(nullIndex + 1);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
if (output.length > 0 && status) {
|
|
595
|
-
yield parseDiffLine(status, output);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const exitCode = await exitCodePromise;
|
|
599
|
-
|
|
600
|
-
if (exitCode != 0 || error) {
|
|
601
|
-
throw new Error(`git-diff-tree exited with code ${exitCode}: ${error}`);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
function parseDiffLine (statusLine, path) {
|
|
606
|
-
const [
|
|
607
|
-
srcMode, dstMode,
|
|
608
|
-
srcHash, dstHash,
|
|
609
|
-
status,
|
|
610
|
-
] = statusLine.substr(1).split(' ');
|
|
611
|
-
|
|
612
|
-
return {
|
|
613
|
-
path: path.substr(0, path.length - 5),
|
|
614
|
-
status: DIFF_STATUS_MAP[status[0]],
|
|
615
|
-
statusCount: parseInt(status.substr(1), 10) || null,
|
|
616
|
-
srcMode, dstMode,
|
|
617
|
-
srcHash, dstHash,
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
function rfc6902DiffAny (input, output, ptr) {
|
|
622
|
-
if (input instanceof Date && output instanceof Date && input.valueOf() != output.valueOf()) {
|
|
623
|
-
return [{op: 'replace', path: ptr.toString(), value: output}]
|
|
624
|
-
}
|
|
625
|
-
}
|
package/lib/errors.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
class CustomError extends Error {};
|
|
2
|
-
CustomError.prototype.status = 500;
|
|
3
|
-
|
|
4
|
-
class SerializationError extends CustomError {};
|
|
5
|
-
SerializationError.prototype.status = 422;
|
|
6
|
-
|
|
7
|
-
class ConfigError extends CustomError {};
|
|
8
|
-
ConfigError.prototype.status = 500;
|
|
9
|
-
|
|
10
|
-
class InvalidRefError extends CustomError {};
|
|
11
|
-
InvalidRefError.prototype.status = 404;
|
|
12
|
-
|
|
13
|
-
class MergeError extends CustomError {};
|
|
14
|
-
MergeError.prototype.status = 409;
|
|
15
|
-
|
|
16
|
-
module.exports = {
|
|
17
|
-
SerializationError,
|
|
18
|
-
ConfigError,
|
|
19
|
-
InvalidRefError,
|
|
20
|
-
MergeError,
|
|
21
|
-
};
|
package/lib/hologit.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require('hologit/lib');
|
package/lib/logger.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var emptyFn = function() {};
|
|
4
|
-
|
|
5
|
-
module.exports = (require.main && require.main.exports.logger) || {
|
|
6
|
-
log: emptyFn,
|
|
7
|
-
|
|
8
|
-
error: emptyFn,
|
|
9
|
-
warn: emptyFn,
|
|
10
|
-
help: emptyFn,
|
|
11
|
-
data: emptyFn,
|
|
12
|
-
info: emptyFn,
|
|
13
|
-
debug: emptyFn,
|
|
14
|
-
prompt: emptyFn,
|
|
15
|
-
verbose: emptyFn,
|
|
16
|
-
input: emptyFn,
|
|
17
|
-
silly: emptyFn
|
|
18
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
class BaseComponent
|
|
2
|
-
{
|
|
3
|
-
constructor ({ name, prefix = null, suffix = null }) {
|
|
4
|
-
this.name = name;
|
|
5
|
-
|
|
6
|
-
if (prefix) {
|
|
7
|
-
this.prefix = prefix;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if (suffix) {
|
|
11
|
-
this.suffix = suffix;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
formatValue (value) {
|
|
16
|
-
if (typeof value === 'function' || typeof value === 'undefined') {
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return `${this.prefix||''}${value}${this.suffix||''}`;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
module.exports = BaseComponent;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const vm = require('vm');
|
|
2
|
-
const notDefinedErrorRe = / is not defined$/;
|
|
3
|
-
|
|
4
|
-
class ExpressionComponent extends require('./BaseComponent.js') {
|
|
5
|
-
constructor () {
|
|
6
|
-
super(...arguments);
|
|
7
|
-
this.expression = vm.runInNewContext(`record => { with (record) { return (${this.name}) } }`);
|
|
8
|
-
Object.freeze(this);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
render (record = {}) {
|
|
12
|
-
try {
|
|
13
|
-
return this.formatValue(this.expression(record));
|
|
14
|
-
} catch (err) {
|
|
15
|
-
if (notDefinedErrorRe.test(err.message)) {
|
|
16
|
-
// treat expressions failing due to undefined factors as un-renderable
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// any other error should be fatal
|
|
21
|
-
throw err;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = ExpressionComponent;
|