proto.io 0.0.137 → 0.0.139

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 (39) hide show
  1. package/dist/adapters/file/database.d.ts +2 -2
  2. package/dist/adapters/file/database.mjs +2 -2
  3. package/dist/adapters/file/filesystem.d.ts +2 -2
  4. package/dist/adapters/file/google-cloud-storage.d.ts +2 -2
  5. package/dist/adapters/storage/progres.d.ts +5 -2
  6. package/dist/adapters/storage/progres.js +360 -365
  7. package/dist/adapters/storage/progres.js.map +1 -1
  8. package/dist/adapters/storage/progres.mjs +363 -368
  9. package/dist/adapters/storage/progres.mjs.map +1 -1
  10. package/dist/client.d.ts +3 -3
  11. package/dist/client.js +1 -1
  12. package/dist/client.mjs +3 -3
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.js +1 -1
  15. package/dist/index.mjs +5 -5
  16. package/dist/internals/index-C6zdNpc_.js.map +1 -1
  17. package/dist/internals/{index-BPAEvTP6.mjs → index-C8hhBfuh.mjs} +2 -11
  18. package/dist/internals/index-C8hhBfuh.mjs.map +1 -0
  19. package/dist/internals/{index-CoOTI_pk.js → index-CTyYPai3.js} +1 -10
  20. package/dist/internals/index-CTyYPai3.js.map +1 -0
  21. package/dist/internals/{index-CYO8Wsos.d.ts → index-D1h-5NmA.d.ts} +2 -2
  22. package/dist/internals/index-D1h-5NmA.d.ts.map +1 -0
  23. package/dist/internals/{index-0dgO9Wxb.d.ts → index-DRgmWum4.d.ts} +2 -2
  24. package/dist/internals/index-DRgmWum4.d.ts.map +1 -0
  25. package/dist/internals/{index-DFAi7Z7H.mjs → index-DTZ5uRgF.mjs} +2 -2
  26. package/dist/internals/{index-DFAi7Z7H.mjs.map → index-DTZ5uRgF.mjs.map} +1 -1
  27. package/dist/internals/{index-DRzRIomK.mjs → index-EOtjV6U_.mjs} +2 -2
  28. package/dist/internals/index-EOtjV6U_.mjs.map +1 -0
  29. package/dist/internals/{index-DvJvDUrO.d.ts → index-rcLPSrw6.d.ts} +1 -2
  30. package/dist/internals/index-rcLPSrw6.d.ts.map +1 -0
  31. package/dist/internals/{random-D0f0zJgp.mjs → random-Dn9rhwfh.mjs} +3 -3
  32. package/dist/internals/{random-D0f0zJgp.mjs.map → random-Dn9rhwfh.mjs.map} +1 -1
  33. package/package.json +1 -1
  34. package/dist/internals/index-0dgO9Wxb.d.ts.map +0 -1
  35. package/dist/internals/index-BPAEvTP6.mjs.map +0 -1
  36. package/dist/internals/index-CYO8Wsos.d.ts.map +0 -1
  37. package/dist/internals/index-CoOTI_pk.js.map +0 -1
  38. package/dist/internals/index-DRzRIomK.mjs.map +0 -1
  39. package/dist/internals/index-DvJvDUrO.d.ts.map +0 -1
@@ -1,13 +1,13 @@
1
1
  import _ from 'lodash';
2
- import { _ as _decodeValue, f as _encodeValue, a as isPointer, b as isRelation, i as isShape, d as decodeUpdateOp, s as shapePaths, e as isPrimitive, T as TObject, g as dimensionOf, h as _typeof, j as isVector } from '../../internals/index-DRzRIomK.mjs';
2
+ import { a as isPointer, b as isRelation, i as isShape, d as decodeUpdateOp, s as shapePaths, e as isPrimitive, T as TObject, f as isVector, _ as _decodeValue, g as _encodeValue, h as dimensionOf, j as _typeof } from '../../internals/index-EOtjV6U_.mjs';
3
3
  import { Pool, types } from 'pg';
4
4
  import QueryStream from 'pg-query-stream';
5
5
  import { asyncStream } from '@o2ter/utils-js';
6
6
  import Decimal from 'decimal.js';
7
7
  import { escapeLiteral, escapeIdentifier } from 'pg/lib/utils';
8
- import { a as QueryCoditionalSelector, b as QueryFieldSelector, c as QueryExpressionSelector, d as QueryDistanceExpression, e as QueryCoditionalExpression, f as QueryComparisonExpression, g as QueryNotExpression, h as QueryArrayExpression, i as QueryValueExpression, j as QueryKeyExpression, Q as QuerySelector, F as FieldSelectorExpression } from '../../internals/index-DFAi7Z7H.mjs';
8
+ import { a as QueryCoditionalSelector, b as QueryFieldSelector, c as QueryExpressionSelector, d as QueryDistanceExpression, e as QueryCoditionalExpression, f as QueryComparisonExpression, g as QueryNotExpression, h as QueryArrayExpression, i as QueryValueExpression, j as QueryKeyExpression, Q as QuerySelector, F as FieldSelectorExpression } from '../../internals/index-DTZ5uRgF.mjs';
9
9
  import '@o2ter/crypto-js';
10
- import { _ as _resolveColumn, g as generateId, Q as QueryValidator } from '../../internals/random-D0f0zJgp.mjs';
10
+ import { _ as _resolveColumn, g as generateId, Q as QueryValidator } from '../../internals/random-Dn9rhwfh.mjs';
11
11
  import { P as PVK } from '../../internals/private-BUpLAMZi.mjs';
12
12
 
13
13
  //
@@ -136,267 +136,6 @@ class SQL {
136
136
  }
137
137
  const sql = (templates, ...values) => new SQL(templates, values);
138
138
 
139
- //
140
- // basic.ts
141
- //
142
- // The MIT License
143
- // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
144
- //
145
- // Permission is hereby granted, free of charge, to any person obtaining a copy
146
- // of this software and associated documentation files (the "Software"), to deal
147
- // in the Software without restriction, including without limitation the rights
148
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
149
- // copies of the Software, and to permit persons to whom the Software is
150
- // furnished to do so, subject to the following conditions:
151
- //
152
- // The above copyright notice and this permission notice shall be included in
153
- // all copies or substantial portions of the Software.
154
- //
155
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
156
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
157
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
158
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
159
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
160
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
161
- // THE SOFTWARE.
162
- //
163
- const stringArrayAttrs = ['_rperm', '_wperm'];
164
- const nullSafeEqual = () => sql `IS NOT DISTINCT FROM`;
165
- const nullSafeNotEqual = () => sql `IS DISTINCT FROM`;
166
- const quote = (str) => escapeLiteral(str);
167
- const identifier = (name) => escapeIdentifier(name);
168
- const placeholder = (idx) => `$${idx}`;
169
- const boolean = (value) => value ? 'true' : 'false';
170
- const encodeSortKey = (className, key) => {
171
- const [colname, ...subpath] = _.toPath(key);
172
- if (_.isEmpty(subpath))
173
- return sql `${{ identifier: className }}.${{ identifier: colname }}`;
174
- return sql `jsonb_extract_path(
175
- ${{ identifier: className }}.${{ identifier: colname }},
176
- ${_.map(subpath, x => sql `${{ quote: x }}`)}
177
- )`;
178
- };
179
- const random = (opts) => {
180
- return opts.weight ? sql `-ln(random()) / ${{ identifier: opts.weight }}` : sql `random()`;
181
- };
182
-
183
- //
184
- // index.ts
185
- //
186
- // The MIT License
187
- // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
188
- //
189
- // Permission is hereby granted, free of charge, to any person obtaining a copy
190
- // of this software and associated documentation files (the "Software"), to deal
191
- // in the Software without restriction, including without limitation the rights
192
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
193
- // copies of the Software, and to permit persons to whom the Software is
194
- // furnished to do so, subject to the following conditions:
195
- //
196
- // The above copyright notice and this permission notice shall be included in
197
- // all copies or substantial portions of the Software.
198
- //
199
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
200
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
201
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
202
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
203
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
204
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
205
- // THE SOFTWARE.
206
- //
207
- const typeParser = (oid, format) => {
208
- format = format ?? 'text';
209
- if (format === 'text') {
210
- switch (oid) {
211
- case types.builtins.MONEY:
212
- return (value) => new Decimal(value);
213
- }
214
- }
215
- return types.getTypeParser(oid, format);
216
- };
217
- class PostgresClientDriver {
218
- db;
219
- constructor(db) {
220
- this.db = db;
221
- }
222
- query(text, values = [], batchSize) {
223
- const db = this.db;
224
- return asyncStream(async function* () {
225
- const client = db instanceof Pool ? await db.connect() : db;
226
- const stream = new QueryStream(text, values, { batchSize });
227
- client.query(stream);
228
- try {
229
- yield* stream;
230
- }
231
- finally {
232
- stream.destroy();
233
- if (db instanceof Pool)
234
- client.release();
235
- }
236
- });
237
- }
238
- async version() {
239
- const [{ version }] = await this.query('SELECT version()');
240
- return version;
241
- }
242
- async databases() {
243
- return _.compact(_.map(await this.query('SELECT datname FROM pg_catalog.pg_database'), x => x.datname));
244
- }
245
- async tables() {
246
- return _.compact(_.map(await this.query(`
247
- SELECT tablename FROM pg_catalog.pg_tables
248
- WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
249
- `), x => x.tablename));
250
- }
251
- async views() {
252
- return _.compact(_.map(await this.query(`
253
- SELECT viewname FROM pg_catalog.pg_views
254
- WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
255
- `), x => x.viewname));
256
- }
257
- async materializedViews() {
258
- return _.compact(_.map(await this.query(`
259
- SELECT matviewname FROM pg_catalog.pg_matviews
260
- WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
261
- `), x => x.matviewname));
262
- }
263
- async columns(table, namespace) {
264
- const columns = await this.query(`
265
- SELECT
266
- a.attname AS column_name,
267
- format_type(a.atttypid, a.atttypmod) AS data_type,
268
- a.attnum ,
269
- a.attnotnull
270
- FROM
271
- pg_namespace n,
272
- pg_class t,
273
- pg_attribute a
274
- WHERE
275
- a.attnum > 0
276
- AND n.oid = t.relnamespace
277
- AND a.attrelid = t.oid
278
- AND NOT a.attisdropped
279
- AND t.relname = '${table}'
280
- ${namespace ? `AND n.nspname = '${namespace}'` : ''}
281
- `);
282
- return _.map(columns, ({ column_name, data_type, attnotnull }) => ({
283
- name: column_name,
284
- type: data_type,
285
- required: !!attnotnull,
286
- }));
287
- }
288
- async indices(table, namespace) {
289
- const indices = await this.query(`
290
- SELECT
291
- n.nspname AS schema_name,
292
- t.relname AS table_name,
293
- i.relname AS index_name,
294
- ix.indisprimary AS is_primary,
295
- ix.indisunique AS is_unique,
296
- a.attname AS column_name,
297
- k.indseq AS seq
298
- FROM
299
- pg_namespace n,
300
- pg_class t,
301
- pg_class i,
302
- pg_index ix,
303
- UNNEST(ix.indkey) WITH ORDINALITY k(attnum, indseq),
304
- pg_attribute a
305
- WHERE
306
- t.oid = ix.indrelid
307
- AND n.oid = t.relnamespace
308
- AND i.oid = ix.indexrelid
309
- AND a.attrelid = t.oid
310
- AND a.attnum = k.attnum
311
- AND t.relkind = 'r'
312
- AND t.relname = '${table}'
313
- ${namespace ? `AND n.nspname = '${namespace}'` : ''}
314
- `);
315
- return _.mapValues(_.groupBy(indices, 'index_name'), v => ({
316
- keys: _.map(_.sortBy(v, ({ seq }) => parseInt(seq)), 'column_name'),
317
- ..._.pick(_.first(v), [
318
- 'schema_name',
319
- 'table_name',
320
- 'index_name',
321
- 'is_primary',
322
- 'is_unique',
323
- ])
324
- }));
325
- }
326
- async withClient(callback) {
327
- const client = this.db instanceof Pool ? await this.db.connect() : this.db;
328
- try {
329
- return await callback(new PostgresClientDriver(client));
330
- }
331
- finally {
332
- if (this.db instanceof Pool)
333
- client.release();
334
- }
335
- }
336
- async publish(payload) {
337
- await this.withClient(async (db) => {
338
- await db.query(`NOTIFY ${PROTO_POSTGRES_MSG}, ${quote(JSON.stringify(_encodeValue(payload)))}`);
339
- });
340
- }
341
- }
342
- class PostgresDriver extends PostgresClientDriver {
343
- database;
344
- pubsub;
345
- subscribers = [];
346
- constructor(config) {
347
- if (_.isEmpty(config))
348
- throw Error('Invalid postgre config.');
349
- const _types = { getTypeParser: typeParser };
350
- const database = new Pool(_.isString(config) ? { connectionString: config, types: _types } : { ...config, types: _types });
351
- super(database);
352
- this.database = database;
353
- }
354
- async shutdown() {
355
- await this._release_pubsub();
356
- await this.database.end();
357
- }
358
- async _init_pubsub() {
359
- if (this.pubsub)
360
- return;
361
- try {
362
- this.pubsub = this.database.connect();
363
- const pubsub = await this.pubsub;
364
- pubsub.on('notification', ({ channel, payload }) => {
365
- if (_.toUpper(channel) !== PROTO_POSTGRES_MSG || !payload)
366
- return;
367
- try {
368
- const _payload = _decodeValue(JSON.parse(payload));
369
- for (const subscriber of this.subscribers) {
370
- subscriber(_payload);
371
- }
372
- }
373
- catch (e) {
374
- console.error(`Unknown payload: ${e}`);
375
- }
376
- });
377
- await pubsub.query(`LISTEN ${PROTO_POSTGRES_MSG}`);
378
- }
379
- catch (e) {
380
- console.error(e);
381
- }
382
- }
383
- async _release_pubsub() {
384
- const pubsub = this.pubsub;
385
- this.pubsub = undefined;
386
- await (await pubsub)?.release();
387
- }
388
- subscribe(callback) {
389
- this._init_pubsub();
390
- this.subscribers.push(callback);
391
- return () => {
392
- this.subscribers = this.subscribers.filter(x => x !== callback);
393
- if (_.isEmpty(this.subscribers)) {
394
- this._release_pubsub();
395
- }
396
- };
397
- }
398
- }
399
-
400
139
  //
401
140
  // compiler.ts
402
141
  //
@@ -692,7 +431,7 @@ class QueryCompiler {
692
431
  })}`;
693
432
  }
694
433
  return sql `${_.map(sort, (order, key) => sql `
695
- ${this.dialect.encodeSortKey(parent.name, key)} ${{ literal: order === 1 ? 'ASC' : 'DESC' }}
434
+ ${this.dialect.encodeSortKey(this, parent, key)} ${{ literal: order === 1 ? 'ASC' : 'DESC' }}
696
435
  `)}`;
697
436
  }
698
437
  _selectPopulateMap(context, className, name) {
@@ -1019,7 +758,7 @@ class SqlStorage {
1019
758
  }
1020
759
 
1021
760
  //
1022
- // encode.ts
761
+ // utils.ts
1023
762
  //
1024
763
  // The MIT License
1025
764
  // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
@@ -1042,7 +781,363 @@ class SqlStorage {
1042
781
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1043
782
  // THE SOFTWARE.
1044
783
  //
1045
- const _encodeJsonValue = (value) => {
784
+ const _fetchElement = (parent, colname, subpath, dataType) => {
785
+ const element = sql `${{ identifier: parent.name }}.${{ identifier: parent.name.startsWith('_expr_$') ? '$' : colname }}`;
786
+ if (!parent.className) {
787
+ if (colname !== '$') {
788
+ return {
789
+ element: sql `jsonb_extract_path(${element}, ${_.map([colname, ...subpath], x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)})`,
790
+ json: true,
791
+ };
792
+ }
793
+ else if (!_.isEmpty(subpath)) {
794
+ return {
795
+ element: sql `jsonb_extract_path(${element}, ${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)})`,
796
+ json: true,
797
+ };
798
+ }
799
+ }
800
+ else if (!_.isEmpty(subpath)) {
801
+ const _subpath = sql `${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)}`;
802
+ if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'relation'))) {
803
+ return {
804
+ element: sql `jsonb_extract_path(to_jsonb(${element}), ${_subpath})`,
805
+ json: true,
806
+ };
807
+ }
808
+ else {
809
+ return {
810
+ element: sql `jsonb_extract_path(${element}, ${_subpath})`,
811
+ json: true,
812
+ };
813
+ }
814
+ }
815
+ if (parent.name.startsWith('_expr_$') && colname !== '$') {
816
+ return {
817
+ element: sql `jsonb_extract_path(${element}, ${{ quote: colname.startsWith('$') ? `$${colname}` : colname }})`,
818
+ json: true,
819
+ };
820
+ }
821
+ return { element, json: false };
822
+ };
823
+ const resolvePaths = (compiler, className, paths) => {
824
+ const { paths: [colname, ...subpath], dataType } = _resolveColumn(compiler.schema, className, paths.join('.'));
825
+ if (!_.isEmpty(subpath) && isVector(dataType)) {
826
+ if (subpath.length !== 1)
827
+ throw Error(`Invalid key: ${paths.join('.')}`);
828
+ const idx = parseInt(subpath[0]);
829
+ if (_.isSafeInteger(idx) && idx >= 0 && idx < dataType.dimension) {
830
+ return { dataType: 'number', colname: `${colname}[${idx}]`, subpath: [] };
831
+ }
832
+ else {
833
+ throw Error(`Invalid key: ${paths.join('.')}`);
834
+ }
835
+ }
836
+ if (!_.isEmpty(subpath) && isPointer(dataType)) {
837
+ const resolved = resolvePaths(compiler, dataType.target, subpath);
838
+ return { ...resolved, colname: `${colname}.${resolved.colname}` };
839
+ }
840
+ const digit = _.first(subpath);
841
+ if (!_.isEmpty(subpath) && isRelation(dataType) && digit?.match(QueryValidator.patterns.digits)) {
842
+ const resolved = resolvePaths(compiler, dataType.target, _.slice(subpath, 1));
843
+ return { dataType, colname, subpath: [digit, resolved.colname, ...resolved.subpath] };
844
+ }
845
+ return { dataType, colname, subpath };
846
+ };
847
+ const fetchElement = (compiler, parent, field) => {
848
+ if (parent.className) {
849
+ const { dataType, colname, subpath } = resolvePaths(compiler, parent.className, _.toPath(field));
850
+ if (isPointer(dataType))
851
+ return { element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}._id` }}`, dataType };
852
+ const { element, json } = _fetchElement(parent, colname, subpath, dataType);
853
+ return { element, json, dataType: json ? null : dataType, relation: isRelation(dataType) ? dataType : null };
854
+ }
855
+ const [colname, ...subpath] = _.toPath(field);
856
+ const { element, json } = _fetchElement(parent, colname, subpath);
857
+ return { element, json, dataType: null, relation: null };
858
+ };
859
+
860
+ //
861
+ // basic.ts
862
+ //
863
+ // The MIT License
864
+ // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
865
+ //
866
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
867
+ // of this software and associated documentation files (the "Software"), to deal
868
+ // in the Software without restriction, including without limitation the rights
869
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
870
+ // copies of the Software, and to permit persons to whom the Software is
871
+ // furnished to do so, subject to the following conditions:
872
+ //
873
+ // The above copyright notice and this permission notice shall be included in
874
+ // all copies or substantial portions of the Software.
875
+ //
876
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
877
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
878
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
879
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
880
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
881
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
882
+ // THE SOFTWARE.
883
+ //
884
+ const stringArrayAttrs = ['_rperm', '_wperm'];
885
+ const nullSafeEqual = () => sql `IS NOT DISTINCT FROM`;
886
+ const nullSafeNotEqual = () => sql `IS DISTINCT FROM`;
887
+ const quote = (str) => escapeLiteral(str);
888
+ const identifier = (name) => escapeIdentifier(name);
889
+ const placeholder = (idx) => `$${idx}`;
890
+ const boolean = (value) => value ? 'true' : 'false';
891
+ const encodeSortKey = (compiler, parent, key) => {
892
+ const { element } = fetchElement(compiler, parent, key);
893
+ return element;
894
+ };
895
+ const random = (opts) => {
896
+ return opts.weight ? sql `-ln(random()) / ${{ identifier: opts.weight }}` : sql `random()`;
897
+ };
898
+
899
+ //
900
+ // index.ts
901
+ //
902
+ // The MIT License
903
+ // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
904
+ //
905
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
906
+ // of this software and associated documentation files (the "Software"), to deal
907
+ // in the Software without restriction, including without limitation the rights
908
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
909
+ // copies of the Software, and to permit persons to whom the Software is
910
+ // furnished to do so, subject to the following conditions:
911
+ //
912
+ // The above copyright notice and this permission notice shall be included in
913
+ // all copies or substantial portions of the Software.
914
+ //
915
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
916
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
917
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
918
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
919
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
920
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
921
+ // THE SOFTWARE.
922
+ //
923
+ const typeParser = (oid, format) => {
924
+ format = format ?? 'text';
925
+ if (format === 'text') {
926
+ switch (oid) {
927
+ case types.builtins.MONEY:
928
+ return (value) => new Decimal(value);
929
+ }
930
+ }
931
+ return types.getTypeParser(oid, format);
932
+ };
933
+ class PostgresClientDriver {
934
+ db;
935
+ constructor(db) {
936
+ this.db = db;
937
+ }
938
+ query(text, values = [], batchSize) {
939
+ const db = this.db;
940
+ return asyncStream(async function* () {
941
+ const client = db instanceof Pool ? await db.connect() : db;
942
+ const stream = new QueryStream(text, values, { batchSize });
943
+ client.query(stream);
944
+ try {
945
+ yield* stream;
946
+ }
947
+ finally {
948
+ stream.destroy();
949
+ if (db instanceof Pool)
950
+ client.release();
951
+ }
952
+ });
953
+ }
954
+ async version() {
955
+ const [{ version }] = await this.query('SELECT version()');
956
+ return version;
957
+ }
958
+ async databases() {
959
+ return _.compact(_.map(await this.query('SELECT datname FROM pg_catalog.pg_database'), x => x.datname));
960
+ }
961
+ async tables() {
962
+ return _.compact(_.map(await this.query(`
963
+ SELECT tablename FROM pg_catalog.pg_tables
964
+ WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
965
+ `), x => x.tablename));
966
+ }
967
+ async views() {
968
+ return _.compact(_.map(await this.query(`
969
+ SELECT viewname FROM pg_catalog.pg_views
970
+ WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
971
+ `), x => x.viewname));
972
+ }
973
+ async materializedViews() {
974
+ return _.compact(_.map(await this.query(`
975
+ SELECT matviewname FROM pg_catalog.pg_matviews
976
+ WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
977
+ `), x => x.matviewname));
978
+ }
979
+ async columns(table, namespace) {
980
+ const columns = await this.query(`
981
+ SELECT
982
+ a.attname AS column_name,
983
+ format_type(a.atttypid, a.atttypmod) AS data_type,
984
+ a.attnum ,
985
+ a.attnotnull
986
+ FROM
987
+ pg_namespace n,
988
+ pg_class t,
989
+ pg_attribute a
990
+ WHERE
991
+ a.attnum > 0
992
+ AND n.oid = t.relnamespace
993
+ AND a.attrelid = t.oid
994
+ AND NOT a.attisdropped
995
+ AND t.relname = '${table}'
996
+ ${namespace ? `AND n.nspname = '${namespace}'` : ''}
997
+ `);
998
+ return _.map(columns, ({ column_name, data_type, attnotnull }) => ({
999
+ name: column_name,
1000
+ type: data_type,
1001
+ required: !!attnotnull,
1002
+ }));
1003
+ }
1004
+ async indices(table, namespace) {
1005
+ const indices = await this.query(`
1006
+ SELECT
1007
+ n.nspname AS schema_name,
1008
+ t.relname AS table_name,
1009
+ i.relname AS index_name,
1010
+ ix.indisprimary AS is_primary,
1011
+ ix.indisunique AS is_unique,
1012
+ a.attname AS column_name,
1013
+ k.indseq AS seq
1014
+ FROM
1015
+ pg_namespace n,
1016
+ pg_class t,
1017
+ pg_class i,
1018
+ pg_index ix,
1019
+ UNNEST(ix.indkey) WITH ORDINALITY k(attnum, indseq),
1020
+ pg_attribute a
1021
+ WHERE
1022
+ t.oid = ix.indrelid
1023
+ AND n.oid = t.relnamespace
1024
+ AND i.oid = ix.indexrelid
1025
+ AND a.attrelid = t.oid
1026
+ AND a.attnum = k.attnum
1027
+ AND t.relkind = 'r'
1028
+ AND t.relname = '${table}'
1029
+ ${namespace ? `AND n.nspname = '${namespace}'` : ''}
1030
+ `);
1031
+ return _.mapValues(_.groupBy(indices, 'index_name'), v => ({
1032
+ keys: _.map(_.sortBy(v, ({ seq }) => parseInt(seq)), 'column_name'),
1033
+ ..._.pick(_.first(v), [
1034
+ 'schema_name',
1035
+ 'table_name',
1036
+ 'index_name',
1037
+ 'is_primary',
1038
+ 'is_unique',
1039
+ ])
1040
+ }));
1041
+ }
1042
+ async withClient(callback) {
1043
+ const client = this.db instanceof Pool ? await this.db.connect() : this.db;
1044
+ try {
1045
+ return await callback(new PostgresClientDriver(client));
1046
+ }
1047
+ finally {
1048
+ if (this.db instanceof Pool)
1049
+ client.release();
1050
+ }
1051
+ }
1052
+ async publish(payload) {
1053
+ await this.withClient(async (db) => {
1054
+ await db.query(`NOTIFY ${PROTO_POSTGRES_MSG}, ${quote(JSON.stringify(_encodeValue(payload)))}`);
1055
+ });
1056
+ }
1057
+ }
1058
+ class PostgresDriver extends PostgresClientDriver {
1059
+ database;
1060
+ pubsub;
1061
+ subscribers = [];
1062
+ constructor(config) {
1063
+ if (_.isEmpty(config))
1064
+ throw Error('Invalid postgre config.');
1065
+ const _types = { getTypeParser: typeParser };
1066
+ const database = new Pool(_.isString(config) ? { connectionString: config, types: _types } : { ...config, types: _types });
1067
+ super(database);
1068
+ this.database = database;
1069
+ }
1070
+ async shutdown() {
1071
+ await this._release_pubsub();
1072
+ await this.database.end();
1073
+ }
1074
+ async _init_pubsub() {
1075
+ if (this.pubsub)
1076
+ return;
1077
+ try {
1078
+ this.pubsub = this.database.connect();
1079
+ const pubsub = await this.pubsub;
1080
+ pubsub.on('notification', ({ channel, payload }) => {
1081
+ if (_.toUpper(channel) !== PROTO_POSTGRES_MSG || !payload)
1082
+ return;
1083
+ try {
1084
+ const _payload = _decodeValue(JSON.parse(payload));
1085
+ for (const subscriber of this.subscribers) {
1086
+ subscriber(_payload);
1087
+ }
1088
+ }
1089
+ catch (e) {
1090
+ console.error(`Unknown payload: ${e}`);
1091
+ }
1092
+ });
1093
+ await pubsub.query(`LISTEN ${PROTO_POSTGRES_MSG}`);
1094
+ }
1095
+ catch (e) {
1096
+ console.error(e);
1097
+ }
1098
+ }
1099
+ async _release_pubsub() {
1100
+ const pubsub = this.pubsub;
1101
+ this.pubsub = undefined;
1102
+ await (await pubsub)?.release();
1103
+ }
1104
+ subscribe(callback) {
1105
+ this._init_pubsub();
1106
+ this.subscribers.push(callback);
1107
+ return () => {
1108
+ this.subscribers = this.subscribers.filter(x => x !== callback);
1109
+ if (_.isEmpty(this.subscribers)) {
1110
+ this._release_pubsub();
1111
+ }
1112
+ };
1113
+ }
1114
+ }
1115
+
1116
+ //
1117
+ // encode.ts
1118
+ //
1119
+ // The MIT License
1120
+ // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
1121
+ //
1122
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
1123
+ // of this software and associated documentation files (the "Software"), to deal
1124
+ // in the Software without restriction, including without limitation the rights
1125
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1126
+ // copies of the Software, and to permit persons to whom the Software is
1127
+ // furnished to do so, subject to the following conditions:
1128
+ //
1129
+ // The above copyright notice and this permission notice shall be included in
1130
+ // all copies or substantial portions of the Software.
1131
+ //
1132
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1133
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1134
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1135
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1136
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1137
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1138
+ // THE SOFTWARE.
1139
+ //
1140
+ const _encodeJsonValue = (value) => {
1046
1141
  if (_.isArray(value))
1047
1142
  return sql `jsonb_build_array(${_.map(value, x => _encodeJsonValue(x))})`;
1048
1143
  if (_.isPlainObject(value))
@@ -1466,106 +1561,6 @@ const updateOperation = (paths, dataType, operation) => {
1466
1561
  throw Error('Invalid update operation');
1467
1562
  };
1468
1563
 
1469
- //
1470
- // utils.ts
1471
- //
1472
- // The MIT License
1473
- // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
1474
- //
1475
- // Permission is hereby granted, free of charge, to any person obtaining a copy
1476
- // of this software and associated documentation files (the "Software"), to deal
1477
- // in the Software without restriction, including without limitation the rights
1478
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1479
- // copies of the Software, and to permit persons to whom the Software is
1480
- // furnished to do so, subject to the following conditions:
1481
- //
1482
- // The above copyright notice and this permission notice shall be included in
1483
- // all copies or substantial portions of the Software.
1484
- //
1485
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1486
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1487
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1488
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1489
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1490
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1491
- // THE SOFTWARE.
1492
- //
1493
- const _fetchElement = (parent, colname, subpath, dataType) => {
1494
- const element = sql `${{ identifier: parent.name }}.${{ identifier: parent.name.startsWith('_expr_$') ? '$' : colname }}`;
1495
- if (!parent.className) {
1496
- if (colname !== '$') {
1497
- return {
1498
- element: sql `jsonb_extract_path(${element}, ${_.map([colname, ...subpath], x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)})`,
1499
- json: true,
1500
- };
1501
- }
1502
- else if (!_.isEmpty(subpath)) {
1503
- return {
1504
- element: sql `jsonb_extract_path(${element}, ${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)})`,
1505
- json: true,
1506
- };
1507
- }
1508
- }
1509
- else if (!_.isEmpty(subpath)) {
1510
- const _subpath = sql `${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)}`;
1511
- if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'relation'))) {
1512
- return {
1513
- element: sql `jsonb_extract_path(to_jsonb(${element}), ${_subpath})`,
1514
- json: true,
1515
- };
1516
- }
1517
- else {
1518
- return {
1519
- element: sql `jsonb_extract_path(${element}, ${_subpath})`,
1520
- json: true,
1521
- };
1522
- }
1523
- }
1524
- if (parent.name.startsWith('_expr_$') && colname !== '$') {
1525
- return {
1526
- element: sql `jsonb_extract_path(${element}, ${{ quote: colname.startsWith('$') ? `$${colname}` : colname }})`,
1527
- json: true,
1528
- };
1529
- }
1530
- return { element, json: false };
1531
- };
1532
- const resolvePaths = (compiler, className, paths) => {
1533
- const { paths: [colname, ...subpath], dataType } = _resolveColumn(compiler.schema, className, paths.join('.'));
1534
- if (!_.isEmpty(subpath) && isVector(dataType)) {
1535
- if (subpath.length !== 1)
1536
- throw Error(`Invalid key: ${paths.join('.')}`);
1537
- const idx = parseInt(subpath[0]);
1538
- if (_.isSafeInteger(idx) && idx >= 0 && idx < dataType.dimension) {
1539
- return { dataType: 'number', colname: `${colname}[${idx}]`, subpath: [] };
1540
- }
1541
- else {
1542
- throw Error(`Invalid key: ${paths.join('.')}`);
1543
- }
1544
- }
1545
- if (!_.isEmpty(subpath) && isPointer(dataType)) {
1546
- const resolved = resolvePaths(compiler, dataType.target, subpath);
1547
- return { ...resolved, colname: `${colname}.${resolved.colname}` };
1548
- }
1549
- const digit = _.first(subpath);
1550
- if (!_.isEmpty(subpath) && isRelation(dataType) && digit?.match(QueryValidator.patterns.digits)) {
1551
- const resolved = resolvePaths(compiler, dataType.target, _.slice(subpath, 1));
1552
- return { dataType, colname, subpath: [digit, resolved.colname, ...resolved.subpath] };
1553
- }
1554
- return { dataType, colname, subpath };
1555
- };
1556
- const fetchElement = (compiler, parent, field) => {
1557
- if (parent.className) {
1558
- const { dataType, colname, subpath } = resolvePaths(compiler, parent.className, _.toPath(field));
1559
- if (isPointer(dataType))
1560
- return { element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}._id` }}`, dataType };
1561
- const { element, json } = _fetchElement(parent, colname, subpath, dataType);
1562
- return { element, json, dataType: json ? null : dataType, relation: isRelation(dataType) ? dataType : null };
1563
- }
1564
- const [colname, ...subpath] = _.toPath(field);
1565
- const { element, json } = _fetchElement(parent, colname, subpath);
1566
- return { element, json, dataType: null, relation: null };
1567
- };
1568
-
1569
1564
  //
1570
1565
  // expressions.ts
1571
1566
  //
@@ -2256,7 +2251,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
2256
2251
  SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
2257
2252
  SELECT ${_.map(subpaths, ({ path, type }) => _encodePopulateInclude(populate.name, path, type))}
2258
2253
  FROM ${{ identifier: populate.name }} WHERE ${cond}
2259
- ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.name, name: populate.name })}` : sql ``}
2254
+ ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
2260
2255
  ${populate.limit ? sql `LIMIT ${{ literal: `${populate.limit}` }}` : sql ``}
2261
2256
  ${populate.skip ? sql `OFFSET ${{ literal: `${populate.skip}` }}` : sql ``}
2262
2257
  ${compiler.selectLock ? compiler.isUpdate ? sql `FOR UPDATE NOWAIT` : sql `FOR SHARE NOWAIT` : sql ``}