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/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;