@vsaas/loopback-datasource-juggler 10.0.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.
Files changed (63) hide show
  1. package/LICENSE +25 -0
  2. package/NOTICE +23 -0
  3. package/README.md +74 -0
  4. package/dist/_virtual/_rolldown/runtime.js +4 -0
  5. package/dist/index.js +26 -0
  6. package/dist/lib/browser.depd.js +13 -0
  7. package/dist/lib/case-utils.js +21 -0
  8. package/dist/lib/connectors/kv-memory.js +158 -0
  9. package/dist/lib/connectors/memory.js +810 -0
  10. package/dist/lib/connectors/transient.js +126 -0
  11. package/dist/lib/dao.js +2445 -0
  12. package/dist/lib/datasource.js +2215 -0
  13. package/dist/lib/date-string.js +87 -0
  14. package/dist/lib/geo.js +244 -0
  15. package/dist/lib/globalize.js +33 -0
  16. package/dist/lib/hooks.js +79 -0
  17. package/dist/lib/id-utils.js +66 -0
  18. package/dist/lib/include.js +795 -0
  19. package/dist/lib/include_utils.js +104 -0
  20. package/dist/lib/introspection.js +37 -0
  21. package/dist/lib/jutil.js +65 -0
  22. package/dist/lib/kvao/delete-all.js +57 -0
  23. package/dist/lib/kvao/delete.js +43 -0
  24. package/dist/lib/kvao/expire.js +35 -0
  25. package/dist/lib/kvao/get.js +34 -0
  26. package/dist/lib/kvao/index.js +28 -0
  27. package/dist/lib/kvao/iterate-keys.js +38 -0
  28. package/dist/lib/kvao/keys.js +55 -0
  29. package/dist/lib/kvao/set.js +39 -0
  30. package/dist/lib/kvao/ttl.js +35 -0
  31. package/dist/lib/list.js +101 -0
  32. package/dist/lib/mixins.js +58 -0
  33. package/dist/lib/model-builder.js +608 -0
  34. package/dist/lib/model-definition.js +231 -0
  35. package/dist/lib/model-utils.js +368 -0
  36. package/dist/lib/model.js +586 -0
  37. package/dist/lib/observer.js +235 -0
  38. package/dist/lib/relation-definition.js +2604 -0
  39. package/dist/lib/relations.js +587 -0
  40. package/dist/lib/scope.js +392 -0
  41. package/dist/lib/transaction.js +183 -0
  42. package/dist/lib/types.js +58 -0
  43. package/dist/lib/utils.js +625 -0
  44. package/dist/lib/validations.js +742 -0
  45. package/dist/package.js +93 -0
  46. package/package.json +85 -0
  47. package/types/common.d.ts +28 -0
  48. package/types/connector.d.ts +52 -0
  49. package/types/datasource.d.ts +324 -0
  50. package/types/date-string.d.ts +21 -0
  51. package/types/inclusion-mixin.d.ts +44 -0
  52. package/types/index.d.ts +36 -0
  53. package/types/kv-model.d.ts +201 -0
  54. package/types/model.d.ts +368 -0
  55. package/types/observer-mixin.d.ts +174 -0
  56. package/types/persisted-model.d.ts +505 -0
  57. package/types/query.d.ts +108 -0
  58. package/types/relation-mixin.d.ts +577 -0
  59. package/types/relation.d.ts +301 -0
  60. package/types/scope.d.ts +92 -0
  61. package/types/transaction-mixin.d.ts +47 -0
  62. package/types/types.d.ts +65 -0
  63. package/types/validation-mixin.d.ts +287 -0
@@ -0,0 +1,625 @@
1
+ "use strict";
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.js");
3
+ const require_lib_globalize = require("./globalize.js");
4
+ //#region src/lib/utils.ts
5
+ var require_utils = /* @__PURE__ */ require_runtime.__commonJSMin(((exports) => {
6
+ exports.safeRequire = safeRequire;
7
+ exports.fieldsToArray = fieldsToArray;
8
+ exports.selectFields = selectFields;
9
+ exports.sanitizeQuery = sanitizeQuery;
10
+ exports.parseSettings = parseSettings;
11
+ exports.mergeSettings = exports.deepMerge = deepMerge;
12
+ exports.deepMergeProperty = deepMergeProperty;
13
+ exports.isPlainObject = isPlainObject;
14
+ exports.defineCachedRelations = defineCachedRelations;
15
+ exports.sortObjectsByIds = sortObjectsByIds;
16
+ exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
17
+ exports.mergeQuery = mergeQuery;
18
+ exports.mergeIncludes = mergeIncludes;
19
+ exports.createPromiseCallback = createPromiseCallback;
20
+ exports.uniq = uniq;
21
+ exports.toRegExp = toRegExp;
22
+ exports.hasRegExpFlags = hasRegExpFlags;
23
+ exports.idEquals = idEquals;
24
+ exports.findIndexOf = findIndexOf;
25
+ exports.collectTargetIds = collectTargetIds;
26
+ exports.idName = idName;
27
+ exports.rankArrayElements = rankArrayElements;
28
+ exports.idsHaveDuplicates = idsHaveDuplicates;
29
+ exports.isClass = isClass;
30
+ exports.escapeRegExp = escapeRegExp;
31
+ exports.applyParentProperty = applyParentProperty;
32
+ const g = require_lib_globalize();
33
+ const traverse = require("neotraverse/legacy");
34
+ const assert = require("assert");
35
+ const debug = require("debug")("loopback:juggler:utils");
36
+ /**
37
+ * The name of the property in modelBuilder settings that will enable the child parent reference functionality
38
+ * @type {string}
39
+ */
40
+ const BUILDER_PARENT_SETTING = "parentRef";
41
+ /**
42
+ * The property name that should be defined on each child instance if parent feature flag enabled
43
+ * @type {string}
44
+ */
45
+ const PARENT_PROPERTY_NAME = "__parent";
46
+ const REGEXP_OPERATORS = new Set([
47
+ "like",
48
+ "nlike",
49
+ "ilike",
50
+ "nilike",
51
+ "regexp"
52
+ ]);
53
+ function safeRequire(module$1) {
54
+ try {
55
+ return require(module$1);
56
+ } catch {
57
+ g.log("Run \"{{npm install loopback-datasource-juggler}} %s\" command ", "to use {{loopback-datasource-juggler}} using %s database engine", module$1, module$1);
58
+ process.exit(1);
59
+ }
60
+ }
61
+ function setScopeValuesFromWhere(data, where, targetModel) {
62
+ for (const i in where) {
63
+ if (i === "and") {
64
+ for (let w = 0, n = where[i].length; w < n; w++) setScopeValuesFromWhere(data, where[i][w], targetModel);
65
+ continue;
66
+ }
67
+ const prop = targetModel.definition.properties[i];
68
+ if (prop) {
69
+ const val = where[i];
70
+ if (typeof val !== "object" || val instanceof prop.type || prop.type.name === "ObjectID" || prop.type.name === "uuidFromString") data[i] = where[i];
71
+ }
72
+ }
73
+ }
74
+ /**
75
+ * Merge include options of default scope with runtime include option.
76
+ * exhibits the _.extend behaviour. Property value of source overrides
77
+ * property value of destination if property name collision occurs
78
+ * @param {String|Array|Object} destination The default value of `include` option
79
+ * @param {String|Array|Object} source The runtime value of `include` option
80
+ * @returns {Object}
81
+ */
82
+ function mergeIncludes(destination, source) {
83
+ const destArray = convertToArray(destination);
84
+ const sourceArray = convertToArray(source);
85
+ if (destArray.length === 0) return sourceArray;
86
+ if (sourceArray.length === 0) return destArray;
87
+ const relationNames = [];
88
+ const resultArray = [];
89
+ for (const j in sourceArray) {
90
+ const sourceEntry = sourceArray[j];
91
+ const sourceEntryRelationName = typeof (sourceEntry.rel || sourceEntry.relation) === "string" ? sourceEntry.relation : Object.keys(sourceEntry)[0];
92
+ relationNames.push(sourceEntryRelationName);
93
+ resultArray.push(sourceEntry);
94
+ }
95
+ for (const i in destArray) {
96
+ const destEntry = destArray[i];
97
+ const destEntryRelationName = typeof (destEntry.rel || destEntry.relation) === "string" ? destEntry.relation : Object.keys(destEntry)[0];
98
+ if (relationNames.indexOf(destEntryRelationName) === -1) resultArray.push(destEntry);
99
+ }
100
+ return resultArray;
101
+ }
102
+ /**
103
+ * Converts input parameter into array of objects which wraps the value.
104
+ * "someValue" is converted to [{"someValue":true}]
105
+ * ["someValue"] is converted to [{"someValue":true}]
106
+ * {"someValue":true} is converted to [{"someValue":true}]
107
+ * @param {String|Array|Object} param - Input parameter to be converted
108
+ * @returns {Array}
109
+ */
110
+ function convertToArray(include) {
111
+ if (typeof include === "string") {
112
+ const obj = {};
113
+ obj[include] = true;
114
+ return [obj];
115
+ } else if (isPlainObject(include)) {
116
+ if (include.rel || include.relation) return [include];
117
+ const newInclude = [];
118
+ for (const key in include) {
119
+ const obj = {};
120
+ obj[key] = include[key];
121
+ newInclude.push(obj);
122
+ }
123
+ return newInclude;
124
+ } else if (Array.isArray(include)) {
125
+ const normalized = [];
126
+ for (const i in include) {
127
+ const includeEntry = include[i];
128
+ if (typeof includeEntry === "string") {
129
+ const obj = {};
130
+ obj[includeEntry] = true;
131
+ normalized.push(obj);
132
+ } else normalized.push(includeEntry);
133
+ }
134
+ return normalized;
135
+ }
136
+ return [];
137
+ }
138
+ /*!
139
+ * Merge query parameters
140
+ * @param {Object} base The base object to contain the merged results
141
+ * @param {Object} update The object containing updates to be merged
142
+ * @param {Object} spec Optionally specifies parameters to exclude (set to false)
143
+ * @returns {*|Object} The base object
144
+ * @private
145
+ */
146
+ function mergeQuery(base, update, spec) {
147
+ if (!update) return;
148
+ spec = spec || {};
149
+ base = base || {};
150
+ if (update.where && Object.keys(update.where).length > 0) if (base.where && Object.keys(base.where).length > 0) base.where = { and: [base.where, update.where] };
151
+ else base.where = update.where;
152
+ if (spec.include !== false && update.include) if (!base.include) base.include = update.include;
153
+ else if (spec.nestedInclude === true) {
154
+ const saved = base.include;
155
+ base.include = {};
156
+ base.include[update.include] = saved;
157
+ } else base.include = mergeIncludes(base.include, update.include);
158
+ if (spec.collect !== false && update.collect) base.collect = update.collect;
159
+ if (spec.fields !== false && update.fields !== void 0) base.fields = update.fields;
160
+ else if (update.fields !== void 0) base.fields = [].concat(base.fields).concat(update.fields);
161
+ if ((!base.order || spec.order === false) && update.order) base.order = update.order;
162
+ if (spec.limit !== false && update.limit !== void 0) base.limit = update.limit;
163
+ const skip = spec.skip !== false && spec.offset !== false;
164
+ if (skip && update.skip !== void 0) base.skip = update.skip;
165
+ if (skip && update.offset !== void 0) base.offset = update.offset;
166
+ return base;
167
+ }
168
+ /**
169
+ * Normalize fields to an array of included properties
170
+ * @param {String|String[]|Object} fields Fields filter
171
+ * @param {String[]} properties Property names
172
+ * @param {Boolean} excludeUnknown To exclude fields that are unknown properties
173
+ * @returns {String[]} An array of included property names
174
+ */
175
+ function fieldsToArray(fields, properties, excludeUnknown) {
176
+ if (!fields) return;
177
+ let result = properties;
178
+ let i, n;
179
+ if (typeof fields === "string") result = [fields];
180
+ else if (Array.isArray(fields) && fields.length > 0) result = fields;
181
+ else if ("object" === typeof fields) {
182
+ const included = [];
183
+ const excluded = [];
184
+ const keys = Object.keys(fields);
185
+ if (!keys.length) return;
186
+ for (i = 0, n = keys.length; i < n; i++) {
187
+ const k = keys[i];
188
+ if (fields[k]) included.push(k);
189
+ else if (k in fields && !fields[k]) excluded.push(k);
190
+ }
191
+ if (included.length > 0) result = included;
192
+ else if (excluded.length > 0) for (i = 0, n = excluded.length; i < n; i++) {
193
+ const index = result.indexOf(excluded[i]);
194
+ if (index !== -1) result.splice(index, 1);
195
+ }
196
+ }
197
+ let fieldArray = [];
198
+ if (excludeUnknown) {
199
+ for (i = 0, n = result.length; i < n; i++) if (properties.indexOf(result[i]) !== -1) fieldArray.push(result[i]);
200
+ } else fieldArray = result;
201
+ return fieldArray;
202
+ }
203
+ function selectFields(fields) {
204
+ return function(obj) {
205
+ const result = {};
206
+ let key;
207
+ for (let i = 0; i < fields.length; i++) {
208
+ key = fields[i];
209
+ result[key] = obj[key];
210
+ }
211
+ return result;
212
+ };
213
+ }
214
+ function isProhibited(key, prohibitedKeys) {
215
+ if (!prohibitedKeys || !prohibitedKeys.length) return false;
216
+ if (typeof key !== "string") return false;
217
+ const keySegments = key.indexOf(".") === -1 ? null : key.split(".");
218
+ for (const k of prohibitedKeys) {
219
+ if (k === key) return true;
220
+ if (keySegments && keySegments.indexOf(k) !== -1) return true;
221
+ }
222
+ return false;
223
+ }
224
+ /**
225
+ * Accept an operator key and return whether it is used for a regular expression query or not
226
+ * @param {string} operator
227
+ * @returns {boolean}
228
+ */
229
+ function isRegExpOperator(operator) {
230
+ return REGEXP_OPERATORS.has(operator);
231
+ }
232
+ /**
233
+ * Accept a RegExp string and make sure that any special characters for RegExp are escaped in case they
234
+ * create an invalid Regexp
235
+ * @param {string} str
236
+ * @returns {string}
237
+ */
238
+ function escapeRegExp(str) {
239
+ assert.strictEqual(typeof str, "string", "String required for regexp escaping");
240
+ try {
241
+ new RegExp(str);
242
+ return str;
243
+ } catch {
244
+ console.warn("Auto-escaping invalid RegExp value %j supplied by the caller. Please note this behavior may change in the future.", str);
245
+ return str.replace(/[-[\]/{}()+?.\\^$|]/g, "\\$&");
246
+ }
247
+ }
248
+ /**
249
+ * Sanitize the query object
250
+ * @param query {object} The query object
251
+ * @param options
252
+ * @property normalizeUndefinedInQuery {String} either "nullify", "throw" or "ignore" (default: "ignore")
253
+ * @property prohibitedKeys {String[]} An array of prohibited keys to be removed
254
+ * @returns {*}
255
+ */
256
+ function sanitizeQuery(query, options) {
257
+ debug("Sanitizing query object: %j", query);
258
+ if (typeof query !== "object" || query === null) return query;
259
+ options = options || {};
260
+ if (typeof options === "string") options = { normalizeUndefinedInQuery: options };
261
+ const prohibitedKeys = options.prohibitedKeys;
262
+ const offendingKeys = [];
263
+ const normalizeUndefinedInQuery = options.normalizeUndefinedInQuery;
264
+ const maxDepth = options.maxDepth || Number.MAX_SAFE_INTEGER;
265
+ const result = traverse(query).forEach(function(x) {
266
+ /**
267
+ * Security risk if the client passes in a very deep where object
268
+ */
269
+ if (this.circular) {
270
+ const msg = g.f("The query object is circular");
271
+ const err = new Error(msg);
272
+ err.statusCode = 400;
273
+ err.code = "QUERY_OBJECT_IS_CIRCULAR";
274
+ throw err;
275
+ }
276
+ if (this.level > maxDepth) {
277
+ const msg = g.f("The query object exceeds maximum depth %d", maxDepth);
278
+ const err = new Error(msg);
279
+ err.statusCode = 400;
280
+ err.code = "QUERY_OBJECT_TOO_DEEP";
281
+ throw err;
282
+ }
283
+ /**
284
+ * Make sure prohibited keys are removed from the query to prevent
285
+ * sensitive values from being guessed
286
+ */
287
+ if (isProhibited(this.key, prohibitedKeys)) {
288
+ offendingKeys.push(this.key);
289
+ this.remove();
290
+ return;
291
+ }
292
+ /**
293
+ * Handle undefined values
294
+ */
295
+ if (x === void 0) switch (normalizeUndefinedInQuery) {
296
+ case "nullify":
297
+ this.update(null);
298
+ break;
299
+ case "throw": throw new Error(g.f("Unexpected `undefined` in query"));
300
+ default: this.remove();
301
+ }
302
+ if (!Array.isArray(x) && typeof x === "object" && x !== null && x.constructor !== Object) {
303
+ this.update(x, true);
304
+ return x;
305
+ }
306
+ if (isRegExpOperator(this.key) && typeof x === "string") return escapeRegExp(x);
307
+ return x;
308
+ });
309
+ if (offendingKeys.length) console.error(g.f("Potential security alert: hidden/protected properties %j are used in query.", offendingKeys));
310
+ return result;
311
+ }
312
+ const url = require("url");
313
+ const qs = require("qs");
314
+ /**
315
+ * Parse a URL into a settings object
316
+ * @param {String} urlStr The URL for connector settings
317
+ * @returns {Object} The settings object
318
+ */
319
+ function parseSettings(urlStr) {
320
+ if (!urlStr) return {};
321
+ const uri = url.parse(urlStr, false);
322
+ const settings = {};
323
+ settings.connector = uri.protocol && uri.protocol.split(":")[0];
324
+ settings.host = settings.hostname = uri.hostname;
325
+ settings.port = uri.port && Number(uri.port);
326
+ settings.user = settings.username = uri.auth && uri.auth.split(":")[0];
327
+ settings.password = uri.auth && uri.auth.split(":")[1];
328
+ settings.database = uri.pathname && uri.pathname.split("/")[1];
329
+ settings.url = urlStr;
330
+ if (uri.query) {
331
+ const params = qs.parse(uri.query);
332
+ for (const p in params) settings[p] = params[p];
333
+ }
334
+ return settings;
335
+ }
336
+ /**
337
+ * Objects deep merge
338
+ *
339
+ * Forked from https://github.com/nrf110/deepmerge/blob/master/index.js
340
+ *
341
+ * The original function tries to merge array items if they are objects, this
342
+ * was changed to always push new items in arrays, independently of their type.
343
+ *
344
+ * NOTE: The function operates as a deep clone when called with a single object
345
+ * argument.
346
+ *
347
+ * @param {Object} base The base object
348
+ * @param {Object} extras The object to merge with base
349
+ * @returns {Object} The merged object
350
+ */
351
+ function deepMerge(base, extras) {
352
+ const array = Array.isArray(base) && (Array.isArray(extras) || !extras);
353
+ let dst = array && [] || {};
354
+ if (array) {
355
+ extras = extras || [];
356
+ dst = base.slice();
357
+ const seen = new Set(dst);
358
+ for (let i = 0; i < extras.length; i++) {
359
+ const extraItem = extras[i];
360
+ if (seen.has(extraItem)) continue;
361
+ seen.add(extraItem);
362
+ dst.push(extraItem);
363
+ }
364
+ } else {
365
+ if (base != null && typeof base === "object") {
366
+ const baseKeys = Object.keys(base);
367
+ for (let i = 0; i < baseKeys.length; i++) {
368
+ const key = baseKeys[i];
369
+ if (base[key] && typeof base[key] === "object") dst[key] = deepMerge(base[key]);
370
+ else dst[key] = base[key];
371
+ }
372
+ }
373
+ if (extras != null && typeof extras === "object") {
374
+ const extraKeys = Object.keys(extras);
375
+ for (let i = 0; i < extraKeys.length; i++) {
376
+ const key = extraKeys[i];
377
+ const extra = extras[key];
378
+ if (extra == null || typeof extra !== "object") dst[key] = extra;
379
+ else if (base == null || typeof base !== "object" || base[key] == null) dst[key] = extra;
380
+ else dst[key] = deepMerge(base[key], extra);
381
+ }
382
+ }
383
+ }
384
+ return dst;
385
+ }
386
+ /**
387
+ * Properties deep merge
388
+ * Similar as deepMerge but also works on single properties of any type
389
+ *
390
+ * @param {Object} base The base property
391
+ * @param {Object} extras The property to merge with base
392
+ * @returns {Object} The merged property
393
+ */
394
+ function deepMergeProperty(base, extras) {
395
+ return deepMerge({ key: base }, { key: extras }).key;
396
+ }
397
+ const numberIsFinite = Number.isFinite || function(value) {
398
+ return typeof value === "number" && isFinite(value);
399
+ };
400
+ /**
401
+ * Adds a property __rank to array elements of type object {}
402
+ * If an inner element already has the __rank property it is not altered
403
+ * NOTE: the function mutates the provided array
404
+ *
405
+ * @param array The original array
406
+ * @param rank The rank to apply to array elements
407
+ * @return rankedArray The original array with newly ranked elements
408
+ */
409
+ function rankArrayElements(array, rank) {
410
+ if (!Array.isArray(array) || !numberIsFinite(rank)) return array;
411
+ for (let i = 0, n = array.length; i < n; i++) {
412
+ const el = array[i];
413
+ if (!el || typeof el != "object" || Array.isArray(el)) continue;
414
+ if (el.__rank) continue;
415
+ Object.defineProperty(el, "__rank", {
416
+ writable: false,
417
+ enumerable: false,
418
+ configurable: false,
419
+ value: rank
420
+ });
421
+ }
422
+ return array;
423
+ }
424
+ /**
425
+ * Define an non-enumerable __cachedRelations property
426
+ * @param {Object} obj The obj to receive the __cachedRelations
427
+ */
428
+ function defineCachedRelations(obj) {
429
+ if (!obj.__cachedRelations) Object.defineProperty(obj, "__cachedRelations", {
430
+ writable: true,
431
+ enumerable: false,
432
+ configurable: true,
433
+ value: {}
434
+ });
435
+ }
436
+ /**
437
+ * Check if the argument is plain object
438
+ * @param {*} obj The obj value
439
+ * @returns {boolean}
440
+ */
441
+ function isPlainObject(obj) {
442
+ return typeof obj === "object" && obj !== null && obj.constructor === Object;
443
+ }
444
+ function sortObjectsByIds(idName, ids, objects, strict) {
445
+ ids = ids.map(function(id) {
446
+ return typeof id === "object" ? String(id) : id;
447
+ });
448
+ const ranks = /* @__PURE__ */ new Map();
449
+ for (let i = 0; i < ids.length; i++) if (!ranks.has(ids[i])) ranks.set(ids[i], i);
450
+ const heading = [];
451
+ const tailing = [];
452
+ objects.forEach(function(x) {
453
+ if (typeof x === "object") {
454
+ const id = typeof x[idName] === "object" ? String(x[idName]) : x[idName];
455
+ const idx = ranks.has(id) ? ranks.get(id) : -1;
456
+ if (strict && idx === -1) return;
457
+ if (idx === -1) tailing.push(x);
458
+ else heading.push({
459
+ idx,
460
+ value: x
461
+ });
462
+ }
463
+ });
464
+ heading.sort(function(x, y) {
465
+ return x.idx - y.idx;
466
+ });
467
+ const sorted = Array.from({ length: heading.length + tailing.length });
468
+ for (let i = 0; i < heading.length; i++) sorted[i] = heading[i].value;
469
+ for (let i = 0; i < tailing.length; i++) sorted[heading.length + i] = tailing[i];
470
+ return sorted;
471
+ }
472
+ function createPromiseCallback() {
473
+ let cb;
474
+ const promise = new Promise(function(resolve, reject) {
475
+ cb = function(err, data) {
476
+ if (err) return reject(err);
477
+ return resolve(data);
478
+ };
479
+ });
480
+ cb.promise = promise;
481
+ return cb;
482
+ }
483
+ function isBsonType(value) {
484
+ return value.hasOwnProperty("_bsontype") || value.constructor.prototype.hasOwnProperty("_bsontype");
485
+ }
486
+ /**
487
+ * Dedupe an array
488
+ * @param {Array} an array
489
+ * @returns {Array} an array with unique items
490
+ */
491
+ function uniq(a) {
492
+ const uniqArray = [];
493
+ if (!a) return uniqArray;
494
+ assert(Array.isArray(a), "array argument is required");
495
+ const seen = /* @__PURE__ */ new Set();
496
+ for (let i = 0, n = a.length; i < n; i++) {
497
+ const item = a[i];
498
+ const comparable = isBsonType(item) ? item.toString() : item;
499
+ if (seen.has(comparable)) continue;
500
+ seen.add(comparable);
501
+ uniqArray.push(item);
502
+ }
503
+ return uniqArray;
504
+ }
505
+ /**
506
+ * Converts a string, regex literal, or a RegExp object to a RegExp object.
507
+ * @param {String|Object} The string, regex literal, or RegExp object to convert
508
+ * @returns {Object} A RegExp object
509
+ */
510
+ function toRegExp(regex) {
511
+ const isString = typeof regex === "string";
512
+ const isRegExp = regex instanceof RegExp;
513
+ if (!(isString || isRegExp)) return new Error(g.f("Invalid argument, must be a string, {{regex}} literal, or {{RegExp}} object"));
514
+ if (isRegExp) return regex;
515
+ if (!hasRegExpFlags(regex)) return new RegExp(regex);
516
+ const flags = regex.split("/").pop().split("");
517
+ const validFlags = [
518
+ "i",
519
+ "g",
520
+ "m"
521
+ ];
522
+ const invalidFlags = [];
523
+ flags.forEach(function(flag) {
524
+ if (validFlags.indexOf(flag) === -1) invalidFlags.push(flag);
525
+ });
526
+ if (invalidFlags.length > 0) return new Error(g.f("Invalid {{regex}} flags: %s", invalidFlags));
527
+ const expression = regex.substr(1, regex.lastIndexOf("/") - 1);
528
+ return new RegExp(expression, flags.join(""));
529
+ }
530
+ function hasRegExpFlags(regex) {
531
+ return regex instanceof RegExp ? regex.toString().split("/").pop() : !!regex.match(/.*\/.+$/);
532
+ }
533
+ function idEquals(id1, id2) {
534
+ if (id1 === id2) return true;
535
+ if (typeof id1 === "number" && typeof id2 === "string" || typeof id1 === "string" && typeof id2 === "number") return id1 == id2;
536
+ id1 = JSON.stringify(id1);
537
+ id2 = JSON.stringify(id2);
538
+ if (id1 === id2) return true;
539
+ return false;
540
+ }
541
+ function findIndexOf(arr, target, isEqual) {
542
+ if (!isEqual) return arr.indexOf(target);
543
+ for (let i = 0; i < arr.length; i++) if (isEqual(arr[i], target)) return i;
544
+ return -1;
545
+ }
546
+ /**
547
+ * Returns an object that queries targetIds.
548
+ * @param {Array} The array of targetData
549
+ * @param {String} The Id property name of target model
550
+ * @returns {Object} The object that queries targetIds
551
+ */
552
+ function collectTargetIds(targetData, idPropertyName) {
553
+ const targetIds = [];
554
+ for (let i = 0; i < targetData.length; i++) {
555
+ const targetId = targetData[i][idPropertyName];
556
+ targetIds.push(targetId);
557
+ }
558
+ return { inq: uniq(targetIds) };
559
+ }
560
+ /**
561
+ * Find the idKey of a Model.
562
+ * @param {ModelConstructor} m - Model Constructor
563
+ * @returns {String}
564
+ */
565
+ function idName(m) {
566
+ return m.definition.idName() || "id";
567
+ }
568
+ /**
569
+ * Check a list of IDs to see if there are any duplicates.
570
+ *
571
+ * @param {Array} The array of IDs to check
572
+ * @returns {boolean} If any duplicates were found
573
+ */
574
+ function idsHaveDuplicates(ids) {
575
+ let hasDuplicates = void 0;
576
+ let i, j;
577
+ if (typeof Set === "function") {
578
+ const uniqueIds = /* @__PURE__ */ new Set();
579
+ for (i = 0; i < ids.length; ++i) {
580
+ const idType = typeof ids[i];
581
+ if (idType === "string" || idType === "number") if (uniqueIds.has(ids[i])) {
582
+ hasDuplicates = true;
583
+ break;
584
+ } else uniqueIds.add(ids[i]);
585
+ else break;
586
+ }
587
+ if (hasDuplicates === void 0 && uniqueIds.size === ids.length) hasDuplicates = false;
588
+ }
589
+ if (hasDuplicates === void 0) {
590
+ for (i = 0; i < ids.length && hasDuplicates === void 0; ++i) for (j = 0; j < i; ++j) if (idEquals(ids[i], ids[j])) {
591
+ hasDuplicates = true;
592
+ break;
593
+ }
594
+ }
595
+ return hasDuplicates === true;
596
+ }
597
+ function isClass(fn) {
598
+ return fn && fn.toString().startsWith("class ");
599
+ }
600
+ /**
601
+ * Accept an element, and attach the __parent property to it, unless no object given, while also
602
+ * making sure to check for already created properties
603
+ *
604
+ * @param {object} element
605
+ * @param {Model} parent
606
+ */
607
+ function applyParentProperty(element, parent) {
608
+ assert.strictEqual(typeof element, "object", "Non object element given to assign parent");
609
+ const { constructor: { modelBuilder: { settings: builderSettings } = {} } = {} } = element;
610
+ if (!builderSettings || !builderSettings[BUILDER_PARENT_SETTING]) return;
611
+ if (element.hasOwnProperty(PARENT_PROPERTY_NAME)) {
612
+ const existingParent = element[PARENT_PROPERTY_NAME];
613
+ if (existingParent && existingParent !== parent) g.warn(`Re-assigning child model instance to another parent than the original!
614
+ Although supported, this is not a recommended practice: ${element.constructor.name} -> ${parent.constructor.name}\nYou should create an independent copy of the child model using \`new Model(CHILD)\` OR \`new Model(CHILD.toJSON())\` and assign to new parent`);
615
+ element[PARENT_PROPERTY_NAME] = parent;
616
+ } else Object.defineProperty(element, PARENT_PROPERTY_NAME, {
617
+ value: parent,
618
+ writable: true,
619
+ enumerable: false,
620
+ configurable: false
621
+ });
622
+ }
623
+ }));
624
+ //#endregion
625
+ module.exports = require_utils();