@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,58 @@
1
+ "use strict";
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.js");
3
+ const require_lib_model = require("./model.js");
4
+ //#region src/lib/mixins.ts
5
+ var require_mixins = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
6
+ const debug = require("debug")("loopback:mixin");
7
+ const assert = require("assert");
8
+ const DefaultModelBaseClass = require_lib_model;
9
+ function isModelClass(cls) {
10
+ if (!cls) return false;
11
+ return cls.prototype instanceof DefaultModelBaseClass;
12
+ }
13
+ module.exports = MixinProvider;
14
+ function MixinProvider(modelBuilder) {
15
+ this.modelBuilder = modelBuilder;
16
+ this.mixins = {};
17
+ }
18
+ /**
19
+ * Apply named mixin to the model class
20
+ * @param {Model} modelClass
21
+ * @param {String} name
22
+ * @param {Object} options
23
+ */
24
+ MixinProvider.prototype.applyMixin = function applyMixin(modelClass, name, options) {
25
+ const fn = this.mixins[name];
26
+ if (typeof fn === "function") if (modelClass.dataSource) fn(modelClass, options || {});
27
+ else modelClass.once("dataSourceAttached", function() {
28
+ fn(modelClass, options || {});
29
+ });
30
+ else {
31
+ const model = this.modelBuilder.getModel(name);
32
+ if (model) {
33
+ debug("Mixin is resolved to a model: %s", name);
34
+ modelClass.mixin(model, options);
35
+ } else {
36
+ const errMsg = "Model \"" + modelClass.modelName + "\" uses unknown mixin: " + name;
37
+ debug(errMsg);
38
+ throw new Error(errMsg);
39
+ }
40
+ }
41
+ };
42
+ /**
43
+ * Define a mixin with name
44
+ * @param {String} name Name of the mixin
45
+ * @param {*) mixin The mixin function or a model
46
+ */
47
+ MixinProvider.prototype.define = function defineMixin(name, mixin) {
48
+ assert(typeof mixin === "function", "The mixin must be a function or model class");
49
+ if (this.mixins[name]) debug("Duplicate mixin: %s", name);
50
+ else debug("Defining mixin: %s", name);
51
+ if (isModelClass(mixin)) this.mixins[name] = function(Model, options) {
52
+ Model.mixin(mixin, options);
53
+ };
54
+ else if (typeof mixin === "function") this.mixins[name] = mixin;
55
+ };
56
+ }));
57
+ //#endregion
58
+ module.exports = require_mixins();
@@ -0,0 +1,608 @@
1
+ "use strict";
2
+ const require_runtime = require("../_virtual/_rolldown/runtime.js");
3
+ const require_lib_globalize = require("./globalize.js");
4
+ const require_lib_types = require("./types.js");
5
+ const require_lib_utils = require("./utils.js");
6
+ const require_lib_list = require("./list.js");
7
+ const require_lib_model = require("./model.js");
8
+ const require_lib_model_definition = require("./model-definition.js");
9
+ const require_lib_mixins = require("./mixins.js");
10
+ const require_lib_introspection = require("./introspection.js");
11
+ //#region src/lib/model-builder.ts
12
+ var require_model_builder = /* @__PURE__ */ require_runtime.__commonJSMin(((exports) => {
13
+ /*!
14
+ * Module dependencies
15
+ */
16
+ const g = require_lib_globalize();
17
+ const inflection = require("inflection");
18
+ const EventEmitter = require("events").EventEmitter;
19
+ const util = require("util");
20
+ const assert = require("assert");
21
+ const deprecated = require("depd")("loopback-datasource-juggler");
22
+ const DefaultModelBaseClass = require_lib_model;
23
+ const List = require_lib_list;
24
+ const ModelDefinition = require_lib_model_definition;
25
+ const MixinProvider = require_lib_mixins;
26
+ const { deepMerge, deepMergeProperty, rankArrayElements, isClass, applyParentProperty } = require_lib_utils;
27
+ require_lib_types(ModelBuilder);
28
+ const introspect = require_lib_introspection(ModelBuilder);
29
+ /*!
30
+ * Export public API
31
+ */
32
+ exports.ModelBuilder = exports.Schema = ModelBuilder;
33
+ /*!
34
+ * Helpers
35
+ */
36
+ const slice = Array.prototype.slice;
37
+ /**
38
+ * ModelBuilder - A builder to define data models.
39
+ *
40
+ * @property {Object} definitions Definitions of the models.
41
+ * @property {Object} models Model constructors
42
+ * @class
43
+ */
44
+ function ModelBuilder() {
45
+ this.models = {};
46
+ this.definitions = {};
47
+ this.settings = {};
48
+ this.mixins = new MixinProvider(this);
49
+ this.defaultModelBaseClass = DefaultModelBaseClass;
50
+ }
51
+ util.inherits(ModelBuilder, EventEmitter);
52
+ ModelBuilder.defaultInstance = new ModelBuilder();
53
+ function isModelClass(cls) {
54
+ if (!cls) return false;
55
+ return cls.prototype instanceof DefaultModelBaseClass;
56
+ }
57
+ /**
58
+ * Get a model by name.
59
+ *
60
+ * @param {String} name The model name
61
+ * @param {Boolean} forceCreate Whether the create a stub for the given name if a model doesn't exist.
62
+ * @returns {ModelClass} The model class
63
+ */
64
+ ModelBuilder.prototype.getModel = function(name, forceCreate) {
65
+ let model = this.models[name];
66
+ if (!model && forceCreate) model = this.define(name, {}, { unresolved: true });
67
+ return model;
68
+ };
69
+ /**
70
+ * Get the model definition by name
71
+ * @param {String} name The model name
72
+ * @returns {ModelDefinition} The model definition
73
+ */
74
+ ModelBuilder.prototype.getModelDefinition = function(name) {
75
+ return this.definitions[name];
76
+ };
77
+ /**
78
+ * Define a model class.
79
+ * Simple example:
80
+ * ```
81
+ * var User = modelBuilder.define('User', {
82
+ * email: String,
83
+ * password: String,
84
+ * birthDate: Date,
85
+ * activated: Boolean
86
+ * });
87
+ * ```
88
+ * More advanced example:
89
+ * ```
90
+ * var User = modelBuilder.define('User', {
91
+ * email: { type: String, limit: 150, index: true },
92
+ * password: { type: String, limit: 50 },
93
+ * birthDate: Date,
94
+ * registrationDate: {type: Date, default: function () { return new Date }},
95
+ * activated: { type: Boolean, default: false }
96
+ * });
97
+ * ```
98
+ *
99
+ * @param {String} className Name of class
100
+ * @param {Object} properties Hash of class properties in format `{property: Type, property2: Type2, ...}` or `{property: {type: Type}, property2: {type: Type2}, ...}`
101
+ * @param {Object} settings Other configuration of class
102
+ * @param {Function} parent Parent model
103
+ * @return {ModelClass} The class constructor.
104
+ *
105
+ */
106
+ ModelBuilder.prototype.define = function defineClass(className, properties, settings, parent) {
107
+ const modelBuilder = this;
108
+ const args = slice.call(arguments);
109
+ const pluralName = settings && settings.plural || inflection.pluralize(className);
110
+ let pathName = (settings && settings.http || {}).path || pluralName;
111
+ if (!className) throw new Error(g.f("Class name required"));
112
+ if (args.length === 1) {
113
+ properties = {};
114
+ args.push(properties);
115
+ }
116
+ if (args.length === 2) {
117
+ settings = {};
118
+ args.push(settings);
119
+ }
120
+ properties = properties || {};
121
+ settings = settings || {};
122
+ if (settings.strict === void 0 || settings.strict === null) settings.strict = false;
123
+ let ModelBaseClass = parent || this.defaultModelBaseClass;
124
+ const baseClass = settings.base || settings["super"];
125
+ if (baseClass) {
126
+ settings.base = baseClass;
127
+ delete settings["super"];
128
+ if (isModelClass(baseClass)) ModelBaseClass = baseClass;
129
+ else {
130
+ ModelBaseClass = this.models[baseClass];
131
+ assert(ModelBaseClass, "Base model is not found: " + baseClass);
132
+ }
133
+ }
134
+ assert(ModelBaseClass.getMergePolicy, `Base class ${ModelBaseClass.modelName}
135
+ does not provide method getMergePolicy(). Most likely it is not inheriting
136
+ from datasource-juggler's built-in default ModelBaseClass, which is an
137
+ incorrect usage of the framework.`);
138
+ ModelBaseClass.__rank = ModelBaseClass.__rank || 1;
139
+ if (parent && !settings.base || !parent && settings.base) return ModelBaseClass.extend(className, properties, settings);
140
+ let ModelClass = this.models[className];
141
+ if (!ModelClass || !ModelClass.settings.unresolved) {
142
+ ModelClass = createModelClassCtor(className, ModelBaseClass);
143
+ const events = new EventEmitter();
144
+ events.setMaxListeners(32);
145
+ for (const f in EventEmitter.prototype) if (typeof EventEmitter.prototype[f] === "function") ModelClass[f] = EventEmitter.prototype[f].bind(events);
146
+ hiddenProperty(ModelClass, "modelName", className);
147
+ }
148
+ ModelClass.__rank = ModelBaseClass.__rank + 1;
149
+ util.inherits(ModelClass, ModelBaseClass);
150
+ this.models[className] = ModelClass;
151
+ if (settings.unresolved) {
152
+ ModelClass.settings = { unresolved: true };
153
+ return ModelClass;
154
+ }
155
+ hiddenProperty(ModelClass, "modelBuilder", modelBuilder);
156
+ hiddenProperty(ModelClass, "dataSource", null);
157
+ hiddenProperty(ModelClass, "pluralModelName", pluralName);
158
+ hiddenProperty(ModelClass, "relations", {});
159
+ if (pathName[0] !== "/") pathName = "/" + pathName;
160
+ hiddenProperty(ModelClass, "http", { path: pathName });
161
+ hiddenProperty(ModelClass, "base", ModelBaseClass);
162
+ hiddenProperty(ModelClass, "_observers", {});
163
+ hiddenProperty(ModelClass, "_warned", {});
164
+ for (const i in ModelBaseClass) if (i !== "_mixins" && !(i in ModelClass)) ModelClass[i] = ModelBaseClass[i];
165
+ if (settings.models) Object.keys(settings.models).forEach(function(m) {
166
+ const model = settings.models[m];
167
+ ModelClass[m] = typeof model === "string" ? modelBuilder.getModel(model, true) : model;
168
+ });
169
+ ModelClass.getter = {};
170
+ ModelClass.setter = {};
171
+ for (const p in properties) {
172
+ const excludePropertyList = settings["excludeBaseProperties"];
173
+ if (properties[p] === null || properties[p] === false || excludePropertyList != null && excludePropertyList.indexOf(p) != -1) delete properties[p];
174
+ if (/\./.test(p)) throw new Error(g.f("Property names containing dot(s) are not supported. Model: %s, property: %s", className, p));
175
+ if (p === "constructor") deprecated(g.f("Property name should not be \"{{constructor}}\" in Model: %s", className));
176
+ }
177
+ const modelDefinition = new ModelDefinition(this, className, properties, settings);
178
+ this.definitions[className] = modelDefinition;
179
+ ModelClass.definition = modelDefinition;
180
+ ModelClass.settings = modelDefinition.settings;
181
+ let idInjection = settings.idInjection;
182
+ if (idInjection !== false) idInjection = true;
183
+ let idNames = modelDefinition.idNames();
184
+ if (idNames.length > 0) idInjection = false;
185
+ if (idInjection) ModelClass.definition.defineProperty("id", {
186
+ type: Number,
187
+ id: 1,
188
+ generated: true
189
+ });
190
+ idNames = modelDefinition.idNames();
191
+ if (idNames.length === 1) {
192
+ if (idNames[0] !== "id") Object.defineProperty(ModelClass.prototype, "id", {
193
+ get: function() {
194
+ const idProp = ModelClass.definition.idNames()[0];
195
+ return this.__data[idProp];
196
+ },
197
+ configurable: true,
198
+ enumerable: false
199
+ });
200
+ } else Object.defineProperty(ModelClass.prototype, "id", {
201
+ get: function() {
202
+ const compositeId = {};
203
+ const idNames = ModelClass.definition.idNames();
204
+ for (let i = 0, p; i < idNames.length; i++) {
205
+ p = idNames[i];
206
+ compositeId[p] = this.__data[p];
207
+ }
208
+ return compositeId;
209
+ },
210
+ configurable: true,
211
+ enumerable: false
212
+ });
213
+ let forceId = ModelClass.settings.forceId;
214
+ if (idNames.length > 0) {
215
+ const idName = modelDefinition.idName();
216
+ const idProp = ModelClass.definition.rawProperties[idName];
217
+ if (idProp.generated && forceId !== false) forceId = "auto";
218
+ else if (!idProp.generated && forceId === "auto") forceId = false;
219
+ if (forceId) ModelClass.validatesAbsenceOf(idName, { if: "isNewRecord" });
220
+ ModelClass.definition.properties[idName].updateOnly = !!forceId;
221
+ ModelClass.definition.rawProperties[idName].updateOnly = !!forceId;
222
+ ModelClass.settings.forceId = forceId;
223
+ }
224
+ ModelClass.forEachProperty = function(cb) {
225
+ const props = ModelClass.definition.properties;
226
+ const keys = Object.keys(props);
227
+ for (let i = 0, n = keys.length; i < n; i++) cb(keys[i], props[keys[i]]);
228
+ };
229
+ ModelClass.attachTo = function(dataSource) {
230
+ dataSource.attach(this);
231
+ };
232
+ /** Extend the model with the specified model, properties, and other settings.
233
+ * For example, to extend an existing model, for example, a built-in model:
234
+ *
235
+ * ```js
236
+ * var Customer = User.extend('customer', {
237
+ * accountId: String,
238
+ * vip: Boolean
239
+ * });
240
+ * ```
241
+ *
242
+ * To extend the base model, essentially creating a new model:
243
+ * ```js
244
+ * var user = loopback.Model.extend('user', properties, options);
245
+ * ```
246
+ *
247
+ * @param {String} className Name of the new model being defined.
248
+ * @options {Object} subClassProperties child model properties, added to base model
249
+ * properties.
250
+ * @options {Object} subClassSettings child model settings such as relations and acls,
251
+ * merged with base model settings.
252
+ */
253
+ ModelClass.extend = function(className, subClassProperties, subClassSettings) {
254
+ const baseClassProperties = ModelClass.definition.properties;
255
+ const baseClassSettings = ModelClass.definition.settings;
256
+ subClassProperties = subClassProperties || {};
257
+ subClassSettings = subClassSettings || {};
258
+ let idFound = false;
259
+ for (const k in subClassProperties) if (subClassProperties[k] && subClassProperties[k].id) {
260
+ idFound = true;
261
+ break;
262
+ }
263
+ const keys = Object.keys(baseClassProperties);
264
+ for (let i = 0, n = keys.length; i < n; i++) {
265
+ const key = keys[i];
266
+ if (idFound && baseClassProperties[key].id) continue;
267
+ if (subClassProperties[key] === void 0) {
268
+ const baseProp = baseClassProperties[key];
269
+ let basePropCopy = baseProp;
270
+ if (baseProp && typeof baseProp === "object") basePropCopy = deepMerge(baseProp);
271
+ subClassProperties[key] = basePropCopy;
272
+ }
273
+ }
274
+ const originalSubclassSettings = subClassSettings;
275
+ const mergePolicy = ModelClass.getMergePolicy(subClassSettings);
276
+ subClassSettings = mergeSettings(baseClassSettings, subClassSettings, mergePolicy);
277
+ if (!originalSubclassSettings.base) subClassSettings.base = ModelClass;
278
+ const subClass = modelBuilder.define(className, subClassProperties, subClassSettings, ModelClass);
279
+ if (typeof subClass.setup === "function") subClass.setup.call(subClass);
280
+ return subClass;
281
+ };
282
+ function mergeSettings(baseClassSettings, subClassSettings, mergePolicy) {
283
+ const mergedSettings = deepMerge(baseClassSettings);
284
+ Object.keys(baseClassSettings).forEach(function(key) {
285
+ if (mergePolicy[key] && mergePolicy[key].rank) baseClassSettings[key] = rankArrayElements(baseClassSettings[key], ModelBaseClass.__rank);
286
+ });
287
+ Object.keys(subClassSettings).forEach(function(key) {
288
+ if (mergePolicy[key] == null) mergePolicy[key] = mergePolicy.__default || {};
289
+ if (subClassSettings[key] === mergePolicy.__delete) {
290
+ delete mergedSettings[key];
291
+ return;
292
+ }
293
+ if (mergePolicy[key].rank) subClassSettings[key] = rankArrayElements(subClassSettings[key], ModelBaseClass.__rank + 1);
294
+ if (mergePolicy[key].replace) {
295
+ mergedSettings[key] = subClassSettings[key];
296
+ return;
297
+ }
298
+ if (mergePolicy[key].patch) {
299
+ mergedSettings[key] = mergedSettings[key] || {};
300
+ Object.keys(subClassSettings[key]).forEach(function(innerKey) {
301
+ mergedSettings[key][innerKey] = subClassSettings[key][innerKey];
302
+ });
303
+ return;
304
+ }
305
+ mergedSettings[key] = deepMergeProperty(baseClassSettings[key], subClassSettings[key]);
306
+ });
307
+ return mergedSettings;
308
+ }
309
+ /**
310
+ * Register a property for the model class
311
+ * @param {String} propertyName Name of the property.
312
+ */
313
+ ModelClass.registerProperty = function(propertyName) {
314
+ const properties = modelDefinition.build();
315
+ const prop = properties[propertyName];
316
+ const DataType = prop.type;
317
+ if (!DataType) throw new Error(g.f("Invalid type for property %s", propertyName));
318
+ if (prop.required) {
319
+ const requiredOptions = typeof prop.required === "object" ? prop.required : void 0;
320
+ ModelClass.validatesPresenceOf(propertyName, requiredOptions);
321
+ }
322
+ if (DataType === Date) ModelClass.validatesDateOf(propertyName);
323
+ Object.defineProperty(ModelClass.prototype, propertyName, {
324
+ get: function() {
325
+ if (ModelClass.getter[propertyName]) return ModelClass.getter[propertyName].call(this);
326
+ else return this.__data && this.__data[propertyName];
327
+ },
328
+ set: function(value) {
329
+ let DataType = ModelClass.definition.properties[propertyName].type;
330
+ if (Array.isArray(DataType) || DataType === Array) DataType = List;
331
+ else if (DataType === Date) DataType = DateType;
332
+ else if (DataType === Boolean) DataType = BooleanType;
333
+ else if (typeof DataType === "string") DataType = modelBuilder.resolveType(DataType);
334
+ const persistUndefinedAsNull = ModelClass.definition.settings.persistUndefinedAsNull;
335
+ if (value === void 0 && persistUndefinedAsNull) value = null;
336
+ if (ModelClass.setter[propertyName]) ModelClass.setter[propertyName].call(this, value);
337
+ else {
338
+ this.__data = this.__data || {};
339
+ if (value === null || value === void 0) this.__data[propertyName] = value;
340
+ else if (DataType === List) this.__data[propertyName] = isClass(DataType) ? new DataType(value, properties[propertyName].type, this) : DataType(value, properties[propertyName].type, this);
341
+ else {
342
+ this.__data[propertyName] = value instanceof DataType ? value : isClass(DataType) ? new DataType(value) : DataType(value);
343
+ if (value && this.__data[propertyName] instanceof DefaultModelBaseClass) applyParentProperty(this.__data[propertyName], this);
344
+ }
345
+ }
346
+ },
347
+ configurable: true,
348
+ enumerable: true
349
+ });
350
+ Object.defineProperty(ModelClass.prototype, "$" + propertyName, {
351
+ get: function() {
352
+ return this.__data && this.__data[propertyName];
353
+ },
354
+ set: function(value) {
355
+ if (!this.__data) this.__data = {};
356
+ this.__data[propertyName] = value;
357
+ },
358
+ configurable: true,
359
+ enumerable: false
360
+ });
361
+ };
362
+ const props = ModelClass.definition.properties;
363
+ let keys = Object.keys(props);
364
+ let size = keys.length;
365
+ for (let i = 0; i < size; i++) {
366
+ const propertyName = keys[i];
367
+ ModelClass.registerProperty(propertyName);
368
+ }
369
+ const mixinSettings = settings.mixins || {};
370
+ keys = Object.keys(mixinSettings);
371
+ size = keys.length;
372
+ for (let i = 0; i < size; i++) {
373
+ const name = keys[i];
374
+ let mixin = mixinSettings[name];
375
+ if (mixin === true) mixin = {};
376
+ if (Array.isArray(mixin)) mixin.forEach(function(m) {
377
+ if (m === true) m = {};
378
+ if (typeof m === "object") modelBuilder.mixins.applyMixin(ModelClass, name, m);
379
+ });
380
+ else if (typeof mixin === "object") modelBuilder.mixins.applyMixin(ModelClass, name, mixin);
381
+ }
382
+ ModelClass.emit("defined", ModelClass);
383
+ return ModelClass;
384
+ };
385
+ function createModelClassCtor(name, ModelBaseClass) {
386
+ name = name.replace(/[-.:]/g, "_");
387
+ try {
388
+ return new Function("ModelBaseClass", `
389
+ // every class can receive hash of data as optional param
390
+ return function ${name}(data, options) {
391
+ if (!(this instanceof ${name})) {
392
+ return new ${name}(data, options);
393
+ }
394
+ if (${name}.settings.unresolved) {
395
+ throw new Error(g.f('Model %s is not defined.', ${JSON.stringify(name)}));
396
+ }
397
+ ModelBaseClass.apply(this, arguments);
398
+ };`)(ModelBaseClass);
399
+ } catch (err) {
400
+ if (err.name === "SyntaxError") return createModelClassCtor("ModelConstructor", ModelBaseClass);
401
+ else throw err;
402
+ }
403
+ }
404
+ function DateType(arg) {
405
+ if (arg === null) return null;
406
+ return new Date(arg);
407
+ }
408
+ function BooleanType(arg) {
409
+ if (typeof arg === "string") switch (arg) {
410
+ case "true":
411
+ case "1": return true;
412
+ case "false":
413
+ case "0": return false;
414
+ }
415
+ if (arg == null) return null;
416
+ return Boolean(arg);
417
+ }
418
+ /**
419
+ * Define single property named `propertyName` on `model`
420
+ *
421
+ * @param {String} model Name of model
422
+ * @param {String} propertyName Name of property
423
+ * @param {Object} propertyDefinition Property settings
424
+ */
425
+ ModelBuilder.prototype.defineProperty = function(model, propertyName, propertyDefinition) {
426
+ this.definitions[model].defineProperty(propertyName, propertyDefinition);
427
+ this.models[model].registerProperty(propertyName);
428
+ };
429
+ /**
430
+ * Define a new value type that can be used in model schemas as a property type.
431
+ * @param {function()} type Type constructor.
432
+ * @param {string[]=} aliases Optional list of alternative names for this type.
433
+ */
434
+ ModelBuilder.prototype.defineValueType = function(type, aliases) {
435
+ ModelBuilder.registerType(type, aliases);
436
+ };
437
+ /**
438
+ * Extend existing model with specified properties
439
+ *
440
+ * Example:
441
+ * Instead of extending a model with attributes like this (for example):
442
+ *
443
+ * ```js
444
+ * db.defineProperty('Content', 'competitionType',
445
+ * { type: String });
446
+ * db.defineProperty('Content', 'expiryDate',
447
+ * { type: Date, index: true });
448
+ * db.defineProperty('Content', 'isExpired',
449
+ * { type: Boolean, index: true });
450
+ *```
451
+ * This method enables you to extend a model as follows (for example):
452
+ * ```js
453
+ * db.extendModel('Content', {
454
+ * competitionType: String,
455
+ * expiryDate: { type: Date, index: true },
456
+ * isExpired: { type: Boolean, index: true }
457
+ * });
458
+ *```
459
+ *
460
+ * @param {String} model Name of model
461
+ * @options {Object} properties JSON object specifying properties. Each property is a key whos value is
462
+ * either the [type](http://docs.strongloop.com/display/LB/LoopBack+types) or `propertyName: {options}`
463
+ * where the options are described below.
464
+ * @property {String} type Datatype of property: Must be an [LDL type](http://docs.strongloop.com/display/LB/LoopBack+types).
465
+ * @property {Boolean} index True if the property is an index; false otherwise.
466
+ */
467
+ ModelBuilder.prototype.extendModel = function(model, props) {
468
+ const t = this;
469
+ const keys = Object.keys(props);
470
+ for (let i = 0; i < keys.length; i++) {
471
+ const definition = props[keys[i]];
472
+ t.defineProperty(model, keys[i], definition);
473
+ }
474
+ };
475
+ ModelBuilder.prototype.copyModel = function copyModel(Master) {
476
+ const modelBuilder = this;
477
+ const className = Master.modelName;
478
+ const md = Master.modelBuilder.definitions[className];
479
+ const Slave = function SlaveModel() {
480
+ Master.apply(this, [].slice.call(arguments));
481
+ };
482
+ util.inherits(Slave, Master);
483
+ Slave.__proto__ = Master;
484
+ hiddenProperty(Slave, "modelBuilder", modelBuilder);
485
+ hiddenProperty(Slave, "modelName", className);
486
+ hiddenProperty(Slave, "relations", Master.relations);
487
+ if (!(className in modelBuilder.models)) {
488
+ modelBuilder.models[className] = Slave;
489
+ modelBuilder.definitions[className] = {
490
+ properties: md.properties,
491
+ settings: md.settings
492
+ };
493
+ }
494
+ return Slave;
495
+ };
496
+ /**
497
+ * Remove a model from the registry.
498
+ *
499
+ * @param {String} modelName
500
+ */
501
+ ModelBuilder.prototype.deleteModelByName = function(modelName) {
502
+ delete this.models[modelName];
503
+ delete this.definitions[modelName];
504
+ };
505
+ /*!
506
+ * Define hidden property
507
+ */
508
+ function hiddenProperty(where, property, value) {
509
+ Object.defineProperty(where, property, {
510
+ writable: true,
511
+ enumerable: false,
512
+ configurable: true,
513
+ value
514
+ });
515
+ }
516
+ /**
517
+ * Get the schema name. If no parameter is given, then an anonymous model name
518
+ * is generated and returned.
519
+ * @param {string=} name The optional name parameter.
520
+ * @returns {string} The schema name.
521
+ */
522
+ ModelBuilder.prototype.getSchemaName = function(name) {
523
+ if (name) return name;
524
+ if (typeof this._nameCount !== "number") this._nameCount = 0;
525
+ else this._nameCount++;
526
+ return "AnonymousModel_" + this._nameCount;
527
+ };
528
+ /**
529
+ * Resolve the type string to be a function, for example, 'String' to String.
530
+ * Returns {Function} if the type is resolved
531
+ * @param {String | Object | Array} prop The object whose type is to be resolved
532
+ */
533
+ ModelBuilder.prototype.resolveType = function(prop, isSubProperty) {
534
+ if (!prop) return prop;
535
+ if (Array.isArray(prop) && prop.length > 0) {
536
+ const itemType = this.resolveType(prop[0]);
537
+ if (typeof itemType === "function") return [itemType];
538
+ else return itemType;
539
+ }
540
+ if (typeof prop === "string") {
541
+ const schemaType = ModelBuilder.schemaTypes[prop.toLowerCase()] || this.models[prop];
542
+ if (schemaType) return schemaType;
543
+ else {
544
+ prop = this.define(prop, {}, { unresolved: true });
545
+ return prop;
546
+ }
547
+ } else if (prop.constructor.name === "Object") if (!isSubProperty && prop.type) return this.resolveType(prop.type, true);
548
+ else return this.define(this.getSchemaName(null), prop, {
549
+ anonymous: true,
550
+ idInjection: false,
551
+ strict: this.settings.strictEmbeddedModels || false
552
+ });
553
+ else if ("function" === typeof prop) return prop;
554
+ return prop;
555
+ };
556
+ /**
557
+ * Build models from schema definitions
558
+ *
559
+ * `schemas` can be one of the following:
560
+ *
561
+ * 1. An array of named schema definition JSON objects
562
+ * 2. A schema definition JSON object
563
+ * 3. A list of property definitions (anonymous)
564
+ *
565
+ * @param {*} schemas The schemas
566
+ * @returns {Object.<string, ModelClass>} A map of model constructors keyed by
567
+ * model name.
568
+ */
569
+ ModelBuilder.prototype.buildModels = function(schemas, createModel) {
570
+ const models = {};
571
+ if (!Array.isArray(schemas)) if (schemas.properties && schemas.name) schemas = [schemas];
572
+ else schemas = [{
573
+ name: this.getSchemaName(),
574
+ properties: schemas,
575
+ options: { anonymous: true }
576
+ }];
577
+ let relations = [];
578
+ for (let s = 0, n = schemas.length; s < n; s++) {
579
+ const name = this.getSchemaName(schemas[s].name);
580
+ schemas[s].name = name;
581
+ const model = typeof createModel === "function" ? createModel(schemas[s].name, schemas[s].properties, schemas[s].options) : this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
582
+ models[name] = model;
583
+ relations = relations.concat(model.definition.relations);
584
+ }
585
+ for (let i = 0; i < relations.length; i++) {
586
+ const relation = relations[i];
587
+ const sourceModel = models[relation.source];
588
+ const targetModel = models[relation.target];
589
+ if (sourceModel && targetModel) {
590
+ if (typeof sourceModel[relation.type] === "function") sourceModel[relation.type](targetModel, { as: relation.as });
591
+ }
592
+ }
593
+ return models;
594
+ };
595
+ /**
596
+ * Introspect the JSON document to build a corresponding model.
597
+ * @param {String} name The model name
598
+ * @param {Object} json The JSON object
599
+ * @param {Object} options The options
600
+ * @returns {ModelClass} The generated model class constructor.
601
+ */
602
+ ModelBuilder.prototype.buildModelFromInstance = function(name, json, options) {
603
+ const schema = introspect(json);
604
+ return this.define(name, schema, options);
605
+ };
606
+ }));
607
+ //#endregion
608
+ module.exports = require_model_builder();