@opra/core 0.8.0 → 0.12.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/esm/index.d.ts CHANGED
@@ -8,5 +8,4 @@ export * from './adapter/adapter.js';
8
8
  export * from './adapter/http-adapter.js';
9
9
  export * from './adapter/express-adapter.js';
10
10
  export * from './services/data-service.js';
11
- export * from './services/json-collection-service.js';
12
11
  export * from './services/json-singleton-service.js';
package/esm/index.js CHANGED
@@ -8,5 +8,4 @@ export * from './adapter/adapter.js';
8
8
  export * from './adapter/http-adapter.js';
9
9
  export * from './adapter/express-adapter.js';
10
10
  export * from './services/data-service.js';
11
- export * from './services/json-collection-service.js';
12
11
  export * from './services/json-singleton-service.js';
@@ -2,7 +2,7 @@
2
2
  /// <reference types="node" />
3
3
  import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'http';
4
4
  import { Readable, Writable } from 'stream';
5
- export declare type ContextType = 'http' | 'ws' | 'rpc';
5
+ export type ContextType = 'http' | 'ws' | 'rpc';
6
6
  export declare namespace IExecutionContext {
7
7
  type OnFinishArgs = {
8
8
  userContext: any;
@@ -0,0 +1,7 @@
1
+ export interface ILogger {
2
+ log(message: any, ...optionalParams: any[]): any;
3
+ error(message: any, ...optionalParams: any[]): any;
4
+ warn(message: any, ...optionalParams: any[]): any;
5
+ debug?(message: any, ...optionalParams: any[]): any;
6
+ verbose?(message: any, ...optionalParams: any[]): any;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -3,6 +3,7 @@ import { ResourceInfo } from '@opra/common';
3
3
  import { PartialOutput } from '../types.js';
4
4
  export interface IResource {
5
5
  init?(resource: ResourceInfo): void | Promise<void>;
6
+ shutDown?(): void | Promise<void>;
6
7
  }
7
8
  export interface ICollectionResource<T, TOutput = PartialOutput<T>> extends IResource {
8
9
  create?(...args: any[]): TOutput | Promise<TOutput>;
@@ -1,4 +1,5 @@
1
- import { isNil, omitBy } from 'lodash';
1
+ import isNil from 'lodash.isnil';
2
+ import omitBy from 'lodash.omitby';
2
3
  import { SingletonResourceInfo } from '@opra/common';
3
4
  export class JsonSingletonService {
4
5
  constructor(dataType, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/core",
3
- "version": "0.8.0",
3
+ "version": "0.12.0",
4
4
  "description": "Opra schema package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -19,32 +19,33 @@
19
19
  "_copy_pkg_files": "cp README.md package.json ../../LICENSE ../../build/core && cp ../../package.cjs.json ../../build/core/cjs/package.json",
20
20
  "_copyi18n": "cp -R i18n ../../build/core/i18n",
21
21
  "lint": "eslint . --max-warnings=0",
22
- "test": "jest",
23
- "cover": "jest --collect-coverage",
22
+ "test": "NODE_OPTIONS=--experimental-vm-modules npx jest",
23
+ "cover": "NODE_OPTIONS=--experimental-vm-modules npx jest --collect-coverage",
24
24
  "clean": "npm run clean:src && npm run clean:dist && npm run clean:cover",
25
- "clean:src": "ts-cleanup -s src --all",
25
+ "clean:src": "ts-cleanup -s src --all && ts-cleanup -s test --all",
26
26
  "clean:dist": "rimraf ../../build/core",
27
27
  "clean:cover": "rimraf ../../coverage/core"
28
28
  },
29
29
  "dependencies": {
30
- "@nano-sql/core": "^2.3.7",
31
- "@opra/common": "^0.8.0",
32
- "body-parser": "^1.20.1",
33
- "dicer": "^0.3.1",
34
- "express": "^4.18.2",
35
- "http-parser-js": "^0.5.8",
36
- "lodash": "^4.17.21",
37
- "power-tasks": "^1.6.1",
38
- "putil-isplainobject": "^1.1.4",
39
- "putil-merge": "^3.9.0",
40
- "putil-varhelpers": "^1.6.4",
41
- "strict-typed-events": "^2.3.1",
42
- "ts-gems": "^2.3.0"
30
+ "@opra/common": "^0.12.0",
31
+ "lodash.isnil": "^4.0.0",
32
+ "lodash.omitby": "^4.6.0",
33
+ "power-tasks": "^1.6.4",
34
+ "putil-isplainobject": "^1.1.5",
35
+ "putil-merge": "^3.10.1",
36
+ "putil-varhelpers": "^1.6.5",
37
+ "strict-typed-events": "^2.3.1"
38
+ },
39
+ "peerDependencies": {
40
+ "body-parser": ">= 1.0.0",
41
+ "express": "^4.x.x || ^5.x.x"
43
42
  },
44
43
  "devDependencies": {
45
44
  "@faker-js/faker": "^7.6.0",
46
45
  "@types/dicer": "^0.2.2",
47
- "@types/express": "^4.17.14"
46
+ "@types/express": "^4.17.16",
47
+ "cors": "^2.8.5",
48
+ "ts-gems": "^2.3.0"
48
49
  },
49
50
  "type": "module",
50
51
  "types": "esm/index.d.ts",
@@ -79,4 +80,4 @@
79
80
  "http",
80
81
  "web"
81
82
  ]
82
- }
83
+ }
@@ -1,497 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JsonCollectionService = void 0;
4
- const tslib_1 = require("tslib");
5
- const lodash_1 = require("lodash");
6
- const putil_merge_1 = tslib_1.__importDefault(require("putil-merge"));
7
- const core_1 = require("@nano-sql/core");
8
- const common_1 = require("@opra/common");
9
- let dbId = 1;
10
- const indexingTypes = ['int', 'float', 'number', 'date', 'string'];
11
- class JsonCollectionService {
12
- constructor(resource, options) {
13
- this.resource = resource;
14
- this._status = '';
15
- if (this.resource.keyFields.length > 1)
16
- throw new TypeError('JsonDataService currently doesn\'t support multiple primary keys');
17
- this.defaultLimit = options?.defaultLimit ?? 10;
18
- this._initData = options?.data;
19
- }
20
- get dataType() {
21
- return this.resource.dataType;
22
- }
23
- get primaryKey() {
24
- return this.resource.keyFields[0];
25
- }
26
- get resourceName() {
27
- return this.resource.name;
28
- }
29
- async close() {
30
- await this._waitInitializing();
31
- if (this._status === 'initialized') {
32
- this._status = 'initializing';
33
- try {
34
- await (0, core_1.nSQL)().disconnect(this._dbName);
35
- }
36
- finally {
37
- this._status = '';
38
- }
39
- }
40
- }
41
- async processRequest(ctx) {
42
- const prepared = this._prepare(ctx.query);
43
- const fn = this[prepared.method];
44
- if (!fn)
45
- throw new TypeError(`Unimplemented method (${prepared.method})`);
46
- // @ts-ignore
47
- return fn.apply(this, prepared.args);
48
- }
49
- async get(keyValue, options) {
50
- await this._init();
51
- const select = this._convertSelect({
52
- pick: options?.pick,
53
- omit: options?.omit,
54
- include: options?.include,
55
- });
56
- (0, core_1.nSQL)().useDatabase(this._dbName);
57
- try {
58
- const rows = await (0, core_1.nSQL)(this.resourceName)
59
- .query('select', select)
60
- .where([this.primaryKey, '=', keyValue])
61
- .exec();
62
- return unFlatten(rows[0]);
63
- }
64
- catch (e) {
65
- throw e;
66
- }
67
- }
68
- async count(options) {
69
- await this._init();
70
- (0, core_1.nSQL)().useDatabase(this._dbName);
71
- const rows = await (0, core_1.nSQL)(this.resourceName)
72
- .query('select', ['COUNT(*) as count'])
73
- .where(options?.filter || [])
74
- .exec();
75
- return (rows[0]?.count) || 0;
76
- }
77
- async search(options) {
78
- await this._init();
79
- const select = this._convertSelect({
80
- pick: options?.pick,
81
- omit: options?.omit,
82
- include: options?.include,
83
- });
84
- const filter = this._convertFilter(options?.filter);
85
- (0, core_1.nSQL)().useDatabase(this._dbName);
86
- const query = (0, core_1.nSQL)(this.resourceName)
87
- .query('select', select)
88
- .limit(options?.limit || 10)
89
- .offset(options?.skip || 0)
90
- .orderBy(options?.sort || [])
91
- .where(filter || []);
92
- return (await query.exec()).map(x => unFlatten(x));
93
- }
94
- async create(data, options) {
95
- if (!data[this.primaryKey])
96
- throw new common_1.BadRequestError({
97
- message: 'You must provide primary key value'
98
- });
99
- await this._init();
100
- const keyValue = data[this.primaryKey];
101
- (0, core_1.nSQL)().useDatabase(this._dbName);
102
- const rows = await (0, core_1.nSQL)(this.resourceName).query('select', [this.primaryKey])
103
- .where([this.primaryKey, '=', keyValue])
104
- .exec();
105
- if (rows.length)
106
- throw new common_1.ResourceConflictError(this.resourceName, this.primaryKey);
107
- await (0, core_1.nSQL)(this.resourceName).query('upsert', data)
108
- .exec();
109
- return await this.get(keyValue, options);
110
- }
111
- async update(keyValue, data, options) {
112
- await this._init();
113
- (0, core_1.nSQL)().useDatabase(this._dbName);
114
- await (0, core_1.nSQL)(this.resourceName)
115
- .query('conform rows', (row) => {
116
- const out = (0, putil_merge_1.default)({}, row, { deep: true, clone: true });
117
- (0, putil_merge_1.default)(out, data, { deep: true });
118
- return out;
119
- })
120
- .where([this.primaryKey, '=', keyValue])
121
- .exec();
122
- // await nSQL(this.resourceName).query("rebuild indexes").exec();
123
- return this.get(keyValue, options);
124
- }
125
- async updateMany(data, options) {
126
- await this._init();
127
- const filter = this._convertFilter(options?.filter);
128
- (0, core_1.nSQL)().useDatabase(this._dbName);
129
- let affected = 0;
130
- await (0, core_1.nSQL)(this.resourceName)
131
- .query('conform rows', (row) => {
132
- const out = (0, putil_merge_1.default)({}, row, { deep: true, clone: true });
133
- (0, putil_merge_1.default)(out, data, { deep: true });
134
- affected++;
135
- return out;
136
- })
137
- .where(filter)
138
- .exec();
139
- // await nSQL(this.resourceName).query("rebuild indexes").exec();
140
- return affected;
141
- }
142
- async delete(keyValue) {
143
- await this._init();
144
- (0, core_1.nSQL)().useDatabase(this._dbName);
145
- const result = await (0, core_1.nSQL)(this.resourceName)
146
- .query('delete')
147
- .where([this.primaryKey, '=', keyValue])
148
- .exec();
149
- return !!result.length;
150
- }
151
- async deleteMany(options) {
152
- await this._init();
153
- const filter = this._convertFilter(options?.filter);
154
- (0, core_1.nSQL)().useDatabase(this._dbName);
155
- const result = await (0, core_1.nSQL)(this.resourceName)
156
- .query('delete')
157
- .where(filter)
158
- .exec();
159
- return result.length;
160
- }
161
- async _waitInitializing() {
162
- if (this._status === 'initializing') {
163
- return new Promise((resolve, reject) => {
164
- const reTry = () => setTimeout(() => {
165
- if (this._status === '')
166
- return resolve(this._init());
167
- if (this._status === 'error')
168
- return reject(this._initError);
169
- if (this._status === 'initialized')
170
- return resolve();
171
- reTry();
172
- }, 50).unref();
173
- reTry();
174
- });
175
- }
176
- }
177
- async _init() {
178
- await this._waitInitializing();
179
- if (this._status === 'initialized')
180
- return;
181
- this._status = 'initializing';
182
- this._dbName = 'JsonDataService_DB_' + (dbId++);
183
- try {
184
- const table = {
185
- name: this.resourceName,
186
- model: {},
187
- indexes: {}
188
- };
189
- for (const [k, f] of this.resource.dataType.fields.entries()) {
190
- const fieldType = this.resource.document.getDataType(f.type || 'string');
191
- const o = table.model[k + ':' + dataTypeToSQLType(fieldType, !!f.isArray)] = {};
192
- if (k === this.primaryKey)
193
- o.pk = true;
194
- }
195
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
196
- const indexes = table.indexes;
197
- // Add indexes for sort fields
198
- const searchResolver = this.resource.metadata.search;
199
- if (searchResolver) {
200
- if (searchResolver.sortFields) {
201
- searchResolver.sortFields.forEach(fieldName => {
202
- const f = this.dataType.getField(fieldName);
203
- const fieldType = this.resource.document.getDataType(f.type || 'string');
204
- const t = dataTypeToSQLType(fieldType, !!f.isArray);
205
- if (indexingTypes.includes(t))
206
- indexes[fieldName + ':' + t] = {};
207
- });
208
- }
209
- if (searchResolver.filters) {
210
- searchResolver.filters.forEach(filter => {
211
- const f = this.dataType.getField(filter.field);
212
- const fieldType = this.resource.document.getDataType(f.type || 'string');
213
- const t = dataTypeToSQLType(fieldType, !!f.isArray);
214
- if (indexingTypes.includes(t))
215
- indexes[filter.field + ':' + t] = {};
216
- });
217
- }
218
- }
219
- await (0, core_1.nSQL)().createDatabase({
220
- id: this._dbName,
221
- version: 3,
222
- tables: [table]
223
- });
224
- this._status = 'initialized';
225
- if (this._initData) {
226
- (0, core_1.nSQL)().useDatabase(this._dbName);
227
- await (0, core_1.nSQL)(this.resourceName)
228
- .query('upsert', this._initData)
229
- .exec();
230
- delete this._initData;
231
- }
232
- }
233
- catch (e) {
234
- this._initError = e;
235
- this._status = 'error';
236
- throw e;
237
- }
238
- }
239
- _prepare(query) {
240
- if (query.resource instanceof common_1.CollectionResourceInfo) {
241
- if (query.dataType !== this.dataType)
242
- throw new TypeError(`Query data type (${query.dataType.name}) ` +
243
- `differs from JsonCollectionService data type (${this.dataType.name})`);
244
- }
245
- switch (query.method) {
246
- case 'count': {
247
- const options = (0, lodash_1.omitBy)({
248
- filter: this._convertFilter(query.filter)
249
- }, lodash_1.isNil);
250
- return {
251
- method: query.method,
252
- options,
253
- args: [options]
254
- };
255
- }
256
- case 'create': {
257
- const options = (0, lodash_1.omitBy)({
258
- pick: query.pick,
259
- omit: query.omit,
260
- include: query.include
261
- }, lodash_1.isNil);
262
- const { data } = query;
263
- return {
264
- method: query.method,
265
- values: data,
266
- options,
267
- args: [data, options]
268
- };
269
- }
270
- case 'get': {
271
- if (query.kind === 'CollectionGetQuery') {
272
- const options = (0, lodash_1.omitBy)({
273
- pick: query.pick,
274
- omit: query.omit,
275
- include: query.include
276
- }, lodash_1.isNil);
277
- const keyValue = query.keyValue;
278
- return {
279
- method: query.method,
280
- keyValue,
281
- options,
282
- args: [keyValue, options]
283
- };
284
- }
285
- if (query.kind === 'FieldGetQuery') {
286
- // todo
287
- }
288
- break;
289
- }
290
- case 'search': {
291
- if (query.distinct)
292
- throw new common_1.MethodNotAllowedError({
293
- message: '$distinct parameter is not supported by JsonDataService'
294
- });
295
- const options = (0, lodash_1.omitBy)({
296
- pick: query.pick,
297
- omit: query.omit,
298
- include: query.include,
299
- filter: this._convertFilter(query.filter),
300
- sort: query.sort?.length ? query.sort : undefined,
301
- skip: query.skip,
302
- limit: query.limit,
303
- offset: query.skip,
304
- count: query.count,
305
- }, lodash_1.isNil);
306
- return {
307
- method: query.method,
308
- options,
309
- args: [options]
310
- };
311
- }
312
- case 'update': {
313
- const options = (0, lodash_1.omitBy)({
314
- pick: query.pick,
315
- omit: query.omit,
316
- include: query.include
317
- }, lodash_1.isNil);
318
- const { data } = query;
319
- const keyValue = query.keyValue;
320
- return {
321
- method: query.method,
322
- keyValue: query.keyValue,
323
- values: data,
324
- options,
325
- args: [keyValue, data, options]
326
- };
327
- }
328
- case 'updateMany': {
329
- const options = (0, lodash_1.omitBy)({
330
- filter: this._convertFilter(query.filter)
331
- }, lodash_1.isNil);
332
- const { data } = query;
333
- return {
334
- method: query.method,
335
- options,
336
- args: [data, options]
337
- };
338
- }
339
- case 'delete': {
340
- const options = {};
341
- const keyValue = query.keyValue;
342
- return {
343
- method: query.method,
344
- keyValue,
345
- options,
346
- args: [keyValue, options]
347
- };
348
- }
349
- case 'deleteMany': {
350
- const options = (0, lodash_1.omitBy)({
351
- filter: this._convertFilter(query.filter)
352
- }, lodash_1.isNil);
353
- return {
354
- method: query.method,
355
- options,
356
- args: [options]
357
- };
358
- }
359
- }
360
- throw new Error(`Unimplemented query type "${query.method}"`);
361
- }
362
- _convertSelect(args) {
363
- const result = [];
364
- const document = this.dataType.document;
365
- const processDataType = (dt, path, pick, omit, include) => {
366
- let kl;
367
- for (const [k, f] of dt.fields) {
368
- kl = k.toLowerCase();
369
- if (omit?.[kl] === true)
370
- continue;
371
- if ((((!pick && !f.exclusive) || pick?.[kl])) || include?.[kl]) {
372
- const fieldType = document.getDataType(f.type);
373
- const subPath = (path ? path + '.' : '') + f.name;
374
- if (fieldType instanceof common_1.ComplexType) {
375
- processDataType(fieldType, subPath, typeof pick?.[kl] === 'object' ? pick?.[kl] : undefined, typeof omit?.[kl] === 'object' ? omit?.[kl] : undefined, typeof include?.[kl] === 'object' ? include?.[kl] : undefined);
376
- continue;
377
- }
378
- result.push(subPath);
379
- }
380
- }
381
- };
382
- processDataType(this.dataType, '', (args.pick ? (0, common_1.pathToTree)(args.pick, true) : undefined), (args.omit ? (0, common_1.pathToTree)(args.omit, true) : undefined), (args.include ? (0, common_1.pathToTree)(args.include, true) : undefined));
383
- return result;
384
- }
385
- _convertFilter(str) {
386
- const ast = typeof str === 'string'
387
- ? (0, common_1.parseFilter)(str)
388
- : str;
389
- if (!ast || !(ast instanceof common_1.Expression))
390
- return ast;
391
- if (ast instanceof common_1.ComparisonExpression) {
392
- const left = this._convertFilter(ast.left);
393
- const right = this._convertFilter(ast.right);
394
- switch (ast.op) {
395
- case '=':
396
- return [left, '=', right];
397
- case '!=':
398
- return [left, '!=', right];
399
- case '>':
400
- return [left, '>', right];
401
- case '>=':
402
- return [left, '>=', right];
403
- case '<':
404
- return [left, '<', right];
405
- case '<=':
406
- return [left, '<=', right];
407
- case 'like':
408
- return [left, 'LIKE', right];
409
- case '!like':
410
- return [left, 'NOT LIKE', right];
411
- case 'in':
412
- return [left, 'IN', Array.isArray(right) ? right : [right]];
413
- case '!in':
414
- return [left, 'NOT IN', Array.isArray(right) ? right : [right]];
415
- default:
416
- throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
417
- }
418
- }
419
- if (ast instanceof common_1.QualifiedIdentifier) {
420
- return ast.value;
421
- }
422
- if (ast instanceof common_1.NumberLiteral ||
423
- ast instanceof common_1.StringLiteral ||
424
- ast instanceof common_1.BooleanLiteral ||
425
- ast instanceof common_1.NullLiteral ||
426
- ast instanceof common_1.DateLiteral ||
427
- ast instanceof common_1.TimeLiteral) {
428
- return ast.value;
429
- }
430
- if (ast instanceof common_1.ArrayExpression) {
431
- return ast.items.map(item => this._convertFilter(item));
432
- }
433
- if (ast instanceof common_1.LogicalExpression) {
434
- return ast.items.map(item => this._convertFilter(item))
435
- .reduce((a, v) => {
436
- if (a.length)
437
- a.push(ast.op.toUpperCase());
438
- a.push(v);
439
- return a;
440
- }, []);
441
- }
442
- if (ast instanceof common_1.ArrayExpression) {
443
- return ast.items.map(item => this._convertFilter(item));
444
- }
445
- if (ast instanceof common_1.ParenthesesExpression) {
446
- return this._convertFilter(ast.expression);
447
- }
448
- throw new Error(`${ast.kind} is not implemented yet`);
449
- }
450
- }
451
- exports.JsonCollectionService = JsonCollectionService;
452
- function unFlatten(input) {
453
- if (!input)
454
- return;
455
- const target = {};
456
- for (const k of Object.keys(input)) {
457
- if (k.includes('.')) {
458
- const keys = k.split('.');
459
- let o = target;
460
- for (let i = 0; i < keys.length - 1; i++) {
461
- o = o[keys[i]] = o[keys[i]] || {};
462
- }
463
- o[keys[keys.length - 1]] = input[k];
464
- }
465
- else
466
- target[k] = input[k];
467
- }
468
- return target;
469
- }
470
- function dataTypeToSQLType(dataType, isArray) {
471
- let out = 'any';
472
- if (dataType.kind !== 'SimpleType')
473
- out = 'object';
474
- else {
475
- switch (dataType.name) {
476
- case 'boolean':
477
- case 'number':
478
- case 'string':
479
- out = dataType.name;
480
- break;
481
- case 'integer':
482
- out = 'int';
483
- break;
484
- // case 'date': //there is bug in nano-sql.
485
- // case 'date-time':
486
- // out = 'date';
487
- // break;
488
- case 'time':
489
- out = 'string';
490
- break;
491
- case 'uuid':
492
- out = 'uuid';
493
- break;
494
- }
495
- }
496
- return out + (isArray ? '[]' : '');
497
- }