@cepharum/concrete-db 0.2.1 → 0.3.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/concrete-db.d.ts +20 -32
- package/lib/shaper.mjs +60 -54
- package/package.json +11 -11
package/concrete-db.d.ts
CHANGED
|
@@ -1,32 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* (c) 2022 cepharum GmbH, Berlin, http://cepharum.de
|
|
3
|
-
*
|
|
4
|
-
* The MIT License (MIT)
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2021 cepharum GmbH
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
* in the Software without restriction, including without limitation the rights
|
|
11
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
* furnished to do so, subject to the following conditions:
|
|
14
|
-
*
|
|
15
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
* copies or substantial portions of the Software.
|
|
17
|
-
*
|
|
18
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
* SOFTWARE.
|
|
25
|
-
*
|
|
26
|
-
* @author: cepharum
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
declare module "concrete-db" {
|
|
1
|
+
declare namespace ConcreteDB {
|
|
30
2
|
export interface CuringOptions extends CollectorOptions, ShaperOptions, GeneratorOptions {
|
|
31
3
|
/**
|
|
32
4
|
* Selects base folder for writing files of resulting concrete-db.
|
|
@@ -260,18 +232,34 @@ declare module "concrete-db" {
|
|
|
260
232
|
*/
|
|
261
233
|
export type ShapeCollections = ShapeCollection[];
|
|
262
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Defines optional constraints to apply.
|
|
237
|
+
*/
|
|
238
|
+
export interface ShapeConstraints {
|
|
239
|
+
/**
|
|
240
|
+
* If set, multiple source records addressing same target record aren't
|
|
241
|
+
* merged, but some exception is thrown.
|
|
242
|
+
*
|
|
243
|
+
* This doesn't affect definition of variants. However, merging single
|
|
244
|
+
* variant from multiple sources is rejected, too.
|
|
245
|
+
*/
|
|
246
|
+
singleSource?: boolean;
|
|
247
|
+
}
|
|
248
|
+
|
|
263
249
|
/**
|
|
264
250
|
* Describes rules controlling shaping of a model.
|
|
265
251
|
*/
|
|
266
252
|
export interface ShapeModel {
|
|
267
253
|
defaults: ShapePropertyDefaults;
|
|
268
|
-
properties
|
|
269
|
-
collections
|
|
270
|
-
contributions
|
|
254
|
+
properties?: ShapeProperties;
|
|
255
|
+
collections?: ShapeCollections;
|
|
256
|
+
contributions?: {
|
|
271
257
|
[contributedModelName: string]: {
|
|
272
258
|
[targetPropertyName: string]: string;
|
|
273
259
|
};
|
|
274
260
|
};
|
|
261
|
+
|
|
262
|
+
constraints?: ShapeConstraints;
|
|
275
263
|
}
|
|
276
264
|
|
|
277
265
|
export interface Shape {
|
package/lib/shaper.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// noinspection ExceptionCaughtLocallyJS
|
|
2
|
+
/* eslint-disable no-await-in-loop */
|
|
2
3
|
|
|
3
4
|
import EventEmitter from "events";
|
|
4
5
|
|
|
@@ -50,16 +51,17 @@ export class Shaper extends EventEmitter {
|
|
|
50
51
|
* @param {Shape[]} localShapes local shapes of folders records have been collected from
|
|
51
52
|
* @returns {Database} resulting database records
|
|
52
53
|
*/
|
|
53
|
-
transform( recordsCollection, localShapes ) {
|
|
54
|
+
async transform( recordsCollection, localShapes ) {
|
|
54
55
|
if ( recordsCollection.size > 0 && !localShapes?.length ) {
|
|
55
56
|
throw new Error( "invalid use of Shaper: missing list of local shapes per folder records have been collected from" );
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
const mergedLocalShapes = merge( {}, DefaultShape, ...localShapes );
|
|
59
|
-
const
|
|
60
|
+
const grouped = await this.groupByModel( recordsCollection );
|
|
61
|
+
const models = await this.handleContributions( grouped, mergedLocalShapes );
|
|
60
62
|
const endpoints = {};
|
|
61
63
|
|
|
62
|
-
this.populateModels( endpoints, models, mergedLocalShapes );
|
|
64
|
+
await this.populateModels( endpoints, models, mergedLocalShapes );
|
|
63
65
|
|
|
64
66
|
return { prefix: this.prefix, endpoints };
|
|
65
67
|
}
|
|
@@ -70,7 +72,7 @@ export class Shaper extends EventEmitter {
|
|
|
70
72
|
* @param {Map<string,CollectedRecord[]>} collectedRecordsByName collection of records to transform
|
|
71
73
|
* @returns {Object<string,Object<string, ModelItem>>} provided records grouped by model and unique item ID
|
|
72
74
|
*/
|
|
73
|
-
groupByModel( collectedRecordsByName ) {
|
|
75
|
+
async groupByModel( collectedRecordsByName ) {
|
|
74
76
|
const globalItemsPerModel = {};
|
|
75
77
|
let index = 0;
|
|
76
78
|
|
|
@@ -83,7 +85,7 @@ export class Shaper extends EventEmitter {
|
|
|
83
85
|
process.stderr.write( `\r[${++index}/${collectedRecordsByName.size}]` );
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
this.compileRecords( globalItemsPerModel, records );
|
|
88
|
+
await this.compileRecords( globalItemsPerModel, records );
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
return globalItemsPerModel;
|
|
@@ -97,20 +99,20 @@ export class Shaper extends EventEmitter {
|
|
|
97
99
|
* @param {Shape} mergedLocalShapes local shapes of source folders merged in order of collecting records
|
|
98
100
|
* @returns {Object<string,Object<string, CollectedRecord[]>>} collections of items per model as provided
|
|
99
101
|
*/
|
|
100
|
-
handleContributions( globalItemsPerModel, mergedLocalShapes ) {
|
|
102
|
+
async handleContributions( globalItemsPerModel, mergedLocalShapes ) {
|
|
101
103
|
const postProcessingQueue = new Set();
|
|
102
104
|
|
|
103
105
|
if ( this.options.verbose ) {
|
|
104
106
|
console.error( `\nprocessing contributions ...` );
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
this.processContributions( globalItemsPerModel, postProcessingQueue, mergedLocalShapes );
|
|
109
|
+
await this.processContributions( globalItemsPerModel, postProcessingQueue, mergedLocalShapes );
|
|
108
110
|
|
|
109
111
|
if ( this.options.verbose ) {
|
|
110
112
|
console.error( `post-processing ${postProcessingQueue.size} contributions` );
|
|
111
113
|
}
|
|
112
114
|
|
|
113
|
-
this.recompileContributionTargets( globalItemsPerModel, postProcessingQueue );
|
|
115
|
+
await this.recompileContributionTargets( globalItemsPerModel, postProcessingQueue );
|
|
114
116
|
|
|
115
117
|
return globalItemsPerModel;
|
|
116
118
|
}
|
|
@@ -123,11 +125,11 @@ export class Shaper extends EventEmitter {
|
|
|
123
125
|
* @param {CollectedRecord[]} records list of records collected from a source
|
|
124
126
|
* @returns {Object<string,Object<string,ModelItem>>} compiled items grouped by model, addressable by either item's ID
|
|
125
127
|
*/
|
|
126
|
-
compileRecords( itemsPerModel, records ) {
|
|
128
|
+
async compileRecords( itemsPerModel, records ) {
|
|
127
129
|
const shapesPerModelCache = {};
|
|
128
130
|
|
|
129
131
|
for ( let read = 0, numRecords = records.length; read < numRecords; read++ ) {
|
|
130
|
-
const item = this.compileModelItemFromRecord( shapesPerModelCache, itemsPerModel, records[read] );
|
|
132
|
+
const item = await this.compileModelItemFromRecord( shapesPerModelCache, itemsPerModel, records[read] );
|
|
131
133
|
const modelPool = itemsPerModel[item.$model];
|
|
132
134
|
|
|
133
135
|
if ( item.$variantId == null ) {
|
|
@@ -154,9 +156,9 @@ export class Shaper extends EventEmitter {
|
|
|
154
156
|
* @param {Object<string,ShapeModel>} shapesPerModelCache caches per-model shapes compiled before
|
|
155
157
|
* @param {Object<string,Object<string,Object>>} itemsPerModel collection of previously compiled items per model
|
|
156
158
|
* @param {CollectedRecord} record augmented data collected from a source to be compiled
|
|
157
|
-
* @returns {
|
|
159
|
+
* @returns {Promise<ModelItem>} instance of model with hidden meta information injected
|
|
158
160
|
*/
|
|
159
|
-
compileModelItemFromRecord( shapesPerModelCache, itemsPerModel, record ) {
|
|
161
|
+
async compileModelItemFromRecord( shapesPerModelCache, itemsPerModel, record ) {
|
|
160
162
|
const { segments, shape, data } = record;
|
|
161
163
|
|
|
162
164
|
const metaProperties = {
|
|
@@ -165,9 +167,9 @@ export class Shaper extends EventEmitter {
|
|
|
165
167
|
|
|
166
168
|
const augmentedData = augmentDataSpace( data, metaProperties );
|
|
167
169
|
|
|
168
|
-
metaProperties.$model = this.detectModel( augmentedData, shape );
|
|
169
|
-
metaProperties.$shape = this.getShapeOfModel(
|
|
170
|
-
metaProperties.$id = this.identifyRecord( augmentedData, metaProperties.$shape );
|
|
170
|
+
metaProperties.$model = await this.detectModel( augmentedData, shape );
|
|
171
|
+
metaProperties.$shape = this.getShapeOfModel( shapesPerModelCache, shape, metaProperties.$model );
|
|
172
|
+
metaProperties.$id = await this.identifyRecord( augmentedData, metaProperties.$shape );
|
|
171
173
|
|
|
172
174
|
if ( !itemsPerModel.hasOwnProperty( metaProperties.$model ) ) {
|
|
173
175
|
// eslint-disable-next-line no-param-reassign
|
|
@@ -177,7 +179,7 @@ export class Shaper extends EventEmitter {
|
|
|
177
179
|
const model = itemsPerModel[metaProperties.$model];
|
|
178
180
|
|
|
179
181
|
const { properties, defaults } = metaProperties.$shape;
|
|
180
|
-
const item = this.compileItem( augmentedData, properties, model[metaProperties.$id] ? {} : defaults );
|
|
182
|
+
const item = await this.compileItem( augmentedData, properties, model[metaProperties.$id] ? {} : defaults );
|
|
181
183
|
|
|
182
184
|
Object.defineProperties( item, {
|
|
183
185
|
$segments: { value: segments },
|
|
@@ -185,7 +187,7 @@ export class Shaper extends EventEmitter {
|
|
|
185
187
|
$shape: { value: metaProperties.$shape, configurable: true },
|
|
186
188
|
$model: { value: metaProperties.$model },
|
|
187
189
|
$id: { value: metaProperties.$id },
|
|
188
|
-
$variantId: { value: this.computeVariantIdOfItem( metaProperties.$shape.properties.$variant, augmentedData ) },
|
|
190
|
+
$variantId: { value: await this.computeVariantIdOfItem( metaProperties.$shape.properties.$variant, augmentedData ) },
|
|
189
191
|
} );
|
|
190
192
|
|
|
191
193
|
return item;
|
|
@@ -200,9 +202,9 @@ export class Shaper extends EventEmitter {
|
|
|
200
202
|
* @param {ModelItem} item previously compiled item to re-compile
|
|
201
203
|
* @returns {ModelItem} re-compiled item
|
|
202
204
|
*/
|
|
203
|
-
recompileItem( item ) {
|
|
205
|
+
async recompileItem( item ) {
|
|
204
206
|
const data = augmentDataSpace( merge( {}, item.$original, item ), { ...item, $post: true } );
|
|
205
|
-
const updatedItem = this.compileItem( data, item.$shape.properties, {} );
|
|
207
|
+
const updatedItem = await this.compileItem( data, item.$shape.properties, {} );
|
|
206
208
|
|
|
207
209
|
Object.defineProperties( updatedItem, {
|
|
208
210
|
$segments: { value: item.$segments },
|
|
@@ -221,9 +223,9 @@ export class Shaper extends EventEmitter {
|
|
|
221
223
|
*
|
|
222
224
|
* @param {string?} term term expressing computation of variant ID
|
|
223
225
|
* @param {ModelItem} item an item to get variant ID for
|
|
224
|
-
* @returns {null|any} computed variant ID of provided item, nullish if item is not a variant
|
|
226
|
+
* @returns {Promise<null|any>} computed variant ID of provided item, nullish if item is not a variant
|
|
225
227
|
*/
|
|
226
|
-
computeVariantIdOfItem( term, item ) {
|
|
228
|
+
async computeVariantIdOfItem( term, item ) {
|
|
227
229
|
if ( term == null ) {
|
|
228
230
|
return null;
|
|
229
231
|
}
|
|
@@ -232,7 +234,7 @@ export class Shaper extends EventEmitter {
|
|
|
232
234
|
const data = augmentDataSpace( merge( {}, item.$original, item ), item );
|
|
233
235
|
|
|
234
236
|
try {
|
|
235
|
-
const value = Compiler.compile( String( term ), this.termFunctions, this.termsCache )( data );
|
|
237
|
+
const value = await Compiler.compile( String( term ), this.termFunctions, this.termsCache )( data );
|
|
236
238
|
|
|
237
239
|
// support processed term requesting to fail computation of variant ID
|
|
238
240
|
if ( value instanceof Error ) {
|
|
@@ -354,7 +356,7 @@ export class Shaper extends EventEmitter {
|
|
|
354
356
|
* @param {Shape} mergedLocalShapes local shapes of source folders merged in order of collecting records
|
|
355
357
|
* @returns {void}
|
|
356
358
|
*/
|
|
357
|
-
processContributions( itemsPerModel, contributedItems, mergedLocalShapes ) {
|
|
359
|
+
async processContributions( itemsPerModel, contributedItems, mergedLocalShapes ) {
|
|
358
360
|
const contributingModels = this.listContributingModels( itemsPerModel );
|
|
359
361
|
const shapePerModelCache = {};
|
|
360
362
|
|
|
@@ -427,7 +429,7 @@ export class Shaper extends EventEmitter {
|
|
|
427
429
|
};
|
|
428
430
|
|
|
429
431
|
const augmentedData = augmentDataSpace( item, contributionMeta );
|
|
430
|
-
const targetId = this.computeId( contributionTerms.$id, augmentedData );
|
|
432
|
+
const targetId = await this.computeId( contributionTerms.$id, augmentedData );
|
|
431
433
|
|
|
432
434
|
if ( !isOptionalContribution && ( !targetId || ( Array.isArray( targetId ) && !targetId.length ) ) ) {
|
|
433
435
|
console.warn( `ignoring contribution of record #${item.$id} in model ${item.$model} to model ${targetModelName} due to lack of target ID` ); // eslint-disable-line max-len
|
|
@@ -446,7 +448,7 @@ export class Shaper extends EventEmitter {
|
|
|
446
448
|
|
|
447
449
|
|
|
448
450
|
// try computing variant ID based on contributing item
|
|
449
|
-
const variantId = this.computeVariantIdOfItem( contributionTerms.$variant, {
|
|
451
|
+
const variantId = await this.computeVariantIdOfItem( contributionTerms.$variant, {
|
|
450
452
|
...item,
|
|
451
453
|
$segments: item.$segments,
|
|
452
454
|
$original: item.$original,
|
|
@@ -499,7 +501,7 @@ export class Shaper extends EventEmitter {
|
|
|
499
501
|
|
|
500
502
|
// compile actual properties of contribution
|
|
501
503
|
const contributionDefaults = targetItemExists || variantId != null ? {} : targetModelShape.defaults;
|
|
502
|
-
const contribution = this.compileItem( augmentedData, contributionTerms, contributionDefaults );
|
|
504
|
+
const contribution = await this.compileItem( augmentedData, contributionTerms, contributionDefaults );
|
|
503
505
|
|
|
504
506
|
|
|
505
507
|
// merge this item's contribution with existing
|
|
@@ -551,6 +553,10 @@ export class Shaper extends EventEmitter {
|
|
|
551
553
|
* @returns {ModelItem} provided target
|
|
552
554
|
*/
|
|
553
555
|
mergeModelItem( target, source ) {
|
|
556
|
+
if ( target?.$shape?.constraints?.singleSource ) {
|
|
557
|
+
throw new Error( `merging ${target.$model} with ID ${target.$id} from multiple source records rejected by shape` );
|
|
558
|
+
}
|
|
559
|
+
|
|
554
560
|
const _to = target || {};
|
|
555
561
|
const oldNames = target ? Object.keys( _to.$shape.properties ) : [];
|
|
556
562
|
const newNames = Object.keys( source.$shape.properties );
|
|
@@ -558,8 +564,8 @@ export class Shaper extends EventEmitter {
|
|
|
558
564
|
// merge properties of items
|
|
559
565
|
for ( const name of Object.keys( source ) ) {
|
|
560
566
|
if ( !String( name ).startsWith( "$" ) ) {
|
|
561
|
-
const wasCollecting = oldNames.some( n => n.replace( /\s
|
|
562
|
-
const isCollecting = newNames.some( n => n.replace( /\s
|
|
567
|
+
const wasCollecting = oldNames.some( n => n.replace( /\s+/g, "" ) === name + "[]" );
|
|
568
|
+
const isCollecting = newNames.some( n => n.replace( /\s+/g, "" ) === name + "[]" );
|
|
563
569
|
|
|
564
570
|
if ( _to.hasOwnProperty( name ) ) {
|
|
565
571
|
if ( isCollecting ) {
|
|
@@ -610,7 +616,7 @@ export class Shaper extends EventEmitter {
|
|
|
610
616
|
* @param {Set<string>} contributions lists IDs of items or their variants that have been contributed to
|
|
611
617
|
* @returns {Object<string,Object<string,ModelItem>>} pool of items per model as provided
|
|
612
618
|
*/
|
|
613
|
-
recompileContributionTargets( itemsPerModel, contributions ) {
|
|
619
|
+
async recompileContributionTargets( itemsPerModel, contributions ) {
|
|
614
620
|
for ( const track of contributions.values() ) {
|
|
615
621
|
const [ model, id, variantId ] = JSON.parse( track );
|
|
616
622
|
|
|
@@ -629,7 +635,7 @@ export class Shaper extends EventEmitter {
|
|
|
629
635
|
const item = modelPool[id];
|
|
630
636
|
|
|
631
637
|
if ( variantId == null ) {
|
|
632
|
-
modelPool[id] = this.recompileItem( item );
|
|
638
|
+
modelPool[id] = await this.recompileItem( item );
|
|
633
639
|
} else {
|
|
634
640
|
if ( !item.$variants ) {
|
|
635
641
|
console.warn( `inconsistency: missing variants of item ${id} of model ${model} though contributions have been tracked` );
|
|
@@ -643,7 +649,7 @@ export class Shaper extends EventEmitter {
|
|
|
643
649
|
continue;
|
|
644
650
|
}
|
|
645
651
|
|
|
646
|
-
variants[variantId] = this.recompileItem( variants[variantId] );
|
|
652
|
+
variants[variantId] = await this.recompileItem( variants[variantId] );
|
|
647
653
|
}
|
|
648
654
|
}
|
|
649
655
|
|
|
@@ -655,16 +661,16 @@ export class Shaper extends EventEmitter {
|
|
|
655
661
|
*
|
|
656
662
|
* @param {object} data source of record
|
|
657
663
|
* @param {Shape} shape shape of database affecting record
|
|
658
|
-
* @returns {string} name of model to use
|
|
664
|
+
* @returns {Promise<string>} name of model to use
|
|
659
665
|
*/
|
|
660
|
-
detectModel( data, shape ) {
|
|
666
|
+
async detectModel( data, shape ) {
|
|
661
667
|
const selector = shape?.common?.properties?.$model;
|
|
662
668
|
|
|
663
669
|
if ( !selector || typeof selector !== "string" ) {
|
|
664
670
|
throw new Error( "missing or invalid definition of common property $model in shape" );
|
|
665
671
|
}
|
|
666
672
|
|
|
667
|
-
const name = Compiler.compile( selector, this.termFunctions, this.termsCache )( data );
|
|
673
|
+
const name = await Compiler.compile( selector, this.termFunctions, this.termsCache )( data );
|
|
668
674
|
|
|
669
675
|
// support processed term requesting to fail model detection
|
|
670
676
|
if ( name instanceof Error ) {
|
|
@@ -683,10 +689,10 @@ export class Shaper extends EventEmitter {
|
|
|
683
689
|
*
|
|
684
690
|
* @param {object} data source of record
|
|
685
691
|
* @param {ShapeModel} modelShape shape of particular model
|
|
686
|
-
* @returns {string} ID of record
|
|
692
|
+
* @returns {Promise<string>} ID of record
|
|
687
693
|
*/
|
|
688
|
-
identifyRecord( data, modelShape ) {
|
|
689
|
-
const id = this.computeId( modelShape?.properties?.$id, data );
|
|
694
|
+
async identifyRecord( data, modelShape ) {
|
|
695
|
+
const id = await this.computeId( modelShape?.properties?.$id, data );
|
|
690
696
|
|
|
691
697
|
if ( !id ) {
|
|
692
698
|
throw new Error( `detecting ID of item related to record ${data.$segments.join( "/" )} failed` );
|
|
@@ -702,14 +708,14 @@ export class Shaper extends EventEmitter {
|
|
|
702
708
|
* @param {ShapeProperties} properties definition of resulting item's properties
|
|
703
709
|
* @param {ShapePropertyDefaults} defaults definition of default values for resulting item's properties
|
|
704
710
|
* @param {string[]} accept list names of properties to accept even when starting with a dollar sign
|
|
705
|
-
* @returns {
|
|
711
|
+
* @returns {Promise<Partial<ModelItem>>} compiled item
|
|
706
712
|
*/
|
|
707
|
-
compileItem( data, properties = {}, defaults = {}, accept = [] ) {
|
|
713
|
+
async compileItem( data, properties = {}, defaults = {}, accept = [] ) {
|
|
708
714
|
const item = {};
|
|
709
715
|
|
|
710
716
|
for ( const property of Object.keys( properties ) ) {
|
|
711
717
|
if ( accept.indexOf( property ) > -1 || !String( property ).startsWith( "$" ) ) {
|
|
712
|
-
const propertyName = property.replace( /\s*\[]
|
|
718
|
+
const propertyName = property.replace( /\s*\[]\s*$/, "" );
|
|
713
719
|
const isCollecting = property !== propertyName;
|
|
714
720
|
|
|
715
721
|
const term = properties[property];
|
|
@@ -724,7 +730,7 @@ export class Shaper extends EventEmitter {
|
|
|
724
730
|
let value;
|
|
725
731
|
|
|
726
732
|
try {
|
|
727
|
-
value = Compiler.compile( term, this.termFunctions, this.termsCache )( data );
|
|
733
|
+
value = await Compiler.compile( term, this.termFunctions, this.termsCache )( data );
|
|
728
734
|
|
|
729
735
|
// support processed term requesting to fail computation of property's value
|
|
730
736
|
if ( value instanceof Error ) {
|
|
@@ -741,7 +747,7 @@ export class Shaper extends EventEmitter {
|
|
|
741
747
|
|
|
742
748
|
try {
|
|
743
749
|
const code = trimmed.startsWith( "=" ) ? trimmed.slice( 1 ) : undefined;
|
|
744
|
-
value = code ? Compiler.compile( code, this.termFunctions, this.termsCache )( data ) : defaultValue;
|
|
750
|
+
value = code ? await Compiler.compile( code, this.termFunctions, this.termsCache )( data ) : defaultValue;
|
|
745
751
|
|
|
746
752
|
// support processed term requesting to fail computation of default value
|
|
747
753
|
if ( value instanceof Error ) {
|
|
@@ -779,15 +785,15 @@ export class Shaper extends EventEmitter {
|
|
|
779
785
|
*
|
|
780
786
|
* @param {string} term term to process
|
|
781
787
|
* @param {object} data data space available to term processing
|
|
782
|
-
* @returns {any} processed term's result
|
|
788
|
+
* @returns {Promise<any>} processed term's result
|
|
783
789
|
*/
|
|
784
|
-
computeId( term, data ) {
|
|
790
|
+
async computeId( term, data ) {
|
|
785
791
|
if ( !term || typeof term !== "string" ) {
|
|
786
792
|
throw new Error( `missing or invalid term for computing ID of ${data.$segments.join( "/" )}` );
|
|
787
793
|
}
|
|
788
794
|
|
|
789
795
|
try {
|
|
790
|
-
const value = Compiler.compile( term, this.termFunctions, this.termsCache )( data );
|
|
796
|
+
const value = await Compiler.compile( term, this.termFunctions, this.termsCache )( data );
|
|
791
797
|
|
|
792
798
|
// support processed term requesting to fail computation of ID
|
|
793
799
|
if ( value instanceof Error ) {
|
|
@@ -808,7 +814,7 @@ export class Shaper extends EventEmitter {
|
|
|
808
814
|
* @param {Shape} mergedLocalShapes local shapes of source folders merged in order of collecting records
|
|
809
815
|
* @returns {DatabaseEndpoints} map of routes into data sets to expose eventually
|
|
810
816
|
*/
|
|
811
|
-
populateModels( endpoints, models, mergedLocalShapes ) {
|
|
817
|
+
async populateModels( endpoints, models, mergedLocalShapes ) {
|
|
812
818
|
const modelNames = Object.keys( models );
|
|
813
819
|
let index = 0;
|
|
814
820
|
|
|
@@ -833,7 +839,7 @@ export class Shaper extends EventEmitter {
|
|
|
833
839
|
endpoints[normalized] = model[itemId]; // eslint-disable-line no-param-reassign
|
|
834
840
|
}
|
|
835
841
|
|
|
836
|
-
this.populateModelCollections( endpoints, models, modelName, mergedLocalShapes );
|
|
842
|
+
await this.populateModelCollections( endpoints, models, modelName, mergedLocalShapes );
|
|
837
843
|
}
|
|
838
844
|
|
|
839
845
|
return endpoints;
|
|
@@ -849,7 +855,7 @@ export class Shaper extends EventEmitter {
|
|
|
849
855
|
* @param {Shape} mergedLocalShapes local shapes of source folders merged in order of collecting records
|
|
850
856
|
* @returns {DatabaseEndpoints} map of routes into data sets to expose eventually
|
|
851
857
|
*/
|
|
852
|
-
populateModelCollections( endpoints, models, modelName, mergedLocalShapes ) {
|
|
858
|
+
async populateModelCollections( endpoints, models, modelName, mergedLocalShapes ) {
|
|
853
859
|
const finalModelShape = merge( {}, mergedLocalShapes.common, mergedLocalShapes.models[modelName] );
|
|
854
860
|
const collections = finalModelShape.collections || {};
|
|
855
861
|
const cache = {};
|
|
@@ -893,7 +899,7 @@ export class Shaper extends EventEmitter {
|
|
|
893
899
|
collection = cache[definition.reference];
|
|
894
900
|
isKeyed = collection.$isKeyed;
|
|
895
901
|
} else {
|
|
896
|
-
collection = this.compileCollection( models, modelName, definition, normalized );
|
|
902
|
+
collection = await this.compileCollection( models, modelName, definition, normalized );
|
|
897
903
|
isKeyed = definition.key && typeof definition.key === "string";
|
|
898
904
|
|
|
899
905
|
Object.defineProperties( collection, {
|
|
@@ -964,7 +970,7 @@ export class Shaper extends EventEmitter {
|
|
|
964
970
|
const items = collection.items;
|
|
965
971
|
|
|
966
972
|
for ( let offset = 0; offset < items.length; offset += limit ) {
|
|
967
|
-
const normalized = normalizePathname( prefix + route.replace( ptnOffset, offset ) );
|
|
973
|
+
const normalized = normalizePathname( prefix + route.replace( ptnOffset, String( offset ) ) );
|
|
968
974
|
endpoints[normalized] = { count: items.length, items: items.slice( offset, offset + limit ) }; // eslint-disable-line no-param-reassign
|
|
969
975
|
}
|
|
970
976
|
} else if ( isKeyed ) {
|
|
@@ -1032,7 +1038,7 @@ export class Shaper extends EventEmitter {
|
|
|
1032
1038
|
* @param {string} route route for resulting collection
|
|
1033
1039
|
* @returns {DatabaseCollection} compiled collection
|
|
1034
1040
|
*/
|
|
1035
|
-
compileCollection( models, modelName, definition, route ) {
|
|
1041
|
+
async compileCollection( models, modelName, definition, route ) {
|
|
1036
1042
|
const model = models[modelName];
|
|
1037
1043
|
const { key: $key, filter: $filter, properties: $properties } = definition;
|
|
1038
1044
|
const isKeyed = $key && typeof $key === "string";
|
|
@@ -1103,9 +1109,9 @@ export class Shaper extends EventEmitter {
|
|
|
1103
1109
|
context.$id = id;
|
|
1104
1110
|
|
|
1105
1111
|
// ignore this item or prepare its spot in resulting collection?
|
|
1106
|
-
if ( !filterFn || filterFn( data ) ) {
|
|
1112
|
+
if ( !filterFn || await filterFn( data ) ) {
|
|
1107
1113
|
if ( isKeyed ) {
|
|
1108
|
-
const key = keyFn( data );
|
|
1114
|
+
const key = await keyFn( data );
|
|
1109
1115
|
if ( key != null ) {
|
|
1110
1116
|
const _key = String( key );
|
|
1111
1117
|
|
|
@@ -1131,7 +1137,7 @@ export class Shaper extends EventEmitter {
|
|
|
1131
1137
|
if ( propertiesFn ) {
|
|
1132
1138
|
// compile custom representation of item in collection
|
|
1133
1139
|
for ( const fn of propertiesFn ) {
|
|
1134
|
-
extracted[fn.target] = fn( data );
|
|
1140
|
+
extracted[fn.target] = await fn( data );
|
|
1135
1141
|
}
|
|
1136
1142
|
} else {
|
|
1137
1143
|
// expose all properties of item in collection, too
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cepharum/concrete-db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "a read-only web database generator",
|
|
5
5
|
"main": "lib/collector.mjs",
|
|
6
6
|
"types": "concrete-db.d.ts",
|
|
@@ -20,22 +20,22 @@
|
|
|
20
20
|
"author": "cepharum GmbH",
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"ajv": "^8.
|
|
23
|
+
"ajv": "^8.12.0",
|
|
24
24
|
"file-essentials": "^0.1.2",
|
|
25
25
|
"lodash.merge": "^4.6.2",
|
|
26
|
-
"minimist": "^1.2.
|
|
26
|
+
"minimist": "^1.2.8",
|
|
27
27
|
"promise-essentials": "^0.2.0",
|
|
28
|
-
"simple-terms": "^0.4.
|
|
29
|
-
"yaml": "^2.
|
|
28
|
+
"simple-terms": "^0.4.1",
|
|
29
|
+
"yaml": "^2.2.2"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"c8": "^7.
|
|
33
|
-
"eslint": "^8.
|
|
34
|
-
"eslint-config-cepharum": "^1.0.
|
|
35
|
-
"eslint-plugin-promise": "^6.
|
|
36
|
-
"mocha": "^10.
|
|
32
|
+
"c8": "^7.13.0",
|
|
33
|
+
"eslint": "^8.40.0",
|
|
34
|
+
"eslint-config-cepharum": "^1.0.13",
|
|
35
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
36
|
+
"mocha": "^10.2.0",
|
|
37
37
|
"should": "^13.2.3",
|
|
38
|
-
"vuepress": "^1.9.
|
|
38
|
+
"vuepress": "^1.9.9",
|
|
39
39
|
"vuepress-plugin-mermaidjs": "^1.9.1"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|