mythix-orm 1.11.6 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,60 @@
1
+ ///! Import `const { Types: { Helpers } } = require('mythix-orm');`
2
+ ///!
3
+ ///! Helpers are a set of utilities used to assist with
4
+ ///! `defaultValue` attributes on model fields.
5
+ ///!
6
+ ///! Default values for fields in Mythix ORM are generally used in
7
+ ///! two different places: As column values directly, or as `DEFAULT VALUE`
8
+ ///! for columns when defining tables in the database. When creating tables
9
+ ///! the `defaultValue` of a field will generally only ever be used if it
10
+ ///! has the `remote` flag set. Otherwise, Mythix ORM simply provides the
11
+ ///! `defaultValue` to the database directly during `INSERT` and `UPDATE`
12
+ ///! operations.
13
+ ///!
14
+ ///! ```javascript
15
+ ///! const { Model, Types } = require('mythix-orm');
16
+ ///!
17
+ ///! class TestModel extends Model {
18
+ ///! static fields = {
19
+ ///! myCustomDate: {
20
+ ///! type: Types.STRING(32),
21
+ ///! // Inform Mythix ORM that our `defaultValue` should be
22
+ ///! // applied on every `UPDATE` operation... even if the
23
+ ///! // field already has a value.
24
+ ///! defaultValue: Types.Helpers.defaultValueFlags(() => {
25
+ ///! return (new Date()).toISOString();
26
+ ///! }, { onUpdate: true }),
27
+ ///! allowNull: false,
28
+ ///! index: true,
29
+ ///! }
30
+ ///! };
31
+ ///! }
32
+ ///! ```
33
+ ///!
34
+ ///! Properties:
35
+ ///! FLAG_ON_INITIALIZE: number = 0x01
36
+ ///! This flag informs Mythix ORM that the `defaultValue` should
37
+ ///! be fetched and used as soon as a model instance is first
38
+ ///! initialized.
39
+ ///! FLAG_ON_INSERT: number = 0x02
40
+ ///! This flag informs Mythix ORM that the `defaultValue` should
41
+ ///! only be used on `INSERT` operations.
42
+ ///! FLAG_ON_UPDATE: number = 0x04
43
+ ///! This flag informs Mythix ORM that the `defaultValue` should
44
+ ///! only be used on `UPDATE` operations. This is used for example
45
+ ///! by `Types.DATE.Defaults.NOW.UPDATE`.
46
+ ///! FLAG_ON_STORE: number = 0x06
47
+ ///! This flag informs Mythix ORM that the `defaultValue` should
48
+ ///! only be used on `INSERT` and `UPDATE` operations.
49
+ ///! FLAG_LITERAL: number = 0x08
50
+ ///! This flag informs Mythix ORM that the `defaultValue` is intended
51
+ ///! to be a literal value, and should not be escaped.
52
+ ///! FLAG_REMOTE: number = 0x10
53
+ ///! This flag informs Mythix ORM that the `defaultValue` is provided
54
+ ///! by the database itself. For example the `Types.BIGINT.Defaults.AUTO_INCREMENT`
55
+ ///! type uses this flag.
56
+ ///!
57
+ ///! DocScope: Helpers
1
58
  'use strict';
2
59
 
3
60
  const FLAG_ON_INITIALIZE = 0x01;
@@ -7,6 +64,46 @@ const FLAG_ON_STORE = FLAG_ON_INSERT | FLAG_ON_UPDATE;
7
64
  const FLAG_LITERAL = 0x08;
8
65
  const FLAG_REMOTE = 0x10;
9
66
 
67
+ /// Give a `defaultValue` method certain flags to
68
+ /// assist Mythix ORM in working with a field's
69
+ /// `defaultValue`.
70
+ ///
71
+ /// `defaultValue` flags change the way a `defaultValue`
72
+ /// for a field behaves. For example, if the `FLAG_LITERAL`
73
+ /// is set, then the database will not escape the default value.
74
+ ///
75
+ /// By default, all `defaultValue` attributes on fields (that
76
+ /// are methods) have the flags `FLAG_ON_INITIALIZE`, which
77
+ /// simply tells Mythix ORM to call the `defaultValue` method
78
+ /// and assign the result to the field as soon as the model is
79
+ /// instantiated. This is the default behavior of all `defaultValue`
80
+ /// attributes... unless the defined flags change that default
81
+ /// behavior. As soon as any flag is set on a `defaultValue` method,
82
+ /// the `FLAG_ON_INITIALIZE` value will be cleared.
83
+ ///
84
+ /// This adds an attribute named `mythixFlags` to
85
+ /// the provided method. This attribute is then
86
+ /// used by Mythix ORM to know what to do with the
87
+ /// `defaultValue` method provided on a field.
88
+ ///
89
+ /// Arguments:
90
+ /// func: Function
91
+ /// The `defaultValue` function to apply flags to.
92
+ /// flags: object
93
+ /// An object, specifying which flags to enable
94
+ /// or disable. The allowed properties are listed in
95
+ /// the table below.
96
+ /// | Option | Type | Default Value | Description |
97
+ /// | ------------- | ---- | ------------- | ----------- |
98
+ /// | `onInitialize` | `boolean` | `true` | If `true`, then assign the `defaultValue` as soon as a model is instantiated. |
99
+ /// | `onInsert` | `boolean` | `false` | If `true`, then only assign the `defaultValue` when an `INSERT` operation is being executed. |
100
+ /// | `onUpdate` | `boolean` | `false` | If `true`, then only assign the `defaultValue` when an `UPDATE` operation is being executed. |
101
+ /// | `onStore` | `boolean` | `false` | If `true`, then only assign the `defaultValue` when an `INSERT` or `UPDATE` operation is being executed. |
102
+ /// | `literal` | `boolean` | `false` | If `true`, then inform Mythix ORM that the value is a literal, and not to escape it. |
103
+ /// | `remote` | `boolean` | `false` | If `true`, then inform Mythix ORM that the value is provided by the database itself. |
104
+ ///
105
+ /// Return: Function
106
+ /// The method provided, with a new `mythixFlags` assigned to it.
10
107
  function defaultValueFlags(func, _flagsObj) {
11
108
  let flags = FLAG_ON_INITIALIZE;
12
109
  let flagsObj = _flagsObj || {};
@@ -36,6 +133,15 @@ function defaultValueFlags(func, _flagsObj) {
36
133
  return func;
37
134
  }
38
135
 
136
+ /// Fetch the `mythixFlags` attribute on the provided
137
+ /// function. If none is found, or no method is provided,
138
+ /// then the value `0` will be returned instead.
139
+ ///
140
+ /// Return: number
141
+ /// The `defaultValue` flags as a bitmask. If none is found, or no
142
+ /// method is provided, then `0` will be returned instead.
143
+ ///
144
+ /// See: Helpers.defaultValueFlags
39
145
  function getDefaultValueFlags(func) {
40
146
  if (!func)
41
147
  return 0;
@@ -43,6 +149,24 @@ function getDefaultValueFlags(func) {
43
149
  return func.mythixFlags || 0;
44
150
  }
45
151
 
152
+ /// Check if the provided `defaultValue` method has
153
+ /// specific flags set. The flags are provided via
154
+ /// their name.
155
+ ///
156
+ /// Arguments:
157
+ /// func: Function
158
+ /// The `defaultValue` function to check.
159
+ /// checkFlags: Array<string>
160
+ /// The flags to check. Acceptable values are:
161
+ /// `[ 'onInitialize', 'onInsert', 'onUpdate', 'onStore', 'literal', 'remote' ]`
162
+ ///
163
+ /// Return: boolean
164
+ /// Return `true` if all provided `checkFlags` are `true`. If any
165
+ /// provided flag results in `false`, then the method will return
166
+ /// `false`. In short, all provided flags must be `true`, otherwise
167
+ /// `false` is returned.
168
+ ///
169
+ /// See: Helpers.defaultValueFlags
46
170
  function checkDefaultValueFlags(func, checkFlags) {
47
171
  if (!checkFlags || !checkFlags.length || !func)
48
172
  return false;
package/lib/types/type.js CHANGED
@@ -8,6 +8,20 @@ class Type {
8
8
  return '<UNKNOWN>';
9
9
  }
10
10
 
11
+ /// Get the "display name" for this type instance.
12
+ ///
13
+ /// This will return a "human friendly" name for the type,
14
+ /// generally used for logging or debugging. It will change
15
+ /// based on the type it is called upon. For example, a
16
+ /// <see>BooleanType</see> will return the string value `'BOOLEAN'`.
17
+ ///
18
+ /// Note:
19
+ /// This method is also a static method on the type class.
20
+ ///
21
+ /// Return: string
22
+ /// A string representing the type. This is not the same as
23
+ /// the database equivalent type. Rather, it is simply used
24
+ /// to represent the type for logging or debugging.
11
25
  getDisplayName() {
12
26
  return this.constructor.getDisplayName();
13
27
  }
@@ -18,6 +32,18 @@ class Type {
18
32
  return this;
19
33
  };
20
34
 
35
+ /// Use this method to check if a class
36
+ /// is a Mythix ORM field type. It will return
37
+ /// `true` if the provided value is a class
38
+ /// that inherits from <see>Type</see>, or
39
+ /// if the provided value has an attribute
40
+ /// named `_isMythixFieldType` that is truthy.
41
+ ///
42
+ /// Return: boolean
43
+ ///
44
+ /// Arguments:
45
+ /// value: Function
46
+ /// Value to check.
21
47
  static isTypeClass(value) {
22
48
  if (!value)
23
49
  return false;
@@ -31,6 +57,22 @@ class Type {
31
57
  return false;
32
58
  }
33
59
 
60
+ /// Check to see if the provided value is
61
+ /// an *instance* of a Mythix ORM <see>Type</see>.
62
+ /// Unlike <see>Type.static isTypeClass</see>, which
63
+ /// checks if a *class* is a <see>Type</see>, this will check
64
+ /// to see if an *instance* is an instance of a
65
+ /// Mythix ORM <see>Type</see>. It will return
66
+ /// `true` if the provided value is an `instanceof`
67
+ /// <see>Type</see>, or if the value's `constructor`
68
+ /// property has a truthy `_isMythixFieldType` property
69
+ /// (`value.constructor._isMythixFieldType`)
70
+ ///
71
+ /// Return: boolean
72
+ ///
73
+ /// Arguments:
74
+ /// value: any
75
+ /// Value to check
34
76
  static isType(value) {
35
77
  if (!value)
36
78
  return false;
@@ -44,10 +86,42 @@ class Type {
44
86
  return false;
45
87
  }
46
88
 
89
+ /// Check to see if the provided type is the same as this type.
90
+ ///
91
+ /// This checks if the two types are the same type by comparing
92
+ /// the type names. For example, a `Types.BooleanType.isSameType(new BooleanType())`
93
+ /// would return `true`.
94
+ ///
95
+ /// Arguments:
96
+ /// value: <see>Type</see>
97
+ /// A type instance to check. This must be a type instance, not a type class.
98
+ ///
99
+ /// Return: boolean
100
+ /// Return `true` if the provided `value` is the same type instance as this type class.
101
+ /// Return `false` otherwise. Similarity is based on the name of the types. If the class
102
+ /// names between the two match, then the types are considered the same type.
47
103
  static isSameType(value) {
48
104
  return (this.isType(value) && value.constructor && value.constructor.name === this.name);
49
105
  }
50
106
 
107
+ /// Instantiate this field type.
108
+ ///
109
+ /// This method will instantiate the type class it
110
+ /// is called from. It won't instantiate the type if
111
+ /// it is already instantiated, and it will call the
112
+ /// type wrapper if one was provided (see <see>Type.static wrapConstructor</see>).
113
+ /// It may not need to instantiate the type if the user already
114
+ /// did so (for example `type: new Types.StringType()`,
115
+ /// or `type: Types.STRING()`). It may need to instantiate
116
+ /// however if the user didn't do so, for example `type: Types.StringType`,
117
+ /// or `type: Types.STRING`.
118
+ ///
119
+ /// Note:
120
+ /// If the type is already instantiated, then it will be
121
+ /// cloned instead using <see>Type.clone</see>.
122
+ ///
123
+ /// Return: <see>Type</see>
124
+ /// The instantiated type instance.
51
125
  static instantiateType(_type) {
52
126
  let type = _type;
53
127
  if (!type)
@@ -77,6 +151,28 @@ class Type {
77
151
  return true;
78
152
  }
79
153
 
154
+ /// This method is used to create the "shortcut" types
155
+ /// for Mythix ORM. For example, a "string" type can be
156
+ /// created either via `new Types.StringType()`, or via `Types.STRING()`.
157
+ /// Both are equivalent. `Types.STRING` was created by calling
158
+ /// `Types.STRING = Type.wrapConstructor(Types.StringType)`.
159
+ ///
160
+ /// This method "wraps" a type constructor, providing a more
161
+ /// convenient way to create and use field types. It works
162
+ /// by wrapping the provided class's constructor in a callable
163
+ /// method. If the method **is not** called (i.e. `type: Types.STRING`)
164
+ /// then the type system still knows what to do, because the wrapping
165
+ /// method will be called by Mythix ORM to instantiate the type
166
+ /// when the model's fields are first fetched.
167
+ ///
168
+ /// Arguments:
169
+ /// TypeKlass: class <see>Type</see>
170
+ /// A `Type` class to wrap.
171
+ ///
172
+ /// Return: Function
173
+ /// A callable function to instantiate the type class. If not called
174
+ /// by the user, then Mythix ORM will call the method when the type
175
+ /// is being created to fetch the type instance.
80
176
  static wrapConstructor(TypeKlass) {
81
177
  let TypeWrapper = function(...args) {
82
178
  return new TypeKlass(...args);
@@ -116,6 +212,15 @@ class Type {
116
212
  return TypeWrapper;
117
213
  }
118
214
 
215
+ /// Instantiate a new field Type.
216
+ ///
217
+ /// Arguments:
218
+ /// ...args: Array<any>
219
+ /// All field types "record" their constructor arguments, always.
220
+ /// Mythix ORM requires that all field types, when calling `super` in
221
+ /// their constructor, provide the base `Type` class all arguments
222
+ /// provided to their constructor.
223
+ /// This is so that the field type can later be cloned or serialized.
119
224
  constructor(...args) {
120
225
  Object.defineProperties(this, {
121
226
  '_args': {
@@ -139,6 +244,10 @@ class Type {
139
244
  });
140
245
  }
141
246
 
247
+ /// Clone this field type.
248
+ ///
249
+ /// Return: Type
250
+ /// This field type, cloned to a new instance.
142
251
  clone() {
143
252
  let Type = this.constructor;
144
253
  let newInstance = new Type(...this._args);
@@ -149,22 +258,86 @@ class Type {
149
258
  return newInstance;
150
259
  }
151
260
 
261
+ /// Check if this field type is a virtual field type.
262
+ ///
263
+ /// Note:
264
+ /// This method is also a static method on the type class.
265
+ ///
266
+ /// Return: boolean
267
+ /// Return `true` if this field type is a virtual field
268
+ /// type. Virtual fields are fields that don't *directly*
269
+ /// store a concrete value themselves. They often pull
270
+ /// and combine values from other fields, or other models.
271
+ /// Currently the only virtual fields in Mythix ORM are
272
+ /// <see>ModelType</see> and <see>ModelsType</see>.
152
273
  isVirtual() {
153
274
  return this.constructor.isVirtual.call(this.constructor);
154
275
  }
155
276
 
277
+ /// Check if this field type is a relational field type.
278
+ ///
279
+ /// Note:
280
+ /// This method is also a static method on the type class.
281
+ ///
282
+ /// Return: boolean
283
+ /// Return `true` if this field type defines a relation,
284
+ /// instead of a direct value. Field types such as
285
+ /// <see>ModelType</see> and <see>ModelsType</see> are
286
+ /// examples of relational field types. <see>ForeignKeyType</see>
287
+ /// **is not** a relational type, but is instead considered
288
+ /// a "concrete" type. Even though it does technically define
289
+ /// a relationship, it is also a direct field, that actually
290
+ /// has a concrete value, backed by database storage.
156
291
  isRelational() {
157
292
  return this.constructor.isRelational.call(this.constructor);
158
293
  }
159
294
 
295
+ /// Check if this field type is a foreign key type.
296
+ ///
297
+ /// Note:
298
+ /// This method is also a static method on the type class.
299
+ ///
300
+ /// Return: boolean
301
+ /// Return `true` if this field type defines a foreign key,
302
+ /// or `false` otherwise.
160
303
  isForeignKey() {
161
304
  return this.constructor.isForeignKey.call(this.constructor);
162
305
  }
163
306
 
307
+ /// Used to decide if a field should be exposed on the model
308
+ /// or not.
309
+ ///
310
+ /// Some fields, such as types like <see>ModelType</see> and
311
+ /// <see>ModelsType</see> do not expose themselves on the model
312
+ /// as model fields. Instead they only expose themselves via the
313
+ /// relationship methods they inject onto the model instance.
314
+ /// Most fields **do** expose themselves on the model, but any
315
+ /// field that overrides this method and returns `false` will
316
+ /// not be exposed on the model instance.
317
+ ///
318
+ /// Note:
319
+ /// This method is also a static method on the type class.
320
+ ///
321
+ /// Return: boolean
322
+ /// If `true` is returned, then the field owning this type
323
+ /// will be added to the model instance, and accessible by
324
+ /// the user. If `false` is returned, then the field will
325
+ /// be "hidden", and not exposed to the user on model instances.
164
326
  exposeToModel() {
165
327
  return this.constructor.exposeToModel.call(this.constructor);
166
328
  }
167
329
 
330
+ /// Check if this field's value is driven by the database.
331
+ ///
332
+ /// For example, given an AUTOINCREMENTING id field, this
333
+ /// would return `true`, because the value of the field is
334
+ /// provided by the database itself. This can be true for
335
+ /// nearly any field type, but is generally only true for
336
+ /// auto-incrementing ids, and date/time types.
337
+ ///
338
+ /// Return: boolean
339
+ /// Return `true` for any field type that has a value provided
340
+ /// directly by the underlying database, or `false` otherwise.
168
341
  isRemote() {
169
342
  let field = this.getField();
170
343
  if (!field)
@@ -176,19 +349,41 @@ class Type {
176
349
  return checkDefaultValueFlags(field.defaultValue, [ 'remote' ]);
177
350
  }
178
351
 
352
+ /// Check if the value provided is a valid value for this field type.
353
+ ///
354
+ /// This is used to check if any value *would* be a valid value for
355
+ /// this type. For example, if this type happens to be a <see>BooleanType</see>,
356
+ /// then `isValidValue` would only return `true` if the provided `value`
357
+ /// was either `true` or `false`. All field types have their own custom
358
+ /// `isValidValue` implementation, to verify values against their type.
359
+ ///
360
+ /// Arguments:
361
+ /// value: any
362
+ /// Any value to check.
363
+ ///
364
+ /// Return: boolean
365
+ /// Return `true` if the provided `value` is a valid value for the field
366
+ /// type this was called upon, or `false` otherwise.
179
367
  // eslint-disable-next-line no-unused-vars
180
368
  isValidValue(value) {
181
369
  return true;
182
370
  }
183
371
 
372
+ /// Get the field that owns this type.
184
373
  getField() {
185
374
  return this._field;
186
375
  }
187
376
 
377
+ /// Set the field that owns this type.
378
+ ///
379
+ /// Arguments:
380
+ /// field: <see>Field</see>
381
+ /// The field that owns this type.
188
382
  setField(field) {
189
383
  this._field = field;
190
384
  }
191
385
 
386
+ /// Get the model class that owns this field type.
192
387
  getModel() {
193
388
  let Model = this._Model;
194
389
  if (!Model) {
@@ -206,6 +401,11 @@ class Type {
206
401
  return Model;
207
402
  }
208
403
 
404
+ /// Set the model class that this type belongs to.
405
+ ///
406
+ /// Arguments:
407
+ /// Model: class <see>Model</see>
408
+ /// The model class that owns this field type.
209
409
  setModel(Model) {
210
410
  this._Model = Model;
211
411
  }
@@ -258,17 +458,111 @@ class Type {
258
458
  initialize(connection, self) {
259
459
  }
260
460
 
261
- isDirty() {
461
+ /// Check if the field value is dirty.
462
+ ///
463
+ /// This is only used by some field types, such as <see>SerializedType</see>.
464
+ /// It will respond with `true` if the field is dirty, or `false` if it isn't.
465
+ ///
466
+ /// Interface:
467
+ /// interface CheckDirtyContext {
468
+ /// value: any; // The field's current value
469
+ /// field: Field; // The field that is being checked
470
+ /// fieldName: string; // The fieldName of the field that is being checked
471
+ /// self: Model; // The model instance the field is from
472
+ /// connection: ConnectionBase; // The current connection
473
+ /// }
474
+ ///
475
+ /// Arguments:
476
+ /// context: CheckDirtyContext
477
+ /// The context provided to the call.
478
+ ///
479
+ /// Return: boolean
480
+ /// Returns `true` if the field is dirty, or `false` otherwise.
481
+ // eslint-disable-next-line no-unused-vars
482
+ isDirty(context) {
262
483
  }
263
484
 
485
+ /// This method is called any time a field's value is set on a model.
486
+ ///
487
+ /// It is only used by the <see>SerializedType</see> to update its internal
488
+ /// "dirty" cache. It can be used by any field type however to detect when
489
+ /// a model's field's value changes.
490
+ ///
491
+ /// Interface:
492
+ /// interface SetFieldValueContext {
493
+ /// value: any;
494
+ /// field: Field;
495
+ /// fieldName: string;
496
+ /// self: Model;
497
+ /// }
498
+ ///
499
+ /// Arguments:
500
+ /// context: SetFieldValueContext
501
+ /// The context provided to the call.
502
+ ///
503
+ /// Return: undefined
504
+ /// This method returns nothing.
264
505
  onSetFieldValue() {
265
506
  }
266
507
 
267
- serialize(value) {
508
+ /// Serialize the field's value. This is the opposite of
509
+ /// <see>Type.deserialize</see>.
510
+ ///
511
+ /// This is used mostly by the <see>Model.toJSON</see> method
512
+ /// to serialize values for JSON format. However, it is also used
513
+ /// in some other cases, such as formatting for <see>DateType</see>
514
+ /// and <see>DateTimeType</see>, and for checking dirtiness in the
515
+ /// <see>SerializedType</see>.
516
+ ///
517
+ /// The job of this method is to serialize the field's value for any serialize operation,
518
+ /// generally to JSON. If a `connection` is provided as the second argument,
519
+ /// then it is assumed by the field type that it is being serialized for the
520
+ /// database. In this case the connection generally assists with the serializing
521
+ /// of the value. This is used for example with <see>DateType</see>
522
+ /// and <see>DateTimeType</see> types, when they need to be serialized to or from
523
+ /// a database operation.
524
+ ///
525
+ /// Arguments:
526
+ /// value: any
527
+ /// The field's value to serialize.
528
+ /// connection?: <see>Connection</see>
529
+ /// An optional connection, which if provided will likely change
530
+ /// how the value is serialized.
531
+ ///
532
+ /// Return: any | string
533
+ /// Though the return value will generally be a string, it isn't required
534
+ /// to be.
535
+ ///
536
+ /// See: Type.deserialize
537
+ // eslint-disable-next-line no-unused-vars
538
+ serialize(value, connection) {
268
539
  return value;
269
540
  }
270
541
 
271
- deserialize(value) {
542
+ /// Deserialize the field's value. This is the opposite of
543
+ /// <see>Type.serialize</see>.
544
+ ///
545
+ /// The job of this method to serialize the field's value for any serialize operation,
546
+ /// generally to JSON. If a `connection` is provided as the second argument,
547
+ /// then it is assumed by the field type that it is being serialized for the
548
+ /// database. In this case the connection generally assists with the serializing
549
+ /// of the value. This is used for example with <see>DateType</see>
550
+ /// and <see>DateTimeType</see> types, when they need to be serialized to or from
551
+ /// a database operation.
552
+ ///
553
+ /// Arguments:
554
+ /// value: any
555
+ /// The field's value to deserialize.
556
+ /// connection?: <see>Connection</see>
557
+ /// An optional connection, which if provided will likely change
558
+ /// how the value is deserialized.
559
+ ///
560
+ /// Return: any
561
+ /// The deserialized value for the field.
562
+ ///
563
+ /// See: Type.serialize
564
+ // eslint-disable-next-line no-unused-vars
565
+ deserialize(value, connection) {
272
566
  return value;
273
567
  }
274
568
 
@@ -1,3 +1,29 @@
1
+ ///! import `var { Utils: { AsyncStore } } = require('mythix-orm');`
2
+ ///!
3
+ ///! AsyncStore utilities provide the only global
4
+ ///! used in mythix-orm. The global used here is an
5
+ ///! [AsyncLocalStorage](https://nodejs.org/docs/latest-v18.x/api/async_context.html#class-asynclocalstorage) instance used to track
6
+ ///! connections (and transactions) through asynchronous
7
+ ///! calls in the engine.
8
+ ///!
9
+ ///! The `node:async_hooks` module is imported inside a
10
+ ///! `try/catch` block, so if your Javascript engine doesn't
11
+ ///! support `AsyncLocalStorage`, this will fail, and silently
12
+ ///! fallback to running the engine with no `AsyncLocalStorage`
13
+ ///! support... which simply means that connection instances need
14
+ ///! to be manually passed around everywhere.
15
+ ///!
16
+ ///! **!WARNING!: Never set the `'connection'` key, or a string key
17
+ ///! that matches one of your model names to this `AsyncLocalStorage` context
18
+ ///! unless you know exactly what you are doing. These keys are reserved
19
+ ///! by Mythix ORM to pass connections and transactions through calls.** Any
20
+ ///! and all other custom keys are available for use, though it would be
21
+ ///! wise for you to prefix your key names, so as to avoid future name collisions
22
+ ///! that might occur due to newer versions of Mythix ORM, or name collisions with
23
+ ///! other 3rd party plugins or code that might set keys on the context as well.
24
+ ///!
25
+ ///! DocScope: AsyncStore
26
+
1
27
  'use strict';
2
28
 
3
29
  let globalAsyncStore = global._mythixGlobalAsyncLocalStore;
@@ -14,10 +40,33 @@ if (!globalAsyncStore) {
14
40
  }
15
41
  }
16
42
 
43
+ /// Fetch the AsyncLocalStorage store.
44
+ /// This calls `.getStore()` on the global
45
+ /// `AsyncLocalStorage` instance.
46
+ ///
47
+ /// Return: any
48
+ /// The value from a `.getStore()` call on the global `AsyncLocalStorage` instance.
49
+ /// This will be `undefined` if no `AsyncLocalStorage` context is in scope.
17
50
  function getContextStore() {
18
51
  return globalAsyncStore.getStore();
19
52
  }
20
53
 
54
+ /// Get a specific value from the global `AsyncLocalStorage` context
55
+ /// by name.
56
+ ///
57
+ /// Arguments:
58
+ /// key: any
59
+ /// The name of the property to return. The `AsyncLocalStorage`
60
+ /// context internally uses a `Map` instance, so the `key` provided
61
+ /// can be of any type.
62
+ /// defaultValue: any
63
+ /// The default value to return if the key specified is not found.
64
+ ///
65
+ /// Return:
66
+ /// Return the property named by `key` if one is found, otherwise
67
+ /// return the `defaultValue` that was provided. If the global `AsyncLocalStorage` context is not
68
+ /// in scope when this is called, then the `defaultValue` will always be
69
+ /// returned.
21
70
  function getContextValue(key, defaultValue) {
22
71
  let store = globalAsyncStore.getStore();
23
72
  while (store) {
@@ -33,19 +82,47 @@ function getContextValue(key, defaultValue) {
33
82
  return defaultValue;
34
83
  }
35
84
 
85
+ /// Set a specific value on the global `AsyncLocalStorage` context
86
+ /// by name. A `Map` instance is used internally, so the `key` can
87
+ /// be of any type.
88
+ ///
89
+ /// Note:
90
+ /// The global `AsyncLocalStorage` context must be in scope for this method to work.
91
+ ///
92
+ /// Arguments:
93
+ /// key: any
94
+ /// The key you wish to set on the global `AsyncLocalStorage` context.
95
+ /// value: any
96
+ /// The value you wish to set on the global `AsyncLocalStorage` context.
97
+ ///
98
+ /// Return: undefined
99
+ /// This method returns nothing.
36
100
  function setContextValue(key, value) {
37
101
  let store = globalAsyncStore.getStore();
38
102
  if (!store || !store.context)
39
103
  return;
40
104
 
41
- return store.context.set(key, value);
105
+ store.context.set(key, value);
42
106
  }
43
107
 
108
+ /// Run an asynchronous method in the global `AsyncLocalStorage` context.
109
+ ///
110
+ /// Running a method this way will provide the method, and all calls within
111
+ /// its scope the global `AsyncLocalStorage` context.
112
+ ///
113
+ /// Arguments:
114
+ /// context: Map | null
115
+ /// The context `Map` to use for the operation.
116
+ /// callback: Function
117
+ /// The asynchronous method to call and provide the context to.
118
+ ///
119
+ /// Return: any
120
+ /// The return value from the callback.
44
121
  function runInContext(context, callback) {
45
122
  return globalAsyncStore.run(
46
123
  {
47
- parent: globalAsyncStore.getStore(),
48
- context,
124
+ parent: globalAsyncStore.getStore(),
125
+ context: context || new Map(),
49
126
  },
50
127
  callback,
51
128
  );